// Copyright (c) 2007 The Regents of The University of Michigan
// 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: Gabe Black
//          Ali Saidi

// This template provides the execute functions for a swap
def template SwapExecute {{
        Fault %(class_name)s::execute(%(CPU_exec_context)s *xc,
                Trace::InstRecord *traceData) const
        {
            Fault fault = NoFault;
            // This is to support the conditional store in cas instructions.
            // It should be optomized out in all the others
            bool storeCond = true;
            Addr EA;
            %(fp_enable_check)s;
            %(op_decl)s;
            uint64_t mem_data = 0;

            %(op_rd)s;
            %(ea_code)s;
            DPRINTF(Sparc, "%s: The address is 0x%x\n", mnemonic, EA);
            %(fault_check)s;
            if (fault == NoFault) {
                %(code)s;
            }
            if (storeCond && fault == NoFault) {
                %(EA_trunc)s
                fault = writeMemAtomic(xc, traceData, Mem, EA,
                        %(asi_val)s, &mem_data);
            }
            if (fault == NoFault) {
                // Handle the swapping
                %(postacc_code)s;
            }
            if (fault == NoFault) {
                // Write the resulting state to the execution context
                %(op_wb)s;
            }

            return fault;
        }
}};


def template SwapInitiateAcc {{
        Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s * xc,
                Trace::InstRecord * traceData) const
        {
            Fault fault = NoFault;
            Addr EA;
            %(fp_enable_check)s;
            uint64_t mem_data = 0;
            %(op_decl)s;
            %(op_rd)s;
            %(ea_code)s;

            DPRINTF(Sparc, "%s: The address is 0x%x\n", mnemonic, EA);
            %(fault_check)s;

            if (fault == NoFault) {
                %(code)s;
            }
            if (fault == NoFault) {
                %(EA_trunc)s
                fault = writeMemTiming(xc, traceData, Mem, EA, %(asi_val)s,
                        &mem_data);
            }
            return fault;
        }
}};



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

            getMem(pkt, Mem, traceData);
            uint64_t mem_data = Mem;

            if (fault == NoFault) {
                // Handle the swapping
                %(postacc_code)s;
            }
            if (fault == NoFault) {
                // Write the resulting state to the execution context
                %(op_wb)s;
            }

            return fault;
        }
}};

let {{
    SwapFuncs = [SwapExecute, SwapInitiateAcc, SwapCompleteAcc]
}};


def format Swap(code, postacc_code, mem_flags, *opt_flags) {{
    mem_flags = makeList(mem_flags)
    mem_flags = [ 'Request::%s' % flag for flag in mem_flags ]
    flags = string.join(mem_flags, '|')

    (header_output,
     decoder_output,
     exec_output,
     decode_block) = doMemFormat(code, SwapFuncs, '', name, Name, flags,
         ["IsStoreConditional"], postacc_code)
}};

def format SwapAlt(code, postacc_code, mem_flags, *opt_flags) {{
    mem_flags = makeList(mem_flags)
    mem_flags = [ 'Request::%s' % flag for flag in mem_flags ]
    mem_flags.append("EXT_ASI")
    flags = string.join(mem_flags, '|')
    (header_output,
     decoder_output,
     exec_output,
     decode_block) = doMemFormat(code, SwapFuncs, AlternateASIPrivFaultCheck,
         name, Name, flags, ["IsStoreConditional"], postacc_code)
}};


let {{
    def doCasFormat(code, execute, faultCode, name, Name, mem_flags, opt_flags, postacc_code = ''):
        addrCalcReg = 'EA = Rs1;'
        iop = InstObjParams(name, Name, 'Mem',
                {"code": code, "postacc_code" : postacc_code,
                 "fault_check": faultCode, "ea_code": addrCalcReg,
                 "EA_trunc" : TruncateEA}, opt_flags)
        header_output = MemDeclare.subst(iop)
        decoder_output = BasicConstructor.subst(iop)
        decode_block = BasicDecode.subst(iop)
        microParams = {"code": code, "postacc_code" : postacc_code,
            "ea_code" : addrCalcReg, "fault_check" : faultCode,
            "EA_trunc" : TruncateEA}
        exec_output = doSplitExecute(execute, name, Name, mem_flags,
                ["IsStoreConditional"], microParams);
        return (header_output, decoder_output, exec_output + EACompExecute.subst(iop), decode_block)
}};


def format CasAlt(code, postacc_code, mem_flags, *opt_flags) {{
    mem_flags = makeList(mem_flags)
    mem_flags = [ 'Request::%s' % flag for flag in mem_flags ]
    mem_flags.append("EXT_ASI")
    flags = string.join(mem_flags, '|')
    (header_output,
     decoder_output,
     exec_output,
     decode_block) = doCasFormat(code, SwapFuncs, AlternateASIPrivFaultCheck,
         name, Name, flags, ["IsStoreConditional"], postacc_code)
}};


