Initial commit
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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!
|
||||
@@ -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.
@@ -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
Reference in New Issue
Block a user