Tracer rewrite

Instantiate tracer module in a separate core file and use only RVFI
signals.
This commit is contained in:
Tobias Wölfel 2019-06-12 10:45:00 +02:00 committed by Philipp Wagner
parent 1e2d5b7eda
commit 014c753dde
8 changed files with 237 additions and 134 deletions

View file

@ -16,6 +16,7 @@ Ibex User Manual
exception_interrupts
interrupts
debug
tracer
rvfi

View file

@ -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`.

11
doc/tracer.rst Normal file
View file

@ -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.

View file

@ -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

View file

@ -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

180
rtl/ibex_core_tracer.sv Normal file
View file

@ -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

View file

@ -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 //
///////////////////////

View file

@ -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)