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