//  *********************************************************************************************
//  Description      : Verilator tb
//  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
//                     -----------  ---------  -------------------------------------------------
//  *********************************************************************************************



using namespace std;

#include  <stdlib.h>
#include  <iostream>
#include  <verilated.h>
#include  <verilated_fst_c.h>
#include  "Vdecoder_harness.h"
#include  "Vdecoder_harness__Dpi.h"

// only required for accessing model internal memory ressources via the rootp pointer.
// Bad style, better use systemverilog harness with tasks and functions to do that.
// #include  "Vdecoder_harness___024root.h"
// #include "svdpi.h"


#define DECODER_CLK_PERIOD 40

//--------------------------------------------------------------
/// @brief Simulation environment class for the decoder model.
/// Contains a set of high level methods for controlling the model from test environment.
class decoder
{
  private:

        Vdecoder_harness *dut;
        VerilatedFstC *mTrace;
        vluint64_t    T;

        uint32_t      decoderClkState;
        bool          running;

  public:

    //--------------------------------------------------------------
    /// @brief Constructor
    decoder (int argc, char** argv)
    {
        // setup verilator stuff
        T = 0;
        dut = new Vdecoder_harness();
        Verilated::commandArgs(argc, argv);
        Verilated::traceEverOn(true);
        mTrace = new VerilatedFstC;
        dut->trace(mTrace, 5);
        mTrace->open("wavedump.fst");

        cout << "Registered DPI-C functions:\n";
        Verilated::scopesDump();
        const svScope scope = svGetScopeFromName("TOP.decoder_harness");
        assert(scope);  // Check for nullptr if scope not found
        svSetScope(scope);

        running = false;

        // initialize all input signals
        initSignals();

    } // swirl()


    //--------------------------------------------------------------
    /// @brief simulation tick, advances the simulation time and calls the eval() function of the model on every active clock edge
    void tick ()
    {
        int do_eval;

        if (T%DECODER_CLK_PERIOD==0) {
            dut->setClk(decoderClkState);
            decoderClkState = 1-decoderClkState;
            do_eval = 1;
        }

        if (do_eval) {
            dut->eval();
            mTrace->dump(T);
            do_eval = 0;
        }

        T++;

    } // tick()


    //--------------------------------------------------------------
    /// @brief Initialize all input signals of the hardware model to a defined value
    void initSignals()
    {
      dut->setInitial();
    } // initSignals()

    //--------------------------------------------------------------
    /// @brief Wait for a number of active edges of clk signal
    /// @param numEdges number of rising edges
    /// @param active active 1=rising 0=falling edge
    void waitClocks (int numEdges=1, int activeEdge=1)
    {
        int clk_d;

        for (int j=0; j<numEdges; ++j) {
          while (1) {
            clk_d = decoderClkState;
            tick();
            if (clk_d==1-activeEdge && decoderClkState==activeEdge) break;
          }
        } // for
    } // waitClocks()

    //--------------------------------------------------------------
    /// @brief load the decoder memory from vmem formatted file
    void loadRAM(char *vmemFile)
    {
      dut->loadRAM(vmemFile);
    } // loadRAM()

    //--------------------------------------------------------------
    /// @brief start the DUT
    void start()
    {
      running = true;
      dut->enable(1);
    } // start()


    //--------------------------------------------------------------
    /// @brief close the trace file and delete the Verilator object
    void stop()
    {
      dut->enable(0);
      mTrace->close();
      delete dut;
    } // stop()

    //--------------------------------------------------------------
    /// @brief print signal info
    void printInfo()
    {
      dut->printInfo();
    }
}; // class decoder





int main (int argc, char** argv)
{
    uint32_t nCycles;
    int r;

    // Get number of simulation cycles
    nCycles = atoi(argv[1]);

    cout << "\nCreating model...\n";
    decoder *m = new decoder(argc, argv);
    m->loadRAM(argv[2]);

    cout << "Starting DECODER simulation...\n" << std::flush;
		m->start();

    cout << "Running for " << nCycles << " clock cycles...\n" << std::flush;
    m->waitClocks(nCycles);

    m->printInfo();

    cout << "\nDone, closing simulation.\n\n\n" << std::flush;
    m->stop();
    delete m;

    exit(EXIT_SUCCESS);
}
