blob: b07b742c5066235b93ea07aa7a553a666725ce6e [file] [log] [blame]
// Copyright lowRISC contributors.
// Copyright 2018 ETH Zurich and University of Bologna, see also CREDITS.md.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
/**
* Trace executed instructions in simulation
*
* This tracer takes execution information from the RISC-V Verification Interface (RVFI) and
* produces a text file with a human-readable trace.
*
* All traced instructions are written to a log file. By default, the log file is named
* trace_core_<HARTID>.log, with <HARTID> being the 8 digit hart ID of the core being traced.
*
* The file name base, defaulting to "trace_core" can be set using the "ibex_tracer_file_base"
* plusarg passed to the simulation, e.g. "+ibex_tracer_file_base=ibex_my_trace". The exact syntax
* of passing plusargs to a simulation depends on the simulator.
*
* The creation of the instruction trace is enabled by default but can be disabled for a simulation.
* This behaviour is controlled by the plusarg "ibex_tracer_enable". Use "ibex_tracer_enable=0" to
* disable the tracer.
*
* The trace contains six columns, separated by tabs:
* - The simulation time
* - The clock cycle count since reset
* - The program counter (PC)
* - The instruction
* - The decoded instruction in the same format as objdump, together with the accessed registers and
* read/written memory values. Jumps and branches show the target address.
* This column may be omitted if the instruction does not decode into a long form.
* - Accessed registers and memory locations.
*
* Significant effort is spent to make the decoding produced by this tracer as similar as possible
* to the one produced by objdump. This simplifies the correlation between the static program
* information from the objdump-generated disassembly, and the runtime information from this tracer.
*/
module ibex_tracer (
input logic clk_i,
input logic rst_ni,
input logic [31:0] hart_id_i,
// RVFI as described at https://github.com/SymbioticEDA/riscv-formal/blob/master/docs/rvfi.md
// The standard interface does not have _i/_o suffixes. For consistency with the standard the
// signals in this module don't have the suffixes either.
input logic rvfi_valid,
input logic [63:0] rvfi_order,
input logic [31:0] rvfi_insn,
input logic rvfi_trap,
input logic rvfi_halt,
input logic rvfi_intr,
input logic [ 1:0] rvfi_mode,
input logic [ 1:0] rvfi_ixl,
input logic [ 4:0] rvfi_rs1_addr,
input logic [ 4:0] rvfi_rs2_addr,
input logic [ 4:0] rvfi_rs3_addr,
input logic [31:0] rvfi_rs1_rdata,
input logic [31:0] rvfi_rs2_rdata,
input logic [31:0] rvfi_rs3_rdata,
input logic [ 4:0] rvfi_rd_addr,
input logic [31:0] rvfi_rd_wdata,
input logic [31:0] rvfi_pc_rdata,
input logic [31:0] rvfi_pc_wdata,
input logic [31:0] rvfi_mem_addr,
input logic [ 3:0] rvfi_mem_rmask,
input logic [ 3:0] rvfi_mem_wmask,
input logic [31:0] rvfi_mem_rdata,
input logic [31:0] rvfi_mem_wdata
);
// These signals are part of RVFI, but not used in this module currently.
// Keep them as part of the interface to change the tracer more easily in the future. Assigning
// these signals to unused_* signals marks them explicitly as unused, an annotation picked up by
// linters, including Verilator lint.
logic [63:0] unused_rvfi_order = rvfi_order;
logic unused_rvfi_trap = rvfi_trap;
logic unused_rvfi_halt = rvfi_halt;
logic unused_rvfi_intr = rvfi_intr;
logic [ 1:0] unused_rvfi_mode = rvfi_mode;
logic [ 1:0] unused_rvfi_ixl = rvfi_ixl;
import ibex_tracer_pkg::*;
int file_handle;
string file_name;
int unsigned cycle;
string decoded_str;
logic insn_is_compressed;
// Data items accessed during this instruction
localparam logic [4:0] RS1 = (1 << 0);
localparam logic [4:0] RS2 = (1 << 1);
localparam logic [4:0] RS3 = (1 << 2);
localparam logic [4:0] RD = (1 << 3);
localparam logic [4:0] MEM = (1 << 4);
logic [4:0] data_accessed;
logic trace_log_enable;
initial begin
if ($value$plusargs("ibex_tracer_enable=%b", trace_log_enable)) begin
if (trace_log_enable == 1'b0) begin
$display("%m: Instruction trace disabled.");
end
end else begin
trace_log_enable = 1'b1;
end
end
function automatic void printbuffer_dumpline();
string rvfi_insn_str;
if (file_handle == 32'h0) begin
string file_name_base = "trace_core";
void'($value$plusargs("ibex_tracer_file_base=%s", file_name_base));
$sformat(file_name, "%s_%h.log", file_name_base, hart_id_i);
$display("%m: Writing execution trace to %s", file_name);
file_handle = $fopen(file_name, "w");
$fwrite(file_handle,
"Time\tCycle\tPC\tInsn\tDecoded instruction\tRegister and memory contents\n");
end
// Write compressed instructions as four hex digits (16 bit word), and
// uncompressed ones as 8 hex digits (32 bit words).
if (insn_is_compressed) begin
rvfi_insn_str = $sformatf("%h", rvfi_insn[15:0]);
end else begin
rvfi_insn_str = $sformatf("%h", rvfi_insn);
end
$fwrite(file_handle, "%15t\t%d\t%h\t%s\t%s\t",
$time, cycle, rvfi_pc_rdata, rvfi_insn_str, decoded_str);
if ((data_accessed & RS1) != 0) begin
$fwrite(file_handle, " %s:0x%08x", reg_addr_to_str(rvfi_rs1_addr), rvfi_rs1_rdata);
end
if ((data_accessed & RS2) != 0) begin
$fwrite(file_handle, " %s:0x%08x", reg_addr_to_str(rvfi_rs2_addr), rvfi_rs2_rdata);
end
if ((data_accessed & RS3) != 0) begin
$fwrite(file_handle, " %s:0x%08x", reg_addr_to_str(rvfi_rs3_addr), rvfi_rs3_rdata);
end
if ((data_accessed & RD) != 0) begin
$fwrite(file_handle, " %s=0x%08x", reg_addr_to_str(rvfi_rd_addr), rvfi_rd_wdata);
end
if ((data_accessed & MEM) != 0) begin
$fwrite(file_handle, " PA:0x%08x", rvfi_mem_addr);
if (rvfi_mem_rmask != 4'b0000) begin
$fwrite(file_handle, " store:0x%08x", rvfi_mem_wdata);
end
if (rvfi_mem_wmask != 4'b0000) begin
$fwrite(file_handle, " load:0x%08x", rvfi_mem_rdata);
end
end
$fwrite(file_handle, "\n");
endfunction
// Format register address with "x" prefix, left-aligned to a fixed width of 3 characters.
function automatic string reg_addr_to_str(input logic [4:0] addr);
if (addr < 10) begin
return $sformatf(" x%0d", addr);
end else begin
return $sformatf("x%0d", addr);
end
endfunction
// Get a CSR name for a CSR address.
function automatic string get_csr_name(input logic [11:0] csr_addr);
unique case (csr_addr)
12'd0: return "ustatus";
12'd4: return "uie";
12'd5: return "utvec";
12'd64: return "uscratch";
12'd65: return "uepc";
12'd66: return "ucause";
12'd67: return "utval";
12'd68: return "uip";
12'd1: return "fflags";
12'd2: return "frm";
12'd3: return "fcsr";
12'd3072: return "cycle";
12'd3073: return "time";
12'd3074: return "instret";
12'd3075: return "hpmcounter3";
12'd3076: return "hpmcounter4";
12'd3077: return "hpmcounter5";
12'd3078: return "hpmcounter6";
12'd3079: return "hpmcounter7";
12'd3080: return "hpmcounter8";
12'd3081: return "hpmcounter9";
12'd3082: return "hpmcounter10";
12'd3083: return "hpmcounter11";
12'd3084: return "hpmcounter12";
12'd3085: return "hpmcounter13";
12'd3086: return "hpmcounter14";
12'd3087: return "hpmcounter15";
12'd3088: return "hpmcounter16";
12'd3089: return "hpmcounter17";
12'd3090: return "hpmcounter18";
12'd3091: return "hpmcounter19";
12'd3092: return "hpmcounter20";
12'd3093: return "hpmcounter21";
12'd3094: return "hpmcounter22";
12'd3095: return "hpmcounter23";
12'd3096: return "hpmcounter24";
12'd3097: return "hpmcounter25";
12'd3098: return "hpmcounter26";
12'd3099: return "hpmcounter27";
12'd3100: return "hpmcounter28";
12'd3101: return "hpmcounter29";
12'd3102: return "hpmcounter30";
12'd3103: return "hpmcounter31";
12'd3200: return "cycleh";
12'd3201: return "timeh";
12'd3202: return "instreth";
12'd3203: return "hpmcounter3h";
12'd3204: return "hpmcounter4h";
12'd3205: return "hpmcounter5h";
12'd3206: return "hpmcounter6h";
12'd3207: return "hpmcounter7h";
12'd3208: return "hpmcounter8h";
12'd3209: return "hpmcounter9h";
12'd3210: return "hpmcounter10h";
12'd3211: return "hpmcounter11h";
12'd3212: return "hpmcounter12h";
12'd3213: return "hpmcounter13h";
12'd3214: return "hpmcounter14h";
12'd3215: return "hpmcounter15h";
12'd3216: return "hpmcounter16h";
12'd3217: return "hpmcounter17h";
12'd3218: return "hpmcounter18h";
12'd3219: return "hpmcounter19h";
12'd3220: return "hpmcounter20h";
12'd3221: return "hpmcounter21h";
12'd3222: return "hpmcounter22h";
12'd3223: return "hpmcounter23h";
12'd3224: return "hpmcounter24h";
12'd3225: return "hpmcounter25h";
12'd3226: return "hpmcounter26h";
12'd3227: return "hpmcounter27h";
12'd3228: return "hpmcounter28h";
12'd3229: return "hpmcounter29h";
12'd3230: return "hpmcounter30h";
12'd3231: return "hpmcounter31h";
12'd256: return "sstatus";
12'd258: return "sedeleg";
12'd259: return "sideleg";
12'd260: return "sie";
12'd261: return "stvec";
12'd262: return "scounteren";
12'd320: return "sscratch";
12'd321: return "sepc";
12'd322: return "scause";
12'd323: return "stval";
12'd324: return "sip";
12'd384: return "satp";
12'd3857: return "mvendorid";
12'd3858: return "marchid";
12'd3859: return "mimpid";
12'd3860: return "mhartid";
12'd768: return "mstatus";
12'd769: return "misa";
12'd770: return "medeleg";
12'd771: return "mideleg";
12'd772: return "mie";
12'd773: return "mtvec";
12'd774: return "mcounteren";
12'd832: return "mscratch";
12'd833: return "mepc";
12'd834: return "mcause";
12'd835: return "mtval";
12'd836: return "mip";
12'd928: return "pmpcfg0";
12'd929: return "pmpcfg1";
12'd930: return "pmpcfg2";
12'd931: return "pmpcfg3";
12'd944: return "pmpaddr0";
12'd945: return "pmpaddr1";
12'd946: return "pmpaddr2";
12'd947: return "pmpaddr3";
12'd948: return "pmpaddr4";
12'd949: return "pmpaddr5";
12'd950: return "pmpaddr6";
12'd951: return "pmpaddr7";
12'd952: return "pmpaddr8";
12'd953: return "pmpaddr9";
12'd954: return "pmpaddr10";
12'd955: return "pmpaddr11";
12'd956: return "pmpaddr12";
12'd957: return "pmpaddr13";
12'd958: return "pmpaddr14";
12'd959: return "pmpaddr15";
12'd2816: return "mcycle";
12'd2818: return "minstret";
12'd2819: return "mhpmcounter3";
12'd2820: return "mhpmcounter4";
12'd2821: return "mhpmcounter5";
12'd2822: return "mhpmcounter6";
12'd2823: return "mhpmcounter7";
12'd2824: return "mhpmcounter8";
12'd2825: return "mhpmcounter9";
12'd2826: return "mhpmcounter10";
12'd2827: return "mhpmcounter11";
12'd2828: return "mhpmcounter12";
12'd2829: return "mhpmcounter13";
12'd2830: return "mhpmcounter14";
12'd2831: return "mhpmcounter15";
12'd2832: return "mhpmcounter16";
12'd2833: return "mhpmcounter17";
12'd2834: return "mhpmcounter18";
12'd2835: return "mhpmcounter19";
12'd2836: return "mhpmcounter20";
12'd2837: return "mhpmcounter21";
12'd2838: return "mhpmcounter22";
12'd2839: return "mhpmcounter23";
12'd2840: return "mhpmcounter24";
12'd2841: return "mhpmcounter25";
12'd2842: return "mhpmcounter26";
12'd2843: return "mhpmcounter27";
12'd2844: return "mhpmcounter28";
12'd2845: return "mhpmcounter29";
12'd2846: return "mhpmcounter30";
12'd2847: return "mhpmcounter31";
12'd2944: return "mcycleh";
12'd2946: return "minstreth";
12'd2947: return "mhpmcounter3h";
12'd2948: return "mhpmcounter4h";
12'd2949: return "mhpmcounter5h";
12'd2950: return "mhpmcounter6h";
12'd2951: return "mhpmcounter7h";
12'd2952: return "mhpmcounter8h";
12'd2953: return "mhpmcounter9h";
12'd2954: return "mhpmcounter10h";
12'd2955: return "mhpmcounter11h";
12'd2956: return "mhpmcounter12h";
12'd2957: return "mhpmcounter13h";
12'd2958: return "mhpmcounter14h";
12'd2959: return "mhpmcounter15h";
12'd2960: return "mhpmcounter16h";
12'd2961: return "mhpmcounter17h";
12'd2962: return "mhpmcounter18h";
12'd2963: return "mhpmcounter19h";
12'd2964: return "mhpmcounter20h";
12'd2965: return "mhpmcounter21h";
12'd2966: return "mhpmcounter22h";
12'd2967: return "mhpmcounter23h";
12'd2968: return "mhpmcounter24h";
12'd2969: return "mhpmcounter25h";
12'd2970: return "mhpmcounter26h";
12'd2971: return "mhpmcounter27h";
12'd2972: return "mhpmcounter28h";
12'd2973: return "mhpmcounter29h";
12'd2974: return "mhpmcounter30h";
12'd2975: return "mhpmcounter31h";
12'd803: return "mhpmevent3";
12'd804: return "mhpmevent4";
12'd805: return "mhpmevent5";
12'd806: return "mhpmevent6";
12'd807: return "mhpmevent7";
12'd808: return "mhpmevent8";
12'd809: return "mhpmevent9";
12'd810: return "mhpmevent10";
12'd811: return "mhpmevent11";
12'd812: return "mhpmevent12";
12'd813: return "mhpmevent13";
12'd814: return "mhpmevent14";
12'd815: return "mhpmevent15";
12'd816: return "mhpmevent16";
12'd817: return "mhpmevent17";
12'd818: return "mhpmevent18";
12'd819: return "mhpmevent19";
12'd820: return "mhpmevent20";
12'd821: return "mhpmevent21";
12'd822: return "mhpmevent22";
12'd823: return "mhpmevent23";
12'd824: return "mhpmevent24";
12'd825: return "mhpmevent25";
12'd826: return "mhpmevent26";
12'd827: return "mhpmevent27";
12'd828: return "mhpmevent28";
12'd829: return "mhpmevent29";
12'd830: return "mhpmevent30";
12'd831: return "mhpmevent31";
12'd1952: return "tselect";
12'd1953: return "tdata1";
12'd1954: return "tdata2";
12'd1955: return "tdata3";
12'd1968: return "dcsr";
12'd1969: return "dpc";
12'd1970: return "dscratch";
12'd512: return "hstatus";
12'd514: return "hedeleg";
12'd515: return "hideleg";
12'd516: return "hie";
12'd517: return "htvec";
12'd576: return "hscratch";
12'd577: return "hepc";
12'd578: return "hcause";
12'd579: return "hbadaddr";
12'd580: return "hip";
12'd896: return "mbase";
12'd897: return "mbound";
12'd898: return "mibase";
12'd899: return "mibound";
12'd900: return "mdbase";
12'd901: return "mdbound";
12'd800: return "mcountinhibit";
default: return $sformatf("0x%x", csr_addr);
endcase
endfunction
function automatic void decode_mnemonic(input string mnemonic);
decoded_str = mnemonic;
endfunction
function automatic void decode_r_insn(input string mnemonic);
data_accessed = RS1 | RS2 | RD;
decoded_str = $sformatf("%s\tx%0d,x%0d,x%0d", mnemonic, rvfi_rd_addr, rvfi_rs1_addr,
rvfi_rs2_addr);
endfunction
function automatic void decode_r1_insn(input string mnemonic);
data_accessed = RS1 | RD;
decoded_str = $sformatf("%s\tx%0d,x%0d", mnemonic, rvfi_rd_addr, rvfi_rs1_addr);
endfunction
function automatic void decode_r_cmixcmov_insn(input string mnemonic);
data_accessed = RS1 | RS2 | RS3 | RD;
decoded_str = $sformatf("%s\tx%0d,x%0d,x%0d,x%0d", mnemonic, rvfi_rd_addr, rvfi_rs2_addr,
rvfi_rs1_addr, rvfi_rs3_addr);
endfunction
function automatic void decode_r_funnelshift_insn(input string mnemonic);
data_accessed = RS1 | RS2 | RS3 | RD;
decoded_str = $sformatf("%s\tx%0d,x%0d,x%0d,x%0d", mnemonic, rvfi_rd_addr, rvfi_rs1_addr,
rvfi_rs3_addr, rvfi_rs2_addr);
endfunction
function automatic void decode_i_insn(input string mnemonic);
data_accessed = RS1 | RD;
decoded_str = $sformatf("%s\tx%0d,x%0d,%0d", mnemonic, rvfi_rd_addr, rvfi_rs1_addr,
$signed({{20 {rvfi_insn[31]}}, rvfi_insn[31:20]}));
endfunction
function automatic void decode_i_shift_insn(input string mnemonic);
// SLLI, SRLI, SRAI, SROI, SLOI, RORI
logic [4:0] shamt;
shamt = {rvfi_insn[24:20]};
data_accessed = RS1 | RD;
decoded_str = $sformatf("%s\tx%0d,x%0d,0x%0x", mnemonic, rvfi_rd_addr, rvfi_rs1_addr, shamt);
endfunction
function automatic void decode_i_funnelshift_insn( input string mnemonic);
// fsri
logic [5:0] shamt;
shamt = {rvfi_insn[25:20]};
data_accessed = RS1 | RS3 | RD;
decoded_str = $sformatf("%s\tx%0d,x%0d,x%0d,0x%0x", mnemonic, rvfi_rd_addr, rvfi_rs1_addr,
rvfi_rs3_addr, shamt);
endfunction
function automatic void decode_i_jalr_insn(input string mnemonic);
// JALR
data_accessed = RS1 | RD;
decoded_str = $sformatf("%s\tx%0d,%0d(x%0d)", mnemonic, rvfi_rd_addr,
$signed({{20 {rvfi_insn[31]}}, rvfi_insn[31:20]}), rvfi_rs1_addr);
endfunction
function automatic void decode_u_insn(input string mnemonic);
data_accessed = RD;
decoded_str = $sformatf("%s\tx%0d,0x%0x", mnemonic, rvfi_rd_addr, {rvfi_insn[31:12]});
endfunction
function automatic void decode_j_insn(input string mnemonic);
// JAL
data_accessed = RD;
decoded_str = $sformatf("%s\tx%0d,%0x", mnemonic, rvfi_rd_addr, rvfi_pc_wdata);
endfunction
function automatic void decode_b_insn(input string mnemonic);
logic [31:0] branch_target;
logic [31:0] imm;
// We cannot use rvfi_pc_wdata for conditional jumps.
imm = $signed({ {19 {rvfi_insn[31]}}, rvfi_insn[31], rvfi_insn[7],
rvfi_insn[30:25], rvfi_insn[11:8], 1'b0 });
branch_target = rvfi_pc_rdata + imm;
data_accessed = RS1 | RS2;
decoded_str = $sformatf("%s\tx%0d,x%0d,%0x",
mnemonic, rvfi_rs1_addr, rvfi_rs2_addr, branch_target);
endfunction
function automatic void decode_csr_insn(input string mnemonic);
logic [11:0] csr;
string csr_name;
csr = rvfi_insn[31:20];
csr_name = get_csr_name(csr);
data_accessed = RD;
if (!rvfi_insn[14]) begin
data_accessed |= RS1;
decoded_str = $sformatf("%s\tx%0d,%s,x%0d",
mnemonic, rvfi_rd_addr, csr_name, rvfi_rs1_addr);
end else begin
decoded_str = $sformatf("%s\tx%0d,%s,%0d",
mnemonic, rvfi_rd_addr, csr_name, {27'b0, rvfi_insn[19:15]});
end
endfunction
function automatic void decode_cr_insn(input string mnemonic);
if (rvfi_rs2_addr == 5'b0) begin
if (rvfi_insn[12] == 1'b1) begin
// C.JALR
data_accessed = RS1 | RD;
end else begin
// C.JR
data_accessed = RS1;
end
decoded_str = $sformatf("%s\tx%0d", mnemonic, rvfi_rs1_addr);
end else begin
data_accessed = RS1 | RS2 | RD; // RS1 == RD
decoded_str = $sformatf("%s\tx%0d,x%0d", mnemonic, rvfi_rd_addr, rvfi_rs2_addr);
end
endfunction
function automatic void decode_ci_cli_insn(input string mnemonic);
logic [5:0] imm;
imm = {rvfi_insn[12], rvfi_insn[6:2]};
data_accessed = RD;
decoded_str = $sformatf("%s\tx%0d,%0d", mnemonic, rvfi_rd_addr, $signed(imm));
endfunction
function automatic void decode_ci_caddi_insn(input string mnemonic);
logic [5:0] nzimm;
nzimm = {rvfi_insn[12], rvfi_insn[6:2]};
data_accessed = RS1 | RD;
decoded_str = $sformatf("%s\tx%0d,%0d", mnemonic, rvfi_rd_addr, $signed(nzimm));
endfunction
function automatic void decode_ci_caddi16sp_insn(input string mnemonic);
logic [9:0] nzimm;
nzimm = {rvfi_insn[12], rvfi_insn[4:3], rvfi_insn[5], rvfi_insn[2], rvfi_insn[6], 4'b0};
data_accessed = RS1 | RD;
decoded_str = $sformatf("%s\tx%0d,%0d", mnemonic, rvfi_rd_addr, $signed(nzimm));
endfunction
function automatic void decode_ci_clui_insn(input string mnemonic);
logic [5:0] nzimm;
nzimm = {rvfi_insn[12], rvfi_insn[6:2]};
data_accessed = RD;
decoded_str = $sformatf("%s\tx%0d,0x%0x", mnemonic, rvfi_rd_addr, 20'($signed(nzimm)));
endfunction
function automatic void decode_ci_cslli_insn(input string mnemonic);
logic [5:0] shamt;
shamt = {rvfi_insn[12], rvfi_insn[6:2]};
data_accessed = RS1 | RD;
decoded_str = $sformatf("%s\tx%0d,0x%0x", mnemonic, rvfi_rd_addr, shamt);
endfunction
function automatic void decode_ciw_insn(input string mnemonic);
// C.ADDI4SPN
logic [9:0] nzuimm;
nzuimm = {rvfi_insn[10:7], rvfi_insn[12:11], rvfi_insn[5], rvfi_insn[6], 2'b00};
data_accessed = RD;
decoded_str = $sformatf("%s\tx%0d,x2,%0d", mnemonic, rvfi_rd_addr, nzuimm);
endfunction
function automatic void decode_cb_sr_insn(input string mnemonic);
logic [5:0] shamt;
shamt = {rvfi_insn[12], rvfi_insn[6:2]};
data_accessed = RS1 | RD;
decoded_str = $sformatf("%s\tx%0d,0x%0x", mnemonic, rvfi_rs1_addr, shamt);
endfunction
function automatic void decode_cb_insn(input string mnemonic);
logic [7:0] imm;
logic [31:0] jump_target;
if (rvfi_insn[15:13] == 3'b110 || rvfi_insn[15:13] == 3'b111) begin
// C.BNEZ and C.BEQZ
// We cannot use rvfi_pc_wdata for conditional jumps.
imm = {rvfi_insn[12], rvfi_insn[6:5], rvfi_insn[2], rvfi_insn[11:10], rvfi_insn[4:3]};
jump_target = rvfi_pc_rdata + 32'($signed({imm, 1'b0}));
data_accessed = RS1;
decoded_str = $sformatf("%s\tx%0d,%0x", mnemonic, rvfi_rs1_addr, jump_target);
end else if (rvfi_insn[15:13] == 3'b100) begin
// C.ANDI
imm = {{2{rvfi_insn[12]}}, rvfi_insn[12], rvfi_insn[6:2]};
data_accessed = RS1 | RD; // RS1 == RD
decoded_str = $sformatf("%s\tx%0d,%0d", mnemonic, rvfi_rd_addr, $signed(imm));
end else begin
imm = {rvfi_insn[12], rvfi_insn[6:2], 2'b00};
data_accessed = RS1;
decoded_str = $sformatf("%s\tx%0d,0x%0x", mnemonic, rvfi_rs1_addr, imm);
end
endfunction
function automatic void decode_cs_insn(input string mnemonic);
data_accessed = RS1 | RS2 | RD; // RS1 == RD
decoded_str = $sformatf("%s\tx%0d,x%0d", mnemonic, rvfi_rd_addr, rvfi_rs2_addr);
endfunction
function automatic void decode_cj_insn(input string mnemonic);
if (rvfi_insn[15:13] == 3'b001) begin
// C.JAL
data_accessed = RD;
end
decoded_str = $sformatf("%s\t%0x", mnemonic, rvfi_pc_wdata);
endfunction
function automatic void decode_compressed_load_insn(input string mnemonic);
logic [7:0] imm;
if (rvfi_insn[1:0] == OPCODE_C0) begin
// C.LW
imm = {1'b0, rvfi_insn[5], rvfi_insn[12:10], rvfi_insn[6], 2'b00};
end else begin
// C.LWSP
imm = {rvfi_insn[3:2], rvfi_insn[12], rvfi_insn[6:4], 2'b00};
end
data_accessed = RS1 | RD | MEM;
decoded_str = $sformatf("%s\tx%0d,%0d(x%0d)", mnemonic, rvfi_rd_addr, imm, rvfi_rs1_addr);
endfunction
function automatic void decode_compressed_store_insn(input string mnemonic);
logic [7:0] imm;
if (rvfi_insn[1:0] == OPCODE_C0) begin
// C.SW
imm = {1'b0, rvfi_insn[5], rvfi_insn[12:10], rvfi_insn[6], 2'b00};
end else begin
// C.SWSP
imm = {rvfi_insn[8:7], rvfi_insn[12:9], 2'b00};
end
data_accessed = RS1 | RS2 | MEM;
decoded_str = $sformatf("%s\tx%0d,%0d(x%0d)", mnemonic, rvfi_rs2_addr, imm, rvfi_rs1_addr);
endfunction
function automatic void decode_load_insn();
string mnemonic;
/*
Gives wrong results in Verilator < 4.020.
See https://github.com/lowRISC/ibex/issues/372 and
https://www.veripool.org/issues/1536-Verilator-Misoptimization-in-if-and-case-with-default-statement-inside-a-function
unique case (rvfi_insn[14:12])
3'b000: mnemonic = "lb";
3'b001: mnemonic = "lh";
3'b010: mnemonic = "lw";
3'b100: mnemonic = "lbu";
3'b101: mnemonic = "lhu";
default: begin
decode_mnemonic("INVALID");
return;
end
endcase
*/
logic [2:0] size;
size = rvfi_insn[14:12];
if (size == 3'b000) begin
mnemonic = "lb";
end else if (size == 3'b001) begin
mnemonic = "lh";
end else if (size == 3'b010) begin
mnemonic = "lw";
end else if (size == 3'b100) begin
mnemonic = "lbu";
end else if (size == 3'b101) begin
mnemonic = "lhu";
end else begin
decode_mnemonic("INVALID");
return;
end
data_accessed = RD | RS1 | MEM;
decoded_str = $sformatf("%s\tx%0d,%0d(x%0d)", mnemonic, rvfi_rd_addr,
$signed({{20 {rvfi_insn[31]}}, rvfi_insn[31:20]}), rvfi_rs1_addr);
endfunction
function automatic void decode_store_insn();
string mnemonic;
unique case (rvfi_insn[13:12])
2'b00: mnemonic = "sb";
2'b01: mnemonic = "sh";
2'b10: mnemonic = "sw";
default: begin
decode_mnemonic("INVALID");
return;
end
endcase
if (!rvfi_insn[14]) begin
// regular store
data_accessed = RS1 | RS2 | MEM;
decoded_str = $sformatf("%s\tx%0d,%0d(x%0d)",
mnemonic,
rvfi_rs2_addr,
$signed({{20{rvfi_insn[31]}}, rvfi_insn[31:25], rvfi_insn[11:7]}),
rvfi_rs1_addr);
end else begin
decode_mnemonic("INVALID");
end
endfunction
function automatic string get_fence_description(logic [3:0] bits);
string desc = "";
if (bits[3]) begin
desc = {desc, "i"};
end
if (bits[2]) begin
desc = {desc, "o"};
end
if (bits[1]) begin
desc = {desc, "r"};
end
if (bits[0]) begin
desc = {desc, "w"};
end
return desc;
endfunction
function automatic void decode_fence();
string predecessor;
string successor;
predecessor = get_fence_description(rvfi_insn[27:24]);
successor = get_fence_description(rvfi_insn[23:20]);
decoded_str = $sformatf("fence\t%s,%s", predecessor, successor);
endfunction
// cycle counter
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
cycle <= 0;
end else begin
cycle <= cycle + 1;
end
end
// close output file for writing
final begin
if (file_handle != 32'h0) begin
$fclose(file_handle);
end
end
// log execution
always_ff @(posedge clk_i) begin
if (rvfi_valid && trace_log_enable) begin
printbuffer_dumpline();
end
end
always_comb begin
decoded_str = "";
data_accessed = 5'h0;
insn_is_compressed = 0;
// Check for compressed instructions
if (rvfi_insn[1:0] != 2'b11) begin
insn_is_compressed = 1;
// Separate case to avoid overlapping decoding
if (rvfi_insn[15:13] == INSN_CMV[15:13] && rvfi_insn[1:0] == OPCODE_C2) begin
if (rvfi_insn[12] == INSN_CADD[12]) begin
if (rvfi_insn[11:2] == INSN_CEBREAK[11:2]) begin
decode_mnemonic("c.ebreak");
end else if (rvfi_insn[6:2] == INSN_CJALR[6:2]) begin
decode_cr_insn("c.jalr");
end else begin
decode_cr_insn("c.add");
end
end else begin
if (rvfi_insn[6:2] == INSN_CJR[6:2]) begin
decode_cr_insn("c.jr");
end else begin
decode_cr_insn("c.mv");
end
end
end else begin
unique casez (rvfi_insn[15:0])
// C0 Opcodes
INSN_CADDI4SPN: begin
if (rvfi_insn[12:2] == 11'h0) begin
// Align with pseudo-mnemonic used by GNU binutils and LLVM's MC layer
decode_mnemonic("c.unimp");
end else begin
decode_ciw_insn("c.addi4spn");
end
end
INSN_CLW: decode_compressed_load_insn("c.lw");
INSN_CSW: decode_compressed_store_insn("c.sw");
// C1 Opcodes
INSN_CADDI: decode_ci_caddi_insn("c.addi");
INSN_CJAL: decode_cj_insn("c.jal");
INSN_CJ: decode_cj_insn("c.j");
INSN_CLI: decode_ci_cli_insn("c.li");
INSN_CLUI: begin
// These two instructions share opcode
if (rvfi_insn[11:7] == 5'd2) begin
decode_ci_caddi16sp_insn("c.addi16sp");
end else begin
decode_ci_clui_insn("c.lui");
end
end
INSN_CSRLI: decode_cb_sr_insn("c.srli");
INSN_CSRAI: decode_cb_sr_insn("c.srai");
INSN_CANDI: decode_cb_insn("c.andi");
INSN_CSUB: decode_cs_insn("c.sub");
INSN_CXOR: decode_cs_insn("c.xor");
INSN_COR: decode_cs_insn("c.or");
INSN_CAND: decode_cs_insn("c.and");
INSN_CBEQZ: decode_cb_insn("c.beqz");
INSN_CBNEZ: decode_cb_insn("c.bnez");
// C2 Opcodes
INSN_CSLLI: decode_ci_cslli_insn("c.slli");
INSN_CLWSP: decode_compressed_load_insn("c.lwsp");
INSN_SWSP: decode_compressed_store_insn("c.swsp");
default: decode_mnemonic("INVALID");
endcase
end
end else begin
unique casez (rvfi_insn)
// Regular opcodes
INSN_LUI: decode_u_insn("lui");
INSN_AUIPC: decode_u_insn("auipc");
INSN_JAL: decode_j_insn("jal");
INSN_JALR: decode_i_jalr_insn("jalr");
// BRANCH
INSN_BEQ: decode_b_insn("beq");
INSN_BNE: decode_b_insn("bne");
INSN_BLT: decode_b_insn("blt");
INSN_BGE: decode_b_insn("bge");
INSN_BLTU: decode_b_insn("bltu");
INSN_BGEU: decode_b_insn("bgeu");
// OPIMM
INSN_ADDI: begin
if (rvfi_insn == 32'h00_00_00_13) begin
// TODO: objdump doesn't decode this as nop currently, even though it would be helpful
// Decide what to do here: diverge from objdump, or make the trace less readable to
// users.
//decode_mnemonic("nop");
decode_i_insn("addi");
end else begin
decode_i_insn("addi");
end
end
INSN_SLTI: decode_i_insn("slti");
INSN_SLTIU: decode_i_insn("sltiu");
INSN_XORI: decode_i_insn("xori");
INSN_ORI: decode_i_insn("ori");
// Unlike the ratified v.1.0.0 bitmanip extension, the v.0.94 draft extension continues to
// define the pseudo-instruction
// zext.b rd rs = andi rd, rs, 255.
// However, for now the tracer doesn't emit this due to a lack of support in the LLVM and
// GCC toolchains. Enabling this functionality when the time is right is tracked in
// https://github.com/lowRISC/ibex/issues/1228
INSN_ANDI: decode_i_insn("andi");
// INSN_ANDI:begin
// casez (rvfi_insn)
// INSN_ZEXTB: decode_r1_insn("zext.b");
// default: decode_i_insn("andi");
// endcase
// end
INSN_SLLI: decode_i_shift_insn("slli");
INSN_SRLI: decode_i_shift_insn("srli");
INSN_SRAI: decode_i_shift_insn("srai");
// OP
INSN_ADD: decode_r_insn("add");
INSN_SUB: decode_r_insn("sub");
INSN_SLL: decode_r_insn("sll");
INSN_SLT: decode_r_insn("slt");
INSN_SLTU: decode_r_insn("sltu");
INSN_XOR: decode_r_insn("xor");
INSN_SRL: decode_r_insn("srl");
INSN_SRA: decode_r_insn("sra");
INSN_OR: decode_r_insn("or");
INSN_AND: decode_r_insn("and");
// SYSTEM (CSR manipulation)
INSN_CSRRW: decode_csr_insn("csrrw");
INSN_CSRRS: decode_csr_insn("csrrs");
INSN_CSRRC: decode_csr_insn("csrrc");
INSN_CSRRWI: decode_csr_insn("csrrwi");
INSN_CSRRSI: decode_csr_insn("csrrsi");
INSN_CSRRCI: decode_csr_insn("csrrci");
// SYSTEM (others)
INSN_ECALL: decode_mnemonic("ecall");
INSN_EBREAK: decode_mnemonic("ebreak");
INSN_MRET: decode_mnemonic("mret");
INSN_DRET: decode_mnemonic("dret");
INSN_WFI: decode_mnemonic("wfi");
// RV32M
INSN_PMUL: decode_r_insn("mul");
INSN_PMUH: decode_r_insn("mulh");
INSN_PMULHSU: decode_r_insn("mulhsu");
INSN_PMULHU: decode_r_insn("mulhu");
INSN_DIV: decode_r_insn("div");
INSN_DIVU: decode_r_insn("divu");
INSN_REM: decode_r_insn("rem");
INSN_REMU: decode_r_insn("remu");
// LOAD & STORE
INSN_LOAD: decode_load_insn();
INSN_STORE: decode_store_insn();
// MISC-MEM
INSN_FENCE: decode_fence();
INSN_FENCEI: decode_mnemonic("fence.i");
// RV32B - ZBA
INSN_SH1ADD: decode_r_insn("sh1add");
INSN_SH2ADD: decode_r_insn("sh2add");
INSN_SH3ADD: decode_r_insn("sh3add");
// RV32B - ZBB
INSN_RORI: decode_i_shift_insn("rori");
INSN_ROL: decode_r_insn("rol");
INSN_ROR: decode_r_insn("ror");
INSN_MIN: decode_r_insn("min");
INSN_MAX: decode_r_insn("max");
INSN_MINU: decode_r_insn("minu");
INSN_MAXU: decode_r_insn("maxu");
INSN_XNOR: decode_r_insn("xnor");
INSN_ORN: decode_r_insn("orn");
INSN_ANDN: decode_r_insn("andn");
// The ratified v.1.0.0 bitmanip extension defines the pseudo-instruction
// zext.h rd rs = pack rd, rs, zero.
// However, for now the tracer doesn't emit this due to a lack of support in the LLVM and
// GCC toolchains. Enabling this functionality when the time is right is tracked in
// https://github.com/lowRISC/ibex/issues/1228
INSN_PACK: decode_r_insn("pack");
// INSN_PACK: begin
// casez (rvfi_insn)
// INSN_ZEXTH: decode_r1_insn("zext.h");
// default: decode_r_insn("pack");
// endcase
// end
INSN_PACKH: decode_r_insn("packh");
INSN_PACKU: decode_r_insn("packu");
INSN_CLZ: decode_r1_insn("clz");
INSN_CTZ: decode_r1_insn("ctz");
INSN_CPOP: decode_r1_insn("cpop");
INSN_SEXTB: decode_r1_insn("sext.b");
INSN_SEXTH: decode_r1_insn("sext.h");
// RV32B - ZBS
INSN_BCLRI: decode_i_shift_insn("bclri");
INSN_BSETI: decode_i_shift_insn("bseti");
INSN_BINVI: decode_i_shift_insn("binvi");
INSN_BEXTI: decode_i_shift_insn("bexti");
INSN_BCLR: decode_r_insn("bclr");
INSN_BSET: decode_r_insn("bset");
INSN_BINV: decode_r_insn("binv");
INSN_BEXT: decode_r_insn("bext");
// RV32B - ZBE
INSN_BDECOMPRESS: decode_r_insn("bdecompress");
INSN_BCOMPRESS: decode_r_insn("bcompress");
// RV32B - ZBP
INSN_GREV: decode_r_insn("grev");
INSN_GREVI: begin
unique casez (rvfi_insn)
INSN_REV_P: decode_r1_insn("rev.p");
INSN_REV2_N: decode_r1_insn("rev2.n");
INSN_REV_N: decode_r1_insn("rev.n");
INSN_REV4_B: decode_r1_insn("rev4.b");
INSN_REV2_B: decode_r1_insn("rev2.b");
INSN_REV_B: decode_r1_insn("rev.b");
INSN_REV8_H: decode_r1_insn("rev8.h");
INSN_REV4_H: decode_r1_insn("rev4.h");
INSN_REV2_H: decode_r1_insn("rev2.h");
INSN_REV_H: decode_r1_insn("rev.h");
INSN_REV16: decode_r1_insn("rev16");
INSN_REV8: decode_r1_insn("rev8");
INSN_REV4: decode_r1_insn("rev4");
INSN_REV2: decode_r1_insn("rev2");
INSN_REV: decode_r1_insn("rev");
default: decode_i_insn("grevi");
endcase
end
INSN_GORC: decode_r_insn("gorc");
INSN_GORCI: begin
unique casez (rvfi_insn)
INSN_ORC_P: decode_r1_insn("orc.p");
INSN_ORC2_N: decode_r1_insn("orc2.n");
INSN_ORC_N: decode_r1_insn("orc.n");
INSN_ORC4_B: decode_r1_insn("orc4.b");
INSN_ORC2_B: decode_r1_insn("orc2.b");
INSN_ORC_B: decode_r1_insn("orc.b");
INSN_ORC8_H: decode_r1_insn("orc8.h");
INSN_ORC4_H: decode_r1_insn("orc4.h");
INSN_ORC2_H: decode_r1_insn("orc2.h");
INSN_ORC_H: decode_r1_insn("orc.h");
INSN_ORC16: decode_r1_insn("orc16");
INSN_ORC8: decode_r1_insn("orc8");
INSN_ORC4: decode_r1_insn("orc4");
INSN_ORC2: decode_r1_insn("orc2");
INSN_ORC: decode_r1_insn("orc");
default: decode_i_insn("gorci");
endcase
end
INSN_SHFL: decode_r_insn("shfl");
INSN_SHFLI: begin
unique casez (rvfi_insn)
INSN_ZIP_N: decode_r1_insn("zip.n");
INSN_ZIP2_B: decode_r1_insn("zip2.b");
INSN_ZIP_B: decode_r1_insn("zip.b");
INSN_ZIP4_H: decode_r1_insn("zip4.h");
INSN_ZIP2_H: decode_r1_insn("zip2.h");
INSN_ZIP_H: decode_r1_insn("zip.h");
INSN_ZIP8: decode_r1_insn("zip8");
INSN_ZIP4: decode_r1_insn("zip4");
INSN_ZIP2: decode_r1_insn("zip2");
INSN_ZIP: decode_r1_insn("zip");
default: decode_i_insn("shfli");
endcase
end
INSN_UNSHFL: decode_r_insn("unshfl");
INSN_UNSHFLI: begin
unique casez (rvfi_insn)
INSN_UNZIP_N: decode_r1_insn("unzip.n");
INSN_UNZIP2_B: decode_r1_insn("unzip2.b");
INSN_UNZIP_B: decode_r1_insn("unzip.b");
INSN_UNZIP4_H: decode_r1_insn("unzip4.h");
INSN_UNZIP2_H: decode_r1_insn("unzip2.h");
INSN_UNZIP_H: decode_r1_insn("unzip.h");
INSN_UNZIP8: decode_r1_insn("unzip8");
INSN_UNZIP4: decode_r1_insn("unzip4");
INSN_UNZIP2: decode_r1_insn("unzip2");
INSN_UNZIP: decode_r1_insn("unzip");
default: decode_i_insn("unshfli");
endcase
end
INSN_XPERM_N: decode_r_insn("xperm_n");
INSN_XPERM_B: decode_r_insn("xperm_b");
INSN_XPERM_H: decode_r_insn("xperm_h");
INSN_SLO: decode_r_insn("slo");
INSN_SRO: decode_r_insn("sro");
INSN_SLOI: decode_i_shift_insn("sloi");
INSN_SROI: decode_i_shift_insn("sroi");
// RV32B - ZBT
INSN_CMIX: decode_r_cmixcmov_insn("cmix");
INSN_CMOV: decode_r_cmixcmov_insn("cmov");
INSN_FSR: decode_r_funnelshift_insn("fsr");
INSN_FSL: decode_r_funnelshift_insn("fsl");
INSN_FSRI: decode_i_funnelshift_insn("fsri");
// RV32B - ZBF
INSN_BFP: decode_r_insn("bfp");
// RV32B - ZBC
INSN_CLMUL: decode_r_insn("clmul");
INSN_CLMULR: decode_r_insn("clmulr");
INSN_CLMULH: decode_r_insn("clmulh");
// RV32B - ZBR
INSN_CRC32_B: decode_r1_insn("crc32.b");
INSN_CRC32_H: decode_r1_insn("crc32.h");
INSN_CRC32_W: decode_r1_insn("crc32.w");
INSN_CRC32C_B: decode_r1_insn("crc32c.b");
INSN_CRC32C_H: decode_r1_insn("crc32c.h");
INSN_CRC32C_W: decode_r1_insn("crc32c.w");
default: decode_mnemonic("INVALID");
endcase
end
end
endmodule