// ************************************************************************** //
//                                                                            //
//    eses                   eses                                             //
//   eses                     eses                                            //
//  eses    eseses  esesese    eses   Embedded Systems Group                  //
//  ese    ese  ese ese         ese                                           //
//  ese    eseseses eseseses    ese   Department of Computer Science          //
//  eses   eses          ese   eses                                           //
//   eses   eseses  eseseses  eses    University of Kaiserslautern            //
//    eses                   eses                                             //
//                                                                            //
// ************************************************************************** //

// ----------------------------------------------------------------------------
// opcodes of the instructions
// ----------------------------------------------------------------------------

macro ADD   = 0b000000;
macro ADDU  = 0b000001;
macro ADDI  = 0b000010;
macro ADDIU = 0b000011;
macro SUB   = 0b000100;
macro SUBU  = 0b000101;
macro SUBI  = 0b000110;
macro SUBIU = 0b000111;
macro MUL   = 0b001000;
macro MULU  = 0b001001;
macro MULI  = 0b001010;
macro MULIU = 0b001011;
macro DIV   = 0b001100;
macro DIVU  = 0b001101;
macro DIVI  = 0b001110;
macro DIVIU = 0b001111;

macro SLT   = 0b010000;
macro SLTU  = 0b010001;
macro SLE   = 0b010010;
macro SLEU  = 0b010011;
macro SEQ   = 0b010100;
macro SNE   = 0b010101;

macro AND   = 0b010110;
macro OR    = 0b010111;
macro NAND  = 0b011000;
macro NOR   = 0b011001;

macro LD    = 0b011010;
macro ST    = 0b011011;
macro LVWS  = 0b011100;
macro SVWS  = 0b011101;
macro LL    = 0b011110;
macro SC    = 0b011111;
macro MOV   = 0b100000;
macro MOVU  = 0b100001;

macro BEZ   = 0b100010;
macro BNZ   = 0b100011;
macro JMP   = 0b100100;
macro J     = 0b100101;

macro SYNC  = 0b100111; // note that this group of instructions share the same
macro OVF   = 0b100111; // opcode, and differ in the additional function code
macro MVTM  = 0b100111; // listed below
macro MVFM  = 0b100111;
macro MVTL  = 0b100111;
macro MVFL  = 0b100111;

macro fn_SYNC  = 0b0000000;
macro fn_OVF   = 0b0000001;
macro fn_MVTM  = 0b0000010;
macro fn_MVFM  = 0b0000011;
macro fn_MVTL  = 0b0000100;
macro fn_MVFL  = 0b0000101;


// --------------------------------------------------------------------------
// special macros for this stage
// --------------------------------------------------------------------------

macro DataWidth =  8; // bit-width of registers



// --------------------------------------------------------------------------
// memory access stage
// --------------------------------------------------------------------------

module MemAccess(
    nat pc,                         // program counter
    bv{6}  ?opc_MA,                 // opcode of instr
    bv{7}  ?fnc_MA,                 // constant operand of S-type instructions
    bv{10} ?adr_MA,                 // jump address of J-type instruction
    nat{8} ?rd_MA,                  // register destination index
    bv{DataWidth} ?opS_MA,          // value to be stored in case of store op.
    bv{2*DataWidth} ?alu_MA,        // ALU results (immediate and delayed)
    bool ?cnd_MA,                   // result of branch condition
    bv{6}  !opc_WB,                 // opcode of instr
    bv{7}  !fnc_WB,                 // function code
    bv{10} !adr_WB,                 // jump address of J-type instruction
    nat{8} !rd_WB,                  // register destination index
    bv{2*DataWidth} !alu_WB,        // ALU result (contains memory address)
    bool !cnd_WB,                   // result of branch condition
    bv{DataWidth} ld_MA,!ld_WB,     // load result
    event nat !adrBus,              // address for memory access
    event bv{DataWidth} dataBus,    // data for memory access
    event readMem,writeMem,         // whether data is read or written to memory
    event reqMem,ackMem,doneMem     // signals for memory transaction
) {
    // --------------------------------------------------------------------
    // memory access takes only place for load/store instructions:
    // --------------------------------------------------------------------
    if(opc_MA==LD | opc_MA==LL | 
       opc_MA==ST | opc_MA==SC | 
       opc_MA==SYNC & fnc_MA==fn_SYNC) {
            // apply for memory access by emitting reqMem until
            // ackMem holds; may take time on multiprocessors
            weak immediate abort {
                loop {
                    emit(reqMem);
                    if(!ackMem) next(pc) = pc;
                    waitMem1: pause;
                }
            } when(ackMem);
            // provide address and read/write request signal
            // until memory transaction is done
            weak immediate abort {
                loop {
                    adrBus = bv2nat(alu_MA);
                    case
                        (opc_MA==LD | opc_MA==LL) do
                            emit(readMem);
                        (opc_MA==ST | opc_MA==SC) do {
                            emit(writeMem);
                            dataBus = opS_MA;
                            }
                        (opc_MA==SYNC) do {
                            emit(readMem);
                            emit(writeMem);
                            }
                    default nothing;
                    if(!doneMem) next(pc) = pc;
                    waitMem2: pause;
                }
            } when(doneMem);
            // in case of load, transfer dataBus to LoadRes
            if(opc_MA==LD | opc_MA==LL)
                ld_MA = dataBus;
            }

    // --------------------------------------------------------------------
    // forwarding values to WriteBack stage
    // --------------------------------------------------------------------
    next(opc_WB) = opc_MA;
    next(fnc_WB) = fnc_MA;
    next(adr_WB) = adr_MA;
    next(rd_WB)  =  rd_MA;
    next(alu_WB) = alu_MA;
    next(cnd_WB) = cnd_MA;
    next(ld_WB)  =  ld_MA;

}