mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-22 04:47:25 -04:00
444 lines
No EOL
14 KiB
Systemverilog
444 lines
No EOL
14 KiB
Systemverilog
// Copyright 2017 ETH Zurich and University of Bologna.
|
|
// Copyright and related rights are licensed under the Solderpad Hardware
|
|
// License, Version 0.51 (the “License”); you may not use this file except in
|
|
// compliance with the License. You may obtain a copy of the License at
|
|
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
|
// or agreed to in writing, software, hardware and materials distributed under
|
|
// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR
|
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations under the License.
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// 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: zero-riscy //
|
|
// Language: SystemVerilog //
|
|
// //
|
|
// Description: Traces the executed instructions //
|
|
// //
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
`ifndef VERILATOR
|
|
|
|
|
|
`include "zeroriscy_config.sv"
|
|
|
|
import zeroriscy_defines::*;
|
|
import zeroriscy_tracer_defines::*;
|
|
|
|
|
|
// 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
|
|
|
|
|
|
module zeroriscy_tracer
|
|
#(
|
|
parameter REG_ADDR_WIDTH = 5
|
|
)
|
|
(
|
|
// Clock and Reset
|
|
input logic clk,
|
|
input logic rst_n,
|
|
|
|
input logic fetch_enable,
|
|
input logic [3:0] core_id,
|
|
input logic [5:0] cluster_id,
|
|
|
|
input logic [31:0] pc,
|
|
input logic [31:0] instr,
|
|
input logic compressed,
|
|
input logic id_valid,
|
|
input logic is_decoding,
|
|
input logic is_branch,
|
|
input logic branch_taken,
|
|
input logic pipe_flush,
|
|
input logic mret_insn,
|
|
input logic ecall_insn,
|
|
input logic ebrk_insn,
|
|
input logic csr_status,
|
|
input logic [31:0] rs1_value,
|
|
input logic [31:0] rs2_value,
|
|
input logic [31:0] lsu_value,
|
|
|
|
input logic [(REG_ADDR_WIDTH-1):0] ex_reg_addr,
|
|
input logic ex_reg_we,
|
|
input logic [31:0] ex_reg_wdata,
|
|
input logic data_valid_lsu,
|
|
input logic ex_data_req,
|
|
input logic ex_data_gnt,
|
|
input logic ex_data_we,
|
|
input logic [31:0] ex_data_addr,
|
|
input logic [31:0] ex_data_wdata,
|
|
|
|
input logic [31:0] lsu_reg_wdata,
|
|
|
|
input logic [31:0] imm_u_type,
|
|
input logic [31:0] imm_uj_type,
|
|
input logic [31:0] imm_i_type,
|
|
input logic [11:0] imm_iz_type,
|
|
input logic [31:0] imm_z_type,
|
|
input logic [31:0] imm_s_type,
|
|
input logic [31:0] imm_sb_type
|
|
);
|
|
|
|
integer f;
|
|
string fn;
|
|
integer cycles;
|
|
logic [ 4:0] rd, rs1, rs2, rs3;
|
|
|
|
typedef struct {
|
|
logic [(REG_ADDR_WIDTH-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 [(REG_ADDR_WIDTH-1):0] addr);
|
|
begin
|
|
if (addr < 10)
|
|
return $sformatf(" x%0d", addr);
|
|
else
|
|
return $sformatf("x%0d", addr);
|
|
end
|
|
endfunction
|
|
|
|
function void printInstrTrace();
|
|
mem_acc_t mem_acc;
|
|
begin
|
|
$fwrite(f, "%t %15d %h %h %-36s", simtime,
|
|
cycles,
|
|
pc,
|
|
instr,
|
|
str);
|
|
|
|
foreach(regs_write[i]) begin
|
|
if (regs_write[i].addr != 0)
|
|
$fwrite(f, " %s=%08x", regAddrToStr(regs_write[i].addr), regs_write[i].value);
|
|
end
|
|
|
|
foreach(regs_read[i]) begin
|
|
if (regs_read[i].addr != 0)
|
|
$fwrite(f, " %s:%08x", regAddrToStr(regs_read[i].addr), regs_read[i].value);
|
|
end
|
|
|
|
if (mem_access.size() > 0) begin
|
|
mem_acc = mem_access.pop_front();
|
|
|
|
$fwrite(f, " PA:%08x", mem_acc.addr);
|
|
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});
|
|
regs_read.push_back('{rs2, rs2_value});
|
|
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});
|
|
regs_write.push_back('{rd, 'x});
|
|
str = $sformatf("%-16s x%0d, x%0d, %0d", mnemonic, rd, rs1, $signed(imm_i_type));
|
|
end
|
|
endfunction // printIInstr
|
|
|
|
function void printIuInstr(input string mnemonic);
|
|
begin
|
|
regs_read.push_back('{rs1, rs1_value});
|
|
regs_write.push_back('{rd, 'x});
|
|
str = $sformatf("%-16s x%0d, x%0d, 0x%0x", mnemonic, rd, rs1, imm_i_type);
|
|
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[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_uj_type));
|
|
end
|
|
endfunction // printUJInstr
|
|
|
|
function void printSBInstr(input string mnemonic);
|
|
begin
|
|
regs_read.push_back('{rs1, rs1_value});
|
|
regs_read.push_back('{rs2, rs2_value});
|
|
str = $sformatf("%-16s x%0d, x%0d, %0d", mnemonic, rs1, rs2, $signed(imm_sb_type));
|
|
end
|
|
endfunction // printSBInstr
|
|
|
|
function void printCSRInstr(input string mnemonic);
|
|
logic [11:0] csr;
|
|
begin
|
|
csr = instr[31:20];
|
|
|
|
regs_write.push_back('{rd, 'x});
|
|
|
|
if (instr[14] == 1'b0) begin
|
|
regs_read.push_back('{rs1, rs1_value});
|
|
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, imm_z_type, csr);
|
|
end
|
|
end
|
|
endfunction // printCSRInstr
|
|
|
|
function void printLoadInstr();
|
|
string mnemonic;
|
|
logic [2:0] size;
|
|
begin
|
|
// detect reg-reg load and find size
|
|
size = instr[14:12];
|
|
if (instr[14:12] == 3'b111)
|
|
size = instr[30:28];
|
|
|
|
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
|
|
endcase
|
|
|
|
regs_write.push_back('{rd, 'x});
|
|
|
|
if (instr[14:12] != 3'b111) begin
|
|
// regular load
|
|
regs_read.push_back('{rs1, rs1_value});
|
|
str = $sformatf("%-16s x%0d, %0d(x%0d)", mnemonic, rd, $signed(imm_i_type), rs1);
|
|
end else begin
|
|
printMnemonic("INVALID");
|
|
end
|
|
end
|
|
endfunction
|
|
|
|
function void printStoreInstr();
|
|
string mnemonic;
|
|
begin
|
|
|
|
case (instr[13:12])
|
|
2'b00: mnemonic = "sb";
|
|
2'b01: mnemonic = "sh";
|
|
2'b10: mnemonic = "sw";
|
|
2'b11: begin
|
|
printMnemonic("INVALID");
|
|
return;
|
|
end
|
|
endcase
|
|
|
|
if (instr[14] == 1'b0) begin
|
|
// regular store
|
|
regs_read.push_back('{rs2, rs2_value});
|
|
regs_read.push_back('{rs1, rs1_value});
|
|
str = $sformatf("%-16s x%0d, %0d(x%0d)", mnemonic, rs2, $signed(imm_s_type), rs1);
|
|
end else begin
|
|
printMnemonic("INVALID");
|
|
end
|
|
end
|
|
endfunction // printSInstr
|
|
|
|
endclass
|
|
|
|
mailbox #(instr_trace_t) instr_ex = new ();
|
|
mailbox #(instr_trace_t) instr_wb = new ();
|
|
|
|
// cycle counter
|
|
always_ff @(posedge clk, negedge rst_n)
|
|
begin
|
|
if (rst_n == 1'b0)
|
|
cycles = 0;
|
|
else
|
|
cycles = cycles + 1;
|
|
end
|
|
|
|
// open/close output file for writing
|
|
initial
|
|
begin
|
|
wait(rst_n == 1'b1);
|
|
wait(fetch_enable == 1'b1);
|
|
$sformat(fn, "trace_core_%h_%h.log", cluster_id, core_id);
|
|
$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[`REG_D];
|
|
assign rs1 = instr[`REG_S1];
|
|
assign rs2 = instr[`REG_S2];
|
|
assign rs3 = instr[`REG_S3];
|
|
|
|
// log execution
|
|
always @(negedge clk)
|
|
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 || mret_insn || ecall_insn || pipe_flush || ebrk_insn || csr_status || ex_data_req) && is_decoding)
|
|
begin
|
|
trace = new ();
|
|
|
|
trace.simtime = $time;
|
|
trace.cycles = cycles;
|
|
trace.pc = pc;
|
|
trace.instr = instr;
|
|
|
|
// use casex instead of case inside due to ModelSim bug
|
|
casex (instr)
|
|
// Aliases
|
|
32'h00_00_00_13: trace.printMnemonic("nop");
|
|
// 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_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");
|
|
{25'b?, OPCODE_LOAD}: trace.printLoadInstr();
|
|
{25'b?, OPCODE_STORE}: trace.printStoreInstr();
|
|
default: trace.printMnemonic("INVALID");
|
|
endcase // unique case (instr)
|
|
|
|
// replace register written back
|
|
foreach(trace.regs_write[i]) begin
|
|
if ((trace.regs_write[i].addr == ex_reg_addr) && ex_reg_we)
|
|
trace.regs_write[i].value = ex_reg_wdata;
|
|
end
|
|
// look for data accesses and log them
|
|
if (ex_data_req) begin
|
|
|
|
if(~ex_data_gnt) begin
|
|
//we wait until the the gnt comes
|
|
do @(negedge clk);
|
|
while (!ex_data_gnt);
|
|
end
|
|
|
|
mem_acc.addr = ex_data_addr;
|
|
mem_acc.we = ex_data_we;
|
|
|
|
if (mem_acc.we)
|
|
mem_acc.wdata = ex_data_wdata;
|
|
else
|
|
mem_acc.wdata = 'x;
|
|
//we wait until the the data instruction ends
|
|
do @(negedge clk);
|
|
while (!data_valid_lsu);
|
|
|
|
if (~mem_acc.we)
|
|
//load operations
|
|
foreach(trace.regs_write[i])
|
|
trace.regs_write[i].value = lsu_reg_wdata;
|
|
trace.mem_access.push_back(mem_acc);
|
|
end
|
|
trace.printInstrTrace();
|
|
|
|
end
|
|
end // always @ (posedge clk)
|
|
|
|
endmodule
|
|
`endif |