diff --git a/doc/index.rst b/doc/index.rst index 1d6fef39..89b674f3 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -16,6 +16,7 @@ Ibex User Manual exception_interrupts interrupts debug + tracer rvfi diff --git a/doc/introduction.rst b/doc/introduction.rst index 5a118a10..ccaa1a03 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -48,6 +48,7 @@ The control and status registers are explained in :ref:`cs-registers`. :ref:`performance-counters` gives an overview of the performance monitors and event counters available in Ibex. :ref:`exceptions-interrupts` deals with the infrastructure for handling exceptions and interrupts, :ref:`debug-support` gives a brief overview on the debug infrastructure. +:ref:`tracer` gives a brief overview of the tracer module. For information regarding formal verification support, check out :ref:`rvfi`. diff --git a/doc/tracer.rst b/doc/tracer.rst new file mode 100644 index 00000000..1e1e918e --- /dev/null +++ b/doc/tracer.rst @@ -0,0 +1,11 @@ +.. _tracer: + +Tracer +====== + +The module ``ibex_tracer`` can be used to create a log of the executed instructions. +It is used by ``ibex_core_tracer`` which forwards the signals added by :ref:`rvfi` as an input for the tracer. + +.. note:: + + ``ibex_tracer`` is not compatible with Verilator. diff --git a/ibex_tracer.core b/ibex_tracer.core index 6c460838..49489fa2 100644 --- a/ibex_tracer.core +++ b/ibex_tracer.core @@ -11,9 +11,18 @@ filesets: files: - rtl/ibex_tracer_defines.sv - rtl/ibex_tracer.sv + - rtl/ibex_core_tracer.sv file_type: systemVerilogSource +parameters: + RVFI: + datatype: bool + paramtype: vlogdefine + description: Enable RVFI signals for tracing + targets: default: filesets: - files_rtl + parameters: + - RVFI=true diff --git a/rtl/ibex_core.sv b/rtl/ibex_core.sv index 4b61e650..ba5e17c0 100644 --- a/rtl/ibex_core.sv +++ b/rtl/ibex_core.sv @@ -747,56 +747,4 @@ module ibex_core #( assign rvfi_changed_pc = rvfi_pc_id_q != pc_id; `endif - -`ifndef VERILATOR -`ifdef TRACE_EXECUTION - ibex_tracer ibex_tracer_i ( - .clk_i ( clk_i ), // always-on clk for tracer - .rst_ni ( rst_ni ), - - .fetch_enable_i ( fetch_enable_i ), - .core_id_i ( core_id_i ), - .cluster_id_i ( cluster_id_i ), - - .pc_i ( id_stage_i.pc_id_i ), - .instr_i ( id_stage_i.instr_rdata_i ), - .compressed_i ( id_stage_i.instr_is_compressed_i ), - .id_valid_i ( id_stage_i.id_valid_o ), - .is_decoding_i ( id_stage_i.is_decoding_o ), - .is_branch_i ( id_stage_i.branch_in_id ), - .branch_taken_i ( id_stage_i.branch_set_q ), - .pipe_flush_i ( id_stage_i.controller_i.pipe_flush_i ), - .mret_insn_i ( id_stage_i.controller_i.mret_insn_i ), - .dret_insn_i ( id_stage_i.controller_i.dret_insn_i ), - .ecall_insn_i ( id_stage_i.controller_i.ecall_insn_i ), - .ebrk_insn_i ( id_stage_i.controller_i.ebrk_insn_i ), - .csr_status_i ( id_stage_i.controller_i.csr_status_i ), - .rs1_value_i ( id_stage_i.operand_a_fw_id ), - .rs2_value_i ( id_stage_i.operand_b_fw_id ), - - .lsu_value_i ( data_wdata_ex ), - - .ex_reg_addr_i ( id_stage_i.regfile_waddr ), - .ex_reg_we_i ( id_stage_i.regfile_we ), - .ex_reg_wdata_i ( id_stage_i.regfile_wdata ), - .lsu_data_valid_i ( lsu_data_valid ), - .ex_data_addr_i ( data_addr_o ), - .ex_data_req_i ( data_req_o ), - .ex_data_gnt_i ( data_gnt_i ), - .ex_data_we_i ( data_we_o ), - - .ex_data_wdata_i ( data_wdata_o ), - - .lsu_reg_wdata_i ( regfile_wdata_lsu ), - - .imm_i_type_i ( id_stage_i.imm_i_type ), - .imm_s_type_i ( id_stage_i.imm_s_type ), - .imm_b_type_i ( id_stage_i.imm_b_type ), - .imm_u_type_i ( id_stage_i.imm_u_type ), - .imm_j_type_i ( id_stage_i.imm_j_type ), - .zimm_rs1_type_i ( id_stage_i.zimm_rs1_type ) - ); -`endif -`endif - endmodule diff --git a/rtl/ibex_core_tracer.sv b/rtl/ibex_core_tracer.sv new file mode 100644 index 00000000..973db61b --- /dev/null +++ b/rtl/ibex_core_tracer.sv @@ -0,0 +1,180 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + + +// ibex_tracer relies on the signals from the RISC-V Formal Interface +`define RVFI + +/** + * Top level module of the ibex RISC-V core with tracing enabled + */ +module ibex_core_tracer #( + parameter int unsigned MHPMCounterNum = 8, + parameter int unsigned MHPMCounterWidth = 40, + parameter bit RV32E = 0, + parameter bit RV32M = 1, + parameter int unsigned DmHaltAddr = 32'h1A110800, + parameter int unsigned DmExceptionAddr = 32'h1A110808 +) ( + // Clock and Reset + input logic clk_i, + input logic rst_ni, + + input logic test_en_i, // enable all clock gates for testing + + // Core ID, Cluster ID and boot address are considered more or less static + input logic [ 3:0] core_id_i, + input logic [ 5:0] cluster_id_i, + input logic [31:0] boot_addr_i, + + // Instruction memory interface + output logic instr_req_o, + input logic instr_gnt_i, + input logic instr_rvalid_i, + output logic [31:0] instr_addr_o, + input logic [31:0] instr_rdata_i, + + // Data memory interface + output logic data_req_o, + input logic data_gnt_i, + input logic data_rvalid_i, + output logic data_we_o, + output logic [3:0] data_be_o, + output logic [31:0] data_addr_o, + output logic [31:0] data_wdata_o, + input logic [31:0] data_rdata_i, + input logic data_err_i, + + // Interrupt inputs + input logic irq_i, // level sensitive IR lines + input logic [4:0] irq_id_i, + output logic irq_ack_o, // irq ack + output logic [4:0] irq_id_o, + + // Debug Interface + input logic debug_req_i, + + // RISC-V Formal Interface + // Does not comply with the coding standards of _i/_o suffixes, but follows + // the convention of RISC-V Formal Interface Specification. +`ifdef RVFI + output logic rvfi_valid, + output logic [63:0] rvfi_order, + output logic [31:0] rvfi_insn, + output logic rvfi_trap, + output logic rvfi_halt, + output logic rvfi_intr, + output logic [ 1:0] rvfi_mode, + output logic [ 4:0] rvfi_rs1_addr, + output logic [ 4:0] rvfi_rs2_addr, + output logic [31:0] rvfi_rs1_rdata, + output logic [31:0] rvfi_rs2_rdata, + output logic [ 4:0] rvfi_rd_addr, + output logic [31:0] rvfi_rd_wdata, + output logic [31:0] rvfi_pc_rdata, + output logic [31:0] rvfi_pc_wdata, + output logic [31:0] rvfi_mem_addr, + output logic [ 3:0] rvfi_mem_rmask, + output logic [ 3:0] rvfi_mem_wmask, + output logic [31:0] rvfi_mem_rdata, + output logic [31:0] rvfi_mem_wdata, +`endif + + // CPU Control Signals + input logic fetch_enable_i + +); + + import ibex_defines::*; + + ibex_core #( + .MHPMCounterNum(MHPMCounterNum), + .MHPMCounterWidth(MHPMCounterWidth), + .RV32E(RV32E), + .RV32M(RV32M), + .DmHaltAddr(DmHaltAddr), + .DmExceptionAddr(DmExceptionAddr) + ) u_ibex_core ( + .clk_i, + .rst_ni, + + .test_en_i, + + .core_id_i, + .cluster_id_i, + .boot_addr_i, + + .instr_req_o, + .instr_gnt_i, + .instr_rvalid_i, + .instr_addr_o, + .instr_rdata_i, + + .data_req_o, + .data_gnt_i, + .data_rvalid_i, + .data_we_o, + .data_be_o, + .data_addr_o, + .data_wdata_o, + .data_rdata_i, + .data_err_i, + + .irq_i, + .irq_id_i, + .irq_ack_o, + .irq_id_o, + + .debug_req_i, + +`ifdef RVFI + .rvfi_valid, + .rvfi_order, + .rvfi_insn, + .rvfi_trap, + .rvfi_halt, + .rvfi_intr, + .rvfi_mode, + .rvfi_rs1_addr, + .rvfi_rs2_addr, + .rvfi_rs1_rdata, + .rvfi_rs2_rdata, + .rvfi_rd_addr, + .rvfi_rd_wdata, + .rvfi_pc_rdata, + .rvfi_pc_wdata, + .rvfi_mem_addr, + .rvfi_mem_rmask, + .rvfi_mem_wmask, + .rvfi_mem_rdata, + .rvfi_mem_wdata, +`endif + + .fetch_enable_i + ); + + +`ifndef VERILATOR + ibex_tracer ibex_tracer_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .fetch_enable_i ( fetch_enable_i ), + .core_id_i ( core_id_i ), + .cluster_id_i ( cluster_id_i ), + + .valid_i ( rvfi_valid ), + .pc_i ( rvfi_pc_rdata ), + .instr_i ( rvfi_insn ), + .rs1_value_i ( rvfi_rs1_rdata ), + .rs2_value_i ( rvfi_rs2_rdata ), + .ex_reg_addr_i ( rvfi_rd_addr ), + .ex_reg_wdata_i ( rvfi_rd_wdata ), + .ex_data_addr_i ( rvfi_mem_addr ), + .ex_data_wdata_i ( rvfi_mem_wdata ), + .ex_data_rdata_i ( rvfi_mem_rdata ) + ); +`endif // VERILATOR + +endmodule diff --git a/rtl/ibex_id_stage.sv b/rtl/ibex_id_stage.sv index b6cb128a..2774ef7c 100644 --- a/rtl/ibex_id_stage.sv +++ b/rtl/ibex_id_stage.sv @@ -228,10 +228,6 @@ module ibex_id_stage #( // CSR control logic csr_status; - // For tracer - logic [31:0] operand_a_fw_id, unused_operand_a_fw_id; - logic [31:0] operand_b_fw_id, unused_operand_b_fw_id; - logic [31:0] alu_operand_a; logic [31:0] alu_operand_b; @@ -286,13 +282,6 @@ module ibex_id_stage #( // ALU MUX for Operand B assign alu_operand_b = (alu_op_b_mux_sel == OP_B_IMM) ? imm_b : regfile_rdata_b; - // Signals used by tracer - assign operand_a_fw_id = lsu_addr_incr_req_i ? lsu_addr_last_i : regfile_rdata_a; - assign operand_b_fw_id = regfile_rdata_b; - - assign unused_operand_a_fw_id = operand_a_fw_id; - assign unused_operand_b_fw_id = operand_b_fw_id; - /////////////////////// // Register File MUX // /////////////////////// diff --git a/rtl/ibex_tracer.sv b/rtl/ibex_tracer.sv index 41ee14ba..a1def12f 100644 --- a/rtl/ibex_tracer.sv +++ b/rtl/ibex_tracer.sv @@ -42,41 +42,16 @@ module ibex_tracer #( input logic [3:0] core_id_i, input logic [5:0] cluster_id_i, + input logic valid_i, input logic [31:0] pc_i, input logic [31:0] instr_i, - input logic compressed_i, - input logic id_valid_i, - input logic is_decoding_i, - input logic is_branch_i, - input logic branch_taken_i, - input logic pipe_flush_i, - input logic mret_insn_i, - input logic dret_insn_i, - input logic ecall_insn_i, - input logic ebrk_insn_i, - input logic csr_status_i, input logic [31:0] rs1_value_i, input logic [31:0] rs2_value_i, - input logic [31:0] lsu_value_i, - input logic [(RegAddrWidth-1):0] ex_reg_addr_i, - input logic ex_reg_we_i, input logic [31:0] ex_reg_wdata_i, - input logic lsu_data_valid_i, - input logic ex_data_req_i, - input logic ex_data_gnt_i, - input logic ex_data_we_i, input logic [31:0] ex_data_addr_i, input logic [31:0] ex_data_wdata_i, - - input logic [31:0] lsu_reg_wdata_i, - - input logic [31:0] imm_i_type_i, - input logic [31:0] imm_s_type_i, - input logic [31:0] imm_b_type_i, - input logic [31:0] imm_u_type_i, - input logic [31:0] imm_j_type_i, - input logic [31:0] zimm_rs1_type_i + input logic [31:0] ex_data_rdata_i ); integer f; @@ -135,20 +110,26 @@ module ibex_tracer #( foreach(regs_write[i]) begin if (regs_write[i].addr != 0) begin - $fwrite(f, " %s=%08x", regAddrToStr(regs_write[i].addr), regs_write[i].value); + $fwrite(f, " %s=0x%08x", regAddrToStr(regs_write[i].addr), regs_write[i].value); end end foreach(regs_read[i]) begin if (regs_read[i].addr != 0) begin - $fwrite(f, " %s:%08x", regAddrToStr(regs_read[i].addr), regs_read[i].value); + $fwrite(f, " %s:0x%08x", regAddrToStr(regs_read[i].addr), regs_read[i].value); end end if (mem_access.size() > 0) begin mem_acc = mem_access.pop_front(); - $fwrite(f, " PA:%08x", mem_acc.addr); + $fwrite(f, " PA:0x%08x", mem_acc.addr); + + if (mem_acc.we == 1'b1) begin + $fwrite(f, " store:0x%08x", mem_acc.wdata); + end else begin + $fwrite(f, " load:0x%08x", mem_acc.rdata); + end end $fwrite(f, "\n"); @@ -174,7 +155,7 @@ module ibex_tracer #( begin regs_read.push_back('{rs1, rs1_value_i}); regs_write.push_back('{rd, 'x}); - str = $sformatf("%-16s x%0d, x%0d, %0d", mnemonic, rd, rs1, $signed(imm_i_type_i)); + str = $sformatf("%-16s x%0d, x%0d, %0d", mnemonic, rd, rs1, $signed({{20 {instr[31]}}, instr[31:20]})); end endfunction // printIInstr @@ -182,21 +163,21 @@ module ibex_tracer #( begin regs_read.push_back('{rs1, rs1_value_i}); regs_write.push_back('{rd, 'x}); - str = $sformatf("%-16s x%0d, x%0d, 0x%0x", mnemonic, rd, rs1, imm_i_type_i); + str = $sformatf("%-16s x%0d, x%0d, 0x%0x", mnemonic, rd, rs1, {{20 {instr[31]}}, instr[31:20]}); end endfunction // printIuInstr function void printUInstr(input string mnemonic); begin regs_write.push_back('{rd, 'x}); - str = $sformatf("%-16s x%0d, 0x%0h", mnemonic, rd, {imm_u_type_i[31:12], 12'h000}); + str = $sformatf("%-16s x%0d, 0x%0h", mnemonic, rd, {instr[31:12], 12'h000}); end endfunction // printUInstr function void printUJInstr(input string mnemonic); begin regs_write.push_back('{rd, 'x}); - str = $sformatf("%-16s x%0d, %0d", mnemonic, rd, $signed(imm_j_type_i)); + str = $sformatf("%-16s x%0d, %0d", mnemonic, rd, $signed({ {12 {instr[31]}}, instr[19:12], instr[20], instr[30:21], 1'b0 })); end endfunction // printUJInstr @@ -204,7 +185,7 @@ module ibex_tracer #( begin regs_read.push_back('{rs1, rs1_value_i}); regs_read.push_back('{rs2, rs2_value_i}); - str = $sformatf("%-16s x%0d, x%0d, %0d", mnemonic, rs1, rs2, $signed(imm_b_type_i)); + str = $sformatf("%-16s x%0d, x%0d, %0d", mnemonic, rs1, rs2, $signed({ {19 {instr[31]}}, instr[31], instr[7], instr[30:25], instr[11:8], 1'b0 })); end endfunction // printSBInstr @@ -219,14 +200,15 @@ module ibex_tracer #( regs_read.push_back('{rs1, rs1_value_i}); str = $sformatf("%-16s x%0d, x%0d, 0x%h", mnemonic, rd, rs1, csr); end else begin - str = $sformatf("%-16s x%0d, 0x%h, 0x%h", mnemonic, rd, zimm_rs1_type_i, csr); + str = $sformatf("%-16s x%0d, 0x%h, 0x%h", mnemonic, rd, { 27'b0, instr[`REG_S1] }, csr); end end endfunction // printCSRInstr function void printLoadInstr(); - string mnemonic; + string mnemonic; logic [2:0] size; + mem_acc_t mem_acc; begin // detect reg-reg load and find size size = instr_i[14:12]; @@ -257,15 +239,20 @@ module ibex_tracer #( if (instr_i[14:12] != 3'b111) begin // regular load regs_read.push_back('{rs1, rs1_value_i}); - str = $sformatf("%-16s x%0d, %0d(x%0d)", mnemonic, rd, $signed(imm_i_type_i), rs1); + str = $sformatf("%-16s x%0d, %0d(x%0d)", mnemonic, rd, $signed({{20 {instr[31]}}, instr[31:20]}), rs1); end else begin printMnemonic("INVALID"); end + + mem_acc.addr = ex_data_addr_i; + mem_acc.rdata = ex_data_rdata_i; + mem_access.push_back(mem_acc); end endfunction function void printStoreInstr(); - string mnemonic; + string mnemonic; + mem_acc_t mem_acc; begin unique case (instr_i[13:12]) @@ -286,10 +273,15 @@ module ibex_tracer #( // regular store regs_read.push_back('{rs2, rs2_value_i}); regs_read.push_back('{rs1, rs1_value_i}); - str = $sformatf("%-16s x%0d, %0d(x%0d)", mnemonic, rs2, $signed(imm_s_type_i), rs1); + str = $sformatf("%-16s x%0d, %0d(x%0d)", mnemonic, rs2, $signed({ {20 {instr[31]}}, instr[31:25], instr[11:7] }), rs1); end else begin printMnemonic("INVALID"); end + + mem_acc.addr = ex_data_addr_i; + mem_acc.we = 1'b1; + mem_acc.wdata = ex_data_wdata_i; + mem_access.push_back(mem_acc); end endfunction // printSInstr @@ -327,12 +319,11 @@ module ibex_tracer #( assign rs3 = instr_i[`REG_S3]; // log execution - always @(negedge clk_i) begin + always @(posedge clk_i) begin instr_trace_t trace; mem_acc_t mem_acc; // special case for WFI because we don't wait for unstalling there - if ((id_valid_i || mret_insn_i || ecall_insn_i || pipe_flush_i || ebrk_insn_i || - dret_insn_i || csr_status_i || ex_data_req_i) && is_decoding_i) begin + if (valid_i) begin trace = new (); trace.simtime = $time; @@ -410,38 +401,11 @@ module ibex_tracer #( // replace register written back foreach(trace.regs_write[i]) begin - if ((trace.regs_write[i].addr == ex_reg_addr_i) && ex_reg_we_i) begin + if ((trace.regs_write[i].addr == ex_reg_addr_i)) begin trace.regs_write[i].value = ex_reg_wdata_i; end end - // look for data accesses and log them - if (ex_data_req_i) begin - if (!ex_data_gnt_i) begin - //we wait until the the gnt comes - do @(negedge clk_i); - while (!ex_data_gnt_i); - end - - mem_acc.addr = ex_data_addr_i; - mem_acc.we = ex_data_we_i; - - if (mem_acc.we) begin - mem_acc.wdata = ex_data_wdata_i; - end else begin - mem_acc.wdata = 'x; - end - //we wait until the the data instruction ends - do @(negedge clk_i); - while (!lsu_data_valid_i); - - if (!mem_acc.we) begin - //load operations - foreach(trace.regs_write[i]) - trace.regs_write[i].value = lsu_reg_wdata_i; - end - trace.mem_access.push_back(mem_acc); - end trace.printInstrTrace(); end end // always @ (posedge clk_i)