Skip to content

Commit 4cf30ed

Browse files
✨ Add hci_transaction_tracer and the hci-tracer comparison utility
Add a purely passive, simulation-only monitor that observes an HCI-Core stream through the `monitor` modport of an `hci_core_intf` interface and appends every transaction to a JSON log file. HCI-Core being bidirectional, two logs are produced, named by the `REQ_LOG_FILE` and `RSP_LOG_FILE` parameters: accepted requests (`req & gnt`) and consumed responses (`r_valid & r_ready`). Side channels are logged only when their width parameter is non-zero. The body of the module sits inside an `ifndef SYNTHESIS` guard, so the tracer can be instantiated unconditionally and elaborates to an empty module under synthesis. Each log is a single well-formed JSON document, closed by a `final` block, and follows one of the new `hci_transaction_request-v1` / `hci_transaction_response-v1` JSON schemas in `tracer/`. `tracer/` also holds `hci-tracer`, a Rust utility that compares such logs: hci-tracer hci-vs-hci --a-req F --a-rsp F --b-req F --b-rsp F hci-tracer hci-req-vs-stream --hci F --stream F hci-tracer hci-rsp-vs-stream --hci F --stream F hci-tracer stream-vs-stream --a F --b F Comparison runs on the content of valid data only, never on the time of a transaction: `seq` and `cycle` are reported so a difference can be found in a waveform, but they are structurally excluded from the comparison keys. Byte enables are canonicalized to bit-level masks before being compared, so `be` and `strb` encodings with different granularities compare equal when they enable the same data bits, and bytes neither side considers meaningful are don't-care. The two sequences are aligned with Myers' algorithm, so an inserted transaction is reported as an insertion instead of cascading into a mismatch for every transaction after it. Output is hexadecimal, with only the differing nibbles painted red, a caret row when colour is off, wide values folded into 64-bit rows, and a summary table per section. Logs cut short by an aborted simulation are repaired automatically. Includes unit tests for every module and integration tests over synthetic logs in `tracer/tests/fixtures/`, regenerated by `gen_fixtures.py`.
1 parent a8d8132 commit 4cf30ed

64 files changed

