From 2e173f9b56a28690c2d95960145e7b5ed6f49179 Mon Sep 17 00:00:00 2001
From: Thomas Benz <tbenz@iis.ee.ethz.ch>
Date: Fri, 27 Oct 2023 16:13:29 +0200
Subject: [PATCH] tracer: Update tracer to the multiprotocol version of iDMA
 (#8)

* Add a tracer for the DMA #8
---
 Bender.yml                            |   1 +
 idma.mk                               |  35 ++++++-
 requirements.txt                      |   1 +
 src/db/idma_axi.yml                   |  10 ++
 src/db/idma_axi_lite.yml              |  10 ++
 src/db/idma_axi_stream.yml            |  11 +++
 src/db/idma_init.yml                  |   9 ++
 src/db/idma_obi.yml                   |  11 +++
 src/db/idma_tilelink.yml              |  10 ++
 src/include/idma/tpl/tracer.svh.tpl   |  30 ++++++
 src/include/idma/tracer.svh           | 101 --------------------
 target/rtl/.gitignore                 |   1 +
 target/rtl/tpl/Bender.yml.tpl         |   1 +
 test/frontend/tb_idma_desc64_bench.sv |   2 +-
 test/frontend/tb_idma_desc64_top.sv   |   2 +-
 test/tb_idma_nd_backend.sv            |  27 +++++-
 test/tpl/tb_idma_backend.sv.tpl       |  20 +++-
 util/gen_idma.py                      |   5 +-
 util/mario/synth.py                   |   2 +-
 util/mario/tracer.py                  | 132 ++++++++++++++++++++++++++
 util/trace_idma.py                    |  68 ++++++++++---
 21 files changed, 360 insertions(+), 129 deletions(-)
 create mode 100644 src/include/idma/tpl/tracer.svh.tpl
 delete mode 100644 src/include/idma/tracer.svh
 create mode 100644 util/mario/tracer.py

diff --git a/Bender.yml b/Bender.yml
index 342c867b..7c8dbd29 100644
--- a/Bender.yml
+++ b/Bender.yml
@@ -19,6 +19,7 @@ dependencies:
 
 export_include_dirs:
   - src/include
+  - target/rtl/include
 
 sources:
   # Source files grouped in levels. Files in level 0 have no dependencies on files in this
diff --git a/idma.mk b/idma.mk
index 0db0a202..acd88d62 100644
--- a/idma.mk
+++ b/idma.mk
@@ -68,9 +68,11 @@ IDMA_GEN        := $(IDMA_UTIL_DIR)/gen_idma.py
 IDMA_GEN_SRC    := $(IDMA_UTIL_DIR)/mario/backend.py \
 				   $(IDMA_UTIL_DIR)/mario/bender.py \
 				   $(IDMA_UTIL_DIR)/mario/database.py \
+				   $(IDMA_UTIL_DIR)/mario/frontend.py \
 				   $(IDMA_UTIL_DIR)/mario/legalizer.py \
 				   $(IDMA_UTIL_DIR)/mario/synth.py \
 				   $(IDMA_UTIL_DIR)/mario/testbench.py \
+				   $(IDMA_UTIL_DIR)/mario/tracer.py \
 				   $(IDMA_UTIL_DIR)/mario/transport_layer.py \
 				   $(IDMA_UTIL_DIR)/mario/util.py \
 				   $(IDMA_UTIL_DIR)/mario/wave.py
@@ -112,12 +114,18 @@ $(IDMA_RTL_DIR)/tb_idma_backend_%.sv: $(IDMA_GEN) $(IDMA_RTL_DIR)/idma_backend_%
 $(IDMA_VSIM_DIR)/wave/backend_%.do: $(IDMA_GEN) $(IDMA_RTL_DIR)/tb_idma_backend_%.sv $(IDMA_VSIM_DIR)/wave/tpl/backend.do.tpl
 	$(call idma_gen,vsim_wave,$(IDMA_VSIM_DIR)/wave/tpl/backend.do.tpl,$(IDMA_DB_FILES),$*,,$@)
 
+$(IDMA_RTL_DIR)/include/idma/tracer.svh: $(IDMA_GEN) $(IDMA_GEN_SRC) $(IDMA_ROOT)/src/include/idma/tpl/tracer.svh.tpl $(IDMA_DB_FILES) $(IDMA_ROOT)/idma.mk
+	mkdir -p $(IDMA_RTL_DIR)/include/idma
+	$(call idma_gen,tracer,$(IDMA_ROOT)/src/include/idma/tpl/tracer.svh.tpl,$(IDMA_DB_FILES),$(IDMA_BACKEND_IDS),$(IDMA_FE_IDS),$@)
+
 idma_rtl_clean:
 	rm -f $(IDMA_RTL_DIR)/Bender.yml
 	rm -f $(IDMA_RTL_DIR)/*.sv
 	rm -f $(IDMA_VSIM_DIR)/wave/*.do
+	rm -f $(IDMA_RTL_DIR)/include/idma/tracer.svh
 
 # assemble the required files
+IDMA_RTL_ALL += $(IDMA_RTL_DIR)/include/idma/tracer.svh
 IDMA_RTL_ALL += $(foreach X,$(IDMA_RTL_FILES),$(foreach Y,$(IDMA_BACKEND_IDS),$X_$Y.sv))
 IDMA_TB_ALL  += $(foreach Y,$(IDMA_BACKEND_IDS),$(IDMA_RTL_DIR)/tb_idma_backend_$Y.sv)
 IDMA_TB_ALL  += $(foreach Y,$(IDMA_BACKEND_IDS),$(IDMA_VSIM_DIR)/wave/backend_$Y.do)
@@ -263,12 +271,12 @@ idma_sim_clean:
 	rm -f  $(IDMA_VSIM_DIR)/dma_transfers.txt
 	rm -f  $(IDMA_VSIM_DIR)/transcript
 	rm -f  $(IDMA_VSIM_DIR)/wlf*
-	rm -f  $(IDMA_VSIM_DIR)/logs/wlf*
-	rm -f  $(IDMA_VSIM_DIR)/logs/*.wlf
+	rm -f  $(IDMA_VSIM_DIR)/*.wlf
 	rm -f  $(IDMA_VSIM_DIR)/*.vstf
 	rm -f  $(IDMA_VSIM_DIR)/*.vcd
 	rm -f  $(IDMA_VSIM_DIR)/modelsim.ini
-	rm -f  $(IDMA_VSIM_DIR)/logs/*vsim.log
+	rm -f  $(IDMA_VSIM_DIR)/*.log
+	rm -f  $(IDMA_VSIM_DIR)/*.txt
 
 
 # --------------
@@ -312,7 +320,8 @@ idma_vcs_clean:
 	rm -rf $(IDMA_VCS_DIR)/bin
 	rm -f  $(IDMA_VCS_DIR)/ucli.key
 	rm -f  $(IDMA_VCS_DIR)/vc_hdrs.h
-	rm -f  $(IDMA_VCS_DIR)/logs/*.vcs.log
+	rm -f  $(IDMA_VCS_DIR)/*.log
+	rm -f  $(IDMA_VCS_DIR)/*.txt
 
 
 # --------------
@@ -346,6 +355,22 @@ idma_verilator_clean:
 	rm -rf $(IDMA_VLT_DIR)
 
 
+# ---------------
+# Trace
+# ---------------
+
+.PHONY: idma_trace_clean
+
+IDMA_TRACE := $(IDMA_UTIL_DIR)/trace_idma.py
+
+%_trace.rpt: $(IDMA_TRACE) $(IDMA_DB_FILES) %.txt
+	$(PYTHON) $(IDMA_TRACE) --db $(IDMA_DB_FILES) --trace $*.txt > $@
+
+idma_trace_clean:
+	rm -f $(IDMA_VSIM_DIR)/*_trace.rpt
+	rm -f $(IDMA_VCS_DIR)/*_trace.rpt
+
+
 # ---------------
 # Doc
 # ---------------
@@ -385,7 +410,7 @@ idma_nonfree_clean:
 
 .PHONY: idma_clean_all idma_clean idma_misc_clean
 
-idma_clean_all idma_clean: idma_rtl_clean idma_reg_clean idma_morty_clean idma_sim_clean idma_vcs_clean idma_verilator_clean idma_spinx_doc_clean
+idma_clean_all idma_clean: idma_rtl_clean idma_reg_clean idma_morty_clean idma_sim_clean idma_vcs_clean idma_verilator_clean idma_spinx_doc_clean idma_trace_clean
 
 idma_misc_clean:
 	rm -rf scripts/__pycache__
diff --git a/requirements.txt b/requirements.txt
index 3bef2fab..1bfe7864 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,3 +8,4 @@ sphinx-rtd-theme
 recommonmark
 sphinxcontrib-svg2pdfconverter
 pylint
+flatdict
diff --git a/src/db/idma_axi.yml b/src/db/idma_axi.yml
index 1d308ea3..91b2c0a9 100644
--- a/src/db/idma_axi.yml
+++ b/src/db/idma_axi.yml
@@ -224,3 +224,13 @@ synth_wrapper_assign_read: |
     assign axi_read_rsp.r.last   = axi_r_last_i;
     assign axi_read_rsp.r.user   = axi_r_user_i;
     assign axi_read_rsp.r_valid  = axi_r_valid_i;
+trace_signals:
+    read:
+        rsp:
+            valid: axi_read_rsp_i.r_valid
+            ready: axi_read_req_o.r_ready
+    write:
+        req:
+            valid: axi_write_req_o.w_valid
+            ready: axi_write_rsp_i.w_ready
+            strobe: axi_write_req_o.w.strb
diff --git a/src/db/idma_axi_lite.yml b/src/db/idma_axi_lite.yml
index bd6e42f1..0f40fe08 100644
--- a/src/db/idma_axi_lite.yml
+++ b/src/db/idma_axi_lite.yml
@@ -157,3 +157,13 @@ synth_wrapper_assign_read: |
     assign axi_lite_read_rsp.r.data    = axi_lite_r_data_i;
     assign axi_lite_read_rsp.r.resp    = axi_lite_r_resp_i;
     assign axi_lite_read_rsp.r_valid   = axi_lite_r_valid_i;
+trace_signals:
+    read:
+        rsp:
+            valid: axi_lite_read_rsp_i.r_valid
+            ready: axi_lite_read_req_o.r_ready
+    write:
+        req:
+            valid: axi_lite_write_req_o.w_valid
+            ready: axi_lite_write_rsp_i.w_ready
+            strobe: axi_lite_write_req_o.w.strb
diff --git a/src/db/idma_axi_stream.yml b/src/db/idma_axi_stream.yml
index b19ca710..04cb6e33 100644
--- a/src/db/idma_axi_stream.yml
+++ b/src/db/idma_axi_stream.yml
@@ -320,3 +320,14 @@ synth_wrapper_assign_write: |
     assign axi_stream_write_tvalid_o = axi_stream_write_req.tvalid;
 
     assign axi_stream_write_rsp.tready = axi_stream_write_tready_i;
+trace_signals:
+    read:
+        rsp:
+            valid: axi_stream_read_rsp_i.tvalid
+            ready: axi_stream_read_req_o.tready
+            strobe: axi_stream_read_rsp_i.t.strb
+    write:
+        req:
+            valid: axi_stream_write_req_o.tvalid
+            ready: axi_stream_write_rsp_i.tready
+            strobe: axi_stream_write_req_o.t.strb
diff --git a/src/db/idma_init.yml b/src/db/idma_init.yml
index 58fc51d9..bc30c5bc 100644
--- a/src/db/idma_init.yml
+++ b/src/db/idma_init.yml
@@ -100,3 +100,12 @@ synth_wrapper_assign_read: |
     assign init_read_rsp.rsp_valid           = init_read_rsp_valid_i;
     assign init_read_rsp.rsp_chan.init_value = init_read_rsp_init_value_i;
     assign init_read_rsp_ready_o             = init_read_req.rsp_ready;
+trace_signals:
+    read:
+        req:
+            valid: init_read_req_o.req_valid
+            config: init_read_req_o.req_chan.cfg
+            ready: init_read_rsp_i.req_ready
+        rsp:
+            valid: init_read_rsp_i.rsp_valid
+            ready: init_read_req_o.rsp_ready
diff --git a/src/db/idma_obi.yml b/src/db/idma_obi.yml
index 4a7d7aa4..86802252 100644
--- a/src/db/idma_obi.yml
+++ b/src/db/idma_obi.yml
@@ -161,3 +161,14 @@ synth_wrapper_assign_read: |
     assign obi_read_rsp.r_valid = obi_read_rsp_r_valid_i;
     assign obi_read_rsp.r.rdata = obi_read_rsp_r_rdata_i;
     assign obi_read_rsp.r.rid   = obi_read_rsp_r_rid_i;
+trace_signals:
+    read:
+        rsp:
+            valid: obi_write_req_o.r_valid
+            ready: obi_write_rsp_i.r_ready
+    write:
+        req:
+            valid: obi_write_req_o.a_req
+            ready: obi_write_rsp_i.a_gnt
+            strobe: obi_write_req_o.a.be
+            write_en: obi_write_req_o.a.we
diff --git a/src/db/idma_tilelink.yml b/src/db/idma_tilelink.yml
index 4edf7d9e..273fed38 100644
--- a/src/db/idma_tilelink.yml
+++ b/src/db/idma_tilelink.yml
@@ -241,3 +241,13 @@ synth_wrapper_assign_read: |
     assign tilelink_read_rsp.d.denied  = tilelink_read_rsp_d_denied_i;
     assign tilelink_read_rsp.d.data    = tilelink_read_rsp_d_data_i;
     assign tilelink_read_rsp.d.corrupt = tilelink_read_rsp_d_corrupt_i;
+trace_signals:
+    read:
+        rsp:
+            valid: tilelink_read_rsp_i.d_valid
+            ready: tilelink_read_req_o.d_ready
+    write:
+        req:
+            valid: tilelink_write_req_o.a_valid
+            ready: tilelink_write_rsp_i.a_ready
+            strobe: tilelink_write_req_o.a.mask
diff --git a/src/include/idma/tpl/tracer.svh.tpl b/src/include/idma/tpl/tracer.svh.tpl
new file mode 100644
index 00000000..457c5d91
--- /dev/null
+++ b/src/include/idma/tpl/tracer.svh.tpl
@@ -0,0 +1,30 @@
+// Copyright 2023 ETH Zurich and University of Bologna.
+// Solderpad Hardware License, Version 0.51, see LICENSE for details.
+// SPDX-License-Identifier: SHL-0.51
+//
+// Authors:
+// - Thomas Benz <tbenz@ethz.ch>
+
+// Macro holding all the resources for the iDMA backend tracer
+`ifndef IDMA_TRACER_SVH_
+`define IDMA_TRACER_SVH_
+
+// largest type to trace
+`define IDMA_TRACER_MAX_TYPE_WIDTH 1024
+`define IDMA_TRACER_MAX_TYPE logic [`IDMA_TRACER_MAX_TYPE_WIDTH-1:0]
+
+// string assembly function
+`define IDMA_TRACER_STR_ASSEMBLY(__dict, __cond) <%text>\</%text>
+    if(__cond) begin <%text>\</%text>
+        trace = $sformatf("%s'%s':{", trace, `"__dict`"); <%text>\</%text>
+        foreach(__dict``[key]) trace = $sformatf("%s'%s': 0x%0x,", trace, key, __dict``[key]); <%text>\</%text>
+        trace = $sformatf("%s},", trace); <%text>\</%text>
+    end
+
+// helper to clear a condition
+`define IDMA_TRACER_CLEAR_COND(__cond) <%text>\</%text>
+    if(__cond) begin <%text>\</%text>
+        __cond = ~__cond; <%text>\</%text>
+    end
+${body}
+`endif
diff --git a/src/include/idma/tracer.svh b/src/include/idma/tracer.svh
deleted file mode 100644
index d8f6740d..00000000
--- a/src/include/idma/tracer.svh
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2022 ETH Zurich and University of Bologna.
-// Solderpad Hardware License, Version 0.51, see LICENSE for details.
-// SPDX-License-Identifier: SHL-0.51
-//
-// Thomas Benz <tbenz@ethz.ch>
-
-// Macro holding all the resources for the iDMA backend tracer
-`ifndef IDMA_TRACER_SVH_
-`define IDMA_TRACER_SVH_
-
-// largest type to trace
-`define IDMA_TRACER_MAX_TYPE_WIDTH 1024
-`define IDMA_TRACER_MAX_TYPE logic [`IDMA_TRACER_MAX_TYPE_WIDTH-1:0]
-
-// string assembly function
-`define IDMA_TRACER_STR_ASSEMBLY(__dict, __cond)                                                   \
-    if(__cond) begin                                                                               \
-        trace = $sformatf("%s'%s':{", trace, `"__dict`");                                          \
-        foreach(__dict``[key]) trace = $sformatf("%s'%s': 0x%0x,", trace, key, __dict``[key]);     \
-        trace = $sformatf("%s},", trace);                                                          \
-    end
-
-// helper to clear a condition
-`define IDMA_TRACER_CLEAR_COND(__cond)                                                             \
-    if(__cond) begin                                                                               \
-        __cond = ~__cond;                                                                          \
-    end
-
-// The tracer for the iDMA
-`define IDMA_TRACER(__backend_inst, __out_f_name)                                                  \
-`ifndef SYNTHESYS                                                                                  \
-`ifndef VERILATOR                                                                                  \
-    initial begin : inital_tracer                                                                  \
-        automatic bit first_iter = 1;                                                              \
-        automatic integer tf;                                                                      \
-        automatic `IDMA_TRACER_MAX_TYPE cnst [string];                                             \
-        automatic `IDMA_TRACER_MAX_TYPE meta [string];                                             \
-        automatic `IDMA_TRACER_MAX_TYPE busy [string];                                             \
-        automatic `IDMA_TRACER_MAX_TYPE axib [string];                                             \
-        automatic string trace;                                                                    \
-        #0;                                                                                        \
-        tf = $fopen(__out_f_name, "w");                                                            \
-        $display("[Tracer] Logging iDMA backend %s to %s", `"__backend_inst`", __out_f_name);      \
-        forever begin                                                                              \
-            @(posedge __backend_inst``.clk_i);                                                     \
-            if(__backend_inst``.rst_ni & |__backend_inst``.busy_o) begin                           \
-                /* Trace */                                                                        \
-                trace = "{";                                                                       \
-                /* Constants */                                                                    \
-                cnst = '{                                                                          \
-                    "inst"                  : `"__backend_inst`",                                  \
-                    "data_width"            : __backend_inst``.DataWidth,                          \
-                    "addr_width"            : __backend_inst``.AddrWidth,                          \
-                    "user_width"            : __backend_inst``.UserWidth,                          \
-                    "axi_id_width"          : __backend_inst``.AxiIdWidth,                         \
-                    "num_ax_in_flight"      : __backend_inst``.NumAxInFlight,                      \
-                    "buffer_depth"          : __backend_inst``.BufferDepth,                        \
-                    "tf_len_width"          : __backend_inst``.TFLenWidth,                         \
-                    "mem_sys_depth"         : __backend_inst``.MemSysDepth,                        \
-                    "rw_coupling_avail"     : __backend_inst``.RAWCouplingAvail,                   \
-                    "mask_invalid_data"     : __backend_inst``.MaskInvalidData,                    \
-                    "hardware_legalizer"    : __backend_inst``.HardwareLegalizer,                  \
-                    "reject_zero_transfers" : __backend_inst``.RejectZeroTransfers,                \
-                    "error_cap"             : __backend_inst``.ErrorCap,                           \
-                    "print_fifo_info"       : __backend_inst``.PrintFifoInfo                       \
-                };                                                                                 \
-                meta = '{                                                                          \
-                    "time" : $time()                                                               \
-                };                                                                                 \
-                busy = '{                                                                          \
-                    "buffer"      : __backend_inst``.busy_o.buffer_busy,                           \
-                    "r_dp"        : __backend_inst``.busy_o.r_dp_busy,                             \
-                    "w_dp"        : __backend_inst``.busy_o.w_dp_busy,                             \
-                    "r_leg"       : __backend_inst``.busy_o.r_leg_busy,                            \
-                    "w_leg"       : __backend_inst``.busy_o.w_leg_busy,                            \
-                    "eh_fsm"      : __backend_inst``.busy_o.eh_fsm_busy,                           \
-                    "eh_cnt"      : __backend_inst``.busy_o.eh_cnt_busy,                           \
-                    "raw_coupler" : __backend_inst``.busy_o.raw_coupler_busy                       \
-                };                                                                                 \
-                axib = '{                                                                          \
-                    "w_valid" : __backend_inst``.axi_req_o.w_valid,                                \
-                    "w_ready" : __backend_inst``.axi_rsp_i.w_ready,                                \
-                    "w_strb"  : __backend_inst``.axi_req_o.w.strb,                                 \
-                    "r_valid" : __backend_inst``.axi_rsp_i.r_valid,                                \
-                    "r_ready" : __backend_inst``.axi_req_o.r_ready                                 \
-                };                                                                                 \
-                /* Assembly */                                                                     \
-                `IDMA_TRACER_STR_ASSEMBLY(cnst, first_iter);                                       \
-                `IDMA_TRACER_STR_ASSEMBLY(meta, 1);                                                \
-                `IDMA_TRACER_STR_ASSEMBLY(busy, 1);                                                \
-                `IDMA_TRACER_STR_ASSEMBLY(axib, 1);                                                \
-                `IDMA_TRACER_CLEAR_COND(first_iter);                                               \
-                /* Commit */                                                                       \
-                $fwrite(tf, $sformatf("%s}\n", trace));                                            \
-            end                                                                                    \
-        end                                                                                        \
-    end                                                                                            \
-`endif                                                                                             \
-`endif
-
-`endif
diff --git a/target/rtl/.gitignore b/target/rtl/.gitignore
index 9eddf5ef..a2e0e0a0 100644
--- a/target/rtl/.gitignore
+++ b/target/rtl/.gitignore
@@ -1,3 +1,4 @@
 Bender.yml
+include
 *.sv
 *.hjson
diff --git a/target/rtl/tpl/Bender.yml.tpl b/target/rtl/tpl/Bender.yml.tpl
index b449aee0..03641b51 100644
--- a/target/rtl/tpl/Bender.yml.tpl
+++ b/target/rtl/tpl/Bender.yml.tpl
@@ -16,6 +16,7 @@ dependencies:
 export_include_dirs:
   - ../../src/include
   - ../../test
+  - include
 
 sources:
   # Source files grouped in levels. Files in level 0 have no dependencies on files in this
diff --git a/test/frontend/tb_idma_desc64_bench.sv b/test/frontend/tb_idma_desc64_bench.sv
index 73191aa7..d7f42c3d 100644
--- a/test/frontend/tb_idma_desc64_bench.sv
+++ b/test/frontend/tb_idma_desc64_bench.sv
@@ -97,7 +97,7 @@ module tb_idma_desc64_bench
 
     // set seed
     initial begin
-        int drop = $urandom(Seed);
+        automatic int drop = $urandom(Seed);
     end
 
     class stimulus_t;
diff --git a/test/frontend/tb_idma_desc64_top.sv b/test/frontend/tb_idma_desc64_top.sv
index cde305f2..9391b0d4 100644
--- a/test/frontend/tb_idma_desc64_top.sv
+++ b/test/frontend/tb_idma_desc64_top.sv
@@ -64,7 +64,7 @@ module tb_idma_desc64_top
 
     // set seed
     initial begin
-        int drop = $urandom(Seed);
+        automatic int drop = $urandom(Seed);
     end
 
     class stimulus_t;
diff --git a/test/tb_idma_nd_backend.sv b/test/tb_idma_nd_backend.sv
index 2c467c39..02741080 100644
--- a/test/tb_idma_nd_backend.sv
+++ b/test/tb_idma_nd_backend.sv
@@ -8,6 +8,7 @@
 
 `timescale 1ns/1ns
 `include "axi/typedef.svh"
+`include "idma/tracer.svh"
 `include "idma/typedef.svh"
 
 // Protocol testbench defines
@@ -34,7 +35,8 @@ module tb_idma_nd_backend import idma_pkg::*; #(
     parameter bit          HardwareLegalizer   = 1,
     parameter bit          RejectZeroTransfers = 1,
     parameter bit          ErrorHandling       = 1,
-    parameter bit          IdealMemory         = 1
+    parameter bit          IdealMemory         = 1,
+    parameter bit          DmaTracing          = 1
 );
 
     // timing parameters
