blob: cc7346aa821f229fabd364907ab28f0f1a838b7e [file] [log] [blame]
// -*- mode:c++ -*-
// Copyright (c) 2015 Riscv Developers
// Copyright (c) 2016 The University of Virginia
// All rights reserved.
//
// 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.
//
// Authors: Alec Roelke
// Declaration templates
def template AtomicMemOpDeclare {{
/**
* Static instruction class for an AtomicMemOp operation
*/
class %(class_name)s : public %(base_class)s
{
public:
// Constructor
%(class_name)s(ExtMachInst machInst);
protected:
/*
* The main RMW part of an AMO
*/
class %(class_name)sRMW : public %(base_class)sMicro
{
public:
// Constructor
%(class_name)sRMW(ExtMachInst machInst, %(class_name)s *_p);
Fault execute(ExecContext *, Trace::InstRecord *) const override;
Fault initiateAcc(ExecContext *,
Trace::InstRecord *) const override;
Fault completeAcc(PacketPtr, ExecContext *,
Trace::InstRecord *) const override;
};
};
}};
def template LRSCDeclare {{
/**
* Static instruction class for an AtomicMemOp operation
*/
class %(class_name)s : public %(base_class)s
{
public:
// Constructor
%(class_name)s(ExtMachInst machInst);
protected:
class %(class_name)sMicro : public %(base_class)sMicro
{
public:
// Constructor
%(class_name)sMicro(ExtMachInst machInst, %(class_name)s *_p);
Fault execute(ExecContext *, Trace::InstRecord *) const override;
Fault initiateAcc(ExecContext *,
Trace::InstRecord *) const override;
Fault completeAcc(PacketPtr, ExecContext *,
Trace::InstRecord *) const override;
};
};
}};
// Constructor templates
def template LRSCMacroConstructor {{
%(class_name)s::%(class_name)s(ExtMachInst machInst):
%(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
{
%(constructor)s;
StaticInstPtr rel_fence;
StaticInstPtr lrsc;
StaticInstPtr acq_fence;
// set up release fence
if (RL) {
rel_fence = new MemFenceMicro(machInst, No_OpClass);
rel_fence->setFlag(IsFirstMicroop);
rel_fence->setFlag(IsMemBarrier);
rel_fence->setFlag(IsDelayedCommit);
}
// set up atomic rmw op
lrsc = new %(class_name)sMicro(machInst, this);
if (!RL) {
lrsc->setFlag(IsFirstMicroop);
}
if (!AQ) {
lrsc->setFlag(IsLastMicroop);
} else {
lrsc->setFlag(IsDelayedCommit);
}
// set up acquire fence
if (AQ) {
acq_fence = new MemFenceMicro(machInst, No_OpClass);
acq_fence->setFlag(IsLastMicroop);
acq_fence->setFlag(IsMemBarrier);
}
if (RL && AQ) {
microops = {rel_fence, lrsc, acq_fence};
} else if (RL) {
microops = {rel_fence, lrsc};
} else if (AQ) {
microops = {lrsc, acq_fence};
} else {
microops = {lrsc};
}
}
}};
def template LRSCMicroConstructor {{
%(class_name)s::%(class_name)sMicro::%(class_name)sMicro(
ExtMachInst machInst, %(class_name)s *_p)
: %(base_class)sMicro("%(mnemonic)s", machInst, %(op_class)s)
{
%(constructor)s;
}
}};
def template AtomicMemOpMacroConstructor {{
%(class_name)s::%(class_name)s(ExtMachInst machInst)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
{
%(constructor)s;
StaticInstPtr rel_fence;
StaticInstPtr rmw_op;
StaticInstPtr acq_fence;
// set up release fence
if (RL) {
rel_fence = new MemFenceMicro(machInst, No_OpClass);
rel_fence->setFlag(IsFirstMicroop);
rel_fence->setFlag(IsMemBarrier);
rel_fence->setFlag(IsDelayedCommit);
}
// set up atomic rmw op
rmw_op = new %(class_name)sRMW(machInst, this);
if (!RL) {
rmw_op->setFlag(IsFirstMicroop);
}
if (!AQ) {
rmw_op->setFlag(IsLastMicroop);
} else {
rmw_op->setFlag(IsDelayedCommit);
}
// set up acquire fence
if (AQ) {
acq_fence = new MemFenceMicro(machInst, No_OpClass);
acq_fence->setFlag(IsLastMicroop);
acq_fence->setFlag(IsMemBarrier);
}
if (RL && AQ) {
microops = {rel_fence, rmw_op, acq_fence};
} else if (RL) {
microops = {rel_fence, rmw_op};
} else if (AQ) {
microops = {rmw_op, acq_fence};
} else {
microops = {rmw_op};
}
}
}};
def template AtomicMemOpRMWConstructor {{
%(class_name)s::%(class_name)sRMW::%(class_name)sRMW(
ExtMachInst machInst, %(class_name)s *_p)
: %(base_class)s("%(mnemonic)s[l]", machInst, %(op_class)s)
{
%(constructor)s;
// overwrite default flags
flags[IsMemRef] = true;
flags[IsLoad] = false;
flags[IsStore] = false;
flags[IsAtomic] = true;
}
}};
// execute() templates
def template LoadReservedExecute {{
Fault
%(class_name)s::%(class_name)sMicro::execute(
ExecContext *xc, Trace::InstRecord *traceData) const
{
Addr EA;
Fault fault = NoFault;
%(op_decl)s;
%(op_rd)s;
%(ea_code)s;
if (fault == NoFault) {
fault = readMemAtomic(xc, traceData, EA, Mem, memAccessFlags);
%(memacc_code)s;
}
if (fault == NoFault) {
%(op_wb)s;
}
return fault;
}
}};
def template StoreCondExecute {{
Fault %(class_name)s::%(class_name)sMicro::execute(ExecContext *xc,
Trace::InstRecord *traceData) const
{
Addr EA;
Fault fault = NoFault;
uint64_t result;
%(op_decl)s;
%(op_rd)s;
%(ea_code)s;
if (fault == NoFault) {
%(memacc_code)s;
}
if (fault == NoFault) {
fault = writeMemAtomic(xc, traceData, Mem, EA, memAccessFlags,
&result);
// RISC-V has the opposite convention gem5 has for success flags,
// so we invert the result here.
result = !result;
}
if (fault == NoFault) {
%(postacc_code)s;
}
if (fault == NoFault) {
%(op_wb)s;
}
return fault;
}
}};
def template AtomicMemOpRMWExecute {{
Fault %(class_name)s::%(class_name)sRMW::execute(ExecContext *xc,
Trace::InstRecord *traceData) const
{
Addr EA;
Fault fault = NoFault;
%(op_decl)s;
%(op_rd)s;
%(ea_code)s;
%(amoop_code)s;
assert(amo_op);
if (fault == NoFault) {
fault = amoMemAtomic(xc, traceData, Mem, EA, memAccessFlags,
amo_op);
%(memacc_code)s;
}
if (fault == NoFault) {
%(postacc_code)s;
}
if (fault == NoFault) {
%(op_wb)s;
}
return fault;
}
}};
// initiateAcc() templates
def template LoadReservedInitiateAcc {{
Fault
%(class_name)s::%(class_name)sMicro::initiateAcc(ExecContext *xc,
Trace::InstRecord *traceData) const
{
Addr EA;
Fault fault = NoFault;
%(op_src_decl)s;
%(op_rd)s;
%(ea_code)s;
if (fault == NoFault) {
fault = initiateMemRead(xc, traceData, EA, Mem, memAccessFlags);
}
return fault;
}
}};
def template StoreCondInitiateAcc {{
Fault
%(class_name)s::%(class_name)sMicro::initiateAcc(ExecContext *xc,
Trace::InstRecord *traceData) const
{
Addr EA;
Fault fault = NoFault;
%(op_decl)s;
%(op_rd)s;
%(ea_code)s;
if (fault == NoFault) {
%(memacc_code)s;
}
if (fault == NoFault) {
fault = writeMemTiming(xc, traceData, Mem, EA,
memAccessFlags, nullptr);
}
if (fault == NoFault) {
%(op_wb)s;
}
return fault;
}
}};
def template AtomicMemOpRMWInitiateAcc {{
Fault
%(class_name)s::%(class_name)sRMW::initiateAcc(ExecContext *xc,
Trace::InstRecord *traceData) const
{
Addr EA;
Fault fault = NoFault;
%(op_src_decl)s;
%(op_rd)s;
%(ea_code)s;
%(amoop_code)s;
assert(amo_op);
if (fault == NoFault) {
fault = initiateMemAMO(xc, traceData, EA, Mem, memAccessFlags,
amo_op);
}
return fault;
}
}};
// completeAcc() templates
def template LoadReservedCompleteAcc {{
Fault
%(class_name)s::%(class_name)sMicro::completeAcc(PacketPtr pkt,
ExecContext *xc, Trace::InstRecord *traceData) const
{
Fault fault = NoFault;
%(op_decl)s;
%(op_rd)s;
getMem(pkt, Mem, traceData);
if (fault == NoFault) {
%(memacc_code)s;
}
if (fault == NoFault) {
%(op_wb)s;
}
return fault;
}
}};
def template StoreCondCompleteAcc {{
Fault %(class_name)s::%(class_name)sMicro::completeAcc(Packet *pkt,
ExecContext *xc, Trace::InstRecord *traceData) const
{
Fault fault = NoFault;
%(op_dest_decl)s;
// RISC-V has the opposite convention gem5 has for success flags,
// so we invert the result here.
uint64_t result = !pkt->req->getExtraData();
if (fault == NoFault) {
%(postacc_code)s;
}
if (fault == NoFault) {
%(op_wb)s;
}
return fault;
}
}};
def template AtomicMemOpRMWCompleteAcc {{
Fault %(class_name)s::%(class_name)sRMW::completeAcc(Packet *pkt,
ExecContext *xc, Trace::InstRecord *traceData) const
{
Fault fault = NoFault;
%(op_decl)s;
%(op_rd)s;
getMem(pkt, Mem, traceData);
if (fault == NoFault) {
%(memacc_code)s;
}
if (fault == NoFault) {
%(op_wb)s;
}
return fault;
}
}};
// LR/SC/AMO decode formats
def format LoadReserved(memacc_code, postacc_code={{ }}, ea_code={{EA = Rs1;}},
mem_flags=[], inst_flags=[]) {{
macro_ea_code = ''
macro_inst_flags = []
macro_iop = InstObjParams(name, Name, 'LoadReserved', macro_ea_code,
macro_inst_flags)
header_output = LRSCDeclare.subst(macro_iop)
decoder_output = LRSCMacroConstructor.subst(macro_iop)
decode_block = BasicDecode.subst(macro_iop)
exec_output = ''
mem_flags = makeList(mem_flags)
inst_flags = makeList(inst_flags)
iop = InstObjParams(name, Name, 'LoadReserved',
{'ea_code': ea_code, 'memacc_code': memacc_code,
'postacc_code': postacc_code}, inst_flags)
iop.constructor += '\n\tmemAccessFlags = memAccessFlags | ' + \
'|'.join(['Request::%s' % flag for flag in mem_flags]) + ';'
decoder_output += LRSCMicroConstructor.subst(iop)
decode_block += BasicDecode.subst(iop)
exec_output += LoadReservedExecute.subst(iop) \
+ LoadReservedInitiateAcc.subst(iop) \
+ LoadReservedCompleteAcc.subst(iop)
}};
def format StoreCond(memacc_code, postacc_code={{ }}, ea_code={{EA = Rs1;}},
mem_flags=[], inst_flags=[]) {{
macro_ea_code = ''
macro_inst_flags = []
macro_iop = InstObjParams(name, Name, 'StoreCond', macro_ea_code,
macro_inst_flags)
header_output = LRSCDeclare.subst(macro_iop)
decoder_output = LRSCMacroConstructor.subst(macro_iop)
decode_block = BasicDecode.subst(macro_iop)
exec_output = ''
mem_flags = makeList(mem_flags)
inst_flags = makeList(inst_flags)
iop = InstObjParams(name, Name, 'StoreCond',
{'ea_code': ea_code, 'memacc_code': memacc_code,
'postacc_code': postacc_code}, inst_flags)
iop.constructor += '\n\tmemAccessFlags = memAccessFlags | ' + \
'|'.join(['Request::%s' % flag for flag in mem_flags]) + ';'
decoder_output += LRSCMicroConstructor.subst(iop)
decode_block += BasicDecode.subst(iop)
exec_output += StoreCondExecute.subst(iop) \
+ StoreCondInitiateAcc.subst(iop) \
+ StoreCondCompleteAcc.subst(iop)
}};
def format AtomicMemOp(memacc_code, amoop_code, postacc_code={{ }},
ea_code={{EA = Rs1;}}, mem_flags=[], inst_flags=[]) {{
macro_ea_code = ''
macro_inst_flags = []
macro_iop = InstObjParams(name, Name, 'AtomicMemOp', macro_ea_code,
macro_inst_flags)
header_output = AtomicMemOpDeclare.subst(macro_iop)
decoder_output = AtomicMemOpMacroConstructor.subst(macro_iop)
decode_block = BasicDecode.subst(macro_iop)
exec_output = ''
rmw_mem_flags = makeList(mem_flags)
rmw_inst_flags = makeList(inst_flags)
rmw_iop = InstObjParams(name, Name, 'AtomicMemOpMicro',
{'ea_code': ea_code,
'memacc_code': memacc_code,
'postacc_code': postacc_code,
'amoop_code': amoop_code},
rmw_inst_flags)
rmw_iop.constructor += '\n\tmemAccessFlags = memAccessFlags | ' + \
'|'.join(['Request::%s' % flag for flag in rmw_mem_flags]) + ';'
decoder_output += AtomicMemOpRMWConstructor.subst(rmw_iop)
decode_block += BasicDecode.subst(rmw_iop)
exec_output += AtomicMemOpRMWExecute.subst(rmw_iop) \
+ AtomicMemOpRMWInitiateAcc.subst(rmw_iop) \
+ AtomicMemOpRMWCompleteAcc.subst(rmw_iop)
}};