// -*- mode:c++ -*-

// Copyright (c) 2009 The University of Edinburgh
// 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: Timothy M. Jones

////////////////////////////////////////////////////////////////////
//
// Memory-format instructions
//

def template LoadStoreDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      public:

        /// Constructor.
        %(class_name)s(ExtMachInst machInst);

        %(BasicExecDeclare)s

        %(InitiateAccDeclare)s

        %(CompleteAccDeclare)s
    };
}};


def template InitiateAccDeclare {{
    Fault initiateAcc(%(CPU_exec_context)s *, Trace::InstRecord *) const;
}};


def template CompleteAccDeclare {{
    Fault completeAcc(PacketPtr,  %(CPU_exec_context)s *, Trace::InstRecord *) const;
}};


def template LoadStoreConstructor {{
    inline %(class_name)s::%(class_name)s(ExtMachInst machInst)
         : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
    {
        %(constructor)s;
    }
}};


def template LoadExecute {{
    Fault %(class_name)s::execute(%(CPU_exec_context)s *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 LoadInitiateAcc {{
    Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s *xc,
                                      Trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(op_src_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (fault == NoFault) {
            fault = readMemTiming(xc, traceData, EA, Mem, memAccessFlags);
            xc->setEA(EA);
        }

        return fault;
    }
}};


def template LoadCompleteAcc {{
    Fault %(class_name)s::completeAcc(PacketPtr pkt,
                                      %(CPU_exec_context)s *xc,
                                      Trace::InstRecord *traceData) const
    {
        Addr M5_VAR_USED EA;
        Fault fault = NoFault;

        %(op_decl)s;
        %(op_rd)s;

        EA = xc->getEA();

        getMem(pkt, Mem, traceData);

        if (fault == NoFault) {
            %(memacc_code)s;
        }

        if (fault == NoFault) {
          %(op_wb)s;
        }

        return fault;
    }
}};


def template StoreExecute {{
    Fault %(class_name)s::execute(%(CPU_exec_context)s *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 = writeMemAtomic(xc, traceData, Mem, EA, memAccessFlags,
                    NULL);
        }

        if (fault == NoFault) {
            %(op_wb)s;
        }

        return fault;
    }
}};


def template StoreInitiateAcc {{
    Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s *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,
                    NULL);
        }

        // Need to write back any potential address register update
        if (fault == NoFault) {
            %(op_wb)s;
        }

        return fault;
    }
}};


def template StoreCompleteAcc {{
    Fault %(class_name)s::completeAcc(PacketPtr pkt,
                                      %(CPU_exec_context)s *xc,
                                      Trace::InstRecord *traceData) const
    {
        return NoFault;
    }
}};


// The generic memory operation generator. This is called when two versions
// of an instruction are needed - when Ra == 0 and otherwise. This is so
// that instructions can use the value 0 when Ra == 0 but avoid having a
// dependence on Ra.
let {{

def GenMemOp(name, Name, memacc_code, ea_code, ea_code_ra0, base,
             load_or_store, mem_flags = [], inst_flags = []):

    # First the version where Ra is non-zero
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags,
                      base_class = base,
                      decode_template = CheckRaDecode,
                      exec_template_base = load_or_store)

    # Now another version where Ra == 0
    (header_output_ra0, decoder_output_ra0, _, exec_output_ra0) = \
        LoadStoreBase(name, Name + 'RaZero', ea_code_ra0, memacc_code,
                      mem_flags, inst_flags,
                      base_class = base,
                      exec_template_base = load_or_store)

    # Finally, add to the other outputs
    header_output += header_output_ra0
    decoder_output += decoder_output_ra0
    exec_output += exec_output_ra0
    return (header_output, decoder_output, decode_block, exec_output)

}};


def format LoadIndexOp(memacc_code, ea_code = {{ EA = Ra + Rb; }},
                       ea_code_ra0 = {{ EA = Rb; }},
                       mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        GenMemOp(name, Name, memacc_code, ea_code, ea_code_ra0,
                 'MemOp', 'Load', mem_flags, inst_flags)
}};


def format StoreIndexOp(memacc_code, ea_code = {{ EA = Ra + Rb; }},
                        ea_code_ra0 = {{ EA = Rb; }},
                        mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        GenMemOp(name, Name, memacc_code, ea_code, ea_code_ra0,
                 'MemOp', 'Store', mem_flags, inst_flags)
}};


def format LoadIndexUpdateOp(memacc_code, ea_code = {{ EA = Ra + Rb; }},
                             mem_flags = [], inst_flags = []) {{

    # Add in the update code
    memacc_code += 'Ra = EA;'

    # Generate the class
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags,
                      base_class = 'MemOp',
                      exec_template_base = 'Load')
}};


def format StoreIndexUpdateOp(memacc_code, ea_code = {{ EA = Ra + Rb; }},
                              mem_flags = [], inst_flags = []) {{

    # Add in the update code
    memacc_code += 'Ra = EA;'

    # Generate the class
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags,
                      base_class = 'MemOp',
                      exec_template_base = 'Store')
}};


def format LoadDispOp(memacc_code, ea_code = {{ EA = Ra + disp; }},
                      ea_code_ra0 = {{ EA = disp; }},
                      mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        GenMemOp(name, Name, memacc_code, ea_code, ea_code_ra0,
                 'MemDispOp', 'Load', mem_flags, inst_flags)
}};


def format StoreDispOp(memacc_code, ea_code = {{ EA = Ra + disp; }},
                       ea_code_ra0 = {{ EA = disp; }},
                       mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        GenMemOp(name, Name, memacc_code, ea_code, ea_code_ra0,
                 'MemDispOp', 'Store', mem_flags, inst_flags)
}};


def format LoadDispUpdateOp(memacc_code, ea_code = {{ EA = Ra + disp; }},
                            mem_flags = [], inst_flags = []) {{

    # Add in the update code
    memacc_code += 'Ra = EA;'

    # Generate the class
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags,
                      base_class = 'MemDispOp',
                      exec_template_base = 'Load')
}};


def format StoreDispUpdateOp(memacc_code, ea_code = {{ EA = Ra + disp; }},
                             mem_flags = [], inst_flags = []) {{

    # Add in the update code
    memacc_code += 'Ra = EA;'

    # Generate the class
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags,
                      base_class = 'MemDispOp',
                      exec_template_base = 'Store')
}};