@@ -422,6 +424,26 @@ module tb_idma_nd_backend import idma_pkg::*; #(
         .busy_o          ( busy            )
     );
 
+
+    //--------------------------------------
+    // DMA Tracer
+    //--------------------------------------
+    // only activate tracer if requested
+    if (DmaTracing) begin
+        // fetch the name of the trace file from CMD line
+        string trace_file;
+        initial begin
+            void'($value$plusargs("trace_file=%s", trace_file));
+        end
+        // attach the tracer
+        `IDMA_TRACER_RW_AXI(i_idma_backend, trace_file);
+    end
+
+
+    //--------------------------------------
+    // TB connections
+    //--------------------------------------
+
     // Read Write Join
     axi_rw_join #(
         .axi_req_t        ( axi_req_t ),
@@ -437,9 +459,6 @@ module tb_idma_nd_backend import idma_pkg::*; #(
         .mst_resp_i       ( axi_rsp       )
     );
 
-    //--------------------------------------
-    // TB connections
-    //--------------------------------------
     // connect virtual driver interface to structs
     assign nd_req                   = idma_nd_dv.req;
     assign nd_req_valid             = idma_nd_dv.req_valid;
diff --git a/test/tpl/tb_idma_backend.sv.tpl b/test/tpl/tb_idma_backend.sv.tpl
index e09c260e..3c3154b4 100644
--- a/test/tpl/tb_idma_backend.sv.tpl
+++ b/test/tpl/tb_idma_backend.sv.tpl
@@ -7,6 +7,7 @@
 
 `timescale 1ns/1ns
 `include "axi/typedef.svh"
+`include "idma/tracer.svh"
 `include "idma/typedef.svh"
 
 // Protocol testbench defines
@@ -53,7 +54,8 @@ module tb_idma_backend_${name_uniqueifier} import idma_pkg::*; #(
 %endif
     parameter bit          HardwareLegalizer     = 1,
     parameter bit          RejectZeroTransfers   = 1,
-    parameter bit          ErrorHandling         = 0
+    parameter bit          ErrorHandling         = 0,
+    parameter bit          DmaTracing            = 1
 );
 
     // timing parameters
@@ -590,6 +592,22 @@ ${p}_${database[p]['write_meta_channel']}_width\
         .busy_o               ( busy            )
     );
 
+
+    //--------------------------------------
+    // DMA Tracer
+    //--------------------------------------
+    // only activate tracer if requested
+    if (DmaTracing) begin
+        // fetch the name of the trace file from CMD line
+        string trace_file;
+        initial begin
+            void'($value$plusargs("trace_file=%s", trace_file));
+        end
+        // attach the tracer
+        `IDMA_TRACER_${name_uniqueifier.upper()}(i_idma_backend, trace_file);
+    end
+
+
     //--------------------------------------
     // TB connections
     //--------------------------------------
