// Copyright lowRISC contributors. // Copyright 2018 ETH Zurich and University of Bologna. // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 //////////////////////////////////////////////////////////////////////////////// // Engineer: Andreas Traber - atraber@iis.ee.ethz.ch // // // // Additional contributions by: // // Davide Schiavone - pschiavo@iis.ee.ethz.ch // // // // Design Name: RISC-V Tracer // // Project Name: ibex // // Language: SystemVerilog // // // // Description: Traces the executed instructions // // // //////////////////////////////////////////////////////////////////////////////// // Source/Destination register instruction index `define REG_S1 19:15 `define REG_S2 24:20 `define REG_S3 29:25 `define REG_D 11:07 /** * Traces the executed instructions * * Note: Verilator does not support the language constructs used in this * module! */ module ibex_tracer #( parameter int unsigned RegAddrWidth = 5 ) ( // Clock and Reset input logic clk_i, input logic rst_ni, input logic fetch_enable_i, 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 [31:0] rs1_value_i, input logic [31:0] rs2_value_i, input logic [(RegAddrWidth-1):0] ex_reg_addr_i, input logic [31:0] ex_reg_wdata_i, input logic [31:0] ex_data_addr_i, input logic [31:0] ex_data_wdata_i, input logic [31:0] ex_data_rdata_i ); import ibex_pkg::*; import ibex_tracer_pkg::*; integer f; string fn; integer cycles; logic [ 4:0] rd, rs1, rs2, rs3; typedef struct { logic [(RegAddrWidth-1):0] addr; logic [31:0] value; } reg_t; typedef struct { logic [31:0] addr; logic we; logic [ 3:0] be; logic [31:0] wdata; logic [31:0] rdata; } mem_acc_t; class instr_trace_t; time simtime; integer cycles; logic [31:0] pc; logic [31:0] instr; string str; reg_t regs_read[$]; reg_t regs_write[$]; mem_acc_t mem_access[$]; function new (); str = ""; regs_read = {}; regs_write = {}; mem_access = {}; endfunction function string regAddrToStr(input logic [(RegAddrWidth-1):0] addr); begin if (addr < 10) begin return $sformatf(" x%0d", addr); end else begin return $sformatf("x%0d", addr); end end endfunction function void printInstrTrace(); mem_acc_t mem_acc; begin $fwrite(f, "%t %15d %h %h %-36s", simtime, cycles, pc_i, instr_i, str); foreach(regs_write[i]) begin if (regs_write[i].addr != 0) begin $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: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: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"); end endfunction function void printMnemonic(input string mnemonic); begin str = mnemonic; end endfunction // printMnemonic function void printRInstr(input string mnemonic); begin regs_read.push_back('{rs1, rs1_value_i}); regs_read.push_back('{rs2, rs2_value_i}); regs_write.push_back('{rd, 'x}); str = $sformatf("%-16s x%0d, x%0d, x%0d", mnemonic, rd, rs1, rs2); end endfunction // printRInstr function void printIInstr(input string mnemonic); 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({{20 {instr[31]}}, instr[31:20]})); end endfunction // printIInstr function void printIuInstr(input string mnemonic); 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, {{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, {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({ {12 {instr[31]}}, instr[19:12], instr[20], instr[30:21], 1'b0 })); end endfunction // printUJInstr function void printSBInstr(input string mnemonic); 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({ {19 {instr[31]}}, instr[31], instr[7], instr[30:25], instr[11:8], 1'b0 })); end endfunction // printSBInstr function void printCSRInstr(input string mnemonic); logic [11:0] csr; begin csr = instr_i[31:20]; regs_write.push_back('{rd, 'x}); if (!instr_i[14]) begin 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, { 27'b0, instr[`REG_S1] }, csr); end end endfunction // printCSRInstr function void printLoadInstr(); string mnemonic; logic [2:0] size; mem_acc_t mem_acc; begin // detect reg-reg load and find size size = instr_i[14:12]; if (instr_i[14:12] == 3'b111) begin size = instr_i[30:28]; end unique case (size) 3'b000: mnemonic = "lb"; 3'b001: mnemonic = "lh"; 3'b010: mnemonic = "lw"; 3'b100: mnemonic = "lbu"; 3'b101: mnemonic = "lhu"; 3'b110: mnemonic = "p.elw"; 3'b011, 3'b111: begin printMnemonic("INVALID"); return; end default: begin printMnemonic("INVALID"); return; end endcase regs_write.push_back('{rd, 'x}); 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({{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; mem_acc_t mem_acc; begin unique case (instr_i[13:12]) 2'b00: mnemonic = "sb"; 2'b01: mnemonic = "sh"; 2'b10: mnemonic = "sw"; 2'b11: begin printMnemonic("INVALID"); return; end default: begin printMnemonic("INVALID"); return; end endcase if (!instr_i[14]) begin // 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({ {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 endclass mailbox #(instr_trace_t) instr_ex = new (); mailbox #(instr_trace_t) instr_wb = new (); // cycle counter always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin cycles = 0; end else begin cycles = cycles + 1; end end // open/close output file for writing initial begin wait(rst_ni == 1'b1); wait(fetch_enable_i == 1'b1); $sformat(fn, "trace_core_%h_%h.log", cluster_id_i, core_id_i); $display("[TRACER] Output filename is: %s", fn); f = $fopen(fn, "w"); $fwrite(f, " Time Cycles PC Instr Mnemonic\n"); end final begin $fclose(f); end assign rd = instr_i[`REG_D]; assign rs1 = instr_i[`REG_S1]; assign rs2 = instr_i[`REG_S2]; assign rs3 = instr_i[`REG_S3]; // log execution 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 (valid_i) begin trace = new (); trace.simtime = $time; trace.cycles = cycles; trace.pc = pc_i; trace.instr = instr_i; // separate case for 'nop' instruction to avoid overlapping with 'addi' if (instr_i == 32'h00_00_00_13) begin trace.printMnemonic("nop"); end else begin // use casex instead of case inside due to ModelSim bug unique casex (instr_i) // Regular opcodes INSTR_LUI: trace.printUInstr("lui"); INSTR_AUIPC: trace.printUInstr("auipc"); INSTR_JAL: trace.printUJInstr("jal"); INSTR_JALR: trace.printIInstr("jalr"); // BRANCH INSTR_BEQ: trace.printSBInstr("beq"); INSTR_BNE: trace.printSBInstr("bne"); INSTR_BLT: trace.printSBInstr("blt"); INSTR_BGE: trace.printSBInstr("bge"); INSTR_BLTU: trace.printSBInstr("bltu"); INSTR_BGEU: trace.printSBInstr("bgeu"); // OPIMM INSTR_ADDI: trace.printIInstr("addi"); INSTR_SLTI: trace.printIInstr("slti"); INSTR_SLTIU: trace.printIInstr("sltiu"); INSTR_XORI: trace.printIInstr("xori"); INSTR_ORI: trace.printIInstr("ori"); INSTR_ANDI: trace.printIInstr("andi"); INSTR_SLLI: trace.printIuInstr("slli"); INSTR_SRLI: trace.printIuInstr("srli"); INSTR_SRAI: trace.printIuInstr("srai"); // OP INSTR_ADD: trace.printRInstr("add"); INSTR_SUB: trace.printRInstr("sub"); INSTR_SLL: trace.printRInstr("sll"); INSTR_SLT: trace.printRInstr("slt"); INSTR_SLTU: trace.printRInstr("sltu"); INSTR_XOR: trace.printRInstr("xor"); INSTR_SRL: trace.printRInstr("srl"); INSTR_SRA: trace.printRInstr("sra"); INSTR_OR: trace.printRInstr("or"); INSTR_AND: trace.printRInstr("and"); // SYSTEM (CSR manipulation) INSTR_CSRRW: trace.printCSRInstr("csrrw"); INSTR_CSRRS: trace.printCSRInstr("csrrs"); INSTR_CSRRC: trace.printCSRInstr("csrrc"); INSTR_CSRRWI: trace.printCSRInstr("csrrwi"); INSTR_CSRRSI: trace.printCSRInstr("csrrsi"); INSTR_CSRRCI: trace.printCSRInstr("csrrci"); // SYSTEM (others) INSTR_ECALL: trace.printMnemonic("ecall"); INSTR_EBREAK: trace.printMnemonic("ebreak"); INSTR_MRET: trace.printMnemonic("mret"); INSTR_DRET: trace.printMnemonic("dret"); INSTR_WFI: trace.printMnemonic("wfi"); // RV32M INSTR_PMUL: trace.printRInstr("mul"); INSTR_PMUH: trace.printRInstr("mulh"); INSTR_PMULHSU: trace.printRInstr("mulhsu"); INSTR_PMULHU: trace.printRInstr("mulhu"); INSTR_DIV: trace.printRInstr("div"); INSTR_DIVU: trace.printRInstr("divu"); INSTR_REM: trace.printRInstr("rem"); INSTR_REMU: trace.printRInstr("remu"); // LOAD & STORE INSTR_LOAD: trace.printLoadInstr(); INSTR_STORE: trace.printStoreInstr(); // MISC-MEM INSTR_FENCE: trace.printMnemonic("fence"); default: trace.printMnemonic("INVALID"); endcase // unique case (instr_i) end // replace register written back foreach(trace.regs_write[i]) begin if ((trace.regs_write[i].addr == ex_reg_addr_i)) begin trace.regs_write[i].value = ex_reg_wdata_i; end end trace.printInstrTrace(); end end // always @ (posedge clk_i) endmodule `undef REG_S1 `undef REG_S2 `undef REG_S3 `undef REG_D