blob: 7bc2b880f94df8b8adbfeba7b487e5091998c1c5 [file] [log] [blame]
// 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