diff --git a/util/gen_idma.py b/util/gen_idma.py
index cbf78711..faffb2e4 100644
--- a/util/gen_idma.py
+++ b/util/gen_idma.py
@@ -22,9 +22,10 @@
 from mario.synth import render_synth_wrapper
 from mario.testbench import render_testbench
 from mario.frontend import render_reg_hjson, render_reg_top
+from mario.tracer import render_tracer
 
 GENABLE_ENTITIES = ['transport', 'legalizer', 'backend', 'vsim_wave', 'testbench', 'synth_wrapper',
-    'bender', 'reg_top', 'reg_hjson']
+    'bender', 'reg_top', 'reg_hjson', 'tracer']
 
 EPILOG = '''
 The iDMA configuration ID is composed of a underscore-separated list of specifiers and protocols.
@@ -73,6 +74,8 @@ def main():
         print(render_reg_hjson(frontend_ids, args.tpl))
     elif args.entity == 'reg_top':
         print(render_reg_top(frontend_ids, args.tpl))
+    elif args.entity == 'tracer':
+        print(render_tracer(protocol_ids, protocol_db, args.tpl))
     else:
         return 1
 
diff --git a/util/mario/synth.py b/util/mario/synth.py
index b83479f9..2baa2c4a 100644
--- a/util/mario/synth.py
+++ b/util/mario/synth.py
@@ -36,7 +36,7 @@ def render_synth_wrapper(prot_ids: dict, db: dict, tpl_file: str) -> str:
             db[rp]['synth_wrapper_assign_read'] =\
                 '    ' + db[rp]['synth_wrapper_assign_read'].replace('\n', '\n    ')
 
-        for wp in used_read_prots:
+        for wp in used_write_prots:
             db[wp]['synth_wrapper_ports_write'] =\
                 '    ' + db[wp]['synth_wrapper_ports_write'].replace('\n', '\n    ')
             db[wp]['synth_wrapper_assign_write'] =\
diff --git a/util/mario/tracer.py b/util/mario/tracer.py
new file mode 100644
index 00000000..493be78d
--- /dev/null
+++ b/util/mario/tracer.py
@@ -0,0 +1,132 @@
+#!/usr/env python3
+# Copyright 2022 ETH Zurich and University of Bologna.
+# Solderpad Hardware License, Version 0.51, see LICENSE for details.
+# SPDX-License-Identifier: SHL-0.51
+
+# Authors:
+# - Tobias Senti <tsenti@.ethz.ch>
+# - Thomas Benz <tbenz@iis.ee.ethz.ch>
+
+""" MARIO tracer interaction"""
+import flatdict
+from mako.template import Template
+
+TRACER_BODY = '''
+// The tracer for the ${identifier} iDMA
+`define IDMA_TRACER_${identifier_cap}(__backend_inst, __out_f) <%text>\\</%text>
+`ifndef SYNTHESYS <%text>\\</%text>
+`ifndef VERILATOR <%text>\\</%text>
+    initial begin : inital_tracer_${identifier} <%text>\\</%text>
+        automatic bit first_iter = 1; <%text>\\</%text>
+        automatic integer tf; <%text>\\</%text>
+        automatic `IDMA_TRACER_MAX_TYPE cnst [string]; <%text>\\</%text>
+        automatic `IDMA_TRACER_MAX_TYPE meta [string]; <%text>\\</%text>
+        automatic `IDMA_TRACER_MAX_TYPE busy [string]; <%text>\\</%text>
+        automatic `IDMA_TRACER_MAX_TYPE bus [string]; <%text>\\</%text>
+        automatic string trace; <%text>\\</%text>
+        #0; <%text>\\</%text>
+        tf = $fopen(__out_f, "w"); <%text>\\</%text>
+        $display("[iDMA Tracer] Logging %s to %s", `"__backend_inst`", __out_f); <%text>\\</%text>
+        forever begin <%text>\\</%text>
+            @(posedge __backend_inst``.clk_i); <%text>\\</%text>
+            if(__backend_inst``.rst_ni & |__backend_inst``.busy_o) begin <%text>\\</%text>
+                /* Trace */ <%text>\\</%text>
+                trace = "{"; <%text>\\</%text>
+                /* Constants */ <%text>\\</%text>
+                cnst = '{ <%text>\\</%text>
+                    "inst"               : `"__backend_inst`", <%text>\\</%text>
+                    "identifier"         : "${identifier}", <%text>\\</%text>
+                    "data_width"         : __backend_inst``.DataWidth, <%text>\\</%text>
+                    "addr_width"         : __backend_inst``.AddrWidth, <%text>\\</%text>
+                    "user_width"         : __backend_inst``.UserWidth, <%text>\\</%text>
+                    "axi_id_width"       : __backend_inst``.AxiIdWidth, <%text>\\</%text>
+                    "num_ax_in_flight"   : __backend_inst``.NumAxInFlight, <%text>\\</%text>
+                    "buffer_depth"       : __backend_inst``.BufferDepth, <%text>\\</%text>
+                    "tf_len_width"       : __backend_inst``.TFLenWidth, <%text>\\</%text>
+                    "mem_sys_depth"      : __backend_inst``.MemSysDepth, <%text>\\</%text>
+                    "combined_shifter"   : __backend_inst``.CombinedShifter, <%text>\\</%text>
+                    "rw_coupling_avail"  : __backend_inst``.RAWCouplingAvail, <%text>\\</%text>
+                    "mask_invalid_data"  : __backend_inst``.MaskInvalidData, <%text>\\</%text>
+                    "hardware_legalizer" : __backend_inst``.HardwareLegalizer, <%text>\\</%text>
+                    "reject_zero_tfs"    : __backend_inst``.RejectZeroTransfers, <%text>\\</%text>
+                    "error_cap"          : __backend_inst``.ErrorCap, <%text>\\</%text>
+                    "print_fifo_info"    : __backend_inst``.PrintFifoInfo <%text>\\</%text>
+                }; <%text>\\</%text>
+                meta = '{ <%text>\\</%text>
+                    "time" : $time() <%text>\\</%text>
+                }; <%text>\\</%text>
+                busy = '{ <%text>\\</%text>
+                    "buffer"      : __backend_inst``.busy_o.buffer_busy, <%text>\\</%text>
+                    "r_dp"        : __backend_inst``.busy_o.r_dp_busy, <%text>\\</%text>
+                    "w_dp"        : __backend_inst``.busy_o.w_dp_busy, <%text>\\</%text>
+                    "r_leg"       : __backend_inst``.busy_o.r_leg_busy, <%text>\\</%text>
+                    "w_leg"       : __backend_inst``.busy_o.w_leg_busy, <%text>\\</%text>
+                    "eh_fsm"      : __backend_inst``.busy_o.eh_fsm_busy, <%text>\\</%text>
+                    "eh_cnt"      : __backend_inst``.busy_o.eh_cnt_busy, <%text>\\</%text>
+                    "raw_coupler" : __backend_inst``.busy_o.raw_coupler_busy <%text>\\</%text>
+                }; <%text>\\</%text>
+                bus = '{ <%text>\\</%text>
+${signals}
+                }; <%text>\\</%text>
+                /* Assembly */ <%text>\\</%text>
+                `IDMA_TRACER_STR_ASSEMBLY(cnst, first_iter); <%text>\\</%text>
+                `IDMA_TRACER_STR_ASSEMBLY(meta, 1); <%text>\\</%text>
+                `IDMA_TRACER_STR_ASSEMBLY(busy, 1); <%text>\\</%text>
+                `IDMA_TRACER_STR_ASSEMBLY(bus, 1); <%text>\\</%text>
+                `IDMA_TRACER_CLEAR_COND(first_iter); <%text>\\</%text>
+                /* Commit */ <%text>\\</%text>
+                $fwrite(tf, $sformatf("%s}<%text>\\</%text>n", trace)); <%text>\\</%text>
+            end <%text>\\</%text>
+        end <%text>\\</%text>
+    end <%text>\\</%text>
+`endif <%text>\\</%text>
+`endif
+'''
+
+
+def render_tracer(prot_ids: dict, db: dict, tpl_file: str) -> str:
+    """Generate racer"""
+    tracer_body = ''
+
+    with open(tpl_file, 'r', encoding='utf-8') as templ_file:
+        tracer_tpl = templ_file.read()
+
+    # render for every is
+    for prot_id in prot_ids:
+
+        # signals
+        signals = ''
+
+        # handle read ports
+        for read_prot in prot_ids[prot_id]['ar']:
+            sig_dict = flatdict.FlatDict(db[read_prot]['trace_signals']['read'], delimiter='_')
+            for signal in sig_dict:
+                signals += '                    '
+                signals += f'"{read_prot}_{signal}": __backend_inst``.{sig_dict[signal]}'
+                signals += ', \\\n'
+
+        for write_prot in prot_ids[prot_id]['aw']:
+            sig_dict = flatdict.FlatDict(db[write_prot]['trace_signals']['write'], delimiter='_')
+            for signal in sig_dict:
+                signals += '                    '
+                signals += f'"{write_prot}_{signal}": __backend_inst``.{sig_dict[signal]}'
+                signals += ', \\\n'
+
+        # post-processing
+        signals = signals[:-4] + ' \\'
+
+        context_body = {
+            'identifier': prot_id,
+            'identifier_cap': prot_id.upper(),
+            'signals': signals
+        }
+
+        # render
+        tracer_body += Template(TRACER_BODY).render(**context_body)
+
+    # render tracer context
+    context = {
+        'body': tracer_body
+    }
+
+    return Template(tracer_tpl).render(**context)
diff --git a/util/trace_idma.py b/util/trace_idma.py
index ad57b213..d211d2a0 100644
--- a/util/trace_idma.py
+++ b/util/trace_idma.py
@@ -1,21 +1,24 @@
 #!/usr/bin/env python3
-# Copyright 2022 ETH Zurich and University of Bologna.
+# Copyright 2023 ETH Zurich and University of Bologna.
 # Solderpad Hardware License, Version 0.51, see LICENSE for details.
 # SPDX-License-Identifier: SHL-0.51
 
-# Author: Thomas Benz <tbenz@iis.ee.ethz.ch>
+# Authors:
+# - Thomas Benz <tbenz@iis.ee.ethz.ch>
 
 """Functions used to parse and evaluate iDMA trace files."""
+import argparse
 import ast
 import sys
 from pprint import pprint as pp
+from mario.database import read_database
+from mario.util import prepare_ids
 
 
 def strb_to_bytes(strobe: int) -> int:
     """Returns the amount of valid bytes in a strobe value"""
 
     res = 0
-
     # iterate over strobe
     for byte_en in str(bin(strobe))[2:]:
         if byte_en == '1':
@@ -29,7 +32,6 @@ def read_trace(fn: str) -> list:
 
     # resulting list of trace events
     trace = []
-
     # read and parse file
     with open(fn, 'r', encoding='utf8') as tf:
         for line in tf:
@@ -45,7 +47,7 @@ def extract_parameter(trace: list) -> dict:
     return trace[0]['cnst']
 
 
-def get_global_utilization(trace: list, data_width: int) -> list:
+def get_global_utilization(trace: list, params: dict, be_info: dict) -> list:
     """Calculates the global utilization [read, write] of the DMA"""
 
     read_data = 0  # in bytes
@@ -53,21 +55,59 @@ def get_global_utilization(trace: list, data_width: int) -> list:
 
     for ele in trace:
         # add read contribution
-        if ele['axib']['r_ready'] and ele['axib']['r_valid']:
-            read_data += data_width // 8
+        for read_prot in be_info['read_prots']:
+            if ele['bus'][f'{read_prot}_rsp_ready'] and ele['bus'][f'{read_prot}_rsp_valid']:
+                read_data += params['data_width'] // 8
 
         # add write contribution
-        if ele['axib']['w_ready'] and ele['axib']['w_valid']:
-            write_data += strb_to_bytes(ele['axib']['w_strb'])
+        for write_prot in be_info['write_prots']:
+            if ele['bus'][f'{write_prot}_req_ready'] and ele['bus'][f'{write_prot}_req_valid']:
+               write_data += strb_to_bytes(ele['bus'][f'{write_prot}_req_strobe'])
 
     # calculate maximum possible amount of data
-    max_data = len(trace) * data_width // 8
+    max_data = len(trace) * params['data_width'] // 8
 
     return [read_data / max_data, write_data / max_data]
 
 
+def main():
+    # Parse Arguments
+    parser = argparse.ArgumentParser(
+        prog='trace_idma',
+        description='Trace iDMA files to analyze them.'
+    )
+    parser.add_argument('--db', dest='db', nargs='*', required=True, help='Database files')
+    parser.add_argument('--trace', dest='trace_file', required=True, help='Trace file')
+    args = parser.parse_args()
+
+    # get database to fetch interface names
+    database = read_database(args.db)
+
+    # read trace, fetch parameters
+    idma_trace = read_trace(args.trace_file)
+    params = extract_parameter(idma_trace)
+
+    # fetch and parse identifier
+    id = bytes.fromhex(hex(params['identifier'])[2:]).decode("ASCII")
+    read_prots = prepare_ids([id])[id]['ar']
+    write_prots = prepare_ids([id])[id]['aw']
+    read_sigs = [database[r]['trace_signals']['read'] for r in read_prots]
+    write_sigs = [database[w]['trace_signals']['write'] for w in write_prots]
+
+    # pack data
+    be_info = {
+        'read_prots': read_prots,
+        'write_prots': write_prots,
+        'read_sigs': read_sigs,
+        'write_sigs': write_sigs
+    }
+
+    # get utilization
+    pp(get_global_utilization(idma_trace, params, be_info))
+
+    # no issues
+    return 0
+
+
 if __name__ == '__main__':
-    _, filename = sys.argv
-    idma_trace = read_trace(filename)
-    idma_data_width = extract_parameter(idma_trace)['data_width']
-    pp(get_global_utilization(idma_trace, idma_data_width))
+    sys.exit(main())