Initial commit
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
export PRJ_ROOT = ../../..
|
||||
|
||||
|
||||
# directroy containig syrinx application programs
|
||||
SWDIR = $(PRJ_ROOT)/sw/risc-v
|
||||
DVDIR = $(PRJ_ROOT)/hw/dv
|
||||
|
||||
# toplevel syrinx file list
|
||||
RTL_FLIST = $(PRJ_ROOT)/hw/file_lists/rtl_flist.f
|
||||
TB_FLIST = $(PRJ_ROOT)/hw/file_lists/tb_flist.f
|
||||
|
||||
|
||||
# if we have multiple file list, we combine them here
|
||||
FLIST = -f $(RTL_FLIST) -f $(TB_FLIST)
|
||||
|
||||
# Default number of simulation cycles
|
||||
NCYCLES ?= 500
|
||||
|
||||
# Standard boot loader and dummy application to be used if nothing else is specified
|
||||
|
||||
PROG ?= $(SWDIR)/hello_world/helloWorld.hex
|
||||
|
||||
# top module name
|
||||
TOPMODULE = cpu_harness
|
||||
|
||||
# NUmber of GCC parallel threads
|
||||
COMPILE_THREADS = 32
|
||||
|
||||
# Verilator CPU usage, should be adapated to simulation host
|
||||
SIM_THREADS = 4
|
||||
TRACE_THREADS = 1
|
||||
|
||||
|
||||
EXTRA_ARGS += --trace-fst --trace-structs --trace-max-array 2048 --trace-threads $(TRACE_THREADS) --threads $(SIM_THREADS)
|
||||
EXTRA_ARGS += --clk clk --no-timing
|
||||
# EXTRA_ARGS += -O2
|
||||
|
||||
# Used by some code constructs
|
||||
VDEFS = +define+SIMULATION
|
||||
|
||||
|
||||
|
||||
all: ./obj_dir/V$(TOPMODULE)
|
||||
|
||||
wave: wavedump.fst
|
||||
@echo
|
||||
@echo "*** Starting waveform viewer..."
|
||||
$(WAVE_VIEWER) wavedump.fst -s $(DVDIR)/wave_configs/verilator_wave.surf.ron
|
||||
|
||||
run:
|
||||
@echo
|
||||
@echo "*** Running simulation..."
|
||||
@./obj_dir/V$(TOPMODULE) $(NCYCLES) $(PROG)
|
||||
|
||||
./obj_dir/V$(TOPMODULE): .stamp.verilate
|
||||
@echo
|
||||
@echo "*** Building simulator..."
|
||||
make -C obj_dir -f V$(TOPMODULE).mk V$(TOPMODULE) -j $(COMPILE_THREADS)
|
||||
|
||||
.stamp.verilate: src/tb_$(TOPMODULE).cpp
|
||||
@echo "*** Generating C++ model..."
|
||||
$(VERILATOR) --trace $(EXTRA_ARGS) $(VDEFS) -cc $(FLIST) --top-module $(TOPMODULE) --exe src/tb_$(TOPMODULE).cpp
|
||||
@touch .stamp.verilate
|
||||
|
||||
lint:$(VERILOG_SOURCES)
|
||||
$(VERILATOR) --lint-only $(INCLUDES) $(VERILOG_SOURCES) --top-module $(TOPMODULE)
|
||||
|
||||
|
||||
paths:
|
||||
@echo $(PRJ_ROOT)/$(APB)
|
||||
|
||||
|
||||
|
||||
clean:
|
||||
rm -rf .stamp.*;
|
||||
rm -rf ./obj_dir
|
||||
rm -rf wavedump.fst*
|
||||
rm -rf *.dasm
|
||||
@@ -0,0 +1,148 @@
|
||||
// *********************************************************************************************
|
||||
// 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 : 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 file and module from soc_harness to cpu_harness
|
||||
// 15.Oct.2025 H.Elzomor Added getMem function, setMem task and printMem task
|
||||
// *********************************************************************************************
|
||||
|
||||
|
||||
|
||||
timeunit 1ps;
|
||||
timeprecision 1ps;
|
||||
|
||||
module cpu_harness;
|
||||
|
||||
// local signals
|
||||
logic clk,
|
||||
rst;
|
||||
|
||||
logic [7:0] led;
|
||||
|
||||
logic [6:0] btn;
|
||||
|
||||
|
||||
// toplevel instance (DUT)
|
||||
|
||||
cpu u_cpu (
|
||||
.led (led),
|
||||
.btn ({btn[6:1], rst}),
|
||||
.clk_25mhz (clk)
|
||||
); // u_cpu
|
||||
|
||||
|
||||
`ifdef VERILATOR
|
||||
|
||||
// Declare all functions with DPI-C interface to make them accessible from the C++ testbench
|
||||
|
||||
export "DPI-C" task setClk;
|
||||
export "DPI-C" task enable;
|
||||
export "DPI-C" task loadRAM;
|
||||
// export "DPI-C" task dumpSRAM;
|
||||
export "DPI-C" task setInitial;
|
||||
export "DPI-C" function getLed;
|
||||
export "DPI-C" task setBtn;
|
||||
export "DPI-C" task setReset;
|
||||
export "DPI-C" function getMem;
|
||||
export "DPI-C" task setMem;
|
||||
export "DPI-C" task printMem;
|
||||
export "DPI-C" function getReg;
|
||||
export "DPI-C" task printReg;
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
// Remaining tasks are identical for Verilator and other simulators
|
||||
|
||||
|
||||
|
||||
task loadRAM (input string fileName);
|
||||
$display ("Initializing SRAM memory from %s", fileName);
|
||||
$readmemh(fileName, u_cpu.theMem.RAM);
|
||||
endtask: loadRAM
|
||||
|
||||
|
||||
/* Some helper stuff */
|
||||
|
||||
task enable(int val);
|
||||
// nothing to do, we have no fetch enable in this design
|
||||
endtask: enable
|
||||
|
||||
task setInitial();
|
||||
btn = 7'b0;
|
||||
rst = 'b0;
|
||||
endtask: setInitial
|
||||
|
||||
function int getLed;
|
||||
getLed = {24'b0, led};
|
||||
endfunction: getLed
|
||||
|
||||
task setBtn(int val);
|
||||
btn[6:1] = val[5:0];
|
||||
endtask: setBtn
|
||||
|
||||
task setReset (input int val);
|
||||
rst = (val==1);
|
||||
endtask: setReset
|
||||
|
||||
function int getMem(int location);
|
||||
getMem = u_cpu.theMem.RAM[location];
|
||||
endfunction: getMem
|
||||
|
||||
task setMem(int location, int value);
|
||||
u_cpu.theMem.RAM[location] = value;
|
||||
endtask: setMem
|
||||
|
||||
task printMem (int location, string prog);
|
||||
case (prog)
|
||||
"fibonacci" :$display("Value at RAM[%0d]: 0x%h : %0d", location, getMem(location), getMem(location));
|
||||
"helloWorld" :$display("Value at RAM[%0d]: 0x%h : %s" , location, getMem(location), getMem(location));
|
||||
default :$display("Value at RAM[%0d]: 0x%h : %0d", location, getMem(location), getMem(location));
|
||||
endcase
|
||||
endtask: printMem
|
||||
|
||||
function int getReg(int location);
|
||||
getReg = u_cpu.theRegisters.registers[location];
|
||||
endfunction: getReg
|
||||
|
||||
task printReg (int location, string prog);
|
||||
case (prog)
|
||||
"primeNumber" :$display("Time =%5dns \t : Register [%0d] updated - Finding the prime factors of %0d", $time, location, getReg(location));
|
||||
"primeFactors":$display("Time =%5dns \t : Register [%0d] updated - %0d is a prime factor", $time, location, getReg(location));
|
||||
default :$display("Time =%5dns \t : Value at Reg[%0d]: 0x%h", $time, location, getReg(location));
|
||||
endcase
|
||||
endtask: printReg
|
||||
|
||||
endmodule: cpu_harness
|
||||
@@ -0,0 +1,173 @@
|
||||
// *********************************************************************************************
|
||||
// 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
|
||||
@@ -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