From cb0be9e2039569ee7d18657e8f675d1f8369b407 Mon Sep 17 00:00:00 2001 From: Flavian Kaufmann Date: Tue, 21 May 2024 13:50:28 +0200 Subject: restructured project --- sim/gentestvec/Makefile | 33 ++++++++ sim/gentestvec/src/alu.c | 117 ++++++++++++++++++++++++++ sim/gentestvec/src/cpu.c | 6 ++ sim/gentestvec/src/register_file.c | 49 +++++++++++ sim/testbench_alu.v | 84 ------------------ sim/testbench_cpu.v | 68 --------------- sim/testbench_register_file.v | 111 ------------------------ sim/testbenches/Makefile | 42 +++++++++ sim/testbenches/src/testbench_alu.v | 84 ++++++++++++++++++ sim/testbenches/src/testbench_cpu.v | 68 +++++++++++++++ sim/testbenches/src/testbench_register_file.v | 111 ++++++++++++++++++++++++ 11 files changed, 510 insertions(+), 263 deletions(-) create mode 100644 sim/gentestvec/Makefile create mode 100644 sim/gentestvec/src/alu.c create mode 100644 sim/gentestvec/src/cpu.c create mode 100644 sim/gentestvec/src/register_file.c delete mode 100644 sim/testbench_alu.v delete mode 100644 sim/testbench_cpu.v delete mode 100644 sim/testbench_register_file.v create mode 100644 sim/testbenches/Makefile create mode 100644 sim/testbenches/src/testbench_alu.v create mode 100644 sim/testbenches/src/testbench_cpu.v create mode 100644 sim/testbenches/src/testbench_register_file.v (limited to 'sim') diff --git a/sim/gentestvec/Makefile b/sim/gentestvec/Makefile new file mode 100644 index 0000000..a546b0d --- /dev/null +++ b/sim/gentestvec/Makefile @@ -0,0 +1,33 @@ +BUILD_DIR ?= build + +# tools +CC = clang + +# flags +CFLAGS = -O3 + +# dirs and files +SOURCE_DIR = src +SOURCES = $(wildcard $(SOURCE_DIR)/*.c) +GENTESTVECS = $(patsubst $(SOURCE_DIR)/%.c, $(BUILD_DIR)/gentestvec_%, $(SOURCES)) +TESTVECS = $(patsubst $(BUILD_DIR)/gentestvec_%, $(BUILD_DIR)/testvec_%.txt, $(GENTESTVECS)) + + +# targets +all: $(TESTVECS) + +# build testvec generator executables +$(BUILD_DIR)/gentestvec_%: $(SOURCE_DIR)/%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -o $@ $< + +# generate testvecs +$(BUILD_DIR)/testvec_%.txt: $(BUILD_DIR)/gentestvec_% + $< > $@ + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +clean: + rm -rf $(BUILD_DIR) + +.PHONY: all clean \ No newline at end of file diff --git a/sim/gentestvec/src/alu.c b/sim/gentestvec/src/alu.c new file mode 100644 index 0000000..4a11a74 --- /dev/null +++ b/sim/gentestvec/src/alu.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include + +typedef enum { + ADD = 0b0000, + SUB = 0b0001, + SLT = 0b0010, + SLTU = 0b0011, + + AND = 0b0100, + OR = 0b0101, + XOR = 0b0110, + + SLL = 0b1000, + SRL = 0b1001, + SRA = 0b1011, +} OP; + +void test_op(OP op, uint32_t a, uint32_t b) { + uint32_t result; + bool zero; + + switch (op) { + case ADD: + result = a + b; + break; + case SUB: + result = a - b; + break; + case SLT: + result = (int32_t)a < (int32_t)b; + break; + case SLTU: + result = a < b; + break; + + case AND: + result = a & b; + break; + case OR: + result = a | b; + break; + case XOR: + result = a ^ b; + break; + + case SLL: + result = a << b % 32; + break; + case SRL: + result = a >> b % 32; + break; + case SRA: + result = ((int32_t)a) >> b % 32; + break; + } + + zero = result == 0; + + printf("%01X__%08X_%08X__%08X_%01X\n", op & 0x0f, a, b, result, zero); +} + +void test_op_random(OP op, int num) { + for (int i = 0; i < num; ++i) { + uint32_t a = (rand() << 16) | rand(); + uint32_t b = (rand() << 16) | rand(); + test_op(op, a, b); + } +} + +int main(int argc, const char *argv[]) { + srand(time(NULL)); + + test_op_random(ADD, 1000); + test_op(ADD, 0x00000000, 0x00000000); + test_op(ADD, 0xffffffff, 0xffffffff); + test_op(ADD, 0xffffffff, 0x00000001); + test_op_random(SUB, 1000); + test_op(SUB, 0xffffffff, 0xffffffff); + test_op_random(SLT, 1000); + test_op(SLT, 0x8fffffff, 0xffffffff); + test_op(SLT, 0xffffffff, 0x00000001); + test_op(SLT, 0x00000001, 0xffffffff); + test_op_random(SLTU, 1000); + + test_op_random(OR, 1000); + test_op(OR, 0x00000000, 0x00000000); + test_op(OR, 0xffffffff, 0x00000000); + test_op(OR, 0x00000000, 0xffffffff); + test_op(OR, 0xffffffff, 0xffffffff); + test_op_random(AND, 1000); + test_op(AND, 0x00000000, 0x00000000); + test_op(AND, 0xffffffff, 0x00000000); + test_op(AND, 0x00000000, 0xffffffff); + test_op(AND, 0xffffffff, 0xffffffff); + test_op_random(XOR, 1000); + test_op(XOR, 0x00000000, 0x00000000); + test_op(XOR, 0xffffffff, 0x00000000); + test_op(XOR, 0x00000000, 0xffffffff); + test_op(XOR, 0xffffffff, 0xffffffff); + + test_op_random(SLL, 1000); + test_op(SLL, 0x0000000f, 0x00000004); + test_op(SLL, 0xffffffff, 0x0000001c); + test_op(SLL, 0xf0000000, 0x00000002); + test_op(SLL, 0x01234567, 0x00000001); + test_op_random(SRL, 1000); + test_op(SRL, 0xf0000000, 0x0000001c); + test_op(SRL, 0x0000000f, 0x0000004); + test_op_random(SRA, 1000); + test_op(SRA, 0xf0000000, 0x0000001c); + test_op(SRA, 0x0000000f, 0x0000004); + + return 0; +} diff --git a/sim/gentestvec/src/cpu.c b/sim/gentestvec/src/cpu.c new file mode 100644 index 0000000..8500dd9 --- /dev/null +++ b/sim/gentestvec/src/cpu.c @@ -0,0 +1,6 @@ +#include +#include +#include +#include + +int main(int argc, const char *argv[]) { return 0; } diff --git a/sim/gentestvec/src/register_file.c b/sim/gentestvec/src/register_file.c new file mode 100644 index 0000000..5a50d9b --- /dev/null +++ b/sim/gentestvec/src/register_file.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +uint32_t registers[32]; + +uint32_t read_reg(uint32_t addr) { return addr == 0 ? 0 : registers[addr]; } + +void write_reg(uint32_t addr, uint32_t data, bool we) { + if (addr != 0 && we) + registers[addr] = data; +} + +void test(uint32_t addr_rs0, uint32_t addr_rs1, uint32_t addr_rd2, + uint32_t data_rd2, bool we) { + + write_reg(addr_rd2, data_rd2, we); + uint32_t data_rs0 = read_reg(addr_rs0); + uint32_t data_rs1 = read_reg(addr_rs1); + printf("%08X_%08X__%08X_%08X__%08X_%08X_%01X\n", addr_rs0, data_rs0, addr_rs1, + data_rs1, addr_rd2, data_rd2, we); +} + +void tests(int num) { + for (int i = 0; i < num; ++i) { + uint32_t addr_rs0 = ((uint32_t)((rand() << 16) | rand())) % 32; + uint32_t addr_rs1 = ((uint32_t)((rand() << 16) | rand())) % 32; + uint32_t addr_rd2 = ((uint32_t)((rand() << 16) | rand())) % 32; + uint32_t data_rd2 = ((uint32_t)((rand() << 16) | rand())); + bool we = ((uint32_t)rand()) % 2; + test(addr_rs0, addr_rs1, addr_rd2, data_rd2, we); + } +} + +int main(int argc, const char *argv[]) { + srand(time(NULL)); + for (int i = 0; i < 32; ++i) + registers[0] = 0; + + for (int i = 0; i < 32; ++i) + test(i, i, 0, 0, false); + + for (int i = 0; i < 32; ++i) + test(i, i, i, 0xffffffff, true); + + tests(10000); + return 0; +} diff --git a/sim/testbench_alu.v b/sim/testbench_alu.v deleted file mode 100644 index f011ed2..0000000 --- a/sim/testbench_alu.v +++ /dev/null @@ -1,84 +0,0 @@ -`timescale 1ns / 1ps - -module testbench_alu(); - - reg reset = 0; - - reg [1023:0] testvec_filename; - reg [1023:0] waveform_filename; - - initial begin - if ($value$plusargs("testvec=%s", testvec_filename)) begin - end else begin - $display("ERROR: testvec not specified"); - $finish; - end - - if ($value$plusargs("waveform=%s", waveform_filename)) begin - end else begin - $display("ERROR: waveform not specified"); - $finish; - end - end - - initial begin - $dumpfile(waveform_filename); - $dumpvars(0,testbench_alu); - end - - reg clk = 0; - always #32 clk = !clk; - - - reg [31:0] a, b, exp_result; - reg [3:0] op; - reg [3:0] exp_flags; - wire [31:0] result; - wire zero, exp_zero; - - assign exp_zero = exp_flags[0]; - - reg [31:0] alu_test_count, alu_error_count; - reg [103:0] alu_testvec [0:20000]; - - initial begin - #5; - $readmemh(testvec_filename, alu_testvec); - alu_test_count = 0; - alu_error_count = 0; - end - - always @ (posedge clk) begin - #16; - {op, a, b, exp_result, exp_flags} = alu_testvec[alu_test_count]; - #32; - if ((result !== exp_result) | (zero !== exp_zero)) begin - $display("ERROR (ALU) time: %5d, test: %d", $time, alu_test_count); - $display(" op: %b, a: %h b: %h", op, a, b); - $display(" result: %h (expected %h)", result, exp_result); - $display(" zero: %b (expected %b)", zero, exp_zero); - alu_error_count = alu_error_count + 1; - end - - alu_test_count = alu_test_count + 1; - - if ((alu_test_count == 10027)) begin - $display("FINISHED (ALU), with %d errors out of %d tests.", alu_error_count, alu_test_count); - #16; - - $finish; - end - end - - - - - alu alu ( - .a(a), - .b(b), - .op(op), - .result(result), - .zero(zero) - ); - -endmodule diff --git a/sim/testbench_cpu.v b/sim/testbench_cpu.v deleted file mode 100644 index ba40dc2..0000000 --- a/sim/testbench_cpu.v +++ /dev/null @@ -1,68 +0,0 @@ -`timescale 1ns / 1ps - -module testbench_register_file(); - -reg clk; -reg rst; - -reg [31:0] io_in; -wire [31:0] io_out; - -cpu cpu ( - .clk(clk), - .rstn(!rst), - .io_in(io_in), - .io_out(io_out) -); - -integer file, r, eof; -reg [100*8:1] line; -reg [31:0] clk_cycle_count; - - -always #5 clk = ~clk; - -reg [1023:0] testvec_filename; -reg [1023:0] waveform_filename; - -initial begin - if ($value$plusargs("testvec=%s", testvec_filename)) begin - end else begin - $display("ERROR: testvec not specified"); - $finish; - end - - if ($value$plusargs("waveform=%s", waveform_filename)) begin - end else begin - $display("ERROR: waveform not specified"); - $finish; - end -end - -initial begin - $dumpfile(waveform_filename); - $dumpvars(0,testbench_register_file); -end - - -initial begin - clk = 0; - rst = 0; - - clk_cycle_count = 0; - - @(negedge clk); - rst = 1; - @(negedge clk); - rst = 0; - - - while (1) begin - @(posedge clk); - clk_cycle_count = clk_cycle_count + 1; - if (clk_cycle_count == 10000) $finish; - end -end - -endmodule - diff --git a/sim/testbench_register_file.v b/sim/testbench_register_file.v deleted file mode 100644 index c1ca542..0000000 --- a/sim/testbench_register_file.v +++ /dev/null @@ -1,111 +0,0 @@ -`timescale 1ns / 1ps - -module testbench_register_file(); - -reg clk; -reg rst; -reg we; -reg [4:0] addr_rs0, addr_rs1, addr_rd2; -reg [31:0] data_rd2; -wire [31:0] data_rs0, data_rs1; - - -register_file uut ( - .clk(clk), - .rstn(!rst), - .we(we), - .ra1(addr_rs0), - .ra2(addr_rs1), - .wa3(addr_rd2), - .rd1(data_rs0), - .rd2(data_rs1), - .wd3(data_rd2) -); - -integer file, r, eof; -reg [100*8:1] line; -reg [31:0] test_count, error_count; - -reg [31:0] expected_data_rs0, expected_data_rs1; - -always #5 clk = ~clk; - -reg [1023:0] testvec_filename; -reg [1023:0] waveform_filename; - -integer i; -initial begin - if ($value$plusargs("testvec=%s", testvec_filename)) begin - end else begin - $display("ERROR: testvec not specified"); - $finish; - end - - if ($value$plusargs("waveform=%s", waveform_filename)) begin - end else begin - $display("ERROR: waveform not specified"); - $finish; - end -end - - initial begin - $dumpfile(waveform_filename); - $dumpvars(0,testbench_register_file); - end - - -initial begin - clk = 0; - rst = 0; - we = 0; - addr_rs0 = 0; - addr_rs1 = 0; - addr_rd2 = 0; - data_rd2 = 0; - - test_count = 0; - error_count = 0; - - rst = 1; - @(posedge clk); - rst = 0; - - for (i = 0; i < 32; i = i + 1) begin - we = 1; - addr_rd2 = i; - data_rd2 = 32'b0; - @(posedge clk); - #1; - end - - file = $fopen(testvec_filename, "r"); - if (file == 0) begin - $display("ERROR: failed to open testvec"); - $finish; - end - - while (!$feof(file)) begin - eof = $fgets(line, file); - eof = $sscanf(line, "%8h_%8h__%8h_%8h__%8h_%8h_%1h", - addr_rs0, expected_data_rs0, - addr_rs1, expected_data_rs1, - addr_rd2, data_rd2, we); - @(posedge clk); - - @(negedge clk); - if (data_rs0 !== expected_data_rs0 || data_rs1 !== expected_data_rs1) begin - $display("ERROR (register_file), test %d: addr_rs0: %08h, addr_rs1: %08h, addr_rd2: %08h, data_rd2: %08h, we: %b", - test_count, addr_rs0, addr_rs1, addr_rd2, data_rd2, we); - $display(" data_rs0: %08h (expected: %08h)", data_rs0, expected_data_rs0); - $display(" data_rs1: %08h (expected: %08h)", data_rs1, expected_data_rs1); - error_count = error_count + 1; - end - test_count = test_count + 1; - end - $display("FINISHED (register_file) with %d errors out of %d tests", error_count, test_count); - $fclose(file); - $finish; -end - -endmodule - diff --git a/sim/testbenches/Makefile b/sim/testbenches/Makefile new file mode 100644 index 0000000..67c8f92 --- /dev/null +++ b/sim/testbenches/Makefile @@ -0,0 +1,42 @@ +BUILD_DIR ?= build +RTL_SOURCES ?= $(wildcard ../../rtl/src/*.v) +TESTVEC_DIR ?= ../../build +INCLUDE = ../../rtl + +TESTVECS ?= $(wildcard $(TESTVEC_DIR)/testvec_*.txt) + +# tools +IVERILOG = iverilog +VVP = vvp +GTKWAVE = gtkwave + +# dirs and files +SOURCE_DIR = src +SOURCES = $(wildcard $(SOURCE_DIR)/*.v) +WAVEFORMS = $(patsubst testvec_%, $(BUILD_DIR)/waveform_%.vcd, $(basename $(notdir $(TESTVECS)))) + + +# targets +all: $(WAVEFORMS) + +# generate testbenches +$(BUILD_DIR)/testbench_%: $(SOURCE_DIR)/testbench_%.v $(RTL_SOURCES) | $(BUILD_DIR) always + $(IVERILOG) -I $(INCLUDE) -o $@ $^ + +# run testbenches +$(BUILD_DIR)/waveform_%.vcd: $(BUILD_DIR)/testbench_% always + $(VVP) $< +testvec=$(shell realpath $(TESTVEC_DIR)/testvec_$*.txt) +waveform=$@ + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +wave: all + $(GTKWAVE) -a ../../debug/cpu.gtkw $(BUILD_DIR)/waveform_cpu.vcd > /dev/null 2>&1 & disown + +# dummy target, s.t. other targets will always be run +always: + +clean: + rm -rf $(BUILD_DIR) + +.PHONY: all clean always \ No newline at end of file diff --git a/sim/testbenches/src/testbench_alu.v b/sim/testbenches/src/testbench_alu.v new file mode 100644 index 0000000..f011ed2 --- /dev/null +++ b/sim/testbenches/src/testbench_alu.v @@ -0,0 +1,84 @@ +`timescale 1ns / 1ps + +module testbench_alu(); + + reg reset = 0; + + reg [1023:0] testvec_filename; + reg [1023:0] waveform_filename; + + initial begin + if ($value$plusargs("testvec=%s", testvec_filename)) begin + end else begin + $display("ERROR: testvec not specified"); + $finish; + end + + if ($value$plusargs("waveform=%s", waveform_filename)) begin + end else begin + $display("ERROR: waveform not specified"); + $finish; + end + end + + initial begin + $dumpfile(waveform_filename); + $dumpvars(0,testbench_alu); + end + + reg clk = 0; + always #32 clk = !clk; + + + reg [31:0] a, b, exp_result; + reg [3:0] op; + reg [3:0] exp_flags; + wire [31:0] result; + wire zero, exp_zero; + + assign exp_zero = exp_flags[0]; + + reg [31:0] alu_test_count, alu_error_count; + reg [103:0] alu_testvec [0:20000]; + + initial begin + #5; + $readmemh(testvec_filename, alu_testvec); + alu_test_count = 0; + alu_error_count = 0; + end + + always @ (posedge clk) begin + #16; + {op, a, b, exp_result, exp_flags} = alu_testvec[alu_test_count]; + #32; + if ((result !== exp_result) | (zero !== exp_zero)) begin + $display("ERROR (ALU) time: %5d, test: %d", $time, alu_test_count); + $display(" op: %b, a: %h b: %h", op, a, b); + $display(" result: %h (expected %h)", result, exp_result); + $display(" zero: %b (expected %b)", zero, exp_zero); + alu_error_count = alu_error_count + 1; + end + + alu_test_count = alu_test_count + 1; + + if ((alu_test_count == 10027)) begin + $display("FINISHED (ALU), with %d errors out of %d tests.", alu_error_count, alu_test_count); + #16; + + $finish; + end + end + + + + + alu alu ( + .a(a), + .b(b), + .op(op), + .result(result), + .zero(zero) + ); + +endmodule diff --git a/sim/testbenches/src/testbench_cpu.v b/sim/testbenches/src/testbench_cpu.v new file mode 100644 index 0000000..ba40dc2 --- /dev/null +++ b/sim/testbenches/src/testbench_cpu.v @@ -0,0 +1,68 @@ +`timescale 1ns / 1ps + +module testbench_register_file(); + +reg clk; +reg rst; + +reg [31:0] io_in; +wire [31:0] io_out; + +cpu cpu ( + .clk(clk), + .rstn(!rst), + .io_in(io_in), + .io_out(io_out) +); + +integer file, r, eof; +reg [100*8:1] line; +reg [31:0] clk_cycle_count; + + +always #5 clk = ~clk; + +reg [1023:0] testvec_filename; +reg [1023:0] waveform_filename; + +initial begin + if ($value$plusargs("testvec=%s", testvec_filename)) begin + end else begin + $display("ERROR: testvec not specified"); + $finish; + end + + if ($value$plusargs("waveform=%s", waveform_filename)) begin + end else begin + $display("ERROR: waveform not specified"); + $finish; + end +end + +initial begin + $dumpfile(waveform_filename); + $dumpvars(0,testbench_register_file); +end + + +initial begin + clk = 0; + rst = 0; + + clk_cycle_count = 0; + + @(negedge clk); + rst = 1; + @(negedge clk); + rst = 0; + + + while (1) begin + @(posedge clk); + clk_cycle_count = clk_cycle_count + 1; + if (clk_cycle_count == 10000) $finish; + end +end + +endmodule + diff --git a/sim/testbenches/src/testbench_register_file.v b/sim/testbenches/src/testbench_register_file.v new file mode 100644 index 0000000..c1ca542 --- /dev/null +++ b/sim/testbenches/src/testbench_register_file.v @@ -0,0 +1,111 @@ +`timescale 1ns / 1ps + +module testbench_register_file(); + +reg clk; +reg rst; +reg we; +reg [4:0] addr_rs0, addr_rs1, addr_rd2; +reg [31:0] data_rd2; +wire [31:0] data_rs0, data_rs1; + + +register_file uut ( + .clk(clk), + .rstn(!rst), + .we(we), + .ra1(addr_rs0), + .ra2(addr_rs1), + .wa3(addr_rd2), + .rd1(data_rs0), + .rd2(data_rs1), + .wd3(data_rd2) +); + +integer file, r, eof; +reg [100*8:1] line; +reg [31:0] test_count, error_count; + +reg [31:0] expected_data_rs0, expected_data_rs1; + +always #5 clk = ~clk; + +reg [1023:0] testvec_filename; +reg [1023:0] waveform_filename; + +integer i; +initial begin + if ($value$plusargs("testvec=%s", testvec_filename)) begin + end else begin + $display("ERROR: testvec not specified"); + $finish; + end + + if ($value$plusargs("waveform=%s", waveform_filename)) begin + end else begin + $display("ERROR: waveform not specified"); + $finish; + end +end + + initial begin + $dumpfile(waveform_filename); + $dumpvars(0,testbench_register_file); + end + + +initial begin + clk = 0; + rst = 0; + we = 0; + addr_rs0 = 0; + addr_rs1 = 0; + addr_rd2 = 0; + data_rd2 = 0; + + test_count = 0; + error_count = 0; + + rst = 1; + @(posedge clk); + rst = 0; + + for (i = 0; i < 32; i = i + 1) begin + we = 1; + addr_rd2 = i; + data_rd2 = 32'b0; + @(posedge clk); + #1; + end + + file = $fopen(testvec_filename, "r"); + if (file == 0) begin + $display("ERROR: failed to open testvec"); + $finish; + end + + while (!$feof(file)) begin + eof = $fgets(line, file); + eof = $sscanf(line, "%8h_%8h__%8h_%8h__%8h_%8h_%1h", + addr_rs0, expected_data_rs0, + addr_rs1, expected_data_rs1, + addr_rd2, data_rd2, we); + @(posedge clk); + + @(negedge clk); + if (data_rs0 !== expected_data_rs0 || data_rs1 !== expected_data_rs1) begin + $display("ERROR (register_file), test %d: addr_rs0: %08h, addr_rs1: %08h, addr_rd2: %08h, data_rd2: %08h, we: %b", + test_count, addr_rs0, addr_rs1, addr_rd2, data_rd2, we); + $display(" data_rs0: %08h (expected: %08h)", data_rs0, expected_data_rs0); + $display(" data_rs1: %08h (expected: %08h)", data_rs1, expected_data_rs1); + error_count = error_count + 1; + end + test_count = test_count + 1; + end + $display("FINISHED (register_file) with %d errors out of %d tests", error_count, test_count); + $fclose(file); + $finish; +end + +endmodule + -- cgit v1.2.3