diff --git a/.gitignore b/.gitignore index 846b1a5..4713dd8 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ /testbench_synth.vvp /testbench.gtkw /testbench.vcd +/testbench.trace /check.smt2 /check.vcd /synth.log diff --git a/Makefile b/Makefile index e1511fd..bb73fd9 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ test: testbench.vvp firmware/firmware.hex vvp -N testbench.vvp testbench.vcd: testbench.vvp firmware/firmware.hex - vvp -N $< +vcd + vvp -N $< +vcd +trace view: testbench.vcd gtkwave $< testbench.gtkw @@ -131,7 +131,7 @@ clean: riscv-gnu-toolchain-riscv32im riscv-gnu-toolchain-riscv32imc rm -vrf $(FIRMWARE_OBJS) $(TEST_OBJS) check.smt2 check.vcd synth.v synth.log \ firmware/firmware.elf firmware/firmware.bin firmware/firmware.hex firmware/firmware.map \ - testbench.vvp testbench_sp.vvp testbench_synth.vvp testbench.vcd + testbench.vvp testbench_sp.vvp testbench_synth.vvp testbench.vcd testbench.trace .PHONY: test view test_sp test_axi test_synth download-tools toc clean diff --git a/firmware/start.S b/firmware/start.S index e256cac..dda08ed 100644 --- a/firmware/start.S +++ b/firmware/start.S @@ -478,6 +478,10 @@ start: sw a4,0(a0) sw a5,0(a0) + li a0, 0x20000000 + li a1, 123456789 + sw a1,0(a0) + /* trap */ ebreak diff --git a/picorv32.v b/picorv32.v index 2d2cf94..a2dc8a2 100644 --- a/picorv32.v +++ b/picorv32.v @@ -58,6 +58,7 @@ module picorv32 #( parameter [ 0:0] ENABLE_IRQ = 0, parameter [ 0:0] ENABLE_IRQ_QREGS = 1, parameter [ 0:0] ENABLE_IRQ_TIMER = 1, + parameter [ 0:0] ENABLE_TRACE = 0, parameter [ 0:0] REGS_INIT_ZERO = 0, parameter [31:0] MASKED_IRQ = 32'h 0000_0000, parameter [31:0] LATCHED_IRQ = 32'h ffff_ffff, @@ -96,7 +97,11 @@ module picorv32 #( // IRQ Interface input [31:0] irq, - output reg [31:0] eoi + output reg [31:0] eoi, + + // Trace Interface + output reg trace_valid, + output reg [35:0] trace_data ); localparam integer irq_timer = 0; localparam integer irq_ebreak = 1; @@ -108,6 +113,10 @@ module picorv32 #( localparam WITH_PCPI = ENABLE_PCPI || ENABLE_MUL || ENABLE_DIV; + localparam [35:0] TRACE_BRANCH = {4'b 0001, 32'b 0}; + localparam [35:0] TRACE_ADDR = {4'b 0010, 32'b 0}; + localparam [35:0] TRACE_IRQ = {4'b 1000, 32'b 0}; + reg [63:0] count_cycle, count_instr; reg [31:0] reg_pc, reg_next_pc, reg_op1, reg_op2, reg_out; reg [31:0] cpuregs [0:regfile_size-1]; @@ -1016,6 +1025,7 @@ module picorv32 #( reg latched_stalu; reg latched_branch; reg latched_compr; + reg latched_trace; reg latched_is_lu; reg latched_is_lh; reg latched_is_lb; @@ -1155,6 +1165,9 @@ module picorv32 #( decoder_pseudo_trigger_q <= decoder_pseudo_trigger; do_waitirq <= 0; + if (ENABLE_TRACE) + trace_valid <= 0; + if (!resetn) begin reg_pc <= PROGADDR_RESET; reg_next_pc <= PROGADDR_RESET; @@ -1163,6 +1176,7 @@ module picorv32 #( latched_store <= 0; latched_stalu <= 0; latched_branch <= 0; + latched_trace <= 0; latched_is_lu <= 0; latched_is_lh <= 0; latched_is_lb <= 0; @@ -1218,6 +1232,15 @@ module picorv32 #( end endcase + if (ENABLE_TRACE && latched_trace) begin + latched_trace <= 0; + trace_valid <= 1; + if (latched_branch) + trace_data <= (irq_active ? TRACE_IRQ : 0) | TRACE_BRANCH | current_pc; + else + trace_data <= (irq_active ? TRACE_IRQ : 0) | (latched_stalu ? alu_out_q : reg_out); + end + reg_pc <= current_pc; reg_next_pc <= current_pc; @@ -1253,6 +1276,8 @@ module picorv32 #( `debug($display("-- %-0t", $time);) irq_delay <= irq_active; reg_next_pc <= current_pc + (compressed_instr ? 2 : 4); + if (ENABLE_TRACE) + latched_trace <= 1; if (ENABLE_COUNTERS) begin count_instr <= count_instr + 1; if (!ENABLE_COUNTERS64) count_instr[63:32] <= 0; @@ -1519,6 +1544,8 @@ module picorv32 #( end cpu_state_stmem: begin + if (ENABLE_TRACE) + reg_out <= reg_op2; if (!mem_do_prefetch || mem_done) begin if (!mem_do_wdata) begin (* parallel_case, full_case *) @@ -1527,6 +1554,10 @@ module picorv32 #( instr_sh: mem_wordsize <= 1; instr_sw: mem_wordsize <= 0; endcase + if (ENABLE_TRACE) begin + trace_valid <= 1; + trace_data <= (irq_active ? TRACE_IRQ : 0) | TRACE_ADDR | (reg_op1 + decoded_imm); + end reg_op1 <= reg_op1 + decoded_imm; set_mem_do_wdata = 1; end @@ -1551,6 +1582,10 @@ module picorv32 #( latched_is_lu <= is_lbu_lhu_lw; latched_is_lh <= instr_lh; latched_is_lb <= instr_lb; + if (ENABLE_TRACE) begin + trace_valid <= 1; + trace_data <= (irq_active ? TRACE_IRQ : 0) | TRACE_ADDR | (reg_op1 + decoded_imm); + end reg_op1 <= reg_op1 + decoded_imm; set_mem_do_rdata = 1; end @@ -1891,6 +1926,7 @@ module picorv32_axi #( parameter [ 0:0] ENABLE_IRQ = 0, parameter [ 0:0] ENABLE_IRQ_QREGS = 1, parameter [ 0:0] ENABLE_IRQ_TIMER = 1, + parameter [ 0:0] ENABLE_TRACE = 0, parameter [ 0:0] REGS_INIT_ZERO = 0, parameter [31:0] MASKED_IRQ = 32'h 0000_0000, parameter [31:0] LATCHED_IRQ = 32'h ffff_ffff, @@ -1936,7 +1972,11 @@ module picorv32_axi #( // IRQ interface input [31:0] irq, - output [31:0] eoi + output [31:0] eoi, + + // Trace Interface + output trace_valid, + output [35:0] trace_data ); wire mem_valid; wire [31:0] mem_addr; @@ -1993,6 +2033,7 @@ module picorv32_axi #( .ENABLE_IRQ (ENABLE_IRQ ), .ENABLE_IRQ_QREGS (ENABLE_IRQ_QREGS ), .ENABLE_IRQ_TIMER (ENABLE_IRQ_TIMER ), + .ENABLE_TRACE (ENABLE_TRACE ), .REGS_INIT_ZERO (REGS_INIT_ZERO ), .MASKED_IRQ (MASKED_IRQ ), .LATCHED_IRQ (LATCHED_IRQ ), @@ -2021,7 +2062,10 @@ module picorv32_axi #( .pcpi_ready(pcpi_ready), .irq(irq), - .eoi(eoi) + .eoi(eoi), + + .trace_valid(trace_valid), + .trace_data (trace_data) ); endmodule diff --git a/showtrace.py b/showtrace.py new file mode 100644 index 0000000..56c91ff --- /dev/null +++ b/showtrace.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +import sys, re, subprocess + +trace_filename = sys.argv[1] +elf_filename = sys.argv[2] + +insns = dict() + +with subprocess.Popen(["riscv32-unknown-elf-objdump", "-d", elf_filename], stdout=subprocess.PIPE) as proc: + while True: + line = proc.stdout.readline().decode("ascii") + if line == '': break + match = re.match(r'^\s*([0-9a-f]+):\s+(\S+)\s*(.*)', line) + if match: insns[int(match.group(1), 16)] = (int(match.group(2), 16), match.group(3).replace("\t", " ")) + +with open(trace_filename, "r") as f: + pc = -1 + last_irq = False + for line in f: + raw_data = int(line.replace("x", "0"), 16) + payload = raw_data & 0xffffffff + irq_active = (raw_data & 0x800000000) != 0 + is_addr = (raw_data & 0x200000000) != 0 + is_branch = (raw_data & 0x100000000) != 0 + + if irq_active and not last_irq: + pc = 0x10 + + if pc >= 0: + if pc in insns: + insn_opcode, insn_desc = insns[pc] + opcode_fmt = "%08x" if (insn_opcode & 3) == 3 else " %04x" + print(("%s %s%08x | %08x | " + opcode_fmt + " | %s") % ("IRQ" if irq_active else " ", + ">" if is_branch else "@" if is_addr else "=", payload, pc, insn_opcode, insn_desc)) + if not is_addr: + pc += 4 if (insn_opcode & 3) == 3 else 2 + else: + print("%s %s%08x ** NO INFORMATION ON INSN AT %08x! **" % ("IRQ" if irq_active else " ", + ">" if is_branch else "@" if is_addr else "=", payload, pc)) + pc = -1 + else: + print("%s %s%08x ** SKIPPING DATA UNTIL NEXT BRANCH **" % ("IRQ" if irq_active else " ", + ">" if is_branch else "@" if is_addr else "=", payload)) + + if is_branch: + pc = payload + + last_irq = irq_active + diff --git a/testbench.v b/testbench.v index 23e81c9..dc7a7f3 100644 --- a/testbench.v +++ b/testbench.v @@ -15,6 +15,7 @@ module testbench #( reg clk = 1; reg resetn = 0; + wire trap; always #5 clk = ~clk; @@ -33,12 +34,32 @@ module testbench #( $finish; end + wire trace_valid; + wire [35:0] trace_data; + integer trace_file; + + initial begin + if ($test$plusargs("trace")) begin + trace_file = $fopen("testbench.trace", "w"); + repeat (10) @(posedge clk); + while (!trap) begin + @(posedge clk); + if (trace_valid) + $fwrite(trace_file, "%x\n", trace_data); + end + $fclose(trace_file); + end + end + picorv32_wrapper #( .AXI_TEST (AXI_TEST), .VERBOSE (VERBOSE) ) top ( - .clk (clk ), - .resetn (resetn) + .clk(clk), + .resetn(resetn), + .trap(trap), + .trace_valid(trace_valid), + .trace_data(trace_data) ); endmodule `endif @@ -48,10 +69,14 @@ module picorv32_wrapper #( parameter VERBOSE = 0 ) ( input clk, - input resetn + input resetn, + output trap, + output trace_valid, + output [35:0] trace_data ); - wire trap; + wire trap; + wire tests_passed; reg [31:0] irq; always @* begin @@ -107,7 +132,9 @@ module picorv32_wrapper #( .mem_axi_rvalid (mem_axi_rvalid ), .mem_axi_rready (mem_axi_rready ), - .mem_axi_rdata (mem_axi_rdata ) + .mem_axi_rdata (mem_axi_rdata ), + + .tests_passed (tests_passed ) ); picorv32_axi #( @@ -119,7 +146,8 @@ module picorv32_wrapper #( `endif .ENABLE_MUL(1), .ENABLE_DIV(1), - .ENABLE_IRQ(1) + .ENABLE_IRQ(1), + .ENABLE_TRACE(1) ) uut ( .clk (clk ), .resetn (resetn ), @@ -141,7 +169,9 @@ module picorv32_wrapper #( .mem_axi_rvalid (mem_axi_rvalid ), .mem_axi_rready (mem_axi_rready ), .mem_axi_rdata (mem_axi_rdata ), - .irq (irq ) + .irq (irq ), + .trace_valid (trace_valid ), + .trace_data (trace_data ) ); reg [1023:0] firmware_file; @@ -159,7 +189,13 @@ module picorv32_wrapper #( repeat (10) @(posedge clk); `endif $display("TRAP after %1d clock cycles", cycle_counter); - $finish; + if (tests_passed) begin + $display("ALL TESTS PASSED."); + $finish; + end else begin + $display("ERROR!"); + $stop; + end end end endmodule @@ -189,7 +225,9 @@ module axi4_memory #( output reg mem_axi_rvalid = 0, input mem_axi_rready, - output reg [31:0] mem_axi_rdata + output reg [31:0] mem_axi_rdata, + + output reg tests_passed ); reg [31:0] memory [0:64*1024/4-1] /* verilator public */; @@ -199,6 +237,8 @@ module axi4_memory #( reg axi_test; initial axi_test = $test$plusargs("axi_test") || AXI_TEST; + initial tests_passed = 0; + reg [63:0] xorshift64_state = 64'd88172645463325252; task xorshift64_next; @@ -292,6 +332,10 @@ module axi4_memory #( $fflush(); `endif end + end else + if (latched_waddr == 32'h2000_0000) begin + if (latched_wdata == 123456789) + tests_passed = 1; end else begin $display("OUT-OF-BOUNDS MEMORY WRITE TO %08x", latched_waddr); $finish;