From 51a87a5dacbd5de3718991215a86bc2e8d76b802 Mon Sep 17 00:00:00 2001 From: plittlefield6 Date: Sat, 5 Sep 2026 16:59:51 -0400 Subject: [PATCH] make testbench visible so simvision works --- sim/behav/Include/cpu.include | 2 +- src/verilog/tb_processor.sv | 287 ++++++++++++++++++++++++++++++++++ src/verilog/tb_processor.svp | 209 ------------------------- 3 files changed, 288 insertions(+), 210 deletions(-) create mode 100644 src/verilog/tb_processor.sv delete mode 100644 src/verilog/tb_processor.svp diff --git a/sim/behav/Include/cpu.include b/sim/behav/Include/cpu.include index 850febc..05d4f8d 100644 --- a/sim/behav/Include/cpu.include +++ b/sim/behav/Include/cpu.include @@ -7,7 +7,7 @@ ../../src/verilog/cpu/cpu_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 // aka '../../src/verilog/cpu/(your module here).sv' diff --git a/src/verilog/tb_processor.sv b/src/verilog/tb_processor.sv new file mode 100644 index 0000000..5859488 --- /dev/null +++ b/src/verilog/tb_processor.sv @@ -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= 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= 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 diff --git a/src/verilog/tb_processor.svp b/src/verilog/tb_processor.svp deleted file mode 100644 index b6c3082..0000000 --- a/src/verilog/tb_processor.svp +++ /dev/null @@ -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