diff --git a/src/include/idma/tracer.svh b/src/include/idma/tracer.svh new file mode 100644 index 00000000..d8f6740d --- /dev/null +++ b/src/include/idma/tracer.svh @@ -0,0 +1,101 @@ +// 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 + +// 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/test/tb_idma_backend.sv b/test/tb_idma_backend.sv index 1402cc27..0316f129 100644 --- a/test/tb_idma_backend.sv +++ b/test/tb_idma_backend.sv @@ -7,6 +7,7 @@ `timescale 1ns/1ns `include "axi/typedef.svh" `include "idma/typedef.svh" +`include "idma/tracer.svh" module tb_idma_backend import idma_pkg::*; #( parameter int unsigned BufferDepth = 3, @@ -25,7 +26,8 @@ module tb_idma_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 = 0 ); // timing parameters @@ -105,6 +107,21 @@ module tb_idma_backend import idma_pkg::*; #( idma_busy_t 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(i_idma_backend, trace_file); + end + + //-------------------------------------- // DMA Driver //-------------------------------------- diff --git a/test/tb_idma_nd_backend.sv b/test/tb_idma_nd_backend.sv index 6b604f70..75107006 100644 --- a/test/tb_idma_nd_backend.sv +++ b/test/tb_idma_nd_backend.sv @@ -7,6 +7,7 @@ `timescale 1ns/1ns `include "axi/typedef.svh" `include "idma/typedef.svh" +`include "idma/tracer.svh" module tb_idma_nd_backend import idma_pkg::*; #( parameter int unsigned BufferDepth = 3, @@ -28,7 +29,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 = 0 ); // timing parameters @@ -123,6 +125,21 @@ module tb_idma_nd_backend import idma_pkg::*; #( idma_busy_t 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(i_idma_backend, trace_file); + end + + //-------------------------------------- // DMA Driver //-------------------------------------- diff --git a/util/trace.py b/util/trace.py new file mode 100644 index 00000000..b74c8563 --- /dev/null +++ b/util/trace.py @@ -0,0 +1,73 @@ +#!/usr/bin/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 + +# Author: Thomas Benz + +"""Functions used to parse and evaluate iDMA trace files.""" +import ast +import sys +from pprint import pprint as pp + + +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': + res += 1 + + return res + + +def read_trace (fn: str) -> list: + """Reads a trace file and returns it as a list of dict objects""" + + # resulting list of trace events + trace = [] + + # read and parse file + with open(fn, 'r', encoding='utf8') as tf: + for line in tf: + trace_dict = ast.literal_eval(line) + trace.append(trace_dict) + + return trace + + +def extract_parameter (trace: list) -> dict: + """Extracts the parameter of the DMA backend the run resulted from""" + + return trace[0]['cnst'] + + +def get_global_utilization (trace: list, data_width: int) -> list: + """Calculates the global utilization [read, write] of the DMA""" + + read_data = 0 # in bytes + write_data = 0 # in bytes + + for ele in trace: + # add read contribution + if ele['axib']['r_ready'] and ele['axib']['r_valid']: + read_data += 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']) + + # calculate maximum possible amount of data + max_data = len(trace) * data_width // 8 + + return [read_data / max_data, write_data / max_data ] + + +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))