Initial commit

This commit is contained in:
muellerlu
2026-05-29 10:19:13 +02:00
commit fedfd92270
726 changed files with 542654 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
// *********************************************************************************************
// 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 Bomin Kim
// Last Modified : 23.Oct.2025 by Bomin Kim [commit 2f8f03d]
// -----
// HISTORY : Date By Comments
// ----------- --------- -------------------------------------------------
// *********************************************************************************************
`timescale 1ns/1ns
module alu_tb();
// Local Signals
logic[2:0] aluOp;
logic aluNegAr;
logic aluBypass;
logic[31:0] op1;
logic[31:0] op2;
logic[31:0] result;
logic eqFlag;
// Toplevel instance (DUT)
alu u_alu (
.aluOp(aluOp),
.aluNegAr(aluNegAr),
.aluBypass(aluBypass),
.op1(op1),
.op2(op2),
.result(result),
.eqFlag(eqFlag)
);
// list of aluOp
localparam logic[2:0] f3add = 3'b000;
localparam logic[2:0] f3sl = 3'b001;
localparam logic[2:0] f3slt = 3'b010;
localparam logic[2:0] f3sltU = 3'b011;
localparam logic[2:0] f3xor = 3'b100;
localparam logic[2:0] f3sr = 3'b101;
localparam logic[2:0] f3or = 3'b110;
localparam logic[2:0] f3and = 3'b111;
// Initialize and run simulation
initial begin
dumpWave("wave.vcd");
// Initialize inputs
aluOp = 0;
aluNegAr = 0;
aluBypass = 0;
op1 = 0;
op2 = 0;
#10
// Set op1 and op2
op1 = 32'hDEAD_BEEF; op2 = 32'h0000_0001; #70
$display("\nTime = %0dns \t : op1 = %h, op2 = %h", $time, op1, op2);
$display("\nTime = %0dns \t : Is op1 and op2 equal?; eqFlag = %d", $time, eqFlag);
aluBypass = 1; #70
$display("\nTime = %0dns \t : AluBypass is set; result = %h", $time, result);
aluBypass = 0; aluOp = f3add; #70
$display("\nTime = %0dns \t : Alu operates addition; result = %h", $time, result);
aluNegAr = 1; #70
$display("\nTime = %0dns \t : Alu operates subtraction; result = %h", $time, result);
aluNegAr = 0; aluOp = f3sl; #70
$display("\nTime = %0dns \t : Alu operates shift left; result = %h", $time, result);
aluOp = f3slt; #70
$display("\nTime = %0dns \t : Alu operates set less than; result = %h", $time, result);
aluOp = f3sltU; #70
$display("\nTime = %0dns \t : Alu operates set less than (unsigned); result = %h", $time, result);
aluOp = f3xor; #70
$display("\nTime = %0dns \t : Alu operates bit-wise XOR; result = %h", $time, result);
aluOp = f3sr; aluNegAr = 1; #70
$display("\nTime = %0dns \t : Alu operates arithmetic right shift; result = %h", $time, result);
aluNegAr = 0; #70
$display("\nTime = %0dns \t : Alu operates logical right shift; result = %h", $time, result);
aluOp = f3or; #70
$display("\nTime = %0dns \t : Alu operates bit-wise OR; result = %h", $time, result);
aluOp = f3and; #70
$display("\nTime = %0dns \t : Alu operates bit-wise AND; result = %h", $time, result);
$finish;
end
// Wave Dump Helper Task
task dumpWave(string fileName);
// Open wave file and dump all signals (2D arrays not included)
$display("\nTime = %0dns \t : Opening wave file '%s'", $time, fileName);
$dumpfile(fileName);
$display("Time = %0dns \t : Dumping all %s signals in wave file (2D arrays not included)", $time, "alu_tb");
$dumpvars(0, alu_tb);
endtask: dumpWave
endmodule
+164
View File
@@ -0,0 +1,164 @@
// *********************************************************************************************
// 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 : 23.Oct.2025 by Hussein Elzomor [commit 2f8f03d]
// -----
// HISTORY : Date By Comments
// ----------- --------- -------------------------------------------------
// 15.Oct.2025 H.Elzomor Renamed file and module from soc_tb to cpu_tb
// *********************************************************************************************
// `define fibonacci
// `define helloWorld
// `define primeFactors
`timescale 1ns/1ns
module cpu_tb ();
// local signals
logic[7:0] led;
logic[6:0] btn;
logic reset;
logic clk;
// Toplevel instance (DUT)
cpu u_cpu (
.led(led),
.btn(btn),
.clk_25mhz(clk)
);
// Tie reset signal to the reset button
assign btn[0] = reset;
// Clock generation
always #20 clk = ~clk;
// Initialize and run simulation
initial begin
dumpWave("wave.vcd");
`ifdef fibonacci
loadMem("fibonacci.hex");
`elsif helloWorld
loadMem("helloWorld.hex");
`elsif primeFactors
loadMem("primeFactors.hex");
`endif
clk = 0;
$display("\nTime = %0dns \t : Resetting the CPU", $time);
reset = 0; #100;
$display("\nTime = %0dns \t : Reset released", $time);
reset = 1; #500000;
$finish;
end
// Fibonacci Program Monitor
`ifdef fibonacci
localparam int fibonacciStartLoc = 2027;
localparam int fibonacciLengthInWords = 10;
always_comb begin: Monitor_Fibonacci_Series_Calculation
$display("\nTime =%5dns\t\t\t Hex \t Dec", $time);
$display("Value at RAM[%0d]: 0x%h : %0d",fibonacciStartLoc+0, u_cpu.theMem.RAM[fibonacciStartLoc+0], u_cpu.theMem.RAM[fibonacciStartLoc+0]);
$display("Value at RAM[%0d]: 0x%h : %0d",fibonacciStartLoc+1, u_cpu.theMem.RAM[fibonacciStartLoc+1], u_cpu.theMem.RAM[fibonacciStartLoc+1]);
$display("Value at RAM[%0d]: 0x%h : %0d",fibonacciStartLoc+2, u_cpu.theMem.RAM[fibonacciStartLoc+2], u_cpu.theMem.RAM[fibonacciStartLoc+2]);
$display("Value at RAM[%0d]: 0x%h : %0d",fibonacciStartLoc+3, u_cpu.theMem.RAM[fibonacciStartLoc+3], u_cpu.theMem.RAM[fibonacciStartLoc+3]);
$display("Value at RAM[%0d]: 0x%h : %0d",fibonacciStartLoc+4, u_cpu.theMem.RAM[fibonacciStartLoc+4], u_cpu.theMem.RAM[fibonacciStartLoc+4]);
$display("Value at RAM[%0d]: 0x%h : %0d",fibonacciStartLoc+5, u_cpu.theMem.RAM[fibonacciStartLoc+5], u_cpu.theMem.RAM[fibonacciStartLoc+5]);
$display("Value at RAM[%0d]: 0x%h : %0d",fibonacciStartLoc+6, u_cpu.theMem.RAM[fibonacciStartLoc+6], u_cpu.theMem.RAM[fibonacciStartLoc+6]);
$display("Value at RAM[%0d]: 0x%h : %0d",fibonacciStartLoc+7, u_cpu.theMem.RAM[fibonacciStartLoc+7], u_cpu.theMem.RAM[fibonacciStartLoc+7]);
$display("Value at RAM[%0d]: 0x%h : %0d",fibonacciStartLoc+8, u_cpu.theMem.RAM[fibonacciStartLoc+8], u_cpu.theMem.RAM[fibonacciStartLoc+8]);
$display("Value at RAM[%0d]: 0x%h : %0d",fibonacciStartLoc+9, u_cpu.theMem.RAM[fibonacciStartLoc+9], u_cpu.theMem.RAM[fibonacciStartLoc+9]);
$display("");
end
// Hello World Program Monitor
`elsif helloWorld
localparam int helloWorldStartLoc = 1024;
localparam int helloWorldLengthInWords = 4;
always_comb begin: Monitor_Hello_World_Printing
$display("\nTime =%5dns\t\t Hex \t\t ASCII", $time);
$display("Value at RAM[%0d]: 0x%h : %0s", helloWorldStartLoc+0, u_cpu.theMem.RAM[helloWorldStartLoc+0], u_cpu.theMem.RAM[helloWorldStartLoc+0]);
$display("Value at RAM[%0d]: 0x%h : %0s", helloWorldStartLoc+1, u_cpu.theMem.RAM[helloWorldStartLoc+1], u_cpu.theMem.RAM[helloWorldStartLoc+1]);
$display("Value at RAM[%0d]: 0x%h : %0s", helloWorldStartLoc+2, u_cpu.theMem.RAM[helloWorldStartLoc+2], u_cpu.theMem.RAM[helloWorldStartLoc+2]);
$display("Value at RAM[%0d]: 0x%h : %0s", helloWorldStartLoc+3, u_cpu.theMem.RAM[helloWorldStartLoc+3], u_cpu.theMem.RAM[helloWorldStartLoc+3]);
$display("");
end
// Prime Factors Program Monitor
`elsif primeFactors
localparam int primeNumberReg = 16;
always_comb begin: Monitor_Factorization_Number
if(u_cpu.theRegisters.registers[primeNumberReg] > 1)
$display("\nTime =%5dns \t : Register [%0d] updated - Finding the prime factors of %0d", $time,primeNumberReg, u_cpu.theRegisters.registers[primeNumberReg]);
end
localparam int primeFactorsReg = 17;
always_comb begin: Monitor_Prime_Number
if(u_cpu.theRegisters.registers[primeFactorsReg] > 1)
$display("Time =%5dns \t : Register [%0d] updated - %0d is a prime factor", $time,primeFactorsReg, u_cpu.theRegisters.registers[primeFactorsReg]);
end
`endif
// Wave Dump Helper Task
int i;
task dumpWave(string fileName);
// Open wave file and dump all signals (2D arrays not included)
$display("\nTime = %0dns \t : Opening wave file '%s'", $time, fileName);
$dumpfile(fileName);
$display("Time = %0dns \t : Dumping all %s signals in wave file (2D arrays not included)", $time, "cpu_tb");
$dumpvars(0, cpu_tb);
// Dump Memory in wave file
$display("\nTime = %0dns \t : Dumping Memory in wave file", $time);
for (i = 0; i < 100; i++) begin $dumpvars(0, cpu_tb.u_cpu.theMem.RAM[i]); end // A part of the Instruction Memory
for (i = 1024; i < 1124; i++) begin $dumpvars(0, cpu_tb.u_cpu.theMem.RAM[i]); end // A part of the Data Memory
for (i = 1968; i < 2048; i++) begin $dumpvars(0, cpu_tb.u_cpu.theMem.RAM[i]); end // A part of the Stack
// Dump registers in wave file
$display("\nTime = %0dns \t : Dumping Registers in wave file", $time);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[1]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[2]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[3]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[4]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[5]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[6]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[7]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[8]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[9]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[10]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[11]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[12]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[13]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[14]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[15]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[16]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[17]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[18]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[19]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[20]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[21]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[22]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[23]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[24]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[25]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[26]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[27]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[28]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[29]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[30]);
$dumpvars(0, cpu_tb.u_cpu.theRegisters.registers[31]);
endtask: dumpWave
// Load hex file into memory
task loadMem (string fileName);
$display("\nTime = %0dns \t : Loading '%s' into Memory", $time, fileName);
$readmemh(fileName, cpu_tb.u_cpu.theMem.RAM);
endtask: loadMem
endmodule
+150
View File
@@ -0,0 +1,150 @@
// *********************************************************************************************
// 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 : 23.Oct.2025 by Hussein Elzomor [commit 2f8f03d]
// -----
// HISTORY : Date By Comments
// ----------- --------- -------------------------------------------------
// *********************************************************************************************
`timescale 1ns/1ns
module decoder_tb ();
// 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)
);
// Clock generation
always begin
clk = ~clk; #1;
end
// 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
// Initialize and run simulation
initial begin
dumpWave("wave.vcd");
loadMem("decoder.hex");
clk = 1;
counter = 0;
CurrentPC = 32'hdeadbeef;
RData = 32'hbeefdead;
for(int i=0; i<REG_FILE_SIZE; i++) begin
regFile[i] = i;
end
while (counter < MEM_SIZE) #1;
printInfo();
#1 $finish;
end
// Functions
// Wave Dump Helper Task
task dumpWave(string fileName);
// Open wave file and dump all signals (2D arrays not included)
$display("\nTime = %0dns \t : Opening wave file '%s'", $time, fileName);
$dumpfile(fileName);
$display("Time = %0dns \t : Dumping all %s signals in wave file (2D arrays not included)", $time, "decoder_tb");
$dumpvars(0, decoder_tb);
endtask: dumpWave
// Load hex file into memory
task loadMem (string fileName);
$display("\nTime = %0dns \t : Loading '%s' into Memory", $time, fileName);
$readmemh(fileName, mem);
endtask: loadMem
// Print decoder signals
task printInfo();
$displayh("\n(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 );
endtask: printInfo
endmodule
+238
View File
@@ -0,0 +1,238 @@
// *********************************************************************************************
// Project Version : v1.0
// Project : [BCDC] Microtec Academy Course: Building a RISC-V CPU with SystemVerilog
// -----
// Copyright (c) : 2025 Fraunhofer IIS, Department IDS
// Created : 20.Oct.2025 by Aliakbar Merchant
// Last Modified : 24.Oct.2025 by Aliakbar Merchant [commit 89a5087]
// -----
// HISTORY : Date By Comments
// ----------- --------- -------------------------------------------------
// *********************************************************************************************
`timescale 1ns/1ns
module main_mem_tb ();
// local Parameters
localparam string MEM_INIT_FILE = "main_mem.hex";
// Helpers for addressing width
localparam logic[1:0] _byte = 2'b00; // byte: 8 bits
localparam logic[1:0] _half = 2'b01; // half: 16 bits
localparam logic[1:0] _word = 2'b10; // word: 32 bits
// Local Signals
// clk and reset
logic clk;
logic reset;
// Memory
logic[31:0] DAddr;
logic[31:0] IAddr;
logic[31:0] DWData;
logic[31:0] DRData;
logic[31:0] IRData;
logic DWE;
logic[1:0] DWidth;
// Helpers for display results purpose
logic[31:0] value_at_0;
logic[31:0] value_at_1;
logic[31:0] value_at_2;
logic[31:0] value_at_3;
logic[31:0] value_at_1024;
logic[31:0] value_at_1025;
logic[31:0] value_at_1026;
logic[31:0] value_at_1027;
// Toplevel instance (DUT)
main_mem u_main_mem (
.clk (clk ),
.reset (reset ),
.DAddr (DAddr ),
.IAddr (IAddr ),
.DWData (DWData ),
.DRData (DRData ),
.IRData (IRData ),
.DWE (DWE ),
.DWidth (DWidth )
);
// Clock generation
always #1 clk = ~clk;
// Initialize and run simulation
initial begin
loadMem(MEM_INIT_FILE);
value_at_0 = main_mem_tb.u_main_mem.RAM[0];
value_at_1 = main_mem_tb.u_main_mem.RAM[1];
value_at_2 = main_mem_tb.u_main_mem.RAM[2];
value_at_3 = main_mem_tb.u_main_mem.RAM[3];
value_at_1024 = main_mem_tb.u_main_mem.RAM[1024];
value_at_1025 = main_mem_tb.u_main_mem.RAM[1025];
value_at_1026 = main_mem_tb.u_main_mem.RAM[1026];
value_at_1027 = main_mem_tb.u_main_mem.RAM[1027];
clk = 0;
DAddr = 32'd4096;
IAddr = 0;
DWData = 0;
DWidth = 0;
reset = 1; DWE=1; #7;
DWE = 0;
reset = 0; #2;
test_scenario1(); #30;
$finish;
end
// Test scenario 1
task test_scenario1();
fork
read_inst_mem(15);
display_inst_read_results();
write_data_mem(4096);
join
display_write_results();
fork
display_read_results();
read_data_mem(4096);
join
endtask: test_scenario1
// Intruction memory read transactions and results display
task read_inst_mem(int unsigned n = 1);
repeat(n) begin
cycles_pe(1);
IAddr = IAddr+4;
end
endtask:read_inst_mem
task display_inst_read_results();
$display("\n----------------------------------------------------------------------------------" );
$display("Instruction MEMORY READ RESULTS" );
$display("15 instruction are read by testbench starting at adrress 0x0000 i.e at (RAM[0]) but only 4 displyed for simplicity" );
$display("----------------------------------------------------------------------------------" );
cycles_pe(2);
$display("Time = %0dns \t : IAddr = 0x0000(RAM[0]): Expected value = 0x%h Actual Value at IRData: 0x%h" , $time, value_at_0, IRData );
cycles_pe(1);
$display("Time = %0dns \t : IAddr = 0x0004(RAM[1]): Expected value = 0x%h Actual Value at IRData: 0x%h" , $time, value_at_1, IRData );
cycles_pe(1);
$display("Time = %0dns \t : IAddr = 0x0008(RAM[2]): Expected value = 0x%h Actual Value at IRData: 0x%h" , $time, value_at_2, IRData );
cycles_pe(1);
$display("Time = %0dns \t : IAddr = 0x000c(RAM[3]): Expected value = 0x%h Actual Value at IRData: 0x%h" , $time, value_at_3, IRData );
endtask:display_inst_read_results
// Data memory write transaction and results display
task write_data_mem(logic [31:0] daddr_init=32'd4096);
cycles_pe(1);
DWE = 1;
DWidth = 0;
DAddr = daddr_init+0;
DWData = 32'hDEADBEEF;
cycles_pe(1);
DWE = 1;
DWidth = 1;
DAddr = daddr_init+4;
DWData = 32'hDEADBEEF;
cycles_pe(1);
DWE = 1;
DWidth = 2;
DAddr = daddr_init+8;
DWData = 32'hDEADBEEF;
cycles_pe(1);
DWE = 1;
DWidth = 3;
DAddr = daddr_init+12;
DWData = 32'hDEADBEEF;
cycles_pe(1);
DWE = 0;
endtask:write_data_mem
task display_write_results();
$display("\n----------------------------------------------------------------------------------" );
$display("DATA MEMORY WRITE RESULTS" );
$display("4 Write transaction are being done by testbench starting at adrress 0x1000 i.e at (RAM[1024]) with all 4 combination of DWidth" );
$display("Data written is always 0xDEADBEEF i.e DWData = DEADBEEF" );
$display("----------------------------------------------------------------------------------" );
$display("Time = %0dns \t : DWidth = 0 DAddr = 0x1000(RAM[1024]): Start value at RAM[1024] = 0x%h Expected value at RAM[1024]= 0xef%h Actual Value at RAM[1024]: 0x%h" , $time, value_at_1024,value_at_1024[23:0], getMem(1024) );
$display("Time = %0dns \t : DWidth = 1 DAddr = 0x1004(RAM[1025]): Start value at RAM[1025] = 0x%h Expected value at RAM[1025]= 0xbeef%h Actual Value at RAM[1025]: 0x%h" , $time, value_at_1025,value_at_1025[15:0], getMem(1025) );
$display("Time = %0dns \t : DWidth = 2 DAddr = 0x1008(RAM[1026]): Start value at RAM[1026] = 0x%h Expected value at RAM[1026]= 0xdeadbeef Actual Value at RAM[1026]: 0x%h" , $time, value_at_1026, getMem(1026) );
$display("Time = %0dns \t : DWidth = 3 DAddr = 0x100c(RAM[1027]): Start value at RAM[1027] = 0x%h Expected value at RAM[1027]= 0x%h Actual Value at RAM[1027]: 0x%h" , $time, value_at_1027,value_at_1027, getMem(1027) );
endtask:display_write_results
// Data Memory read transaction and results display
task read_data_mem(logic [31:0] daddr_init=32'd4096);
cycles_pe(1);
DWidth = 0;
DAddr = daddr_init+0;
cycles_pe(1);
DWidth = 1;
DAddr = daddr_init+4;
cycles_pe(1);
DWidth = 2;
DAddr = daddr_init+8;
cycles_pe(1);
DWidth = 3;
DAddr = daddr_init+12;
cycles_pe(1);
DWidth = 2;
DAddr = daddr_init+0;
cycles_pe(1);
DWidth = 2;
DAddr = daddr_init+4;
cycles_pe(1);
DWidth = 2;
DAddr = daddr_init+8;
cycles_pe(1);
DWidth = 2;
DAddr = daddr_init+12;
endtask:read_data_mem
task display_read_results();
$display("\n----------------------------------------------------------------------------------" );
$display("DATA MEMORY READ RESULTS" );
$display("8 Read transaction are being done by testbench starting at adrress 0x1000 i.e at (RAM[1024])" );
$display("----------------------------------------------------------------------------------" );
cycles_pe(2);
$display("Time = %0dns \t : DWidth = 0 DAddr = 0x1000(RAM[1024]): Expected value = 0x000000ef Actual Value at DRData: 0x%h" , $time, DRData );
cycles_pe(1);
$display("Time = %0dns \t : DWidth = 1 DAddr = 0x1004(RAM[1025]): Expected value = 0x0000beef Actual Value at DRData: 0x%h" , $time, DRData );
cycles_pe(1);
$display("Time = %0dns \t : DWidth = 2 DAddr = 0x1008(RAM[1026]): Expected value = 0xdeadbeef Actual Value at DRData: 0x%h" , $time, DRData );
cycles_pe(1);
$display("Time = %0dns \t : DWidth = 3 DAddr = 0x100c(RAM[1027]): Expected value = 0xdeadbeef Actual Value at DRData: 0x%h" , $time, DRData );
cycles_pe(1);
$display("Time = %0dns \t : DWidth = 2 DAddr = 0x1000(RAM[1024]): Expected value = 0xef%h Actual Value at DRData: 0x%h" , $time, value_at_1024[23:0], DRData );
cycles_pe(1);
$display("Time = %0dns \t : DWidth = 2 DAddr = 0x1004(RAM[1025]): Expected value = 0xbeef%h Actual Value at DRData: 0x%h" , $time, value_at_1025[15:0], DRData );
cycles_pe(1);
$display("Time = %0dns \t : DWidth = 2 DAddr = 0x1008(RAM[1026]): Expected value = 0xdeadbeef Actual Value at DRData: 0x%h" , $time, DRData );
cycles_pe(1);
$display("Time = %0dns \t : DWidth = 2 DAddr = 0x100c(RAM[1027]): Expected value = 0x%h Actual Value at DRData: 0x%h" , $time, value_at_1027, DRData );
endtask:display_read_results
// Helper tasks
// Memory
function int getMem(int location);
getMem = u_main_mem.RAM[location];
endfunction: getMem
task cycles_pe(int unsigned N = 1);
repeat(N) @(posedge clk);
endtask: cycles_pe
task cycles_ne(int unsigned N = 1);
repeat(N) @(negedge clk);
endtask: cycles_ne
// Load hex file into memory
task loadMem (string fileName);
$display("\nTime = %0dns \t : Loading '%s' into Memory", $time, fileName);
$readmemh(fileName, main_mem_tb.u_main_mem.RAM);
endtask: loadMem
endmodule
+68
View File
@@ -0,0 +1,68 @@
// *********************************************************************************************
// 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 Bomin Kim
// Last Modified : 23.Oct.2025 by Bomin Kim [commit 2f8f03d]
// -----
// HISTORY : Date By Comments
// ----------- --------- -------------------------------------------------
// *********************************************************************************************
`timescale 1ns/1ns
module pc_tb ();
// Local Signals
logic[31:0] CurrentPC;
logic[31:0] JumpOrBranchPC;
logic JumpOrBranch;
logic[31:0] NextPC;
logic reset;
logic clk;
// Toplevel instance (DUT)
pc u_pc (
.CurrentPC(CurrentPC),
.JumpOrBranchPC(JumpOrBranchPC),
.JumpOrBranch(JumpOrBranch),
.NextPC(NextPC),
.reset(reset),
.clk(clk)
);
// Clock generation
always #20 clk = ~clk;
// Initialization and run simulation
initial begin
dumpWave("wave.vcd");
clk = 0;
JumpOrBranchPC = 32'h8;
JumpOrBranch = 0;
reset = 0; #100;
reset = 1; #100;
reset = 0; #490;
JumpOrBranch = 1; #60;
JumpOrBranch = 0; #500;
$finish;
end
// NextPC Monitor
initial begin
$monitor("time=%0t clk=%b reset=%b JumpOrBranch=%0h CurrentPC=%0h NextPC=%0h", $time, clk, reset, JumpOrBranch, CurrentPC, NextPC);
end
// Wave Dump Helper Task
task dumpWave(string fileName);
// Open wave file and dump all signals (2D arrays not included)
$display("\nTime = %0dns \t : Opening wave file '%s'", $time, fileName);
$dumpfile(fileName);
$display("Time = %0dns \t : Dumping all %s signals in wave file (2D arrays not included)", $time, "pc_tb");
$dumpvars(0, pc_tb);
endtask: dumpWave
endmodule
+137
View File
@@ -0,0 +1,137 @@
// *********************************************************************************************
// 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 Bomin Kim
// Last Modified : 23.Oct.2025 by Bomin Kim [commit 2f8f03d]
// -----
// HISTORY : Date By Comments
// ----------- --------- -------------------------------------------------
// *********************************************************************************************
`timescale 1ns/1ns
module reg_file_tb ();
// Local Signals
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;
logic reset;
logic clk;
// Toplevel instance (DUT)
reg_file u_reg_file (
.Rs1(Rs1),
.Rs2(Rs2),
.Rd(Rd),
.RRs1(RRs1),
.RRs2(RRs2),
.WRd(WRd),
.WrReg(WrReg),
.reset(reset),
.clk(clk)
);
// Clock generation
always #20 clk = ~clk;
// Initialization and run simulation
initial begin
dumpWave("wave.vcd");
// Initialize inputs
clk = 0;
Rs1 = 0; Rs2 = 0; Rd = 0;
WRd = 0; WrReg = 0; // Read
reset = 1;
#50
reset = 0;
#50 // Write 0xDEADBEEF to reg21
Rd = 5'd21;
WrReg = 1; // Write
WRd = 32'hDEADBEEF;
#50; WrReg = 0;
#50
Rs1 = 5'd21; // read reg21 into RRs1
Rs2 = 5'd0;
#50 // Write 0x12345678 to reg5
Rd = 5'd5;
WrReg = 1; // Write
WRd = 32'h12345678;
#50; WrReg = 0;
#50
Rs1 = 5'd5; // read reg10 into RRs1
#50
Rs1 = 5'd0; // read zero register
#50
Rd = 5'd0; // attempt to write 0xCAFEBABE to zero register
WRd = 32'hCAFEBABE;
WrReg = 1; // Write
#50; WrReg = 0;
#50
Rs1 = 5'd5; // Read from reg5
#200
$finish;
end
// Wave Dump Helper Task
task dumpWave(string fileName);
// Open wave file and dump all signals (2D arrays not included)
$display("\nTime = %0dns \t : Opening wave file '%s'", $time, fileName);
$dumpfile(fileName);
$display("Time = %0dns \t : Dumping all %s signals in wave file (2D arrays not included)", $time, "reg_file_tb");
$dumpvars(0, reg_file_tb);
// Dump registers in wave file
$display("\nTime = %0dns \t : Dumping Registers in wave file", $time);
$dumpvars(0, reg_file_tb.u_reg_file.registers[1]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[2]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[3]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[4]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[5]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[6]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[7]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[8]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[9]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[10]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[11]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[12]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[13]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[14]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[15]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[16]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[17]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[18]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[19]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[20]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[21]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[22]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[23]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[24]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[25]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[26]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[27]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[28]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[29]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[30]);
$dumpvars(0, reg_file_tb.u_reg_file.registers[31]);
endtask: dumpWave
endmodule
+79
View File
@@ -0,0 +1,79 @@
# OpenFile ./cpu_tb.sv
ls
# alu_tb.sv decoder_tb.sv pc_tb.sv transcript
# cpu_tb.sv main_mem_tb.sv reg_file_tb.sv work
vsim compile.tcl
# vsim compile.tcl
# Start time: 11:01:25 on Nov 28,2025
# ** Error (suppressible): (vsim-19) Failed to access library 'compile' at "compile".
# No such file or directory. (errno = ENOENT)
# Error loading design
# End time: 11:01:25 on Nov 28,2025, Elapsed time: 0:00:00
# Errors: 1, Warnings: 0
ls
# alu_tb.sv decoder_tb.sv pc_tb.sv transcript
# cpu_tb.sv main_mem_tb.sv reg_file_tb.sv work
cd ..
ls
# rtl verilator wave_configs
cd ..
pwd
# C:/PhD/RISC-V Design Course Material/RISC-V Desgin Course - Complete Source Code/riscv_in3days-public/hw
cd ..
ls
# README.md doc hw sw
cd ..
ls
# riscv_in3days-public
cd ..
ls
# Course Introduction & Closing - Public.pdf
# Day 1
# Day 2
# Day 3
# Day 4
# RISC-V Desgin Course - Complete Source Code
# References
cd ..
cd ..
la
# ambiguous command name "la": label labelframe langOf lappend lassign lattice_edition layout
ls
# Drivers Program Files (x86) inetpub
# DumpStack.log Steam_Community_Markt intelFPGA
# Lukas Users intelFPGA_lite
# PhD Windows vfcompat.dll
# ProcLogs appverifUI.dll
# Program Files flexlm
pwd
# C:/
ls
# Drivers Program Files (x86) inetpub
# DumpStack.log Steam_Community_Markt intelFPGA
# Lukas Users intelFPGA_lite
# PhD Windows vfcompat.dll
# ProcLogs appverifUI.dll
# Program Files flexlm
cd PhD
ls
# Code compile.tcl
# RISC-V Design Course Material
cd RISC-V Design Course Material
# wrong # args: should be "cd ?dirName?"
ls
# Code compile.tcl
# RISC-V Design Course Material
ls
# Code compile.tcl
# RISC-V Design Course Material
cd RISC-V Design Course Material
# wrong # args: should be "cd ?dirName?"
ls
# Code compile.tcl
# RISC-V Design Course Material
run compile.tcl
# No Design Loaded!
compile.tcl
# couldn't execute ".\compile.tcl": no such file or directory
run compile.tcl
# No Design Loaded!
+10
View File
@@ -0,0 +1,10 @@
m255
K4
z2
13
!s112 1.1
!i10d 8192
!i10e 25
!i10f 100
cModel Technology
dC:/PhD/RISC-V Design Course Material/RISC-V Desgin Course - Complete Source Code/riscv_in3days-public/hw/dv/rtl
Binary file not shown.
Binary file not shown.
Binary file not shown.
+78
View File
@@ -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);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff