//  *********************************************************************************************
//  Description      : Verilator simulation harness
//  Project Version  : v1.0
//  Project          : [BCDC] Microtec Academy Course: Building a RISC-V CPU with SystemVerilog
//  -----
//  Copyright (c)    : 2025 Fraunhofer IIS, Department IDS
//  Created          : 15.Oct.2025  by Hussein Elzomor
//  Last Modified    : 15.Oct.2025  by Hussein Elzomor
//  -----
//  HISTORY          : Date         By         Comments
//                     -----------  ---------  -------------------------------------------------
//  *********************************************************************************************



timeunit 1ps;
timeprecision 1ps;

module decoder_harness;
  // local Parameters
    localparam MEM_SIZE = 37;
    localparam REG_FILE_SIZE = 32;

  // local signals
    logic       clk;
    int         counter;
    logic[31:0] mem [MEM_SIZE-1:0];
    logic[31:0] regFile [REG_FILE_SIZE-1:0];
    // PC
    logic[31:0] CurrentPC;
    logic[31:0] JumpOrBranchPC;
    logic       JumpOrBranch;
    logic[31:0] NextPC;
    // Memory
    logic[31:0] DAddr;
    logic[31:0] WData;
    logic[31:0] RData;
    logic[31:0] Instruction;
    logic       WrMem;
    logic[1:0]  DWidth;
    // Register File;
    logic[4:0]  Rs1;
    logic[4:0]  Rs2;
    logic[4:0]  Rd;
    logic[31:0] RRs1;
    logic[31:0] RRs2;
    logic[31:0] WRd;
    logic       WrReg;
    // Protection
    logic       Illegal;

  // toplevel instance (DUT)
  decoder u_decoder  (
      // PC
      .CurrentPC(CurrentPC),
      .JumpOrBranchPC(JumpOrBranchPC),
      .JumpOrBranch(JumpOrBranch),
      // Memory
      .DAddr(DAddr),
      .WData(WData),
      .RData(RData),
      .Instruction(Instruction),
      .WrMem(WrMem),
      .DWidth(DWidth),
      // Register File
      .Rs1(Rs1),
      .Rs2(Rs2),
      .Rd(Rd),
      .RRs1(RRs1),
      .RRs2(RRs2),
      .WRd(WRd),
      .WrReg(WrReg),
      // Protection
      .Illegal(Illegal)
  ); // u_decoder


  // Special Verilator specific tasks
  `ifdef VERILATOR

    // Declare all functions with DPI-C interface to make them accessible from the C++ testbench
      // Setters
      export "DPI-C" task setClk;
      export "DPI-C" task enable;
      export "DPI-C" task loadRAM;
      export "DPI-C" task setInitial;
      export "DPI-C" task printInfo;

    // Clocking is controlled from C++ testbench
    task setClk (input logic val);
      clk = val;
    endtask: setClk

  `else

    // If not using verilator, generate clock here
    const realtime   clk_PERIOD  = 1.0ns;

    initial begin: clock_driver
      clk = '1;
      forever begin
        #(clk_PERIOD/2.0);
        clk <= ~clk;
        end
    end: clock_driver

    task wait_clk_cycles(int unsigned n);
      repeat(n) @(posedge clk);
    endtask

  `endif

  // Load a new instruction every cycle
  always_ff @(posedge clk) begin: increment_instruction_and_print_info
      if (counter < MEM_SIZE) begin
          if (counter > 0) printInfo();
          Instruction = mem[counter++];
      end
  end

  // Return the value of the RegFile
  always_comb begin: reg_file_assignment
    RRs1 = regFile[Rs1];
    RRs2 = regFile[Rs2];
  end

  // Remaining tasks are identical for Verilator and other simulators
    task enable(int val);
      // nothing to do, we have no fetch enable in this design
    endtask: enable

    // Load data into memory
    task loadRAM (input string fileName);
      $display ("Initializing SRAM memory from %s", fileName);
      $readmemh(fileName, mem);
    endtask: loadRAM

    // Set initial values
    task setInitial();
      Instruction = mem[0];
      counter     = 0;
      CurrentPC   = 32'hdeadbeef;
      RData       = 32'hbeefdead;
      for(int i=0; i<REG_FILE_SIZE; i++) begin
        regFile[i] = i;
      end
    endtask: setInitial

    // Print decoder signals
    task printInfo();
      $displayh("(cycle %0d) Decoder (I/O)             \tDecoder (Internal Signals)                     \tALU"    , counter                                                 );
      $displayh("          --------------------------  \t---------------------------------------------  \t---------------------"  ,                                         );
      $displayh("          CurrentPC     : 0x%h    OpCode   : 0b%b                            aluOp    : 0b%b"    , CurrentPC     , u_decoder.theOp    , u_decoder.aluOp    );
      $displayh("          JumpOrBranchPC: 0x%h    theFunct3: 0b%b                                aluNegAr : 0b%b", JumpOrBranchPC, u_decoder.theFunct3, u_decoder.aluNegAr );
      $displayh("          JumpOrBranch  : 0b%b      \ttheFunct7: 0b%b                            aluBypass: 0b%b", JumpOrBranch  , u_decoder.theFunct7, u_decoder.aluBypass);
      $displayh("          DAddr         : 0x%h    i_imm    : 0b%b   op1      : 0x%h"                             , DAddr         , u_decoder.i_imm    , u_decoder.op1      );
      $displayh("          WData         : 0x%h    s_imm    : 0b%b   op2      : 0x%h"                             , WData         , u_decoder.s_imm    , u_decoder.op2      );
      $displayh("          RData         : 0x%h    b_imm    : 0b%b   result   : 0x%h"                             , RData         , u_decoder.b_imm    , u_decoder.result   );
      $displayh("          Instruction   : 0x%h    u_imm    : 0b%b   eqFlag   : 0b%b"                             , Instruction   , u_decoder.u_imm    , u_decoder.eqFlag   );
      $displayh("          WrMem         : 0b%b      \tj_imm    : 0b%b"                                           , WrMem         , u_decoder.j_imm                         );
      $displayh("          DWidth        : 0b%b"                                                                  , DWidth                                                  );
      $displayh("          Rs1           : %0d "                                                                  , Rs1                                                     );
      $displayh("          Rs2           : %0d "                                                                  , Rs2                                                     );
      $displayh("          Rd            : 0x%h"                                                                  , Rd                                                      );
      $displayh("          RRs1          : %0d "                                                                  , RRs1                                                    );
      $displayh("          RRs2          : %0d "                                                                  , RRs2                                                    );
      $displayh("          WRd           : 0x%h"                                                                  , WRd                                                     );
      $displayh("          WrReg         : 0b%b"                                                                  , WrReg                                                   );
      $displayh("          Illegal       : 0b%b"                                                                  , Illegal                                                 );
      $displayh("");
    endtask: printInfo

endmodule: decoder_harness