Lines changed: 8437 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Bender.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ sources:
3434
- rtl/common/hci_interfaces.sv
3535
- rtl/ecc/hci_ecc_manager_reg_top.sv
3636
# Level 2
37+
- rtl/common/hci_transaction_tracer.sv
3738
- rtl/core/hci_core_assign.sv
3839
- rtl/core/hci_core_assign_expand.sv
3940
- rtl/core/hci_core_cut.sv
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/*
2+
* hci_transaction_tracer.sv
3+
* Francesco Conti <f.conti@unibo.it>
4+
*
5+
* Copyright (C) 2026 ETH Zurich, University of Bologna
6+
* Copyright and related rights are licensed under the Solderpad Hardware
7+
* License, Version 0.51 (the "License"); you may not use this file except in
8+
* compliance with the License. You may obtain a copy of the License at
9+
* http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
10+
* or agreed to in writing, software, hardware and materials distributed under
11+
* this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12+
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
13+
* specific language governing permissions and limitations under the License.
14+
*/
15+
16+
/**
17+
* The **hci_transaction_tracer** module is a purely passive, simulation-only
18+
* monitor: it observes an HCI-Core stream through the `monitor` modport of an
19+
* `hci_core_intf` interface and appends every transaction to a JSON log file.
20+
* Since HCI-Core is bidirectional, two log files are produced, whose names are
21+
* given by the `REQ_LOG_FILE` and `RSP_LOG_FILE` parameters:
22+
*
23+
* - the *request* log records one entry per **accepted** request, i.e. per
24+
* cycle in which `req & gnt` holds, with the `add`, `wen`, `data` and `be`
25+
* payload and, when the corresponding width is non-zero, the `user`, `id`
26+
* and `ecc` side channels;
27+
* - the *response* log records one entry per **consumed** response, i.e. per
28+
* cycle in which `r_valid & r_ready` holds, with the `r_data` and `r_opc`
29+
* payload and, when the corresponding width is non-zero, the `r_user`,
30+
* `r_id` and `r_ecc` side channels.
31+
*
32+
* Their formats are described by the `hci_transaction_request-v1` and
33+
* `hci_transaction_response-v1` JSON schemas in `tracer/`. The resulting logs
34+
* can be compared against each other -- or against HWPE-Stream logs produced
35+
* by **hwpe_stream_transaction_tracer** -- with the `hci-tracer` utility found
36+
* in `tracer/`.
37+
*
38+
* Each log is a single well-formed JSON document: the header and the opening of
39+
* the `transactions` array are emitted by an `initial` block, one object per
40+
* transaction is emitted as the simulation progresses, and the array and object
41+
* are closed by a `final` block. Should the simulation be aborted before the
42+
* `final` block runs, the resulting truncated files are still accepted (and
43+
* repaired) by `hci-tracer`.
44+
*
45+
* Logging is gated by `enable_i`, which can be used to restrict tracing to a
46+
* region of interest; `cycle` counts clock cycles from the release of `rst_ni`
47+
* and is reported for waveform cross-reference only -- transaction *time* is
48+
* never used when comparing two logs.
49+
*
50+
* The body of the module is enclosed in a `ifndef SYNTHESIS guard, so that the
51+
* tracer can be instantiated unconditionally: under synthesis it elaborates to
52+
* an empty module.
53+
*/
54+
55+
`include "hci_helpers.svh"
56+
57+
module hci_transaction_tracer
58+
import hci_package::*;
59+
#(
60+
parameter hci_size_parameter_t `HCI_SIZE_PARAM(tcdm) = '0,
61+
parameter string REQ_LOG_FILE = "hci_trace_req.json",
62+
parameter string RSP_LOG_FILE = "hci_trace_rsp.json"
63+
)
64+
(
65+
input logic clk_i,
66+
input logic rst_ni,
67+
input logic enable_i,
68+
hci_core_intf.monitor tcdm
69+
);
70+
71+
`ifndef SYNTHESIS
72+
73+
localparam int unsigned DW = `HCI_SIZE_GET_DW(tcdm);
74+
localparam int unsigned AW = `HCI_SIZE_GET_AW(tcdm);
75+
localparam int unsigned BW = `HCI_SIZE_GET_BW(tcdm);
76+
localparam int unsigned UW = `HCI_SIZE_GET_UW(tcdm);
77+
localparam int unsigned IW = `HCI_SIZE_GET_IW(tcdm);
78+
localparam int unsigned EW = `HCI_SIZE_GET_EW(tcdm);
79+
80+
int fd_req;
81+
int fd_rsp;
82+
longint unsigned cycle_q;
83+
longint unsigned seq_req;
84+
longint unsigned seq_rsp;
85+
string sep_req;
86+
string sep_rsp;
87+
string record;
88+
89+
function automatic string interface_json();
90+
return $sformatf({"{ \"DW\": %0d, \"AW\": %0d, \"BW\": %0d,",
91+
" \"UW\": %0d, \"IW\": %0d, \"EW\": %0d }"},
92+
DW, AW, BW, UW, IW, EW);
93+
endfunction // interface_json
94+
95+
initial
96+
begin
97+
fd_req = $fopen(REQ_LOG_FILE, "w");
98+
if(fd_req == 0) begin
99+
$fatal(1, "[hci_transaction_tracer] could not open log file %s", REQ_LOG_FILE);
100+
end
101+
fd_rsp = $fopen(RSP_LOG_FILE, "w");
102+
if(fd_rsp == 0) begin
103+
$fatal(1, "[hci_transaction_tracer] could not open log file %s", RSP_LOG_FILE);
104+
end
105+
seq_req = 0;
106+
seq_rsp = 0;
107+
sep_req = "";
108+
sep_rsp = "";
109+
$fwrite(fd_req, "{\n \"schema\": \"hci_transaction_request-v1\",\n");
110+
$fwrite(fd_rsp, "{\n \"schema\": \"hci_transaction_response-v1\",\n");
111+
$fwrite(fd_req, " \"interface\": %s,\n", interface_json());
112+
$fwrite(fd_rsp, " \"interface\": %s,\n", interface_json());
113+
$fwrite(fd_req, " \"path\": \"%m\",\n \"transactions\": [");
114+
$fwrite(fd_rsp, " \"path\": \"%m\",\n \"transactions\": [");
115+
end
116+
117+
always_ff @(posedge clk_i or negedge rst_ni)
118+
begin
119+
if(~rst_ni) begin
120+
cycle_q <= '0;
121+
end
122+
else begin
123+
cycle_q <= cycle_q + 1;
124+
end
125+
end
126+
127+
/*
128+
* Request logging: one record per accepted request (req & gnt)
129+
*/
130+
always @(posedge clk_i)
131+
begin
132+
if(rst_ni & enable_i & tcdm.req & tcdm.gnt & (fd_req != 0)) begin
133+
record = $sformatf("{\"seq\": %0d, \"cycle\": %0d, \"add\": \"0x%h\", \"wen\": %0d",
134+
seq_req, cycle_q, tcdm.add, tcdm.wen);
135+
record = {record, $sformatf(", \"data\": \"0x%h\", \"be\": \"0x%h\"", tcdm.data, tcdm.be)};
136+
if(UW > 0) begin
137+
record = {record, $sformatf(", \"user\": \"0x%h\"", tcdm.user)};
138+
end
139+
if(IW > 0) begin
140+
record = {record, $sformatf(", \"id\": \"0x%h\"", tcdm.id)};
141+
end
142+
if(EW > 0) begin
143+
record = {record, $sformatf(", \"ecc\": \"0x%h\"", tcdm.ecc)};
144+
end
145+
$fwrite(fd_req, "%s\n %s}", sep_req, record);
146+
sep_req = ",";
147+
seq_req = seq_req + 1;
148+
end
149+
end
150+
151+
/*
152+
* Response logging: one record per consumed response (r_valid & r_ready)
153+
*/
154+
always @(posedge clk_i)
155+
begin
156+
if(rst_ni & enable_i & tcdm.r_valid & tcdm.r_ready & (fd_rsp != 0)) begin
157+
record = $sformatf("{\"seq\": %0d, \"cycle\": %0d, \"r_data\": \"0x%h\", \"r_opc\": %0d",
158+
seq_rsp, cycle_q, tcdm.r_data, tcdm.r_opc);
159+
if(UW > 0) begin
160+
record = {record, $sformatf(", \"r_user\": \"0x%h\"", tcdm.r_user)};
161+
end
162+
if(IW > 0) begin
163+
record = {record, $sformatf(", \"r_id\": \"0x%h\"", tcdm.r_id)};
164+
end
165+
if(EW > 0) begin
166+
record = {record, $sformatf(", \"r_ecc\": \"0x%h\"", tcdm.r_ecc)};
167+
end
168+
$fwrite(fd_rsp, "%s\n %s}", sep_rsp, record);
169+
sep_rsp = ",";
170+
seq_rsp = seq_rsp + 1;
171+
end
172+
end
173+
174+
final
175+
begin
176+
if(fd_req != 0) begin
177+
$fwrite(fd_req, "\n ]\n}\n");
178+
$fclose(fd_req);
179+
end
180+
if(fd_rsp != 0) begin
181+
$fwrite(fd_rsp, "\n ]\n}\n");
182+
$fclose(fd_rsp);
183+
end
184+
end
185+
186+
/*
187+
* Interface size asserts
188+
*/
189+
`ifndef VERILATOR
190+
`ifndef VCS
191+
`HCI_SIZE_CHECK_ASSERTS(tcdm);
192+
`endif
193+
`endif
194+
195+
`endif /* `ifndef SYNTHESIS */
196+
197+
endmodule // hci_transaction_tracer

src_files.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ hci:
1616
- rtl/common/hci_package.sv
1717
# Level 1
1818
- rtl/common/hci_interfaces.sv
19+
- rtl/common/hci_transaction_tracer.sv
1920
- rtl/core/hci_core_assign.sv
2021
- rtl/core/hci_core_fifo.sv
2122
- rtl/core/hci_core_mux_dynamic.sv

tracer/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target/

0 commit comments

Comments
 (0)