// ************************************************************************** //
//                                                                            //
//    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;



// --------------------------------------------------------------------------
//                   macros for implementing the behavior
// --------------------------------------------------------------------------

macro OpCode(i)  = i{15:10}; // opcode of instruction i
macro DestReg(i) = i{9:7};   // register index of destination
macro SrcLReg(i) = i{6:4};   // register index of left operand 
macro SrcRReg(i) = i{3:1};   // register index of right operand 
macro VctFlag(i) = i{0};     // register index of right operand 
macro ConstOp(i) = i{3:0};   // constant operand of instruction i
macro JumpAdr(i) = i{9:0};   // address of jump instruction
macro FctCode(i) = i{6:0};   // function code of instruction i

macro DataWidth     =  8;                         // bit-width of registers
macro One           = {true::DataWidth};          // bitvector consisting of 1s
macro Zero          = {false::DataWidth};         // bitvector consisting of 0s
macro UpperWord(r)  = r{2*DataWidth-1:DataWidth}; // upper half of double word
macro LowerWord(r)  = r{DataWidth-1:0};           // lower half of double word


// sign and zero extensions
macro zext4(b) = {false::DataWidth-sizeOf(b)}@b;
macro zext7(b) = {false::DataWidth-sizeOf(b)}@b;
macro sext4(b) = {b{-1}::DataWidth-sizeOf(b)}@b;
macro sext7(b) = {b{-1}::DataWidth-sizeOf(b)}@b;



// --------------------------------------------------------------------------
// Decode stage of the pipeline
// --------------------------------------------------------------------------

module Decode(
    bv{16} ?instr,                  // instruction to be performed now
    [8]bv{DataWidth} ?Reg,          // scalar registers
    bv{DataWidth} ?overflw,         // overflw register (completing result)
    bv{6} opc_ID,opc_EX,            // opcodes of instr
    bv{7} fnc_EX,                   // constant operand of S-type instructions
    bv{10} !adr_EX,                 // jump address of J-type instruction
    nat{8} rd_EX,                   // register destination index
    bv{DataWidth} !opS_EX,          // value to be stored in case of store op.
    bv{DataWidth} !opL_EX,!opR_EX   // ALU operands
) {
    // local variables
    nat{8} rd,rs1,rs2;      // register indices
    bv{4} cst;              // constant operand of I-type instructions
    bv{7} fnc;              // constant operand of S-type instructions

    // --------------------------------------------------------------------
    // extract parts of instruction word
    // --------------------------------------------------------------------
    opc_ID = OpCode(instr);
    rd  = bv2nat(DestReg(instr));
    rs1 = bv2nat(SrcLReg(instr));
    rs2 = bv2nat(SrcRReg(instr));
    cst = ConstOp(instr);
    fnc = FctCode(instr);

    next(opc_EX) = opc_ID;
    next(rd_EX)  = rd;
    next(fnc_EX) = fnc;
    next(adr_EX) = JumpAdr(instr);
    
    // --------------------------------------------------------------------
    // determine the first operand
    // --------------------------------------------------------------------
    case
        (opc_ID==MOV | opc_ID==MOVU | opc_ID==J | opc_ID==OVF) do next(opL_EX) = Reg[0];
        (opc_ID==JMP) do next(opL_EX) = Reg[rd];
    default
        next(opL_EX) = Reg[rs1];

    // --------------------------------------------------------------------
    // determine the second operand
    // --------------------------------------------------------------------
    case
        // signed arithmetic instruction with immediate operand 
        (opc_ID{5:4}==0b00 & opc_ID{1} & !opc_ID{0})     do next(opR_EX) = sext4(cst);
        // unsigned arithmetic instruction with immediate operand 
        (opc_ID{5:4}==0b00 & opc_ID{1} &  opc_ID{0})     do next(opR_EX) = zext4(cst);
        (opc_ID==LD | opc_ID==LL | opc_ID==ST | opc_ID==SC) do next(opR_EX) = zext4(cst);
        (opc_ID==BEZ | opc_ID==BNZ)                   do next(opR_EX) = sext4(cst);
        (opc_ID==MOV | opc_ID==JMP)                   do next(opR_EX) = sext7(fnc);
        (opc_ID==MOVU)                             do next(opR_EX) = zext7(fnc);
        (opc_ID==OVF)                              do next(opR_EX) = overflw;
        (opc_ID==J)                                do next(opR_EX) = Reg[0];
    default
        next(opR_EX) = Reg[rs2];

    // --------------------------------------------------------------------
    // determine the third operand of store instructions
    // --------------------------------------------------------------------
    next(opS_EX) = Reg[rd];

}