blob: 1bec2a3a711cc4aefa24e5e2eb1053ab866ca180 [file] [log] [blame]
// Copyright (c) 2022 ARM Limited
// All rights reserved
//
// The license below extends only to copyright in the software and shall
// not be construed as granting a license to any other intellectual
// property including but not limited to intellectual property relating
// to a hardware implementation of the functionality of the software
// licensed hereunder. You may use the software subject to the license
// terms below provided that you ensure that this notice is replicated
// unmodified and in its entirety in all distributions of the software,
// modified or unmodified, in source code or in binary form.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// @file Definition of SME instruction templates.
let {{
# All SME instructions should be checking if Streaming Mode is
# enabled in the PSTATE. The following call checks both the SME and
# the FP enable flags in the relevant registers depending on the
# current EL.
smEnCheckCodeNoPstate = '''
if (FullSystem) {
fault = this->checkSmeEnabled(xc->tcBase(), Cpsr, Cpacr64);
if (fault != NoFault) {
return fault;
}
}
'''
smPreamble = '''
CPSR cpsr = (CPSR) Cpsr;
ExceptionLevel target_el = (ExceptionLevel) (uint8_t) cpsr.el;
if (target_el == EL0) {
target_el = EL1;
}
'''
smCheckCode = '''
// Check streaming mode first
if ((Svcr & 1) != 0b1) {
fault = smeAccessTrap(target_el, 0b10);
return fault;
}
'''
zaCheckCode = '''
// Check if ZA is enabled
if ((Svcr & 2) >> 1 != 0b1) {
fault = smeAccessTrap(target_el, 0b11);
return fault;
}
'''
# If streaming mode is disabled or ZA is disabled we trap
smEnCheckCode = smPreamble + smCheckCode + zaCheckCode + \
smEnCheckCodeNoPstate
# If ZA is disabled we trap
smEnCheckCodeNoSM = smPreamble + zaCheckCode + smEnCheckCodeNoPstate
# If streaming mode is disabled we trap
smEnCheckCodeNoZA = smPreamble + smCheckCode + smEnCheckCodeNoPstate
smeZaWrite = '''
// Force the ISA parser to see the access to ZA as a write,
// not a read.
ZA = ZA;
'''
}};
def template SmeAddDeclare {{
template <typename TPElem>
class %(class_name)s : public %(base_class)s
{
private:
%(reg_idx_arr_decl)s;
public:
/// Constructor.
%(class_name)s(ExtMachInst machInst, uint64_t imm,
RegIndex op1, RegIndex gp1,
RegIndex gp2)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
imm, op1, gp1, gp2)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
Fault execute(ExecContext *, trace::InstRecord *) const override;
};
}};
def template SmeAddVlDeclare {{
class %(class_name)s : public %(base_class)s
{
private:
%(reg_idx_arr_decl)s;
public:
/// Constructor.
%(class_name)s(ExtMachInst machInst,
RegIndex dest, RegIndex op1,
int8_t imm)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
dest, op1, imm)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
Fault execute(ExecContext *, trace::InstRecord *) const override;
};
}};
def template SmeLd1xDeclare {{
template <typename TPElem>
class %(class_name)s : public %(base_class)s
{
private:
%(reg_idx_arr_decl)s;
public:
/// Constructor.
%(class_name)s(ExtMachInst machInst, uint64_t imm,
RegIndex op1, RegIndex mpop1,
RegIndex op2, RegIndex op3,
bool V)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
imm, op1, mpop1, op2, op3, V)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
Fault execute(ExecContext *, trace::InstRecord *) const override;
Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
Fault completeAcc(PacketPtr, ExecContext *,
trace::InstRecord *) const override;
};
}};
def template SmeLd1xExecute {{
template <typename TPElem>
Fault %(class_name)s<TPElem>::execute(ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
Request::Flags flags = 0;
%(op_decl)s;
%(op_rd)s;
%(code)s;
// We need a buffer in which to store the data:
TPElem data[MaxSmeVecLenInBytes / sizeof(TPElem)];
if (fault == NoFault) {
// The size of the access is controlled by the type of data, and
// the number of elements.
fault = xc->readMem(EA, (uint8_t*)data, eCount * sizeof(TPElem),
flags, rdEn);
}
if (fault == NoFault) {
%(za_write)s
// Write back the changes to the actual tile
%(op_wb)s;
}
return fault;
}
}};
def template SmeLd1xInitiateAcc {{
template <typename TPElem>
Fault %(class_name)s<TPElem>::initiateAcc(ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
Request::Flags flags = 0;
%(op_decl)s;
%(op_rd)s;
%(code)s;
if (fault == NoFault) {
fault = xc->initiateMemRead(EA, eCount * sizeof(TPElem),
flags, rdEn);
}
return fault;
}
}};
def template SmeLd1xCompleteAcc {{
template <typename TPElem>
Fault %(class_name)s<TPElem>::completeAcc(PacketPtr pkt, ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
%(op_decl)s;
%(op_rd)s;
%(code)s;
// The O3 CPU will call this with a NULL-pointer if the access was
// disabled. Just return.
if (pkt == NULL) {
return fault;
}
if (fault == NoFault) {
// We need a buffer in which to store the data:
TPElem data[MaxSmeVecLenInBytes / sizeof(TPElem)];
// The size for the amount of data returned here should
// have been set in initiateAcc.
memcpy((uint8_t*)data, pkt->getPtr<uint8_t>(), pkt->getSize());
%(za_write)s
// Write back the changes to the tile
%(op_wb)s;
}
return fault;
}
}};
def template SmeLd1xExecDeclare {{
template
Fault %(class_name)s<%(targs)s>::execute(
ExecContext *, trace::InstRecord *) const;
template
Fault %(class_name)s<%(targs)s>::initiateAcc(
ExecContext *, trace::InstRecord *) const;
template
Fault %(class_name)s<%(targs)s>::completeAcc(
PacketPtr, ExecContext *, trace::InstRecord *) const;
}};
def template SmeLdrDeclare {{
class %(class_name)s : public %(base_class)s
{
private:
%(reg_idx_arr_decl)s;
public:
/// Constructor.
%(class_name)s(ExtMachInst machInst, uint64_t imm,
RegIndex op1, RegIndex op2)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
imm, op1, op2)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
Fault execute(ExecContext *, trace::InstRecord *) const override;
Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
Fault completeAcc(PacketPtr, ExecContext *,
trace::InstRecord *) const override;
};
}};
def template SmeLdrExecute {{
Fault %(class_name)s::execute(ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
Request::Flags flags = 0;
%(op_decl)s;
%(op_rd)s;
%(code)s;
auto rdEn = std::vector<bool>(eCount, true);
// We need a buffer in which to store the data:
uint8_t data[MaxSmeVecLenInBytes];
if (fault == NoFault) {
fault = xc->readMem(EA, (uint8_t*)data, eCount, flags, rdEn);
}
if (fault == NoFault) {
auto row = getTileHSlice<uint8_t>(ZA, 0, vec_index);
for (int i = 0; i < eCount; ++i) {
row[i] = data[i];
}
%(op_wb)s;
}
return fault;
}
}};
def template SmeLdrInitiateAcc {{
Fault %(class_name)s::initiateAcc(ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
Request::Flags flags = 0;
%(op_decl)s;
%(op_rd)s;
%(code)s;
auto rdEn = std::vector<bool>(eCount, true);
if (fault == NoFault) {
fault = xc->initiateMemRead(EA, eCount, flags, rdEn);
}
return fault;
}
}};
def template SmeLdrCompleteAcc {{
Fault %(class_name)s::completeAcc(PacketPtr pkt, ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
%(op_decl)s;
%(op_rd)s;
%(code)s;
// The O3 CPU will call this with a NULL-pointer if the access was
// disabled. Just return.
if (pkt == NULL) {
return fault;
}
if (fault == NoFault) {
// Get the data out of the packet
auto row = getTileHSlice<uint8_t>(ZA, 0, vec_index);
for (int i = 0; i < eCount; ++i) {
row[i] = pkt->getPtr<uint8_t>()[i];
}
%(op_wb)s;
}
return fault;
}
}};
def template SMEMgmtDeclare {{
class %(class_name)s : public %(base_class)s
{
private:
%(reg_idx_arr_decl)s;
public:
/// Constructor.
%(class_name)s(ExtMachInst machInst, uint64_t imm)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, imm)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
Fault execute(ExecContext *, trace::InstRecord *) const override;
};
}};
def template SmeMovaExtractDeclare {{
template <typename TPElem>
class %(class_name)s : public %(base_class)s
{
private:
%(reg_idx_arr_decl)s;
public:
/// Constructor.
%(class_name)s(ExtMachInst machInst, RegIndex op1,
uint8_t imm, RegIndex gp,
RegIndex op2, bool v)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
op1, imm, gp, op2, v)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
Fault execute(ExecContext *, trace::InstRecord *) const override;
};
}};
def template SmeMovaInsertDeclare {{
template <typename TPElem>
class %(class_name)s : public %(base_class)s
{
private:
%(reg_idx_arr_decl)s;
public:
/// Constructor.
%(class_name)s(ExtMachInst machInst, uint8_t imm,
RegIndex op1, RegIndex gp,
RegIndex op2, bool v)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
imm, op1, gp, op2, v)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
Fault execute(ExecContext *, trace::InstRecord *) const override;
};
}};
def template SmeFPOPDeclare {{
template <typename TPSElem, typename TPDElem>
class %(class_name)s : public %(base_class)s
{
private:
%(reg_idx_arr_decl)s;
public:
/// Constructor.
%(class_name)s(ExtMachInst machInst, uint64_t imm,
RegIndex op1, RegIndex gp1,
RegIndex gp2, RegIndex op2)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
imm, op1, gp1, gp2, op2)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
Fault execute(ExecContext *, trace::InstRecord *) const override;
};
}};
def template SmeIntOPDeclare {{
template <typename TPS1Elem, typename TPS2Elem, typename TPDElem>
class %(class_name)s : public %(base_class)s
{
private:
%(reg_idx_arr_decl)s;
public:
/// Constructor.
%(class_name)s(ExtMachInst machInst, uint64_t imm,
RegIndex op1, RegIndex gp1,
RegIndex gp2, RegIndex op2)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
imm, op1, gp1, gp2, op2)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
Fault execute(ExecContext *, trace::InstRecord *) const override;
};
}};
def template SmeRdsvlDeclare {{
class %(class_name)s : public %(base_class)s
{
private:
%(reg_idx_arr_decl)s;
public:
/// Constructor.
%(class_name)s(ExtMachInst machInst,
RegIndex dest, int8_t imm)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
dest, imm)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
Fault execute(ExecContext *, trace::InstRecord *) const override;
};
}};
def template SmeSt1xDeclare {{
template <typename TPElem>
class %(class_name)s : public %(base_class)s
{
private:
%(reg_idx_arr_decl)s;
public:
/// Constructor.
%(class_name)s(ExtMachInst machInst, uint64_t imm,
RegIndex op1, RegIndex mpop1,
RegIndex op2, RegIndex op3, bool V)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
imm, op1, mpop1, op2, op3, V)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
Fault execute(ExecContext *, trace::InstRecord *) const override;
Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
Fault completeAcc(PacketPtr, ExecContext *,
trace::InstRecord *) const override;
};
}};
def template SmeSt1xExecute {{
template <typename TPElem>
Fault %(class_name)s<TPElem>::execute(ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
Request::Flags flags = 0;
%(op_decl)s;
%(op_rd)s;
%(code)s;
if (fault == NoFault) {
fault = xc->writeMem((uint8_t*)data, eCount * sizeof(TPElem), EA,
flags, NULL, wrEn);
}
return fault;
}
}};
def template SmeSt1xInitiateAcc {{
template <typename TPElem>
Fault %(class_name)s<TPElem>::initiateAcc(ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
Request::Flags flags = 0;
%(op_decl)s;
%(op_rd)s;
%(code)s;
if (fault == NoFault) {
fault = xc->writeMem((uint8_t*)data, eCount * sizeof(TPElem), EA,
flags, NULL, wrEn);
}
return fault;
}
}};
def template SmeSt1xCompleteAcc {{
template <typename TPElem>
Fault %(class_name)s<TPElem>::completeAcc(PacketPtr pkt, ExecContext *xc,
trace::InstRecord *traceData) const
{
return NoFault;
}
}};
def template SmeStrDeclare {{
class %(class_name)s : public %(base_class)s
{
private:
%(reg_idx_arr_decl)s;
public:
/// Constructor.
%(class_name)s(ExtMachInst machInst, uint64_t imm,
RegIndex op1, RegIndex op2)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
imm, op1, op2)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
Fault execute(ExecContext *, trace::InstRecord *) const override;
Fault initiateAcc(ExecContext *, trace::InstRecord *) const override;
Fault completeAcc(PacketPtr, ExecContext *,
trace::InstRecord *) const override;
};
}};
def template SmeStrExecute {{
Fault %(class_name)s::execute(ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
Request::Flags flags = 0;
%(op_decl)s;
%(op_rd)s;
%(code)s;
if (fault == NoFault) {
auto wrEn = std::vector<bool>(eCount, true);
fault = xc->writeMem((uint8_t*)data, eCount, EA,
flags, NULL, wrEn);
}
return fault;
}
}};
def template SmeStrInitiateAcc {{
Fault %(class_name)s::initiateAcc(ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
Request::Flags flags = 0;
%(op_decl)s;
%(op_rd)s;
%(code)s;
if (fault == NoFault) {
auto wrEn = std::vector<bool>(eCount, true);
fault = xc->writeMem((uint8_t*)data, eCount, EA,
flags, NULL, wrEn);
}
return fault;
}
}};
def template SmeStrCompleteAcc {{
Fault %(class_name)s::completeAcc(PacketPtr pkt, ExecContext *xc,
trace::InstRecord *traceData) const
{
// TODO-SME: Can this fail?
return NoFault;
}
}};
def template SmeSt1xExecDeclare {{
template
Fault %(class_name)s<%(targs)s>::execute(
ExecContext *, trace::InstRecord *) const;
template
Fault %(class_name)s<%(targs)s>::initiateAcc(
ExecContext *, trace::InstRecord *) const;
template
Fault %(class_name)s<%(targs)s>::completeAcc(
PacketPtr, ExecContext *, trace::InstRecord *) const;
}};
def template SmeZeroDeclare {{
template <typename TPElem>
class %(class_name)s : public %(base_class)s
{
private:
%(reg_idx_arr_decl)s;
public:
/// Constructor.
%(class_name)s(ExtMachInst machInst, uint8_t imm)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, imm)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
Fault execute(ExecContext *, trace::InstRecord *) const override;
};
}};
def template SmeExecute {{
Fault
%(class_name)s::execute(ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
%(op_decl)s;
%(op_rd)s;
%(code)s;
if (fault == NoFault) {
%(op_wb)s;
}
return fault;
}
}};
def template SmeTemplatedExecute {{
template <typename TPElem>
Fault
%(class_name)s<TPElem>::execute(ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
%(op_decl)s;
%(op_rd)s;
%(code)s;
if (fault == NoFault) {
%(op_wb)s;
}
return fault;
}
}};
def template SmeDualTemplatedExecute {{
template <typename TPSElem, typename TPDElem>
Fault
%(class_name)s<TPSElem, TPDElem>::execute(ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
%(op_decl)s;
%(op_rd)s;
%(code)s;
if (fault == NoFault) {
%(op_wb)s;
}
return fault;
}
}};
def template SmeTripleTemplatedExecute {{
template <typename TPS1Elem, typename TPS2Elem, typename TPDElem>
Fault
%(class_name)s<TPS1Elem, TPS2Elem, TPDElem>::execute(ExecContext *xc,
trace::InstRecord *traceData) const
{
Fault fault = NoFault;
%(op_decl)s;
%(op_rd)s;
%(code)s;
if (fault == NoFault) {
%(op_wb)s;
}
return fault;
}
}};
def template SmeOpExecDeclare {{
template
Fault %(class_name)s<%(targs)s>::execute(
ExecContext *, trace::InstRecord *) const;
}};