make testbench visible so simvision works

This commit is contained in:
plittlefield6
2026-09-05 16:59:51 -04:00
parent 49a639f69a
commit 51a87a5dac
3 changed files with 288 additions and 210 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
../../src/verilog/cpu/cpu_top.sv ../../src/verilog/cpu/cpu_top.sv
../../src/verilog/chip_top.sv ../../src/verilog/chip_top.sv
../../src/verilog/tb_processor.svp ../../src/verilog/tb_processor.sv
// you MUST add any new files you make to this include file, follow the path naming above // you MUST add any new files you make to this include file, follow the path naming above
// aka '../../src/verilog/cpu/(your module here).sv' // aka '../../src/verilog/cpu/(your module here).sv'
+287
View File
@@ -0,0 +1,287 @@
`timescale 1ns/1ps
module tb_processor;
localparam time CLK_PERIOD = 20ns;
localparam logic [13:0] ISRAM_BASE = 14'h0000;
localparam logic [13:0] DSRAM_BASE = 14'h1000;
localparam logic [13:0] REG_BASE = 14'h2000;
localparam int ISRAM_WORDS = 1024;
localparam int DSRAM_WORDS = 1024;
localparam int REG_COUNT = 32;
logic clk_i = 1'b0;
logic rst_i;
logic cpu_en_i;
logic cpu_halt_o;
logic halt_cpu;
logic [13:0] addr;
logic [31:0] wdata;
logic w_en;
logic r_en;
logic [31:0] rdata;
logic ready;
logic [31:0] program_image [0:ISRAM_WORDS-1];
logic [31:0] initial_data_image [0:DSRAM_WORDS-1]; //starting data for dsram
logic [31:0] expected_data_image [0:DSRAM_WORDS-1]; //expected final dsram
logic [31:0] expected_regs_image [0:REG_COUNT-1];
string program_file;
string data_file;
string expected_data_file;
string expected_regs_file;
int program_words;
int data_words;
int expected_data_words;
int max_cpu_cycles;
int crossbar_timeout;
int failures;
always #(CLK_PERIOD / 2) clk_i = ~clk_i;
chip_top dut (
.clk_i (clk_i),
.rst_i (rst_i),
.en_cpu_i (cpu_en_i),
.cpu_halted_o (cpu_halt_o),
.halt_cpu_i (halt_cpu),
.addr_i (addr),
.wdata_i (wdata),
.w_en_i (w_en),
.r_en_i (r_en),
.rdata_o (rdata),
.rready_o (ready)
);
function automatic logic [13:0] word_address(
input logic [13:0] base,
input int unsigned word_index
);
word_address = base + (word_index << 2); //cleaning up byte offset
endfunction
task automatic crossbar_write(
input logic [13:0] write_addr,
input logic [31:0] write_data
);
int wait_cycles;
begin
if (write_addr[1:0] != 2'b00)
$fatal(1, "Crossbar write address %h is not word-aligned",
write_addr);
@(negedge clk_i);
addr <= write_addr;
wdata <= write_data;
w_en <= 1'b1;
r_en <= 1'b0;
@(negedge clk_i);
w_en <= 1'b0;
addr <= '0;
wdata <= '0;
end
endtask
task automatic crossbar_read(
input logic [13:0] read_addr,
output logic [31:0] read_data
);
int wait_cycles;
begin
if (read_addr[1:0] != 2'b00)
$fatal(1, "Crossbar read address %h is not word-aligned",
read_addr);
@(negedge clk_i);
addr <= read_addr;
w_en <= 1'b0;
r_en <= 1'b1;
wait_cycles = 0;
while (ready !== 1'b1) begin
@(posedge clk_i);
#1;
wait_cycles++;
if (wait_cycles >= crossbar_timeout)
$fatal(1,
"Crossbar read timed out at address %h",
read_addr);
end
read_data = rdata;
@(negedge clk_i);
r_en <= 1'b0;
addr <= '0;
end
endtask
task automatic reset_dut;
begin
cpu_en_i <= 1'b0;
rst_i <= 1'b1;
repeat (3) @(posedge clk_i);
@(negedge clk_i);
rst_i <= 1'b0;
end
endtask
task automatic load_program;
int i;
begin
$display("Loading %0d instruction words from %s",
program_words, program_file);
$readmemh(program_file, program_image);
for (i = 0; i < program_words; i++)
crossbar_write(word_address(ISRAM_BASE, i),
program_image[i]);
end
endtask
task automatic load_initial_data;
int i;
begin
if (data_words > 0) begin
$display("Loading %0d data words from %s",
data_words, data_file);
$readmemh(data_file, initial_data_image);
for (i = 0; i < data_words; i++)
crossbar_write(word_address(DSRAM_BASE, i),
initial_data_image[i]);
end
end
endtask
task automatic run_until_halt;
int cycles;
begin
@(negedge clk_i);
cpu_en_i <= 1'b1;
cycles = 0;
@(negedge clk_i);
cpu_en_i <= 1'b0;
while (cpu_halt_o !== 1'b1) begin
@(posedge clk_i);
#1;
cycles++;
if (cycles >= max_cpu_cycles)
$fatal(1, "CPU did not halt within %0d cycles",
max_cpu_cycles);
end
$display("CPU halted after %0d cycles", cycles);
@(negedge clk_i);
cpu_en_i <= 1'b0;
repeat (3) @(negedge clk_i);
end
endtask
task automatic check_word(
input logic [13:0] check_addr,
input logic [31:0] expected,
input string description
);
logic [31:0] actual;
begin
crossbar_read(check_addr, actual);
if (actual !== expected) begin
failures++;
$error("FAIL: %s at %h: expected %h, got %h",
description, check_addr, expected, actual);
end
end
endtask
task automatic check_registers;
int i;
begin
$readmemh(expected_regs_file, expected_regs_image);
for (i = 0; i < REG_COUNT; i++)
check_word(word_address(REG_BASE, i),
expected_regs_image[i],
$sformatf("register x%0d", i));
end
endtask
task automatic check_data_memory;
int i;
begin
if (expected_data_words > 0) begin
$readmemh(expected_data_file, expected_data_image);
for (i = 0; i < expected_data_words; i++)
check_word(word_address(DSRAM_BASE, i),
expected_data_image[i],
$sformatf("data word %0d", i));
end
end
endtask
initial begin
$shm_open("waves.shm");
$shm_probe("AC");
rst_i = 1'b1;
cpu_en_i = 1'b0;
halt_cpu = 1'b0;
addr = '0;
wdata = '0;
w_en = 1'b0;
r_en = 1'b0;
failures = 0;
program_words = 0;
data_words = 0;
expected_data_words = 0;
max_cpu_cycles = 1000;
crossbar_timeout = 20;
if (!$value$plusargs("PROGRAM=%s", program_file))
$fatal(1, "Missing required +PROGRAM=<program.hex> plusarg");
if (!$value$plusargs("PROGRAM_WORDS=%d", program_words) ||
program_words <= 0 || program_words > ISRAM_WORDS)
$fatal(1, "PROGRAM_WORDS must be between 1 and %0d",
ISRAM_WORDS);
if ($value$plusargs("DATA=%s", data_file)) begin
if (!$value$plusargs("DATA_WORDS=%d", data_words) ||
data_words < 0 || data_words > DSRAM_WORDS)
$fatal(1, "DATA_WORDS must be between 0 and %0d",
DSRAM_WORDS);
end
if (!$value$plusargs("EXPECTED_REGS=%s", expected_regs_file))
$fatal(1,
"Missing required +EXPECTED_REGS=<expected_regs.hex> plusarg");
if ($value$plusargs("EXPECTED_DATA=%s", expected_data_file)) begin
if (!$value$plusargs("EXPECTED_DATA_WORDS=%d",
expected_data_words) ||
expected_data_words < 0 ||
expected_data_words > DSRAM_WORDS)
$fatal(1, "EXPECTED_DATA_WORDS must be between 0 and %0d",
DSRAM_WORDS);
end
void'($value$plusargs("MAX_CPU_CYCLES=%d", max_cpu_cycles));
void'($value$plusargs("CROSSBAR_TIMEOUT=%d", crossbar_timeout));
reset_dut();
load_program();
load_initial_data();
run_until_halt();
check_registers();
check_data_memory();
if (failures == 0)
$display("PASS: processor behavior matches expected results");
else
$fatal(1, "FAIL: processor test found %0d mismatch(es)",
failures);
$finish;
end
endmodule
-209
View File
@@ -1,209 +0,0 @@
//pragma protect begin_protected
//pragma protect encrypt_agent="NCPROTECT"
//pragma protect encrypt_agent_info="Encrypted using API"
//pragma protect key_keyowner=Cadence Design Systems.
//pragma protect key_keyname=prv(CDS_RSA_KEY_VER_2)
//pragma protect key_method=RSA
//pragma protect key_block
AyB4/PdaKdcNPQnQ3SkT7E1YxNMdnVzwKFqaJL5r0UzLOm11Gy0Q+lg9ibUJsOar
bv95EIiFSVLXgztIWsRlk/fcG9g1JYQo+kn1uMCv7/qrlfIhWSB3CaUU/RSBWNqk
Eryj9VkWkAYvBPheECeTwbt6GTZQqqV9Fj46iH9hmAIvP5hB3aKd+khmEblM7Jp5
YG8rXNFLgvagatedQ0aJWWL4M7ghm53Lb8oYbKPgrIKfBLC4vP4ZHE6RkJakKsT5
Zehseo+GnnxZwtXKx/tODhCk2TrCwYH2UXlmKEkf7V1RtDit/u0KPhku7plv1vli
IhFrnPc4OrY9I2nVchGY8w==
//pragma protect end_key_block
//pragma protect digest_block
NFoMbWuzMDAJl2pWNb4BaFJV+ik=
//pragma protect end_digest_block
//pragma protect data_block
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
//pragma protect end_data_block
//pragma protect digest_block
n/HPYx0H7ZW2nhgXuEVMBJXzP0Q=
//pragma protect end_digest_block
//pragma protect end_protected