// ************************************************************************** //
//                                                                            //
//    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
// ----------------------------------------------------------------------------
macro DataWidth     =  8;                         // bit-width of registers
macro One           = {true::DataWidth};          // bitvector consisting of 1s
macro Zero          = {false::DataWidth};         // bitvector consisting of 0s

// --------------------------------------------------------------------------
// operations of the ALU: there are always two bitvector inputs of length 
// DataWidth and the result is always of size 2*DataWidth 
// --------------------------------------------------------------------------
macro ALUADDS(x1,x2) = int2bv(bv2int(x1) + bv2int(x2),2*DataWidth);
macro ALUSUBS(x1,x2) = int2bv(bv2int(x1) - bv2int(x2),2*DataWidth);
macro ALUMULS(x1,x2) = int2bv(bv2int(x1) * bv2int(x2),2*DataWidth);
macro ALUDIVS(x1,x2) = int2bv(bv2int(x1) % bv2int(x2),  DataWidth)
                     @ int2bv(bv2int(x1) / bv2int(x2),  DataWidth);
macro ALUADDU(x1,x2) = nat2bv(bv2nat(x1) + bv2nat(x2),2*DataWidth);
macro ALUSUBU(x1,x2) = nat2bv(bv2nat(x1) - bv2nat(x2),2*DataWidth);
macro ALUMULU(x1,x2) = nat2bv(bv2nat(x1) * bv2nat(x2),2*DataWidth);
macro ALUDIVU(x1,x2) = nat2bv(bv2nat(x1) % bv2nat(x2),  DataWidth)
                     @ nat2bv(bv2nat(x1) / bv2nat(x2),  DataWidth);
macro ALUSLTS(x1,x2) = Zero@(bv2int(x1) <  bv2int(x2)?One:Zero);
macro ALUSLTU(x1,x2) = Zero@(bv2nat(x1) <  bv2nat(x2)?One:Zero);
macro ALUSLES(x1,x2) = Zero@(bv2int(x1) <= bv2int(x2)?One:Zero);
macro ALUSLEU(x1,x2) = Zero@(bv2nat(x1) <= bv2nat(x2)?One:Zero);
macro ALUSEQ(x1,x2)  = Zero@(x1 == x2?One:Zero);
macro ALUSNE(x1,x2)  = Zero@(x1 != x2?One:Zero);
macro ALUAND(x1,x2)  = Zero@(x1  & x2);
macro ALUOR(x1,x2)   = Zero@(x1  | x2);
macro ALUNAND(x1,x2) = Zero@(!(x1 & x2));
macro ALUNOR(x1,x2)  = Zero@(!(x1 | x2));




// --------------------------------------------------------------------------
// execute stage of the structural implementation
// --------------------------------------------------------------------------

module Execute(
    bv{6} ?opc,                     // opcode of instr
    bv{7} ?fnc,                     // constant operand of S-type instructions
    bv{DataWidth} ?opL,?opR,        // ALU operands
    bv{2*DataWidth} AluRes,         // ALU result
    bool CondRes                    // result of branch condition
) {
        case
            // ----------------------------------------------------------------
            // arithmetic instructions
            // ----------------------------------------------------------------
            (opc{5:4}==0b00) do {
                case
                    (opc{3:2}==0b00 &  opc{0}) do AluRes = ALUADDS(opL,opR);
                    (opc{3:2}==0b00 & !opc{0}) do AluRes = ALUADDU(opL,opR);
                    (opc{3:2}==0b01 &  opc{0}) do AluRes = ALUSUBS(opL,opR);
                    (opc{3:2}==0b01 & !opc{0}) do AluRes = ALUSUBU(opL,opR);
                    (opc{3:2}==0b10 &  opc{0}) do AluRes = ALUMULS(opL,opR);
                    (opc{3:2}==0b10 & !opc{0}) do AluRes = ALUMULU(opL,opR);
                    (opc{3:2}==0b11 &  opc{0}) do AluRes = ALUDIVS(opL,opR);
                    (opc{3:2}==0b11 & !opc{0}) do AluRes = ALUDIVU(opL,opR);
                default
                    nothing;
                }
            // ----------------------------------------------------------------
            // comparison instructions
            // ----------------------------------------------------------------
            (opc==SLT)  do AluRes = ALUSLTS(opL,opR);
            (opc==SLTU) do AluRes = ALUSLTU(opL,opR);
            (opc==SLE)  do AluRes = ALUSLES(opL,opR);
            (opc==SLEU) do AluRes = ALUSLEU(opL,opR);
            (opc==SEQ)  do AluRes = ALUSEQ(opL,opR);
            (opc==SNE)  do AluRes = ALUSNE(opL,opR);
            // ----------------------------------------------------------------
            // logic instructions
            // ----------------------------------------------------------------
            (opc==AND)  do AluRes = ALUAND(opL,opR);
            (opc==OR)   do AluRes = ALUOR(opL,opR);
            (opc==NAND) do AluRes = ALUNAND(opL,opR);
            (opc==NOR)  do AluRes = ALUNOR(opL,opR);
            // ----------------------------------------------------------------
            // load and store instructions
            // ----------------------------------------------------------------
            (opc==LD | opc==LL | opc==ST | opc==SC | opc==SYNC & fnc==fn_SYNC) do
                AluRes = ALUADDU(opL,opR);
            // ----------------------------------------------------------------
            // moving constants to registers
            // ----------------------------------------------------------------
            (opc==MOV)  do AluRes = ALUADDS(opL,opR);
            (opc==MOVU) do AluRes = ALUADDU(opL,opR);
            // ----------------------------------------------------------------
            // branch and jump instructions
            // ----------------------------------------------------------------
            (opc==BEZ | opc==BNZ | opc==JMP | opc==J) do {
                AluRes = ALUADDS(opL,opR);
                if(opc==BEZ) CondRes = opL==Zero;
                if(opc==BNZ) CondRes = opL!=Zero;
                }
            // ----------------------------------------------------------------
            // move content of overflow register to destination register rd
            // ----------------------------------------------------------------
            (opc==OVF & fnc==fn_OVF) do AluRes = ALUADDS(opL,opR);
            // ----------------------------------------------------------------
        default nothing;
}