From 96cdede2f98f1517a40d60509cedee85183536f4 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Tue, 4 Oct 2022 18:55:26 +0200 Subject: [PATCH 1/2] axi_bus_compare: Add a module that compares two buses --- Bender.yml | 4 + CHANGELOG.md | 2 + axi.core | 4 + src/axi_bus_compare.sv | 587 +++++++++++++++++++++++++++++++++++ src/axi_slave_compare.sv | 184 +++++++++++ src_files.yml | 2 + test/tb_axi_bus_compare.sv | 310 ++++++++++++++++++ test/tb_axi_slave_compare.sv | 239 ++++++++++++++ 8 files changed, 1332 insertions(+) create mode 100644 src/axi_bus_compare.sv create mode 100644 src/axi_slave_compare.sv create mode 100644 test/tb_axi_bus_compare.sv create mode 100644 test/tb_axi_slave_compare.sv diff --git a/Bender.yml b/Bender.yml index 952d5139e..e6e094fa2 100644 --- a/Bender.yml +++ b/Bender.yml @@ -35,6 +35,7 @@ sources: # Level 2 - src/axi_atop_filter.sv - src/axi_burst_splitter.sv + - src/axi_bus_compare.sv - src/axi_cdc_dst.sv - src/axi_cdc_src.sv - src/axi_cut.sv @@ -58,6 +59,7 @@ sources: - src/axi_modify_address.sv - src/axi_mux.sv - src/axi_serializer.sv + - src/axi_slave_compare.sv - src/axi_throttle.sv - src/axi_to_mem.sv # Level 3 @@ -97,6 +99,7 @@ sources: # Level 1 - test/tb_axi_addr_test.sv - test/tb_axi_atop_filter.sv + - test/tb_axi_bus_compare.sv - test/tb_axi_cdc.sv - test/tb_axi_delayer.sv - test/tb_axi_dw_downsizer.sv @@ -112,6 +115,7 @@ sources: - test/tb_axi_modify_address.sv - test/tb_axi_serializer.sv - test/tb_axi_sim_mem.sv + - test/tb_axi_slave_compare.sv - test/tb_axi_to_axi_lite.sv - test/tb_axi_to_mem_banked.sv - test/tb_axi_xbar.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index 6301e4c8f..8a4677001 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Add `axi_channel_compare.sv`: Non-synthesizable module comparing two AXI channels of the same type +- Add `axi_bus_compare` and `axi_slave_compare`; two synthesizable verification IPs meant to be used + to compare two AXI buses on an FPGA. ### Changed - `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents diff --git a/axi.core b/axi.core index 198fd5013..de34be2ea 100644 --- a/axi.core +++ b/axi.core @@ -17,6 +17,7 @@ filesets: # Level 2 - src/axi_atop_filter.sv - src/axi_burst_splitter.sv + - src/axi_bus_compare.sv - src/axi_cdc_dst.sv - src/axi_cdc_src.sv - src/axi_cut.sv @@ -40,6 +41,7 @@ filesets: - src/axi_modify_address.sv - src/axi_mux.sv - src/axi_serializer.sv + - src/axi_slave_compare.sv - src/axi_throttle.sv - src/axi_to_mem.sv # Level 3 @@ -73,6 +75,7 @@ filesets: - test/axi_synth_bench.sv - test/tb_axi_addr_test.sv - test/tb_axi_atop_filter.sv + - test/tb_axi_bus_compare.sv - test/tb_axi_cdc.sv - test/tb_axi_delayer.sv - test/tb_axi_dw_downsizer.sv @@ -88,6 +91,7 @@ filesets: - test/tb_axi_modify_address.sv - test/tb_axi_serializer.sv - test/tb_axi_sim_mem.sv + - test/tb_axi_slave_compare.sv - test/tb_axi_to_axi_lite.sv - test/tb_axi_to_mem_banked.sv - test/tb_axi_xbar.sv diff --git a/src/axi_bus_compare.sv b/src/axi_bus_compare.sv new file mode 100644 index 000000000..84d396613 --- /dev/null +++ b/src/axi_bus_compare.sv @@ -0,0 +1,587 @@ +// Copyright (c) 2019-2020 ETH Zurich, 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. +// +// Authors: +// - Thomas Benz + +/// Synthesizable test module comparing two AXI channels of the same type +/// This module is meant to be used in FPGA-based verification. +module axi_bus_compare #( + /// ID width of the AXI4+ATOP interface + parameter int unsigned AxiIdWidth = 32'd0, + /// FIFO depth + parameter int unsigned FifoDepth = 32'd0, + /// AW channel type of the AXI4+ATOP interface + parameter type axi_aw_chan_t = logic, + /// W channel type of the AXI4+ATOP interface + parameter type axi_w_chan_t = logic, + /// B channel type of the AXI4+ATOP interface + parameter type axi_b_chan_t = logic, + /// AR channel type of the AXI4+ATOP interface + parameter type axi_ar_chan_t = logic, + /// R channel type of the AXI4+ATOP interface + parameter type axi_r_chan_t = logic, + /// Request struct type of the AXI4+ATOP slave port + parameter type axi_req_t = logic, + /// Response struct type of the AXI4+ATOP slave port + parameter type axi_rsp_t = logic, + /// ID type (*do not overwrite*) + parameter type id_t = logic [2**AxiIdWidth-1:0] +)( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4+ATOP A channel request in + input axi_req_t axi_a_req_i, + /// AXI4+ATOP A channel response out + output axi_rsp_t axi_a_rsp_o, + /// AXI4+ATOP A channel request out + output axi_req_t axi_a_req_o, + /// AXI4+ATOP A channel response in + input axi_rsp_t axi_a_rsp_i, + /// AXI4+ATOP B channel request in + input axi_req_t axi_b_req_i, + /// AXI4+ATOP B channel response out + output axi_rsp_t axi_b_rsp_o, + /// AXI4+ATOP B channel request out + output axi_req_t axi_b_req_o, + /// AXI4+ATOP B channel response in + input axi_rsp_t axi_b_rsp_i, + /// AW mismatch + output id_t aw_mismatch_o, + /// W mismatch + output logic w_mismatch_o, + /// B mismatch + output id_t b_mismatch_o, + /// AR mismatch + output id_t ar_mismatch_o, + /// R mismatch + output id_t r_mismatch_o, + /// General mismatch + output logic mismatch_o, + /// Unit is busy + output logic busy_o +); + + + //----------------------------------- + // Channel Signals + //----------------------------------- + // assign request payload A + assign axi_a_req_o.aw = axi_a_req_i.aw; + assign axi_a_req_o.w = axi_a_req_i.w; + assign axi_a_req_o.ar = axi_a_req_i.ar; + + // assign response payload A + assign axi_a_rsp_o.r = axi_a_rsp_i.r; + assign axi_a_rsp_o.b = axi_a_rsp_i.b; + + // assign request payload B + assign axi_b_req_o.aw = axi_b_req_i.aw; + assign axi_b_req_o.w = axi_b_req_i.w; + assign axi_b_req_o.ar = axi_b_req_i.ar; + + // assign response payload B + assign axi_b_rsp_o.r = axi_b_rsp_i.r; + assign axi_b_rsp_o.b = axi_b_rsp_i.b; + + // fifo handshaking signals A + id_t fifo_valid_aw_a, fifo_ready_aw_a; + id_t fifo_valid_b_a, fifo_ready_b_a; + id_t fifo_valid_ar_a, fifo_ready_ar_a; + id_t fifo_valid_r_a, fifo_ready_r_a; + + logic fifo_sel_valid_aw_a, fifo_sel_ready_aw_a; + logic fifo_sel_valid_w_a, fifo_sel_ready_w_a; + logic fifo_sel_valid_b_a, fifo_sel_ready_b_a; + logic fifo_sel_valid_ar_a, fifo_sel_ready_ar_a; + logic fifo_sel_valid_r_a, fifo_sel_ready_r_a; + + // fifo handshaking signals B + id_t fifo_valid_aw_b, fifo_ready_aw_b; + id_t fifo_valid_b_b, fifo_ready_b_b; + id_t fifo_valid_ar_b, fifo_ready_ar_b; + id_t fifo_valid_r_b, fifo_ready_r_b; + + logic fifo_sel_valid_aw_b, fifo_sel_ready_aw_b; + logic fifo_sel_valid_w_b, fifo_sel_ready_w_b; + logic fifo_sel_valid_b_b, fifo_sel_ready_b_b; + logic fifo_sel_valid_ar_b, fifo_sel_ready_ar_b; + logic fifo_sel_valid_r_b, fifo_sel_ready_r_b; + + + //----------------------------------- + // FIFO Output Signals + //----------------------------------- + id_t fifo_cmp_valid_aw_a; + logic fifo_cmp_valid_w_a; + id_t fifo_cmp_valid_b_a; + id_t fifo_cmp_valid_ar_a; + id_t fifo_cmp_valid_r_a; + + id_t fifo_cmp_valid_aw_b; + logic fifo_cmp_valid_w_b; + id_t fifo_cmp_valid_b_b; + id_t fifo_cmp_valid_ar_b; + id_t fifo_cmp_valid_r_b; + + axi_aw_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_aw_a; + axi_w_chan_t fifo_cmp_data_w_a; + axi_b_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_b_a; + axi_ar_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_ar_a; + axi_r_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_r_a; + + axi_aw_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_aw_b; + axi_w_chan_t fifo_cmp_data_w_b; + axi_b_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_b_b; + axi_ar_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_ar_b; + axi_r_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_r_b; + + + //----------------------------------- + // Channel A stream forks + //----------------------------------- + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_aw_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_req_i.aw_valid ), + .ready_o ( axi_a_rsp_o.aw_ready ), + .valid_o ( {fifo_sel_valid_aw_a, axi_a_req_o.aw_valid} ), + .ready_i ( {fifo_sel_ready_aw_a, axi_a_rsp_i.aw_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_w_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_req_i.w_valid ), + .ready_o ( axi_a_rsp_o.w_ready ), + .valid_o ( {fifo_sel_valid_w_a, axi_a_req_o.w_valid} ), + .ready_i ( {fifo_sel_ready_w_a, axi_a_rsp_i.w_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_b_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_rsp_i.b_valid ), + .ready_o ( axi_a_req_o.b_ready ), + .valid_o ( {fifo_sel_valid_b_a, axi_a_rsp_o.b_valid} ), + .ready_i ( {fifo_sel_ready_b_a, axi_a_req_i.b_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_ar_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_req_i.ar_valid ), + .ready_o ( axi_a_rsp_o.ar_ready ), + .valid_o ( {fifo_sel_valid_ar_a, axi_a_req_o.ar_valid} ), + .ready_i ( {fifo_sel_ready_ar_a, axi_a_rsp_i.ar_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_r_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_rsp_i.r_valid ), + .ready_o ( axi_a_req_o.r_ready ), + .valid_o ( {fifo_sel_valid_r_a, axi_a_rsp_o.r_valid} ), + .ready_i ( {fifo_sel_ready_r_a, axi_a_req_i.r_ready} ) + ); + + + //----------------------------------- + // Channel A FIFOs + //----------------------------------- + for (genvar id = 0; id < 2**AxiIdWidth; id++) begin : gen_fifos_a + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_aw_chan_t ) + ) i_stream_fifo_aw_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_req_i.aw ), + .valid_i ( fifo_valid_aw_a [id] ), + .ready_o ( fifo_ready_aw_a [id] ), + .data_o ( fifo_cmp_data_aw_a [id] ), + .valid_o ( fifo_cmp_valid_aw_a [id] ), + .ready_i ( fifo_cmp_valid_aw_a [id] & fifo_cmp_valid_aw_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_b_chan_t ) + ) i_stream_fifo_b_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_rsp_i.b ), + .valid_i ( fifo_valid_b_a [id] ), + .ready_o ( fifo_ready_b_a [id] ), + .data_o ( fifo_cmp_data_b_a [id] ), + .valid_o ( fifo_cmp_valid_b_a [id] ), + .ready_i ( fifo_cmp_valid_b_a [id] & fifo_cmp_valid_b_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_ar_chan_t ) + ) i_stream_fifo_ar_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_req_i.ar ), + .valid_i ( fifo_valid_ar_a [id] ), + .ready_o ( fifo_ready_ar_a [id] ), + .data_o ( fifo_cmp_data_ar_a [id] ), + .valid_o ( fifo_cmp_valid_ar_a [id] ), + .ready_i ( fifo_cmp_valid_ar_a [id] & fifo_cmp_valid_ar_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_r_chan_t ) + ) i_stream_fifo_r_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_rsp_i.r ), + .valid_i ( fifo_valid_r_a [id] ), + .ready_o ( fifo_ready_r_a [id] ), + .data_o ( fifo_cmp_data_r_a [id] ), + .valid_o ( fifo_cmp_valid_r_a [id] ), + .ready_i ( fifo_cmp_valid_r_a [id] & fifo_cmp_valid_r_b [id] ) + ); + end + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_w_chan_t ) + ) i_stream_fifo_w_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_req_i.w ), + .valid_i ( fifo_sel_valid_w_a ), + .ready_o ( fifo_sel_ready_w_a ), + .data_o ( fifo_cmp_data_w_a ), + .valid_o ( fifo_cmp_valid_w_a ), + .ready_i ( fifo_cmp_valid_w_a & fifo_cmp_valid_w_b ) + ); + + + //----------------------------------- + // Input Handshaking A + //----------------------------------- + always_comb begin : gen_handshaking_a + for (int id = 0; id < 2**AxiIdWidth; id++) begin + // aw + // defaults + fifo_valid_aw_a = '0; + fifo_sel_ready_aw_a = '0; + // assign according id + fifo_valid_aw_a [axi_a_req_i.aw.id] = fifo_sel_valid_aw_a; + fifo_sel_ready_aw_a = fifo_ready_aw_a[axi_a_req_i.aw.id]; + + + // b + // defaults + fifo_valid_b_a = '0; + fifo_sel_ready_b_a = '0; + // assign according id + fifo_valid_b_a [axi_a_rsp_i.b.id] = fifo_sel_valid_b_a; + fifo_sel_ready_b_a = fifo_ready_b_a[axi_a_rsp_i.b.id]; + + // ar + // defaults + fifo_valid_ar_a = '0; + fifo_sel_ready_ar_a = '0; + // assign according id + fifo_valid_ar_a [axi_a_req_i.ar.id] = fifo_sel_valid_ar_a; + fifo_sel_ready_ar_a = fifo_ready_ar_a[axi_a_req_i.ar.id]; + + // b + // defaults + fifo_valid_r_a = '0; + fifo_sel_ready_r_a = '0; + // assign according id + fifo_valid_r_a [axi_a_rsp_i.r.id] = fifo_sel_valid_r_a; + fifo_sel_ready_r_a = fifo_ready_r_a[axi_a_rsp_i.r.id]; + end + end + + + //----------------------------------- + // Channel B stream forks + //----------------------------------- + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_aw_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_req_i.aw_valid ), + .ready_o ( axi_b_rsp_o.aw_ready ), + .valid_o ( {fifo_sel_valid_aw_b, axi_b_req_o.aw_valid} ), + .ready_i ( {fifo_sel_ready_aw_b, axi_b_rsp_i.aw_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_w_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_req_i.w_valid ), + .ready_o ( axi_b_rsp_o.w_ready ), + .valid_o ( {fifo_sel_valid_w_b, axi_b_req_o.w_valid} ), + .ready_i ( {fifo_sel_ready_w_b, axi_b_rsp_i.w_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_b_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_rsp_i.b_valid ), + .ready_o ( axi_b_req_o.b_ready ), + .valid_o ( {fifo_sel_valid_b_b, axi_b_rsp_o.b_valid} ), + .ready_i ( {fifo_sel_ready_b_b, axi_b_req_i.b_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_ar_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_req_i.ar_valid ), + .ready_o ( axi_b_rsp_o.ar_ready ), + .valid_o ( {fifo_sel_valid_ar_b, axi_b_req_o.ar_valid} ), + .ready_i ( {fifo_sel_ready_ar_b, axi_b_rsp_i.ar_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_r_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_rsp_i.r_valid ), + .ready_o ( axi_b_req_o.r_ready ), + .valid_o ( {fifo_sel_valid_r_b, axi_b_rsp_o.r_valid} ), + .ready_i ( {fifo_sel_ready_r_b, axi_b_req_i.r_ready} ) + ); + + + //----------------------------------- + // Channel B FIFOs + //----------------------------------- + for (genvar id = 0; id < 2**AxiIdWidth; id++) begin : gen_fifos_b + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_aw_chan_t ) + ) i_stream_fifo_aw_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_req_i.aw ), + .valid_i ( fifo_valid_aw_b [id] ), + .ready_o ( fifo_ready_aw_b [id] ), + .data_o ( fifo_cmp_data_aw_b [id] ), + .valid_o ( fifo_cmp_valid_aw_b [id] ), + .ready_i ( fifo_cmp_valid_aw_a [id] & fifo_cmp_valid_aw_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_b_chan_t ) + ) i_stream_fifo_b_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_rsp_i.b ), + .valid_i ( fifo_valid_b_b [id] ), + .ready_o ( fifo_ready_b_b [id] ), + .data_o ( fifo_cmp_data_b_b [id] ), + .valid_o ( fifo_cmp_valid_b_b [id] ), + .ready_i ( fifo_cmp_valid_b_a [id] & fifo_cmp_valid_b_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_ar_chan_t ) + ) i_stream_fifo_ar_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_req_i.ar ), + .valid_i ( fifo_valid_ar_b [id] ), + .ready_o ( fifo_ready_ar_b [id] ), + .data_o ( fifo_cmp_data_ar_b [id] ), + .valid_o ( fifo_cmp_valid_ar_b [id] ), + .ready_i ( fifo_cmp_valid_ar_a [id] & fifo_cmp_valid_ar_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_r_chan_t ) + ) i_stream_fifo_r_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_rsp_i.r ), + .valid_i ( fifo_valid_r_b [id] ), + .ready_o ( fifo_ready_r_b [id] ), + .data_o ( fifo_cmp_data_r_b [id] ), + .valid_o ( fifo_cmp_valid_r_b [id] ), + .ready_i ( fifo_cmp_valid_r_a [id] & fifo_cmp_valid_r_b [id] ) + ); + end + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_w_chan_t ) + ) i_stream_fifo_w_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_req_i.w ), + .valid_i ( fifo_sel_valid_w_b ), + .ready_o ( fifo_sel_ready_w_b ), + .data_o ( fifo_cmp_data_w_b ), + .valid_o ( fifo_cmp_valid_w_b ), + .ready_i ( fifo_cmp_valid_w_a & fifo_cmp_valid_w_b ) + ); + + + //----------------------------------- + // Input Handshaking B + //----------------------------------- + always_comb begin : gen_handshaking_b + for (int id = 0; id < 2**AxiIdWidth; id++) begin + // aw + // defaults + fifo_valid_aw_b = '0; + fifo_sel_ready_aw_b = '0; + // assign according id + fifo_valid_aw_b [axi_b_req_i.aw.id] = fifo_sel_valid_aw_b; + fifo_sel_ready_aw_b = fifo_ready_aw_b[axi_b_req_i.aw.id]; + + + // b + // defaults + fifo_valid_b_b = '0; + fifo_sel_ready_b_b = '0; + // assign according id + fifo_valid_b_b [axi_b_rsp_i.b.id] = fifo_sel_valid_b_b; + fifo_sel_ready_b_b = fifo_ready_b_b[axi_b_rsp_i.b.id]; + + // ar + // defaults + fifo_valid_ar_b = '0; + fifo_sel_ready_ar_b = '0; + // assign according id + fifo_valid_ar_b [axi_b_req_i.ar.id] = fifo_sel_valid_ar_b; + fifo_sel_ready_ar_b = fifo_ready_ar_b[axi_b_req_i.ar.id]; + + // b + // defaults + fifo_valid_r_b = '0; + fifo_sel_ready_r_b = '0; + // assign according id + fifo_valid_r_b [axi_b_rsp_i.r.id] = fifo_sel_valid_r_b; + fifo_sel_ready_r_b = fifo_ready_r_b[axi_b_rsp_i.r.id]; + end + end + + + //----------------------------------- + // Comparison + //----------------------------------- + for (genvar id = 0; id < 2**AxiIdWidth; id++) begin : gen_cmp + assign aw_mismatch_o [id] = (fifo_cmp_valid_aw_a [id] & fifo_cmp_valid_aw_b [id]) ? + fifo_cmp_data_aw_a [id] == fifo_cmp_data_aw_b [id] : '0; + assign b_mismatch_o [id] = (fifo_cmp_valid_b_a [id] & fifo_cmp_valid_b_b [id]) ? + fifo_cmp_data_b_a [id] == fifo_cmp_data_b_b [id] : '0; + assign ar_mismatch_o [id] = (fifo_cmp_valid_ar_a [id] & fifo_cmp_valid_ar_b [id]) ? + fifo_cmp_data_ar_a [id] == fifo_cmp_data_ar_b [id] : '0; + assign r_mismatch_o [id] = (fifo_cmp_valid_r_a [id] & fifo_cmp_valid_r_b [id]) ? + fifo_cmp_data_r_a [id] == fifo_cmp_data_r_b [id] : '0; + end + + assign w_mismatch_o = (fifo_cmp_valid_w_a & fifo_cmp_valid_w_b ) ? + fifo_cmp_data_w_a != fifo_cmp_data_w_b : '0; + + + //----------------------------------- + // Outputs + //----------------------------------- + assign busy_o = (|fifo_cmp_valid_aw_a) | (|fifo_cmp_valid_aw_b) | + (|fifo_cmp_valid_w_a) | (|fifo_cmp_valid_w_b) | + (|fifo_cmp_valid_b_a) | (|fifo_cmp_valid_b_b) | + (|fifo_cmp_valid_ar_a) | (|fifo_cmp_valid_ar_b) | + (|fifo_cmp_valid_r_a) | (|fifo_cmp_valid_r_b); + + + assign mismatch_o = (|aw_mismatch_o) | (|w_mismatch_o) | (|b_mismatch_o) | + (|ar_mismatch_o) | (|r_mismatch_o); + +endmodule : axi_bus_compare diff --git a/src/axi_slave_compare.sv b/src/axi_slave_compare.sv new file mode 100644 index 000000000..28a380fc4 --- /dev/null +++ b/src/axi_slave_compare.sv @@ -0,0 +1,184 @@ +// Copyright (c) 2019-2020 ETH Zurich, 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. +// +// Authors: +// - Thomas Benz + +/// Synthesizable test module comparing two AXI slaves of the same type. +/// The reference response is always passed to the master, whereas the test response +/// is discarded after handshaking. +/// This module is meant to be used in FPGA-based verification. +module axi_slave_compare #( + /// ID width of the AXI4+ATOP interface + parameter int unsigned AxiIdWidth = 32'd0, + /// FIFO depth + parameter int unsigned FifoDepth = 32'd0, + /// AW channel type of the AXI4+ATOP interface + parameter type axi_aw_chan_t = logic, + /// W channel type of the AXI4+ATOP interface + parameter type axi_w_chan_t = logic, + /// B channel type of the AXI4+ATOP interface + parameter type axi_b_chan_t = logic, + /// AR channel type of the AXI4+ATOP interface + parameter type axi_ar_chan_t = logic, + /// R channel type of the AXI4+ATOP interface + parameter type axi_r_chan_t = logic, + /// Request struct type of the AXI4+ATOP slave port + parameter type axi_req_t = logic, + /// Response struct type of the AXI4+ATOP slave port + parameter type axi_rsp_t = logic, + /// ID type (*do not overwrite*) + parameter type id_t = logic [2**AxiIdWidth-1:0] +)( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4+ATOP channel request in + input axi_req_t axi_mst_req_i, + /// AXI4+ATOP channel response out + output axi_rsp_t axi_mst_rsp_o, + /// AXI4+ATOP reference channel request out + output axi_req_t axi_ref_req_o, + /// AXI4+ATOP reference channel response in + input axi_rsp_t axi_ref_rsp_i, + /// AXI4+ATOP test channel request out + output axi_req_t axi_test_req_o, + /// AXI4+ATOP test channel response in + input axi_rsp_t axi_test_rsp_i, + /// AW mismatch + output id_t aw_mismatch_o, + /// W mismatch + output logic w_mismatch_o, + /// B mismatch + output id_t b_mismatch_o, + /// AR mismatch + output id_t ar_mismatch_o, + /// R mismatch + output id_t r_mismatch_o, + /// General mismatch + output logic mismatch_o, + /// Unit is busy + output logic busy_o +); + + axi_req_t axi_ref_req_in, axi_test_req_in; + axi_rsp_t axi_ref_rsp_in, axi_test_rsp_in; + + logic aw_valid_ref, aw_ready_ref; + logic w_valid_ref, w_ready_ref; + logic ar_valid_ref, ar_ready_ref; + + logic aw_valid_test, aw_ready_test; + logic w_valid_test, w_ready_test; + logic ar_valid_test, ar_ready_test; + + logic aw_ready_mst; + logic w_ready_mst; + logic ar_ready_mst; + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_aw ( + .clk_i, + .rst_ni, + .valid_i ( axi_mst_req_i.aw_valid ), + .ready_o ( aw_ready_mst ), + .valid_o ( { aw_valid_ref, aw_valid_test } ), + .ready_i ( { aw_ready_ref, aw_ready_test } ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_ar ( + .clk_i, + .rst_ni, + .valid_i ( axi_mst_req_i.ar_valid ), + .ready_o ( ar_ready_mst ), + .valid_o ( { ar_valid_ref, ar_valid_test } ), + .ready_i ( { ar_ready_ref, ar_ready_test } ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_w ( + .clk_i, + .rst_ni, + .valid_i ( axi_mst_req_i.w_valid ), + .ready_o ( w_ready_mst ), + .valid_o ( { w_valid_ref, w_valid_test } ), + .ready_i ( { w_ready_ref, w_ready_test } ) + ); + + // assemble buses + always_comb begin + // request + axi_ref_req_in = axi_mst_req_i; + axi_test_req_in = axi_mst_req_i; + // overwrite valids in requests + axi_ref_req_in.aw_valid = aw_valid_ref; + axi_ref_req_in.ar_valid = ar_valid_ref; + axi_ref_req_in.w_valid = w_valid_ref; + axi_test_req_in.aw_valid = aw_valid_test; + axi_test_req_in.ar_valid = ar_valid_test; + axi_test_req_in.w_valid = w_valid_test; + // get readies + aw_ready_ref = axi_ref_rsp_in.aw_ready; + ar_ready_ref = axi_ref_rsp_in.ar_ready; + w_ready_ref = axi_ref_rsp_in.w_ready; + aw_ready_test = axi_test_rsp_in.aw_ready; + ar_ready_test = axi_test_rsp_in.ar_ready; + w_ready_test = axi_test_rsp_in.w_ready; + // response + axi_mst_rsp_o = axi_ref_rsp_in; + // overwrite readies + axi_mst_rsp_o.aw_ready = aw_ready_mst; + axi_mst_rsp_o.w_ready = w_ready_mst; + axi_mst_rsp_o.ar_ready = ar_ready_mst; + // b interface is not used + axi_test_req_in.r_ready = '1; + axi_test_req_in.b_ready = '1; + end + + axi_bus_compare #( + .AxiIdWidth ( AxiIdWidth ), + .FifoDepth ( FifoDepth ), + .axi_aw_chan_t ( axi_aw_chan_t ), + .axi_w_chan_t ( axi_w_chan_t ), + .axi_b_chan_t ( axi_b_chan_t ), + .axi_ar_chan_t ( axi_ar_chan_t ), + .axi_r_chan_t ( axi_r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ) + ) i_axi_bus_compare ( + .clk_i, + .rst_ni, + .testmode_i, + .aw_mismatch_o, + .w_mismatch_o, + .b_mismatch_o, + .ar_mismatch_o, + .r_mismatch_o, + .mismatch_o, + .busy_o, + .axi_a_req_i ( axi_ref_req_in ), + .axi_a_rsp_o ( axi_ref_rsp_in ), + .axi_a_req_o ( axi_ref_req_o ), + .axi_a_rsp_i ( axi_ref_rsp_i ), + .axi_b_req_i ( axi_test_req_in ), + .axi_b_rsp_o ( axi_test_rsp_in ), + .axi_b_req_o ( axi_test_req_o ), + .axi_b_rsp_i ( axi_test_rsp_i ) + ); + +endmodule : axi_slave_compare diff --git a/src_files.yml b/src_files.yml index 60367172d..3703488ea 100644 --- a/src_files.yml +++ b/src_files.yml @@ -16,6 +16,7 @@ axi: # Level 2 - src/axi_atop_filter.sv - src/axi_burst_splitter.sv + - src/axi_bus_compare.sv - src/axi_cdc_dst.sv - src/axi_cdc_src.sv - src/axi_cut.sv @@ -39,6 +40,7 @@ axi: - src/axi_modify_address.sv - src/axi_mux.sv - src/axi_serializer.sv + - src/axi_slave_compare.sv - src/axi_throttle.sv - src/axi_to_mem.sv # Level 3 diff --git a/test/tb_axi_bus_compare.sv b/test/tb_axi_bus_compare.sv new file mode 100644 index 000000000..050cbdd46 --- /dev/null +++ b/test/tb_axi_bus_compare.sv @@ -0,0 +1,310 @@ +// Copyright (c) 2020 ETH Zurich and University of Bologna +// SPDX-License-Identifier: SHL-0.51 +// +// Authors: +// - Thomas Benz + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +/// Testbench for `axi_bus_compare` +module tb_axi_bus_compare #( + // TB Parameters + parameter time TbTclk = 10ns, + // Module Parameters + parameter int unsigned TbAddrWidth = 32'd64, + parameter int unsigned TbDataWidth = 32'd128, + parameter int unsigned TbIdWidth = 32'd6, + parameter int unsigned TbUserWidth = 32'd2, + parameter bit TbWarnUninitialized = 1'b0, + parameter time TbApplDelay = 2ns, + parameter time TbAcqDelay = 8ns +); + + logic clk, + rst_n; + clk_rst_gen #( + .ClkPeriod (TbTclk), + .RstClkCycles (5) + ) i_clk_rst_gen ( + .clk_o (clk), + .rst_no (rst_n) + ); + + localparam int unsigned StrbWidth = TbDataWidth / 8; + typedef logic [TbAddrWidth-1:0] addr_t; + typedef logic [TbDataWidth-1:0] data_t; + typedef logic [TbIdWidth-1:0] id_t; + typedef logic [StrbWidth-1:0] strb_t; + typedef logic [TbUserWidth-1:0] user_t; + + AXI_BUS #( + .AXI_ADDR_WIDTH (TbAddrWidth), + .AXI_DATA_WIDTH (TbDataWidth), + .AXI_ID_WIDTH (TbIdWidth), + .AXI_USER_WIDTH (TbUserWidth) + ) axi (); + + AXI_BUS_DV #( + .AXI_ADDR_WIDTH (TbAddrWidth), + .AXI_DATA_WIDTH (TbDataWidth), + .AXI_ID_WIDTH (TbIdWidth), + .AXI_USER_WIDTH (TbUserWidth) + ) axi_dv (clk); + typedef axi_test::axi_driver #( + .AW(TbAddrWidth), .DW(TbDataWidth), .IW(TbIdWidth), .UW(TbUserWidth), + .TA(1ns), .TT(6ns) + ) drv_t; + drv_t drv = new(axi_dv); + + `AXI_ASSIGN (axi, axi_dv) + + `AXI_TYPEDEF_ALL(axi, addr_t, id_t, data_t, strb_t, user_t) + + axi_req_t axi_req, axi_req_a_in, axi_req_b_in, axi_req_a_out, axi_req_b_out, axi_req_b_dly; + axi_resp_t axi_rsp, axi_rsp_a_in, axi_rsp_b_in, axi_rsp_a_out, axi_rsp_b_out, axi_rsp_b_dly; + + `AXI_ASSIGN_TO_REQ(axi_req, axi) + `AXI_ASSIGN_FROM_RESP(axi, axi_rsp) + + logic aw_valid_a, aw_ready_a; + logic w_valid_a, w_ready_a; + logic ar_valid_a, ar_ready_a; + + logic aw_valid_b, aw_ready_b; + logic w_valid_b, w_ready_b; + logic ar_valid_b, ar_ready_b; + + logic aw_ready; + logic w_ready; + logic ar_ready; + + stream_fork #(.N_OUP(32'd2)) i_stream_fork_aw ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .valid_i ( axi_req.aw_valid ), + .ready_o ( aw_ready ), + .valid_o ( { aw_valid_a, aw_valid_b } ), + .ready_i ( { aw_ready_a, aw_ready_b } ) + ); + + stream_fork #(.N_OUP(32'd2)) i_stream_fork_ar ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .valid_i ( axi_req.ar_valid ), + .ready_o ( ar_ready ), + .valid_o ( { ar_valid_a, ar_valid_b } ), + .ready_i ( { ar_ready_a, ar_ready_b } ) + ); + + stream_fork #(.N_OUP(32'd2)) i_stream_fork_w ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .valid_i ( axi_req.w_valid ), + .ready_o ( w_ready ), + .valid_o ( { w_valid_a, w_valid_b } ), + .ready_i ( { w_ready_a, w_ready_b } ) + ); + + // assemble buses + always_comb begin + // request + axi_req_a_in = axi_req; + axi_req_b_in = axi_req; + // overwrite valids in requests + axi_req_a_in.aw_valid = aw_valid_a; + axi_req_a_in.ar_valid = ar_valid_a; + axi_req_a_in.w_valid = w_valid_a; + axi_req_b_in.aw_valid = aw_valid_b; + axi_req_b_in.ar_valid = ar_valid_b; + axi_req_b_in.w_valid = w_valid_b; + // get readies + aw_ready_a = axi_rsp_a_in.aw_ready; + ar_ready_a = axi_rsp_a_in.ar_ready; + w_ready_a = axi_rsp_a_in.w_ready; + aw_ready_b = axi_rsp_b_in.aw_ready; + ar_ready_b = axi_rsp_b_in.ar_ready; + w_ready_b = axi_rsp_b_in.w_ready; + // response + axi_rsp = axi_rsp_a_in; + // overwrite readies + axi_rsp.aw_ready = aw_ready; + axi_rsp.w_ready = w_ready; + axi_rsp.ar_ready = ar_ready; + // b interface is not used + axi_req_b_in.r_ready = '1; + axi_req_b_in.b_ready = '1; + end + + axi_bus_compare #( + .AxiIdWidth ( TbIdWidth ), + .FifoDepth ( 32'd16 ), + .axi_aw_chan_t ( axi_aw_chan_t ), + .axi_w_chan_t ( axi_w_chan_t ), + .axi_b_chan_t ( axi_b_chan_t ), + .axi_ar_chan_t ( axi_ar_chan_t ), + .axi_r_chan_t ( axi_r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_resp_t ) + ) i_axi_bus_compare ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .testmode_i ( 1'b0 ), + .axi_a_req_i ( axi_req_a_in ), + .axi_a_rsp_o ( axi_rsp_a_in ), + .axi_a_req_o ( axi_req_a_out ), + .axi_a_rsp_i ( axi_rsp_a_out ), + .axi_b_req_i ( axi_req_b_in ), + .axi_b_rsp_o ( axi_rsp_b_in ), + .axi_b_req_o ( axi_req_b_out ), + .axi_b_rsp_i ( axi_rsp_b_out ), + .aw_mismatch_o ( ), + .w_mismatch_o ( ), + .b_mismatch_o ( ), + .ar_mismatch_o ( ), + .r_mismatch_o ( ), + .mismatch_o ( ), + .busy_o ( ) + ); + + axi_sim_mem #( + .AddrWidth(TbAddrWidth), + .DataWidth(TbDataWidth), + .IdWidth (TbIdWidth), + .UserWidth(TbUserWidth), + .axi_req_t(axi_req_t), + .axi_rsp_t(axi_resp_t), + .ApplDelay(TbApplDelay), + .AcqDelay (TbAcqDelay) + ) i_axi_sim_mem_a ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .axi_req_i ( axi_req_a_out ), + .axi_rsp_o ( axi_rsp_a_out ), + .mon_w_valid_o ( ), + .mon_w_addr_o ( ), + .mon_w_data_o ( ), + .mon_w_id_o ( ), + .mon_w_user_o ( ), + .mon_w_beat_count_o( ), + .mon_w_last_o ( ), + .mon_r_valid_o ( ), + .mon_r_addr_o ( ), + .mon_r_data_o ( ), + .mon_r_id_o ( ), + .mon_r_user_o ( ), + .mon_r_beat_count_o( ), + .mon_r_last_o ( ) + ); + + axi_multicut #( + .NoCuts (8), + .aw_chan_t (axi_aw_chan_t), + .w_chan_t (axi_w_chan_t), + .b_chan_t (axi_b_chan_t), + .ar_chan_t (axi_ar_chan_t), + .r_chan_t (axi_r_chan_t), + .axi_req_t (axi_req_t), + .axi_resp_t(axi_resp_t) + ) i_axi_multicut ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .slv_req_i ( axi_req_b_out ), + .slv_resp_o ( axi_rsp_b_out ), + .mst_req_o ( axi_req_b_dly ), + .mst_resp_i ( axi_rsp_b_dly ) + ); + + axi_sim_mem #( + .AddrWidth(TbAddrWidth), + .DataWidth(TbDataWidth), + .IdWidth (TbIdWidth), + .UserWidth(TbUserWidth), + .axi_req_t(axi_req_t), + .axi_rsp_t(axi_resp_t), + .ApplDelay(TbApplDelay), + .AcqDelay (TbAcqDelay) + ) i_axi_sim_mem_b ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .axi_req_i ( axi_req_b_dly ), + .axi_rsp_o ( axi_rsp_b_dly ), + .mon_w_valid_o ( ), + .mon_w_addr_o ( ), + .mon_w_data_o ( ), + .mon_w_id_o ( ), + .mon_w_user_o ( ), + .mon_w_beat_count_o( ), + .mon_w_last_o ( ), + .mon_r_valid_o ( ), + .mon_r_addr_o ( ), + .mon_r_data_o ( ), + .mon_r_id_o ( ), + .mon_r_user_o ( ), + .mon_r_beat_count_o( ), + .mon_r_last_o ( ) + ); + + + // Simply read and write a random memory region. + initial begin + automatic logic rand_success; + automatic data_t exp_data[$]; + automatic drv_t::ax_beat_t aw_beat = new, ar_beat = new; + automatic drv_t::w_beat_t w_beat = new; + automatic drv_t::b_beat_t b_beat; + automatic drv_t::r_beat_t r_beat; + drv.reset_master(); + wait (rst_n); + // AW +`ifdef XSIM + // std::randomize(aw_beat) may behave differently to aw_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only + rand_success = std::randomize(aw_beat); assert (rand_success); +`else + rand_success = aw_beat.randomize(); assert (rand_success); +`endif + aw_beat.ax_addr >>= $clog2(StrbWidth); // align address with data width + aw_beat.ax_addr <<= $clog2(StrbWidth); + aw_beat.ax_len = $urandom(); + aw_beat.ax_size = $clog2(StrbWidth); + aw_beat.ax_burst = axi_pkg::BURST_INCR; + drv.send_aw(aw_beat); + // W beats + for (int unsigned i = 0; i <= aw_beat.ax_len; i++) begin +`ifdef XSIM + // std::randomize(w_beat) may behave differently to w_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only + rand_success = std::randomize(w_beat); assert (rand_success); +`else + rand_success = w_beat.randomize(); assert (rand_success); +`endif + w_beat.w_strb = '1; + if (i == aw_beat.ax_len) begin + w_beat.w_last = 1'b1; + end + drv.send_w(w_beat); + exp_data.push_back(w_beat.w_data); + end + // B + drv.recv_b(b_beat); + assert(b_beat.b_resp == axi_pkg::RESP_OKAY); + // AR + ar_beat.ax_addr = aw_beat.ax_addr; + ar_beat.ax_len = aw_beat.ax_len; + ar_beat.ax_size = aw_beat.ax_size; + ar_beat.ax_burst = aw_beat.ax_burst; + drv.send_ar(ar_beat); + // R beats + for (int unsigned i = 0; i <= ar_beat.ax_len; i++) begin + automatic data_t exp = exp_data.pop_front(); + drv.recv_r(r_beat); + assert(r_beat.r_data == exp) else + $error("Received 0x%h != expected 0x%h!", r_beat.r_data, exp); + end + // Done. + #(TbTclk); + $finish(); + end + +endmodule : tb_axi_bus_compare diff --git a/test/tb_axi_slave_compare.sv b/test/tb_axi_slave_compare.sv new file mode 100644 index 000000000..5413bf86d --- /dev/null +++ b/test/tb_axi_slave_compare.sv @@ -0,0 +1,239 @@ +// Copyright (c) 2020 ETH Zurich and University of Bologna +// SPDX-License-Identifier: SHL-0.51 +// +// Authors: +// - Thomas Benz + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +/// Testbench for `axi_slave_compare` +module tb_axi_slave_compare #( + // TB Parameters + parameter time TbTclk = 10ns, + // Module Parameters + parameter int unsigned TbAddrWidth = 32'd64, + parameter int unsigned TbDataWidth = 32'd128, + parameter int unsigned TbIdWidth = 32'd6, + parameter int unsigned TbUserWidth = 32'd2, + parameter bit TbWarnUninitialized = 1'b0, + parameter time TbApplDelay = 2ns, + parameter time TbAcqDelay = 8ns +); + + logic clk, + rst_n; + clk_rst_gen #( + .ClkPeriod (TbTclk), + .RstClkCycles (5) + ) i_clk_rst_gen ( + .clk_o (clk), + .rst_no (rst_n) + ); + + localparam int unsigned StrbWidth = TbDataWidth / 8; + typedef logic [TbAddrWidth-1:0] addr_t; + typedef logic [TbDataWidth-1:0] data_t; + typedef logic [TbIdWidth-1:0] id_t; + typedef logic [StrbWidth-1:0] strb_t; + typedef logic [TbUserWidth-1:0] user_t; + + AXI_BUS #( + .AXI_ADDR_WIDTH (TbAddrWidth), + .AXI_DATA_WIDTH (TbDataWidth), + .AXI_ID_WIDTH (TbIdWidth), + .AXI_USER_WIDTH (TbUserWidth) + ) axi (); + + AXI_BUS_DV #( + .AXI_ADDR_WIDTH (TbAddrWidth), + .AXI_DATA_WIDTH (TbDataWidth), + .AXI_ID_WIDTH (TbIdWidth), + .AXI_USER_WIDTH (TbUserWidth) + ) axi_dv (clk); + typedef axi_test::axi_driver #( + .AW(TbAddrWidth), .DW(TbDataWidth), .IW(TbIdWidth), .UW(TbUserWidth), + .TA(1ns), .TT(6ns) + ) drv_t; + drv_t drv = new(axi_dv); + + `AXI_ASSIGN (axi, axi_dv) + + `AXI_TYPEDEF_ALL(axi, addr_t, id_t, data_t, strb_t, user_t) + + axi_req_t axi_req, axi_req_a_out, axi_req_b_out, axi_req_b_dly; + axi_resp_t axi_rsp, axi_rsp_a_out, axi_rsp_b_out, axi_rsp_b_dly; + + `AXI_ASSIGN_TO_REQ(axi_req, axi) + `AXI_ASSIGN_FROM_RESP(axi, axi_rsp) + + axi_slave_compare #( + .AxiIdWidth ( TbIdWidth ), + .FifoDepth ( 32'd16 ), + .axi_aw_chan_t ( axi_aw_chan_t ), + .axi_w_chan_t ( axi_w_chan_t ), + .axi_b_chan_t ( axi_b_chan_t ), + .axi_ar_chan_t ( axi_ar_chan_t ), + .axi_r_chan_t ( axi_r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_resp_t ) + ) i_axi_bus_compare ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .testmode_i ( 1'b0 ), + .axi_mst_req_i ( axi_req ), + .axi_mst_rsp_o ( axi_rsp ), + .axi_ref_req_o ( axi_req_a_out ), + .axi_ref_rsp_i ( axi_rsp_a_out ), + .axi_test_req_o ( axi_req_b_out ), + .axi_test_rsp_i ( axi_rsp_b_out ), + .aw_mismatch_o ( ), + .w_mismatch_o ( ), + .b_mismatch_o ( ), + .ar_mismatch_o ( ), + .r_mismatch_o ( ), + .mismatch_o ( ), + .busy_o ( ) + ); + + axi_sim_mem #( + .AddrWidth(TbAddrWidth), + .DataWidth(TbDataWidth), + .IdWidth (TbIdWidth), + .UserWidth(TbUserWidth), + .axi_req_t(axi_req_t), + .axi_rsp_t(axi_resp_t), + .ApplDelay(TbApplDelay), + .AcqDelay (TbAcqDelay) + ) i_axi_sim_mem_a ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .axi_req_i ( axi_req_a_out ), + .axi_rsp_o ( axi_rsp_a_out ), + .mon_w_valid_o ( ), + .mon_w_addr_o ( ), + .mon_w_data_o ( ), + .mon_w_id_o ( ), + .mon_w_user_o ( ), + .mon_w_beat_count_o( ), + .mon_w_last_o ( ), + .mon_r_valid_o ( ), + .mon_r_addr_o ( ), + .mon_r_data_o ( ), + .mon_r_id_o ( ), + .mon_r_user_o ( ), + .mon_r_beat_count_o( ), + .mon_r_last_o ( ) + ); + + axi_multicut #( + .NoCuts (8), + .aw_chan_t (axi_aw_chan_t), + .w_chan_t (axi_w_chan_t), + .b_chan_t (axi_b_chan_t), + .ar_chan_t (axi_ar_chan_t), + .r_chan_t (axi_r_chan_t), + .axi_req_t (axi_req_t), + .axi_resp_t(axi_resp_t) + ) i_axi_multicut ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .slv_req_i ( axi_req_b_out ), + .slv_resp_o ( axi_rsp_b_out ), + .mst_req_o ( axi_req_b_dly ), + .mst_resp_i ( axi_rsp_b_dly ) + ); + + axi_sim_mem #( + .AddrWidth(TbAddrWidth), + .DataWidth(TbDataWidth), + .IdWidth (TbIdWidth), + .UserWidth(TbUserWidth), + .axi_req_t(axi_req_t), + .axi_rsp_t(axi_resp_t), + .ApplDelay(TbApplDelay), + .AcqDelay (TbAcqDelay) + ) i_axi_sim_mem_b ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .axi_req_i ( axi_req_b_dly ), + .axi_rsp_o ( axi_rsp_b_dly ), + .mon_w_valid_o ( ), + .mon_w_addr_o ( ), + .mon_w_data_o ( ), + .mon_w_id_o ( ), + .mon_w_user_o ( ), + .mon_w_beat_count_o( ), + .mon_w_last_o ( ), + .mon_r_valid_o ( ), + .mon_r_addr_o ( ), + .mon_r_data_o ( ), + .mon_r_id_o ( ), + .mon_r_user_o ( ), + .mon_r_beat_count_o( ), + .mon_r_last_o ( ) + ); + + + // Simply read and write a random memory region. + initial begin + automatic logic rand_success; + automatic data_t exp_data[$]; + automatic drv_t::ax_beat_t aw_beat = new, ar_beat = new; + automatic drv_t::w_beat_t w_beat = new; + automatic drv_t::b_beat_t b_beat; + automatic drv_t::r_beat_t r_beat; + drv.reset_master(); + wait (rst_n); + // AW +`ifdef XSIM + // std::randomize(aw_beat) may behave differently to aw_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only + rand_success = std::randomize(aw_beat); assert (rand_success); +`else + rand_success = aw_beat.randomize(); assert (rand_success); +`endif + aw_beat.ax_addr >>= $clog2(StrbWidth); // align address with data width + aw_beat.ax_addr <<= $clog2(StrbWidth); + aw_beat.ax_len = $urandom(); + aw_beat.ax_size = $clog2(StrbWidth); + aw_beat.ax_burst = axi_pkg::BURST_INCR; + drv.send_aw(aw_beat); + // W beats + for (int unsigned i = 0; i <= aw_beat.ax_len; i++) begin +`ifdef XSIM + // std::randomize(w_beat) may behave differently to w_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only + rand_success = std::randomize(w_beat); assert (rand_success); +`else + rand_success = w_beat.randomize(); assert (rand_success); +`endif + w_beat.w_strb = '1; + if (i == aw_beat.ax_len) begin + w_beat.w_last = 1'b1; + end + drv.send_w(w_beat); + exp_data.push_back(w_beat.w_data); + end + // B + drv.recv_b(b_beat); + assert(b_beat.b_resp == axi_pkg::RESP_OKAY); + // AR + ar_beat.ax_addr = aw_beat.ax_addr; + ar_beat.ax_len = aw_beat.ax_len; + ar_beat.ax_size = aw_beat.ax_size; + ar_beat.ax_burst = aw_beat.ax_burst; + drv.send_ar(ar_beat); + // R beats + for (int unsigned i = 0; i <= ar_beat.ax_len; i++) begin + automatic data_t exp = exp_data.pop_front(); + drv.recv_r(r_beat); + assert(r_beat.r_data == exp) else + $error("Received 0x%h != expected 0x%h!", r_beat.r_data, exp); + end + // Done. + #(TbTclk); + $finish(); + end + +endmodule : tb_axi_slave_compare From 88103536cfea3a91e3b8f95d0c28ccbfbeca9836 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 6 Oct 2022 17:55:43 +0200 Subject: [PATCH 2/2] axi_bus_compare: Fixes and changes from the PR, add README entry --- README.md | 9 ++ src/axi_bus_compare.sv | 156 +++++++++++++++++------------------ src/axi_slave_compare.sv | 9 +- test/tb_axi_bus_compare.sv | 8 +- test/tb_axi_slave_compare.sv | 2 +- 5 files changed, 96 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index 489eee09b..b6729e4fc 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,15 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_xbar`](src/axi_xbar.sv) | Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_xbar.md) | | [`axi_xp`](src/axi_xp.sv) | AXI Crosspoint (XP) with homomorphous slave and master ports. | | +## Synthesizable Verification Modules + +The following modules are meant to be used for verification purposes only but are synthesizable to be used in FPGA environments. + +| Name | Description | +|------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| [`axi_bus_compare`](src/axi_bus_compare.sv) | Compares two buses of the same type (and in the same clock domain), returns events on mismatch. | +| [`axi_slave_compare`](src/axi_slave_compare.sv) | Compares two slave devices of the same type (and in the same clock domain), returns events on mismatch. | + ### Simulation-Only Modules In addition to the modules above, which are available in synthesis and simulation, the following modules are available only in simulation. Those modules are widely used in our testbenches, but they are also suitable to build testbenches for AXI modules and systems outside this repository. diff --git a/src/axi_bus_compare.sv b/src/axi_bus_compare.sv index 84d396613..b92e6159f 100644 --- a/src/axi_bus_compare.sv +++ b/src/axi_bus_compare.sv @@ -12,6 +12,7 @@ // Authors: // - Thomas Benz +`include "axi/assign.svh" /// Synthesizable test module comparing two AXI channels of the same type /// This module is meant to be used in FPGA-based verification. module axi_bus_compare #( @@ -79,22 +80,23 @@ module axi_bus_compare #( // Channel Signals //----------------------------------- // assign request payload A - assign axi_a_req_o.aw = axi_a_req_i.aw; - assign axi_a_req_o.w = axi_a_req_i.w; - assign axi_a_req_o.ar = axi_a_req_i.ar; + + `AXI_ASSIGN_AW_STRUCT(axi_a_req_o.aw, axi_a_req_i.aw) + `AXI_ASSIGN_W_STRUCT(axi_a_req_o.w, axi_a_req_i.w) + `AXI_ASSIGN_AR_STRUCT(axi_a_req_o.ar, axi_a_req_i.ar) // assign response payload A - assign axi_a_rsp_o.r = axi_a_rsp_i.r; - assign axi_a_rsp_o.b = axi_a_rsp_i.b; + `AXI_ASSIGN_R_STRUCT(axi_a_rsp_o.r, axi_a_rsp_i.r) + `AXI_ASSIGN_B_STRUCT(axi_a_rsp_o.b, axi_a_rsp_i.b) // assign request payload B - assign axi_b_req_o.aw = axi_b_req_i.aw; - assign axi_b_req_o.w = axi_b_req_i.w; - assign axi_b_req_o.ar = axi_b_req_i.ar; + `AXI_ASSIGN_AW_STRUCT(axi_b_req_o.aw, axi_b_req_i.aw) + `AXI_ASSIGN_W_STRUCT(axi_b_req_o.w, axi_b_req_i.w) + `AXI_ASSIGN_AR_STRUCT(axi_b_req_o.ar, axi_b_req_i.ar) // assign response payload B - assign axi_b_rsp_o.r = axi_b_rsp_i.r; - assign axi_b_rsp_o.b = axi_b_rsp_i.b; + `AXI_ASSIGN_R_STRUCT(axi_b_rsp_o.r, axi_b_rsp_i.r) + `AXI_ASSIGN_B_STRUCT(axi_b_rsp_o.b, axi_b_rsp_i.b) // fifo handshaking signals A id_t fifo_valid_aw_a, fifo_ready_aw_a; @@ -314,40 +316,38 @@ module axi_bus_compare #( // Input Handshaking A //----------------------------------- always_comb begin : gen_handshaking_a - for (int id = 0; id < 2**AxiIdWidth; id++) begin - // aw - // defaults - fifo_valid_aw_a = '0; - fifo_sel_ready_aw_a = '0; - // assign according id - fifo_valid_aw_a [axi_a_req_i.aw.id] = fifo_sel_valid_aw_a; - fifo_sel_ready_aw_a = fifo_ready_aw_a[axi_a_req_i.aw.id]; - - - // b - // defaults - fifo_valid_b_a = '0; - fifo_sel_ready_b_a = '0; - // assign according id - fifo_valid_b_a [axi_a_rsp_i.b.id] = fifo_sel_valid_b_a; - fifo_sel_ready_b_a = fifo_ready_b_a[axi_a_rsp_i.b.id]; - - // ar - // defaults - fifo_valid_ar_a = '0; - fifo_sel_ready_ar_a = '0; - // assign according id - fifo_valid_ar_a [axi_a_req_i.ar.id] = fifo_sel_valid_ar_a; - fifo_sel_ready_ar_a = fifo_ready_ar_a[axi_a_req_i.ar.id]; - - // b - // defaults - fifo_valid_r_a = '0; - fifo_sel_ready_r_a = '0; - // assign according id - fifo_valid_r_a [axi_a_rsp_i.r.id] = fifo_sel_valid_r_a; - fifo_sel_ready_r_a = fifo_ready_r_a[axi_a_rsp_i.r.id]; - end + // aw + // defaults + fifo_valid_aw_a = '0; + fifo_sel_ready_aw_a = '0; + // assign according id + fifo_valid_aw_a [axi_a_req_i.aw.id] = fifo_sel_valid_aw_a; + fifo_sel_ready_aw_a = fifo_ready_aw_a[axi_a_req_i.aw.id]; + + + // b + // defaults + fifo_valid_b_a = '0; + fifo_sel_ready_b_a = '0; + // assign according id + fifo_valid_b_a [axi_a_rsp_i.b.id] = fifo_sel_valid_b_a; + fifo_sel_ready_b_a = fifo_ready_b_a[axi_a_rsp_i.b.id]; + + // ar + // defaults + fifo_valid_ar_a = '0; + fifo_sel_ready_ar_a = '0; + // assign according id + fifo_valid_ar_a [axi_a_req_i.ar.id] = fifo_sel_valid_ar_a; + fifo_sel_ready_ar_a = fifo_ready_ar_a[axi_a_req_i.ar.id]; + + // b + // defaults + fifo_valid_r_a = '0; + fifo_sel_ready_r_a = '0; + // assign according id + fifo_valid_r_a [axi_a_rsp_i.r.id] = fifo_sel_valid_r_a; + fifo_sel_ready_r_a = fifo_ready_r_a[axi_a_rsp_i.r.id]; end @@ -516,40 +516,38 @@ module axi_bus_compare #( // Input Handshaking B //----------------------------------- always_comb begin : gen_handshaking_b - for (int id = 0; id < 2**AxiIdWidth; id++) begin - // aw - // defaults - fifo_valid_aw_b = '0; - fifo_sel_ready_aw_b = '0; - // assign according id - fifo_valid_aw_b [axi_b_req_i.aw.id] = fifo_sel_valid_aw_b; - fifo_sel_ready_aw_b = fifo_ready_aw_b[axi_b_req_i.aw.id]; - - - // b - // defaults - fifo_valid_b_b = '0; - fifo_sel_ready_b_b = '0; - // assign according id - fifo_valid_b_b [axi_b_rsp_i.b.id] = fifo_sel_valid_b_b; - fifo_sel_ready_b_b = fifo_ready_b_b[axi_b_rsp_i.b.id]; - - // ar - // defaults - fifo_valid_ar_b = '0; - fifo_sel_ready_ar_b = '0; - // assign according id - fifo_valid_ar_b [axi_b_req_i.ar.id] = fifo_sel_valid_ar_b; - fifo_sel_ready_ar_b = fifo_ready_ar_b[axi_b_req_i.ar.id]; - - // b - // defaults - fifo_valid_r_b = '0; - fifo_sel_ready_r_b = '0; - // assign according id - fifo_valid_r_b [axi_b_rsp_i.r.id] = fifo_sel_valid_r_b; - fifo_sel_ready_r_b = fifo_ready_r_b[axi_b_rsp_i.r.id]; - end + // aw + // defaults + fifo_valid_aw_b = '0; + fifo_sel_ready_aw_b = '0; + // assign according id + fifo_valid_aw_b [axi_b_req_i.aw.id] = fifo_sel_valid_aw_b; + fifo_sel_ready_aw_b = fifo_ready_aw_b[axi_b_req_i.aw.id]; + + + // b + // defaults + fifo_valid_b_b = '0; + fifo_sel_ready_b_b = '0; + // assign according id + fifo_valid_b_b [axi_b_rsp_i.b.id] = fifo_sel_valid_b_b; + fifo_sel_ready_b_b = fifo_ready_b_b[axi_b_rsp_i.b.id]; + + // ar + // defaults + fifo_valid_ar_b = '0; + fifo_sel_ready_ar_b = '0; + // assign according id + fifo_valid_ar_b [axi_b_req_i.ar.id] = fifo_sel_valid_ar_b; + fifo_sel_ready_ar_b = fifo_ready_ar_b[axi_b_req_i.ar.id]; + + // b + // defaults + fifo_valid_r_b = '0; + fifo_sel_ready_r_b = '0; + // assign according id + fifo_valid_r_b [axi_b_rsp_i.r.id] = fifo_sel_valid_r_b; + fifo_sel_ready_r_b = fifo_ready_r_b[axi_b_rsp_i.r.id]; end @@ -584,4 +582,4 @@ module axi_bus_compare #( assign mismatch_o = (|aw_mismatch_o) | (|w_mismatch_o) | (|b_mismatch_o) | (|ar_mismatch_o) | (|r_mismatch_o); -endmodule : axi_bus_compare +endmodule diff --git a/src/axi_slave_compare.sv b/src/axi_slave_compare.sv index 28a380fc4..a4442985c 100644 --- a/src/axi_slave_compare.sv +++ b/src/axi_slave_compare.sv @@ -12,6 +12,7 @@ // Authors: // - Thomas Benz +`include "axi/assign.svh" /// Synthesizable test module comparing two AXI slaves of the same type. /// The reference response is always passed to the master, whereas the test response /// is discarded after handshaking. @@ -123,8 +124,8 @@ module axi_slave_compare #( // assemble buses always_comb begin // request - axi_ref_req_in = axi_mst_req_i; - axi_test_req_in = axi_mst_req_i; + `AXI_SET_REQ_STRUCT(axi_ref_req_in, axi_mst_req_i) + `AXI_SET_REQ_STRUCT(axi_test_req_in, axi_mst_req_i) // overwrite valids in requests axi_ref_req_in.aw_valid = aw_valid_ref; axi_ref_req_in.ar_valid = ar_valid_ref; @@ -140,7 +141,7 @@ module axi_slave_compare #( ar_ready_test = axi_test_rsp_in.ar_ready; w_ready_test = axi_test_rsp_in.w_ready; // response - axi_mst_rsp_o = axi_ref_rsp_in; + `AXI_SET_RESP_STRUCT(axi_mst_rsp_o, axi_ref_rsp_in) // overwrite readies axi_mst_rsp_o.aw_ready = aw_ready_mst; axi_mst_rsp_o.w_ready = w_ready_mst; @@ -181,4 +182,4 @@ module axi_slave_compare #( .axi_b_rsp_i ( axi_test_rsp_i ) ); -endmodule : axi_slave_compare +endmodule diff --git a/test/tb_axi_bus_compare.sv b/test/tb_axi_bus_compare.sv index 050cbdd46..c8cd1c91c 100644 --- a/test/tb_axi_bus_compare.sv +++ b/test/tb_axi_bus_compare.sv @@ -109,8 +109,8 @@ module tb_axi_bus_compare #( // assemble buses always_comb begin // request - axi_req_a_in = axi_req; - axi_req_b_in = axi_req; + `AXI_SET_REQ_STRUCT(axi_req_a_in, axi_req) + `AXI_SET_REQ_STRUCT(axi_req_b_in, axi_req) // overwrite valids in requests axi_req_a_in.aw_valid = aw_valid_a; axi_req_a_in.ar_valid = ar_valid_a; @@ -126,7 +126,7 @@ module tb_axi_bus_compare #( ar_ready_b = axi_rsp_b_in.ar_ready; w_ready_b = axi_rsp_b_in.w_ready; // response - axi_rsp = axi_rsp_a_in; + `AXI_SET_RESP_STRUCT(axi_rsp, axi_rsp_a_in) // overwrite readies axi_rsp.aw_ready = aw_ready; axi_rsp.w_ready = w_ready; @@ -307,4 +307,4 @@ module tb_axi_bus_compare #( $finish(); end -endmodule : tb_axi_bus_compare +endmodule diff --git a/test/tb_axi_slave_compare.sv b/test/tb_axi_slave_compare.sv index 5413bf86d..45809e5d2 100644 --- a/test/tb_axi_slave_compare.sv +++ b/test/tb_axi_slave_compare.sv @@ -236,4 +236,4 @@ module tb_axi_slave_compare #( $finish(); end -endmodule : tb_axi_slave_compare +endmodule