RISC_V_LAB hinzugefügt
This commit is contained in:
@@ -0,0 +1,328 @@
|
||||
// *********************************************************************************************
|
||||
// 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 : 12.Aug.2025 by Marcus Bednara
|
||||
// Last Modified : 15.Oct.2025 by Hussein Elzomor [commit d0452cd]
|
||||
// -----
|
||||
// HISTORY : Date By Comments
|
||||
// ----------- --------- -------------------------------------------------
|
||||
// 15.Oct.2025 H.Elzomor Renamed modules from *soc* to *cpu*
|
||||
// 15.Oct.2025 H.Elzomor Added evalText function
|
||||
// *********************************************************************************************
|
||||
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <verilated.h>
|
||||
#include <verilated_fst_c.h>
|
||||
#include "Vcpu_harness.h"
|
||||
#include "Vcpu_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 "Vcpu_harness___024root.h"
|
||||
// #include "svdpi.h"
|
||||
|
||||
|
||||
#define SOC_CLK_PERIOD 40
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/// @brief Simulation environment class for the cpu model.
|
||||
/// Contains a set of high level methods for controlling the model from test environment.
|
||||
class cpu
|
||||
{
|
||||
private:
|
||||
|
||||
Vcpu_harness *dut;
|
||||
VerilatedFstC *mTrace;
|
||||
vluint64_t T;
|
||||
|
||||
uint32_t cpuClkState;
|
||||
|
||||
bool running;
|
||||
|
||||
string program;
|
||||
|
||||
public:
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/// @brief Constructor
|
||||
cpu (int argc, char** argv)
|
||||
{
|
||||
// setup verilator stuff
|
||||
T = 0;
|
||||
dut = new Vcpu_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.cpu_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%SOC_CLK_PERIOD==0) {
|
||||
dut->setClk(cpuClkState);
|
||||
cpuClkState = 1-cpuClkState;
|
||||
do_eval = 1;
|
||||
}
|
||||
|
||||
|
||||
// Other clock domains must be generated the same way
|
||||
|
||||
// if (T%OTHER_CLK_PERIOD==0) {
|
||||
// dut->setOtherClk(otherClkState);
|
||||
// otherClkState = 1-otherClkState;
|
||||
// do_eval = 1;
|
||||
// }
|
||||
|
||||
if (do_eval) {
|
||||
dut->eval();
|
||||
// evaluate the LED status in each clock cycle if the CPU is running (i.e., after reset)
|
||||
if (running && program == "unknown") evalLed();
|
||||
// evaluate the Fibonacci Series location in stack (starting at 2027) and print value if it has changed
|
||||
if (running && program == "fibonacci") evalFibonacci();
|
||||
// evaluate the 'hello world!' location in the DataMem in each clock cycle if the CPU is running (i.e., after reset)
|
||||
if (running && program == "helloWorld") evalHelloWorld();
|
||||
// evaluate the Prime Factors location in RegFile (x17) and print value if it has changed
|
||||
if (running && program == "primeFactors") evalPrimeFactors();
|
||||
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 = cpuClkState;
|
||||
tick();
|
||||
if (clk_d==1-activeEdge && cpuClkState==activeEdge) break;
|
||||
}
|
||||
} // for
|
||||
} // waitClocks()
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/// @brief Toggle the reset signal active (low)
|
||||
/// @param numEdges number of clock cyles keeping the reset active
|
||||
void reset (int numEdges=1)
|
||||
{
|
||||
dut->setReset(0); // reset is active low
|
||||
waitClocks (numEdges);
|
||||
dut->setReset(1);
|
||||
} // reset();
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/// @brief load the cpu memory from vmem formatted file
|
||||
void loadRAM (char* vmemFile)
|
||||
{
|
||||
dut->loadRAM(vmemFile);
|
||||
} // loadRAM()
|
||||
|
||||
/// @brief check the LED status and print value if it has change
|
||||
void evalLed ()
|
||||
{
|
||||
static uint8_t oldLedStatus=0xff;
|
||||
uint8_t currentLedStatus;
|
||||
currentLedStatus=dut->getLed();
|
||||
if (currentLedStatus!=oldLedStatus) {
|
||||
cout << "T=" << T << ": LED=" << std::bitset<8>(currentLedStatus) << endl;
|
||||
oldLedStatus = currentLedStatus;
|
||||
}
|
||||
} // evalLed()
|
||||
|
||||
/// @brief check Fibonacci Series location in stack (starting at 2027) and print value if it has changed
|
||||
void evalFibonacci()
|
||||
{
|
||||
int memStartLoc = 2027;
|
||||
const int memLengthInBytes = 40;
|
||||
const int memLengthInWords = ceil(memLengthInBytes / 4.0);
|
||||
static uint32_t oldValue[memLengthInWords];
|
||||
uint32_t vlaue[memLengthInWords];
|
||||
bool update = 0;
|
||||
for (int i = 0; i < memLengthInWords; i++)
|
||||
{
|
||||
vlaue[i] = dut->getMem(memStartLoc + i);
|
||||
if (oldValue[i] != vlaue[i])
|
||||
update = 1;
|
||||
}
|
||||
if (update)
|
||||
{
|
||||
cout << endl
|
||||
<< "T=" << T << ": \t Hex \t Dec" << endl;
|
||||
for (int i = 0; i < memLengthInWords; i++)
|
||||
{
|
||||
dut->printMem(memStartLoc + i, "fibonacci");
|
||||
oldValue[i] = vlaue[i];
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
} // evalFibonacci()
|
||||
|
||||
/// @brief check 'hello world' location in DataMem (starting at 1024) and print value if it has changed
|
||||
void evalHelloWorld()
|
||||
{
|
||||
int memStartLoc = 1024;
|
||||
const int memLengthInBytes = 13;
|
||||
const int memLengthInWords = ceil(memLengthInBytes / 4.0);
|
||||
static uint32_t oldValue[memLengthInWords];
|
||||
uint32_t vlaue[memLengthInWords];
|
||||
bool update = 0;
|
||||
for (int i = 0; i < memLengthInWords; i++)
|
||||
{
|
||||
vlaue[i] = dut->getMem(memStartLoc + i);
|
||||
if (oldValue[i] != vlaue[i])
|
||||
update = 1;
|
||||
}
|
||||
if (update)
|
||||
{
|
||||
cout << endl
|
||||
<< "T=" << T << ": \t Hex \t ASCII" << endl;
|
||||
for (int i = 0; i < memLengthInWords; i++)
|
||||
{
|
||||
dut->printMem(memStartLoc + i, "helloWorld");
|
||||
oldValue[i] = vlaue[i];
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
} // evalHelloWorld()
|
||||
|
||||
/// @brief check Prime Factors location in RegFile (x17) and print value if it has changed
|
||||
void evalPrimeFactors()
|
||||
{
|
||||
// Number to be factorised
|
||||
int regLoc_Number = 16;
|
||||
static uint32_t oldValue_Number;
|
||||
uint32_t value_Number;
|
||||
value_Number = dut->getReg(regLoc_Number);
|
||||
if (oldValue_Number != value_Number & value_Number > 1) dut->printReg(regLoc_Number, "primeNumber");
|
||||
oldValue_Number = value_Number;
|
||||
|
||||
// Prime Factors
|
||||
int regLoc_PrimeFactor = 17;
|
||||
static uint32_t oldValue_PrimeFactor;
|
||||
uint32_t value_PrimeFactor;
|
||||
value_PrimeFactor = dut->getReg(regLoc_PrimeFactor);
|
||||
if (oldValue_PrimeFactor != value_PrimeFactor & value_PrimeFactor > 1) dut->printReg(regLoc_PrimeFactor, "primeFactors");
|
||||
oldValue_PrimeFactor = value_PrimeFactor;
|
||||
|
||||
} // evalPrimeFactors()
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/// @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 Set the program name
|
||||
void setProgram(string prog)
|
||||
{
|
||||
if (prog.find("fibonacci") != string::npos) program = "fibonacci";
|
||||
else if (prog.find("helloWorld") != string::npos) program = "helloWorld";
|
||||
else if (prog.find("primeFactors") != string::npos) program = "primeFactors";
|
||||
else program = "unknown";
|
||||
|
||||
cout << "program set to " << program << endl;
|
||||
} // setProgram()
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/// @brief Get the program name
|
||||
string getProgram()
|
||||
{
|
||||
return program;
|
||||
} // getProgram()
|
||||
|
||||
}; // class cpu
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
uint32_t nCycles;
|
||||
char* program_path = new char[strlen(argv[2])]();
|
||||
|
||||
// Get number of simulation cycles
|
||||
nCycles = atoi(argv[1]);
|
||||
|
||||
// Get the program
|
||||
program_path = argv[2];
|
||||
string program_path_str = program_path;
|
||||
|
||||
cout << "\nCreating model...\n";
|
||||
cpu *m = new cpu(argc, argv);
|
||||
m->loadRAM(program_path);
|
||||
m->setProgram(program_path_str);
|
||||
|
||||
cout << "\nStarting model...\n";
|
||||
|
||||
cout << "\nResetting...\n";
|
||||
m->reset(10);
|
||||
|
||||
cout << "Starting CPU...\n" << std::flush;
|
||||
m->start();
|
||||
|
||||
cout << "Running for " << nCycles << " clock cycles...\n\n" << std::flush;
|
||||
cout << "Printing evaluation for " << m->getProgram() << " program!\n" << std::flush;
|
||||
m->waitClocks(nCycles);
|
||||
|
||||
cout << "\nDone, closing simulation.\n\n\n" << std::flush;
|
||||
m->stop();
|
||||
delete m;
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// *********************************************************************************************
|
||||
// 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);
|
||||
}
|
||||
Reference in New Issue
Block a user