From 35c3dd66827c769064b47507d4d8c51b1e80cd90 Mon Sep 17 00:00:00 2001 From: Juan Miguel de Haro Ruiz Date: Wed, 6 Jul 2022 17:16:38 +0200 Subject: [PATCH 001/145] axi_intf: Add Monitor modport to AXI_BUS and AXI_LITE interfaces --- CHANGELOG.md | 1 + src/axi_intf.sv | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9f5e91f8..51b1d0526 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased ### Added +- Add Monitor modport to `AXI_BUS` and `AXI_LITE` interfaces. ### Changed diff --git a/src/axi_intf.sv b/src/axi_intf.sv index 0d43ffbae..d401010da 100644 --- a/src/axi_intf.sv +++ b/src/axi_intf.sv @@ -98,6 +98,14 @@ interface AXI_BUS #( output r_id, r_data, r_resp, r_last, r_user, r_valid, input r_ready ); + modport Monitor ( + input aw_id, aw_addr, aw_len, aw_size, aw_burst, aw_lock, aw_cache, aw_prot, aw_qos, aw_region, aw_atop, aw_user, aw_valid, aw_ready, + w_data, w_strb, w_last, w_user, w_valid, w_ready, + b_id, b_resp, b_user, b_valid, b_ready, + ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_valid, ar_ready, + r_id, r_data, r_resp, r_last, r_user, r_valid, r_ready + ); + endinterface @@ -441,6 +449,14 @@ interface AXI_LITE #( output r_data, r_resp, r_valid, input r_ready ); + modport Monitor ( + input aw_addr, aw_prot, aw_valid, aw_ready, + w_data, w_strb, w_valid, w_ready, + b_resp, b_valid, b_ready, + ar_addr, ar_prot, ar_valid, ar_ready, + r_data, r_resp, r_valid, r_ready + ); + endinterface From 416d895afafdfa0a74d824bae6f85ecd67aa99bf Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Thu, 7 Jul 2022 08:48:53 +0200 Subject: [PATCH 002/145] axi_intf: Add Monitor modport to AXI_LITE_DV interface --- CHANGELOG.md | 2 +- src/axi_intf.sv | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51b1d0526..382ad5102 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased ### Added -- Add Monitor modport to `AXI_BUS` and `AXI_LITE` interfaces. +- Add Monitor modport to `AXI_BUS`, `AXI_LITE`, and `AXI_LITE_DV` interfaces. ### Changed diff --git a/src/axi_intf.sv b/src/axi_intf.sv index d401010da..c0257f21c 100644 --- a/src/axi_intf.sv +++ b/src/axi_intf.sv @@ -515,6 +515,14 @@ interface AXI_LITE_DV #( output r_data, r_resp, r_valid, input r_ready ); + modport Monitor ( + input aw_addr, aw_prot, aw_valid, aw_ready, + w_data, w_strb, w_valid, w_ready, + b_resp, b_valid, b_ready, + ar_addr, ar_prot, ar_valid, ar_ready, + r_data, r_resp, r_valid, r_ready + ); + endinterface From 32ce14ca80c555bb7dc33558f228df8babe95323 Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Thu, 7 Jul 2022 16:20:05 +0200 Subject: [PATCH 003/145] Release v0.36.0 --- CHANGELOG.md | 6 +----- VERSION | 2 +- axi.core | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 382ad5102..efe7b1c47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,15 +5,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## Unreleased +## 0.36.0 - 2022-07-07 ### Added - Add Monitor modport to `AXI_BUS`, `AXI_LITE`, and `AXI_LITE_DV` interfaces. -### Changed - -### Fixed - ## 0.35.3 - 2022-05-03 diff --git a/VERSION b/VERSION index cd1220447..93d4c1ef0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.35.4-dev +0.36.0 diff --git a/axi.core b/axi.core index ba74e9e6a..4e7204f9b 100644 --- a/axi.core +++ b/axi.core @@ -1,6 +1,6 @@ CAPI=2: -name : pulp-platform.org::axi:0.35.4-dev +name : pulp-platform.org::axi:0.36.0 filesets: rtl: From 64ca21eb2ffcc26ed9b7635d19723fa227f38699 Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Thu, 7 Jul 2022 16:20:17 +0200 Subject: [PATCH 004/145] Increment development version towards next patch release --- CHANGELOG.md | 9 +++++++++ VERSION | 2 +- axi.core | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efe7b1c47..236cf6d26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added + +### Changed + +### Fixed + + ## 0.36.0 - 2022-07-07 ### Added diff --git a/VERSION b/VERSION index 93d4c1ef0..66ca32f1b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.36.0 +0.36.1-dev diff --git a/axi.core b/axi.core index 4e7204f9b..d5a6043eb 100644 --- a/axi.core +++ b/axi.core @@ -1,6 +1,6 @@ CAPI=2: -name : pulp-platform.org::axi:0.36.0 +name : pulp-platform.org::axi:0.36.1-dev filesets: rtl: From 250534b7e800a95a0be48433d3ce4954ca1aa03c Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Fri, 5 Aug 2022 08:20:48 +0200 Subject: [PATCH 005/145] axi_lite_mux: Reduce complexity of master port W channel See #243. --- CHANGELOG.md | 2 ++ src/axi_lite_mux.sv | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 236cf6d26..e7ff06361 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed ### Fixed +- `axi_lite_mux`: Reduce complexity of W channel at master port by removing an unnecessary + multiplexer. ## 0.36.0 - 2022-07-07 diff --git a/src/axi_lite_mux.sv b/src/axi_lite_mux.sv index 75a9a256c..577280279 100644 --- a/src/axi_lite_mux.sv +++ b/src/axi_lite_mux.sv @@ -283,7 +283,7 @@ module axi_lite_mux #( // W Channel //-------------------------------------- // multiplexer - assign mst_w_chan = (!w_fifo_empty && !b_fifo_full) ? slv_reqs_i[w_select].w : '0; + assign mst_w_chan = slv_reqs_i[w_select].w; assign mst_w_valid = (!w_fifo_empty && !b_fifo_full) ? slv_reqs_i[w_select].w_valid : 1'b0; for (genvar i = 0; i < NoSlvPorts; i++) begin : gen_slv_w_ready assign slv_resps_o[i].w_ready = mst_w_ready & ~w_fifo_empty & From 151d3b0e775e493df5fd0929c63982bf7b19d72c Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Wed, 24 Aug 2022 09:53:08 +0200 Subject: [PATCH 006/145] axi_sim_mem: Add the possibility to emit read and write errors (#242) --- src/axi_sim_mem.sv | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/axi_sim_mem.sv b/src/axi_sim_mem.sv index a518be9a0..67d278603 100644 --- a/src/axi_sim_mem.sv +++ b/src/axi_sim_mem.sv @@ -14,6 +14,8 @@ /// axi_sim_mem #( ... ) i_sim_mem ( ... ); /// initial begin /// $readmemh("file_with_memory_addrs_and_data.mem", i_sim_mem.mem); +/// $readmemh("file_with_memory_addrs_and_read_errors.mem", i_sim_mem.rerr); +/// $readmemh("file_with_memory_addrs_and_write_errors.mem", i_sim_mem.werr); /// end /// ``` /// `mem` is addressed (or indexed) byte-wise with `AddrWidth`-wide addresses. @@ -34,6 +36,8 @@ module axi_sim_mem #( parameter type axi_rsp_t = logic, /// Warn on accesses to uninitialized bytes parameter bit WarnUninitialized = 1'b0, + /// Clear error on access + parameter bit ClearErrOnAccess = 1'b0, /// Application delay (measured after rising clock edge) parameter time ApplDelay = 0ps, /// Acquisition delay (measured after rising clock edge) @@ -104,7 +108,12 @@ module axi_sim_mem #( } monitor_t; monitor_t mon_w, mon_r; - logic [7:0] mem[addr_t]; + logic [7:0] mem[addr_t]; + axi_pkg::resp_t rerr[addr_t] = '{default: axi_pkg::RESP_OKAY}; + axi_pkg::resp_t werr[addr_t] = '{default: axi_pkg::RESP_OKAY}; + + // error happened in write burst + axi_pkg::resp_t error_happened = axi_pkg::RESP_OKAY; initial begin automatic ar_t ar_queue[$]; @@ -156,16 +165,20 @@ module axi_sim_mem #( if (axi_req_i.w.strb[i_byte]) begin automatic addr_t byte_addr = (addr / StrbWidth) * StrbWidth + i_byte; mem[byte_addr] = axi_req_i.w.data[i_byte*8+:8]; + error_happened = axi_pkg::resp_precedence(werr[byte_addr], error_happened); + if (ClearErrOnAccess) + werr[byte_addr] = axi_pkg::RESP_OKAY; end end if (w_cnt == aw_queue[0].len) begin automatic b_t b_beat = '0; assert (axi_req_i.w.last) else $error("Expected last beat of W burst!"); b_beat.id = aw_queue[0].id; - b_beat.resp = axi_pkg::RESP_OKAY; + b_beat.resp = error_happened; b_queue.push_back(b_beat); w_cnt = 0; mon_w.last = 1'b1; + error_happened = axi_pkg::RESP_OKAY; void'(aw_queue.pop_front()); end else begin assert (!axi_req_i.w.last) else $error("Did not expect last beat of W burst!"); @@ -211,6 +224,7 @@ module axi_sim_mem #( automatic axi_pkg::size_t size = ar_queue[0].size; automatic addr_t addr = axi_pkg::beat_addr(ar_queue[0].addr, size, len, burst, r_cnt); automatic r_t r_beat = '0; + automatic data_t r_data = 'x; // compatibility reasons r_beat.data = 'x; r_beat.id = ar_queue[0].id; r_beat.resp = axi_pkg::RESP_OKAY; @@ -224,11 +238,16 @@ module axi_sim_mem #( $warning("Access to non-initialized byte at address 0x%016x by ID 0x%x.", byte_addr, r_beat.id); end - r_beat.data[i_byte*8+:8] = 'x; + r_data[i_byte*8+:8] = 'x; end else begin - r_beat.data[i_byte*8+:8] = mem[byte_addr]; + r_data[i_byte*8+:8] = mem[byte_addr]; + end + r_beat.resp = axi_pkg::resp_precedence(rerr[byte_addr], r_beat.resp); + if (ClearErrOnAccess & axi_req_i.r_ready) begin + rerr[byte_addr] = axi_pkg::RESP_OKAY; end end + r_beat.data = r_data; if (r_cnt == ar_queue[0].len) begin r_beat.last = 1'b1; mon_r.last = 1'b1; @@ -320,6 +339,7 @@ module axi_sim_mem_intf #( parameter int unsigned AXI_ID_WIDTH = 32'd0, parameter int unsigned AXI_USER_WIDTH = 32'd0, parameter bit WARN_UNINITIALIZED = 1'b0, + parameter bit ClearErrOnAccess = 1'b0, parameter time APPL_DELAY = 0ps, parameter time ACQ_DELAY = 0ps ) ( @@ -363,6 +383,7 @@ module axi_sim_mem_intf #( .axi_req_t (axi_req_t), .axi_rsp_t (axi_resp_t), .WarnUninitialized (WARN_UNINITIALIZED), + .ClearErrOnAccess (ClearErrOnAccess), .ApplDelay (APPL_DELAY), .AcqDelay (ACQ_DELAY) ) i_sim_mem ( From 609d354e294c6fa9d00c4426b455da599c039db6 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Fri, 5 Aug 2022 17:43:23 +0200 Subject: [PATCH 007/145] axi_to_mem: Add on chip memory slave module --- Bender.yml | 1 + CHANGELOG.md | 1 + axi.core | 1 + src/axi_to_mem.sv | 697 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 700 insertions(+) create mode 100644 src/axi_to_mem.sv diff --git a/Bender.yml b/Bender.yml index e88148e32..f512a26ac 100644 --- a/Bender.yml +++ b/Bender.yml @@ -46,6 +46,7 @@ sources: - src/axi_modify_address.sv - src/axi_mux.sv - src/axi_serializer.sv + - src/axi_to_mem.sv # Level 3 - src/axi_cdc.sv - src/axi_err_slv.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index e7ff06361..33cdc4c06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased ### Added +- `axi_to_mem`: AXI4+ATOP slave to control on chip memory. ### Changed diff --git a/axi.core b/axi.core index d5a6043eb..c09f2e588 100644 --- a/axi.core +++ b/axi.core @@ -38,6 +38,7 @@ filesets: - src/axi_modify_address.sv - src/axi_mux.sv - src/axi_serializer.sv + - src/axi_to_mem.sv # Level 3 - src/axi_cdc.sv - src/axi_err_slv.sv diff --git a/src/axi_to_mem.sv b/src/axi_to_mem.sv new file mode 100644 index 000000000..735d47c12 --- /dev/null +++ b/src/axi_to_mem.sv @@ -0,0 +1,697 @@ +// Copyright 2020 ETH Zurich and 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: +// - Andreas Kurth +// - Wolfgang Roenninger + +`include "common_cells/registers.svh" +/// AXI4+ATOP slave module which translates AXI bursts into a memory stream. +/// If both read and write channels of the AXI4+ATOP are active, both will have an +/// utilization of 50%. +module axi_to_mem #( + /// AXI4+ATOP request type. See `include/axi/typedef.svh`. + parameter type axi_req_t = logic, + /// AXI4+ATOP response type. See `include/axi/typedef.svh`. + parameter type axi_resp_t = logic, + /// Address width, has to be less or equal than the width off the AXI address field. + /// Determines the width of `mem_addr_o`. Has to be wide enough to emit the memory region + /// which should be accessible. + parameter int unsigned AddrWidth = 0, + /// AXI4+ATOP data width. + parameter int unsigned DataWidth = 0, + /// AXI4+ATOP ID width. + parameter int unsigned IdWidth = 0, + /// Number of banks at output, must evenly divide `DataWidth`. + parameter int unsigned NumBanks = 0, + /// Depth of memory response buffer. This should be equal to the memory response latency. + parameter int unsigned BufDepth = 1, + /// Dependent parameter, do not override. Memory address type. + localparam type addr_t = logic [AddrWidth-1:0], + /// Dependent parameter, do not override. Memory data type. + localparam type mem_data_t = logic [DataWidth/NumBanks-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + localparam type mem_strb_t = logic [DataWidth/NumBanks/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// The unit is busy handling an AXI4+ATOP request. + output logic busy_o, + /// AXI4+ATOP slave port, request input. + input axi_req_t axi_req_i, + /// AXI4+ATOP slave port, response output. + output axi_resp_t axi_resp_o, + /// Memory stream master, request is valid for this bank. + output logic [NumBanks-1:0] mem_req_o, + /// Memory stream master, request can be granted by this bank. + input logic [NumBanks-1:0] mem_gnt_i, + /// Memory stream master, byte address of the request. + output addr_t [NumBanks-1:0] mem_addr_o, + /// Memory stream master, write data for this bank. Valid when `mem_req_o`. + output mem_data_t [NumBanks-1:0] mem_wdata_o, + /// Memory stream master, byte-wise strobe (byte enable). + output mem_strb_t [NumBanks-1:0] mem_strb_o, + /// Memory stream master, `axi_pkg::atop_t` signal associated with this request. + output axi_pkg::atop_t [NumBanks-1:0] mem_atop_o, + /// Memory stream master, write enable. Then asserted store of `mem_w_data` is requested. + output logic [NumBanks-1:0] mem_we_o, + /// Memory stream master, response is valid. This module expects always a response valid for a + /// request regardless if the request was a write or a read. + input logic [NumBanks-1:0] mem_rvalid_i, + /// Memory stream master, read response data. + input mem_data_t [NumBanks-1:0] mem_rdata_i +); + + typedef logic [DataWidth-1:0] axi_data_t; + typedef logic [DataWidth/8-1:0] axi_strb_t; + typedef logic [IdWidth-1:0] axi_id_t; + + typedef struct packed { + addr_t addr; + axi_pkg::atop_t atop; + axi_strb_t strb; + axi_data_t wdata; + logic we; + } mem_req_t; + + typedef struct packed { + addr_t addr; + axi_pkg::atop_t atop; + axi_id_t id; + logic last; + axi_pkg::qos_t qos; + axi_pkg::size_t size; + logic write; + } meta_t; + + axi_data_t mem_rdata, + m2s_resp; + axi_pkg::len_t r_cnt_d, r_cnt_q, + w_cnt_d, w_cnt_q; + logic arb_valid, arb_ready, + rd_valid, rd_ready, + wr_valid, wr_ready, + sel_b, sel_buf_b, + sel_r, sel_buf_r, + sel_valid, sel_ready, + sel_buf_valid, sel_buf_ready, + sel_lock_d, sel_lock_q, + meta_valid, meta_ready, + meta_buf_valid, meta_buf_ready, + meta_sel_d, meta_sel_q, + m2s_req_valid, m2s_req_ready, + m2s_resp_valid, m2s_resp_ready, + mem_req_valid, mem_req_ready, + mem_rvalid; + mem_req_t m2s_req, + mem_req; + meta_t rd_meta, + rd_meta_d, rd_meta_q, + wr_meta, + wr_meta_d, wr_meta_q, + meta, meta_buf; + + assign busy_o = axi_req_i.aw_valid | axi_req_i.ar_valid | axi_req_i.w_valid | + axi_resp_o.b_valid | axi_resp_o.r_valid | + (r_cnt_q > 0) | (w_cnt_q > 0); + + // Handle reads. + always_comb begin + // Default assignments + axi_resp_o.ar_ready = 1'b0; + rd_meta_d = rd_meta_q; + rd_meta = meta_t'{default: '0}; + rd_valid = 1'b0; + r_cnt_d = r_cnt_q; + // Handle R burst in progress. + if (r_cnt_q > '0) begin + rd_meta_d.last = (r_cnt_q == 8'd1); + rd_meta = rd_meta_d; + rd_meta.addr = rd_meta_q.addr + axi_pkg::num_bytes(rd_meta_q.size); + rd_valid = 1'b1; + if (rd_ready) begin + r_cnt_d--; + rd_meta_d.addr = rd_meta.addr; + end + // Handle new AR if there is one. + end else if (axi_req_i.ar_valid) begin + rd_meta_d = '{ + addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.ar.addr, axi_req_i.ar.size)), + atop: '0, + id: axi_req_i.ar.id, + last: (axi_req_i.ar.len == '0), + qos: axi_req_i.ar.qos, + size: axi_req_i.ar.size, + write: 1'b0 + }; + rd_meta = rd_meta_d; + rd_meta.addr = addr_t'(axi_req_i.ar.addr); + rd_valid = 1'b1; + if (rd_ready) begin + r_cnt_d = axi_req_i.ar.len; + axi_resp_o.ar_ready = 1'b1; + end + end + end + + // Handle writes. + always_comb begin + // Default assignments + axi_resp_o.aw_ready = 1'b0; + axi_resp_o.w_ready = 1'b0; + wr_meta_d = wr_meta_q; + wr_meta = meta_t'{default: '0}; + wr_valid = 1'b0; + w_cnt_d = w_cnt_q; + // Handle W bursts in progress. + if (w_cnt_q > '0) begin + wr_meta_d.last = (w_cnt_q == 8'd1); + wr_meta = wr_meta_d; + wr_meta.addr = wr_meta_q.addr + axi_pkg::num_bytes(wr_meta_q.size); + if (axi_req_i.w_valid) begin + wr_valid = 1'b1; + if (wr_ready) begin + axi_resp_o.w_ready = 1'b1; + w_cnt_d--; + wr_meta_d.addr = wr_meta.addr; + end + end + // Handle new AW if there is one. + end else if (axi_req_i.aw_valid && axi_req_i.w_valid) begin + wr_meta_d = '{ + addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.aw.addr, axi_req_i.aw.size)), + atop: axi_req_i.aw.atop, + id: axi_req_i.aw.id, + last: (axi_req_i.aw.len == '0), + qos: axi_req_i.aw.qos, + size: axi_req_i.aw.size, + write: 1'b1 + }; + wr_meta = wr_meta_d; + wr_meta.addr = addr_t'(axi_req_i.aw.addr); + wr_valid = 1'b1; + if (wr_ready) begin + w_cnt_d = axi_req_i.aw.len; + axi_resp_o.aw_ready = 1'b1; + axi_resp_o.w_ready = 1'b1; + end + end + end + + // Arbitrate between reads and writes. + stream_mux #( + .DATA_T ( meta_t ), + .N_INP ( 32'd2 ) + ) i_ax_mux ( + .inp_data_i ({wr_meta, rd_meta }), + .inp_valid_i ({wr_valid, rd_valid}), + .inp_ready_o ({wr_ready, rd_ready}), + .inp_sel_i ( meta_sel_d ), + .oup_data_o ( meta ), + .oup_valid_o ( arb_valid ), + .oup_ready_i ( arb_ready ) + ); + always_comb begin + meta_sel_d = meta_sel_q; + sel_lock_d = sel_lock_q; + if (sel_lock_q) begin + meta_sel_d = meta_sel_q; + if (arb_valid && arb_ready) begin + sel_lock_d = 1'b0; + end + end else begin + if (wr_valid ^ rd_valid) begin + // If either write or read is valid but not both, select the valid one. + meta_sel_d = wr_valid; + end else if (wr_valid && rd_valid) begin + // If both write and read are valid, decide according to QoS then burst properties. + // Prioritize higher QoS. + if (wr_meta.qos > rd_meta.qos) begin + meta_sel_d = 1'b1; + end else if (rd_meta.qos > wr_meta.qos) begin + meta_sel_d = 1'b0; + // Decide requests with identical QoS. + end else if (wr_meta.qos == rd_meta.qos) begin + // 1. Prioritize individual writes over read bursts. + // Rationale: Read bursts can be interleaved on AXI but write bursts cannot. + if (wr_meta.last && !rd_meta.last) begin + meta_sel_d = 1'b1; + // 2. Prioritize ongoing burst. + // Rationale: Stalled bursts create back-pressure or require costly buffers. + end else if (w_cnt_q > '0) begin + meta_sel_d = 1'b1; + end else if (r_cnt_q > '0) begin + meta_sel_d = 1'b0; + // 3. Otherwise arbitrate round robin to prevent starvation. + end else begin + meta_sel_d = ~meta_sel_q; + end + end + end + // Lock arbitration if valid but not yet ready. + if (arb_valid && !arb_ready) begin + sel_lock_d = 1'b1; + end + end + end + + // Fork arbitrated stream to meta data, memory requests, and R/B channel selection. + stream_fork #( + .N_OUP ( 32'd3 ) + ) i_fork ( + .clk_i, + .rst_ni, + .valid_i ( arb_valid ), + .ready_o ( arb_ready ), + .valid_o ({sel_valid, meta_valid, m2s_req_valid}), + .ready_i ({sel_ready, meta_ready, m2s_req_ready}) + ); + + assign sel_b = meta.write & meta.last; + assign sel_r = ~meta.write | meta.atop[5]; + + stream_fifo #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( 32'd1 + BufDepth ), + .T ( logic[1:0] ) + ) i_sel_buf ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .data_i ({sel_b, sel_r }), + .valid_i ( sel_valid ), + .ready_o ( sel_ready ), + .data_o ({sel_buf_b, sel_buf_r}), + .valid_o ( sel_buf_valid ), + .ready_i ( sel_buf_ready ), + .usage_o ( /* unused */ ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( 32'd1 + BufDepth ), + .T ( meta_t ) + ) i_meta_buf ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .data_i ( meta ), + .valid_i ( meta_valid ), + .ready_o ( meta_ready ), + .data_o ( meta_buf ), + .valid_o ( meta_buf_valid ), + .ready_i ( meta_buf_ready ), + .usage_o ( /* unused */ ) + ); + + // Assemble the actual memory request from meta information and write data. + assign m2s_req = mem_req_t'{ + addr: meta.addr, + atop: meta.atop, + strb: axi_req_i.w.strb, + wdata: axi_req_i.w.data, + we: meta.write + }; + + // Interface memory as stream. + stream_to_mem #( + .mem_req_t ( mem_req_t ), + .mem_resp_t ( axi_data_t ), + .BufDepth ( BufDepth ) + ) i_stream_to_mem ( + .clk_i, + .rst_ni, + .req_i ( m2s_req ), + .req_valid_i ( m2s_req_valid ), + .req_ready_o ( m2s_req_ready ), + .resp_o ( m2s_resp ), + .resp_valid_o ( m2s_resp_valid ), + .resp_ready_i ( m2s_resp_ready ), + .mem_req_o ( mem_req ), + .mem_req_valid_o ( mem_req_valid ), + .mem_req_ready_i ( mem_req_ready ), + .mem_resp_i ( mem_rdata ), + .mem_resp_valid_i ( mem_rvalid ) + ); + + // Split single memory request to desired number of banks. + mem_to_banks #( + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .NumBanks ( NumBanks ) + ) i_mem_to_banks ( + .clk_i, + .rst_ni, + .req_i ( mem_req_valid ), + .gnt_o ( mem_req_ready ), + .addr_i ( mem_req.addr ), + .wdata_i ( mem_req.wdata ), + .strb_i ( mem_req.strb ), + .atop_i ( mem_req.atop ), + .we_i ( mem_req.we ), + .rvalid_o ( mem_rvalid ), + .rdata_o ( mem_rdata ), + .bank_req_o ( mem_req_o ), + .bank_gnt_i ( mem_gnt_i ), + .bank_addr_o ( mem_addr_o ), + .bank_wdata_o ( mem_wdata_o ), + .bank_strb_o ( mem_strb_o ), + .bank_atop_o ( mem_atop_o ), + .bank_we_o ( mem_we_o ), + .bank_rvalid_i ( mem_rvalid_i ), + .bank_rdata_i ( mem_rdata_i ) + ); + + // Join memory read data and meta data stream. + logic mem_join_valid, mem_join_ready; + stream_join #( + .N_INP ( 32'd2 ) + ) i_join ( + .inp_valid_i ({m2s_resp_valid, meta_buf_valid}), + .inp_ready_o ({m2s_resp_ready, meta_buf_ready}), + .oup_valid_o ( mem_join_valid ), + .oup_ready_i ( mem_join_ready ) + ); + + // Dynamically fork the joined stream to B and R channels. + stream_fork_dynamic #( + .N_OUP ( 32'd2 ) + ) i_fork_dynamic ( + .clk_i, + .rst_ni, + .valid_i ( mem_join_valid ), + .ready_o ( mem_join_ready ), + .sel_i ({sel_buf_b, sel_buf_r }), + .sel_valid_i ( sel_buf_valid ), + .sel_ready_o ( sel_buf_ready ), + .valid_o ({axi_resp_o.b_valid, axi_resp_o.r_valid}), + .ready_i ({axi_req_i.b_ready, axi_req_i.r_ready }) + ); + + // Compose B responses. + assign axi_resp_o.b = '{ + id: meta_buf.id, + resp: axi_pkg::RESP_OKAY, + user: '0 + }; + + // Compose R responses. + assign axi_resp_o.r = '{ + data: m2s_resp, + id: meta_buf.id, + last: meta_buf.last, + resp: axi_pkg::RESP_OKAY, + user: '0 + }; + + // Registers + `FFARN(meta_sel_q, meta_sel_d, 1'b0, clk_i, rst_ni) + `FFARN(sel_lock_q, sel_lock_d, 1'b0, clk_i, rst_ni) + `FFARN(rd_meta_q, rd_meta_d, meta_t'{default: '0}, clk_i, rst_ni) + `FFARN(wr_meta_q, wr_meta_d, meta_t'{default: '0}, clk_i, rst_ni) + `FFARN(r_cnt_q, r_cnt_d, '0, clk_i, rst_ni) + `FFARN(w_cnt_q, w_cnt_d, '0, clk_i, rst_ni) + + // Assertions + // pragma translate_off + `ifndef VERILATOR + default disable iff (!rst_ni); + assume property (@(posedge clk_i) + axi_req_i.ar_valid && !axi_resp_o.ar_ready |=> $stable(axi_req_i.ar)) + else $error("AR must remain stable until handshake has happened!"); + assert property (@(posedge clk_i) + axi_resp_o.r_valid && !axi_req_i.r_ready |=> $stable(axi_resp_o.r)) + else $error("R must remain stable until handshake has happened!"); + assume property (@(posedge clk_i) + axi_req_i.aw_valid && !axi_resp_o.aw_ready |=> $stable(axi_req_i.aw)) + else $error("AW must remain stable until handshake has happened!"); + assume property (@(posedge clk_i) + axi_req_i.w_valid && !axi_resp_o.w_ready |=> $stable(axi_req_i.w)) + else $error("W must remain stable until handshake has happened!"); + assert property (@(posedge clk_i) + axi_resp_o.b_valid && !axi_req_i.b_ready |=> $stable(axi_resp_o.b)) + else $error("B must remain stable until handshake has happened!"); + assert property (@(posedge clk_i) axi_req_i.ar_valid && axi_req_i.ar.len > 0 |-> + axi_req_i.ar.burst == axi_pkg::BURST_INCR) + else $error("Non-incrementing bursts are not supported!"); + assert property (@(posedge clk_i) axi_req_i.aw_valid && axi_req_i.aw.len > 0 |-> + axi_req_i.aw.burst == axi_pkg::BURST_INCR) + else $error("Non-incrementing bursts are not supported!"); + assert property (@(posedge clk_i) meta_valid && meta.atop != '0 |-> meta.write) + else $warning("Unexpected atomic operation on read."); + `endif + // pragma translate_on +endmodule + + +`include "axi/assign.svh" +`include "axi/typedef.svh" +/// Interface wrapper for module `axi_to_mem`. +module axi_to_mem_intf #( + /// See `axi_to_mem`, parameter `AddrWidth`. + parameter int unsigned ADDR_WIDTH = 32'd0, + /// See `axi_to_mem`, parameter `DataWidth`. + parameter int unsigned DATA_WIDTH = 32'd0, + /// AXI4+ATOP ID width. + parameter int unsigned ID_WIDTH = 32'd0, + /// AXI4+ATOP user width. + parameter int unsigned USER_WIDTH = 32'd0, + /// See `axi_to_mem`, parameter `NumBanks`. + parameter int unsigned NUM_BANKS = 32'd0, + /// See `axi_to_mem`, parameter `BufDepth`. + parameter int unsigned BUF_DEPTH = 32'd1, + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `addr_t`. + localparam type addr_t = logic [ADDR_WIDTH-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_data_t`. + localparam type mem_data_t = logic [DATA_WIDTH/NUM_BANKS-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_strb_t`. + localparam type mem_strb_t = logic [DATA_WIDTH/NUM_BANKS/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// See `axi_to_mem`, port `busy_o`. + output logic busy_o, + /// AXI4+ATOP slave interface port. + AXI_BUS.Slave slv, + /// See `axi_to_mem`, port `mem_req_o`. + output logic [NUM_BANKS-1:0] mem_req_o, + /// See `axi_to_mem`, port `mem_gnt_i`. + input logic [NUM_BANKS-1:0] mem_gnt_i, + /// See `axi_to_mem`, port `mem_addr_o`. + output addr_t [NUM_BANKS-1:0] mem_addr_o, + /// See `axi_to_mem`, port `mem_wdata_o`. + output mem_data_t [NUM_BANKS-1:0] mem_wdata_o, + /// See `axi_to_mem`, port `mem_strb_o`. + output mem_strb_t [NUM_BANKS-1:0] mem_strb_o, + /// See `axi_to_mem`, port `mem_atop_o`. + output axi_pkg::atop_t [NUM_BANKS-1:0] mem_atop_o, + /// See `axi_to_mem`, port `mem_we_o`. + output logic [NUM_BANKS-1:0] mem_we_o, + /// See `axi_to_mem`, port `mem_rvalid_i`. + input logic [NUM_BANKS-1:0] mem_rvalid_i, + /// See `axi_to_mem`, port `mem_rdata_i`. + input mem_data_t [NUM_BANKS-1:0] mem_rdata_i +); + typedef logic [ID_WIDTH-1:0] id_t; + typedef logic [DATA_WIDTH-1:0] data_t; + typedef logic [DATA_WIDTH/8-1:0] strb_t; + typedef logic [USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + req_t req; + resp_t resp; + `AXI_ASSIGN_TO_REQ(req, slv) + `AXI_ASSIGN_FROM_RESP(slv, resp) + axi_to_mem #( + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .AddrWidth ( ADDR_WIDTH ), + .DataWidth ( DATA_WIDTH ), + .IdWidth ( ID_WIDTH ), + .NumBanks ( NUM_BANKS ), + .BufDepth ( BUF_DEPTH ) + ) i_axi_to_mem ( + .clk_i, + .rst_ni, + .busy_o, + .axi_req_i ( req ), + .axi_resp_o ( resp ), + .mem_req_o, + .mem_gnt_i, + .mem_addr_o, + .mem_wdata_o, + .mem_strb_o, + .mem_atop_o, + .mem_we_o, + .mem_rvalid_i, + .mem_rdata_i + ); +endmodule + +/// Split memory access over multiple parallel banks, where each bank has its own req/gnt +/// request and valid response direction. +module mem_to_banks #( + /// Input address width. + parameter int unsigned AddrWidth = 32'd0, + /// Input data width, must be a power of two. + parameter int unsigned DataWidth = 32'd0, + /// Number of banks at output, must evenly divide `DataWidth`. + parameter int unsigned NumBanks = 32'd0, + /// Dependent parameter, do not override! Address type. + localparam type addr_t = logic [AddrWidth-1:0], + /// Dependent parameter, do not override! Input data type. + localparam type inp_data_t = logic [DataWidth-1:0], + /// Dependent parameter, do not override! Input write strobe type. + localparam type inp_strb_t = logic [DataWidth/8-1:0], + /// Dependent parameter, do not override! Output data type. + localparam type oup_data_t = logic [DataWidth/NumBanks-1:0], + /// Dependent parameter, do not override! Output write strobe type. + localparam type oup_strb_t = logic [DataWidth/NumBanks/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Memory request to split, request is valid. + input logic req_i, + /// Memory request to split, request can be granted. + output logic gnt_o, + /// Memory request to split, request address, byte-wise. + input addr_t addr_i, + /// Memory request to split, request write data. + input inp_data_t wdata_i, + /// Memory request to split, request write strobe. + input inp_strb_t strb_i, + /// Memory request to split, request Atomic signal from AXI4+ATOP. + input axi_pkg::atop_t atop_i, + /// Memory request to split, request write enable, active high. + input logic we_i, + /// Memory request to split, response is valid. Required for read and write requests + output logic rvalid_o, + /// Memory request to split, response read data. + output inp_data_t rdata_o, + /// Memory bank request, request is valid. + output logic [NumBanks-1:0] bank_req_o, + /// Memory bank request, request can be granted. + input logic [NumBanks-1:0] bank_gnt_i, + /// Memory bank request, request address, byte-wise. Will be different for each bank. + output addr_t [NumBanks-1:0] bank_addr_o, + /// Memory bank request, request write data. + output oup_data_t [NumBanks-1:0] bank_wdata_o, + /// Memory bank request, request write strobe. + output oup_strb_t [NumBanks-1:0] bank_strb_o, + /// Memory bank request, request Atomic signal from AXI4+ATOP. + output axi_pkg::atop_t [NumBanks-1:0] bank_atop_o, + /// Memory bank request, request write enable, active high. + output logic [NumBanks-1:0] bank_we_o, + /// Memory bank request, response is valid. Required for read and write requests + input logic [NumBanks-1:0] bank_rvalid_i, + /// Memory bank request, response read data. + input oup_data_t [NumBanks-1:0] bank_rdata_i +); + + localparam DataBytes = $bits(inp_strb_t); + localparam BitsPerBank = $bits(oup_data_t); + localparam BytesPerBank = $bits(oup_strb_t); + + typedef struct packed { + addr_t addr; + oup_data_t wdata; + oup_strb_t strb; + axi_pkg::atop_t atop; + logic we; + } req_t; + + logic req_valid; + logic [NumBanks-1:0] req_ready, + resp_valid, resp_ready; + req_t [NumBanks-1:0] bank_req, + bank_oup; + + function automatic addr_t align_addr(input addr_t addr); + return (addr >> $clog2(DataBytes)) << $clog2(DataBytes); + endfunction + + // Handle requests. + assign req_valid = req_i & gnt_o; + for (genvar i = 0; unsigned'(i) < NumBanks; i++) begin : gen_reqs + assign bank_req[i].addr = align_addr(addr_i) + i * BytesPerBank; + assign bank_req[i].wdata = wdata_i[i*BitsPerBank+:BitsPerBank]; + assign bank_req[i].strb = strb_i[i*BytesPerBank+:BytesPerBank]; + assign bank_req[i].atop = atop_i; + assign bank_req[i].we = we_i; + fall_through_register #( + .T ( req_t ) + ) i_ft_reg ( + .clk_i, + .rst_ni, + .clr_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .valid_i ( req_valid ), + .ready_o ( req_ready[i] ), + .data_i ( bank_req[i] ), + .valid_o ( bank_req_o[i] ), + .ready_i ( bank_gnt_i[i] ), + .data_o ( bank_oup[i] ) + ); + assign bank_addr_o[i] = bank_oup[i].addr; + assign bank_wdata_o[i] = bank_oup[i].wdata; + assign bank_strb_o[i] = bank_oup[i].strb; + assign bank_atop_o[i] = bank_oup[i].atop; + assign bank_we_o[i] = bank_oup[i].we; + end + + // Grant output if all our requests have been granted. + assign gnt_o = (&req_ready) & (&resp_ready); + + // Handle responses. + for (genvar i = 0; unsigned'(i) < NumBanks; i++) begin : gen_resp_regs + fall_through_register #( + .T ( oup_data_t ) + ) i_ft_reg ( + .clk_i, + .rst_ni, + .clr_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .valid_i ( bank_rvalid_i[i] ), + .ready_o ( resp_ready[i] ), + .data_i ( bank_rdata_i[i] ), + .data_o ( rdata_o[i*BitsPerBank+:BitsPerBank] ), + .ready_i ( rvalid_o ), + .valid_o ( resp_valid[i] ) + ); + end + assign rvalid_o = &resp_valid; + + // Assertions + // pragma translate_off + `ifndef VERILATOR + initial begin + assume (DataWidth != 0 && (DataWidth & (DataWidth - 1)) == 0) + else $fatal(1, "Data width must be a power of two!"); + assume (DataWidth % NumBanks == 0) + else $fatal(1, "Data width must be evenly divisible over banks!"); + assume ((DataWidth / NumBanks) % 8 == 0) + else $fatal(1, "Data width of each bank must be divisible into 8-bit bytes!"); + end + `endif + // pragma translate_on +endmodule From 8c475d92a96ccd3769cf99ef92fb366faaf71cd8 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Mon, 8 Aug 2022 19:21:02 +0200 Subject: [PATCH 008/145] axi_to_mem: Hide writes with a strobe equal to '0 --- src/axi_to_mem.sv | 60 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/src/axi_to_mem.sv b/src/axi_to_mem.sv index 735d47c12..cbdb311f3 100644 --- a/src/axi_to_mem.sv +++ b/src/axi_to_mem.sv @@ -33,6 +33,8 @@ module axi_to_mem #( parameter int unsigned NumBanks = 0, /// Depth of memory response buffer. This should be equal to the memory response latency. parameter int unsigned BufDepth = 1, + /// Hide write requests if the strb == '0 + parameter bit HideStrb = 1'b0, /// Dependent parameter, do not override. Memory address type. localparam type addr_t = logic [AddrWidth-1:0], /// Dependent parameter, do not override. Memory data type. @@ -349,7 +351,9 @@ module axi_to_mem #( mem_to_banks #( .AddrWidth ( AddrWidth ), .DataWidth ( DataWidth ), - .NumBanks ( NumBanks ) + .NumBanks ( NumBanks ), + .HideStrb ( HideStrb ), + .MaxTrans ( BufDepth ) ) i_mem_to_banks ( .clk_i, .rst_ni, @@ -471,6 +475,8 @@ module axi_to_mem_intf #( parameter int unsigned NUM_BANKS = 32'd0, /// See `axi_to_mem`, parameter `BufDepth`. parameter int unsigned BUF_DEPTH = 32'd1, + /// Hide write requests if the strb == '0 + parameter bit HIDE_STRB = 1'b0, /// Dependent parameter, do not override. See `axi_to_mem`, parameter `addr_t`. localparam type addr_t = logic [ADDR_WIDTH-1:0], /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_data_t`. @@ -527,7 +533,8 @@ module axi_to_mem_intf #( .DataWidth ( DATA_WIDTH ), .IdWidth ( ID_WIDTH ), .NumBanks ( NUM_BANKS ), - .BufDepth ( BUF_DEPTH ) + .BufDepth ( BUF_DEPTH ), + .HideStrb ( HIDE_STRB ) ) i_axi_to_mem ( .clk_i, .rst_ni, @@ -555,6 +562,10 @@ module mem_to_banks #( parameter int unsigned DataWidth = 32'd0, /// Number of banks at output, must evenly divide `DataWidth`. parameter int unsigned NumBanks = 32'd0, + /// Remove transactions that have zero strobe + parameter bit HideStrb = 1'b0, + /// Number of outstanding transactions + parameter int unsigned MaxTrans = 32'b1, /// Dependent parameter, do not override! Address type. localparam type addr_t = logic [AddrWidth-1:0], /// Dependent parameter, do not override! Input data type. @@ -625,6 +636,8 @@ module mem_to_banks #( resp_valid, resp_ready; req_t [NumBanks-1:0] bank_req, bank_oup; + logic [NumBanks-1:0] bank_req_internal, bank_gnt_internal, zero_strobe, dead_response; + logic dead_write_fifo_full; function automatic addr_t align_addr(input addr_t addr); return (addr >> $clog2(DataBytes)) << $clog2(DataBytes); @@ -648,8 +661,8 @@ module mem_to_banks #( .valid_i ( req_valid ), .ready_o ( req_ready[i] ), .data_i ( bank_req[i] ), - .valid_o ( bank_req_o[i] ), - .ready_i ( bank_gnt_i[i] ), + .valid_o ( bank_req_internal[i] ), + .ready_i ( bank_gnt_internal[i] ), .data_o ( bank_oup[i] ) ); assign bank_addr_o[i] = bank_oup[i].addr; @@ -657,10 +670,43 @@ module mem_to_banks #( assign bank_strb_o[i] = bank_oup[i].strb; assign bank_atop_o[i] = bank_oup[i].atop; assign bank_we_o[i] = bank_oup[i].we; + + assign zero_strobe[i] = (bank_oup[i].strb == '0); + + if (HideStrb) begin + assign bank_req_o[i] = (bank_oup[i].we && zero_strobe[i]) ? 1'b0 : bank_req_internal[i]; + assign bank_gnt_internal[i] = (bank_oup[i].we && zero_strobe[i]) ? 1'b1 : bank_gnt_i[i]; + end else begin + assign bank_req_o[i] = bank_req_internal[i]; + assign bank_gnt_internal[i] = bank_gnt_i[i]; + end end // Grant output if all our requests have been granted. - assign gnt_o = (&req_ready) & (&resp_ready); + assign gnt_o = (&req_ready) & (&resp_ready) & !dead_write_fifo_full; + + if (HideStrb) begin : gen_dead_write_fifo + fifo_v3 #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( MaxTrans+1 ), + .DATA_WIDTH ( NumBanks ) + ) i_dead_write_fifo ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( dead_write_fifo_full ), + .empty_o (), + .usage_o (), + .data_i ( bank_we_o & zero_strobe ), + .push_i ( req_i & gnt_o ), + .data_o ( dead_response ), + .pop_i ( rvalid_o ) + ); + end else begin + assign dead_response = '0; + assign dead_write_fifo_full = 1'b0; + end // Handle responses. for (genvar i = 0; unsigned'(i) < NumBanks; i++) begin : gen_resp_regs @@ -675,11 +721,11 @@ module mem_to_banks #( .ready_o ( resp_ready[i] ), .data_i ( bank_rdata_i[i] ), .data_o ( rdata_o[i*BitsPerBank+:BitsPerBank] ), - .ready_i ( rvalid_o ), + .ready_i ( rvalid_o & !dead_response[i] ), .valid_o ( resp_valid[i] ) ); end - assign rvalid_o = &resp_valid; + assign rvalid_o = &(resp_valid | dead_response); // Assertions // pragma translate_off From 6f48b9955feabf3ab8a513a9edbadd270b58cc93 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Mon, 8 Aug 2022 09:43:45 +0200 Subject: [PATCH 009/145] axi_to_mem: Update doc --- README.md | 1 + src_files.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 702089d7c..d26bcc0e3 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_serializer`](src/axi_serializer.sv) | Serializes transactions with different IDs to the same ID. | | | [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | | [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | +| [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. | | | [`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) | ### Simulation-Only Modules diff --git a/src_files.yml b/src_files.yml index 752fd90df..ed4fe70d0 100644 --- a/src_files.yml +++ b/src_files.yml @@ -37,6 +37,7 @@ axi: - src/axi_modify_address.sv - src/axi_mux.sv - src/axi_serializer.sv + - src/axi_to_mem.sv # Level 3 - src/axi_cdc.sv - src/axi_err_slv.sv From 99f1357ea566780d6f338c4b674f1400d4a6ea76 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Wed, 17 Aug 2022 10:05:12 +0200 Subject: [PATCH 010/145] axi_test: Add `mapped` mode to random classes, additions to scoreboard class --- CHANGELOG.md | 2 + src/axi_test.sv | 137 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 123 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33cdc4c06..07e721991 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 - `axi_to_mem`: AXI4+ATOP slave to control on chip memory. +- `axi_test`: Add `mapped` mode to the random classes as well as additional functionality to the + scoreboard class. ### Changed diff --git a/src/axi_test.sv b/src/axi_test.sv index 28e7b2b90..053ac23b3 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -1190,11 +1190,12 @@ package axi_test; static logic rand_success; wait (w_queue.size() > 0 || (aw_done && w_queue.size() == 0)); aw_beat = w_queue.pop_front(); - addr = aw_beat.ax_addr; for (int unsigned i = 0; i < aw_beat.ax_len + 1; i++) begin automatic w_beat_t w_beat = new; automatic int unsigned begin_byte, end_byte, n_bytes; automatic logic [AXI_STRB_WIDTH-1:0] rand_strb, strb_mask; + addr = axi_pkg::beat_addr(aw_beat.ax_addr, aw_beat.ax_size, aw_beat.ax_len, + aw_beat.ax_burst, i); rand_success = w_beat.randomize(); assert (rand_success); // Determine strobe. w_beat.w_strb = '0; @@ -1210,8 +1211,6 @@ package axi_test; w_beat.w_last = (i == aw_beat.ax_len); rand_wait(W_MIN_WAIT_CYCLES, W_MAX_WAIT_CYCLES); drv.send_w(w_beat); - if (aw_beat.ax_burst == axi_pkg::BURST_INCR) - addr += n_bytes; end end endtask @@ -1269,7 +1268,11 @@ package axi_test; parameter int R_MIN_WAIT_CYCLES = 0, parameter int R_MAX_WAIT_CYCLES = 5, parameter int RESP_MIN_WAIT_CYCLES = 0, - parameter int RESP_MAX_WAIT_CYCLES = 20 + parameter int RESP_MAX_WAIT_CYCLES = 20, + /// This parameter eneables an internal memory, which gets randomly initialized, if it is read + /// and retains written data. This mode does currently not support `axi_pkg::BURST_WRAP`! + /// All responses are `axi_pkg::RESP_OKAY` when in this mode. + parameter bit MAPPED = 1'b0 ); typedef axi_test::axi_driver #( .AW(AW), .DW(DW), .IW(IW), .UW(UW), .TA(TA), .TT(TT) @@ -1283,11 +1286,17 @@ package axi_test; typedef axi_driver_t::r_beat_t r_beat_t; typedef axi_driver_t::w_beat_t w_beat_t; + typedef logic [AW-1:0] addr_t; + typedef logic [7:0] byte_t; + axi_driver_t drv; rand_ax_beat_queue_t ar_queue; ax_beat_t aw_queue[$]; int unsigned b_wait_cnt; + // Memory array for when the `MAPPED` parameter is set. + byte_t memory_q[addr_t]; + function new( virtual AXI_BUS_DV #( .AXI_ADDR_WIDTH(AW), @@ -1303,7 +1312,8 @@ package axi_test; endfunction function void reset(); - drv.reset_slave(); + this.drv.reset_slave(); + this.memory_q.delete(); endfunction // TODO: The `rand_wait` task exists in `rand_verif_pkg`, but that task cannot be called with @@ -1323,6 +1333,10 @@ package axi_test; automatic ax_beat_t ar_beat; rand_wait(AX_MIN_WAIT_CYCLES, AX_MAX_WAIT_CYCLES); drv.recv_ar(ar_beat); + if (MAPPED) begin + assert (ar_beat.ax_burst != axi_pkg::BURST_WRAP) else + $error("axi_pkg::BURST_WRAP not supported in MAPPED mode."); + end ar_queue.push(ar_beat.ax_id, ar_beat); end endtask @@ -1331,10 +1345,25 @@ package axi_test; forever begin automatic logic rand_success; automatic ax_beat_t ar_beat; - automatic r_beat_t r_beat = new; + automatic r_beat_t r_beat = new; + automatic addr_t byte_addr; wait (ar_queue.size > 0); - ar_beat = ar_queue.peek(); + ar_beat = ar_queue.peek(); + byte_addr = axi_pkg::aligned_addr(ar_beat.ax_addr, axi_pkg::size_t'($clog2(DW/8))); + rand_success = std::randomize(r_beat); assert(rand_success); rand_success = r_beat.randomize(); assert(rand_success); + if (MAPPED) begin + // Either use the actual data, or save the random generated. + for (int unsigned i = 0; i < (DW/8); i++) begin + if (this.memory_q.exists(byte_addr)) begin + r_beat.r_data[i*8+:8] = this.memory_q[byte_addr]; + end else begin + this.memory_q[byte_addr] = r_beat.r_data[i*8+:8]; + end + byte_addr++; + end + r_beat.r_resp = axi_pkg::RESP_OKAY; + end r_beat.r_id = ar_beat.ax_id; if (RAND_RESP && !ar_beat.ax_atop[axi_pkg::ATOP_R_RESP]) r_beat.r_resp[1] = $random(); @@ -1345,6 +1374,10 @@ package axi_test; r_beat.r_last = 1'b1; void'(ar_queue.pop_id(ar_beat.ax_id)); end else begin + if ((ar_beat.ax_burst == axi_pkg::BURST_INCR) && MAPPED) begin + ar_beat.ax_addr = axi_pkg::aligned_addr(ar_beat.ax_addr, ar_beat.ax_size) + + 2**ar_beat.ax_size; + end ar_beat.ax_len--; ar_queue.set(ar_beat.ax_id, ar_beat); end @@ -1357,6 +1390,12 @@ package axi_test; automatic ax_beat_t aw_beat; rand_wait(AX_MIN_WAIT_CYCLES, AX_MAX_WAIT_CYCLES); drv.recv_aw(aw_beat); + if (MAPPED) begin + assert (aw_beat.ax_atop == '0) else + $error("ATOP not supported in MAPPED mode."); + assert (aw_beat.ax_burst != axi_pkg::BURST_WRAP) else + $error("axi_pkg::BURST_WRAP not supported in MAPPED mode."); + end aw_queue.push_back(aw_beat); // Atomic{Load,Swap,Compare}s require an R response. if (aw_beat.ax_atop[axi_pkg::ATOP_R_RESP]) begin @@ -1368,10 +1407,30 @@ package axi_test; task recv_ws(); forever begin automatic ax_beat_t aw_beat; + automatic addr_t byte_addr; forever begin automatic w_beat_t w_beat; rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); drv.recv_w(w_beat); + if (MAPPED) begin + wait (aw_queue.size() > 0); + aw_beat = aw_queue[0]; + byte_addr = axi_pkg::aligned_addr(aw_beat.ax_addr, $clog2(DW/8)); + + // Write Data if the strobe is defined + for (int unsigned i = 0; i < (DW/8); i++) begin + if (w_beat.w_strb[i]) begin + this.memory_q[byte_addr] = w_beat.w_data[i*8+:8]; + end + byte_addr++; + end + // Update address in beat + if (aw_beat.ax_burst == axi_pkg::BURST_INCR) begin + aw_beat.ax_addr = axi_pkg::aligned_addr(aw_beat.ax_addr, aw_beat.ax_size) + + 2**aw_beat.ax_size; + end + aw_queue[0] = aw_beat; + end if (w_beat.w_last) break; end @@ -1394,6 +1453,9 @@ package axi_test; b_beat.b_resp[0]= $random(); end rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); + if (MAPPED) begin + b_beat.b_resp = axi_pkg::RESP_OKAY; + end drv.send_b(b_beat); b_wait_cnt--; end @@ -1939,8 +2001,6 @@ package axi_test; b_beat = b_sample[id].pop_front(); if (check_en[BRespCheck]) begin assert (b_beat.b_id == id); - assert (b_beat.b_resp == axi_pkg::RESP_OKAY) else - $warning("Behavior for b_resp != axi_pkg::RESP_OKAY not modeled."); end // pop all accessed memory locations by this beat for (int unsigned i = 0; i <= aw_beat.ax_len; i++) begin @@ -1948,7 +2008,11 @@ package axi_test; axi_pkg::beat_addr(aw_beat.ax_addr, aw_beat.ax_size, aw_beat.ax_len, aw_beat.ax_burst, i), BUS_SIZE); for (int j = 0; j < axi_pkg::num_bytes(BUS_SIZE); j++) begin - memory_q[bus_address+j].delete(0); + if (b_beat.b_resp inside {axi_pkg::RESP_OKAY, axi_pkg::RESP_EXOKAY}) begin + memory_q[bus_address+j].delete(0); + end else begin + memory_q[bus_address+j].delete(memory_q[bus_address+j].size() - 1); + end end end end @@ -1982,22 +2046,29 @@ package axi_test; end end // Assert that the correct data is read. - if (this.check_en[ReadCheck]) begin + if (this.check_en[ReadCheck] && + (r_beat.r_resp inside {axi_pkg::RESP_OKAY, axi_pkg::RESP_EXOKAY})) begin for (int unsigned j = 0; j < axi_pkg::num_bytes(ar_beat.ax_size); j++) begin idx_data = 8*BUS_SIZE'(beat_address+j); act_data = r_beat.r_data[idx_data+:8]; exp_data = this.memory_q[beat_address+j]; - tst_data = exp_data.find with (item === 8'hxx || item === act_data); - assert (tst_data.size() > 0) else begin - $warning("Unexpected RData ID: %0h Addr: %0h Byte Idx: %0h Exp Data : %0h Data: %h", - r_beat.r_id, beat_address+j, idx_data, exp_data, act_data); + if (exp_data.size() > 0) begin + tst_data = exp_data.find with (item === 8'hxx || item === act_data); + assert (tst_data.size() > 0) else begin + $warning("Unexpected RData ID: %0h \n \ + Addr: %h \n \ + Byte Idx: %h \n \ + Exp Data: %h \n \ + Act Data: %h \n \ + BeatData: %h", + r_beat.r_id, beat_address+j, idx_data, exp_data, act_data, r_beat.r_data); + end end end end end if (this.check_en[RRespCheck]) begin assert (r_beat.r_id == id); - assert (r_beat.r_resp == axi_pkg::RESP_OKAY); assert (r_beat.r_last); end end @@ -2182,6 +2253,40 @@ package axi_test; assert(this.b_queue[i].size() == 0); end endtask : reset + + /// Check that the byte in memory_q is the same as check_data. + task automatic check_byte(axi_addr_t check_addr, byte_t check_data); + assert(this.memory_q[check_addr][0] === check_data) else + $warning("Byte at ADDR: %h does not match: memory_q: %h check_data: %h", + check_addr, this.memory_q[check_addr][0], check_data); + endtask : check_byte + + /// Clear a byte from memoy. Can be used to partially delete mem space. + task clear_byte(axi_addr_t clear_addr); + if (this.memory_q.exists(clear_addr)) begin + this.memory_q.delete(clear_addr); + end + endtask : clear_byte + + /// Clear a memory range. + /// The end address alo gets cleared. + task automatic clear_range(axi_addr_t clear_start_addr, clear_end_addr); + axi_addr_t curr_addr = clear_start_addr; + while (curr_addr <= clear_end_addr) begin + this.clear_byte(curr_addr); + curr_addr++; + end + endtask : clear_range + + /// Get a byte from the modeled memory. + task automatic get_byte(input axi_addr_t byte_addr, output byte_t byte_data); + if (this.memory_q.exists(byte_addr)) begin + byte_data = this.memory_q[byte_addr][0]; + end else begin + byte_data = 8'hxx; + end + endtask : get_byte + endclass : axi_scoreboard endpackage From 7cb6bac361e6e9d52f43ecd72c188612fe40c109 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Wed, 17 Aug 2022 11:35:06 +0200 Subject: [PATCH 011/145] axi_lite_demux: Increase compatibility to vsim 10.7b --- CHANGELOG.md | 1 + src/axi_lite_demux.sv | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07e721991..c4318d979 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed - `axi_lite_mux`: Reduce complexity of W channel at master port by removing an unnecessary multiplexer. +- `axi_lite_demux`: Improve compatibility with vsim version 10.7b. ## 0.36.0 - 2022-07-07 diff --git a/src/axi_lite_demux.sv b/src/axi_lite_demux.sv index a4e8fdd20..5513fb443 100644 --- a/src/axi_lite_demux.sv +++ b/src/axi_lite_demux.sv @@ -426,8 +426,15 @@ module axi_lite_demux #( ); // connect the response if the FIFO has valid data in it - assign slv_r_chan = (!r_fifo_empty) ? mst_resps_i[r_select].r : '0; - assign slv_r_valid = ~r_fifo_empty & mst_resps_i[r_select].r_valid; + always_comb begin + slv_r_chan = '0; + slv_r_valid = '0; + if (!r_fifo_empty) begin + slv_r_chan = mst_resps_i[r_select].r; + slv_r_valid = mst_resps_i[r_select].r_valid; + end + end + for (genvar i = 0; i < NoMstPorts; i++) begin : gen_mst_r assign mst_reqs_o[i].r_ready = ~r_fifo_empty & slv_r_ready & (r_select == select_t'(i)); end From bdc0ca27948fc011edb87447a0ff818c91f56059 Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Fri, 24 Apr 2020 10:55:17 +0200 Subject: [PATCH 012/145] axi_demux: Add docstring from documentation --- src/axi_demux.sv | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/axi_demux.sv b/src/axi_demux.sv index e91e5a0aa..cae7401eb 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -21,8 +21,22 @@ `define TARGET_VSIM `endif -// axi_demux: Demultiplex an AXI bus from one slave port to multiple master ports. -// See `doc/axi_demux.md` for the documentation, including the definition of parameters and ports. +/// Demultiplex one AXI4+ATOP slave port to multiple AXI4+ATOP master ports. +/// +/// The AW and AR slave channels each have a `select` input to determine to which master port the +/// current request is sent. The `select` can, for example, be driven by an address decoding module +/// to map address ranges to different AXI slaves. +/// +/// ## Design overview +/// +/// ![Block diagram](module.axi_demux.png "Block diagram") +/// +/// Beats on the W channel are routed by demultiplexer according to the selection for the +/// corresponding AW beat. This relies on the AXI property that W bursts must be sent in the same +/// order as AW beats and beats from different W bursts may not be interleaved. +/// +/// Beats on the B and R channel are multiplexed from the master ports to the slave port with +/// a round-robin arbitration tree. module axi_demux #( parameter int unsigned AxiIdWidth = 32'd0, parameter bit AtopSupport = 1'b1, From 6b4ebcafd8883761a03dd976f95292e51ad9bfd1 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Fri, 26 Aug 2022 17:36:16 +0200 Subject: [PATCH 013/145] Move images for doc building --- .github/workflows/doc.yml | 2 ++ {docs => doc/svg}/axi_id_remap_table.svg | 0 2 files changed, 2 insertions(+) rename {docs => doc/svg}/axi_id_remap_table.svg (100%) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 0fe61952e..d598aeb3b 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -53,6 +53,8 @@ jobs: - name: Build documentation run: | mkdir -p docs + cp doc/axi_demux.png docs/module.axi_demux.png + cp doc/svg/axi_id_remap_table.svg docs/axi_id_remap_table.svg morty -I include -I $(bender path common_cells)/include src/*.sv -d docs shell: bash diff --git a/docs/axi_id_remap_table.svg b/doc/svg/axi_id_remap_table.svg similarity index 100% rename from docs/axi_id_remap_table.svg rename to doc/svg/axi_id_remap_table.svg From 620a49346963d81a3469960940b6613b1fe20161 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Mon, 29 Aug 2022 19:30:39 +0200 Subject: [PATCH 014/145] axi_throttle: Add a module limiting the maximum number of outstanding transfers downstream (#254) * axi_throttle: Add a module that limits the maximum number of outstanding transfers sent to the downstream logic --- Bender.yml | 3 +- CHANGELOG.md | 2 + README.md | 1 + axi.core | 3 +- ips_list.yml | 2 +- src/axi_throttle.sv | 101 ++++++++++++++++++++++++++++++++++++++++++++ src_files.yml | 1 + 7 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 src/axi_throttle.sv diff --git a/Bender.yml b/Bender.yml index f512a26ac..65234eec8 100644 --- a/Bender.yml +++ b/Bender.yml @@ -8,7 +8,7 @@ package: - "Wolfgang Roenninger " dependencies: - common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.21.0 } + common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.26.0 } common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.0 } export_include_dirs: @@ -46,6 +46,7 @@ sources: - src/axi_modify_address.sv - src/axi_mux.sv - src/axi_serializer.sv + - src/axi_throttle.sv - src/axi_to_mem.sv # Level 3 - src/axi_cdc.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index c4318d979..1f80374e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `axi_to_mem`: AXI4+ATOP slave to control on chip memory. - `axi_test`: Add `mapped` mode to the random classes as well as additional functionality to the scoreboard class. +- `axi_throttle`: Add a module that limits the maximum number of outstanding transfers sent to the + downstream logic. ### Changed diff --git a/README.md b/README.md index d26bcc0e3..5679df6c2 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_mux`](src/axi_mux.sv) | Multiplexes the AXI4 slave ports down to one master port. | [Doc](doc/axi_mux.md) | | [`axi_pkg`](src/axi_pkg.sv) | Contains AXI definitions, common structs, and useful helper functions. | | | [`axi_serializer`](src/axi_serializer.sv) | Serializes transactions with different IDs to the same ID. | | +| [`axi_throttle`](src/axi_throttle.sv) | Limits the maximum number of outstanding transfers sent to the downstream logic. | | | [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | | [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | | [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. | | diff --git a/axi.core b/axi.core index c09f2e588..489bf516d 100644 --- a/axi.core +++ b/axi.core @@ -38,6 +38,7 @@ filesets: - src/axi_modify_address.sv - src/axi_mux.sv - src/axi_serializer.sv + - src/axi_throttle.sv - src/axi_to_mem.sv # Level 3 - src/axi_cdc.sv @@ -52,7 +53,7 @@ filesets: - src/axi_xbar.sv file_type : systemVerilogSource depend : - - ">=pulp-platform.org::common_cells:1.21.0" + - ">=pulp-platform.org::common_cells:1.26.0" generators: axi_intercon_gen: diff --git a/ips_list.yml b/ips_list.yml index c8afeda72..7056ebac2 100644 --- a/ips_list.yml +++ b/ips_list.yml @@ -1,5 +1,5 @@ common_cells: - commit: v1.21.0 + commit: v1.26.0 group: pulp-platform common_verification: diff --git a/src/axi_throttle.sv b/src/axi_throttle.sv new file mode 100644 index 000000000..ff3c2bcc6 --- /dev/null +++ b/src/axi_throttle.sv @@ -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 + +/// Throttles an AXI4+ATOP bus. The maximum number of outstanding transfers have to +/// be set as a compile-time parameter, whereas the number of outstanding transfers can be set +/// during runtime. This module assumes either in-order processing of the requests or +/// indistinguishability of the request/responses (all ARs and AWs have the same ID respectively). +module axi_throttle #( + /// The maximum amount of allowable outstanding write requests + parameter int unsigned MaxNumAwPending = 1, + /// The maximum amount of allowable outstanding read requests + parameter int unsigned MaxNumArPending = 1, + /// AXI4+ATOP request type + parameter type axi_req_t = logic, + /// AXI4+ATOP response type + parameter type axi_rsp_t = logic, + /// The width of the write credit counter (*DO NOT OVERWRITE*) + parameter int unsigned WCntWidth = cf_math_pkg::idx_width(MaxNumAwPending), + /// The width of the read credit counter (*DO NOT OVERWRITE*) + parameter int unsigned RCntWidth = cf_math_pkg::idx_width(MaxNumArPending), + /// The type of the write credit counter (*DO NOT OVERWRITE*) + parameter type w_credit_t = logic [WCntWidth-1:0], + /// The type of the read credit counter (*DO NOT OVERWRITE*) + parameter type r_credit_t = logic [RCntWidth-1:0] +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + + /// AXI4+ATOP request in + input axi_req_t req_i, + /// AXI4+ATOP response out + output axi_rsp_t rsp_o, + /// AXI4+ATOP request out + output axi_req_t req_o, + /// AXI4+ATOP response in + input axi_rsp_t rsp_i, + + /// Amount of write credit (number of outstanding write transfers) + input w_credit_t w_credit_i, + /// Amount of read credit (number of outstanding read transfers) + input r_credit_t r_credit_i +); + + // ax throttled valids + logic throttled_aw_valid; + logic throttled_ar_valid; + + // ax throttled readies + logic throttled_aw_ready; + logic throttled_ar_ready; + + // limit Aw requests -> wait for b + stream_throttle #( + .MaxNumPending ( MaxNumAwPending ) + ) i_stream_throttle_aw ( + .clk_i, + .rst_ni, + .req_valid_i ( req_i.aw_valid ), + .req_valid_o ( throttled_aw_valid ), + .req_ready_i ( rsp_i.aw_ready ), + .req_ready_o ( throttled_aw_ready ), + .rsp_valid_i ( rsp_i.b_valid ), + .rsp_ready_i ( req_i.b_ready ), + .credit_i ( w_credit_i ) + ); + + // limit Ar requests -> wait for r.last + stream_throttle #( + .MaxNumPending ( MaxNumArPending ) + ) i_stream_throttle_ar ( + .clk_i, + .rst_ni, + .req_valid_i ( req_i.ar_valid ), + .req_valid_o ( throttled_ar_valid ), + .req_ready_i ( rsp_i.ar_ready ), + .req_ready_o ( throttled_ar_ready ), + .rsp_valid_i ( rsp_i.r_valid & rsp_i.r.last ), + .rsp_ready_i ( req_i.r_ready ), + .credit_i ( r_credit_i ) + ); + + // connect the throttled request bus (its a through connection - except for the ax valids) + always_comb begin : gen_throttled_req_conn + req_o = req_i; + req_o.aw_valid = throttled_aw_valid; + req_o.ar_valid = throttled_ar_valid; + end + + // connect the throttled response bus (its a through connection - except for the ax readies) + always_comb begin : gen_throttled_rsp_conn + rsp_o = rsp_i; + rsp_o.aw_ready = throttled_aw_ready; + rsp_o.ar_ready = throttled_ar_ready; + end + +endmodule : axi_throttle diff --git a/src_files.yml b/src_files.yml index ed4fe70d0..2bc5a7a20 100644 --- a/src_files.yml +++ b/src_files.yml @@ -37,6 +37,7 @@ axi: - src/axi_modify_address.sv - src/axi_mux.sv - src/axi_serializer.sv + - src/axi_throttle.sv - src/axi_to_mem.sv # Level 3 - src/axi_cdc.sv From fec8d2e9881c7015bdc6ffe185b81ca9afe51ecc Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 9 Aug 2022 08:15:39 +0200 Subject: [PATCH 015/145] Add axi_to_mem_banked and test --- .ci/Memora.yml | 15 ++ .gitlab-ci.yml | 9 +- Bender.yml | 3 + CHANGELOG.md | 3 + README.md | 2 +- axi.core | 1 + scripts/compile_vsim.sh | 2 +- scripts/run_vsim.sh | 21 ++ src/axi_to_mem_banked.sv | 430 +++++++++++++++++++++++++++++++++++ src_files.yml | 1 + test/axi_synth_bench.sv | 87 +++++++ test/tb_axi_to_mem_banked.sv | 417 +++++++++++++++++++++++++++++++++ 12 files changed, 987 insertions(+), 4 deletions(-) create mode 100644 src/axi_to_mem_banked.sv create mode 100644 test/tb_axi_to_mem_banked.sv diff --git a/.ci/Memora.yml b/.ci/Memora.yml index f3c331574..72cd5ad1e 100644 --- a/.ci/Memora.yml +++ b/.ci/Memora.yml @@ -258,6 +258,21 @@ artifacts: outputs: - build/axi_to_axi_lite-%.tested + axi_to_mem_banked-%: + inputs: + - Bender.yml + - include + - scripts/run_vsim.sh + - src/axi_pkg.sv + - src/axi_intf.sv + - src/axi_test.sv + - src/axi_demux.sv + - src/axi_to_mem.sv + - src/axi_to_mem_banked.sv + - test/tb_axi_to_mem_banked.sv + outputs: + - build/axi_to_mem_banked-%.tested + axi_xbar-%: inputs: - Bender.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f00e47d15..6d450fc0f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,7 +28,7 @@ vsim: fi parallel: matrix: - - VSIM_VER: ['10.7b', '10.7e', '2020.1', '2021.1'] + - VSIM_VER: ['10.7b', '10.7e', '2021.3'] synopsys_dc: stage: build @@ -61,7 +61,7 @@ synopsys_dc: fi parallel: matrix: - - VSIM_VER: ['10.7b', '10.7e', '2020.1', '2021.1'] + - VSIM_VER: ['10.7b', '10.7e', '2021.3'] axi_addr_test: <<: *run_vsim @@ -148,6 +148,11 @@ axi_to_axi_lite: variables: TEST_MODULE: axi_to_axi_lite +axi_to_mem_banked: + <<: *run_vsim + variables: + TEST_MODULE: axi_to_mem_banked + axi_xbar: <<: *run_vsim variables: diff --git a/Bender.yml b/Bender.yml index 65234eec8..ffeaeaa46 100644 --- a/Bender.yml +++ b/Bender.yml @@ -10,6 +10,7 @@ package: dependencies: common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.26.0 } common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.0 } + tech_cells_generic: { git: "https://github.com/pulp-platform/tech_cells_generic.git", version: 0.2.2 } export_include_dirs: - include @@ -55,6 +56,7 @@ sources: - src/axi_id_serialize.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv + - src/axi_to_mem_banked.sv # Level 4 - src/axi_iw_converter.sv - src/axi_lite_xbar.sv @@ -92,4 +94,5 @@ sources: - test/tb_axi_serializer.sv - test/tb_axi_sim_mem.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 1f80374e9..693fc670e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. scoreboard class. - `axi_throttle`: Add a module that limits the maximum number of outstanding transfers sent to the downstream logic. +- `axi_to_mem_banked`: AXI4+ATOP slave to control on chip memory, with banking support, higher + throughput than `axi_to_mem`. +- `Bender`: Add dependency `tech_cells_generic` `v0.2.2` for generic SRAM macro for simulation. ### Changed diff --git a/README.md b/README.md index 5679df6c2..4acefc833 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_throttle`](src/axi_throttle.sv) | Limits the maximum number of outstanding transfers sent to the downstream logic. | | | [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | | [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | -| [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. | | +| [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. Additional banked variant. | | | [`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) | ### Simulation-Only Modules diff --git a/axi.core b/axi.core index 489bf516d..0c1ce62c4 100644 --- a/axi.core +++ b/axi.core @@ -47,6 +47,7 @@ filesets: - src/axi_id_serialize.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv + - src/axi_to_mem_banked.sv # Level 4 - src/axi_iw_converter.sv - src/axi_lite_xbar.sv diff --git a/scripts/compile_vsim.sh b/scripts/compile_vsim.sh index 9ed8b84d1..7109858f9 100755 --- a/scripts/compile_vsim.sh +++ b/scripts/compile_vsim.sh @@ -18,7 +18,7 @@ set -e [ ! -z "$VSIM" ] || VSIM=vsim -bender script vsim -t test \ +bender script vsim -t test -t rtl \ --vlog-arg="-svinputport=compat" \ --vlog-arg="-override_timescale 1ns/1ps" \ --vlog-arg="-suppress 2583" \ diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh index c72be8e8d..b83862bcd 100755 --- a/scripts/run_vsim.sh +++ b/scripts/run_vsim.sh @@ -174,6 +174,27 @@ exec_test() { done done ;; + axi_to_mem_banked) + for MEM_LAT in 1 2; do + for BANK_FACTOR in 1 2; do + for NUM_BANKS in 1 2 ; do + for AXI_DATA_WIDTH in 64 256 ; do + ACT_BANKS=$((2*$BANK_FACTOR*$NUM_BANKS)) + MEM_DATA_WIDTH=$(($AXI_DATA_WIDTH/$NUM_BANKS)) + call_vsim tb_axi_to_mem_banked \ + -voptargs="+acc +cover=bcesfx" \ + -gTbAxiDataWidth=$AXI_DATA_WIDTH \ + -gTbNumWords=2048 \ + -gTbNumBanks=$ACT_BANKS \ + -gTbMemDataWidth=$MEM_DATA_WIDTH \ + -gTbMemLatency=$MEM_LAT \ + -gTbNumWrites=2000 \ + -gTbNumReads=2000 + done + done + done + done + ;; *) call_vsim tb_$1 -t 1ns -coverage -voptargs="+acc +cover=bcesfx" ;; diff --git a/src/axi_to_mem_banked.sv b/src/axi_to_mem_banked.sv new file mode 100644 index 000000000..f60c8b363 --- /dev/null +++ b/src/axi_to_mem_banked.sv @@ -0,0 +1,430 @@ +// Copyright 2020 ETH Zurich and 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: +// - Wolfgang Rönninger + +/// AXI4+ATOP to banked SRAM memory slave. Allows for parallel read and write transactions. +/// Has higher throughput than `axi_to_mem`, however needs more hardware. +/// +/// The used address space starts at 0x0 and ends at the capacity of all memory banks combined. +/// The higher address bits are ignored for accesses. +module axi_to_mem_banked #( + /// AXI4+ATOP ID width + parameter int unsigned AxiIdWidth = 32'd0, + /// AXI4+ATOP address width + parameter int unsigned AxiAddrWidth = 32'd0, + /// AXI4+ATOP data width + parameter int unsigned AxiDataWidth = 32'd0, + /// AXI4+ATOP AW channel struct + parameter type axi_aw_chan_t = logic, + /// AXI4+ATOP W channel struct + parameter type axi_w_chan_t = logic, + /// AXI4+ATOP B channel struct + parameter type axi_b_chan_t = logic, + /// AXI4+ATOP AR channel struct + parameter type axi_ar_chan_t = logic, + /// AXI4+ATOP R channel struct + parameter type axi_r_chan_t = logic, + /// AXI4+ATOP request struct + parameter type axi_req_t = logic, + /// AXI4+ATOP response struct + parameter type axi_resp_t = logic, + /// Number of memory banks / macros + /// Has to satisfy: + /// - MemNumBanks >= 2 * AxiDataWidth / MemDataWidth + /// - MemNumBanks is a power of 2. + parameter int unsigned MemNumBanks = 32'd4, + /// Address width of an individual memory bank. This is treated as a word address. + parameter int unsigned MemAddrWidth = 32'd11, + /// Data width of the memory macros. + /// Has to satisfy: + /// - AxiDataWidth % MemDataWidth = 0 + parameter int unsigned MemDataWidth = 32'd32, + /// Read latency of the connected memory in cycles + parameter int unsigned MemLatency = 32'd1, + /// Hide write requests if the strb == '0 + parameter bit HideStrb = 1'b0, + /// DEPENDENT PARAMETER, DO NOT OVERWRITE! Address type of the memory request. + parameter type mem_addr_t = logic [MemAddrWidth-1:0], + /// DEPENDENT PARAMETER, DO NOT OVERWRITE! Atomic operation type for the memory request. + parameter type mem_atop_t = axi_pkg::atop_t, + /// DEPENDENT PARAMETER, DO NOT OVERWRITE! Data type for the memory request. + parameter type mem_data_t = logic [MemDataWidth-1:0], + /// DEPENDENT PARAMETER, DO NOT OVERWRITE! Byte strobe/enable signal for the memory request. + parameter type mem_strb_t = logic [MemDataWidth/8-1:0] +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Testmode enable + input logic test_i, + /// AXI4+ATOP slave port, request struct + input axi_req_t axi_req_i, + /// AXI4+ATOP slave port, response struct + output axi_resp_t axi_resp_o, + /// Memory bank request + output logic [MemNumBanks-1:0] mem_req_o, + /// Memory request grant + input logic [MemNumBanks-1:0] mem_gnt_i, + /// Request address + output mem_addr_t [MemNumBanks-1:0] mem_add_o, + /// Write request enable, active high + output logic [MemNumBanks-1:0] mem_we_o, + /// Write data + output mem_data_t [MemNumBanks-1:0] mem_wdata_o, + /// Write data byte enable, active high + output mem_strb_t [MemNumBanks-1:0] mem_be_o, + /// Atomic operation + output mem_atop_t [MemNumBanks-1:0] mem_atop_o, + /// Read data response + input mem_data_t [MemNumBanks-1:0] mem_rdata_i, + /// Status output, busy flag of `axi_to_mem` + output logic [1:0] axi_to_mem_busy_o +); + /// This specifies the number of banks needed to have the full data bandwidth of one + /// AXI data channel. + localparam int unsigned BanksPerAxiChannel = AxiDataWidth / MemDataWidth; + /// Offset of the byte address from AXI to determine, where the selection signal for the + /// memory bank should start. + localparam int unsigned BankSelOffset = $clog2(MemDataWidth / 32'd8); + /// Selection signal width of the xbar. This is the reason for power of two banks, otherwise + /// There are holes in the address mapping. + localparam int unsigned BankSelWidth = cf_math_pkg::idx_width(MemNumBanks); + typedef logic [BankSelWidth-1:0] xbar_sel_t; + + // Typedef for defining the channels + typedef enum logic { + ReadAccess = 1'b0, + WriteAccess = 1'b1 + } access_type_e; + typedef logic [AxiAddrWidth-1:0] axi_addr_t; + // typedef logic [TcdmDataWidth-1:0] tcdm_data_t; + + /// Payload definition which is sent over the xbar between the macros and the read/write unit. + typedef struct packed { + /// Address for the memory access + mem_addr_t addr; + /// Write enable, active high + logic we; + /// Write data + mem_data_t wdata; + /// Strobe signal, byte enable + mem_strb_t wstrb; + /// Atomic operation, from AXI + mem_atop_t atop; + } xbar_payload_t; + + /// Read data definition for the shift register, which samples the read response data + typedef struct packed { + /// Selection signal for response routing + xbar_sel_t sel; + /// Selection is valid + logic valid; + } read_sel_t; + + axi_req_t [1:0] mem_axi_reqs; + axi_resp_t [1:0] mem_axi_resps; + + // Fixed select `axi_demux` to split reads and writes to the two `axi_to_mem` + axi_demux #( + .AxiIdWidth ( AxiIdWidth ), + .AtopSupport ( 1'b1 ), + .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 ), + .NoMstPorts ( 32'd2 ), + .MaxTrans ( 32'd4 ), // allow multiple Ax vectors to not starve W channel + .AxiLookBits ( 32'd1 ), // select is fixed, do not need it + .UniqueIds ( 1'b0 ), + .FallThrough ( 1'b1 ), + .SpillAw ( 1'b1 ), + .SpillW ( 1'b1 ), + .SpillB ( 1'b1 ), + .SpillAr ( 1'b1 ), + .SpillR ( 1'b1 ) + ) i_axi_demux ( + .clk_i, + .rst_ni, + .test_i, + .slv_req_i ( axi_req_i ), + .slv_aw_select_i ( WriteAccess ), + .slv_ar_select_i ( ReadAccess ), + .slv_resp_o ( axi_resp_o ), + .mst_reqs_o ( mem_axi_reqs ), + .mst_resps_i ( mem_axi_resps ) + ); + + xbar_payload_t [1:0][BanksPerAxiChannel-1:0] inter_payload; + xbar_sel_t [1:0][BanksPerAxiChannel-1:0] inter_sel; + logic [1:0][BanksPerAxiChannel-1:0] inter_valid, inter_ready; + + // axi_to_mem protocol converter + for (genvar i = 0; i < 2; i++) begin : gen_axi_to_mem + axi_addr_t [BanksPerAxiChannel-1:0] req_addr; // This is a byte address + mem_data_t [BanksPerAxiChannel-1:0] req_wdata, res_rdata; + mem_strb_t [BanksPerAxiChannel-1:0] req_wstrb; + mem_atop_t [BanksPerAxiChannel-1:0] req_atop; + + logic [BanksPerAxiChannel-1:0] req_we, res_valid; + + // Careful, request / grant + // Only assert grant, if there is a ready + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t( axi_resp_t ), + .AddrWidth ( AxiAddrWidth ), + .DataWidth ( AxiDataWidth ), + .IdWidth ( AxiIdWidth ), + .NumBanks ( BanksPerAxiChannel ), + .BufDepth ( MemLatency ), + .HideStrb ( HideStrb ) + ) i_axi_to_mem ( + .clk_i, + .rst_ni, + .busy_o ( axi_to_mem_busy_o[i] ), + .axi_req_i ( mem_axi_reqs[i] ), + .axi_resp_o ( mem_axi_resps[i] ), + .mem_req_o ( inter_valid[i] ), + .mem_gnt_i ( inter_ready[i] & inter_valid[i] ), // convert valid/ready to req/gnt + .mem_addr_o ( req_addr ), + .mem_wdata_o ( req_wdata ), + .mem_strb_o ( req_wstrb ), + .mem_atop_o ( req_atop ), + .mem_we_o ( req_we ), + .mem_rvalid_i ( res_valid ), + .mem_rdata_i ( res_rdata ) + ); + // Pack the payload data together + for (genvar j = 0; unsigned'(j) < BanksPerAxiChannel; j++) begin : gen_response_mux + // Cut out the bank selection signal. + assign inter_sel[i][j] = req_addr[j][BankSelOffset+:BankSelWidth]; + + // Assign the xbar payload. + assign inter_payload[i][j] = xbar_payload_t'{ + // Cut out the word address for the banks. + addr: req_addr[j][(BankSelOffset+BankSelWidth)+:MemAddrWidth], + we: req_we[j], + wdata: req_wdata[j], + wstrb: req_wstrb[j], + atop: req_atop[j], + default: '0 + }; + + // Cut out the portion of the address for the bank selection, each bank is word addressed! + read_sel_t r_shift_inp, r_shift_oup; + // Pack the selection into the shift register + assign r_shift_inp = read_sel_t'{ + sel: inter_sel[i][j], // Selection for response multiplexer + valid: inter_valid[i][j] & inter_ready[i][j], // Valid when req to SRAM + default: '0 + }; + + // Select the right read response data. + // Writes should also generate a `response`. + assign res_valid[j] = r_shift_oup.valid; + assign res_rdata[j] = mem_rdata_i[r_shift_oup.sel]; + + // Connect for the response data `MemLatency` cycles after a request was made to the xbar. + shift_reg #( + .dtype ( read_sel_t ), + .Depth ( MemLatency ) + ) i_shift_reg_rdata_mux ( + .clk_i, + .rst_ni, + .d_i ( r_shift_inp ), + .d_o ( r_shift_oup ) + ); + end + end + + // Xbar to arbitrate data over the different memory banks + xbar_payload_t [MemNumBanks-1:0] mem_payload; + + stream_xbar #( + .NumInp ( 32'd2 * BanksPerAxiChannel ), + .NumOut ( MemNumBanks ), + .payload_t ( xbar_payload_t ), + .OutSpillReg ( 1'b0 ), + .ExtPrio ( 1'b0 ), + .AxiVldRdy ( 1'b1 ), + .LockIn ( 1'b1 ) + ) i_stream_xbar ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .rr_i ( '0 ), + .data_i ( inter_payload ), + .sel_i ( inter_sel ), + .valid_i ( inter_valid ), + .ready_o ( inter_ready ), + .data_o ( mem_payload ), + .idx_o ( /*not used*/ ), + .valid_o ( mem_req_o ), + .ready_i ( mem_gnt_i ) + ); + + // Memory request output assignment + for (genvar i = 0; unsigned'(i) < MemNumBanks; i++) begin : gen_mem_outp + assign mem_add_o[i] = mem_payload[i].addr; + assign mem_we_o[i] = mem_payload[i].we; + assign mem_wdata_o[i] = mem_payload[i].wdata; + assign mem_be_o[i] = mem_payload[i].wstrb; + assign mem_atop_o[i] = mem_payload[i].atop; + end + +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (AxiIdWidth >= 32'd1) else $fatal(1, "AxiIdWidth must be at least 1!"); + assert (AxiAddrWidth >= 32'd1) else $fatal(1, "AxiAddrWidth must be at least 1!"); + assert (AxiDataWidth >= 32'd1) else $fatal(1, "AxiDataWidth must be at least 1!"); + assert (MemNumBanks >= 32'd2 * AxiDataWidth / MemDataWidth) else + $fatal(1, "MemNumBanks has to be >= 2 * AxiDataWidth / MemDataWidth"); + assert (MemLatency >= 32'd1) else $fatal(1, "MemLatency has to be at least 1!"); + assert ($onehot(MemNumBanks)) else $fatal(1, "MemNumBanks has to be a power of 2."); + assert (MemAddrWidth >= 32'd1) else $fatal(1, "MemAddrWidth must be at least 1!"); + assert (MemDataWidth >= 32'd1) else $fatal(1, "MemDataWidth must be at least 1!"); + assert (AxiDataWidth % MemDataWidth == 0) else + $fatal(1, "MemDataWidth has to be a divisor of AxiDataWidth."); + end +`endif +// pragma translate_on +endmodule + +`include "axi/typedef.svh" +`include "axi/assign.svh" +/// AXI4+ATOP interface wrapper for `axi_to_mem` +module axi_to_mem_banked_intf #( + /// AXI4+ATOP ID width + parameter int unsigned AXI_ID_WIDTH = 32'd0, + /// AXI4+ATOP address width + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + /// AXI4+ATOP data width + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + /// AXI4+ATOP user width + parameter int unsigned AXI_USER_WIDTH = 32'd0, + /// Number of memory banks / macros + /// Has to satisfy: + /// - MemNumBanks >= 2 * AxiDataWidth / MemDataWidth + /// - MemNumBanks is a power of 2. + parameter int unsigned MEM_NUM_BANKS = 32'd4, + /// Address width of an individual memory bank. + parameter int unsigned MEM_ADDR_WIDTH = 32'd11, + /// Data width of the memory macros. + /// Has to satisfy: + /// - AxiDataWidth % MemDataWidth = 0 + parameter int unsigned MEM_DATA_WIDTH = 32'd32, + /// Read latency of the connected memory in cycles + parameter int unsigned MEM_LATENCY = 32'd1, + /// Hide write requests if the strb == '0 + parameter bit HIDE_STRB = 1'b0, + // DEPENDENT PARAMETERS, DO NOT OVERWRITE! + parameter type mem_addr_t = logic [MEM_ADDR_WIDTH-1:0], + parameter type mem_atop_t = logic [5:0], + parameter type mem_data_t = logic [MEM_DATA_WIDTH-1:0], + parameter type mem_strb_t = logic [MEM_DATA_WIDTH/8-1:0] +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Testmode enable + input logic test_i, + /// AXI4+ATOP slave port + AXI_BUS.Slave slv, + /// Memory bank request + output logic [MEM_NUM_BANKS-1:0] mem_req_o, + /// Memory request grant + input logic [MEM_NUM_BANKS-1:0] mem_gnt_i, + /// Request address + output mem_addr_t [MEM_NUM_BANKS-1:0] mem_add_o, + /// Write request enable, active high + output logic [MEM_NUM_BANKS-1:0] mem_we_o, + /// Write data + output mem_data_t [MEM_NUM_BANKS-1:0] mem_wdata_o, + /// Write data byte enable, active high + output mem_strb_t [MEM_NUM_BANKS-1:0] mem_be_o, + /// Atomic operation + output mem_atop_t [MEM_NUM_BANKS-1:0] mem_atop_o, + /// Read data response + input mem_data_t [MEM_NUM_BANKS-1:0] mem_rdata_i, + /// Status output, busy flag of `axi_to_mem` + output logic [1:0] axi_to_mem_busy_o +); + typedef logic [AXI_ID_WIDTH-1:0] id_t; + typedef logic [AXI_ADDR_WIDTH-1:0] addr_t; + typedef logic [AXI_DATA_WIDTH-1:0] data_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t; + typedef logic [AXI_USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) + + axi_req_t mem_axi_req; + axi_resp_t mem_axi_resp; + + `AXI_ASSIGN_TO_REQ(mem_axi_req, slv) + `AXI_ASSIGN_FROM_RESP(slv, mem_axi_resp) + + axi_to_mem_banked #( + .AxiIdWidth ( AXI_ID_WIDTH ), + .AxiAddrWidth ( AXI_ADDR_WIDTH ), + .AxiDataWidth ( AXI_DATA_WIDTH ), + .axi_aw_chan_t ( aw_chan_t ), + .axi_w_chan_t ( w_chan_t ), + .axi_b_chan_t ( b_chan_t ), + .axi_ar_chan_t ( ar_chan_t ), + .axi_r_chan_t ( r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .MemNumBanks ( MEM_NUM_BANKS ), + .MemAddrWidth ( MEM_ADDR_WIDTH ), + .MemDataWidth ( MEM_DATA_WIDTH ), + .MemLatency ( MEM_LATENCY ), + .HideStrb ( HIDE_STRB ) + ) i_axi_to_mem_banked ( + .clk_i, + .rst_ni, + .test_i, + .axi_to_mem_busy_o, + .axi_req_i ( mem_axi_req ), + .axi_resp_o ( mem_axi_resp ), + .mem_req_o, + .mem_gnt_i, + .mem_add_o, + .mem_wdata_o, + .mem_be_o, + .mem_atop_o, + .mem_we_o, + .mem_rdata_i + ); + +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (AXI_ADDR_WIDTH >= 1) else $fatal(1, "AXI address width must be at least 1!"); + assert (AXI_DATA_WIDTH >= 1) else $fatal(1, "AXI data width must be at least 1!"); + assert (AXI_ID_WIDTH >= 1) else $fatal(1, "AXI ID width must be at least 1!"); + assert (AXI_USER_WIDTH >= 1) else $fatal(1, "AXI user width must be at least 1!"); + end +`endif +// pragma translate_on +endmodule + diff --git a/src_files.yml b/src_files.yml index 2bc5a7a20..8d9d87c67 100644 --- a/src_files.yml +++ b/src_files.yml @@ -46,6 +46,7 @@ axi: - src/axi_id_serialize.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv + - src/axi_to_mem_banked.sv # Level 4 - src/axi_iw_converter.sv - src/axi_lite_xbar.sv diff --git a/test/axi_synth_bench.sv b/test/axi_synth_bench.sv index 6b95cfe0c..14563e137 100644 --- a/test/axi_synth_bench.sv +++ b/test/axi_synth_bench.sv @@ -174,6 +174,25 @@ module axi_synth_bench ( end end + // AXI4+ATOP on chip memory slave banked + for (genvar i = 0; i < 5; i++) begin : gen_axi_to_mem_banked_data + for (genvar j = 0; j < 4; j++) begin : gen_axi_to_mem_banked_bank_num + for (genvar k = 0; k < 2; k++) begin : gen_axi_to_mem_banked_bank_addr + localparam int unsigned DATA_WIDTH_AXI[5] = {32'd32, 32'd64, 32'd128, 32'd256, 32'd512}; + localparam int unsigned NUM_BANKS[4] = {32'd2, 32'd4, 32'd6, 32'd8}; + localparam int unsigned ADDR_WIDTH_BANKS[2] = {32'd5, 32'd11}; + + synth_axi_to_mem_banked #( + .AxiDataWidth ( DATA_WIDTH_AXI[i] ), + .BankNum ( NUM_BANKS[j] ), + .BankAddrWidth ( ADDR_WIDTH_BANKS[k] ) + ) i_axi_to_mem_banked (.*); + end + end + end + + + endmodule @@ -659,3 +678,71 @@ module synth_axi_iw_converter # ( .mst ( downstream ) ); endmodule + +module synth_axi_to_mem_banked #( + parameter int unsigned AxiDataWidth = 32'd0, + parameter int unsigned BankNum = 32'd0, + parameter int unsigned BankAddrWidth = 32'd0 +) ( + input logic clk_i, + input logic rst_ni +); + localparam int unsigned AxiIdWidth = 32'd10; + localparam int unsigned AxiAddrWidth = 32'd64; + localparam int unsigned AxiStrbWidth = AxiDataWidth / 32'd8; + localparam int unsigned AxiUserWidth = 32'd8; + localparam int unsigned BankDataWidth = 32'd2 * AxiDataWidth / BankNum; + localparam int unsigned BankStrbWidth = BankDataWidth / 32'd8; + localparam int unsigned BankLatency = 32'd1; + + typedef logic [BankAddrWidth-1:0] mem_addr_t; + typedef logic [BankDataWidth-1:0] mem_data_t; + typedef logic [BankStrbWidth-1:0] mem_strb_t; + + AXI_BUS #( + .AXI_ADDR_WIDTH ( AxiIdWidth ), + .AXI_DATA_WIDTH ( AxiAddrWidth ), + .AXI_ID_WIDTH ( AxiDataWidth ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) axi (); + + // Misc signals + logic test; + logic [1:0] axi_to_mem_busy; + // Signals for mem macros + logic [BankNum-1:0] mem_req; + logic [BankNum-1:0] mem_gnt; + mem_addr_t [BankNum-1:0] mem_addr; + logic [BankNum-1:0] mem_we; + mem_data_t [BankNum-1:0] mem_wdata; + mem_strb_t [BankNum-1:0] mem_be; + axi_pkg::atop_t [BankNum-1:0] mem_atop; + mem_data_t [BankNum-1:0] mem_rdata; + + + axi_to_mem_banked_intf #( + .AXI_ID_WIDTH ( AxiIdWidth ), + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( AxiDataWidth ), + .AXI_USER_WIDTH ( AxiUserWidth ), + .MEM_NUM_BANKS ( BankNum ), + .MEM_ADDR_WIDTH ( BankAddrWidth ), + .MEM_DATA_WIDTH ( BankDataWidth ), + .MEM_LATENCY ( BankLatency ) + ) i_axi_to_mem_banked_intf ( + .clk_i, + .rst_ni, + .test_i ( test ), + .slv ( axi ), + .mem_req_o ( mem_req ), + .mem_gnt_i ( mem_gnt ), + .mem_add_o ( mem_addr ), + .mem_we_o ( mem_we ), + .mem_wdata_o ( mem_wdata ), + .mem_be_o ( mem_be ), + .mem_atop_o ( mem_atop ), + .mem_rdata_i ( mem_rdata ), + .axi_to_mem_busy_o ( axi_to_mem_busy ) + ); + +endmodule diff --git a/test/tb_axi_to_mem_banked.sv b/test/tb_axi_to_mem_banked.sv new file mode 100644 index 000000000..e519f9f52 --- /dev/null +++ b/test/tb_axi_to_mem_banked.sv @@ -0,0 +1,417 @@ +// Copyright 2020 ETH Zurich and 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: +// - Wolfgang Roenninger + +`include "axi/typedef.svh" +`include "axi/assign.svh" +`include "common_cells/registers.svh" + +/// Testbench for axi_to_mem_banked. Monitors the performance for random accesses. +module tb_axi_to_mem_banked #( + /// Data Width of the AXI4+ATOP channels. + parameter int unsigned TbAxiDataWidth = 32'd256, + /// Number of words of an individual memory bank. + /// Determines the address width of the request output. + parameter int unsigned TbNumWords = 32'd8192, + /// Number of connected memory banks. + parameter int unsigned TbNumBanks = 32'd8, + /// Data width of an individual memory bank. + parameter int unsigned TbMemDataWidth = 32'd64, + /// Latancy in cycles of a memory bank. + parameter int unsigned TbMemLatency = 32'd2, + /// Number of writes performed by the testbench. + parameter int unsigned TbNumWrites = 32'd10000, + /// Number of writes performed by the testbench. + parameter int unsigned TbNumReads = 32'd10000 +); + // test bench params + localparam time CyclTime = 10ns; + localparam time ApplTime = 2ns; + localparam time TestTime = 8ns; + + // localparam and typedefs for AXI4+ATOP + localparam int unsigned AxiIdWidth = 32'd6; + localparam int unsigned AxiAddrWidth = 32'd64; + localparam int unsigned AxiStrbWidth = TbAxiDataWidth / 8; + localparam int unsigned AxiUserWidth = 32'd4; + + typedef logic [AxiAddrWidth-1:0] axi_addr_t; + + // AXI test defines + typedef axi_test::axi_rand_master #( + // AXI interface parameters + .AW ( AxiAddrWidth ), + .DW ( TbAxiDataWidth ), + .IW ( AxiIdWidth ), + .UW ( AxiUserWidth ), + // Stimuli application and test time + .TA ( ApplTime ), + .TT ( TestTime ), + // Maximum number of read and write transactions in flight + .MAX_READ_TXNS ( 20 ), + .MAX_WRITE_TXNS ( 20 ), + // Upper and lower bounds on wait cycles on Ax, W, and resp (R and B) channels + .AX_MIN_WAIT_CYCLES ( 0 ), + .AX_MAX_WAIT_CYCLES ( 0 ), + .W_MIN_WAIT_CYCLES ( 0 ), + .W_MAX_WAIT_CYCLES ( 0 ), + .RESP_MIN_WAIT_CYCLES ( 0 ), + .RESP_MAX_WAIT_CYCLES ( 0 ), + // AXI feature usage + .AXI_MAX_BURST_LEN ( 0 ), // maximum number of beats in burst; 0 = AXI max (256) + .TRAFFIC_SHAPING ( 0 ), + .AXI_EXCLS ( 1'b0 ), + .AXI_ATOPS ( 1'b0 ), + .AXI_BURST_FIXED ( 1'b0 ), + .AXI_BURST_INCR ( 1'b1 ), + .AXI_BURST_WRAP ( 1'b0 ) + ) axi_rand_master_t; + + // memory defines + localparam int unsigned MemAddrWidth = $clog2(TbNumWords); + + localparam int unsigned MemBufDepth = 1; + // addresses + localparam axi_addr_t StartAddr = axi_addr_t'(64'h0); + localparam axi_addr_t EndAddr = axi_addr_t'(StartAddr + 32'd2 * TbNumWords * TbAxiDataWidth/32'd8); + + typedef logic [MemAddrWidth-1:0] mem_addr_t; + typedef logic [5:0] mem_atop_t; + typedef logic [TbMemDataWidth-1:0] mem_data_t; + typedef logic [TbMemDataWidth/8-1:0] mem_strb_t; + + // sim signals + logic end_of_sim; + + // dut signals + logic clk, rst_n, one_dut_active; + + logic [1:0] dut_busy; + logic [TbNumBanks-1:0] mem_req; + logic [TbNumBanks-1:0] mem_gnt; + mem_addr_t [TbNumBanks-1:0] mem_addr; + mem_data_t [TbNumBanks-1:0] mem_wdata; + mem_strb_t [TbNumBanks-1:0] mem_strb; + logic [TbNumBanks-1:0] mem_we; + mem_atop_t [TbNumBanks-1:0] mem_atop; + logic [TbNumBanks-1:0] mem_rvalid; + mem_data_t [TbNumBanks-1:0] mem_rdata; + + assign one_dut_active = |dut_busy; + + AXI_BUS_DV #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( AxiIdWidth ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) mem_axi_dv (clk); + + AXI_BUS #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( AxiIdWidth ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) mem_axi (); + `AXI_ASSIGN(mem_axi, mem_axi_dv) + + // stimuli generation + initial begin : proc_axi_master + static axi_rand_master_t axi_rand_master = new ( mem_axi_dv ); + end_of_sim <= 1'b0; + axi_rand_master.add_memory_region(StartAddr, EndAddr, axi_pkg::DEVICE_NONBUFFERABLE); + axi_rand_master.reset(); + @(posedge rst_n); + @(posedge clk); + @(posedge clk); + + axi_rand_master.run(TbNumReads, TbNumWrites); + end_of_sim <= 1'b1; + end + + // memory banks + for (genvar i = 0; i < TbNumBanks; i++) begin : gen_tc_sram + tc_sram #( + .NumWords ( TbNumWords ), + .DataWidth ( TbMemDataWidth ), + .ByteWidth ( 32'd8 ), + .NumPorts ( 32'd1 ), + .Latency ( TbMemLatency ), + .SimInit ( "none" ), + .PrintSimCfg ( 1'b1 ) + ) i_tc_sram_bank ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .req_i ( mem_req[i] ), + .we_i ( mem_we[i] ), + .addr_i ( mem_addr[i] ), + .wdata_i ( mem_wdata[i] ), + .be_i ( mem_strb[i] ), + .rdata_o ( mem_rdata[i] ) + ); + // always be ready + assign mem_gnt[i] = 1'b1; + // generate mem_rvalid signal + if (TbMemLatency == 0) begin : gen_no_mem__lat + assign mem_rvalid[i] = mem_req[i]; + end else begin : gen_mem_lat + logic [TbMemLatency-1:0] mem_lat_q, mem_lat_d; + `FFARN(mem_lat_q, mem_lat_d, '0, clk, rst_n) + assign mem_lat_d[TbMemLatency-1] = mem_req[i]; + if (TbMemLatency > 1) begin + for (genvar lat_i = 0; lat_i < TbMemLatency - 1; lat_i++) begin + assign mem_lat_d[lat_i] = mem_lat_q[lat_i+1]; + end + end + assign mem_rvalid[i] = mem_lat_q[0]; + end + end + + // Clock generator + clk_rst_gen #( + .ClkPeriod ( CyclTime ), + .RstClkCycles ( 5 ) + ) i_clk_rst_gen ( + .clk_o ( clk ), + .rst_no ( rst_n ) + ); + + // Design under test + axi_to_mem_banked_intf #( + .AXI_ID_WIDTH ( AxiIdWidth ), + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_USER_WIDTH ( AxiUserWidth ), + .MEM_NUM_BANKS ( TbNumBanks ), + .MEM_ADDR_WIDTH ( MemAddrWidth ), + .MEM_DATA_WIDTH ( TbMemDataWidth ), + .MEM_LATENCY ( TbMemLatency ) + ) i_axi_to_mem_banked_dut ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .test_i ( 1'b0 ), + .axi_to_mem_busy_o ( dut_busy ), + .slv ( mem_axi ), + .mem_req_o ( mem_req ), + .mem_gnt_i ( mem_gnt ), + .mem_add_o ( mem_addr ), // byte address + .mem_wdata_o ( mem_wdata ), // write data + .mem_be_o ( mem_strb ), // byte-wise strobe + .mem_atop_o ( mem_atop ), // atomic operation + .mem_we_o ( mem_we ), // write enable + .mem_rdata_i ( mem_rdata ) // read data + ); + + // monitoring + logic aw_beat, aw_stall, w_beat, b_beat, ar_beat, ar_stall, r_beat; + assign aw_beat = mem_axi.aw_valid & mem_axi.aw_ready; + assign aw_stall = mem_axi.aw_valid & !mem_axi.aw_ready; + assign w_beat = mem_axi.w_valid & mem_axi.w_ready; + assign b_beat = mem_axi.b_valid & mem_axi.b_ready; + assign ar_beat = mem_axi.ar_valid & mem_axi.ar_ready; + assign ar_stall = mem_axi.ar_valid & !mem_axi.ar_ready; + assign r_beat = mem_axi.r_valid & mem_axi.r_ready; + + int unsigned aw_open; + int unsigned ar_open; + + initial begin : proc_monitor + automatic bit aw_new = 1; + automatic bit w_new = 1; + automatic bit b_new = 1; + automatic bit ar_new = 1; + automatic bit r_new = 1; + + + automatic longint wc_cnt = 0; + automatic longint rc_cnt = 0; + automatic longint w_cnt = 0; + automatic longint r_cnt = 0; + + automatic longint busy_cnt; + automatic longint dut_busy_cnt [TbNumBanks]; + automatic real bank_busy_percent; + automatic real axi_busy_percent; + automatic real tmp; + + aw_open = 0; + ar_open = 0; + bank_busy_percent = 0; + axi_busy_percent = 0; + for (int i = 0; i < TbNumBanks; i++) begin + dut_busy_cnt[i] = 0; + end + $display("###############################################################################"); + $display("Sim Parameter:"); + $display("###############################################################################"); + $display("TbAxiDataWidth: %0d", TbAxiDataWidth); + $display("TbMemDataWidth: %0d", TbMemDataWidth); + $display("TbNumBanks: %0d", TbNumBanks); + $display("TbMemLatency: %0d", TbMemLatency); + $display("###############################################################################"); + + @(posedge rst_n); + forever begin + @(posedge clk); + + #TestTime; + // determine the first valid of an AW transaction + if (mem_axi.aw_valid) begin + if (aw_new) begin + aw_open++; + if (!mem_axi.aw_ready) begin + aw_new = 0; + end + end else begin + if (mem_axi.aw_ready) begin + aw_new = 1; + end + end + end + + // determine the first valid of an AR transaction + if (mem_axi.ar_valid) begin + if (ar_new) begin + ar_open++; + if (!mem_axi.ar_ready) begin + ar_new = 0; + end + end else begin + if (mem_axi.ar_ready) begin + ar_new = 1; + end + end + end + + if (b_beat) begin + aw_open--; + end + if (r_beat && mem_axi.r_last) begin + ar_open--; + end + + if (aw_open > 0) begin + wc_cnt++; + end + if (ar_open > 0) begin + rc_cnt++; + end + + if (w_beat) begin + w_cnt++; + end + if (r_beat) begin + r_cnt++; + end + + if ((aw_open > 0) || (ar_open > 0)) begin + busy_cnt++; + end + + for (int unsigned i = 0; i < TbNumBanks; i++) begin + if (mem_req[i]) begin + dut_busy_cnt[i]++; + end + end + + + if (end_of_sim) begin + @(posedge clk); + $display("###############################################################################"); + $display("Statistics:"); + $display("###############################################################################"); + $display("Writes:"); + $display("Cycles Open write tnx: %0d", wc_cnt); + $display("Write beat count: %0d", w_cnt); + $display("Write utilization: %0f", real'(w_cnt) / real'(wc_cnt) * 100); + axi_busy_percent += real'(w_cnt) / real'(wc_cnt) * 100; + $display("###############################################################################"); + $display("Reads:"); + $display("Cycles Open read tnx: %0d", rc_cnt); + $display("Read beat count: %0d", r_cnt); + $display("Read utilization: %0f", real'(r_cnt) / real'(rc_cnt) * 100); + axi_busy_percent += real'(r_cnt) / real'(rc_cnt) * 100; + $display("###############################################################################"); + for (int unsigned i = 0; i < TbNumBanks; i++) begin + bank_busy_percent += real'(dut_busy_cnt[i]) / real'(busy_cnt) * 100; + $display("Bank %0d utilization: %0f", i, real'(dut_busy_cnt[i]) / real'(busy_cnt) * 100); + tmp = dut_busy_cnt[i]; + $display("Bank %0d requests: %0f", i, tmp); + tmp = busy_cnt; + $display("Bank %0d busy cycles: %0f", i, tmp); + end + $display("Sum bank utilization: %0f", bank_busy_percent); + $display("Sum axi utilization: %0f", axi_busy_percent); + $display("###############################################################################"); + $stop(); + end + end + end + + initial begin : proc_sim_progress + longint unsigned ActAwTnx; + longint unsigned ActArTnx; + automatic int unsigned PrintInterv = 100; + + ActAwTnx = 0; + ActArTnx = 0; + $display("Start Addr: %0h", StartAddr); + $display("End addr: %0h", EndAddr); + + @(posedge rst_n); + forever begin + @(posedge clk); + #TestTime; + + if (aw_beat) begin + if (ActAwTnx % PrintInterv == 0) begin + $display("%t > AW Transaction %d of %d ", $time(), ActAwTnx, TbNumWrites); + end + ActAwTnx++; + end + if (ar_beat) begin + if (ActArTnx % PrintInterv == 0) begin + $display("%t > AR Transaction %d of %d ", $time(), ActArTnx, TbNumReads); + end + ActArTnx++; + end + + if (end_of_sim) begin + break; + end + end + end + + AXI_BUS_DV #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( AxiIdWidth ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) monitor_dv (clk); + + `AXI_ASSIGN_MONITOR(monitor_dv, mem_axi) + + typedef axi_test::axi_scoreboard #( + .IW ( AxiIdWidth ), + .AW ( AxiAddrWidth ), + .DW ( TbAxiDataWidth ), + .UW ( AxiUserWidth ), + .TT ( TestTime ) + ) axi_scoreboard_t; + axi_scoreboard_t axi_scoreboard = new(monitor_dv); + initial begin : proc_scoreboard + axi_scoreboard.enable_all_checks(); + @(posedge rst_n); + axi_scoreboard.monitor(); + wait (end_of_sim); + end + +endmodule From ea30f38f888fab3801325523260036bc958c82fc Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Mon, 29 Aug 2022 18:48:51 +0200 Subject: [PATCH 016/145] axi_to_mem_banked: Fixes from PR --- src/axi_to_mem_banked.sv | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/axi_to_mem_banked.sv b/src/axi_to_mem_banked.sv index f60c8b363..a57962a9d 100644 --- a/src/axi_to_mem_banked.sv +++ b/src/axi_to_mem_banked.sv @@ -107,7 +107,6 @@ module axi_to_mem_banked #( WriteAccess = 1'b1 } access_type_e; typedef logic [AxiAddrWidth-1:0] axi_addr_t; - // typedef logic [TcdmDataWidth-1:0] tcdm_data_t; /// Payload definition which is sent over the xbar between the macros and the read/write unit. typedef struct packed { @@ -146,7 +145,7 @@ module axi_to_mem_banked #( .axi_req_t ( axi_req_t ), .axi_resp_t ( axi_resp_t ), .NoMstPorts ( 32'd2 ), - .MaxTrans ( 32'd4 ), // allow multiple Ax vectors to not starve W channel + .MaxTrans ( MemLatency+2 ), // allow multiple Ax vectors to not starve W channel .AxiLookBits ( 32'd1 ), // select is fixed, do not need it .UniqueIds ( 1'b0 ), .FallThrough ( 1'b1 ), From e5c5a1ee29dfc36252d03e45c4ee7032198dfc92 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 9 Aug 2022 08:25:43 +0200 Subject: [PATCH 017/145] Add interleaved axi_to_mem --- Bender.yml | 1 + CHANGELOG.md | 2 + README.md | 2 +- axi.core | 1 + src/axi_to_mem_interleaved.sv | 242 ++++++++++++++++++++++++++++++++++ src_files.yml | 1 + 6 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 src/axi_to_mem_interleaved.sv diff --git a/Bender.yml b/Bender.yml index ffeaeaa46..656a2fca5 100644 --- a/Bender.yml +++ b/Bender.yml @@ -57,6 +57,7 @@ sources: - src/axi_multicut.sv - src/axi_to_axi_lite.sv - src/axi_to_mem_banked.sv + - src/axi_to_mem_interleaved.sv # Level 4 - src/axi_iw_converter.sv - src/axi_lite_xbar.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index 693fc670e..dcb59d1db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. downstream logic. - `axi_to_mem_banked`: AXI4+ATOP slave to control on chip memory, with banking support, higher throughput than `axi_to_mem`. +- `axi_to_mem_interleaved`: AXI4+ATOP slave to control on chip memory, interleaved to prevent + deadlocks. - `Bender`: Add dependency `tech_cells_generic` `v0.2.2` for generic SRAM macro for simulation. ### Changed diff --git a/README.md b/README.md index 4acefc833..784809d0c 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_throttle`](src/axi_throttle.sv) | Limits the maximum number of outstanding transfers sent to the downstream logic. | | | [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | | [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | -| [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. Additional banked variant. | | +| [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. Additional banked & interleaved variant. | | | [`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) | ### Simulation-Only Modules diff --git a/axi.core b/axi.core index 0c1ce62c4..ae8a9009f 100644 --- a/axi.core +++ b/axi.core @@ -48,6 +48,7 @@ filesets: - src/axi_multicut.sv - src/axi_to_axi_lite.sv - src/axi_to_mem_banked.sv + - src/axi_to_mem_interleaved.sv # Level 4 - src/axi_iw_converter.sv - src/axi_lite_xbar.sv diff --git a/src/axi_to_mem_interleaved.sv b/src/axi_to_mem_interleaved.sv new file mode 100644 index 000000000..e33bcc764 --- /dev/null +++ b/src/axi_to_mem_interleaved.sv @@ -0,0 +1,242 @@ +// Copyright 2018 ETH Zurich and 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. + +// Author: +// Thomas Benz + +/// AXI4+ATOP to SRAM memory slave. Allows for parallel read and write transactions. +/// Allows reads to bypass writes, in contrast to `axi_to_mem`, however needs more hardware. +module axi_to_mem_interleaved #( + /// AXI4+ATOP request type. See `include/axi/typedef.svh`. + parameter type axi_req_t = logic, + /// AXI4+ATOP response type. See `include/axi/typedef.svh`. + parameter type axi_resp_t = logic, + /// Address width, has to be less or equal than the width off the AXI address field. + /// Determines the width of `mem_addr_o`. Has to be wide enough to emit the memory region + /// which should be accessible. + parameter int unsigned AddrWidth = 0, + /// AXI4+ATOP data width. + parameter int unsigned DataWidth = 0, + /// AXI4+ATOP ID width. + parameter int unsigned IdWidth = 0, + /// Number of banks at output, must evenly divide `DataWidth`. + parameter int unsigned NumBanks = 0, + /// Depth of memory response buffer. This should be equal to the memory response latency. + parameter int unsigned BufDepth = 1, + /// Hide write requests if the strb == '0 + parameter bit HideStrb = 1'b0, + /// Dependent parameter, do not override. Memory address type. + parameter type addr_t = logic [AddrWidth-1:0], + /// Dependent parameter, do not override. Memory data type. + parameter type mem_data_t = logic [DataWidth/NumBanks-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + parameter type mem_strb_t = logic [DataWidth/NumBanks/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// The unit is busy handling an AXI4+ATOP request. + output logic busy_o, + /// AXI4+ATOP slave port, request input. + input axi_req_t axi_req_i, + /// AXI4+ATOP slave port, response output. + output axi_resp_t axi_resp_o, + /// Memory stream master, request is valid for this bank. + output logic [NumBanks-1:0] mem_req_o, + /// Memory stream master, request can be granted by this bank. + input logic [NumBanks-1:0] mem_gnt_i, + /// Memory stream master, byte address of the request. + output addr_t [NumBanks-1:0] mem_addr_o, + /// Memory stream master, write data for this bank. Valid when `mem_req_o`. + output mem_data_t [NumBanks-1:0] mem_wdata_o, + /// Memory stream master, byte-wise strobe (byte enable). + output mem_strb_t [NumBanks-1:0] mem_strb_o, + /// Memory stream master, `axi_pkg::atop_t` signal associated with this request. + output axi_pkg::atop_t [NumBanks-1:0] mem_atop_o, + /// Memory stream master, write enable. Then asserted store of `mem_w_data` is requested. + output logic [NumBanks-1:0] mem_we_o, + /// Memory stream master, response is valid. This module expects always a response valid for a + /// request regardless if the request was a write or a read. + input logic [NumBanks-1:0] mem_rvalid_i, + /// Memory stream master, read response data. + input mem_data_t [NumBanks-1:0] mem_rdata_i +); + + // internal signals + logic w_busy, r_busy; + logic [NumBanks-1:0] arb_outcome, arb_outcome_head; + + // internal AXI buses + axi_req_t r_axi_req, w_axi_req; + axi_resp_t r_axi_resp, w_axi_resp; + + // internal TCDM buses + logic [NumBanks-1:0] r_mem_req, w_mem_req; + logic [NumBanks-1:0] r_mem_gnt, w_mem_gnt; + addr_t [NumBanks-1:0] r_mem_addr, w_mem_addr; + mem_data_t [NumBanks-1:0] r_mem_wdata, w_mem_wdata; + mem_strb_t [NumBanks-1:0] r_mem_strb, w_mem_strb; + axi_pkg::atop_t [NumBanks-1:0] r_mem_atop, w_mem_atop; + logic [NumBanks-1:0] r_mem_we, w_mem_we; + logic [NumBanks-1:0] r_mem_rvalid, w_mem_rvalid; + mem_data_t [NumBanks-1:0] r_mem_rdata, w_mem_rdata; + + // split AXI bus in read and write + always_comb begin : proc_axi_rw_split + axi_resp_o.r = r_axi_resp.r; + axi_resp_o.r_valid = r_axi_resp.r_valid; + axi_resp_o.ar_ready = r_axi_resp.ar_ready; + axi_resp_o.b = w_axi_resp.b; + axi_resp_o.b_valid = w_axi_resp.b_valid; + axi_resp_o.w_ready = w_axi_resp.w_ready; + axi_resp_o.aw_ready = w_axi_resp.aw_ready; + + w_axi_req = '0; + w_axi_req.aw = axi_req_i.aw; + w_axi_req.aw_valid = axi_req_i.aw_valid; + w_axi_req.w = axi_req_i.w; + w_axi_req.w_valid = axi_req_i.w_valid; + w_axi_req.b_ready = axi_req_i.b_ready; + + r_axi_req = '0; + r_axi_req.ar = axi_req_i.ar; + r_axi_req.ar_valid = axi_req_i.ar_valid; + r_axi_req.r_ready = axi_req_i.r_ready; + end + + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumBanks ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ) + ) i_axi_to_mem_write ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .busy_o ( w_busy ), + .axi_req_i ( w_axi_req ), + .axi_resp_o ( w_axi_resp ), + .mem_req_o ( w_mem_req ), + .mem_gnt_i ( w_mem_gnt ), + .mem_addr_o ( w_mem_addr ), + .mem_wdata_o ( w_mem_wdata ), + .mem_strb_o ( w_mem_strb ), + .mem_atop_o ( w_mem_atop ), + .mem_we_o ( w_mem_we ), + .mem_rvalid_i ( w_mem_rvalid ), + .mem_rdata_i ( w_mem_rdata ) + ); + + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumBanks ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ) + ) i_axi_to_mem_read ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .busy_o ( r_busy ), + .axi_req_i ( r_axi_req ), + .axi_resp_o ( r_axi_resp ), + .mem_req_o ( r_mem_req ), + .mem_gnt_i ( r_mem_gnt ), + .mem_addr_o ( r_mem_addr ), + .mem_wdata_o ( r_mem_wdata ), + .mem_strb_o ( r_mem_strb ), + .mem_atop_o ( r_mem_atop ), + .mem_we_o ( r_mem_we ), + .mem_rvalid_i ( r_mem_rvalid ), + .mem_rdata_i ( r_mem_rdata ) + ); + + // create a struct for the rr-arb-tree + typedef struct packed { + addr_t addr; + mem_data_t wdata; + mem_strb_t strb; + logic we; + axi_pkg::atop_t atop; + } mem_req_payload_t; + + mem_req_payload_t [NumBanks-1:0] r_payload, w_payload, payload; + + for (genvar i = 0; i < NumBanks; i++) begin + // pack the mem + assign r_payload[i].addr = r_mem_addr[i]; + assign r_payload[i].wdata = r_mem_wdata[i]; + assign r_payload[i].strb = r_mem_strb[i]; + assign r_payload[i].we = r_mem_we[i]; + assign r_payload[i].atop = r_mem_atop[i]; + + assign w_payload[i].addr = w_mem_addr[i]; + assign w_payload[i].wdata = w_mem_wdata[i]; + assign w_payload[i].strb = w_mem_strb[i]; + assign w_payload[i].we = w_mem_we[i]; + assign w_payload[i].atop = w_mem_atop[i]; + + assign mem_addr_o [i] = payload[i].addr; + assign mem_wdata_o[i] = payload[i].wdata; + assign mem_strb_o [i] = payload[i].strb; + assign mem_we_o [i] = payload[i].we; + assign mem_atop_o [i] = payload[i].atop; + + // route data back to both channels + assign w_mem_rdata[i] = mem_rdata_i[i]; + assign r_mem_rdata[i] = mem_rdata_i[i]; + + assign w_mem_rvalid[i] = mem_rvalid_i[i] & !arb_outcome_head[i]; + assign r_mem_rvalid[i] = mem_rvalid_i[i] & arb_outcome_head[i]; + + // fine-grain arbitration + rr_arb_tree #( + .NumIn ( 2 ), + .DataType ( mem_req_payload_t ) + ) i_rr_arb_tree ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i ( 1'b0 ), + .rr_i ( '0 ), + .req_i ( { r_mem_req[i], w_mem_req[i] } ), + .gnt_o ( { r_mem_gnt[i], w_mem_gnt[i] } ), + .data_i ( { r_payload[i], w_payload[i] } ), + .req_o ( mem_req_o[i] ), + .gnt_i ( mem_gnt_i[i] ), + .data_o ( payload[i] ), + .idx_o ( arb_outcome[i] ) + ); + + // back-routing store + fifo_v3 #( + .DATA_WIDTH ( 1 ), + .DEPTH ( BufDepth + 1 ) + ) i_fifo_v3_response_trgt_store ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( ), + .empty_o ( ), + .usage_o ( ), + .data_i ( arb_outcome[i] ), + .push_i ( mem_req_o[i] & mem_gnt_i[i] ), + .data_o ( arb_outcome_head[i] ), + .pop_i ( mem_rvalid_i[i] ) + ); + end + +endmodule : axi_to_mem_interleaved diff --git a/src_files.yml b/src_files.yml index 8d9d87c67..1c1690799 100644 --- a/src_files.yml +++ b/src_files.yml @@ -47,6 +47,7 @@ axi: - src/axi_multicut.sv - src/axi_to_axi_lite.sv - src/axi_to_mem_banked.sv + - src/axi_to_mem_interleaved.sv # Level 4 - src/axi_iw_converter.sv - src/axi_lite_xbar.sv From 996474838483a8c25ebd5b62ab6e0a7b2579ed63 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 9 Aug 2022 08:28:58 +0200 Subject: [PATCH 018/145] Add split variant of axi_to_mem for memory interconnects --- Bender.yml | 1 + CHANGELOG.md | 1 + README.md | 74 ++++++------ axi.core | 1 + src/axi_to_mem_split.sv | 251 ++++++++++++++++++++++++++++++++++++++++ src_files.yml | 1 + 6 files changed, 292 insertions(+), 37 deletions(-) create mode 100644 src/axi_to_mem_split.sv diff --git a/Bender.yml b/Bender.yml index 656a2fca5..ccc855ee3 100644 --- a/Bender.yml +++ b/Bender.yml @@ -58,6 +58,7 @@ sources: - src/axi_to_axi_lite.sv - src/axi_to_mem_banked.sv - src/axi_to_mem_interleaved.sv + - src/axi_to_mem_split.sv # Level 4 - src/axi_iw_converter.sv - src/axi_lite_xbar.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index dcb59d1db..5e46abaf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. throughput than `axi_to_mem`. - `axi_to_mem_interleaved`: AXI4+ATOP slave to control on chip memory, interleaved to prevent deadlocks. +- `axi_to_mem_split`: AXI4+ATOP slave to control memory protocol interconnect. - `Bender`: Add dependency `tech_cells_generic` `v0.2.2` for generic SRAM macro for simulation. ### Changed diff --git a/README.md b/README.md index 784809d0c..47a5458bc 100644 --- a/README.md +++ b/README.md @@ -19,43 +19,43 @@ The **design and microarchitecture** of the modules in this repository is descri In addition to the documents linked in the following table, we are setting up [documentation auto-generated from inline docstrings](https://pulp-platform.github.io/axi/master). (Replace `master` in that URL with a tag to get the documentation for a specific version.) -| Name | Description | Doc | -|------------------------------------------------------|---------------------------------------------------------------------------------------------------|--------------------------------| -| [`axi_atop_filter`](src/axi_atop_filter.sv) | Filters atomic operations (ATOPs), i.e., write transactions that have a non-zero `aw_atop` value. | | -| [`axi_burst_splitter`](src/axi_burst_splitter.sv) | Split AXI4 burst transfers into single-beat transactions. | | -| [`axi_cdc`](src/axi_cdc.sv) | AXI clock domain crossing based on a Gray FIFO implementation. | | -| [`axi_cut`](src/axi_cut.sv) | Breaks all combinatorial paths between its input and output. | | -| [`axi_delayer`](src/axi_delayer.sv) | Synthesizable module which can (randomly) delays AXI channels. | | -| [`axi_demux`](src/axi_demux.sv) | Demultiplexes an AXI bus from one slave port to multiple master ports. | [Doc](doc/axi_demux.md) | -| [`axi_dw_converter`](src/axi_dw_converter.sv) | A data width converter between AXI interfaces of any data width. | | -| [`axi_dw_downsizer`](src/axi_dw_downsizer.sv) | A data width converter between a wide AXI master and a narrower AXI slave. | | -| [`axi_dw_upsizer`](src/axi_dw_upsizer.sv) | A data width converter between a narrow AXI master and a wider AXI slave. | | -| [`axi_err_slv`](src/axi_err_slv.sv) | Always responds with an AXI decode/slave error for transactions which are sent to it. | | -| [`axi_id_prepend`](src/axi_id_prepend.sv) | This module prepends/strips the MSB from the AXI IDs. | | -| [`axi_id_remap`](src/axi_id_remap.sv) | Remap AXI IDs from wide IDs at the slave port to narrower IDs at the master port. | [Doc][doc.axi_id_remap] | -| [`axi_id_serialize`](src/axi_id_serialize.sv) | Reduce AXI IDs by serializing transactions when necessary. | [Doc][doc.axi_id_serialize] | -| [`axi_intf`](src/axi_intf.sv) | This file defines the interfaces we support. | | -| [`axi_isolate`](src/axi_isolate.sv) | A module that can isolate downstream slaves from receiving new AXI4 transactions. | | -| [`axi_iw_converter`](src/axi_iw_converter.sv) | Convert between any two AXI ID widths. | [Doc][doc.axi_iw_converter] | -| [`axi_join`](src/axi_join.sv) | A connector that joins two AXI interfaces. | | -| [`axi_lite_demux`](src/axi_lite_demux.sv) | Demultiplexes an AXI4-Lite bus from one slave port to multiple master ports. | [Doc](doc/axi_lite_demux.md) | -| [`axi_lite_join`](src/axi_lite_join.sv) | A connector that joins two AXI-Lite interfaces. | | -| [`axi_lite_mailbox`](src/axi_lite_mailbox.sv) | A AXI4-Lite Mailbox with two slave ports and usage triggered irq. | [Doc](doc/axi_lite_mailbox.md) | -| [`axi_lite_mux`](src/axi_lite_mux.sv) | Multiplexes AXI4-Lite slave ports down to one master port. | [Doc](doc/axi_lite_mux.md) | -| [`axi_lite_regs`](src/axi_lite_regs.sv) | AXI4-Lite registers with optional read-only and protection features. | [Doc][doc.axi_lite_regs] | -| [`axi_lite_to_apb`](src/axi_lite_to_apb.sv) | AXI4-Lite to APB4 protocol converter. | | -| [`axi_lite_to_axi`](src/axi_lite_to_axi.sv) | AXI4-Lite to AXI4 protocol converter. | | -| [`axi_lite_xbar`](src/axi_lite_xbar.sv) | Fully-connected AXI4-Lite crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_lite_xbar.md) | -| [`axi_modify_address`](src/axi_modify_address.sv) | A connector that allows addresses of AXI requests to be changed. | | -| [`axi_multicut`](src/axi_multicut.sv) | AXI register which can be used to relax timing pressure on long AXI buses. | | -| [`axi_mux`](src/axi_mux.sv) | Multiplexes the AXI4 slave ports down to one master port. | [Doc](doc/axi_mux.md) | -| [`axi_pkg`](src/axi_pkg.sv) | Contains AXI definitions, common structs, and useful helper functions. | | -| [`axi_serializer`](src/axi_serializer.sv) | Serializes transactions with different IDs to the same ID. | | -| [`axi_throttle`](src/axi_throttle.sv) | Limits the maximum number of outstanding transfers sent to the downstream logic. | | -| [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | -| [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | -| [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. Additional banked & interleaved variant. | | -| [`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) | +| Name | Description | Doc | +|------------------------------------------------------|------------------------------------------------------------------------------------------------------|--------------------------------| +| [`axi_atop_filter`](src/axi_atop_filter.sv) | Filters atomic operations (ATOPs), i.e., write transactions that have a non-zero `aw_atop` value. | | +| [`axi_burst_splitter`](src/axi_burst_splitter.sv) | Split AXI4 burst transfers into single-beat transactions. | | +| [`axi_cdc`](src/axi_cdc.sv) | AXI clock domain crossing based on a Gray FIFO implementation. | | +| [`axi_cut`](src/axi_cut.sv) | Breaks all combinatorial paths between its input and output. | | +| [`axi_delayer`](src/axi_delayer.sv) | Synthesizable module which can (randomly) delays AXI channels. | | +| [`axi_demux`](src/axi_demux.sv) | Demultiplexes an AXI bus from one slave port to multiple master ports. | [Doc](doc/axi_demux.md) | +| [`axi_dw_converter`](src/axi_dw_converter.sv) | A data width converter between AXI interfaces of any data width. | | +| [`axi_dw_downsizer`](src/axi_dw_downsizer.sv) | A data width converter between a wide AXI master and a narrower AXI slave. | | +| [`axi_dw_upsizer`](src/axi_dw_upsizer.sv) | A data width converter between a narrow AXI master and a wider AXI slave. | | +| [`axi_err_slv`](src/axi_err_slv.sv) | Always responds with an AXI decode/slave error for transactions which are sent to it. | | +| [`axi_id_prepend`](src/axi_id_prepend.sv) | This module prepends/strips the MSB from the AXI IDs. | | +| [`axi_id_remap`](src/axi_id_remap.sv) | Remap AXI IDs from wide IDs at the slave port to narrower IDs at the master port. | [Doc][doc.axi_id_remap] | +| [`axi_id_serialize`](src/axi_id_serialize.sv) | Reduce AXI IDs by serializing transactions when necessary. | [Doc][doc.axi_id_serialize] | +| [`axi_intf`](src/axi_intf.sv) | This file defines the interfaces we support. | | +| [`axi_isolate`](src/axi_isolate.sv) | A module that can isolate downstream slaves from receiving new AXI4 transactions. | | +| [`axi_iw_converter`](src/axi_iw_converter.sv) | Convert between any two AXI ID widths. | [Doc][doc.axi_iw_converter] | +| [`axi_join`](src/axi_join.sv) | A connector that joins two AXI interfaces. | | +| [`axi_lite_demux`](src/axi_lite_demux.sv) | Demultiplexes an AXI4-Lite bus from one slave port to multiple master ports. | [Doc](doc/axi_lite_demux.md) | +| [`axi_lite_join`](src/axi_lite_join.sv) | A connector that joins two AXI-Lite interfaces. | | +| [`axi_lite_mailbox`](src/axi_lite_mailbox.sv) | A AXI4-Lite Mailbox with two slave ports and usage triggered irq. | [Doc](doc/axi_lite_mailbox.md) | +| [`axi_lite_mux`](src/axi_lite_mux.sv) | Multiplexes AXI4-Lite slave ports down to one master port. | [Doc](doc/axi_lite_mux.md) | +| [`axi_lite_regs`](src/axi_lite_regs.sv) | AXI4-Lite registers with optional read-only and protection features. | [Doc][doc.axi_lite_regs] | +| [`axi_lite_to_apb`](src/axi_lite_to_apb.sv) | AXI4-Lite to APB4 protocol converter. | | +| [`axi_lite_to_axi`](src/axi_lite_to_axi.sv) | AXI4-Lite to AXI4 protocol converter. | | +| [`axi_lite_xbar`](src/axi_lite_xbar.sv) | Fully-connected AXI4-Lite crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_lite_xbar.md) | +| [`axi_modify_address`](src/axi_modify_address.sv) | A connector that allows addresses of AXI requests to be changed. | | +| [`axi_multicut`](src/axi_multicut.sv) | AXI register which can be used to relax timing pressure on long AXI buses. | | +| [`axi_mux`](src/axi_mux.sv) | Multiplexes the AXI4 slave ports down to one master port. | [Doc](doc/axi_mux.md) | +| [`axi_pkg`](src/axi_pkg.sv) | Contains AXI definitions, common structs, and useful helper functions. | | +| [`axi_serializer`](src/axi_serializer.sv) | Serializes transactions with different IDs to the same ID. | | +| [`axi_throttle`](src/axi_throttle.sv) | Limits the maximum number of outstanding transfers sent to the downstream logic. | | +| [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | +| [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | +| [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. Additional banked, interleaved, split variant. | | +| [`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) | ### Simulation-Only Modules diff --git a/axi.core b/axi.core index ae8a9009f..9df067ba0 100644 --- a/axi.core +++ b/axi.core @@ -49,6 +49,7 @@ filesets: - src/axi_to_axi_lite.sv - src/axi_to_mem_banked.sv - src/axi_to_mem_interleaved.sv + - src/axi_to_mem_split.sv # Level 4 - src/axi_iw_converter.sv - src/axi_lite_xbar.sv diff --git a/src/axi_to_mem_split.sv b/src/axi_to_mem_split.sv new file mode 100644 index 000000000..47827f537 --- /dev/null +++ b/src/axi_to_mem_split.sv @@ -0,0 +1,251 @@ +// Copyright 2022 ETH Zurich and 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. + +// Author: +// Michael Rogenmoser + +`include "axi/assign.svh" +/// AXI4+ATOP to memory-protocol interconnect. Completely separates the read and write channel to +/// individual mem ports. This can only be used when addresses for the same bank are accessible +/// from different memory ports. +module axi_to_mem_split #( + /// AXI4+ATOP request type. See `include/axi/typedef.svh`. + parameter type axi_req_t = logic, + /// AXI4+ATOP response type. See `include/axi/typedef.svh`. + parameter type axi_resp_t = logic, + /// Address width, has to be less or equal than the width off the AXI address field. + /// Determines the width of `mem_addr_o`. Has to be wide enough to emit the memory region + /// which should be accessible. + parameter int unsigned AddrWidth = 0, + /// AXI4+ATOP data width. + parameter int unsigned AxiDataWidth = 0, + /// AXI4+ATOP ID width. + parameter int unsigned IdWidth = 0, + /// Memory data width, must evenly divide `DataWidth`. + parameter int unsigned MemDataWidth = 0, // must divide `AxiDataWidth` without remainder + /// Depth of memory response buffer. This should be equal to the memory response latency. + parameter int unsigned BufDepth = 0, + /// Hide write requests if the strb == '0 + parameter bit HideStrb = 1'b0, + /// Dependent parameters, do not override. Number of memory ports. + parameter int unsigned NumMemPorts = 2*AxiDataWidth/MemDataWidth, + /// Dependent parameter, do not override. Memory address type. + parameter type addr_t = logic [AddrWidth-1:0], + /// Dependent parameter, do not override. Memory data type. + parameter type mem_data_t = logic [MemDataWidth-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + parameter type mem_strb_t = logic [MemDataWidth/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// The unit is busy handling an AXI4+ATOP request. + output logic busy_o, + /// AXI4+ATOP slave port, request input. + input axi_req_t axi_req_i, + /// AXI4+ATOP slave port, response output. + output axi_resp_t axi_resp_o, + /// Memory stream master, request is valid for this bank. + output logic [NumMemPorts-1:0] mem_req_o, + /// Memory stream master, request can be granted by this bank. + input logic [NumMemPorts-1:0] mem_gnt_i, + /// Memory stream master, byte address of the request. + output addr_t [NumMemPorts-1:0] mem_addr_o, // byte address + /// Memory stream master, write data for this bank. Valid when `mem_req_o`. + output mem_data_t [NumMemPorts-1:0] mem_wdata_o, // write data + /// Memory stream master, byte-wise strobe (byte enable). + output mem_strb_t [NumMemPorts-1:0] mem_strb_o, // byte-wise strobe + /// Memory stream master, `axi_pkg::atop_t` signal associated with this request. + output axi_pkg::atop_t [NumMemPorts-1:0] mem_atop_o, // atomic operation + /// Memory stream master, write enable. Then asserted store of `mem_w_data` is requested. + output logic [NumMemPorts-1:0] mem_we_o, // write enable + /// Memory stream master, response is valid. This module expects always a response valid for a + /// request regardless if the request was a write or a read. + input logic [NumMemPorts-1:0] mem_rvalid_i, // response valid + /// Memory stream master, read response data. + input mem_data_t [NumMemPorts-1:0] mem_rdata_i // read data +); + + axi_req_t axi_read_req, axi_write_req; + axi_resp_t axi_read_resp, axi_write_resp; + + logic read_busy, write_busy; + + always_comb begin: proc_axi_rw_split + `AXI_SET_R_STRUCT(axi_resp_o.r, axi_read_resp.r) + axi_resp_o.r_valid = axi_read_resp.r_valid; + axi_resp_o.ar_ready = axi_read_resp.ar_ready; + `AXI_SET_B_STRUCT(axi_resp_o.b, axi_write_resp.b) + axi_resp_o.b_valid = axi_write_resp.b_valid; + axi_resp_o.aw_ready = axi_write_resp.aw_ready; + axi_resp_o.w_ready = axi_write_resp.w_ready; + + axi_write_req = '0; + `AXI_SET_AW_STRUCT(axi_write_req.aw, axi_req_i.aw) + axi_write_req.aw_valid = axi_req_i.aw_valid; + `AXI_SET_W_STRUCT(axi_write_req.w, axi_req_i.w) + axi_write_req.w_valid = axi_req_i.w_valid; + axi_write_req.b_ready = axi_req_i.b_ready; + + axi_read_req = '0; + `AXI_SET_AR_STRUCT(axi_read_req.ar, axi_req_i.ar) + axi_read_req.ar_valid = axi_req_i.ar_valid; + axi_read_req.r_ready = axi_req_i.r_ready; + end + + assign busy_o = read_busy || write_busy; + + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( AxiDataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumMemPorts/2 ), + .BufDepth ( BufDepth ), + .HideStrb ( 1'b0 ) + ) i_axi_to_mem_read ( + .clk_i, + .rst_ni, + .busy_o ( read_busy ), + .axi_req_i ( axi_read_req ), + .axi_resp_o ( axi_read_resp ), + .mem_req_o ( mem_req_o [NumMemPorts/2-1:0] ), + .mem_gnt_i ( mem_gnt_i [NumMemPorts/2-1:0] ), + .mem_addr_o ( mem_addr_o [NumMemPorts/2-1:0] ), + .mem_wdata_o ( mem_wdata_o [NumMemPorts/2-1:0] ), + .mem_strb_o ( mem_strb_o [NumMemPorts/2-1:0] ), + .mem_atop_o ( mem_atop_o [NumMemPorts/2-1:0] ), + .mem_we_o ( mem_we_o [NumMemPorts/2-1:0] ), + .mem_rvalid_i ( mem_rvalid_i [NumMemPorts/2-1:0] ), + .mem_rdata_i ( mem_rdata_i [NumMemPorts/2-1:0] ) + ); + + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( AxiDataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumMemPorts/2 ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ) + ) i_axi_to_mem_write ( + .clk_i, + .rst_ni, + .busy_o ( write_busy ), + .axi_req_i ( axi_write_req ), + .axi_resp_o ( axi_write_resp ), + .mem_req_o ( mem_req_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_gnt_i ( mem_gnt_i [NumMemPorts-1:NumMemPorts/2] ), + .mem_addr_o ( mem_addr_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_wdata_o ( mem_wdata_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_strb_o ( mem_strb_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_atop_o ( mem_atop_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_we_o ( mem_we_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_rvalid_i ( mem_rvalid_i [NumMemPorts-1:NumMemPorts/2] ), + .mem_rdata_i ( mem_rdata_i [NumMemPorts-1:NumMemPorts/2] ) + ); + +endmodule + +`include "axi/typedef.svh" +/// AXI4+ATOP interface wrapper for `axi_to_mem_split` +module axi_to_mem_split_intf #( + /// AXI4+ATOP ID width + parameter int unsigned AXI_ID_WIDTH = 32'b0, + /// AXI4+ATOP address width + parameter int unsigned AXI_ADDR_WIDTH = 32'b0, + /// AXI4+ATOP data width + parameter int unsigned AXI_DATA_WIDTH = 32'b0, + /// AXI4+ATOP user width + parameter int unsigned AXI_USER_WIDTH = 32'b0, + /// Memory data width, must evenly divide `DataWidth`. + parameter int unsigned MEM_DATA_WIDTH = 32'b0, + /// See `axi_to_mem`, parameter `BufDepth`. + parameter int unsigned BUF_DEPTH = 0, + /// Hide write requests if the strb == '0 + parameter bit HIDE_STRB = 1'b0, + /// Dependent parameters, do not override. Number of memory ports. + parameter int unsigned NUM_MEM_PORTS = 2*AXI_DATA_WIDTH/MEM_DATA_WIDTH, + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `addr_t`. + parameter type addr_t = logic [AXI_ADDR_WIDTH-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_data_t`. + parameter type mem_data_t = logic [MEM_DATA_WIDTH-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_strb_t`. + parameter type mem_strb_t = logic [MEM_DATA_WIDTH/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// See `axi_to_mem_split`, port `busy_o`. + output logic busy_o, + /// AXI4+ATOP slave interface port. + AXI_BUS.Slave axi_bus, + /// See `axi_to_mem_split`, port `mem_req_o`. + output logic [NUM_MEM_PORTS-1:0] mem_req_o, + /// See `axi_to_mem_split`, port `mem_gnt_i`. + input logic [NUM_MEM_PORTS-1:0] mem_gnt_i, + /// See `axi_to_mem_split`, port `mem_addr_o`. + output addr_t [NUM_MEM_PORTS-1:0] mem_addr_o, + /// See `axi_to_mem_split`, port `mem_wdata_o`. + output mem_data_t [NUM_MEM_PORTS-1:0] mem_wdata_o, + /// See `axi_to_mem_split`, port `mem_strb_o`. + output mem_strb_t [NUM_MEM_PORTS-1:0] mem_strb_o, + /// See `axi_to_mem_split`, port `mem_atop_o`. + output axi_pkg::atop_t [NUM_MEM_PORTS-1:0] mem_atop_o, + /// See `axi_to_mem_split`, port `mem_we_o`. + output logic [NUM_MEM_PORTS-1:0] mem_we_o, + /// See `axi_to_mem_split`, port `mem_rvalid_i`. + input logic [NUM_MEM_PORTS-1:0] mem_rvalid_i, + /// See `axi_to_mem_split`, port `mem_rdata_i`. + input mem_data_t [NUM_MEM_PORTS-1:0] mem_rdata_i +); + + typedef logic [AXI_ID_WIDTH-1:0] id_t; + typedef logic [AXI_DATA_WIDTH-1:0] data_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t; + typedef logic [AXI_USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_ALL(axi, addr_t, id_t, data_t, strb_t, user_t) + + axi_req_t axi_req; + axi_resp_t axi_resp; + `AXI_ASSIGN_TO_REQ(axi_req, axi_bus) + `AXI_ASSIGN_FROM_RESP(axi_bus, axi_resp) + + axi_to_mem_split #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AxiDataWidth ( AXI_DATA_WIDTH ), + .AddrWidth ( AXI_ADDR_WIDTH ), + .IdWidth ( AXI_ID_WIDTH ), + .MemDataWidth ( MEM_DATA_WIDTH ), // must divide `AxiDataWidth` without remainder + .BufDepth ( BUF_DEPTH ), + .HideStrb ( HIDE_STRB ) + ) i_axi_to_mem_split ( + .clk_i, + .rst_ni, + .busy_o, + .axi_req_i (axi_req), + .axi_resp_o (axi_resp), + .mem_req_o, + .mem_gnt_i, + .mem_addr_o, + .mem_wdata_o, + .mem_strb_o, + .mem_atop_o, + .mem_we_o, + .mem_rvalid_i, + .mem_rdata_i + ); + +endmodule diff --git a/src_files.yml b/src_files.yml index 1c1690799..fe4fdc67e 100644 --- a/src_files.yml +++ b/src_files.yml @@ -48,6 +48,7 @@ axi: - src/axi_to_axi_lite.sv - src/axi_to_mem_banked.sv - src/axi_to_mem_interleaved.sv + - src/axi_to_mem_split.sv # Level 4 - src/axi_iw_converter.sv - src/axi_lite_xbar.sv From 7916209fd339a66b6c9c1b07125bd3c58b09363f Mon Sep 17 00:00:00 2001 From: Noah Huetter Date: Thu, 28 Apr 2022 13:38:23 +0200 Subject: [PATCH 019/145] axi_fifo: Add module axi_fifo --- Bender.yml | 2 + axi.core | 1 + src/axi_fifo.sv | 254 ++++++++++++++++++++++++++++++++++++++++++++++++ src_files.yml | 1 + 4 files changed, 258 insertions(+) create mode 100644 src/axi_fifo.sv diff --git a/Bender.yml b/Bender.yml index ccc855ee3..ede8af01f 100644 --- a/Bender.yml +++ b/Bender.yml @@ -33,6 +33,7 @@ sources: - src/axi_demux.sv - src/axi_dw_downsizer.sv - src/axi_dw_upsizer.sv + - src/axi_fifo.sv - src/axi_id_remap.sv - src/axi_id_prepend.sv - src/axi_isolate.sv @@ -85,6 +86,7 @@ sources: - test/tb_axi_delayer.sv - test/tb_axi_dw_downsizer.sv - test/tb_axi_dw_upsizer.sv + - test/tb_axi_fifo.sv - test/tb_axi_isolate.sv - test/tb_axi_lite_mailbox.sv - test/tb_axi_lite_regs.sv diff --git a/axi.core b/axi.core index 9df067ba0..711a08e9c 100644 --- a/axi.core +++ b/axi.core @@ -24,6 +24,7 @@ filesets: - src/axi_demux.sv - src/axi_dw_downsizer.sv - src/axi_dw_upsizer.sv + - src/axi_fifo.sv - src/axi_id_remap.sv - src/axi_id_prepend.sv - src/axi_isolate.sv diff --git a/src/axi_fifo.sv b/src/axi_fifo.sv new file mode 100644 index 000000000..89d43ef6a --- /dev/null +++ b/src/axi_fifo.sv @@ -0,0 +1,254 @@ +// Copyright (c) 2014-2022 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: +// - Noah Huetter + +// AXI4 Fifo +// +// Can be used to buffer transactions + +module axi_fifo #( + parameter int unsigned Depth = 32'd1, // Number of FiFo slots. + parameter bit FallThrough = 1'b0, // fifos are in fall-through mode + // AXI channel structs + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, + // AXI request & response structs + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic test_i, + // slave port + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, + // master port + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i +); + + if (Depth == '0) begin : gen_no_fifo + // degenerate case, connect input to output + assign mst_req_o = slv_req_i; + assign slv_resp_o = mst_resp_i; + end else begin : gen_axi_fifo + logic aw_fifo_empty, ar_fifo_empty, w_fifo_empty, r_fifo_empty, b_fifo_empty; + logic aw_fifo_full, ar_fifo_full, w_fifo_full, r_fifo_full, b_fifo_full; + + assign mst_req_o.aw_valid = ~aw_fifo_empty; + assign mst_req_o.ar_valid = ~ar_fifo_empty; + assign mst_req_o.w_valid = ~w_fifo_empty; + assign slv_resp_o.r_valid = ~r_fifo_empty; + assign slv_resp_o.b_valid = ~b_fifo_empty; + + assign slv_resp_o.aw_ready = ~aw_fifo_full; + assign slv_resp_o.ar_ready = ~ar_fifo_full; + assign slv_resp_o.w_ready = ~w_fifo_full; + assign mst_req_o.r_ready = ~r_fifo_full; + assign mst_req_o.b_ready = ~b_fifo_full; + + // A FiFo for each channel + fifo_v3 #( + .dtype(aw_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_aw_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (aw_fifo_full), + .empty_o (aw_fifo_empty), + .usage_o (), + .data_i (slv_req_i.aw), + .push_i (slv_req_i.aw_valid && slv_resp_o.aw_ready), + .data_o (mst_req_o.aw), + .pop_i (mst_req_o.aw_valid && mst_resp_i.aw_ready) + ); + fifo_v3 #( + .dtype(ar_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_ar_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (ar_fifo_full), + .empty_o (ar_fifo_empty), + .usage_o (), + .data_i (slv_req_i.ar), + .push_i (slv_req_i.ar_valid && slv_resp_o.ar_ready), + .data_o (mst_req_o.ar), + .pop_i (mst_req_o.ar_valid && mst_resp_i.ar_ready) + ); + fifo_v3 #( + .dtype(w_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_w_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (w_fifo_full), + .empty_o (w_fifo_empty), + .usage_o (), + .data_i (slv_req_i.w), + .push_i (slv_req_i.w_valid && slv_resp_o.w_ready), + .data_o (mst_req_o.w), + .pop_i (mst_req_o.w_valid && mst_resp_i.w_ready) + ); + fifo_v3 #( + .dtype(r_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_r_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (r_fifo_full), + .empty_o (r_fifo_empty), + .usage_o (), + .data_i (mst_resp_i.r), + .push_i (mst_resp_i.r_valid && mst_req_o.r_ready), + .data_o (slv_resp_o.r), + .pop_i (slv_resp_o.r_valid && slv_req_i.r_ready) + ); + fifo_v3 #( + .dtype(b_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_b_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (b_fifo_full), + .empty_o (b_fifo_empty), + .usage_o (), + .data_i (mst_resp_i.b), + .push_i (mst_resp_i.b_valid && mst_req_o.b_ready), + .data_o (slv_resp_o.b), + .pop_i (slv_resp_o.b_valid && slv_req_i.b_ready) + ); + end + + // Check the invariants + // pragma translate_off +`ifndef VERILATOR + initial begin + assert (Depth >= 0); + end +`endif + // pragma translate_on +endmodule + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +// interface wrapper +module axi_fifo_intf #( + parameter int unsigned ADDR_WIDTH = 0, // The address width. + parameter int unsigned DATA_WIDTH = 0, // The data width. + parameter int unsigned ID_WIDTH = 0, // The ID width. + parameter int unsigned USER_WIDTH = 0, // The user data width. + parameter int unsigned DEPTH = 0, // The number of FiFo slots. + parameter int unsigned FALL_THROUGH = 0 // FiFo in fall-through mode +) ( + input logic clk_i, + input logic rst_ni, + input logic test_i, + AXI_BUS.Slave slv, + AXI_BUS.Master mst +); + + typedef logic [ID_WIDTH-1:0] id_t; + typedef logic [ADDR_WIDTH-1:0] addr_t; + typedef logic [DATA_WIDTH-1:0] data_t; + typedef logic [DATA_WIDTH/8-1:0] strb_t; + typedef logic [USER_WIDTH-1:0] user_t; + + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) + + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; + + `AXI_ASSIGN_TO_REQ(slv_req, slv) + `AXI_ASSIGN_FROM_RESP(slv, slv_resp) + + `AXI_ASSIGN_FROM_REQ(mst, mst_req) + `AXI_ASSIGN_TO_RESP(mst_resp, mst) + + axi_fifo #( + .Depth (DEPTH), + .FallThrough(FALL_THROUGH), + .aw_chan_t (aw_chan_t), + .w_chan_t (w_chan_t), + .b_chan_t (b_chan_t), + .ar_chan_t (ar_chan_t), + .r_chan_t (r_chan_t), + .axi_req_t (axi_req_t), + .axi_resp_t (axi_resp_t) + ) i_axi_fifo ( + .clk_i, + .rst_ni, + .test_i, + .slv_req_i (slv_req), + .slv_resp_o(slv_resp), + .mst_req_o (mst_req), + .mst_resp_i(mst_resp) + ); + + // Check the invariants. + // pragma translate_off +`ifndef VERILATOR + initial begin + assert (ADDR_WIDTH > 0) + else $fatal(1, "Wrong addr width parameter"); + assert (DATA_WIDTH > 0) + else $fatal(1, "Wrong data width parameter"); + assert (ID_WIDTH > 0) + else $fatal(1, "Wrong id width parameter"); + assert (USER_WIDTH > 0) + else $fatal(1, "Wrong user width parameter"); + assert (slv.AXI_ADDR_WIDTH == ADDR_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (slv.AXI_DATA_WIDTH == DATA_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (slv.AXI_ID_WIDTH == ID_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (slv.AXI_USER_WIDTH == USER_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (mst.AXI_ADDR_WIDTH == ADDR_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (mst.AXI_DATA_WIDTH == DATA_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (mst.AXI_ID_WIDTH == ID_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (mst.AXI_USER_WIDTH == USER_WIDTH) + else $fatal(1, "Wrong interface definition"); + end +`endif + // pragma translate_on +endmodule diff --git a/src_files.yml b/src_files.yml index fe4fdc67e..a5bb04f88 100644 --- a/src_files.yml +++ b/src_files.yml @@ -23,6 +23,7 @@ axi: - src/axi_demux.sv - src/axi_dw_downsizer.sv - src/axi_dw_upsizer.sv + - src/axi_fifo.sv - src/axi_id_remap.sv - src/axi_id_prepend.sv - src/axi_isolate.sv From fe3c0b4f88e2c86b801289c0ab17beea27377d7d Mon Sep 17 00:00:00 2001 From: Noah Huetter Date: Thu, 28 Apr 2022 13:38:30 +0200 Subject: [PATCH 020/145] doc: Add axi_fifo --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 47a5458bc..b26d3ae6c 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_dw_downsizer`](src/axi_dw_downsizer.sv) | A data width converter between a wide AXI master and a narrower AXI slave. | | | [`axi_dw_upsizer`](src/axi_dw_upsizer.sv) | A data width converter between a narrow AXI master and a wider AXI slave. | | | [`axi_err_slv`](src/axi_err_slv.sv) | Always responds with an AXI decode/slave error for transactions which are sent to it. | | +| [`axi_fifo`](src/axi_fifo.sv) | A Fifo for each AXI4 channel to buffer requests. | | | [`axi_id_prepend`](src/axi_id_prepend.sv) | This module prepends/strips the MSB from the AXI IDs. | | | [`axi_id_remap`](src/axi_id_remap.sv) | Remap AXI IDs from wide IDs at the slave port to narrower IDs at the master port. | [Doc][doc.axi_id_remap] | | [`axi_id_serialize`](src/axi_id_serialize.sv) | Reduce AXI IDs by serializing transactions when necessary. | [Doc][doc.axi_id_serialize] | From 25803a07dbac962e3da78b9b9e7aa8c5d02b45a2 Mon Sep 17 00:00:00 2001 From: Noah Huetter Date: Thu, 28 Apr 2022 13:38:46 +0200 Subject: [PATCH 021/145] axi_fifo: Add testbench --- scripts/run_vsim.sh | 8 ++ test/tb_axi_fifo.sv | 201 +++++++++++++++++++++++++++++++++++++++ test/tb_axi_fifo.wave.do | 201 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 410 insertions(+) create mode 100644 test/tb_axi_fifo.sv create mode 100644 test/tb_axi_fifo.wave.do diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh index b83862bcd..a6b2678b2 100755 --- a/scripts/run_vsim.sh +++ b/scripts/run_vsim.sh @@ -72,6 +72,14 @@ exec_test() { done done ;; + axi_fifo) + for DEPTH in 0 1 16; do + for FALL_THROUGH in 0 1; do + call_vsim tb_axi_fifo -gDepth=$DEPTH \ + -gFallThrough=$FALL_THROUGH + done + done + ;; axi_iw_converter) for SLV_PORT_IW in 1 2 3 4 8; do MAX_SLV_PORT_IDS=$((2**SLV_PORT_IW)) diff --git a/test/tb_axi_fifo.sv b/test/tb_axi_fifo.sv new file mode 100644 index 000000000..da88a4c81 --- /dev/null +++ b/test/tb_axi_fifo.sv @@ -0,0 +1,201 @@ +// Copyright (c) 2019 ETH Zurich and 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: +// - Wolfgang Roenninger +// - Andreas Kurth +// - Noah Huetter + +`include "axi/typedef.svh" +`include "axi/assign.svh" + +module tb_axi_fifo #( + parameter int unsigned Depth = 16, + parameter int unsigned FallThrough = 0, + parameter int unsigned NoWrites = 200, // How many writes per master + parameter int unsigned NoReads = 200 // How many reads per master +); + // Random Master Atomics + localparam int unsigned MaxAW = 30; + localparam int unsigned MaxAR = 30; + localparam bit EnAtop = 1'b1; + // timing parameters + localparam time CyclTime = 10ns; + localparam time ApplTime = 2ns; + localparam time TestTime = 8ns; + // AXI configuration + localparam int unsigned AxiIdWidth = 4; + localparam int unsigned AxiAddrWidth = 32; // Axi Address Width + localparam int unsigned AxiDataWidth = 64; // Axi Data Width + localparam int unsigned AxiUserWidth = 5; + // Sim print config, how many transactions + localparam int unsigned PrintTnx = 100; + + typedef axi_test::axi_rand_master#( + // AXI interface parameters + .AW (AxiAddrWidth), + .DW (AxiDataWidth), + .IW (AxiIdWidth), + .UW (AxiUserWidth), + // Stimuli application and test time + .TA (ApplTime), + .TT (TestTime), + // Maximum number of read and write transactions in flight + .MAX_READ_TXNS (MaxAR), + .MAX_WRITE_TXNS(MaxAW), + .AXI_ATOPS (EnAtop) + ) axi_rand_master_t; + typedef axi_test::axi_rand_slave#( + // AXI interface parameters + .AW(AxiAddrWidth), + .DW(AxiDataWidth), + .IW(AxiIdWidth), + .UW(AxiUserWidth), + // Stimuli application and test time + .TA(ApplTime), + .TT(TestTime) + ) axi_rand_slave_t; + + // ------------- + // DUT signals + // ------------- + logic clk; + logic rst_n; + logic end_of_sim; + + // interfaces + AXI_BUS #( + .AXI_ADDR_WIDTH(AxiAddrWidth), + .AXI_DATA_WIDTH(AxiDataWidth), + .AXI_ID_WIDTH (AxiIdWidth), + .AXI_USER_WIDTH(AxiUserWidth) + ) + master (), slave (); + AXI_BUS_DV #( + .AXI_ADDR_WIDTH(AxiAddrWidth), + .AXI_DATA_WIDTH(AxiDataWidth), + .AXI_ID_WIDTH (AxiIdWidth), + .AXI_USER_WIDTH(AxiUserWidth) + ) + master_dv (clk), slave_dv (clk); + + `AXI_ASSIGN(master, master_dv) + `AXI_ASSIGN(slave_dv, slave) + + //----------------------------------- + // Clock generator + //----------------------------------- + clk_rst_gen #( + .ClkPeriod (CyclTime), + .RstClkCycles(5) + ) i_clk_gen ( + .clk_o (clk), + .rst_no(rst_n) + ); + + //----------------------------------- + // DUT + //----------------------------------- + axi_fifo_intf #( + .DEPTH (Depth), // number of FiFo slots + .FALL_THROUGH(FallThrough), // FiFos in fall through mode + .ID_WIDTH (AxiIdWidth), // AXI ID width + .ADDR_WIDTH (AxiAddrWidth), // AXI address width + .DATA_WIDTH (AxiDataWidth), // AXI data width + .USER_WIDTH (AxiUserWidth) // AXI user width + ) i_dut ( + .clk_i (clk), // clock + .rst_ni(rst_n), // asynchronous reset active low + .test_i(1'b0), + .slv (master), // slave port + .mst (slave) // master port + ); + + initial begin : proc_axi_master + automatic axi_rand_master_t axi_rand_master = new(master_dv); + end_of_sim <= 1'b0; + axi_rand_master.add_memory_region(32'h0000_0000, 32'h1000_0000, axi_pkg::DEVICE_NONBUFFERABLE); + axi_rand_master.add_memory_region(32'h2000_0000, 32'h3000_0000, axi_pkg::WTHRU_NOALLOCATE); + axi_rand_master.add_memory_region(32'h4000_0000, 32'h5000_0000, axi_pkg::WBACK_RWALLOCATE); + axi_rand_master.reset(); + @(posedge rst_n); + axi_rand_master.run(NoReads, NoWrites); + end_of_sim <= 1'b1; + repeat (10000) @(posedge clk); + $stop(); + end + + initial begin : proc_axi_slave + automatic axi_rand_slave_t axi_rand_slave = new(slave_dv); + axi_rand_slave.reset(); + @(posedge rst_n); + axi_rand_slave.run(); + end + + initial begin : proc_sim_progress + automatic int unsigned aw = 0; + automatic int unsigned ar = 0; + automatic bit aw_printed = 1'b0; + automatic bit ar_printed = 1'b0; + + @(posedge rst_n); + + forever begin + @(posedge clk); + #TestTime; + if (master.aw_valid && master.aw_ready) begin + aw++; + end + if (master.ar_valid && master.ar_ready) begin + ar++; + end + + if ((aw % PrintTnx == 0) && !aw_printed) begin + $display("%t> Transmit AW %d of %d.", $time(), aw, NoWrites); + aw_printed = 1'b1; + end + if ((ar % PrintTnx == 0) && !ar_printed) begin + $display("%t> Transmit AR %d of %d.", $time(), ar, NoReads); + ar_printed = 1'b1; + end + + if (aw % PrintTnx == 1) begin + aw_printed = 1'b0; + end + if (ar % PrintTnx == 1) begin + ar_printed = 1'b0; + end + + if (end_of_sim) begin + $info("All transactions completed."); + break; + end + end + end + + + default disable iff (!rst_n); aw_unstable : + assert property (@(posedge clk) (slave.aw_valid && !slave.aw_ready) |=> $stable(slave.aw_addr)) + else $fatal(1, "AW is unstable."); + w_unstable : + assert property (@(posedge clk) (slave.w_valid && !slave.w_ready) |=> $stable(slave.w_data)) + else $fatal(1, "W is unstable."); + b_unstable : + assert property (@(posedge clk) (master.b_valid && !master.b_ready) |=> $stable(master.b_resp)) + else $fatal(1, "B is unstable."); + ar_unstable : + assert property (@(posedge clk) (slave.ar_valid && !slave.ar_ready) |=> $stable(slave.ar_addr)) + else $fatal(1, "AR is unstable."); + r_unstable : + assert property (@(posedge clk) (master.r_valid && !master.r_ready) |=> $stable(master.r_data)) + else $fatal(1, "R is unstable."); + + +endmodule diff --git a/test/tb_axi_fifo.wave.do b/test/tb_axi_fifo.wave.do new file mode 100644 index 000000000..bdf1d9e20 --- /dev/null +++ b/test/tb_axi_fifo.wave.do @@ -0,0 +1,201 @@ +onerror {resume} +quietly WaveActivateNextPane {} 0 +add wave -noupdate -label Clock /tb_axi_fifo/i_dut/clk_i +add wave -noupdate -label Reset /tb_axi_fifo/i_dut/rst_ni +add wave -noupdate -divider {Slave Ports} +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_id +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_addr +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_len +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_size +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_burst +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_lock +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_cache +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_prot +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_qos +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_region +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_atop +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_user +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_valid +add wave -noupdate -expand -group {Master AW} /tb_axi_fifo/master/aw_ready +add wave -noupdate -group {Master W} /tb_axi_fifo/master/w_data +add wave -noupdate -group {Master W} /tb_axi_fifo/master/w_strb +add wave -noupdate -group {Master W} /tb_axi_fifo/master/w_last +add wave -noupdate -group {Master W} /tb_axi_fifo/master/w_user +add wave -noupdate -group {Master W} /tb_axi_fifo/master/w_valid +add wave -noupdate -group {Master W} /tb_axi_fifo/master/w_ready +add wave -noupdate -group {Master B} /tb_axi_fifo/master/b_id +add wave -noupdate -group {Master B} /tb_axi_fifo/master/b_resp +add wave -noupdate -group {Master B} /tb_axi_fifo/master/b_user +add wave -noupdate -group {Master B} /tb_axi_fifo/master/b_valid +add wave -noupdate -group {Master B} /tb_axi_fifo/master/b_ready +add wave -noupdate -expand -group {Master AR} /tb_axi_fifo/master/ar_id +add wave -noupdate -expand -group {Master AR} /tb_axi_fifo/master/ar_addr +add wave -noupdate -expand -group {Master AR} /tb_axi_fifo/master/ar_len +add wave -noupdate -expand -group {Master AR} /tb_axi_fifo/master/ar_size +add wave -noupdate -expand -group {Master AR} /tb_axi_fifo/master/ar_burst +add wave -noupdate -expand -group {Master AR} /tb_axi_fifo/master/ar_lock +add wave -noupdate -expand -group {Master AR} /tb_axi_fifo/master/ar_cache +add wave -noupdate -expand -group {Master AR} /tb_axi_fifo/master/ar_prot +add wave -noupdate -expand -group {Master AR} /tb_axi_fifo/master/ar_qos +add wave -noupdate -expand -group {Master AR} /tb_axi_fifo/master/ar_region +add wave -noupdate -expand -group {Master AR} /tb_axi_fifo/master/ar_user +add wave -noupdate -expand -group {Master AR} /tb_axi_fifo/master/ar_valid +add wave -noupdate -expand -group {Master AR} /tb_axi_fifo/master/ar_ready +add wave -noupdate -group {Master R} /tb_axi_fifo/master/r_id +add wave -noupdate -group {Master R} /tb_axi_fifo/master/r_data +add wave -noupdate -group {Master R} /tb_axi_fifo/master/r_resp +add wave -noupdate -group {Master R} /tb_axi_fifo/master/r_last +add wave -noupdate -group {Master R} /tb_axi_fifo/master/r_user +add wave -noupdate -group {Master R} /tb_axi_fifo/master/r_valid +add wave -noupdate -group {Master R} /tb_axi_fifo/master/r_ready +add wave -noupdate -divider {Master Ports} +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_id +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_addr +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_len +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_size +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_burst +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_lock +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_cache +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_prot +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_qos +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_region +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_atop +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_user +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_valid +add wave -noupdate -expand -group {Slave AW} /tb_axi_fifo/slave/aw_ready +add wave -noupdate -group {Slave W} /tb_axi_fifo/slave/w_data +add wave -noupdate -group {Slave W} /tb_axi_fifo/slave/w_strb +add wave -noupdate -group {Slave W} /tb_axi_fifo/slave/w_last +add wave -noupdate -group {Slave W} /tb_axi_fifo/slave/w_user +add wave -noupdate -group {Slave W} /tb_axi_fifo/slave/w_valid +add wave -noupdate -group {Slave W} /tb_axi_fifo/slave/w_ready +add wave -noupdate -group {Slave B} /tb_axi_fifo/slave/b_id +add wave -noupdate -group {Slave B} /tb_axi_fifo/slave/b_resp +add wave -noupdate -group {Slave B} /tb_axi_fifo/slave/b_user +add wave -noupdate -group {Slave B} /tb_axi_fifo/slave/b_valid +add wave -noupdate -group {Slave B} /tb_axi_fifo/slave/b_ready +add wave -noupdate -expand -group {Slave AR} /tb_axi_fifo/slave/ar_id +add wave -noupdate -expand -group {Slave AR} /tb_axi_fifo/slave/ar_addr +add wave -noupdate -expand -group {Slave AR} /tb_axi_fifo/slave/ar_len +add wave -noupdate -expand -group {Slave AR} /tb_axi_fifo/slave/ar_size +add wave -noupdate -expand -group {Slave AR} /tb_axi_fifo/slave/ar_burst +add wave -noupdate -expand -group {Slave AR} /tb_axi_fifo/slave/ar_lock +add wave -noupdate -expand -group {Slave AR} /tb_axi_fifo/slave/ar_cache +add wave -noupdate -expand -group {Slave AR} /tb_axi_fifo/slave/ar_prot +add wave -noupdate -expand -group {Slave AR} /tb_axi_fifo/slave/ar_qos +add wave -noupdate -expand -group {Slave AR} /tb_axi_fifo/slave/ar_region +add wave -noupdate -expand -group {Slave AR} /tb_axi_fifo/slave/ar_user +add wave -noupdate -expand -group {Slave AR} /tb_axi_fifo/slave/ar_valid +add wave -noupdate -expand -group {Slave AR} /tb_axi_fifo/slave/ar_ready +add wave -noupdate -group {Slave R} /tb_axi_fifo/slave/r_id +add wave -noupdate -group {Slave R} /tb_axi_fifo/slave/r_data +add wave -noupdate -group {Slave R} /tb_axi_fifo/slave/r_resp +add wave -noupdate -group {Slave R} /tb_axi_fifo/slave/r_last +add wave -noupdate -group {Slave R} /tb_axi_fifo/slave/r_user +add wave -noupdate -group {Slave R} /tb_axi_fifo/slave/r_valid +add wave -noupdate -group {Slave R} /tb_axi_fifo/slave/r_ready +add wave -noupdate -divider Custom +add wave -noupdate -expand -group {AW FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_aw_fifo/clk_i +add wave -noupdate -expand -group {AW FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_aw_fifo/rst_ni +add wave -noupdate -expand -group {AW FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_aw_fifo/flush_i +add wave -noupdate -expand -group {AW FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_aw_fifo/testmode_i +add wave -noupdate -expand -group {AW FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_aw_fifo/full_o +add wave -noupdate -expand -group {AW FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_aw_fifo/empty_o +add wave -noupdate -expand -group {AW FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_aw_fifo/usage_o +add wave -noupdate -expand -group {AW FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_aw_fifo/data_i +add wave -noupdate -expand -group {AW FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_aw_fifo/push_i +add wave -noupdate -expand -group {AW FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_aw_fifo/data_o +add wave -noupdate -expand -group {AW FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_aw_fifo/pop_i +add wave -noupdate -expand -group {W FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_w_fifo/clk_i +add wave -noupdate -expand -group {W FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_w_fifo/rst_ni +add wave -noupdate -expand -group {W FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_w_fifo/flush_i +add wave -noupdate -expand -group {W FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_w_fifo/testmode_i +add wave -noupdate -expand -group {W FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_w_fifo/full_o +add wave -noupdate -expand -group {W FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_w_fifo/empty_o +add wave -noupdate -expand -group {W FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_w_fifo/usage_o +add wave -noupdate -expand -group {W FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_w_fifo/data_i +add wave -noupdate -expand -group {W FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_w_fifo/push_i +add wave -noupdate -expand -group {W FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_w_fifo/data_o +add wave -noupdate -expand -group {W FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_w_fifo/pop_i +add wave -noupdate -expand -group {B FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_b_fifo/clk_i +add wave -noupdate -expand -group {B FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_b_fifo/rst_ni +add wave -noupdate -expand -group {B FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_b_fifo/flush_i +add wave -noupdate -expand -group {B FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_b_fifo/testmode_i +add wave -noupdate -expand -group {B FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_b_fifo/full_o +add wave -noupdate -expand -group {B FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_b_fifo/empty_o +add wave -noupdate -expand -group {B FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_b_fifo/usage_o +add wave -noupdate -expand -group {B FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_b_fifo/data_i +add wave -noupdate -expand -group {B FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_b_fifo/push_i +add wave -noupdate -expand -group {B FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_b_fifo/data_o +add wave -noupdate -expand -group {B FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_b_fifo/pop_i +add wave -noupdate -expand -group {Ar FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_ar_fifo/clk_i +add wave -noupdate -expand -group {Ar FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_ar_fifo/rst_ni +add wave -noupdate -expand -group {Ar FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_ar_fifo/flush_i +add wave -noupdate -expand -group {Ar FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_ar_fifo/testmode_i +add wave -noupdate -expand -group {Ar FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_ar_fifo/full_o +add wave -noupdate -expand -group {Ar FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_ar_fifo/empty_o +add wave -noupdate -expand -group {Ar FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_ar_fifo/usage_o +add wave -noupdate -expand -group {Ar FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_ar_fifo/data_i +add wave -noupdate -expand -group {Ar FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_ar_fifo/push_i +add wave -noupdate -expand -group {Ar FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_ar_fifo/data_o +add wave -noupdate -expand -group {Ar FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_ar_fifo/pop_i +add wave -noupdate -expand -group {R FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_r_fifo/clk_i +add wave -noupdate -expand -group {R FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_r_fifo/rst_ni +add wave -noupdate -expand -group {R FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_r_fifo/flush_i +add wave -noupdate -expand -group {R FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_r_fifo/testmode_i +add wave -noupdate -expand -group {R FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_r_fifo/full_o +add wave -noupdate -expand -group {R FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_r_fifo/empty_o +add wave -noupdate -expand -group {R FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_r_fifo/usage_o +add wave -noupdate -expand -group {R FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_r_fifo/data_i +add wave -noupdate -expand -group {R FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_r_fifo/push_i +add wave -noupdate -expand -group {R FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_r_fifo/data_o +add wave -noupdate -expand -group {R FiFo} /tb_axi_fifo/i_dut/i_axi_fifo/gen_axi_fifo/i_r_fifo/pop_i +add wave -noupdate -divider {DUT Ports} +add wave -noupdate -expand -group {DUT slv AW} /tb_axi_fifo/i_dut/i_axi_fifo/slv_req_i.aw_valid +add wave -noupdate -expand -group {DUT slv AW} /tb_axi_fifo/i_dut/i_axi_fifo/slv_resp_o.aw_ready +add wave -noupdate -expand -group {DUT slv AW} /tb_axi_fifo/i_dut/i_axi_fifo/slv_req_i.aw +add wave -noupdate -expand -group {DUT slv W} /tb_axi_fifo/i_dut/i_axi_fifo/slv_req_i.w +add wave -noupdate -expand -group {DUT slv W} /tb_axi_fifo/i_dut/i_axi_fifo/slv_req_i.w_valid +add wave -noupdate -expand -group {DUT slv W} /tb_axi_fifo/i_dut/i_axi_fifo/slv_resp_o.w_ready +add wave -noupdate -expand -group {DUT slv B} /tb_axi_fifo/i_dut/i_axi_fifo/slv_resp_o.b_valid +add wave -noupdate -expand -group {DUT slv B} /tb_axi_fifo/i_dut/i_axi_fifo/slv_req_i.b_ready +add wave -noupdate -expand -group {DUT slv B} /tb_axi_fifo/i_dut/i_axi_fifo/slv_resp_o.b +add wave -noupdate -expand -group {DUT slv AR} /tb_axi_fifo/i_dut/i_axi_fifo/slv_req_i.ar_valid +add wave -noupdate -expand -group {DUT slv AR} /tb_axi_fifo/i_dut/i_axi_fifo/slv_resp_o.ar_ready +add wave -noupdate -expand -group {DUT slv AR} /tb_axi_fifo/i_dut/i_axi_fifo/slv_req_i.ar +add wave -noupdate -expand -group {DUT slv R} /tb_axi_fifo/i_dut/i_axi_fifo/slv_resp_o.r_valid +add wave -noupdate -expand -group {DUT slv R} /tb_axi_fifo/i_dut/i_axi_fifo/slv_req_i.r_ready +add wave -noupdate -expand -group {DUT slv R} /tb_axi_fifo/i_dut/i_axi_fifo/slv_resp_o.r +add wave -noupdate -expand -group {DUT mst AW} /tb_axi_fifo/i_dut/i_axi_fifo/mst_req_o.aw_valid +add wave -noupdate -expand -group {DUT mst AW} /tb_axi_fifo/i_dut/i_axi_fifo/mst_resp_i.aw_ready +add wave -noupdate -expand -group {DUT mst AW} /tb_axi_fifo/i_dut/i_axi_fifo/mst_req_o.aw +add wave -noupdate -expand -group {DUT mst W} /tb_axi_fifo/i_dut/i_axi_fifo/mst_req_o.w +add wave -noupdate -expand -group {DUT mst W} /tb_axi_fifo/i_dut/i_axi_fifo/mst_req_o.w_valid +add wave -noupdate -expand -group {DUT mst W} /tb_axi_fifo/i_dut/i_axi_fifo/mst_resp_i.w_ready +add wave -noupdate -expand -group {DUT mst B} /tb_axi_fifo/i_dut/i_axi_fifo/mst_resp_i.b_valid +add wave -noupdate -expand -group {DUT mst B} /tb_axi_fifo/i_dut/i_axi_fifo/mst_req_o.b_ready +add wave -noupdate -expand -group {DUT mst B} /tb_axi_fifo/i_dut/i_axi_fifo/mst_resp_i.b +add wave -noupdate -expand -group {DUT mst AR} /tb_axi_fifo/i_dut/i_axi_fifo/mst_req_o.ar_valid +add wave -noupdate -expand -group {DUT mst AR} /tb_axi_fifo/i_dut/i_axi_fifo/mst_resp_i.ar_ready +add wave -noupdate -expand -group {DUT mst AR} /tb_axi_fifo/i_dut/i_axi_fifo/mst_req_o.ar +add wave -noupdate -expand -group {DUT mst R} /tb_axi_fifo/i_dut/i_axi_fifo/mst_resp_i.r_valid +add wave -noupdate -expand -group {DUT mst R} /tb_axi_fifo/i_dut/i_axi_fifo/mst_req_o.r_ready +add wave -noupdate -expand -group {DUT mst R} /tb_axi_fifo/i_dut/i_axi_fifo/mst_resp_i.r +TreeUpdate [SetDefaultTree] +WaveRestoreCursors {{Cursor 1} {70 ns} 0} +quietly wave cursor active 1 +configure wave -namecolwidth 197 +configure wave -valuecolwidth 100 +configure wave -justifyvalue left +configure wave -signalnamewidth 1 +configure wave -snapdistance 10 +configure wave -datasetprefix 0 +configure wave -rowmargin 4 +configure wave -childrowmargin 2 +configure wave -gridoffset 0 +configure wave -gridperiod 1 +configure wave -griddelta 40 +configure wave -timeline 0 +configure wave -timelineunits ns +update +WaveRestoreZoom {0 ns} {841 ns} From 4557d24abd8a0a93b718674cb57907d3c51d2e6a Mon Sep 17 00:00:00 2001 From: Noah Huetter Date: Thu, 28 Apr 2022 13:38:55 +0200 Subject: [PATCH 022/145] ci: Add axi_fifo to gitlab-ci --- .gitlab-ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6d450fc0f..979eef5f2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -93,6 +93,11 @@ axi_dw_upsizer: variables: TEST_MODULE: axi_dw_upsizer +axi_fifo: + <<: *run_vsim + variables: + TEST_MODULE: axi_fifo + axi_isolate: <<: *run_vsim variables: From 0473ff4dbb962aa2a570e4e5fb7716b67c67ddd7 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Tue, 30 Aug 2022 11:14:32 +0200 Subject: [PATCH 023/145] memora: Fix --- .ci/Memora.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.ci/Memora.yml b/.ci/Memora.yml index 72cd5ad1e..176afb4ff 100644 --- a/.ci/Memora.yml +++ b/.ci/Memora.yml @@ -103,6 +103,20 @@ artifacts: outputs: - build/axi_dw_upsizer-%.tested + axi_fifo-%: + inputs: + - Bender.yml + - include + - scripts/run_vsim.sh + - src/axi_pkg.sv + - src/axi_intf.sv + - src/axi_test.sv + - src/axi_fifo.sv + - test/tb_axi_fifo.sv + outputs: + - build/axi_fifo-%.tested + + axi_isolate-%: inputs: - Bender.yml From 9a53b3c66d32725e5c5656b02e1caf07c5f79a97 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 1 Sep 2022 10:00:24 +0200 Subject: [PATCH 024/145] Release v0.37.0 --- CHANGELOG.md | 26 ++++++++++++++++++++++---- README.md | 3 +++ VERSION | 2 +- axi.core | 2 +- ips_list.yml | 4 ++++ 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e46abaf0..a7e88501a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,25 +8,43 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased ### Added -- `axi_to_mem`: AXI4+ATOP slave to control on chip memory. + +### Changed + +### Fixed + + +## 0.37.0 - 2022-08-30 + +### Added +- `axi_fifo`: Inserts a FIFO into all 5 AXI4 channels; add module and its testbench - `axi_test`: Add `mapped` mode to the random classes as well as additional functionality to the scoreboard class. - `axi_throttle`: Add a module that limits the maximum number of outstanding transfers sent to the downstream logic. +- `axi_to_mem`: AXI4+ATOP slave to control on chip memory. - `axi_to_mem_banked`: AXI4+ATOP slave to control on chip memory, with banking support, higher - throughput than `axi_to_mem`. + throughput than `axi_to_mem`. - `axi_to_mem_interleaved`: AXI4+ATOP slave to control on chip memory, interleaved to prevent - deadlocks. + deadlocks. - `axi_to_mem_split`: AXI4+ATOP slave to control memory protocol interconnect. - `Bender`: Add dependency `tech_cells_generic` `v0.2.2` for generic SRAM macro for simulation. ### Changed +- `axi_demux`: Add module docstring +- `axi_sim_mem`: Add the capability to emit read and write errors +- `Bender`: Update dependency `common_cells` to `v1.26.0` from `v1.21.0` (required by + `axi_throttle`) +- Remove `docs` directory, move content to `doc` folder. `docs` is automatically created and + populated during the CI run. +- Update vsim version to `2021.3` in CI, drop test for `2020.1` and `2021.1` ### Fixed +- `axi_lite_demux`: Improve compatibility with vsim version 10.7b. - `axi_lite_mux`: Reduce complexity of W channel at master port by removing an unnecessary multiplexer. -- `axi_lite_demux`: Improve compatibility with vsim version 10.7b. +`v0.37.0` is fully **backward-compatible** to `v0.36.0`. ## 0.36.0 - 2022-07-07 diff --git a/README.md b/README.md index b26d3ae6c..5daef26f8 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,9 @@ We aim to be compatible with a wide range of EDA tools. For this reason, we str - the workaround does not break functionality in other tools, and - the workaround does not significantly complicate code or add maintenance overhead. +In addition, we suggest to report issues with the SystemVerilog language support directly to the EDA vendor. Our code is fully open and +can / should be shared with the EDA vendor as a testcase for any language problem encountered. + All code in each release and on the default branch is tested on a recent version of at least one industry-standard RTL simulator and synthesizer. You can examine the [CI settings](./.gitlab-ci.yml) to find out which version of which tool we are running. diff --git a/VERSION b/VERSION index 66ca32f1b..0f1a7dfc7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.36.1-dev +0.37.0 diff --git a/axi.core b/axi.core index 711a08e9c..49e39886a 100644 --- a/axi.core +++ b/axi.core @@ -1,6 +1,6 @@ CAPI=2: -name : pulp-platform.org::axi:0.36.1-dev +name : pulp-platform.org::axi:0.37.0 filesets: rtl: diff --git a/ips_list.yml b/ips_list.yml index 7056ebac2..c7dad06ac 100644 --- a/ips_list.yml +++ b/ips_list.yml @@ -5,3 +5,7 @@ common_cells: common_verification: commit: v0.2.0 group: pulp-platform + +tech_cells_generic: + commit: v0.2.2 + group: pulp-platform From f9ee69aaec48e8678349c07d14de881076e90123 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Fri, 2 Sep 2022 10:18:38 +0200 Subject: [PATCH 025/145] Add `axi_dumper` and `axi_dumper_interpret` script. This module dumps a log from an AXI bus, which can be analyzed with the interpret script for debugging purposes. --- Bender.yml | 1 + CHANGELOG.md | 1 + scripts/axi_dumper_interpret.py | 213 +++++++++++++++++++++++++++++++ src/axi_dumper.sv | 215 ++++++++++++++++++++++++++++++++ 4 files changed, 430 insertions(+) create mode 100644 scripts/axi_dumper_interpret.py create mode 100644 src/axi_dumper.sv diff --git a/Bender.yml b/Bender.yml index ede8af01f..c5047fc33 100644 --- a/Bender.yml +++ b/Bender.yml @@ -71,6 +71,7 @@ sources: - target: simulation files: + - src/axi_dumper.sv - src/axi_sim_mem.sv - src/axi_test.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index a7e88501a..4d7899135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased ### Added +- Add `axi_dumper` and `axi_dumper_interpret` script to dump log from an AXI bus for debugging purposes. ### Changed diff --git a/scripts/axi_dumper_interpret.py b/scripts/axi_dumper_interpret.py new file mode 100644 index 000000000..6a6a08531 --- /dev/null +++ b/scripts/axi_dumper_interpret.py @@ -0,0 +1,213 @@ +# Copyright (c) 2020 ETH Zurich, University of Bologna +# All rights reserved. +# +# 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. + +# Interpretation script for axi_dumper log files. +# This script can be used to help with finding issues in the AXI transactions along a BUS. + +import ast +import argparse + +def expand_strb(strb, num_bytes): + output = 0 + for i in range(num_bytes): + if strb & 1< 0 and new_list[-1]['addr'] == stat['addr']: + if 'strb' in stat.keys(): + if ~(new_list[-1]['strb'] & stat['strb']): + new_list[-1]['data'] = (new_list[-1]['data'] & expand_strb(new_list[-1]['strb'], num_bytes)) | (stat['data'] & expand_strb(stat['strb'], num_bytes)) + new_list[-1]['strb'] = new_list[-1]['strb'] | stat['strb'] + else: + new_list.append(stat) + else: + continue + else: + new_list.append(stat) + return new_list + +def validate_read(ar_list, r_list): + stat_list = [] + + r_index = 0 + for ar_index in range(len(ar_list)): + ar = ar_list[ar_index] + if (ar['size'] != 0x3): + print("size not 0x3") + stat = { 'start' : 0, + 'end' : 0, + 'addr' : 0, + 'len' : 0, + 'data' : [], + } + stat['start'] = ar['time'] + stat['len'] = ar['len'] + stat['addr'] = ar['addr'] + stat['data'] = [] + for i in range(stat['len'] + 1): + stat['data'].append(r_list[r_index]['data']) + if (r_list[r_index]['last'] and (i != stat['len'])) or ((i == stat['len']) and (not r_list[r_index]['last'])): + print("R LIST MISMATCH") + print(stat) + print("ARs") + for i in range(-2, 3): + print(ar_list[ar_index+i]) + print("Rs") + for i in range(-19, 20): + print(r_list[r_index+i]) + return stat_list + if (r_list[r_index]['last'] and (i == stat['len'])): + stat['end'] = r_list[r_index]['time'] + r_index += 1 + if (r_index >= len(r_list)): + print("R INDEX OVERFLOW") + return stat_list + stat_list.append(stat) + return stat_list + + +def validate_write(aw_list, w_list, b_list): + stat_list = [] + + w_index = 0 + b_index = 0 + for aw in aw_list: + stat = { 'start' : 0, + 'end' : 0, + 'addr' : 0, + 'len' : 0, + 'data' : [], + 'strb' : [], + } + stat['start'] = aw['time'] + stat['len'] = aw['len'] + stat['addr'] = aw['addr'] + stat['data'] = [] + for i in range(stat['len'] + 1): + if (w_index >= len(w_list)): + print("W INDEX OVERFLOW") + return stat_list + if (b_index >= len(b_list)): + print("B INDEX OVERFLOW") + return stat_list + stat['data'].append(w_list[w_index]['data']) + stat['strb'].append(w_list[w_index]['strb']) + if (w_list[w_index]['last'] and (i != stat['len'])) or ((i == stat['len']) and (not w_list[w_index]['last'])): + print("W LIST MISMATCH") + print(stat) + return stat_list + if w_list[w_index]['last'] and (i == stat['len']): + stat['end'] = b_list[b_index]['time'] + b_index += 1 + w_index += 1 + stat_list.append(stat) + return stat_list + + +def trace_file(filename, num_bytes): + axi_dict = { 'type' : '', + 'time' : 0, + 'id' : 0, + 'addr' : 0, + 'len' : 0, + 'size' : 0, + 'burst' : 0, + 'lock' : 0, + 'cache' : 0, + 'prot' : 0, + 'qos' : 0, + 'region' : 0, + 'atop' : 0, + 'user' : 0, + 'data' : 0, + 'strb' : 0, + 'last' : 0, + 'resp' : 0, + } + + aw_list = [] + ar_list = [] + w_list = [] + r_list = [] + b_list = [] + + aw_hex = 0x4157 # AW in ASCII + ar_hex = 0x4152 # AR in ASCII + w_hex = 0x57 # W in ASCII + r_hex = 0x52 # R in ASCII + b_hex = 0x42 # B in ASCII + + with open(filename, 'r') as trace_file: + i = 0 + for line in trace_file: + i += 1 + try: + trace_dict = ast.literal_eval(line) + except: + break + + if trace_dict["type"] == aw_hex: + aw_list.append(trace_dict) + elif trace_dict["type"] == ar_hex: + ar_list.append(trace_dict) + elif trace_dict["type"] == w_hex: + w_list.append(trace_dict) + elif trace_dict["type"] == r_hex: + r_list.append(trace_dict) + elif trace_dict["type"] == b_hex: + b_list.append(trace_dict) + else: + print("type ERROR") + break + # if (i > 4000000): break + read_stats = validate_read(ar_list, r_list) + write_stats = validate_write(aw_list, w_list, b_list) + split_reads = burst_splitter(read_stats) + split_writes = burst_splitter(write_stats) + recomb_reads = recombine_identical(split_reads, num_bytes) + recomb_writes = recombine_identical(split_writes, num_bytes) + print("reads {} {} {}".format(len(read_stats), len(split_reads), len(recomb_reads))) + print("writes {} {} {}".format(len(write_stats), len(split_writes), len(recomb_writes))) + return (recomb_reads, recomb_writes) + +if __name__ == '__main__': + + parser = argparse.ArgumentParser(description='Analyze an axi trace.') + parser.add_argument('axi_trace_log', metavar='axi_trace_log', type=str, help='the raw log file emitted by axi dumper.') + parser.add_argument('num_bytes', metavar='num_bytes', type=int, default=8, help='number of bytes in a data word') + + args = parser.parse_args() + + trace_file(args.axi_trace_log, args.num_bytes) + + + diff --git a/src/axi_dumper.sv b/src/axi_dumper.sv new file mode 100644 index 000000000..672c77b37 --- /dev/null +++ b/src/axi_dumper.sv @@ -0,0 +1,215 @@ +// Copyright (c) 2019 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: +// - Michael Rogenmoser + +/// Simulation-Only dumper for AXI transactions +/// +/// This module writes all handshaked AXI beats to a log file. To use in simulation, +/// ensure `TARGET_SIMULATION` is defined. +module axi_dumper #( + parameter BusName = "axi_bus", + parameter bit LogAW = 1'b1, + parameter bit LogAR = 1'b1, + parameter bit LogW = 1'b0, + parameter bit LogB = 1'b0, + parameter bit LogR = 1'b0, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + input axi_req_t axi_req_i, + input axi_resp_t axi_resp_i +); + +`ifdef TARGET_SIMULATION + string fn; + integer f; + initial begin + #1; + $sformat(fn, "axi_trace_%s.log", BusName); + f = $fopen(fn, "w"); + $display("[Tracer] Logging axi accesses to %s", fn); + end + + always_ff @(posedge clk_i) begin : proc_tracer + automatic longint aw_data [string]; + automatic longint ar_data [string]; + automatic longint w_data [string]; + automatic longint b_data [string]; + automatic longint r_data [string]; + + automatic string aw_string; + automatic string ar_string; + automatic string w_string; + automatic string b_string; + automatic string r_string; + + if (rst_ni) begin + aw_data = '{ + "type" : "AW", + "time" : $time(), + "id" : axi_req_i.aw.id, + "addr" : axi_req_i.aw.addr, + "len" : axi_req_i.aw.len, + "size" : axi_req_i.aw.size, + "burst" : axi_req_i.aw.burst, + "lock" : axi_req_i.aw.lock, + "cache" : axi_req_i.aw.cache, + "prot" : axi_req_i.aw.prot, + "qos" : axi_req_i.aw.qos, + "region" : axi_req_i.aw.region, + "atop" : axi_req_i.aw.atop, + "user" : axi_req_i.aw.user + }; + ar_data = '{ + "type" : "AR", + "time" : $time(), + "id" : axi_req_i.ar.id, + "addr" : axi_req_i.ar.addr, + "len" : axi_req_i.ar.len, + "size" : axi_req_i.ar.size, + "burst" : axi_req_i.ar.burst, + "lock" : axi_req_i.ar.lock, + "cache" : axi_req_i.ar.cache, + "prot" : axi_req_i.ar.prot, + "qos" : axi_req_i.ar.qos, + "region" : axi_req_i.ar.region, + "user" : axi_req_i.ar.user + }; + w_data = '{ + "type" : "W", + "time" : $time(), + "data" : axi_req_i.w.data, + "strb" : axi_req_i.w.strb, + "last" : axi_req_i.w.last, + "user" : axi_req_i.w.user + }; + b_data = '{ + "type" : "B", + "time" : $time(), + "id" : axi_resp_i.b.id, + "resp" : axi_resp_i.b.resp, + "user" : axi_resp_i.b.user + }; + r_data = '{ + "type" : "R", + "time" : $time(), + "id" : axi_resp_i.r.id, + "data" : axi_resp_i.r.data, + "resp" : axi_resp_i.r.resp, + "last" : axi_resp_i.r.last, + "user" : axi_resp_i.r.user + }; + if (LogAW && axi_req_i.aw_valid && axi_resp_i.aw_ready) begin + aw_string = "{"; + foreach(aw_data[key]) aw_string = $sformatf("%s'%s': 0x%0x, ", aw_string, key, aw_data[key]); + aw_string = $sformatf("%s}", aw_string); + $fwrite(f, aw_string); + $fwrite(f, "\n"); + end + if (LogAR && axi_req_i.ar_valid && axi_resp_i.ar_ready) begin + ar_string = "{"; + foreach(ar_data[key]) ar_string = $sformatf("%s'%s': 0x%0x, ", ar_string, key, ar_data[key]); + ar_string = $sformatf("%s}", ar_string); + $fwrite(f, ar_string); + $fwrite(f, "\n"); + end + if (LogW && axi_req_i.w_valid && axi_resp_i.w_ready) begin + w_string = "{"; + foreach(w_data[key]) w_string = $sformatf("%s'%s': 0x%0x, ", w_string, key, w_data[key]); + w_string = $sformatf("%s}", w_string); + $fwrite(f, w_string); + $fwrite(f, "\n"); + end + if (LogB && axi_resp_i.b_valid && axi_req_i.b_ready) begin + b_string = "{"; + foreach(b_data[key]) b_string = $sformatf("%s'%s': 0x%0x, ", b_string, key, b_data[key]); + b_string = $sformatf("%s}", b_string); + $fwrite(f, b_string); + $fwrite(f, "\n"); + end + if (LogR && axi_resp_i.r_valid && axi_req_i.r_ready) begin + r_string = "{"; + foreach(r_data[key]) r_string = $sformatf("%s'%s': 0x%0x, ", r_string, key, r_data[key]); + r_string = $sformatf("%s}", r_string); + $fwrite(f, r_string); + $fwrite(f, "\n"); + end + end + end + final begin + $fclose(f); + end + +`endif + +endmodule + + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +module axi_dumper_intf #( + parameter BusName = "axi_bus", + parameter bit LogAW = 1'b1, + parameter bit LogAR = 1'b1, + parameter bit LogW = 1'b0, + parameter bit LogB = 1'b0, + parameter bit LogR = 1'b0, + parameter int unsigned AXI_ID_WIDTH = 32'd0, + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + parameter int unsigned AXI_USER_WIDTH = 32'd0 +) ( + input logic clk_i, + input logic rst_ni, + AXI_BUS_DV.Monitor axi_bus +); + + typedef logic [AXI_ID_WIDTH-1:0] id_t; + typedef logic [AXI_ADDR_WIDTH-1:0] addr_t; + typedef logic [AXI_DATA_WIDTH-1:0] data_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t; + typedef logic [AXI_USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) + + axi_req_t axi_req; + axi_resp_t axi_resp; + + `AXI_ASSIGN_TO_REQ(axi_req, axi_bus) + `AXI_ASSIGN_TO_RESP(axi_resp, axi_bus) + + axi_dumper #( + .BusName ( BusName ), + .LogAW ( LogAW ), + .LogAR ( LogAR ), + .LogW ( LogW ), + .LogB ( LogB ), + .LogR ( LogR ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) + ) i_axi_dumper ( + .clk_i, + .rst_ni, + .axi_req_i ( axi_req ), + .axi_resp_i ( axi_resp ) + ); + +endmodule From 00c4e7eca08c6e432a5af7967924d30f87fde4c1 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Mon, 4 Apr 2022 11:07:18 +0200 Subject: [PATCH 026/145] axi_dumper: Change datatype to string This allows for larger internal widths, e.g. datawidth=1024 --- src/axi_dumper.sv | 106 +++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/src/axi_dumper.sv b/src/axi_dumper.sv index 672c77b37..c65012141 100644 --- a/src/axi_dumper.sv +++ b/src/axi_dumper.sv @@ -43,11 +43,11 @@ module axi_dumper #( end always_ff @(posedge clk_i) begin : proc_tracer - automatic longint aw_data [string]; - automatic longint ar_data [string]; - automatic longint w_data [string]; - automatic longint b_data [string]; - automatic longint r_data [string]; + automatic string aw_data [string]; + automatic string ar_data [string]; + automatic string w_data [string]; + automatic string b_data [string]; + automatic string r_data [string]; automatic string aw_string; automatic string ar_string; @@ -58,90 +58,90 @@ module axi_dumper #( if (rst_ni) begin aw_data = '{ "type" : "AW", - "time" : $time(), - "id" : axi_req_i.aw.id, - "addr" : axi_req_i.aw.addr, - "len" : axi_req_i.aw.len, - "size" : axi_req_i.aw.size, - "burst" : axi_req_i.aw.burst, - "lock" : axi_req_i.aw.lock, - "cache" : axi_req_i.aw.cache, - "prot" : axi_req_i.aw.prot, - "qos" : axi_req_i.aw.qos, - "region" : axi_req_i.aw.region, - "atop" : axi_req_i.aw.atop, - "user" : axi_req_i.aw.user + "time" : $sformatf("%d", $time()), + "id" : $sformatf("0x%0x", axi_req_i.aw.id), + "addr" : $sformatf("0x%0x", axi_req_i.aw.addr), + "len" : $sformatf("0x%0x", axi_req_i.aw.len), + "size" : $sformatf("0x%0x", axi_req_i.aw.size), + "burst" : $sformatf("0x%0x", axi_req_i.aw.burst), + "lock" : $sformatf("0x%0x", axi_req_i.aw.lock), + "cache" : $sformatf("0x%0x", axi_req_i.aw.cache), + "prot" : $sformatf("0x%0x", axi_req_i.aw.prot), + "qos" : $sformatf("0x%0x", axi_req_i.aw.qos), + "region" : $sformatf("0x%0x", axi_req_i.aw.region), + "atop" : $sformatf("0x%0x", axi_req_i.aw.atop), + "user" : $sformatf("0x%0x", axi_req_i.aw.user) }; ar_data = '{ "type" : "AR", - "time" : $time(), - "id" : axi_req_i.ar.id, - "addr" : axi_req_i.ar.addr, - "len" : axi_req_i.ar.len, - "size" : axi_req_i.ar.size, - "burst" : axi_req_i.ar.burst, - "lock" : axi_req_i.ar.lock, - "cache" : axi_req_i.ar.cache, - "prot" : axi_req_i.ar.prot, - "qos" : axi_req_i.ar.qos, - "region" : axi_req_i.ar.region, - "user" : axi_req_i.ar.user + "time" : $sformatf("%d", $time()), + "id" : $sformatf("0x%0x", axi_req_i.ar.id), + "addr" : $sformatf("0x%0x", axi_req_i.ar.addr), + "len" : $sformatf("0x%0x", axi_req_i.ar.len), + "size" : $sformatf("0x%0x", axi_req_i.ar.size), + "burst" : $sformatf("0x%0x", axi_req_i.ar.burst), + "lock" : $sformatf("0x%0x", axi_req_i.ar.lock), + "cache" : $sformatf("0x%0x", axi_req_i.ar.cache), + "prot" : $sformatf("0x%0x", axi_req_i.ar.prot), + "qos" : $sformatf("0x%0x", axi_req_i.ar.qos), + "region" : $sformatf("0x%0x", axi_req_i.ar.region), + "user" : $sformatf("0x%0x", axi_req_i.ar.user) }; w_data = '{ - "type" : "W", - "time" : $time(), - "data" : axi_req_i.w.data, - "strb" : axi_req_i.w.strb, - "last" : axi_req_i.w.last, - "user" : axi_req_i.w.user + "type" : "W", + "time" : $sformatf("%d", $time()), + "data" : $sformatf("0x%0x", axi_req_i.w.data), + "strb" : $sformatf("0x%0x", axi_req_i.w.strb), + "last" : $sformatf("0x%0x", axi_req_i.w.last), + "user" : $sformatf("0x%0x", axi_req_i.w.user) }; b_data = '{ - "type" : "B", - "time" : $time(), - "id" : axi_resp_i.b.id, - "resp" : axi_resp_i.b.resp, - "user" : axi_resp_i.b.user + "type" : "B", + "time" : $sformatf("%d", $time()), + "id" : $sformatf("0x%0x", axi_resp_i.b.id), + "resp" : $sformatf("0x%0x", axi_resp_i.b.resp), + "user" : $sformatf("0x%0x", axi_resp_i.b.user) }; r_data = '{ - "type" : "R", - "time" : $time(), - "id" : axi_resp_i.r.id, - "data" : axi_resp_i.r.data, - "resp" : axi_resp_i.r.resp, - "last" : axi_resp_i.r.last, - "user" : axi_resp_i.r.user + "type" : "R", + "time" : $sformatf("%d", $time()), + "id" : $sformatf("0x%0x", axi_resp_i.r.id), + "data" : $sformatf("0x%0x", axi_resp_i.r.data), + "resp" : $sformatf("0x%0x", axi_resp_i.r.resp), + "last" : $sformatf("0x%0x", axi_resp_i.r.last), + "user" : $sformatf("0x%0x", axi_resp_i.r.user) }; if (LogAW && axi_req_i.aw_valid && axi_resp_i.aw_ready) begin aw_string = "{"; - foreach(aw_data[key]) aw_string = $sformatf("%s'%s': 0x%0x, ", aw_string, key, aw_data[key]); + foreach(aw_data[key]) aw_string = $sformatf("%s'%s': %s, ", aw_string, key, aw_data[key]); aw_string = $sformatf("%s}", aw_string); $fwrite(f, aw_string); $fwrite(f, "\n"); end if (LogAR && axi_req_i.ar_valid && axi_resp_i.ar_ready) begin ar_string = "{"; - foreach(ar_data[key]) ar_string = $sformatf("%s'%s': 0x%0x, ", ar_string, key, ar_data[key]); + foreach(ar_data[key]) ar_string = $sformatf("%s'%s': %s, ", ar_string, key, ar_data[key]); ar_string = $sformatf("%s}", ar_string); $fwrite(f, ar_string); $fwrite(f, "\n"); end if (LogW && axi_req_i.w_valid && axi_resp_i.w_ready) begin w_string = "{"; - foreach(w_data[key]) w_string = $sformatf("%s'%s': 0x%0x, ", w_string, key, w_data[key]); + foreach(w_data[key]) w_string = $sformatf("%s'%s': %s, ", w_string, key, w_data[key]); w_string = $sformatf("%s}", w_string); $fwrite(f, w_string); $fwrite(f, "\n"); end if (LogB && axi_resp_i.b_valid && axi_req_i.b_ready) begin b_string = "{"; - foreach(b_data[key]) b_string = $sformatf("%s'%s': 0x%0x, ", b_string, key, b_data[key]); + foreach(b_data[key]) b_string = $sformatf("%s'%s': %s, ", b_string, key, b_data[key]); b_string = $sformatf("%s}", b_string); $fwrite(f, b_string); $fwrite(f, "\n"); end if (LogR && axi_resp_i.r_valid && axi_req_i.r_ready) begin r_string = "{"; - foreach(r_data[key]) r_string = $sformatf("%s'%s': 0x%0x, ", r_string, key, r_data[key]); + foreach(r_data[key]) r_string = $sformatf("%s'%s': %s, ", r_string, key, r_data[key]); r_string = $sformatf("%s}", r_string); $fwrite(f, r_string); $fwrite(f, "\n"); From eb5daece95dc39086ce4dfac52c9c371305793ba Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 30 Aug 2022 16:03:46 +0200 Subject: [PATCH 027/145] axi_dumper: improve script's error information --- scripts/axi_dumper_interpret.py | 145 +++++++++++++++++++++----------- src/axi_dumper.sv | 10 +-- 2 files changed, 103 insertions(+), 52 deletions(-) diff --git a/scripts/axi_dumper_interpret.py b/scripts/axi_dumper_interpret.py index 6a6a08531..59541741e 100644 --- a/scripts/axi_dumper_interpret.py +++ b/scripts/axi_dumper_interpret.py @@ -16,13 +16,17 @@ import ast import argparse + +# Expand strobe to bits from bytes def expand_strb(strb, num_bytes): output = 0 for i in range(num_bytes): - if strb & 1<= len(r_list)): + print("No R remaining for {}".format(ar)) + print("Current state: {}".format(stat)) + print("{} AR remaining".format(len(ar_list)-ar_index-1)) + return stat_list stat['data'].append(r_list[r_index]['data']) - if (r_list[r_index]['last'] and (i != stat['len'])) or ((i == stat['len']) and (not r_list[r_index]['last'])): - print("R LIST MISMATCH") - print(stat) + if (r_list[r_index]['last'] and (i != stat['len'])) or \ + ((i == stat['len']) and (not r_list[r_index]['last'])): + print("R last and length mismatch for {}".format(ar)) + print("Current state: ".format(stat)) print("ARs") - for i in range(-2, 3): - print(ar_list[ar_index+i]) + for j in range(-2, 3): + print(ar_list[ar_index + j]) print("Rs") - for i in range(-19, 20): - print(r_list[r_index+i]) + for j in range(-19, 20): + print(r_list[r_index + j]) return stat_list if (r_list[r_index]['last'] and (i == stat['len'])): stat['end'] = r_list[r_index]['time'] r_index += 1 if (r_index >= len(r_list)): - print("R INDEX OVERFLOW") + print("No R remaining for {}".format(ar)) + print("Current state: {}".format(stat)) + print("{} AR remaining".format(len(ar_list)-ar_index-1)) return stat_list stat_list.append(stat) + r_index_list[ar_id] = r_index return stat_list +# Validate all writes based on AW, W, and B def validate_write(aw_list, w_list, b_list): stat_list = [] - w_index = 0 - b_index = 0 - for aw in aw_list: + w_index = 0 # W are always in order of AW as no ID available + b_index_list = {} # To sort for ID + for aw_index in range(len(aw_list)): + aw = aw_list[aw_index] stat = { 'start' : 0, 'end' : 0, 'addr' : 0, 'len' : 0, + 'size' : 0, 'data' : [], 'strb' : [], } stat['start'] = aw['time'] stat['len'] = aw['len'] stat['addr'] = aw['addr'] + stat['size'] = aw['size'] stat['data'] = [] + aw_id = aw['id'] + if aw_id in b_index_list: + b_index = b_index_list[aw_id] + else: + b_index = 0 for i in range(stat['len'] + 1): if (w_index >= len(w_list)): - print("W INDEX OVERFLOW") - return stat_list - if (b_index >= len(b_list)): - print("B INDEX OVERFLOW") + print("No W remaining for {}".format(aw)) + print("Current state: {}".format(stat)) + print("{} AW remaining".format(len(aw_list)-aw_index-1)) return stat_list stat['data'].append(w_list[w_index]['data']) stat['strb'].append(w_list[w_index]['strb']) - if (w_list[w_index]['last'] and (i != stat['len'])) or ((i == stat['len']) and (not w_list[w_index]['last'])): - print("W LIST MISMATCH") - print(stat) + if (w_list[w_index]['last'] and (i != stat['len'])) or \ + ((i == stat['len']) and (not w_list[w_index]['last'])): + print("W last and length mismatch for {}".format(aw)) + print("Current state: {}".format(stat)) return stat_list if w_list[w_index]['last'] and (i == stat['len']): + while b_list[b_index]['id'] != aw_id: + b_index += 1 + if (b_index >= len(b_list)): + print("No B remaining for {}".format(aw)) + print("Current state: {}".format(stat)) + print("{} AW remaining".format(len(aw_list)-aw_index-1)) + return stat_list stat['end'] = b_list[b_index]['time'] b_index += 1 w_index += 1 + b_index_list[aw_id] = b_index stat_list.append(stat) return stat_list +# funtion to trace file def trace_file(filename, num_bytes): axi_dict = { 'type' : '', 'time' : 0, @@ -153,18 +197,18 @@ def trace_file(filename, num_bytes): 'last' : 0, 'resp' : 0, } - + aw_list = [] ar_list = [] w_list = [] r_list = [] b_list = [] - aw_hex = 0x4157 # AW in ASCII - ar_hex = 0x4152 # AR in ASCII - w_hex = 0x57 # W in ASCII - r_hex = 0x52 # R in ASCII - b_hex = 0x42 # B in ASCII + aw_hex = 0x4157 # AW in ASCII + ar_hex = 0x4152 # AR in ASCII + w_hex = 0x57 # W in ASCII + r_hex = 0x52 # R in ASCII + b_hex = 0x42 # B in ASCII with open(filename, 'r') as trace_file: i = 0 @@ -173,41 +217,48 @@ def trace_file(filename, num_bytes): try: trace_dict = ast.literal_eval(line) except: + print("dict parsing failed") break - - if trace_dict["type"] == aw_hex: + + if trace_dict["type"] == aw_hex or trace_dict["type"] == "AW": aw_list.append(trace_dict) - elif trace_dict["type"] == ar_hex: + elif trace_dict["type"] == ar_hex or trace_dict["type"] == "AR": ar_list.append(trace_dict) - elif trace_dict["type"] == w_hex: + elif trace_dict["type"] == w_hex or trace_dict["type"] == "W": w_list.append(trace_dict) - elif trace_dict["type"] == r_hex: + elif trace_dict["type"] == r_hex or trace_dict["type"] == "R": r_list.append(trace_dict) - elif trace_dict["type"] == b_hex: + elif trace_dict["type"] == b_hex or trace_dict["type"] == "B": b_list.append(trace_dict) else: print("type ERROR") break # if (i > 4000000): break + # print(trace_dict) + # TODO: split reads per ID read_stats = validate_read(ar_list, r_list) write_stats = validate_write(aw_list, w_list, b_list) split_reads = burst_splitter(read_stats) split_writes = burst_splitter(write_stats) recomb_reads = recombine_identical(split_reads, num_bytes) recomb_writes = recombine_identical(split_writes, num_bytes) - print("reads {} {} {}".format(len(read_stats), len(split_reads), len(recomb_reads))) - print("writes {} {} {}".format(len(write_stats), len(split_writes), len(recomb_writes))) + print("Successfully processed:") + print("\treads: {} ARs, {} Rs, {} shrunk accesses".format(len(read_stats), len(split_reads), len(recomb_reads))) + print("\twrites: {} AWs, {} Ws, {} shrunk accesses".format(len(write_stats), len(split_writes), len(recomb_writes))) + print("Total in list:") + print("\treads: {} ARs, {} Rs".format(len(ar_list), len(r_list))) + print("\twrites: {} AWs, {} Ws, {} Bs".format(len(aw_list), len(w_list), len(b_list))) return (recomb_reads, recomb_writes) + if __name__ == '__main__': parser = argparse.ArgumentParser(description='Analyze an axi trace.') - parser.add_argument('axi_trace_log', metavar='axi_trace_log', type=str, help='the raw log file emitted by axi dumper.') - parser.add_argument('num_bytes', metavar='num_bytes', type=int, default=8, help='number of bytes in a data word') + parser.add_argument('axi_trace_log', metavar='axi_trace_log', type=str, + help='the raw log file emitted by axi dumper.') + parser.add_argument('num_bytes', metavar='num_bytes', type=int, default=8, + help='number of bytes in a data word') args = parser.parse_args() trace_file(args.axi_trace_log, args.num_bytes) - - - diff --git a/src/axi_dumper.sv b/src/axi_dumper.sv index c65012141..c3031ea5c 100644 --- a/src/axi_dumper.sv +++ b/src/axi_dumper.sv @@ -57,7 +57,7 @@ module axi_dumper #( if (rst_ni) begin aw_data = '{ - "type" : "AW", + "type" : "\"AW\"", "time" : $sformatf("%d", $time()), "id" : $sformatf("0x%0x", axi_req_i.aw.id), "addr" : $sformatf("0x%0x", axi_req_i.aw.addr), @@ -73,7 +73,7 @@ module axi_dumper #( "user" : $sformatf("0x%0x", axi_req_i.aw.user) }; ar_data = '{ - "type" : "AR", + "type" : "\"AR\"", "time" : $sformatf("%d", $time()), "id" : $sformatf("0x%0x", axi_req_i.ar.id), "addr" : $sformatf("0x%0x", axi_req_i.ar.addr), @@ -88,7 +88,7 @@ module axi_dumper #( "user" : $sformatf("0x%0x", axi_req_i.ar.user) }; w_data = '{ - "type" : "W", + "type" : "\"W\"", "time" : $sformatf("%d", $time()), "data" : $sformatf("0x%0x", axi_req_i.w.data), "strb" : $sformatf("0x%0x", axi_req_i.w.strb), @@ -96,14 +96,14 @@ module axi_dumper #( "user" : $sformatf("0x%0x", axi_req_i.w.user) }; b_data = '{ - "type" : "B", + "type" : "\"B\"", "time" : $sformatf("%d", $time()), "id" : $sformatf("0x%0x", axi_resp_i.b.id), "resp" : $sformatf("0x%0x", axi_resp_i.b.resp), "user" : $sformatf("0x%0x", axi_resp_i.b.user) }; r_data = '{ - "type" : "R", + "type" : "\"R\"", "time" : $sformatf("%d", $time()), "id" : $sformatf("0x%0x", axi_resp_i.r.id), "data" : $sformatf("0x%0x", axi_resp_i.r.data), From 226698bd5b8abdf403cf7f90fce99c4040a319d7 Mon Sep 17 00:00:00 2001 From: Sophana KOK Date: Wed, 6 Apr 2022 14:57:07 +0200 Subject: [PATCH 028/145] add support for vivado xsim --- axi.core | 33 +++++++++++++++++++++++++++++++++ src/axi_demux.sv | 4 ++-- src/axi_err_slv.sv | 2 +- src/axi_test.sv | 6 +++++- src/axi_xbar.sv | 5 +++-- test/tb_axi_dw_pkg.sv | 2 +- test/tb_axi_xbar_pkg.sv | 6 +++--- 7 files changed, 48 insertions(+), 10 deletions(-) diff --git a/axi.core b/axi.core index 49e39886a..89e1290b9 100644 --- a/axi.core +++ b/axi.core @@ -58,6 +58,35 @@ filesets: file_type : systemVerilogSource depend : - ">=pulp-platform.org::common_cells:1.26.0" + benchs: + files: + - src/axi_test.sv + - test/tb_axi_dw_pkg.sv + - test/tb_axi_xbar_pkg.sv + - test/axi_synth_bench.sv + - test/tb_axi_addr_test.sv + - test/tb_axi_atop_filter.sv + - test/tb_axi_cdc.sv + - test/tb_axi_delayer.sv + - test/tb_axi_dw_downsizer.sv + - test/tb_axi_dw_upsizer.sv + - test/tb_axi_fifo.sv + - test/tb_axi_isolate.sv + - test/tb_axi_iw_converter.sv + - test/tb_axi_lite_mailbox.sv + - test/tb_axi_lite_regs.sv + - test/tb_axi_lite_to_apb.sv + - test/tb_axi_lite_to_axi.sv + - test/tb_axi_lite_xbar.sv + - test/tb_axi_modify_address.sv + - test/tb_axi_serializer.sv + - test/tb_axi_sim_mem.sv + - test/tb_axi_to_axi_lite.sv + - test/tb_axi_to_mem_banked.sv + - test/tb_axi_xbar.sv + file_type : systemVerilogSource + depend : + - ">=pulp-platform.org::common_verification:0.2.1" generators: axi_intercon_gen: @@ -113,3 +142,7 @@ generators: targets: default: filesets : [rtl] + sim: + filesets : [rtl,benchs] + description: Simulate the design + toplevel: tb_axi_dw_downsizer diff --git a/src/axi_demux.sv b/src/axi_demux.sv index cae7401eb..aa2e328a4 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -625,7 +625,7 @@ module axi_demux #( // Validate parameters. // pragma translate_off `ifndef VERILATOR -`ifndef XSIM +`ifndef XILINX_SIMULATOR initial begin: validate_params no_mst_ports: assume (NoMstPorts > 0) else $fatal(1, "The Number of slaves (NoMstPorts) has to be at least 1"); @@ -774,7 +774,7 @@ module axi_demux_id_counters #( // pragma translate_off `ifndef VERILATOR -`ifndef XSIM +`ifndef XILINX_SIMULATOR // Validate parameters. cnt_underflow: assert property( @(posedge clk_i) disable iff (~rst_ni) (pop_en[i] |=> !overflow)) else diff --git a/src/axi_err_slv.sv b/src/axi_err_slv.sv index e7719c429..68b5163ec 100644 --- a/src/axi_err_slv.sv +++ b/src/axi_err_slv.sv @@ -244,7 +244,7 @@ module axi_err_slv #( // pragma translate_off `ifndef VERILATOR - `ifndef XSIM + `ifndef XILINX_SIMULATOR initial begin assert (Resp == axi_pkg::RESP_DECERR || Resp == axi_pkg::RESP_SLVERR) else $fatal(1, "This module may only generate RESP_DECERR or RESP_SLVERR responses!"); diff --git a/src/axi_test.sv b/src/axi_test.sv index 053ac23b3..b3c0a2028 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -1445,7 +1445,11 @@ package axi_test; automatic logic rand_success; wait (b_wait_cnt > 0 && (aw_queue.size() != 0)); aw_beat = aw_queue.pop_front(); - rand_success = b_beat.randomize(); assert(rand_success); +`ifdef XILINX_SIMULATOR + rand_success = std::randomize(b_beat); assert (rand_success); +`else + rand_success = b_beat.randomize(); assert (rand_success); +`endif b_beat.b_id = aw_beat.ax_id; if (RAND_RESP && !aw_beat.ax_atop[axi_pkg::ATOP_R_RESP]) b_beat.b_resp[1] = $random(); diff --git a/src/axi_xbar.sv b/src/axi_xbar.sv index 764084619..4905b66f5 100644 --- a/src/axi_xbar.sv +++ b/src/axi_xbar.sv @@ -125,7 +125,7 @@ import cf_math_pkg::idx_width; // make sure that the default slave does not get changed, if there is an unserved Ax // pragma translate_off `ifndef VERILATOR - `ifndef XSIM + `ifndef XILINX_SIMULATOR default disable iff (~rst_ni); default_aw_mst_port_en: assert property( @(posedge clk_i) (slv_ports_req_i[i].aw_valid && !slv_ports_resp_o[i].aw_ready) @@ -265,7 +265,7 @@ import cf_math_pkg::idx_width; // pragma translate_off `ifndef VERILATOR - `ifndef XSIM + `ifndef XXILINX_SIMULATOR initial begin : check_params id_slv_req_ports: assert ($bits(slv_ports_req_i[0].aw.id ) == Cfg.AxiIdWidthSlvPorts) else $fatal(1, $sformatf("Slv_req and aw_chan id width not equal.")); @@ -283,6 +283,7 @@ endmodule module axi_xbar_intf import cf_math_pkg::idx_width; #( + parameter axi_pkg::xbar_cfg_t Cfg = '0, parameter int unsigned AXI_USER_WIDTH = 0, parameter axi_pkg::xbar_cfg_t Cfg = '0, parameter bit ATOPS = 1'b1, diff --git a/test/tb_axi_dw_pkg.sv b/test/tb_axi_dw_pkg.sv index 9d9bed7fa..d211da2ae 100644 --- a/test/tb_axi_dw_pkg.sv +++ b/test/tb_axi_dw_pkg.sv @@ -413,7 +413,7 @@ package tb_axi_dw_pkg ; axi_b_id = slv_port_axi.b_id; $display("%0tns > Master: Got last B with ID: %b", $time, axi_b_id); - if (this.exp_slv_port_b_queue.empty()) begin + if (this.exp_slv_port_b_queue.is_empty()) begin incr_failed_tests(1) ; $warning("Master: unexpected B beat with ID: %b detected!", axi_b_id); end else begin diff --git a/test/tb_axi_xbar_pkg.sv b/test/tb_axi_xbar_pkg.sv index 77eff888f..fb7098998 100644 --- a/test/tb_axi_xbar_pkg.sv +++ b/test/tb_axi_xbar_pkg.sv @@ -283,7 +283,7 @@ package tb_axi_xbar_pkg; axi_b_id = masters_axi[i].b_id; $display("%0tns > Master %0d: Got last B with id: %b", $time, i, axi_b_id); - if (this.exp_b_queue[i].empty()) begin + if (this.exp_b_queue[i].is_empty()) begin incr_failed_tests(1); $warning("Master %d: unexpected B beat with ID: %b detected!", i, axi_b_id); end else begin @@ -358,7 +358,7 @@ package tb_axi_xbar_pkg; if (slaves_axi[i].ar_valid && slaves_axi[i].ar_ready) begin incr_conducted_tests(1); slv_axi_id = slaves_axi[i].ar_id; - if (this.exp_ar_queue[i].empty()) begin + if (this.exp_ar_queue[i].is_empty()) begin incr_failed_tests(1); end else begin // check that the ids are the same @@ -386,7 +386,7 @@ package tb_axi_xbar_pkg; $display("%0tns > Master %0d: Got last R with id: %b", $time, i, mst_axi_r_id); end - if (this.exp_r_queue[i].empty()) begin + if (this.exp_r_queue[i].is_empty()) begin incr_failed_tests(1); $warning("Master %d: unexpected R beat with ID: %b detected!", i, mst_axi_r_id); end else begin From e22eafb2040fde5ca1b2ad4c045854ff3652afd7 Mon Sep 17 00:00:00 2001 From: Sophana KOK Date: Tue, 3 May 2022 09:28:28 +0200 Subject: [PATCH 029/145] added more benchs and sim targets in fusesoc. more fixes for vivado --- axi.core | 27 +++++++++++++++++++++++++-- test/tb_axi_sim_mem.sv | 12 ++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/axi.core b/axi.core index 89e1290b9..bf964806c 100644 --- a/axi.core +++ b/axi.core @@ -55,12 +55,13 @@ filesets: - src/axi_iw_converter.sv - src/axi_lite_xbar.sv - src/axi_xbar.sv + - src/axi_sim_mem.sv + - src/axi_test.sv file_type : systemVerilogSource depend : - ">=pulp-platform.org::common_cells:1.26.0" benchs: files: - - src/axi_test.sv - test/tb_axi_dw_pkg.sv - test/tb_axi_xbar_pkg.sv - test/axi_synth_bench.sv @@ -142,7 +143,29 @@ generators: targets: default: filesets : [rtl] - sim: + sim: &sim filesets : [rtl,benchs] description: Simulate the design + toplevel: tb_axi_delayer + sim_dw_downsizer: + <<: *sim toplevel: tb_axi_dw_downsizer + sim_addr_test : { filesets : [rtl,benchs] , toplevel: tb_axi_addr_test } + sim_atop_filter : { filesets : [rtl,benchs] , toplevel: tb_axi_atop_filter } + sim_cdc : { filesets : [rtl,benchs] , toplevel: tb_axi_cdc } + sim_delayer : { filesets : [rtl,benchs] , toplevel: tb_axi_delayer } + sim_dw_pkg : { filesets : [rtl,benchs] , toplevel: tb_axi_dw_pkg } + sim_dw_upsizer : { filesets : [rtl,benchs] , toplevel: tb_axi_dw_upsizer } + sim_isolate : { filesets : [rtl,benchs] , toplevel: tb_axi_isolate } + sim_iw_converter : { filesets : [rtl,benchs] , toplevel: tb_axi_iw_converter } + sim_lite_mailbox : { filesets : [rtl,benchs] , toplevel: tb_axi_lite_mailbox } + sim_lite_regs : { filesets : [rtl,benchs] , toplevel: tb_axi_lite_regs } + sim_lite_to_apb : { filesets : [rtl,benchs] , toplevel: tb_axi_lite_to_apb } + sim_lite_to_axi : { filesets : [rtl,benchs] , toplevel: tb_axi_lite_to_axi } + sim_lite_xbar : { filesets : [rtl,benchs] , toplevel: tb_axi_lite_xbar } + sim_modify_address: { filesets : [rtl,benchs] , toplevel: tb_axi_modify_address } + sim_serializer : { filesets : [rtl,benchs] , toplevel: tb_axi_serializer } + sim_sim_mem : { filesets : [rtl,benchs] , toplevel: tb_axi_sim_mem } + sim_to_axi_lite : { filesets : [rtl,benchs] , toplevel: tb_axi_to_axi_lite } + sim_xbar : { filesets : [rtl,benchs] , toplevel: tb_axi_xbar } + sim_xbar_pkg : { filesets : [rtl,benchs] , toplevel: tb_axi_xbar_pkg } diff --git a/test/tb_axi_sim_mem.sv b/test/tb_axi_sim_mem.sv index 35fa7e980..0e5f51822 100644 --- a/test/tb_axi_sim_mem.sv +++ b/test/tb_axi_sim_mem.sv @@ -84,7 +84,11 @@ module tb_axi_sim_mem #( drv.reset_master(); wait (rst_n); // AW - rand_success = aw_beat.randomize(); assert(rand_success); +`ifdef XILINX_SIMULATOR + 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(); @@ -93,7 +97,11 @@ module tb_axi_sim_mem #( drv.send_aw(aw_beat); // W beats for (int unsigned i = 0; i <= aw_beat.ax_len; i++) begin - rand_success = w_beat.randomize(); assert(rand_success); +`ifdef XILINX_SIMULATOR + 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; From 5877aadcad9aca0728782c771bbdcaeafaf19e16 Mon Sep 17 00:00:00 2001 From: Sophana KOK Date: Mon, 29 Aug 2022 18:27:00 +0200 Subject: [PATCH 030/145] added separate fusesoc core file for axi test library --- axi.core | 1 - axi_test.core | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 axi_test.core diff --git a/axi.core b/axi.core index bf964806c..60c9a555f 100644 --- a/axi.core +++ b/axi.core @@ -56,7 +56,6 @@ filesets: - src/axi_lite_xbar.sv - src/axi_xbar.sv - src/axi_sim_mem.sv - - src/axi_test.sv file_type : systemVerilogSource depend : - ">=pulp-platform.org::common_cells:1.26.0" diff --git a/axi_test.core b/axi_test.core new file mode 100644 index 000000000..328ad4805 --- /dev/null +++ b/axi_test.core @@ -0,0 +1,16 @@ +CAPI=2: +# specific core for using axi testing package from outside project +# it must be removed from rtl fileset because it is not synthesisable + +name : pulp-platform.org::axi-test:0.1.dev + +filesets: + bench: + files: + - src/axi_test.sv + file_type : systemVerilogSource + depend : + - ">=pulp-platform.org::common_verification:0.2.1" +targets: + default: + filesets : [bench] From c84de1b983457644e8e30f50e5615d9d5bfdacff Mon Sep 17 00:00:00 2001 From: Sophana KOK Date: Mon, 29 Aug 2022 18:55:38 +0200 Subject: [PATCH 031/145] remove duplicated parameter --- src/axi_xbar.sv | 1 - 1 file changed, 1 deletion(-) diff --git a/src/axi_xbar.sv b/src/axi_xbar.sv index 4905b66f5..98831547b 100644 --- a/src/axi_xbar.sv +++ b/src/axi_xbar.sv @@ -285,7 +285,6 @@ import cf_math_pkg::idx_width; #( parameter axi_pkg::xbar_cfg_t Cfg = '0, parameter int unsigned AXI_USER_WIDTH = 0, - parameter axi_pkg::xbar_cfg_t Cfg = '0, parameter bit ATOPS = 1'b1, parameter bit [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts-1:0] CONNECTIVITY = '1, parameter type rule_t = axi_pkg::xbar_rule_64_t From 2fb9778796eb7cf6bd67661df2cd8a7230a27d21 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Wed, 14 Sep 2022 16:11:02 +0200 Subject: [PATCH 032/145] Rename `XILINX_SIMULATOR` guard back to `XSIM` --- src/axi_demux.sv | 4 ++-- src/axi_err_slv.sv | 2 +- src/axi_test.sv | 11 +++++++++-- src/axi_xbar.sv | 6 +++--- test/tb_axi_sim_mem.sv | 4 ++-- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/axi_demux.sv b/src/axi_demux.sv index aa2e328a4..cae7401eb 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -625,7 +625,7 @@ module axi_demux #( // Validate parameters. // pragma translate_off `ifndef VERILATOR -`ifndef XILINX_SIMULATOR +`ifndef XSIM initial begin: validate_params no_mst_ports: assume (NoMstPorts > 0) else $fatal(1, "The Number of slaves (NoMstPorts) has to be at least 1"); @@ -774,7 +774,7 @@ module axi_demux_id_counters #( // pragma translate_off `ifndef VERILATOR -`ifndef XILINX_SIMULATOR +`ifndef XSIM // Validate parameters. cnt_underflow: assert property( @(posedge clk_i) disable iff (~rst_ni) (pop_en[i] |=> !overflow)) else diff --git a/src/axi_err_slv.sv b/src/axi_err_slv.sv index 68b5163ec..e7719c429 100644 --- a/src/axi_err_slv.sv +++ b/src/axi_err_slv.sv @@ -244,7 +244,7 @@ module axi_err_slv #( // pragma translate_off `ifndef VERILATOR - `ifndef XILINX_SIMULATOR + `ifndef XSIM initial begin assert (Resp == axi_pkg::RESP_DECERR || Resp == axi_pkg::RESP_SLVERR) else $fatal(1, "This module may only generate RESP_DECERR or RESP_SLVERR responses!"); diff --git a/src/axi_test.sv b/src/axi_test.sv index b3c0a2028..9ec5f6005 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -1196,7 +1196,11 @@ package axi_test; automatic logic [AXI_STRB_WIDTH-1:0] rand_strb, strb_mask; addr = axi_pkg::beat_addr(aw_beat.ax_addr, aw_beat.ax_size, aw_beat.ax_len, aw_beat.ax_burst, i); +`ifdef XSIM + rand_success = std::randomize(w_beat); assert (rand_success); +`else rand_success = w_beat.randomize(); assert (rand_success); +`endif // Determine strobe. w_beat.w_strb = '0; n_bytes = 2**aw_beat.ax_size; @@ -1350,8 +1354,11 @@ package axi_test; wait (ar_queue.size > 0); ar_beat = ar_queue.peek(); byte_addr = axi_pkg::aligned_addr(ar_beat.ax_addr, axi_pkg::size_t'($clog2(DW/8))); +`ifdef XSIM rand_success = std::randomize(r_beat); assert(rand_success); +`else rand_success = r_beat.randomize(); assert(rand_success); +`endif if (MAPPED) begin // Either use the actual data, or save the random generated. for (int unsigned i = 0; i < (DW/8); i++) begin @@ -1445,8 +1452,8 @@ package axi_test; automatic logic rand_success; wait (b_wait_cnt > 0 && (aw_queue.size() != 0)); aw_beat = aw_queue.pop_front(); -`ifdef XILINX_SIMULATOR - rand_success = std::randomize(b_beat); assert (rand_success); +`ifdef XSIM + rand_success = std::randomize(b_beat); assert (rand_success); `else rand_success = b_beat.randomize(); assert (rand_success); `endif diff --git a/src/axi_xbar.sv b/src/axi_xbar.sv index 98831547b..764084619 100644 --- a/src/axi_xbar.sv +++ b/src/axi_xbar.sv @@ -125,7 +125,7 @@ import cf_math_pkg::idx_width; // make sure that the default slave does not get changed, if there is an unserved Ax // pragma translate_off `ifndef VERILATOR - `ifndef XILINX_SIMULATOR + `ifndef XSIM default disable iff (~rst_ni); default_aw_mst_port_en: assert property( @(posedge clk_i) (slv_ports_req_i[i].aw_valid && !slv_ports_resp_o[i].aw_ready) @@ -265,7 +265,7 @@ import cf_math_pkg::idx_width; // pragma translate_off `ifndef VERILATOR - `ifndef XXILINX_SIMULATOR + `ifndef XSIM initial begin : check_params id_slv_req_ports: assert ($bits(slv_ports_req_i[0].aw.id ) == Cfg.AxiIdWidthSlvPorts) else $fatal(1, $sformatf("Slv_req and aw_chan id width not equal.")); @@ -283,8 +283,8 @@ endmodule module axi_xbar_intf import cf_math_pkg::idx_width; #( - parameter axi_pkg::xbar_cfg_t Cfg = '0, parameter int unsigned AXI_USER_WIDTH = 0, + parameter axi_pkg::xbar_cfg_t Cfg = '0, parameter bit ATOPS = 1'b1, parameter bit [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts-1:0] CONNECTIVITY = '1, parameter type rule_t = axi_pkg::xbar_rule_64_t diff --git a/test/tb_axi_sim_mem.sv b/test/tb_axi_sim_mem.sv index 0e5f51822..62fcd8527 100644 --- a/test/tb_axi_sim_mem.sv +++ b/test/tb_axi_sim_mem.sv @@ -84,7 +84,7 @@ module tb_axi_sim_mem #( drv.reset_master(); wait (rst_n); // AW -`ifdef XILINX_SIMULATOR +`ifdef XSIM rand_success = std::randomize(aw_beat); assert (rand_success); `else rand_success = aw_beat.randomize(); assert (rand_success); @@ -97,7 +97,7 @@ module tb_axi_sim_mem #( drv.send_aw(aw_beat); // W beats for (int unsigned i = 0; i <= aw_beat.ax_len; i++) begin -`ifdef XILINX_SIMULATOR +`ifdef XSIM rand_success = std::randomize(w_beat); assert (rand_success); `else rand_success = w_beat.randomize(); assert (rand_success); From 4d3b793d44ed524f9b35ed188e716426a3a88f05 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Wed, 14 Sep 2022 16:19:10 +0200 Subject: [PATCH 033/145] Improve XSIM compatibility --- Bender.yml | 2 +- CHANGELOG.md | 1 + axi.core | 2 +- axi_test.core | 2 +- ips_list.yml | 2 +- test/tb_axi_delayer.sv | 4 ++++ 6 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Bender.yml b/Bender.yml index c5047fc33..3b8996b9b 100644 --- a/Bender.yml +++ b/Bender.yml @@ -9,7 +9,7 @@ package: dependencies: common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.26.0 } - common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.0 } + common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.3 } tech_cells_generic: { git: "https://github.com/pulp-platform/tech_cells_generic.git", version: 0.2.2 } export_include_dirs: diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d7899135..d107c6301 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add `axi_dumper` and `axi_dumper_interpret` script to dump log from an AXI bus for debugging purposes. ### Changed +- Improve compatibility with Vivado XSIM ### Fixed diff --git a/axi.core b/axi.core index 60c9a555f..6359ac3e7 100644 --- a/axi.core +++ b/axi.core @@ -86,7 +86,7 @@ filesets: - test/tb_axi_xbar.sv file_type : systemVerilogSource depend : - - ">=pulp-platform.org::common_verification:0.2.1" + - ">=pulp-platform.org::common_verification:0.2.3" generators: axi_intercon_gen: diff --git a/axi_test.core b/axi_test.core index 328ad4805..02dd83d95 100644 --- a/axi_test.core +++ b/axi_test.core @@ -10,7 +10,7 @@ filesets: - src/axi_test.sv file_type : systemVerilogSource depend : - - ">=pulp-platform.org::common_verification:0.2.1" + - ">=pulp-platform.org::common_verification:0.2.3" targets: default: filesets : [bench] diff --git a/ips_list.yml b/ips_list.yml index c7dad06ac..07b712ffc 100644 --- a/ips_list.yml +++ b/ips_list.yml @@ -3,7 +3,7 @@ common_cells: group: pulp-platform common_verification: - commit: v0.2.0 + commit: v0.2.3 group: pulp-platform tech_cells_generic: diff --git a/test/tb_axi_delayer.sv b/test/tb_axi_delayer.sv index 64b4670da..fb343e158 100644 --- a/test/tb_axi_delayer.sv +++ b/test/tb_axi_delayer.sv @@ -83,7 +83,11 @@ module tb_axi_delayer; @(posedge clk); repeat (200) begin @(posedge clk); +`ifdef XSIM + rand_success = std::randomize(ax_beat); assert(rand_success); +`else rand_success = ax_beat.randomize(); assert(rand_success); +`endif axi_master_drv.send_aw(ax_beat); w_beat.w_data = 'hcafebabe; axi_master_drv.send_w(w_beat); From 4904f7e28a8815d0bdd2e156e541a2e9fd4d63ce Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Thu, 15 Sep 2022 10:23:27 +0200 Subject: [PATCH 034/145] Add minimal fuse example to CI --- .gitlab-ci.yml | 10 ++++++++++ axi.core | 1 + axi_test.core | 16 ---------------- 3 files changed, 11 insertions(+), 16 deletions(-) delete mode 100644 axi_test.core diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 979eef5f2..e4df1ed25 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,6 +39,16 @@ synopsys_dc: $CI_PROJECT_DIR/.gitlab-ci.d/memora_retry.sh insert synopsys_dc fi +fuse_xsim: + stage: build + script: + - bender sources + - python3 -m pip install fusesoc + - mkdir fusesoc && cd fusesoc + - fusesoc library add axi .. + - fusesoc core list + - vitis fusesoc run --tool xsim --target sim --no-export pulp-platform.org::axi:$(cat ../VERSION) + .run_vsim: &run_vsim stage: test script: diff --git a/axi.core b/axi.core index 6359ac3e7..7e487dffd 100644 --- a/axi.core +++ b/axi.core @@ -56,6 +56,7 @@ filesets: - src/axi_lite_xbar.sv - src/axi_xbar.sv - src/axi_sim_mem.sv + - src/axi_test.sv file_type : systemVerilogSource depend : - ">=pulp-platform.org::common_cells:1.26.0" diff --git a/axi_test.core b/axi_test.core deleted file mode 100644 index 02dd83d95..000000000 --- a/axi_test.core +++ /dev/null @@ -1,16 +0,0 @@ -CAPI=2: -# specific core for using axi testing package from outside project -# it must be removed from rtl fileset because it is not synthesisable - -name : pulp-platform.org::axi-test:0.1.dev - -filesets: - bench: - files: - - src/axi_test.sv - file_type : systemVerilogSource - depend : - - ">=pulp-platform.org::common_verification:0.2.3" -targets: - default: - filesets : [bench] From 553733ecc481251e6add9fd13ca7224363b69179 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Thu, 15 Sep 2022 11:30:23 +0200 Subject: [PATCH 035/145] Fix CI python version --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e4df1ed25..e6d4d6f48 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -43,11 +43,11 @@ fuse_xsim: stage: build script: - bender sources - - python3 -m pip install fusesoc + - /usr/local/anaconda3/bin/python3 -m pip install fusesoc --user - mkdir fusesoc && cd fusesoc - - fusesoc library add axi .. - - fusesoc core list - - vitis fusesoc run --tool xsim --target sim --no-export pulp-platform.org::axi:$(cat ../VERSION) + - $HOME/.local/bin/fusesoc library add axi .. + - $HOME/.local/bin/fusesoc core list + - vitis $HOME/.local/bin/fusesoc run --tool xsim --target sim --no-export pulp-platform.org::axi:$(cat ../VERSION | sed s/-/./g) .run_vsim: &run_vsim stage: test From 7445627c14a368bf23547ced93f7ab1e82109e87 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Thu, 15 Sep 2022 13:07:23 +0200 Subject: [PATCH 036/145] XSIM: Add comment for differentiating randomize --- src/axi_test.sv | 6 ++++++ test/tb_axi_delayer.sv | 2 ++ test/tb_axi_sim_mem.sv | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/axi_test.sv b/src/axi_test.sv index 9ec5f6005..b23bf68bd 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -1197,6 +1197,8 @@ package axi_test; addr = axi_pkg::beat_addr(aw_beat.ax_addr, aw_beat.ax_size, aw_beat.ax_len, aw_beat.ax_burst, i); `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); @@ -1355,6 +1357,8 @@ package axi_test; ar_beat = ar_queue.peek(); byte_addr = axi_pkg::aligned_addr(ar_beat.ax_addr, axi_pkg::size_t'($clog2(DW/8))); `ifdef XSIM + // std::randomize(r_beat) may behave differently to r_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only rand_success = std::randomize(r_beat); assert(rand_success); `else rand_success = r_beat.randomize(); assert(rand_success); @@ -1453,6 +1457,8 @@ package axi_test; wait (b_wait_cnt > 0 && (aw_queue.size() != 0)); aw_beat = aw_queue.pop_front(); `ifdef XSIM + // std::randomize(b_beat) may behave differently to b_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only rand_success = std::randomize(b_beat); assert (rand_success); `else rand_success = b_beat.randomize(); assert (rand_success); diff --git a/test/tb_axi_delayer.sv b/test/tb_axi_delayer.sv index fb343e158..5b9c15dc4 100644 --- a/test/tb_axi_delayer.sv +++ b/test/tb_axi_delayer.sv @@ -84,6 +84,8 @@ module tb_axi_delayer; repeat (200) begin @(posedge clk); `ifdef XSIM + // std::randomize(ax_beat) may behave differently to ax_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only rand_success = std::randomize(ax_beat); assert(rand_success); `else rand_success = ax_beat.randomize(); assert(rand_success); diff --git a/test/tb_axi_sim_mem.sv b/test/tb_axi_sim_mem.sv index 62fcd8527..28c2e7587 100644 --- a/test/tb_axi_sim_mem.sv +++ b/test/tb_axi_sim_mem.sv @@ -85,6 +85,8 @@ module tb_axi_sim_mem #( 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); @@ -98,6 +100,8 @@ module tb_axi_sim_mem #( // 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); From 9b904be703fb2d9f4776a85b8b2e348c9f8ada8d Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Thu, 15 Sep 2022 14:05:53 +0200 Subject: [PATCH 037/145] Update Changelog, fix vitis version --- .gitlab-ci.yml | 2 +- CHANGELOG.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e6d4d6f48..a7ff3ac81 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -47,7 +47,7 @@ fuse_xsim: - mkdir fusesoc && cd fusesoc - $HOME/.local/bin/fusesoc library add axi .. - $HOME/.local/bin/fusesoc core list - - vitis $HOME/.local/bin/fusesoc run --tool xsim --target sim --no-export pulp-platform.org::axi:$(cat ../VERSION | sed s/-/./g) + - vitis-2022.1-zr $HOME/.local/bin/fusesoc run --tool xsim --target sim --no-export pulp-platform.org::axi:$(cat ../VERSION | sed s/-/./g) .run_vsim: &run_vsim stage: test diff --git a/CHANGELOG.md b/CHANGELOG.md index d107c6301..da3d17a00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Add `axi_dumper` and `axi_dumper_interpret` script to dump log from an AXI bus for debugging purposes. +- Add FuseSoC and Vivado XSIM limited test to CI ### Changed +- Improve compatibility with FuseSoC - Improve compatibility with Vivado XSIM ### Fixed From d4a940f681063d3582be851b3f737af4b287fbae Mon Sep 17 00:00:00 2001 From: Nils Wistoff Date: Fri, 2 Sep 2022 15:15:37 +0200 Subject: [PATCH 038/145] assign: Add macros to flatten axi structs Flat ports are for instance required by Vivado Signed-off-by: Nils Wistoff --- include/axi/assign.svh | 116 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/include/axi/assign.svh b/include/axi/assign.svh index 14bb1944c..ea4f0e4fe 100644 --- a/include/axi/assign.svh +++ b/include/axi/assign.svh @@ -538,4 +538,120 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros for assigning flattened AXI ports to req/resp AXI structs +// Flat AXI ports are required by the Vivado IP Integrator. Vivado naming convention is followed. +// +// Usage Example: +// `AXI_ASSIGN_MASTER_TO_FLAT("my_bus", my_req_struct, my_rsp_struct) +`define AXI_ASSIGN_MASTER_TO_FLAT(pat, req, rsp) \ + assign m_axi_``pat``_awvalid = req.aw_valid; \ + assign m_axi_``pat``_awid = req.aw.id; \ + assign m_axi_``pat``_awaddr = req.aw.addr; \ + assign m_axi_``pat``_awlen = req.aw.len; \ + assign m_axi_``pat``_awsize = req.aw.size; \ + assign m_axi_``pat``_awburst = req.aw.burst; \ + assign m_axi_``pat``_awlock = req.aw.lock; \ + assign m_axi_``pat``_awcache = req.aw.cache; \ + assign m_axi_``pat``_awprot = req.aw.prot; \ + assign m_axi_``pat``_awqos = req.aw.qos; \ + assign m_axi_``pat``_awregion = req.aw.region; \ + // assign m_axi_``pat``_awatop = req.aw.atop; \ + assign m_axi_``pat``_awuser = req.aw.user; \ + \ + assign m_axi_``pat``_wvalid = req.w_valid; \ + assign m_axi_``pat``_wdata = req.w.data; \ + assign m_axi_``pat``_wstrb = req.w.strb; \ + assign m_axi_``pat``_wlast = req.w.last; \ + assign m_axi_``pat``_wuser = req.w.user; \ + \ + assign m_axi_``pat``_bready = req.b_ready; \ + \ + assign m_axi_``pat``_arvalid = req.ar_valid; \ + assign m_axi_``pat``_arid = req.ar.id; \ + assign m_axi_``pat``_araddr = req.ar.addr; \ + assign m_axi_``pat``_arlen = req.ar.len; \ + assign m_axi_``pat``_arsize = req.ar.size; \ + assign m_axi_``pat``_arburst = req.ar.burst; \ + assign m_axi_``pat``_arlock = req.ar.lock; \ + assign m_axi_``pat``_arcache = req.ar.cache; \ + assign m_axi_``pat``_arprot = req.ar.prot; \ + assign m_axi_``pat``_arqos = req.ar.qos; \ + assign m_axi_``pat``_arregion = req.ar.region; \ + assign m_axi_``pat``_aruser = req.ar.user; \ + \ + assign m_axi_``pat``_rready = req.r_ready; \ + \ + assign rsp.aw_ready = m_axi_``pat``_awready; \ + assign rsp.ar_ready = m_axi_``pat``_arready; \ + assign rsp.w_ready = m_axi_``pat``_wready; \ + \ + assign rsp.b_valid = m_axi_``pat``_bvalid; \ + assign rsp.b.id = m_axi_``pat``_bid; \ + assign rsp.b.resp = m_axi_``pat``_bresp; \ + assign rsp.b.user = m_axi_``pat``_buser; \ + \ + assign rsp.r_valid = m_axi_``pat``_rvalid; \ + assign rsp.r.id = m_axi_``pat``_rid; \ + assign rsp.r.data = m_axi_``pat``_rdata; \ + assign rsp.r.resp = m_axi_``pat``_rresp; \ + assign rsp.r.last = m_axi_``pat``_rlast; \ + assign rsp.r.user = m_axi_``pat``_ruser; + +`define AXI_ASSIGN_SLAVE_TO_FLAT(pat, req, rsp) \ + assign req.aw_valid = s_axi_``pat``_awvalid; \ + assign req.aw.id = s_axi_``pat``_awid; \ + assign req.aw.addr = s_axi_``pat``_awaddr; \ + assign req.aw.len = s_axi_``pat``_awlen; \ + assign req.aw.size = s_axi_``pat``_awsize; \ + assign req.aw.burst = s_axi_``pat``_awburst; \ + assign req.aw.lock = s_axi_``pat``_awlock; \ + assign req.aw.cache = s_axi_``pat``_awcache; \ + assign req.aw.prot = s_axi_``pat``_awprot; \ + assign req.aw.qos = s_axi_``pat``_awqos; \ + assign req.aw.region = s_axi_``pat``_awregion; \ + assign req.aw.atop = '0; \ + assign req.aw.user = s_axi_``pat``_awuser; \ + \ + assign req.w_valid = s_axi_``pat``_wvalid; \ + assign req.w.data = s_axi_``pat``_wdata; \ + assign req.w.strb = s_axi_``pat``_wstrb; \ + assign req.w.last = s_axi_``pat``_wlast; \ + assign req.w.user = s_axi_``pat``_wuser; \ + \ + assign req.b_ready = s_axi_``pat``_bready; \ + \ + assign req.ar_valid = s_axi_``pat``_arvalid; \ + assign req.ar.id = s_axi_``pat``_arid; \ + assign req.ar.addr = s_axi_``pat``_araddr; \ + assign req.ar.len = s_axi_``pat``_arlen; \ + assign req.ar.size = s_axi_``pat``_arsize; \ + assign req.ar.burst = s_axi_``pat``_arburst; \ + assign req.ar.lock = s_axi_``pat``_arlock; \ + assign req.ar.cache = s_axi_``pat``_arcache; \ + assign req.ar.prot = s_axi_``pat``_arprot; \ + assign req.ar.qos = s_axi_``pat``_arqos; \ + assign req.ar.region = s_axi_``pat``_arregion; \ + assign req.ar.user = s_axi_``pat``_aruser; \ + \ + assign req.r_ready = s_axi_``pat``_rready; \ + \ + assign s_axi_``pat``_awready = rsp.aw_ready; \ + assign s_axi_``pat``_arready = rsp.ar_ready; \ + assign s_axi_``pat``_wready = rsp.w_ready; \ + \ + assign s_axi_``pat``_bvalid = rsp.b_valid; \ + assign s_axi_``pat``_bid = rsp.b.id; \ + assign s_axi_``pat``_bresp = rsp.b.resp; \ + assign s_axi_``pat``_buser = rsp.b.user; \ + \ + assign s_axi_``pat``_rvalid = rsp.r_valid; \ + assign s_axi_``pat``_rid = rsp.r.id; \ + assign s_axi_``pat``_rdata = rsp.r.data; \ + assign s_axi_``pat``_rresp = rsp.r.resp; \ + assign s_axi_``pat``_rlast = rsp.r.last; \ + assign s_axi_``pat``_ruser = rsp.r.user; +//////////////////////////////////////////////////////////////////////////////////////////////////// + + `endif From 88d52b1ba132c69b28af34f709d88c0494457414 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Tue, 27 Sep 2022 16:18:11 +0200 Subject: [PATCH 039/145] assign: Remove atops from flat assigns --- include/axi/assign.svh | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/axi/assign.svh b/include/axi/assign.svh index ea4f0e4fe..37fdec958 100644 --- a/include/axi/assign.svh +++ b/include/axi/assign.svh @@ -556,7 +556,6 @@ assign m_axi_``pat``_awprot = req.aw.prot; \ assign m_axi_``pat``_awqos = req.aw.qos; \ assign m_axi_``pat``_awregion = req.aw.region; \ - // assign m_axi_``pat``_awatop = req.aw.atop; \ assign m_axi_``pat``_awuser = req.aw.user; \ \ assign m_axi_``pat``_wvalid = req.w_valid; \ @@ -610,7 +609,6 @@ assign req.aw.prot = s_axi_``pat``_awprot; \ assign req.aw.qos = s_axi_``pat``_awqos; \ assign req.aw.region = s_axi_``pat``_awregion; \ - assign req.aw.atop = '0; \ assign req.aw.user = s_axi_``pat``_awuser; \ \ assign req.w_valid = s_axi_``pat``_wvalid; \ From 9b1a0827123a787c6dcab89bde07dd95bdbc8e0c Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Tue, 27 Sep 2022 16:19:47 +0200 Subject: [PATCH 040/145] assign: Change changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da3d17a00..0f83aa159 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Add `axi_dumper` and `axi_dumper_interpret` script to dump log from an AXI bus for debugging purposes. - Add FuseSoC and Vivado XSIM limited test to CI +- `assign.svh`: Add macros to assign flat buses using the Vivado naming style. ### Changed - Improve compatibility with FuseSoC From d96fb65776ccca45ff859a2e7a60f8ad4fba55fb Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Mon, 29 Aug 2022 11:04:49 +0200 Subject: [PATCH 041/145] axi_lite_lfsr: Add an AXI4 and AXI4 Lite LFSR --- Bender.yml | 2 + CHANGELOG.md | 1 + README.md | 2 + axi.core | 2 + src/axi_lfsr.sv | 121 +++++++++++++++++++++++ src/axi_lite_lfsr.sv | 223 +++++++++++++++++++++++++++++++++++++++++++ src_files.yml | 2 + 7 files changed, 353 insertions(+) create mode 100644 src/axi_lfsr.sv create mode 100644 src/axi_lite_lfsr.sv diff --git a/Bender.yml b/Bender.yml index 3b8996b9b..a695fb8a4 100644 --- a/Bender.yml +++ b/Bender.yml @@ -40,6 +40,7 @@ sources: - src/axi_join.sv - src/axi_lite_demux.sv - src/axi_lite_join.sv + - src/axi_lite_lfsr.sv - src/axi_lite_mailbox.sv - src/axi_lite_mux.sv - src/axi_lite_regs.sv @@ -55,6 +56,7 @@ sources: - src/axi_err_slv.sv - src/axi_dw_converter.sv - src/axi_id_serialize.sv + - src/axi_lfsr.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv - src/axi_to_mem_banked.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f83aa159..4f2faa32a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add `axi_dumper` and `axi_dumper_interpret` script to dump log from an AXI bus for debugging purposes. - Add FuseSoC and Vivado XSIM limited test to CI - `assign.svh`: Add macros to assign flat buses using the Vivado naming style. +- `axi_lfsr` and `axi_lite_lfsr`: Add AXI4 and AXI4 Lite LFSR Subordinate devices. ### Changed - Improve compatibility with FuseSoC diff --git a/README.md b/README.md index 5daef26f8..ed678dfba 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,10 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_isolate`](src/axi_isolate.sv) | A module that can isolate downstream slaves from receiving new AXI4 transactions. | | | [`axi_iw_converter`](src/axi_iw_converter.sv) | Convert between any two AXI ID widths. | [Doc][doc.axi_iw_converter] | | [`axi_join`](src/axi_join.sv) | A connector that joins two AXI interfaces. | | +| [`axi_lfsr`](src/axi_lfsr.sv) | AXI4-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | | [`axi_lite_demux`](src/axi_lite_demux.sv) | Demultiplexes an AXI4-Lite bus from one slave port to multiple master ports. | [Doc](doc/axi_lite_demux.md) | | [`axi_lite_join`](src/axi_lite_join.sv) | A connector that joins two AXI-Lite interfaces. | | +| [`axi_lite_lfsr`](src/axi_lite_lfsr.sv) | AXI4-Lite-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | | [`axi_lite_mailbox`](src/axi_lite_mailbox.sv) | A AXI4-Lite Mailbox with two slave ports and usage triggered irq. | [Doc](doc/axi_lite_mailbox.md) | | [`axi_lite_mux`](src/axi_lite_mux.sv) | Multiplexes AXI4-Lite slave ports down to one master port. | [Doc](doc/axi_lite_mux.md) | | [`axi_lite_regs`](src/axi_lite_regs.sv) | AXI4-Lite registers with optional read-only and protection features. | [Doc][doc.axi_lite_regs] | diff --git a/axi.core b/axi.core index 7e487dffd..3282dbee3 100644 --- a/axi.core +++ b/axi.core @@ -31,6 +31,7 @@ filesets: - src/axi_join.sv - src/axi_lite_demux.sv - src/axi_lite_join.sv + - src/axi_lite_lfsr.sv - src/axi_lite_mailbox.sv - src/axi_lite_mux.sv - src/axi_lite_regs.sv @@ -46,6 +47,7 @@ filesets: - src/axi_err_slv.sv - src/axi_dw_converter.sv - src/axi_id_serialize.sv + - src/axi_lfsr.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv - src/axi_to_mem_banked.sv diff --git a/src/axi_lfsr.sv b/src/axi_lfsr.sv new file mode 100644 index 000000000..2085a76fe --- /dev/null +++ b/src/axi_lfsr.sv @@ -0,0 +1,121 @@ +// Copyright 2022 ETH Zurich and 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 + +`include "axi/typedef.svh" + +/// AXI4 LFSR Subordinate device. Responds with a pseudo random answer. Serial interface to +/// set the internal state. +module axi_lfsr #( + /// AXI4 Data Width + parameter int unsigned DataWidth = 32'd0, + /// AXI4 Addr Width + parameter int unsigned AddrWidth = 32'd0, + /// AXI4 Id Width + parameter int unsigned IdWidth = 32'd0, + /// AXI4 User Width + parameter int unsigned UserWidth = 32'd0, + /// AXI4 request struct definition + parameter type axi_req_t = logic, + /// AXI4 response struct definition + parameter type axi_rsp_t = logic +)( + /// Rising-edge clock + input logic clk_i, + /// Active-low reset + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4 request struct + input axi_req_t req_i, + /// AXI4 response struct + output axi_rsp_t rsp_o, + /// Serial shift data in (write) + input logic w_ser_data_i, + /// Serial shift data out (write) + output logic w_ser_data_o, + /// Serial shift enable (write) + input logic w_ser_en_i, + /// Serial shift data in (read) + input logic r_ser_data_i, + /// Serial shift data out (read) + output logic r_ser_data_o, + /// Serial shift enable (read) + input logic r_ser_en_i +); + + /// AXI4 Strobe Width + localparam int unsigned StrbWidth = DataWidth / 8; + + /// Address Type + typedef logic [AddrWidth-1:0] addr_t; + /// Data type + typedef logic [DataWidth-1:0] data_t; + /// Strobe Type + typedef logic [StrbWidth-1:0] strb_t; + + // AXI Lite typedef + `AXI_LITE_TYPEDEF_AW_CHAN_T(axi_lite_aw_chan_t, addr_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(axi_lite_w_chan_t, data_t, strb_t) + `AXI_LITE_TYPEDEF_B_CHAN_T(axi_lite_b_chan_t) + + `AXI_LITE_TYPEDEF_AR_CHAN_T(axi_lite_ar_chan_t, addr_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(axi_lite_r_chan_t, data_t) + + `AXI_LITE_TYPEDEF_REQ_T(axi_lite_req_t, axi_lite_aw_chan_t, axi_lite_w_chan_t, axi_lite_ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_lite_rsp_t, axi_lite_b_chan_t, axi_lite_r_chan_t) + + // AXI Lite buses + axi_lite_req_t axi_lite_req; + axi_lite_rsp_t axi_lite_rsp; + + axi_to_axi_lite #( + .AxiAddrWidth ( AddrWidth ), + .AxiDataWidth ( DataWidth ), + .AxiIdWidth ( IdWidth ), + .AxiUserWidth ( UserWidth ), + .AxiMaxWriteTxns ( 'd2 ), // We only have 1 cycle latency; 2 is enough + .AxiMaxReadTxns ( 'd2 ), // We only have 1 cycle latency; 2 is enough + .FallThrough ( 1'b0 ), + .full_req_t ( axi_req_t ), + .full_resp_t ( axi_rsp_t ), + .lite_req_t ( axi_lite_req_t ), + .lite_resp_t ( axi_lite_rsp_t ) + ) i_axi_to_axi_lite ( + .clk_i, + .rst_ni, + .test_i ( testmode_i ), + .slv_req_i ( req_i ), + .slv_resp_o ( rsp_o ), + .mst_req_o ( axi_lite_req ), + .mst_resp_i ( axi_lite_rsp ) + ); + + axi_lite_lfsr #( + .DataWidth ( DataWidth ), + .axi_lite_req_t ( axi_lite_req_t ), + .axi_lite_rsp_t ( axi_lite_rsp_t ) + ) i_axi_lite_lfsr ( + .clk_i, + .rst_ni, + .testmode_i, + .w_ser_data_i, + .w_ser_data_o, + .w_ser_en_i, + .r_ser_data_i, + .r_ser_data_o, + .r_ser_en_i, + .req_i ( axi_lite_req ), + .rsp_o ( axi_lite_rsp ) + ); + +endmodule : axi_lfsr diff --git a/src/axi_lite_lfsr.sv b/src/axi_lite_lfsr.sv new file mode 100644 index 000000000..bf8e0f5e5 --- /dev/null +++ b/src/axi_lite_lfsr.sv @@ -0,0 +1,223 @@ +// Copyright 2022 ETH Zurich and 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 + +`include "common_cells/registers.svh" + +/// AXI4 Lite LFSR Subordinate device. Responds with a pseudo random answer. Serial interface to +/// set the internal state. +module axi_lite_lfsr #( + /// AXI4 Lite Data Width + parameter int unsigned DataWidth = 32'd0, + /// AXI4 Lite request struct definition + parameter type axi_lite_req_t = logic, + /// AXI4 Lite response struct definition + parameter type axi_lite_rsp_t = logic +)( + /// Rising-edge clock + input logic clk_i, + /// Active-low reset + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4 Lite request struct + input axi_lite_req_t req_i, + /// AXI4 Lite response struct + output axi_lite_rsp_t rsp_o, + /// Serial shift data in (write) + input logic w_ser_data_i, + /// Serial shift data out (write) + output logic w_ser_data_o, + /// Serial shift enable (write) + input logic w_ser_en_i, + /// Serial shift data in (read) + input logic r_ser_data_i, + /// Serial shift data out (read) + output logic r_ser_data_o, + /// Serial shift enable (read) + input logic r_ser_en_i +); + + /// AXI4 Strobe Width + localparam int unsigned StrbWidth = DataWidth / 8; + + logic w_lfsr_en; + logic r_lfsr_en; + + logic w_b_fifo_ready; + + // LFSR outputs + logic [DataWidth-1:0] w_data_in, w_data_out; + + // AW (ignored) + assign rsp_o.aw_ready = !w_ser_en_i; + + // W + axi_opt_lfsr #( + .Width ( DataWidth ) + ) i_axi_opt_lfsr_w ( + .clk_i, + .rst_ni, + .en_i ( w_lfsr_en ), + .ser_data_i ( w_ser_data_i ), + .ser_data_o ( w_ser_data_o ), + .ser_en_i ( w_ser_en_i ), + .inp_en_i ( w_lfsr_en ), + .data_i ( w_data_in ), + .data_o ( w_data_out ) + ); + assign w_lfsr_en = req_i.w_valid & rsp_o.w_ready; + assign rsp_o.w_ready = !w_ser_en_i & w_b_fifo_ready; + + // only write bytes with strobe signal enabled + always_comb begin : gen_data_strb_connect + for (int unsigned i = 0; i < StrbWidth; i++) begin : gen_strb_en + if (req_i.w.strb[i] == 1'b0) begin + w_data_in[i*8+:8] = w_data_out[i*8+:8]; + end else if (req_i.w.strb[i] == 1'b1) begin + w_data_in[i*8+:8] = req_i.w.data[i*8+:8]; + end else begin + w_data_in[i*8+:8] = 'x; + end + end + end + + // B + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 'd1 ), + .DEPTH ( 'd2 ) + ) i_stream_fifo_w_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( 1'b0 ), + .valid_i ( req_i.w_valid ), + .ready_o ( w_b_fifo_ready ), + .data_o ( /* NOT CONNECTED */ ), + .valid_o ( w_b_fifo_valid ), + .ready_i ( req_i.b_ready ) + ); + assign rsp_o.b.resp = axi_pkg::RESP_OKAY; + assign rsp_o.b_valid = w_b_fifo_valid; + + // AR (ignored) + assign rsp_o.ar_ready = !w_ser_en_i; + + // R + axi_opt_lfsr #( + .Width ( DataWidth ) + ) i_axi_opt_lfsr_r ( + .clk_i, + .rst_ni, + .en_i ( r_lfsr_en ), + .ser_data_i ( r_ser_data_i ), + .ser_data_o ( r_ser_data_o ), + .ser_en_i ( r_ser_en_i ), + .inp_en_i ( 1'b0 ), + .data_i ( /* NOT CONNECTED */ ), + .data_o ( rsp_o.r.data ) + ); + assign rsp_o.r.resp = axi_pkg::RESP_OKAY; + assign r_lfsr_en = req_i.r_ready & rsp_o.r_valid; + assign rsp_o.r_valid = !r_ser_en_i; + +endmodule : axi_lite_lfsr + + +/// XOR LFSR with tabs based on the [lfsr_table](https://datacipy.cz/lfsr_table.pdf). LFSR has +/// a serial interface to set the initial state +module axi_opt_lfsr #( + parameter int unsigned Width = 32'd0 +) ( + /// Rising-edge clock + input logic clk_i, + /// Active-low reset + input logic rst_ni, + input logic en_i, + input logic ser_data_i, + output logic ser_data_o, + input logic ser_en_i, + input logic inp_en_i, + input logic [Width-1:0] data_i, + output logic [Width-1:0] data_o +); + + /// Number of bits required to hold the LFSR tab configuration + localparam int unsigned LfsrIdxWidth = cf_math_pkg::idx_width(Width); + /// Maximum number of tabs + localparam int unsigned MaxNumTabs = 4; + + /// Type specifying the tap positions + typedef logic [LfsrIdxWidth:0] xnor_entry_t [MaxNumTabs-1:0]; + xnor_entry_t XnorFeedback; + + // the shift register + logic [Width-1:0] reg_d, reg_q; + + // the feedback signal + logic xnor_feedback; + + always_comb begin : gen_register + + // get the parameters + case (Width) + 'd8 : XnorFeedback = { 'd8, 'd6, 'd5, 'd4 }; + 'd16 : XnorFeedback = { 'd16, 'd14, 'd13, 'd11 }; + 'd32 : XnorFeedback = { 'd32, 'd30, 'd26, 'd25 }; + 'd64 : XnorFeedback = { 'd64, 'd63, 'd61, 'd60 }; + 'd128 : XnorFeedback = { 'd128, 'd127, 'd126, 'd119 }; + 'd256 : XnorFeedback = { 'd256, 'd256, 'd521, 'd246 }; + 'd512 : XnorFeedback = { 'd512, 'd510, 'd507, 'd504 }; + 'd1024 : XnorFeedback = { 'd1024, 'd1015, 'd1002, 'd1001 }; + default : XnorFeedback = { 'x, 'x, 'x, 'x }; + endcase + + // shift register functionality + // compression mode + if (inp_en_i) begin + for (int unsigned i = 0; i < Width - 1; i++) begin : gen_comp_conection + reg_d[i] = reg_q[i+1] ^ data_i[i]; + end + // generation mode + end else begin + for (int unsigned i = 0; i < Width - 1; i++) begin : gen_gen_conection + reg_d[i] = reg_q[i+1]; + end + end + // serial access mode + if (ser_en_i) begin + // new head element + reg_d[Width-1] = ser_data_i; + // LFSR mode + end else begin + xnor_feedback = reg_q[XnorFeedback[MaxNumTabs-1]-1]; + for (int unsigned t = 0; t < MaxNumTabs - 1; t++) begin : gen_feedback_path + xnor_feedback = xnor_feedback; + if (XnorFeedback[t] != 0) begin + xnor_feedback = xnor_feedback ^ reg_q[XnorFeedback[t]-1]; + end + end + reg_d[Width-1] = inp_en_i ? xnor_feedback ^ data_i[Width-1] : xnor_feedback; + end + end + + // connect outputs + assign ser_data_o = reg_q[0]; + assign data_o = reg_q; + + // state + `FFL(reg_q, reg_d, en_i | ser_en_i, '1, clk_i, rst_ni) + +endmodule : axi_opt_lfsr diff --git a/src_files.yml b/src_files.yml index a5bb04f88..23ec58c3f 100644 --- a/src_files.yml +++ b/src_files.yml @@ -30,6 +30,7 @@ axi: - src/axi_join.sv - src/axi_lite_demux.sv - src/axi_lite_join.sv + - src/axi_lite_lfsr.sv - src/axi_lite_mailbox.sv - src/axi_lite_mux.sv - src/axi_lite_regs.sv @@ -45,6 +46,7 @@ axi: - src/axi_err_slv.sv - src/axi_dw_converter.sv - src/axi_id_serialize.sv + - src/axi_lfsr.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv - src/axi_to_mem_banked.sv From bba477dc6323c52f6d62628db70ee38b212491e6 Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Sun, 24 May 2020 17:17:48 +0200 Subject: [PATCH 042/145] Add initial implementation of AXI crosspoint (`axi_xp`) --- Bender.yml | 2 + src/axi_xp.sv | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 src/axi_xp.sv diff --git a/Bender.yml b/Bender.yml index a695fb8a4..97b2d9478 100644 --- a/Bender.yml +++ b/Bender.yml @@ -66,6 +66,8 @@ sources: - src/axi_iw_converter.sv - src/axi_lite_xbar.sv - src/axi_xbar.sv + # Level 5 + - src/axi_xp.sv - target: synth_test files: diff --git a/src/axi_xp.sv b/src/axi_xp.sv new file mode 100644 index 000000000..616544297 --- /dev/null +++ b/src/axi_xp.sv @@ -0,0 +1,169 @@ +// Copyright (c) 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: +// - Andreas Kurth + +`include "axi/typedef.svh" + +/// AXI Crosspoint (XP) with homomorphous slave and master ports. +module axi_xp #( + /// Number of slave ports. + parameter int unsigned NumSlvPorts = 32'd0, + /// Number of master ports. + parameter int unsigned NumMstPorts = 32'd0, + /// Address width of all ports. + parameter int unsigned AxiAddrWidth = 32'd0, + /// Data width of all ports. + parameter int unsigned AxiDataWidth = 32'd0, + /// ID width of all ports. + parameter int unsigned AxiIdWidth = 32'd0, + /// User signal width of all ports. + parameter int unsigned AxiUserWidth = 32'd0, + /// Maximum number of different IDs that can be in flight at each slave port. Reads and writes + /// are counted separately (except for ATOPs, which count as both read and write). + /// + /// It is legal for upstream to have transactions with more unique IDs than the maximum given by + /// this parameter in flight, but a transaction exceeding the maximum will be stalled until all + /// transactions of another ID complete. + parameter int unsigned AxiSlvPortMaxUniqIds = 32'd0, + /// Maximum number of in-flight write transactions at each slave port. + /// + /// It is legal for upstream to have more write transactions than the maximum given by this + /// parameter in flight, but a write exceeding the maximum will be stalled until the next write + /// at the same slave port completes. + parameter int unsigned AxiSlvPortMaxWriteTxns = 32'd0, + /// Maximum number of in-flight transactions with the same ID. + /// + /// It is legal for upstream to have more transactions than the maximum given by this parameter in + /// flight for any ID, but a transaction exceeding the maximum will be stalled until another + /// transaction with the same ID completes. + parameter int unsigned AxiMaxTxnsPerId = 32'd0, + /// Number of rules in the address map. + parameter int unsigned NumAddrRules = 32'd0, + /// Request struct type of the AXI4+ATOP slave port + parameter type slv_req_t = logic, + /// Response struct type of the AXI4+ATOP slave port + parameter type slv_resp_t = logic, + /// Request struct type of the AXI4+ATOP master port + parameter type mst_req_t = logic, + /// Response struct type of the AXI4+ATOP master port + parameter type mst_resp_t = logic, + /// Rule type (see documentation of `axi_xbar` for details). + parameter type rule_t = axi_pkg::xbar_rule_64_t +) ( + /// Rising-edge clock of all ports + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Test mode enable + input logic test_en_i, + /// Slave ports request + input slv_req_t [NumSlvPorts-1:0] slv_req_i, + /// Slave ports response + output slv_resp_t [NumSlvPorts-1:0] slv_resp_o, + /// Master ports request + output mst_req_t [NumMstPorts-1:0] mst_req_o, + /// Master ports response + input mst_resp_t [NumMstPorts-1:0] mst_resp_i, + /// Address map for transferring transactions from slave to master ports + input rule_t [NumAddrRules-1:0] addr_map_i +); + + parameter int unsigned AxiXbarIdWidth = AxiIdWidth + $clog2(NumSlvPorts); + typedef logic [AxiAddrWidth-1:0] addr_t; + typedef logic [AxiDataWidth-1:0] data_t; + typedef logic [AxiIdWidth-1:0] id_t; + typedef logic [AxiXbarIdWidth-1:0] xbar_id_t; + typedef logic [AxiDataWidth/8-1:0] strb_t; + typedef logic [AxiUserWidth-1:0] user_t; + + `AXI_TYPEDEF_AW_CHAN_T(aw_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_AW_CHAN_T(xbar_aw_t, addr_t, xbar_id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_t, id_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(xbar_b_t, xbar_id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(xbar_ar_t, addr_t, xbar_id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_t, data_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(xbar_r_t, data_t, xbar_id_t, user_t) + `AXI_TYPEDEF_REQ_T(req_t, aw_t, w_t, ar_t) + `AXI_TYPEDEF_REQ_T(xbar_req_t, xbar_aw_t, w_t, xbar_ar_t) + `AXI_TYPEDEF_RESP_T(resp_t, b_t, r_t) + `AXI_TYPEDEF_RESP_T(xbar_resp_t, xbar_b_t, xbar_r_t) + + xbar_req_t [NumMstPorts-1:0] xbar_req; + xbar_resp_t [NumMstPorts-1:0] xbar_resp; + + localparam axi_pkg::xbar_cfg_t xbar_cfg = '{ + NoSlvPorts: NumSlvPorts, + NoMstPorts: NumMstPorts, + MaxMstTrans: AxiMaxTxnsPerId, + MaxSlvTrans: AxiSlvPortMaxWriteTxns, + FallThrough: 1'b0, + LatencyMode: axi_pkg::CUT_ALL_PORTS, + AxiIdWidthSlvPorts: AxiIdWidth, + AxiIdUsedSlvPorts: AxiIdWidth, + AxiAddrWidth: AxiAddrWidth, + AxiDataWidth: AxiDataWidth, + NoAddrRules: NumAddrRules + }; + + axi_xbar #( + .Cfg ( xbar_cfg ), + .slv_aw_chan_t ( aw_t ), + .mst_aw_chan_t ( xbar_aw_t ), + .w_chan_t ( w_t ), + .slv_b_chan_t ( b_t ), + .mst_b_chan_t ( xbar_b_t ), + .slv_ar_chan_t ( ar_t ), + .mst_ar_chan_t ( xbar_ar_t ), + .slv_r_chan_t ( r_t ), + .mst_r_chan_t ( xbar_r_t ), + .slv_req_t ( req_t ), + .slv_resp_t ( resp_t ), + .mst_req_t ( xbar_req_t ), + .mst_resp_t ( xbar_resp_t ), + .rule_t ( rule_t ) + ) i_xbar ( + .clk_i, + .rst_ni, + .test_i ( test_en_i ), + .slv_ports_req_i ( slv_req_i ), + .slv_ports_resp_o ( slv_resp_o ), + .mst_ports_req_o ( xbar_req ), + .mst_ports_resp_i ( xbar_resp ), + .addr_map_i, + .en_default_mst_port_i ( {NumSlvPorts{1'b0}} ), + .default_mst_port_i ( {NumSlvPorts{{$clog2(NumMstPorts){1'b0}}}} ) + ); + + for (genvar i = 0; i < NumMstPorts; i++) begin : gen_remap + axi_id_remap #( + .AxiSlvPortIdWidth ( AxiXbarIdWidth ), + .AxiSlvPortMaxUniqIds ( AxiSlvPortMaxUniqIds ), + .AxiMaxTxnsPerId ( AxiMaxTxnsPerId ), + .AxiMstPortIdWidth ( AxiIdWidth ), + .slv_req_t ( xbar_req_t ), + .slv_resp_t ( xbar_resp_t ), + .mst_req_t ( req_t ), + .mst_resp_t ( resp_t ) + ) i_axi_id_remap ( + .clk_i, + .rst_ni, + .slv_req_i ( xbar_req[i] ), + .slv_resp_o ( xbar_resp[i] ), + .mst_req_o ( mst_req_o[i] ), + .mst_resp_i ( mst_resp_i[i] ) + ); + end + +endmodule From 61caa27c425e38099a90dd249587bc0a7eec2c5a Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Fri, 19 Jun 2020 17:00:23 +0200 Subject: [PATCH 043/145] axi_xp: Add connectivity parameter --- src/axi_xp.sv | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/axi_xp.sv b/src/axi_xp.sv index 616544297..6e0a87d9d 100644 --- a/src/axi_xp.sv +++ b/src/axi_xp.sv @@ -20,6 +20,10 @@ module axi_xp #( parameter int unsigned NumSlvPorts = 32'd0, /// Number of master ports. parameter int unsigned NumMstPorts = 32'd0, + /// Connectivity from a slave port to the master ports. A `1'b1` in `Connectivity[i][j]` means + /// that slave port `i` is connected to master port `j`. By default, all slave ports are + /// connected to all master ports. + parameter bit [NumSlvPorts-1:0][NumMstPorts-1:0] Connectivity = '1, /// Address width of all ports. parameter int unsigned AxiAddrWidth = 32'd0, /// Data width of all ports. @@ -118,21 +122,22 @@ module axi_xp #( }; axi_xbar #( - .Cfg ( xbar_cfg ), - .slv_aw_chan_t ( aw_t ), - .mst_aw_chan_t ( xbar_aw_t ), - .w_chan_t ( w_t ), - .slv_b_chan_t ( b_t ), - .mst_b_chan_t ( xbar_b_t ), - .slv_ar_chan_t ( ar_t ), - .mst_ar_chan_t ( xbar_ar_t ), - .slv_r_chan_t ( r_t ), - .mst_r_chan_t ( xbar_r_t ), - .slv_req_t ( req_t ), - .slv_resp_t ( resp_t ), - .mst_req_t ( xbar_req_t ), - .mst_resp_t ( xbar_resp_t ), - .rule_t ( rule_t ) + .Cfg ( xbar_cfg ), + .Connectivity ( Connectivity ), + .slv_aw_chan_t ( aw_t ), + .mst_aw_chan_t ( xbar_aw_t ), + .w_chan_t ( w_t ), + .slv_b_chan_t ( b_t ), + .mst_b_chan_t ( xbar_b_t ), + .slv_ar_chan_t ( ar_t ), + .mst_ar_chan_t ( xbar_ar_t ), + .slv_r_chan_t ( r_t ), + .mst_r_chan_t ( xbar_r_t ), + .slv_req_t ( req_t ), + .slv_resp_t ( resp_t ), + .mst_req_t ( xbar_req_t ), + .mst_resp_t ( xbar_resp_t ), + .rule_t ( rule_t ) ) i_xbar ( .clk_i, .rst_ni, From 3f7ea9d8e019feba574de2ef35a88e719c9b2983 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Wed, 10 Aug 2022 11:28:14 +0200 Subject: [PATCH 044/145] axi_xp: Update Changelog --- CHANGELOG.md | 1 + axi.core | 2 ++ src_files.yml | 2 ++ 3 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f2faa32a..518a0a23f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add FuseSoC and Vivado XSIM limited test to CI - `assign.svh`: Add macros to assign flat buses using the Vivado naming style. - `axi_lfsr` and `axi_lite_lfsr`: Add AXI4 and AXI4 Lite LFSR Subordinate devices. +- `axi_xp`: Crosspoint with homomorphous slave and master ports. ### Changed - Improve compatibility with FuseSoC diff --git a/axi.core b/axi.core index 3282dbee3..64c8ea524 100644 --- a/axi.core +++ b/axi.core @@ -59,6 +59,8 @@ filesets: - src/axi_xbar.sv - src/axi_sim_mem.sv - src/axi_test.sv + # Level 5 + - src/axi_xp.sv file_type : systemVerilogSource depend : - ">=pulp-platform.org::common_cells:1.26.0" diff --git a/src_files.yml b/src_files.yml index 23ec58c3f..5fc0c4ced 100644 --- a/src_files.yml +++ b/src_files.yml @@ -56,6 +56,8 @@ axi: - src/axi_iw_converter.sv - src/axi_lite_xbar.sv - src/axi_xbar.sv + # Level 5 + - src/axi_xp.sv axi_sim: files: From 31aa3e4b2e36579e61caf0c628276b1853d93a11 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Wed, 10 Aug 2022 11:35:03 +0200 Subject: [PATCH 045/145] axi_xp: Add ATOPs parameter for xbar --- src/axi_xp.sv | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/axi_xp.sv b/src/axi_xp.sv index 6e0a87d9d..a9ab24e5d 100644 --- a/src/axi_xp.sv +++ b/src/axi_xp.sv @@ -11,11 +11,14 @@ // // Authors: // - Andreas Kurth +// - Vikram Jain `include "axi/typedef.svh" /// AXI Crosspoint (XP) with homomorphous slave and master ports. module axi_xp #( + // Atomic operations settings + parameter bit ATOPs = 1'b1, /// Number of slave ports. parameter int unsigned NumSlvPorts = 32'd0, /// Number of master ports. @@ -123,6 +126,7 @@ module axi_xp #( axi_xbar #( .Cfg ( xbar_cfg ), + .ATOPs ( ATOPs ), .Connectivity ( Connectivity ), .slv_aw_chan_t ( aw_t ), .mst_aw_chan_t ( xbar_aw_t ), From 1a681bbd310b394d03a7dc2cf5d4cf80ae9e94d0 Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Thu, 1 Sep 2022 12:52:47 +0200 Subject: [PATCH 046/145] axi_xp: Add comments for parameters --- src/axi_xp.sv | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/axi_xp.sv b/src/axi_xp.sv index a9ab24e5d..2e382fcc5 100644 --- a/src/axi_xp.sv +++ b/src/axi_xp.sv @@ -42,18 +42,32 @@ module axi_xp #( /// this parameter in flight, but a transaction exceeding the maximum will be stalled until all /// transactions of another ID complete. parameter int unsigned AxiSlvPortMaxUniqIds = 32'd0, - /// Maximum number of in-flight write transactions at each slave port. + /// Maximum number of in-flight transactions with the same ID at the slave port. /// - /// It is legal for upstream to have more write transactions than the maximum given by this - /// parameter in flight, but a write exceeding the maximum will be stalled until the next write - /// at the same slave port completes. - parameter int unsigned AxiSlvPortMaxWriteTxns = 32'd0, - /// Maximum number of in-flight transactions with the same ID. + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds <= 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to [`axi_id_remap` as `AxiMaxTxnsPerId` + /// parameter](module.axi_id_remap#parameter.AxiMaxTxnsPerId). + parameter int unsigned AxiSlvPortMaxTxnsPerId = 32'd0, + /// Maximum number of in-flight transactions at the slave port. Reads and writes are counted + /// separately (except for ATOPs, which count as both read and write). /// - /// It is legal for upstream to have more transactions than the maximum given by this parameter in - /// flight for any ID, but a transaction exceeding the maximum will be stalled until another - /// transaction with the same ID completes. - parameter int unsigned AxiMaxTxnsPerId = 32'd0, + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to + /// [`axi_id_serialize`](module.axi_id_serialize#parameter.AxiSlvPortMaxTxns). + parameter int unsigned AxiSlvPortMaxTxns = 32'd0, + /// Maximum number of different IDs that can be in flight at the master port. Reads and writes + /// are counted separately (except for ATOPs, which count as both read and write). + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to + /// [`axi_id_serialize`](module.axi_id_serialize#parameter.AxiMstPortMaxUniqIds). + parameter int unsigned AxiMstPortMaxUniqIds = 32'd0, + /// Maximum number of in-flight transactions with the same ID at the master port. + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to + /// [`axi_id_serialize`](module.axi_id_serialize#parameter.AxiMstPortMaxTxnsPerId). + parameter int unsigned AxiMstPortMaxTxnsPerId = 32'd0, /// Number of rules in the address map. parameter int unsigned NumAddrRules = 32'd0, /// Request struct type of the AXI4+ATOP slave port From def4a00807a8f0e395285d92d72ca4a271cd959d Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Thu, 1 Sep 2022 12:51:53 +0200 Subject: [PATCH 047/145] axi_xp: Make xbar Cfg parametrizable --- src/axi_xp.sv | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/axi_xp.sv b/src/axi_xp.sv index 2e382fcc5..8a045c55f 100644 --- a/src/axi_xp.sv +++ b/src/axi_xp.sv @@ -19,6 +19,8 @@ module axi_xp #( // Atomic operations settings parameter bit ATOPs = 1'b1, + // xbar configuration + parameter axi_pkg::xbar_cfg_t Cfg = '0, /// Number of slave ports. parameter int unsigned NumSlvPorts = 32'd0, /// Number of master ports. @@ -124,22 +126,8 @@ module axi_xp #( xbar_req_t [NumMstPorts-1:0] xbar_req; xbar_resp_t [NumMstPorts-1:0] xbar_resp; - localparam axi_pkg::xbar_cfg_t xbar_cfg = '{ - NoSlvPorts: NumSlvPorts, - NoMstPorts: NumMstPorts, - MaxMstTrans: AxiMaxTxnsPerId, - MaxSlvTrans: AxiSlvPortMaxWriteTxns, - FallThrough: 1'b0, - LatencyMode: axi_pkg::CUT_ALL_PORTS, - AxiIdWidthSlvPorts: AxiIdWidth, - AxiIdUsedSlvPorts: AxiIdWidth, - AxiAddrWidth: AxiAddrWidth, - AxiDataWidth: AxiDataWidth, - NoAddrRules: NumAddrRules - }; - axi_xbar #( - .Cfg ( xbar_cfg ), + .Cfg ( Cfg ), .ATOPs ( ATOPs ), .Connectivity ( Connectivity ), .slv_aw_chan_t ( aw_t ), From 16735d59d82802bf60db189b5541b1ec1826da33 Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Thu, 1 Sep 2022 12:56:24 +0200 Subject: [PATCH 048/145] axi_xp: Uniquify AXI master and slave port --- src/axi_xp.sv | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/axi_xp.sv b/src/axi_xp.sv index 8a045c55f..27b1a680e 100644 --- a/src/axi_xp.sv +++ b/src/axi_xp.sv @@ -72,14 +72,19 @@ module axi_xp #( parameter int unsigned AxiMstPortMaxTxnsPerId = 32'd0, /// Number of rules in the address map. parameter int unsigned NumAddrRules = 32'd0, - /// Request struct type of the AXI4+ATOP slave port - parameter type slv_req_t = logic, - /// Response struct type of the AXI4+ATOP slave port - parameter type slv_resp_t = logic, - /// Request struct type of the AXI4+ATOP master port - parameter type mst_req_t = logic, - /// Response struct type of the AXI4+ATOP master port - parameter type mst_resp_t = logic, + // parameter type slv_aw_chan_t = logic, + parameter type mst_aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type slv_b_chan_t = logic, + parameter type mst_b_chan_t = logic, + parameter type slv_ar_chan_t = logic, + parameter type mst_ar_chan_t = logic, + parameter type slv_r_chan_t = logic, + parameter type mst_r_chan_t = logic, + /// Request struct type of the AXI4+ATOP + parameter type axi_req_t = logic, + /// Response struct type of the AXI4+ATOP + parameter type axi_resp_t = logic, /// Rule type (see documentation of `axi_xbar` for details). parameter type rule_t = axi_pkg::xbar_rule_64_t ) ( @@ -90,13 +95,13 @@ module axi_xp #( /// Test mode enable input logic test_en_i, /// Slave ports request - input slv_req_t [NumSlvPorts-1:0] slv_req_i, + input axi_req_t [NumSlvPorts-1:0] slv_req_i, /// Slave ports response - output slv_resp_t [NumSlvPorts-1:0] slv_resp_o, + output axi_resp_t [NumSlvPorts-1:0] slv_resp_o, /// Master ports request - output mst_req_t [NumMstPorts-1:0] mst_req_o, + output axi_req_t [NumMstPorts-1:0] mst_req_o, /// Master ports response - input mst_resp_t [NumMstPorts-1:0] mst_resp_i, + input axi_resp_t [NumMstPorts-1:0] mst_resp_i, /// Address map for transferring transactions from slave to master ports input rule_t [NumAddrRules-1:0] addr_map_i ); @@ -139,8 +144,8 @@ module axi_xp #( .mst_ar_chan_t ( xbar_ar_t ), .slv_r_chan_t ( r_t ), .mst_r_chan_t ( xbar_r_t ), - .slv_req_t ( req_t ), - .slv_resp_t ( resp_t ), + .slv_req_t ( axi_req_t ), + .slv_resp_t ( axi_resp_t ), .mst_req_t ( xbar_req_t ), .mst_resp_t ( xbar_resp_t ), .rule_t ( rule_t ) @@ -159,14 +164,14 @@ module axi_xp #( for (genvar i = 0; i < NumMstPorts; i++) begin : gen_remap axi_id_remap #( - .AxiSlvPortIdWidth ( AxiXbarIdWidth ), - .AxiSlvPortMaxUniqIds ( AxiSlvPortMaxUniqIds ), - .AxiMaxTxnsPerId ( AxiMaxTxnsPerId ), - .AxiMstPortIdWidth ( AxiIdWidth ), - .slv_req_t ( xbar_req_t ), - .slv_resp_t ( xbar_resp_t ), - .mst_req_t ( req_t ), - .mst_resp_t ( resp_t ) + .AxiSlvPortIdWidth ( AxiXbarIdWidth ), + .AxiSlvPortMaxUniqIds ( AxiSlvPortMaxUniqIds ), + .AxiMaxTxnsPerId ( AxiSlvPortMaxTxnsPerId ), + .AxiMstPortIdWidth ( AxiIdWidth ), + .slv_req_t ( xbar_req_t ), + .slv_resp_t ( xbar_resp_t ), + .mst_req_t ( axi_req_t ), + .mst_resp_t ( axi_resp_t ) ) i_axi_id_remap ( .clk_i, .rst_ni, From c1d21987e67cf59a9f3f0d42242b07d8210322ce Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Thu, 1 Sep 2022 12:57:10 +0200 Subject: [PATCH 049/145] axi_xp: disable default routing --- src/axi_xp.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/axi_xp.sv b/src/axi_xp.sv index 27b1a680e..8edf63411 100644 --- a/src/axi_xp.sv +++ b/src/axi_xp.sv @@ -158,8 +158,8 @@ module axi_xp #( .mst_ports_req_o ( xbar_req ), .mst_ports_resp_i ( xbar_resp ), .addr_map_i, - .en_default_mst_port_i ( {NumSlvPorts{1'b0}} ), - .default_mst_port_i ( {NumSlvPorts{{$clog2(NumMstPorts){1'b0}}}} ) + .en_default_mst_port_i ( '0 ), + .default_mst_port_i ( '0 ) ); for (genvar i = 0; i < NumMstPorts; i++) begin : gen_remap From 6c632fa5087cd60b04340da09799639a0973779e Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Thu, 1 Sep 2022 12:57:29 +0200 Subject: [PATCH 050/145] axi_xp: Add intf version --- src/axi_xp.sv | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/src/axi_xp.sv b/src/axi_xp.sv index 8edf63411..849db0d43 100644 --- a/src/axi_xp.sv +++ b/src/axi_xp.sv @@ -183,3 +183,115 @@ module axi_xp #( end endmodule + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +module axi_xp_intf +import cf_math_pkg::idx_width; +#( + parameter bit ATOPs = 1'b1, + parameter axi_pkg::xbar_cfg_t Cfg = '0, + parameter int unsigned NumSlvPorts = 32'd0, + parameter int unsigned NumMstPorts = 32'd0, + parameter bit [NumSlvPorts-1:0][NumMstPorts-1:0] Connectivity = '1, + parameter int unsigned AxiAddrWidth = 32'd0, + parameter int unsigned AxiDataWidth = 32'd0, + parameter int unsigned AxiIdWidth = 32'd0, + parameter int unsigned AxiUserWidth = 32'd0, + parameter int unsigned AxiSlvPortMaxUniqIds = 32'd0, + parameter int unsigned AxiSlvPortMaxTxnsPerId = 32'd0, + parameter int unsigned AxiSlvPortMaxTxns = 32'd0, + parameter int unsigned AxiMstPortMaxUniqIds = 32'd0, + parameter int unsigned AxiMstPortMaxTxnsPerId = 32'd0, + parameter int unsigned NumAddrRules = 32'd0, + parameter type rule_t = axi_pkg::xbar_rule_64_t +) ( + input logic clk_i, + input logic rst_ni, + input logic test_en_i, + AXI_BUS.Slave slv_ports [NumSlvPorts-1:0], + AXI_BUS.Master mst_ports [NumMstPorts-1:0], + input rule_t [NumAddrRules-1:0] addr_map_i +); + + // localparam int unsigned AxiIdWidthMstPorts = AxiIdWidth + $clog2(NoSlvPorts); + + typedef logic [AxiIdWidth -1:0] id_mst_t; + typedef logic [AxiIdWidth -1:0] id_slv_t; + typedef logic [AxiAddrWidth -1:0] addr_t; + typedef logic [AxiDataWidth -1:0] data_t; + typedef logic [AxiDataWidth/8 -1:0] strb_t; + typedef logic [AxiUserWidth -1:0] user_t; + + `AXI_TYPEDEF_AW_CHAN_T(mst_aw_chan_t, addr_t, id_mst_t, user_t) + `AXI_TYPEDEF_AW_CHAN_T(slv_aw_chan_t, addr_t, id_slv_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(mst_b_chan_t, id_mst_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(slv_b_chan_t, id_slv_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(mst_ar_chan_t, addr_t, id_mst_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(slv_ar_chan_t, addr_t, id_slv_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(mst_r_chan_t, data_t, id_mst_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(slv_r_chan_t, data_t, id_slv_t, user_t) + `AXI_TYPEDEF_REQ_T(mst_req_t, mst_aw_chan_t, w_chan_t, mst_ar_chan_t) + `AXI_TYPEDEF_REQ_T(slv_req_t, slv_aw_chan_t, w_chan_t, slv_ar_chan_t) + `AXI_TYPEDEF_RESP_T(mst_resp_t, mst_b_chan_t, mst_r_chan_t) + `AXI_TYPEDEF_RESP_T(slv_resp_t, slv_b_chan_t, slv_r_chan_t) + + mst_req_t [NumMstPorts-1:0] mst_reqs; + mst_resp_t [NumMstPorts-1:0] mst_resps; + slv_req_t [NumSlvPorts-1:0] slv_reqs; + slv_resp_t [NumSlvPorts-1:0] slv_resps; + + for (genvar i = 0; i < NumMstPorts; i++) begin : gen_assign_mst + `AXI_ASSIGN_FROM_REQ(mst_ports[i], mst_reqs[i]) + `AXI_ASSIGN_TO_RESP(mst_resps[i], mst_ports[i]) + end + + for (genvar i = 0; i < NumSlvPorts; i++) begin : gen_assign_slv + `AXI_ASSIGN_TO_REQ(slv_reqs[i], slv_ports[i]) + `AXI_ASSIGN_FROM_RESP(slv_ports[i], slv_resps[i]) + end + + axi_xp #( + .ATOPs ( ATOPs ), + .Cfg ( Cfg ), + .NumSlvPorts ( NumSlvPorts ), + .NumMstPorts ( NumMstPorts ), + .Connectivity ( Connectivity ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .AxiSlvPortMaxUniqIds ( AxiSlvPortMaxUniqIds ), + .AxiSlvPortMaxTxnsPerId ( AxiSlvPortMaxTxnsPerId ), + .AxiSlvPortMaxTxns ( AxiSlvPortMaxTxns ), + .AxiMstPortMaxUniqIds ( AxiMstPortMaxUniqIds ), + .AxiMstPortMaxTxnsPerId ( AxiMstPortMaxTxnsPerId ), + .NumAddrRules ( NumAddrRules ), + .slv_aw_chan_t ( slv_aw_chan_t ), + .mst_aw_chan_t ( mst_aw_chan_t ), + .w_chan_t ( w_chan_t ), + .slv_b_chan_t ( slv_b_chan_t ), + .mst_b_chan_t ( mst_b_chan_t ), + .slv_ar_chan_t ( slv_ar_chan_t ), + .mst_ar_chan_t ( mst_ar_chan_t ), + .slv_r_chan_t ( slv_r_chan_t ), + .mst_r_chan_t ( mst_r_chan_t ), + .slv_req_t ( slv_req_t ), + .slv_resp_t ( slv_resp_t ), + .mst_req_t ( mst_req_t ), + .mst_resp_t ( mst_resp_t ), + .rule_t ( rule_t ) + ) i_xp ( + .clk_i, + .rst_ni, + .test_en_i, + .slv_req_i (slv_reqs ), + .slv_resp_o (slv_resps), + .mst_req_o (mst_reqs ), + .mst_resp_i (mst_resps), + .addr_map_i + ); + +endmodule \ No newline at end of file From ff176f89ba9c52837f2589dbe5a58773d2d3807a Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Thu, 1 Sep 2022 13:03:56 +0200 Subject: [PATCH 051/145] Bender.yml: Add tb_axi --- Bender.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Bender.yml b/Bender.yml index 97b2d9478..542210165 100644 --- a/Bender.yml +++ b/Bender.yml @@ -84,6 +84,7 @@ sources: # Level 0 - test/tb_axi_dw_pkg.sv - test/tb_axi_xbar_pkg.sv + - test/tb_axi_xp_pkg.sv # Level 1 - test/tb_axi_addr_test.sv - test/tb_axi_atop_filter.sv @@ -105,3 +106,4 @@ sources: - test/tb_axi_to_axi_lite.sv - test/tb_axi_to_mem_banked.sv - test/tb_axi_xbar.sv + - test/tb_axi_xp.sv \ No newline at end of file From db44574dc3ab4c5cd8c6e39894f1e646a8b87b71 Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Thu, 1 Sep 2022 13:04:37 +0200 Subject: [PATCH 052/145] axi_xp: Add initial testbench --- test/tb_axi_xp.sv | 468 ++++++++++++++++++++++++++++++++++++++ test/tb_axi_xp_pkg.sv | 505 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 973 insertions(+) create mode 100644 test/tb_axi_xp.sv create mode 100644 test/tb_axi_xp_pkg.sv diff --git a/test/tb_axi_xp.sv b/test/tb_axi_xp.sv new file mode 100644 index 000000000..ef9a3d8e1 --- /dev/null +++ b/test/tb_axi_xp.sv @@ -0,0 +1,468 @@ +// Copyright (c) 2019 ETH Zurich and 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: +// - Florian Zaruba +// - Andreas Kurth +// - Vikram Jain + +// Directed Random Verification Testbench for `axi_xp`: The crosspoint is instantiated with +// a configurable number of random axi master and slave modules. Each random master executes a +// fixed number of writes and reads over the whole addess map. All masters simultaneously issue +// transactions through the crosspoint, thereby saturating it. A monitor, which snoops the +// transactions of each master and slave port and models the crosspoint with a network of FIFOs, +// checks whether each transaction follows the expected route. + +`include "axi/typedef.svh" +`include "axi/assign.svh" + +module tb_axi_xp #( + // Testbench parameters + parameter bit TbEnAtop = 1'b1, // enable atomic operations (ATOPs) + parameter bit TbEnExcl = 1'b0, // enable exclusive accesses + parameter bit TbUniqueIds = 1'b0, // restrict to only unique IDs + parameter int unsigned TbNumMst = 32'd4, // how many AXI masters there are + parameter int unsigned TbNumSlv = 32'd4 // how many AXI slaves there are +); + // Random master no Transactions + localparam int unsigned NoWrites = 80; // How many writes per master + localparam int unsigned NoReads = 80; // How many reads per master + // timing parameters + localparam time CyclTime = 10ns; + localparam time ApplTime = 2ns; + localparam time TestTime = 8ns; + + // DUT parameters + localparam bit ATOPs = TbEnAtop; + localparam bit [TbNumSlv-1:0][TbNumMst-1:0] Connectivity = '1; + localparam int unsigned AxiSlvPortMaxUniqIds = 32'd16; + localparam int unsigned AxiSlvPortMaxTxnsPerId = 32'd128; + localparam int unsigned AxiSlvPortMaxTxns = 32'd31; + localparam int unsigned AxiMstPortMaxUniqIds = 32'd2; + localparam int unsigned AxiMstPortMaxTxnsPerId = 32'd7; + localparam int unsigned NoAddrRules = 32'd8; + + // axi configuration + localparam int unsigned AxiIdWidthMasters = 4; + localparam int unsigned AxiIdUsed = 3; // Has to be <= AxiIdWidthMasters + localparam int unsigned AxiIdWidthSlaves = AxiIdWidthMasters + $clog2(TbNumMst); + localparam int unsigned AxiAddrWidth = 32; // Axi Address Width + localparam int unsigned AxiDataWidth = 64; // Axi Data Width + localparam int unsigned AxiStrbWidth = AxiDataWidth / 8; + localparam int unsigned AxiUserWidth = 5; + localparam int unsigned AxiIdWidth = AxiIdWidthMasters; + // in the bench can change this variables which are set here freely + localparam axi_pkg::xbar_cfg_t xbar_cfg = '{ + NoSlvPorts: TbNumSlv, + NoMstPorts: TbNumMst, + MaxMstTrans: AxiSlvPortMaxTxns, + MaxSlvTrans: AxiSlvPortMaxTxnsPerId, + FallThrough: 1'b0, + LatencyMode: axi_pkg::CUT_ALL_AX, + AxiIdWidthSlvPorts: AxiIdWidthMasters, + AxiIdUsedSlvPorts: AxiIdUsed, + UniqueIds: TbUniqueIds, + AxiAddrWidth: AxiAddrWidth, + AxiDataWidth: AxiDataWidth, + NoAddrRules: 8 + }; + typedef logic [AxiIdWidthMasters-1:0] id_mst_t; + typedef logic [AxiIdWidthSlaves-1:0] id_slv_t; + typedef logic [AxiAddrWidth-1:0] addr_t; + typedef axi_pkg::xbar_rule_32_t rule_t; // Has to be the same width as axi addr + typedef logic [AxiDataWidth-1:0] data_t; + typedef logic [AxiStrbWidth-1:0] strb_t; + typedef logic [AxiUserWidth-1:0] user_t; + + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_mst_t, addr_t, id_mst_t, user_t) + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_slv_t, addr_t, id_slv_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_mst_t, id_mst_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_slv_t, id_slv_t, user_t) + + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_mst_t, addr_t, id_mst_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_slv_t, addr_t, id_slv_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_mst_t, data_t, id_mst_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_slv_t, data_t, id_slv_t, user_t) + + `AXI_TYPEDEF_REQ_T(mst_req_t, aw_chan_mst_t, w_chan_t, ar_chan_mst_t) + `AXI_TYPEDEF_RESP_T(mst_resp_t, b_chan_mst_t, r_chan_mst_t) + `AXI_TYPEDEF_REQ_T(slv_req_t, aw_chan_slv_t, w_chan_t, ar_chan_slv_t) + `AXI_TYPEDEF_RESP_T(slv_resp_t, b_chan_slv_t, r_chan_slv_t) + + localparam rule_t [xbar_cfg.NoAddrRules-1:0] AddrMap = '{ + '{idx: 32'd7 % TbNumSlv, start_addr: 32'h0001_0000, end_addr: 32'h0001_1000}, + '{idx: 32'd6 % TbNumSlv, start_addr: 32'h0000_9000, end_addr: 32'h0001_0000}, + '{idx: 32'd5 % TbNumSlv, start_addr: 32'h0000_8000, end_addr: 32'h0000_9000}, + '{idx: 32'd4 % TbNumSlv, start_addr: 32'h0000_7000, end_addr: 32'h0000_8000}, + '{idx: 32'd3 % TbNumSlv, start_addr: 32'h0000_6300, end_addr: 32'h0000_7000}, + '{idx: 32'd2 % TbNumSlv, start_addr: 32'h0000_4000, end_addr: 32'h0000_6300}, + '{idx: 32'd1 % TbNumSlv, start_addr: 32'h0000_3000, end_addr: 32'h0000_4000}, + '{idx: 32'd0 % TbNumSlv, start_addr: 32'h0000_0000, end_addr: 32'h0000_3000} + }; + + typedef axi_test::axi_rand_master #( + // AXI interface parameters + .AW ( AxiAddrWidth ), + .DW ( AxiDataWidth ), + .IW ( AxiIdWidthMasters ), + .UW ( AxiUserWidth ), + // Stimuli application and test time + .TA ( ApplTime ), + .TT ( TestTime ), + // Maximum number of read and write transactions in flight + .MAX_READ_TXNS ( 20 ), + .MAX_WRITE_TXNS ( 20 ), + .AXI_EXCLS ( TbEnExcl ), + .AXI_ATOPS ( TbEnAtop ), + .UNIQUE_IDS ( TbUniqueIds ) + ) axi_rand_master_t; + typedef axi_test::axi_rand_slave #( + // AXI interface parameters + .AW ( AxiAddrWidth ), + .DW ( AxiDataWidth ), + .IW ( AxiIdWidthSlaves ), + .UW ( AxiUserWidth ), + // Stimuli application and test time + .TA ( ApplTime ), + .TT ( TestTime ) + ) axi_rand_slave_t; + + // ------------- + // DUT signals + // ------------- + logic clk; + // DUT signals + logic rst_n; + logic [TbNumMst-1:0] end_of_sim; + + // master structs + mst_req_t [TbNumMst-1:0] masters_req; + mst_resp_t [TbNumMst-1:0] masters_resp; + + // slave structs + slv_req_t [TbNumSlv-1:0] slaves_req; + slv_resp_t [TbNumSlv-1:0] slaves_resp; + + // ------------------------------- + // AXI Interfaces + // ------------------------------- + AXI_BUS #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( AxiDataWidth ), + .AXI_ID_WIDTH ( AxiIdWidthMasters ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) master [TbNumMst-1:0] (); + AXI_BUS_DV #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( AxiDataWidth ), + .AXI_ID_WIDTH ( AxiIdWidthMasters ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) master_dv [TbNumMst-1:0] (clk); + AXI_BUS_DV #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( AxiDataWidth ), + .AXI_ID_WIDTH ( AxiIdWidthMasters ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) master_monitor_dv [TbNumMst-1:0] (clk); + for (genvar i = 0; i < TbNumMst; i++) begin : gen_conn_dv_masters + `AXI_ASSIGN (master[i], master_dv[i]) + `AXI_ASSIGN_TO_REQ(masters_req[i], master[i]) + `AXI_ASSIGN_TO_RESP(masters_resp[i], master[i]) + end + + AXI_BUS #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( AxiDataWidth ), + .AXI_ID_WIDTH ( AxiIdWidthSlaves ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) slave [TbNumSlv-1:0] (); + AXI_BUS_DV #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( AxiDataWidth ), + .AXI_ID_WIDTH ( AxiIdWidthSlaves ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) slave_dv [TbNumSlv-1:0](clk); + AXI_BUS_DV #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( AxiDataWidth ), + .AXI_ID_WIDTH ( AxiIdWidthSlaves ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) slave_monitor_dv [TbNumSlv-1:0](clk); + for (genvar i = 0; i < TbNumSlv; i++) begin : gen_conn_dv_slaves + `AXI_ASSIGN(slave_dv[i], slave[i]) + `AXI_ASSIGN_TO_REQ(slaves_req[i], slave[i]) + `AXI_ASSIGN_TO_RESP(slaves_resp[i], slave[i]) + end + // ------------------------------- + // AXI Rand Masters and Slaves + // ------------------------------- + // Masters control simulation run time + axi_rand_master_t axi_rand_master [TbNumMst]; + for (genvar i = 0; i < TbNumMst; i++) begin : gen_rand_master + initial begin + axi_rand_master[i] = new( master_dv[i] ); + end_of_sim[i] <= 1'b0; + axi_rand_master[i].add_memory_region(AddrMap[0].start_addr, + AddrMap[xbar_cfg.NoAddrRules-1].end_addr, + axi_pkg::DEVICE_NONBUFFERABLE); + axi_rand_master[i].reset(); + @(posedge rst_n); + axi_rand_master[i].run(NoReads, NoWrites); + end_of_sim[i] <= 1'b1; + end + end + + axi_rand_slave_t axi_rand_slave [TbNumSlv]; + for (genvar i = 0; i < TbNumSlv; i++) begin : gen_rand_slave + initial begin + axi_rand_slave[i] = new( slave_dv[i] ); + axi_rand_slave[i].reset(); + @(posedge rst_n); + axi_rand_slave[i].run(); + end + end + + initial begin : proc_monitor + static tb_axi_xp_pkg::axi_xp_monitor #( + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiIdWidthMasters ( AxiIdWidthMasters ), + .AxiIdWidthSlaves ( AxiIdWidthSlaves ), + .AxiUserWidth ( AxiUserWidth ), + .NumMasters ( TbNumMst ), + .NumSlaves ( TbNumSlv ), + .NumAddrRules ( xbar_cfg.NoAddrRules ), + .rule_t ( rule_t ), + .AddrMap ( AddrMap ), + .TimeTest ( TestTime ) + ) monitor = new( master_monitor_dv, slave_monitor_dv ); + fork + monitor.run(); + do begin + #TestTime; + if(end_of_sim == '1) begin + monitor.print_result(); + $stop(); + end + @(posedge clk); + end while (1'b1); + join + end + + //----------------------------------- + // Clock generator + //----------------------------------- + clk_rst_gen #( + .ClkPeriod ( CyclTime ), + .RstClkCycles ( 5 ) + ) i_clk_gen ( + .clk_o (clk), + .rst_no(rst_n) + ); + + //----------------------------------- + // DUT + //----------------------------------- + axi_xp_intf #( + .ATOPs ( ATOPs ), + .Cfg ( xbar_cfg ), + .NumSlvPorts ( xbar_cfg.NoSlvPorts ), + .NumMstPorts ( xbar_cfg.NoMstPorts ), + .Connectivity ( Connectivity ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .AxiSlvPortMaxUniqIds ( AxiSlvPortMaxUniqIds ), + .AxiSlvPortMaxTxnsPerId ( AxiSlvPortMaxTxnsPerId ), + .AxiSlvPortMaxTxns ( AxiSlvPortMaxTxns ), + .AxiMstPortMaxUniqIds ( AxiMstPortMaxUniqIds ), + .AxiMstPortMaxTxnsPerId ( AxiMstPortMaxTxnsPerId ), + .NumAddrRules ( xbar_cfg.NoAddrRules ), + .rule_t ( rule_t ) + ) i_xp_dut ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .test_en_i ( 1'b0 ), + .slv_ports ( master ), + .mst_ports ( slave ), + .addr_map_i ( AddrMap ) + ); + + // logger for master modules + for (genvar i = 0; i < TbNumMst; i++) begin : gen_master_logger + axi_chan_logger #( + .TestTime ( TestTime ), // Time after clock, where sampling happens + .LoggerName( $sformatf("axi_logger_master_%0d", i)), + .aw_chan_t ( aw_chan_mst_t ), // axi AW type + .w_chan_t ( w_chan_t ), // axi W type + .b_chan_t ( b_chan_mst_t ), // axi B type + .ar_chan_t ( ar_chan_mst_t ), // axi AR type + .r_chan_t ( r_chan_mst_t ) // axi R type + ) i_mst_channel_logger ( + .clk_i ( clk ), // Clock + .rst_ni ( rst_n ), // Asynchronous reset active low, when `1'b0` no sampling + .end_sim_i ( &end_of_sim ), + // AW channel + .aw_chan_i ( masters_req[i].aw ), + .aw_valid_i ( masters_req[i].aw_valid ), + .aw_ready_i ( masters_resp[i].aw_ready ), + // W channel + .w_chan_i ( masters_req[i].w ), + .w_valid_i ( masters_req[i].w_valid ), + .w_ready_i ( masters_resp[i].w_ready ), + // B channel + .b_chan_i ( masters_resp[i].b ), + .b_valid_i ( masters_resp[i].b_valid ), + .b_ready_i ( masters_req[i].b_ready ), + // AR channel + .ar_chan_i ( masters_req[i].ar ), + .ar_valid_i ( masters_req[i].ar_valid ), + .ar_ready_i ( masters_resp[i].ar_ready ), + // R channel + .r_chan_i ( masters_resp[i].r ), + .r_valid_i ( masters_resp[i].r_valid ), + .r_ready_i ( masters_req[i].r_ready ) + ); + end + // logger for slave modules + for (genvar i = 0; i < TbNumSlv; i++) begin : gen_slave_logger + axi_chan_logger #( + .TestTime ( TestTime ), // Time after clock, where sampling happens + .LoggerName( $sformatf("axi_logger_slave_%0d",i)), + .aw_chan_t ( aw_chan_slv_t ), // axi AW type + .w_chan_t ( w_chan_t ), // axi W type + .b_chan_t ( b_chan_slv_t ), // axi B type + .ar_chan_t ( ar_chan_slv_t ), // axi AR type + .r_chan_t ( r_chan_slv_t ) // axi R type + ) i_slv_channel_logger ( + .clk_i ( clk ), // Clock + .rst_ni ( rst_n ), // Asynchronous reset active low, when `1'b0` no sampling + .end_sim_i ( &end_of_sim ), + // AW channel + .aw_chan_i ( slaves_req[i].aw ), + .aw_valid_i ( slaves_req[i].aw_valid ), + .aw_ready_i ( slaves_resp[i].aw_ready ), + // W channel + .w_chan_i ( slaves_req[i].w ), + .w_valid_i ( slaves_req[i].w_valid ), + .w_ready_i ( slaves_resp[i].w_ready ), + // B channel + .b_chan_i ( slaves_resp[i].b ), + .b_valid_i ( slaves_resp[i].b_valid ), + .b_ready_i ( slaves_req[i].b_ready ), + // AR channel + .ar_chan_i ( slaves_req[i].ar ), + .ar_valid_i ( slaves_req[i].ar_valid ), + .ar_ready_i ( slaves_resp[i].ar_ready ), + // R channel + .r_chan_i ( slaves_resp[i].r ), + .r_valid_i ( slaves_resp[i].r_valid ), + .r_ready_i ( slaves_req[i].r_ready ) + ); + end + + + for (genvar i = 0; i < TbNumMst; i++) begin : gen_connect_master_monitor + assign master_monitor_dv[i].aw_id = master[i].aw_id ; + assign master_monitor_dv[i].aw_addr = master[i].aw_addr ; + assign master_monitor_dv[i].aw_len = master[i].aw_len ; + assign master_monitor_dv[i].aw_size = master[i].aw_size ; + assign master_monitor_dv[i].aw_burst = master[i].aw_burst ; + assign master_monitor_dv[i].aw_lock = master[i].aw_lock ; + assign master_monitor_dv[i].aw_cache = master[i].aw_cache ; + assign master_monitor_dv[i].aw_prot = master[i].aw_prot ; + assign master_monitor_dv[i].aw_qos = master[i].aw_qos ; + assign master_monitor_dv[i].aw_region = master[i].aw_region; + assign master_monitor_dv[i].aw_atop = master[i].aw_atop ; + assign master_monitor_dv[i].aw_user = master[i].aw_user ; + assign master_monitor_dv[i].aw_valid = master[i].aw_valid ; + assign master_monitor_dv[i].aw_ready = master[i].aw_ready ; + assign master_monitor_dv[i].w_data = master[i].w_data ; + assign master_monitor_dv[i].w_strb = master[i].w_strb ; + assign master_monitor_dv[i].w_last = master[i].w_last ; + assign master_monitor_dv[i].w_user = master[i].w_user ; + assign master_monitor_dv[i].w_valid = master[i].w_valid ; + assign master_monitor_dv[i].w_ready = master[i].w_ready ; + assign master_monitor_dv[i].b_id = master[i].b_id ; + assign master_monitor_dv[i].b_resp = master[i].b_resp ; + assign master_monitor_dv[i].b_user = master[i].b_user ; + assign master_monitor_dv[i].b_valid = master[i].b_valid ; + assign master_monitor_dv[i].b_ready = master[i].b_ready ; + assign master_monitor_dv[i].ar_id = master[i].ar_id ; + assign master_monitor_dv[i].ar_addr = master[i].ar_addr ; + assign master_monitor_dv[i].ar_len = master[i].ar_len ; + assign master_monitor_dv[i].ar_size = master[i].ar_size ; + assign master_monitor_dv[i].ar_burst = master[i].ar_burst ; + assign master_monitor_dv[i].ar_lock = master[i].ar_lock ; + assign master_monitor_dv[i].ar_cache = master[i].ar_cache ; + assign master_monitor_dv[i].ar_prot = master[i].ar_prot ; + assign master_monitor_dv[i].ar_qos = master[i].ar_qos ; + assign master_monitor_dv[i].ar_region = master[i].ar_region; + assign master_monitor_dv[i].ar_user = master[i].ar_user ; + assign master_monitor_dv[i].ar_valid = master[i].ar_valid ; + assign master_monitor_dv[i].ar_ready = master[i].ar_ready ; + assign master_monitor_dv[i].r_id = master[i].r_id ; + assign master_monitor_dv[i].r_data = master[i].r_data ; + assign master_monitor_dv[i].r_resp = master[i].r_resp ; + assign master_monitor_dv[i].r_last = master[i].r_last ; + assign master_monitor_dv[i].r_user = master[i].r_user ; + assign master_monitor_dv[i].r_valid = master[i].r_valid ; + assign master_monitor_dv[i].r_ready = master[i].r_ready ; + end + for (genvar i = 0; i < TbNumSlv; i++) begin : gen_connect_slave_monitor + assign slave_monitor_dv[i].aw_id = slave[i].aw_id ; + assign slave_monitor_dv[i].aw_addr = slave[i].aw_addr ; + assign slave_monitor_dv[i].aw_len = slave[i].aw_len ; + assign slave_monitor_dv[i].aw_size = slave[i].aw_size ; + assign slave_monitor_dv[i].aw_burst = slave[i].aw_burst ; + assign slave_monitor_dv[i].aw_lock = slave[i].aw_lock ; + assign slave_monitor_dv[i].aw_cache = slave[i].aw_cache ; + assign slave_monitor_dv[i].aw_prot = slave[i].aw_prot ; + assign slave_monitor_dv[i].aw_qos = slave[i].aw_qos ; + assign slave_monitor_dv[i].aw_region = slave[i].aw_region; + assign slave_monitor_dv[i].aw_atop = slave[i].aw_atop ; + assign slave_monitor_dv[i].aw_user = slave[i].aw_user ; + assign slave_monitor_dv[i].aw_valid = slave[i].aw_valid ; + assign slave_monitor_dv[i].aw_ready = slave[i].aw_ready ; + assign slave_monitor_dv[i].w_data = slave[i].w_data ; + assign slave_monitor_dv[i].w_strb = slave[i].w_strb ; + assign slave_monitor_dv[i].w_last = slave[i].w_last ; + assign slave_monitor_dv[i].w_user = slave[i].w_user ; + assign slave_monitor_dv[i].w_valid = slave[i].w_valid ; + assign slave_monitor_dv[i].w_ready = slave[i].w_ready ; + assign slave_monitor_dv[i].b_id = slave[i].b_id ; + assign slave_monitor_dv[i].b_resp = slave[i].b_resp ; + assign slave_monitor_dv[i].b_user = slave[i].b_user ; + assign slave_monitor_dv[i].b_valid = slave[i].b_valid ; + assign slave_monitor_dv[i].b_ready = slave[i].b_ready ; + assign slave_monitor_dv[i].ar_id = slave[i].ar_id ; + assign slave_monitor_dv[i].ar_addr = slave[i].ar_addr ; + assign slave_monitor_dv[i].ar_len = slave[i].ar_len ; + assign slave_monitor_dv[i].ar_size = slave[i].ar_size ; + assign slave_monitor_dv[i].ar_burst = slave[i].ar_burst ; + assign slave_monitor_dv[i].ar_lock = slave[i].ar_lock ; + assign slave_monitor_dv[i].ar_cache = slave[i].ar_cache ; + assign slave_monitor_dv[i].ar_prot = slave[i].ar_prot ; + assign slave_monitor_dv[i].ar_qos = slave[i].ar_qos ; + assign slave_monitor_dv[i].ar_region = slave[i].ar_region; + assign slave_monitor_dv[i].ar_user = slave[i].ar_user ; + assign slave_monitor_dv[i].ar_valid = slave[i].ar_valid ; + assign slave_monitor_dv[i].ar_ready = slave[i].ar_ready ; + assign slave_monitor_dv[i].r_id = slave[i].r_id ; + assign slave_monitor_dv[i].r_data = slave[i].r_data ; + assign slave_monitor_dv[i].r_resp = slave[i].r_resp ; + assign slave_monitor_dv[i].r_last = slave[i].r_last ; + assign slave_monitor_dv[i].r_user = slave[i].r_user ; + assign slave_monitor_dv[i].r_valid = slave[i].r_valid ; + assign slave_monitor_dv[i].r_ready = slave[i].r_ready ; + end +endmodule diff --git a/test/tb_axi_xp_pkg.sv b/test/tb_axi_xp_pkg.sv new file mode 100644 index 000000000..606895467 --- /dev/null +++ b/test/tb_axi_xp_pkg.sv @@ -0,0 +1,505 @@ +// Copyright (c) 2019 ETH Zurich and 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: +// - Florian Zaruba +// - Wolfgang Roenninger +// - Vikram Jain + +// `axi_xp_monitor` implements an AXI bus monitor that is tuned for the AXI crosspoint. +// It snoops on each of the slaves and master ports of the crosspoint and +// populates FIFOs and ID queues to validate that no AXI beats get +// lost or sent to the wrong destination. It ignores the ID checks for the AW and AR +// channels as they get remapped in the id_remapper + +package tb_axi_xp_pkg; + class axi_xp_monitor #( + parameter int unsigned AxiAddrWidth, + parameter int unsigned AxiDataWidth, + parameter int unsigned AxiIdWidthMasters, + parameter int unsigned AxiIdWidthSlaves, + parameter int unsigned AxiUserWidth, + parameter int unsigned NumMasters, + parameter int unsigned NumSlaves, + parameter int unsigned NumAddrRules, + parameter type rule_t, + parameter rule_t [NumAddrRules-1:0] AddrMap, + // Stimuli application and test time + parameter time TimeTest + ); + typedef logic [AxiIdWidthMasters-1:0] mst_axi_id_t; + typedef logic [AxiIdWidthSlaves-1:0] slv_axi_id_t; + typedef logic [AxiAddrWidth-1:0] axi_addr_t; + + typedef logic [$clog2(NumMasters)-1:0] idx_mst_t; + typedef int unsigned idx_slv_t; // from rule_t + + typedef struct packed { + mst_axi_id_t mst_axi_id; + logic last; + } master_exp_t; + typedef struct packed { + slv_axi_id_t slv_axi_id; + axi_addr_t slv_axi_addr; + axi_pkg::len_t slv_axi_len; + } exp_ax_t; + typedef struct packed { + slv_axi_id_t slv_axi_id; + logic last; + } slave_exp_t; + + typedef rand_id_queue_pkg::rand_id_queue #( + .data_t ( master_exp_t ), + .ID_WIDTH ( AxiIdWidthMasters ) + ) master_exp_queue_t; + typedef rand_id_queue_pkg::rand_id_queue #( + .data_t ( exp_ax_t ), + .ID_WIDTH ( AxiIdWidthSlaves ) + ) ax_queue_t; + + typedef rand_id_queue_pkg::rand_id_queue #( + .data_t ( slave_exp_t ), + .ID_WIDTH ( AxiIdWidthSlaves ) + ) slave_exp_queue_t; + + //----------------------------------------- + // Monitoring virtual interfaces + //----------------------------------------- + virtual AXI_BUS_DV #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( AxiDataWidth ), + .AXI_ID_WIDTH ( AxiIdWidthMasters ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) masters_axi [NumMasters-1:0]; + virtual AXI_BUS_DV #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( AxiDataWidth ), + .AXI_ID_WIDTH ( AxiIdWidthSlaves ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) slaves_axi [NumSlaves-1:0]; + //----------------------------------------- + // Queues and FIFOs to hold the expected ids + //----------------------------------------- + // Write transactions + ax_queue_t exp_aw_queue [NumSlaves-1:0]; + slave_exp_t exp_w_fifo [NumSlaves-1:0][$]; + slave_exp_t act_w_fifo [NumSlaves-1:0][$]; + master_exp_queue_t exp_b_queue [NumMasters-1:0]; + + // Read transactions + ax_queue_t exp_ar_queue [NumSlaves-1:0]; + master_exp_queue_t exp_r_queue [NumMasters-1:0]; + + //----------------------------------------- + // Bookkeeping + //----------------------------------------- + longint unsigned tests_expected; + longint unsigned tests_conducted; + longint unsigned tests_failed; + semaphore cnt_sem; + + //----------------------------------------- + // Constructor + //----------------------------------------- + function new( + virtual AXI_BUS_DV #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( AxiDataWidth ), + .AXI_ID_WIDTH ( AxiIdWidthMasters ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) axi_masters_vif [NumMasters-1:0], + virtual AXI_BUS_DV #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( AxiDataWidth ), + .AXI_ID_WIDTH ( AxiIdWidthSlaves ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) axi_slaves_vif [NumSlaves-1:0] + ); + begin + this.masters_axi = axi_masters_vif; + this.slaves_axi = axi_slaves_vif; + this.tests_expected = 0; + this.tests_conducted = 0; + this.tests_failed = 0; + for (int unsigned i = 0; i < NumMasters; i++) begin + this.exp_b_queue[i] = new; + this.exp_r_queue[i] = new; + end + for (int unsigned i = 0; i < NumSlaves; i++) begin + this.exp_aw_queue[i] = new; + this.exp_ar_queue[i] = new; + end + this.cnt_sem = new(1); + end + endfunction + + // when start the testing + task cycle_start; + #TimeTest; + endtask + + // when is cycle finished + task cycle_end; + @(posedge masters_axi[0].clk_i); + endtask + + // This task monitors a slave ports of the crossbar. Every time an AW beat is seen + // it populates an id queue at the right master port (if there is no expected decode error), + // populates the expected b response in its own id_queue and in case when the atomic bit [5] + // is set it also injects an expected response in the R channel. + task automatic monitor_mst_aw(input int unsigned i); + idx_slv_t to_slave_idx; + exp_ax_t exp_aw; + slv_axi_id_t exp_aw_id; + bit decerr; + + master_exp_t exp_b; + + if (masters_axi[i].aw_valid && masters_axi[i].aw_ready) begin + // check if it should go to a decerror + decerr = 1'b1; + for (int unsigned j = 0; j < NumAddrRules; j++) begin + if ((masters_axi[i].aw_addr >= AddrMap[j].start_addr) && + (masters_axi[i].aw_addr < AddrMap[j].end_addr)) begin + to_slave_idx = idx_slv_t'(AddrMap[j].idx); + decerr = 1'b0; + end + end + // send the exp aw beat down into the queue of the slave when no decerror + if (!decerr) begin + exp_aw_id = {idx_mst_t'(i), masters_axi[i].aw_id}; + // $display("Test exp aw_id: %b",exp_aw_id); + exp_aw = '{slv_axi_id: exp_aw_id, + slv_axi_addr: masters_axi[i].aw_addr, + slv_axi_len: masters_axi[i].aw_len }; + this.exp_aw_queue[to_slave_idx].push(exp_aw_id, exp_aw); + incr_expected_tests(3); + $display("%0tns > Master %0d: AW to Slave %0d: Axi ID: %b", + $time, i, to_slave_idx, masters_axi[i].aw_id); + end else begin + $display("%0tns > Master %0d: AW to Decerror: Axi ID: %b", + $time, i, to_slave_idx, masters_axi[i].aw_id); + end + // populate the expected b queue anyway + exp_b = '{mst_axi_id: masters_axi[i].aw_id, last: 1'b1}; + this.exp_b_queue[i].push(masters_axi[i].aw_id, exp_b); + incr_expected_tests(1); + $display(" Expect B response."); + // inject expected r beats on this id, if it is an atop + if(masters_axi[i].aw_atop[5]) begin + // push the required r beats into the right fifo (reuse the exp_b variable) + $display(" Expect R response, len: %0d.", masters_axi[i].aw_len); + for (int unsigned j = 0; j <= masters_axi[i].aw_len; j++) begin + exp_b = (j == masters_axi[i].aw_len) ? + '{mst_axi_id: masters_axi[i].aw_id, last: 1'b1} : + '{mst_axi_id: masters_axi[i].aw_id, last: 1'b0}; + this.exp_r_queue[i].push(masters_axi[i].aw_id, exp_b); + incr_expected_tests(1); + end + end + end + endtask : monitor_mst_aw + + // This task monitors a slave port of the crossbar. Every time there is an AW vector it + // gets checked for its contents and if it was expected. The task then pushes an expected + // amount of W beats in the respective fifo. Emphasis of the last flag. + task automatic monitor_slv_aw(input int unsigned i); + exp_ax_t exp_aw; + slave_exp_t exp_slv_w; + // $display("%0t > Was triggered: aw_valid %b, aw_ready: %b", + // $time(), slaves_axi[i].aw_valid, slaves_axi[i].aw_ready); + if (slaves_axi[i].aw_valid && slaves_axi[i].aw_ready) begin + // test if the aw beat was expected + exp_aw = this.exp_aw_queue[i].pop_id(slaves_axi[i].aw_id); + $display("%0tns > Slave %0d: AW Axi ID: %b", + $time, i, slaves_axi[i].aw_id); + if (exp_aw.slv_axi_id != slaves_axi[i].aw_id) begin + incr_failed_tests(1); + $warning("Slave %0d: Unexpected AW with ID: %b", i, slaves_axi[i].aw_id); + end + if (exp_aw.slv_axi_addr != slaves_axi[i].aw_addr) begin + incr_failed_tests(1); + $warning("Slave %0d: Unexpected AW with ID: %b and ADDR: %h, exp: %h", + i, slaves_axi[i].aw_id, slaves_axi[i].aw_addr, exp_aw.slv_axi_addr); + end + if (exp_aw.slv_axi_len != slaves_axi[i].aw_len) begin + incr_failed_tests(1); + $warning("Slave %0d: Unexpected AW with ID: %b and LEN: %h, exp: %h", + i, slaves_axi[i].aw_id, slaves_axi[i].aw_len, exp_aw.slv_axi_len); + end + incr_conducted_tests(3); + + // push the required w beats into the right fifo + incr_expected_tests(slaves_axi[i].aw_len + 1); + for (int unsigned j = 0; j <= slaves_axi[i].aw_len; j++) begin + exp_slv_w = (j == slaves_axi[i].aw_len) ? + '{slv_axi_id: slaves_axi[i].aw_id, last: 1'b1} : + '{slv_axi_id: slaves_axi[i].aw_id, last: 1'b0}; + this.exp_w_fifo[i].push_back(exp_slv_w); + end + end + endtask : monitor_slv_aw + + // This task just pushes every W beat that gets sent on a master port in its respective fifo. + task automatic monitor_slv_w(input int unsigned i); + slave_exp_t act_slv_w; + if (slaves_axi[i].w_valid && slaves_axi[i].w_ready) begin + // $display("%0t > W beat on Slave %0d, last flag: %b", $time, i, slaves_axi[i].w_last); + act_slv_w = '{last: slaves_axi[i].w_last , default:'0}; + this.act_w_fifo[i].push_back(act_slv_w); + end + endtask : monitor_slv_w + + // This task compares the expected and actual W beats on a master port. The reason that + // this is not done in `monitor_slv_w` is that there can be per protocol W beats on the + // channel, before AW is sent to the slave. + task automatic check_slv_w(input int unsigned i); + slave_exp_t exp_w, act_w; + while (this.exp_w_fifo[i].size() != 0 && this.act_w_fifo[i].size() != 0) begin + + exp_w = this.exp_w_fifo[i].pop_front(); + act_w = this.act_w_fifo[i].pop_front(); + // do the check + incr_conducted_tests(1); + if(exp_w.last != act_w.last) begin + incr_failed_tests(1); + $warning("Slave %d: unexpected W beat last flag %b, expected: %b.", + i, act_w.last, exp_w.last); + end + end + endtask : check_slv_w + + // This task checks if a B response is allowed on a slave port of the crossbar. + task automatic monitor_mst_b(input int unsigned i); + master_exp_t exp_b; + mst_axi_id_t axi_b_id; + if (masters_axi[i].b_valid && masters_axi[i].b_ready) begin + incr_conducted_tests(1); + axi_b_id = masters_axi[i].b_id; + $display("%0tns > Master %0d: Got last B with id: %b", + $time, i, axi_b_id); + if (this.exp_b_queue[i].empty()) begin + incr_failed_tests(1); + $warning("Master %d: unexpected B beat with ID: %b detected!", i, axi_b_id); + end else begin + exp_b = this.exp_b_queue[i].pop_id(axi_b_id); + if (axi_b_id != exp_b.mst_axi_id) begin + incr_failed_tests(1); + $warning("Master: %d got unexpected B with ID: %b", i, axi_b_id); + end + end + end + endtask : monitor_mst_b + + // This task monitors the AR channel of a slave port of the crossbar. For each AR it populates + // the corresponding ID queue with the number of r beats indicated on the `ar_len` field. + // Emphasis on the last flag. We will detect reordering, if the last flags do not match, + // as each `random` burst tend to have a different length. + task automatic monitor_mst_ar(input int unsigned i); + mst_axi_id_t mst_axi_id; + axi_addr_t mst_axi_addr; + axi_pkg::len_t mst_axi_len; + + idx_slv_t exp_slv_idx; + slv_axi_id_t exp_slv_axi_id; + exp_ax_t exp_slv_ar; + master_exp_t exp_mst_r; + + logic exp_decerr; + + if (masters_axi[i].ar_valid && masters_axi[i].ar_ready) begin + exp_decerr = 1'b1; + mst_axi_id = masters_axi[i].ar_id; + mst_axi_addr = masters_axi[i].ar_addr; + mst_axi_len = masters_axi[i].ar_len; + exp_slv_axi_id = {idx_mst_t'(i), mst_axi_id}; + exp_slv_idx = '0; + for (int unsigned j = 0; j < NumAddrRules; j++) begin + if ((mst_axi_addr >= AddrMap[j].start_addr) && (mst_axi_addr < AddrMap[j].end_addr)) begin + exp_slv_idx = AddrMap[j].idx; + exp_decerr = 1'b0; + end + end + if (exp_decerr) begin + $display("%0tns > Master %0d: AR to Decerror: Axi ID: %b", + $time, i, mst_axi_id); + end else begin + $display("%0tns > Master %0d: AR to Slave %0d: Axi ID: %b", + $time, i, exp_slv_idx, mst_axi_id); + // push the expected vectors AW for exp_slv + exp_slv_ar = '{slv_axi_id: exp_slv_axi_id, + slv_axi_addr: mst_axi_addr, + slv_axi_len: mst_axi_len }; + //$display("Expected Slv Axi Id is: %b", exp_slv_axi_id); + this.exp_ar_queue[exp_slv_idx].push(exp_slv_axi_id, exp_slv_ar); + incr_expected_tests(1); + end + // push the required r beats into the right fifo + $display(" Expect R response, len: %0d.", masters_axi[i].ar_len); + for (int unsigned j = 0; j <= mst_axi_len; j++) begin + exp_mst_r = (j == mst_axi_len) ? '{mst_axi_id: mst_axi_id, last: 1'b1} : + '{mst_axi_id: mst_axi_id, last: 1'b0}; + this.exp_r_queue[i].push(mst_axi_id, exp_mst_r); + incr_expected_tests(1); + end + end + endtask : monitor_mst_ar + + // This task monitors a master port of the crossbar and checks if a transmitted AR beat was + // expected. + task automatic monitor_slv_ar(input int unsigned i); + exp_ax_t exp_slv_ar; + slv_axi_id_t slv_axi_id; + if (slaves_axi[i].ar_valid && slaves_axi[i].ar_ready) begin + incr_conducted_tests(1); + slv_axi_id = slaves_axi[i].ar_id; + if (this.exp_ar_queue[i].empty()) begin + incr_failed_tests(1); + end else begin + // check that the ids are the same + exp_slv_ar = this.exp_ar_queue[i].pop_id(slv_axi_id); + $display("%0tns > Slave %0d: AR Axi ID: %b", $time, i, slv_axi_id); + if (exp_slv_ar.slv_axi_id != slv_axi_id) begin + incr_failed_tests(1); + $warning("Slave %d: Unexpected AR with ID: %b", i, slv_axi_id); + end + end + end + endtask : monitor_slv_ar + + // This task does the R channel monitoring on a slave port. It compares the last flags, + // which are determined by the sequence of previously sent AR vectors. + task automatic monitor_mst_r(input int unsigned i); + master_exp_t exp_mst_r; + mst_axi_id_t mst_axi_r_id; + logic mst_axi_r_last; + if (masters_axi[i].r_valid && masters_axi[i].r_ready) begin + incr_conducted_tests(1); + mst_axi_r_id = masters_axi[i].r_id; + mst_axi_r_last = masters_axi[i].r_last; + if (mst_axi_r_last) begin + $display("%0tns > Master %0d: Got last R with id: %b", + $time, i, mst_axi_r_id); + end + if (this.exp_r_queue[i].empty()) begin + incr_failed_tests(1); + $warning("Master %d: unexpected R beat with ID: %b detected!", i, mst_axi_r_id); + end else begin + exp_mst_r = this.exp_r_queue[i].pop_id(mst_axi_r_id); + if (mst_axi_r_id != exp_mst_r.mst_axi_id) begin + incr_failed_tests(1); + $warning("Master: %d got unexpected R with ID: %b", i, mst_axi_r_id); + end + if (mst_axi_r_last != exp_mst_r.last) begin + incr_failed_tests(1); + $warning("Master: %d got unexpected R with ID: %b and last flag: %b", + i, mst_axi_r_id, mst_axi_r_last); + end + end + end + endtask : monitor_mst_r + + // Some tasks to manage bookkeeping of the tests conducted. + task incr_expected_tests(input int unsigned times); + cnt_sem.get(); + this.tests_expected += times; + cnt_sem.put(); + endtask : incr_expected_tests + + task incr_conducted_tests(input int unsigned times); + cnt_sem.get(); + this.tests_conducted += times; + cnt_sem.put(); + endtask : incr_conducted_tests + + task incr_failed_tests(input int unsigned times); + cnt_sem.get(); + this.tests_failed += times; + cnt_sem.put(); + endtask : incr_failed_tests + + // This task invokes the various monitoring tasks. It first forks in two, spitting + // the tasks that should continuously run and the ones that get invoked every clock cycle. + // For the tasks every clock cycle all processes that only push something in the fifo's and + // Queues get run. When they are finished the processes that pop something get run. + task run(); + Continous: fork + begin + do begin + cycle_start(); + // at every cycle span some monitoring processes + // execute all processes that put something into the queues + PushMon: fork + proc_mst_aw: begin + for (int unsigned i = 0; i < NumMasters; i++) begin + monitor_mst_aw(i); + end + end + proc_mst_ar: begin + for (int unsigned i = 0; i < NumMasters; i++) begin + monitor_mst_ar(i); + end + end + join : PushMon + // this one pops and pushes something + proc_slv_aw: begin + for (int unsigned i = 0; i < NumSlaves; i++) begin + monitor_slv_aw(i); + end + end + proc_slv_w: begin + for (int unsigned i = 0; i < NumSlaves; i++) begin + monitor_slv_w(i); + end + end + // These only pop somethong from the queses + PopMon: fork + proc_mst_b: begin + for (int unsigned i = 0; i < NumMasters; i++) begin + monitor_mst_b(i); + end + end + proc_slv_ar: begin + for (int unsigned i = 0; i < NumSlaves; i++) begin + monitor_slv_ar(i); + end + end + proc_mst_r: begin + for (int unsigned i = 0; i < NumMasters; i++) begin + monitor_mst_r(i); + end + end + join : PopMon + // check the slave W fifos last + proc_check_slv_w: begin + for (int unsigned i = 0; i < NumSlaves; i++) begin + check_slv_w(i); + end + end + cycle_end(); + end while (1'b1); + end + join + endtask : run + + task print_result(); + $info("Simulation has ended!"); + $display("Tests Expected: %d", this.tests_expected); + $display("Tests Conducted: %d", this.tests_conducted); + $display("Tests Failed: %d", this.tests_failed); + if(tests_failed > 0) begin + $error("Simulation encountered unexpected Transactions!!!!!!"); + end + if(tests_conducted == 0) begin + $error("Simulation did not conduct any tests!"); + end + endtask : print_result + endclass : axi_xp_monitor +endpackage From 55aaf2b3d8637e724c991c8149de04fc6cb0fd92 Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Fri, 2 Sep 2022 16:37:29 +0200 Subject: [PATCH 053/145] axi_xp: Remove unnecessary parametrization --- src/axi_xp.sv | 102 +++++++++++++++----------------------------------- 1 file changed, 30 insertions(+), 72 deletions(-) diff --git a/src/axi_xp.sv b/src/axi_xp.sv index 849db0d43..9d133e006 100644 --- a/src/axi_xp.sv +++ b/src/axi_xp.sv @@ -72,15 +72,6 @@ module axi_xp #( parameter int unsigned AxiMstPortMaxTxnsPerId = 32'd0, /// Number of rules in the address map. parameter int unsigned NumAddrRules = 32'd0, - // parameter type slv_aw_chan_t = logic, - parameter type mst_aw_chan_t = logic, - parameter type w_chan_t = logic, - parameter type slv_b_chan_t = logic, - parameter type mst_b_chan_t = logic, - parameter type slv_ar_chan_t = logic, - parameter type mst_ar_chan_t = logic, - parameter type slv_r_chan_t = logic, - parameter type mst_r_chan_t = logic, /// Request struct type of the AXI4+ATOP parameter type axi_req_t = logic, /// Response struct type of the AXI4+ATOP @@ -106,6 +97,7 @@ module axi_xp #( input rule_t [NumAddrRules-1:0] addr_map_i ); + // The master port of the Xbar has a different ID width than the slave ports. parameter int unsigned AxiXbarIdWidth = AxiIdWidth + $clog2(NumSlvPorts); typedef logic [AxiAddrWidth-1:0] addr_t; typedef logic [AxiDataWidth-1:0] data_t; @@ -114,41 +106,31 @@ module axi_xp #( typedef logic [AxiDataWidth/8-1:0] strb_t; typedef logic [AxiUserWidth-1:0] user_t; - `AXI_TYPEDEF_AW_CHAN_T(aw_t, addr_t, id_t, user_t) - `AXI_TYPEDEF_AW_CHAN_T(xbar_aw_t, addr_t, xbar_id_t, user_t) - `AXI_TYPEDEF_W_CHAN_T(w_t, data_t, strb_t, user_t) - `AXI_TYPEDEF_B_CHAN_T(b_t, id_t, user_t) - `AXI_TYPEDEF_B_CHAN_T(xbar_b_t, xbar_id_t, user_t) - `AXI_TYPEDEF_AR_CHAN_T(ar_t, addr_t, id_t, user_t) - `AXI_TYPEDEF_AR_CHAN_T(xbar_ar_t, addr_t, xbar_id_t, user_t) - `AXI_TYPEDEF_R_CHAN_T(r_t, data_t, id_t, user_t) - `AXI_TYPEDEF_R_CHAN_T(xbar_r_t, data_t, xbar_id_t, user_t) - `AXI_TYPEDEF_REQ_T(req_t, aw_t, w_t, ar_t) - `AXI_TYPEDEF_REQ_T(xbar_req_t, xbar_aw_t, w_t, xbar_ar_t) - `AXI_TYPEDEF_RESP_T(resp_t, b_t, r_t) - `AXI_TYPEDEF_RESP_T(xbar_resp_t, xbar_b_t, xbar_r_t) + + `AXI_TYPEDEF_ALL(xp, addr_t, id_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_ALL(xbar, addr_t, xbar_id_t, data_t, strb_t, user_t) xbar_req_t [NumMstPorts-1:0] xbar_req; xbar_resp_t [NumMstPorts-1:0] xbar_resp; axi_xbar #( - .Cfg ( Cfg ), - .ATOPs ( ATOPs ), - .Connectivity ( Connectivity ), - .slv_aw_chan_t ( aw_t ), - .mst_aw_chan_t ( xbar_aw_t ), - .w_chan_t ( w_t ), - .slv_b_chan_t ( b_t ), - .mst_b_chan_t ( xbar_b_t ), - .slv_ar_chan_t ( ar_t ), - .mst_ar_chan_t ( xbar_ar_t ), - .slv_r_chan_t ( r_t ), - .mst_r_chan_t ( xbar_r_t ), - .slv_req_t ( axi_req_t ), - .slv_resp_t ( axi_resp_t ), - .mst_req_t ( xbar_req_t ), - .mst_resp_t ( xbar_resp_t ), - .rule_t ( rule_t ) + .Cfg ( Cfg ), + .ATOPs ( ATOPs ), + .Connectivity ( Connectivity ), + .slv_aw_chan_t ( xp_aw_chan_t ), + .mst_aw_chan_t ( xbar_aw_chan_t ), + .w_chan_t ( xp_w_chan_t ), + .slv_b_chan_t ( xp_b_chan_t ), + .mst_b_chan_t ( xbar_b_chan_t ), + .slv_ar_chan_t ( xp_ar_chan_t ), + .mst_ar_chan_t ( xbar_ar_chan_t ), + .slv_r_chan_t ( xp_r_chan_t ), + .mst_r_chan_t ( xbar_r_chan_t ), + .slv_req_t ( axi_req_t ), + .slv_resp_t ( axi_resp_t ), + .mst_req_t ( xbar_req_t ), + .mst_resp_t ( xbar_resp_t ), + .rule_t ( rule_t ) ) i_xbar ( .clk_i, .rst_ni, @@ -217,31 +199,18 @@ import cf_math_pkg::idx_width; // localparam int unsigned AxiIdWidthMstPorts = AxiIdWidth + $clog2(NoSlvPorts); - typedef logic [AxiIdWidth -1:0] id_mst_t; - typedef logic [AxiIdWidth -1:0] id_slv_t; + typedef logic [AxiIdWidth -1:0] id_t; typedef logic [AxiAddrWidth -1:0] addr_t; typedef logic [AxiDataWidth -1:0] data_t; typedef logic [AxiDataWidth/8 -1:0] strb_t; typedef logic [AxiUserWidth -1:0] user_t; - `AXI_TYPEDEF_AW_CHAN_T(mst_aw_chan_t, addr_t, id_mst_t, user_t) - `AXI_TYPEDEF_AW_CHAN_T(slv_aw_chan_t, addr_t, id_slv_t, user_t) - `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) - `AXI_TYPEDEF_B_CHAN_T(mst_b_chan_t, id_mst_t, user_t) - `AXI_TYPEDEF_B_CHAN_T(slv_b_chan_t, id_slv_t, user_t) - `AXI_TYPEDEF_AR_CHAN_T(mst_ar_chan_t, addr_t, id_mst_t, user_t) - `AXI_TYPEDEF_AR_CHAN_T(slv_ar_chan_t, addr_t, id_slv_t, user_t) - `AXI_TYPEDEF_R_CHAN_T(mst_r_chan_t, data_t, id_mst_t, user_t) - `AXI_TYPEDEF_R_CHAN_T(slv_r_chan_t, data_t, id_slv_t, user_t) - `AXI_TYPEDEF_REQ_T(mst_req_t, mst_aw_chan_t, w_chan_t, mst_ar_chan_t) - `AXI_TYPEDEF_REQ_T(slv_req_t, slv_aw_chan_t, w_chan_t, slv_ar_chan_t) - `AXI_TYPEDEF_RESP_T(mst_resp_t, mst_b_chan_t, mst_r_chan_t) - `AXI_TYPEDEF_RESP_T(slv_resp_t, slv_b_chan_t, slv_r_chan_t) + `AXI_TYPEDEF_ALL(axi, addr_t, id_t, data_t, strb_t, user_t) - mst_req_t [NumMstPorts-1:0] mst_reqs; - mst_resp_t [NumMstPorts-1:0] mst_resps; - slv_req_t [NumSlvPorts-1:0] slv_reqs; - slv_resp_t [NumSlvPorts-1:0] slv_resps; + axi_req_t [NumMstPorts-1:0] mst_reqs; + axi_resp_t [NumMstPorts-1:0] mst_resps; + axi_req_t [NumSlvPorts-1:0] slv_reqs; + axi_resp_t [NumSlvPorts-1:0] slv_resps; for (genvar i = 0; i < NumMstPorts; i++) begin : gen_assign_mst `AXI_ASSIGN_FROM_REQ(mst_ports[i], mst_reqs[i]) @@ -269,20 +238,9 @@ import cf_math_pkg::idx_width; .AxiMstPortMaxUniqIds ( AxiMstPortMaxUniqIds ), .AxiMstPortMaxTxnsPerId ( AxiMstPortMaxTxnsPerId ), .NumAddrRules ( NumAddrRules ), - .slv_aw_chan_t ( slv_aw_chan_t ), - .mst_aw_chan_t ( mst_aw_chan_t ), - .w_chan_t ( w_chan_t ), - .slv_b_chan_t ( slv_b_chan_t ), - .mst_b_chan_t ( mst_b_chan_t ), - .slv_ar_chan_t ( slv_ar_chan_t ), - .mst_ar_chan_t ( mst_ar_chan_t ), - .slv_r_chan_t ( slv_r_chan_t ), - .mst_r_chan_t ( mst_r_chan_t ), - .slv_req_t ( slv_req_t ), - .slv_resp_t ( slv_resp_t ), - .mst_req_t ( mst_req_t ), - .mst_resp_t ( mst_resp_t ), - .rule_t ( rule_t ) + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .rule_t ( rule_t ) ) i_xp ( .clk_i, .rst_ni, From 79aee92a8699f3ee0846ca012a1e9f2f7798da72 Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Fri, 2 Sep 2022 16:38:30 +0200 Subject: [PATCH 054/145] axi_xp: Remove axi_xp testbench --- Bender.yml | 2 - test/tb_axi_xp.sv | 468 -------------------------------------- test/tb_axi_xp_pkg.sv | 505 ------------------------------------------ 3 files changed, 975 deletions(-) delete mode 100644 test/tb_axi_xp.sv delete mode 100644 test/tb_axi_xp_pkg.sv diff --git a/Bender.yml b/Bender.yml index 542210165..97b2d9478 100644 --- a/Bender.yml +++ b/Bender.yml @@ -84,7 +84,6 @@ sources: # Level 0 - test/tb_axi_dw_pkg.sv - test/tb_axi_xbar_pkg.sv - - test/tb_axi_xp_pkg.sv # Level 1 - test/tb_axi_addr_test.sv - test/tb_axi_atop_filter.sv @@ -106,4 +105,3 @@ sources: - test/tb_axi_to_axi_lite.sv - test/tb_axi_to_mem_banked.sv - test/tb_axi_xbar.sv - - test/tb_axi_xp.sv \ No newline at end of file diff --git a/test/tb_axi_xp.sv b/test/tb_axi_xp.sv deleted file mode 100644 index ef9a3d8e1..000000000 --- a/test/tb_axi_xp.sv +++ /dev/null @@ -1,468 +0,0 @@ -// Copyright (c) 2019 ETH Zurich and 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: -// - Florian Zaruba -// - Andreas Kurth -// - Vikram Jain - -// Directed Random Verification Testbench for `axi_xp`: The crosspoint is instantiated with -// a configurable number of random axi master and slave modules. Each random master executes a -// fixed number of writes and reads over the whole addess map. All masters simultaneously issue -// transactions through the crosspoint, thereby saturating it. A monitor, which snoops the -// transactions of each master and slave port and models the crosspoint with a network of FIFOs, -// checks whether each transaction follows the expected route. - -`include "axi/typedef.svh" -`include "axi/assign.svh" - -module tb_axi_xp #( - // Testbench parameters - parameter bit TbEnAtop = 1'b1, // enable atomic operations (ATOPs) - parameter bit TbEnExcl = 1'b0, // enable exclusive accesses - parameter bit TbUniqueIds = 1'b0, // restrict to only unique IDs - parameter int unsigned TbNumMst = 32'd4, // how many AXI masters there are - parameter int unsigned TbNumSlv = 32'd4 // how many AXI slaves there are -); - // Random master no Transactions - localparam int unsigned NoWrites = 80; // How many writes per master - localparam int unsigned NoReads = 80; // How many reads per master - // timing parameters - localparam time CyclTime = 10ns; - localparam time ApplTime = 2ns; - localparam time TestTime = 8ns; - - // DUT parameters - localparam bit ATOPs = TbEnAtop; - localparam bit [TbNumSlv-1:0][TbNumMst-1:0] Connectivity = '1; - localparam int unsigned AxiSlvPortMaxUniqIds = 32'd16; - localparam int unsigned AxiSlvPortMaxTxnsPerId = 32'd128; - localparam int unsigned AxiSlvPortMaxTxns = 32'd31; - localparam int unsigned AxiMstPortMaxUniqIds = 32'd2; - localparam int unsigned AxiMstPortMaxTxnsPerId = 32'd7; - localparam int unsigned NoAddrRules = 32'd8; - - // axi configuration - localparam int unsigned AxiIdWidthMasters = 4; - localparam int unsigned AxiIdUsed = 3; // Has to be <= AxiIdWidthMasters - localparam int unsigned AxiIdWidthSlaves = AxiIdWidthMasters + $clog2(TbNumMst); - localparam int unsigned AxiAddrWidth = 32; // Axi Address Width - localparam int unsigned AxiDataWidth = 64; // Axi Data Width - localparam int unsigned AxiStrbWidth = AxiDataWidth / 8; - localparam int unsigned AxiUserWidth = 5; - localparam int unsigned AxiIdWidth = AxiIdWidthMasters; - // in the bench can change this variables which are set here freely - localparam axi_pkg::xbar_cfg_t xbar_cfg = '{ - NoSlvPorts: TbNumSlv, - NoMstPorts: TbNumMst, - MaxMstTrans: AxiSlvPortMaxTxns, - MaxSlvTrans: AxiSlvPortMaxTxnsPerId, - FallThrough: 1'b0, - LatencyMode: axi_pkg::CUT_ALL_AX, - AxiIdWidthSlvPorts: AxiIdWidthMasters, - AxiIdUsedSlvPorts: AxiIdUsed, - UniqueIds: TbUniqueIds, - AxiAddrWidth: AxiAddrWidth, - AxiDataWidth: AxiDataWidth, - NoAddrRules: 8 - }; - typedef logic [AxiIdWidthMasters-1:0] id_mst_t; - typedef logic [AxiIdWidthSlaves-1:0] id_slv_t; - typedef logic [AxiAddrWidth-1:0] addr_t; - typedef axi_pkg::xbar_rule_32_t rule_t; // Has to be the same width as axi addr - typedef logic [AxiDataWidth-1:0] data_t; - typedef logic [AxiStrbWidth-1:0] strb_t; - typedef logic [AxiUserWidth-1:0] user_t; - - `AXI_TYPEDEF_AW_CHAN_T(aw_chan_mst_t, addr_t, id_mst_t, user_t) - `AXI_TYPEDEF_AW_CHAN_T(aw_chan_slv_t, addr_t, id_slv_t, user_t) - `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) - `AXI_TYPEDEF_B_CHAN_T(b_chan_mst_t, id_mst_t, user_t) - `AXI_TYPEDEF_B_CHAN_T(b_chan_slv_t, id_slv_t, user_t) - - `AXI_TYPEDEF_AR_CHAN_T(ar_chan_mst_t, addr_t, id_mst_t, user_t) - `AXI_TYPEDEF_AR_CHAN_T(ar_chan_slv_t, addr_t, id_slv_t, user_t) - `AXI_TYPEDEF_R_CHAN_T(r_chan_mst_t, data_t, id_mst_t, user_t) - `AXI_TYPEDEF_R_CHAN_T(r_chan_slv_t, data_t, id_slv_t, user_t) - - `AXI_TYPEDEF_REQ_T(mst_req_t, aw_chan_mst_t, w_chan_t, ar_chan_mst_t) - `AXI_TYPEDEF_RESP_T(mst_resp_t, b_chan_mst_t, r_chan_mst_t) - `AXI_TYPEDEF_REQ_T(slv_req_t, aw_chan_slv_t, w_chan_t, ar_chan_slv_t) - `AXI_TYPEDEF_RESP_T(slv_resp_t, b_chan_slv_t, r_chan_slv_t) - - localparam rule_t [xbar_cfg.NoAddrRules-1:0] AddrMap = '{ - '{idx: 32'd7 % TbNumSlv, start_addr: 32'h0001_0000, end_addr: 32'h0001_1000}, - '{idx: 32'd6 % TbNumSlv, start_addr: 32'h0000_9000, end_addr: 32'h0001_0000}, - '{idx: 32'd5 % TbNumSlv, start_addr: 32'h0000_8000, end_addr: 32'h0000_9000}, - '{idx: 32'd4 % TbNumSlv, start_addr: 32'h0000_7000, end_addr: 32'h0000_8000}, - '{idx: 32'd3 % TbNumSlv, start_addr: 32'h0000_6300, end_addr: 32'h0000_7000}, - '{idx: 32'd2 % TbNumSlv, start_addr: 32'h0000_4000, end_addr: 32'h0000_6300}, - '{idx: 32'd1 % TbNumSlv, start_addr: 32'h0000_3000, end_addr: 32'h0000_4000}, - '{idx: 32'd0 % TbNumSlv, start_addr: 32'h0000_0000, end_addr: 32'h0000_3000} - }; - - typedef axi_test::axi_rand_master #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidthMasters ), - .UW ( AxiUserWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ), - // Maximum number of read and write transactions in flight - .MAX_READ_TXNS ( 20 ), - .MAX_WRITE_TXNS ( 20 ), - .AXI_EXCLS ( TbEnExcl ), - .AXI_ATOPS ( TbEnAtop ), - .UNIQUE_IDS ( TbUniqueIds ) - ) axi_rand_master_t; - typedef axi_test::axi_rand_slave #( - // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidthSlaves ), - .UW ( AxiUserWidth ), - // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ) - ) axi_rand_slave_t; - - // ------------- - // DUT signals - // ------------- - logic clk; - // DUT signals - logic rst_n; - logic [TbNumMst-1:0] end_of_sim; - - // master structs - mst_req_t [TbNumMst-1:0] masters_req; - mst_resp_t [TbNumMst-1:0] masters_resp; - - // slave structs - slv_req_t [TbNumSlv-1:0] slaves_req; - slv_resp_t [TbNumSlv-1:0] slaves_resp; - - // ------------------------------- - // AXI Interfaces - // ------------------------------- - AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master [TbNumMst-1:0] (); - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master_dv [TbNumMst-1:0] (clk); - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master_monitor_dv [TbNumMst-1:0] (clk); - for (genvar i = 0; i < TbNumMst; i++) begin : gen_conn_dv_masters - `AXI_ASSIGN (master[i], master_dv[i]) - `AXI_ASSIGN_TO_REQ(masters_req[i], master[i]) - `AXI_ASSIGN_TO_RESP(masters_resp[i], master[i]) - end - - AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave [TbNumSlv-1:0] (); - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave_dv [TbNumSlv-1:0](clk); - AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave_monitor_dv [TbNumSlv-1:0](clk); - for (genvar i = 0; i < TbNumSlv; i++) begin : gen_conn_dv_slaves - `AXI_ASSIGN(slave_dv[i], slave[i]) - `AXI_ASSIGN_TO_REQ(slaves_req[i], slave[i]) - `AXI_ASSIGN_TO_RESP(slaves_resp[i], slave[i]) - end - // ------------------------------- - // AXI Rand Masters and Slaves - // ------------------------------- - // Masters control simulation run time - axi_rand_master_t axi_rand_master [TbNumMst]; - for (genvar i = 0; i < TbNumMst; i++) begin : gen_rand_master - initial begin - axi_rand_master[i] = new( master_dv[i] ); - end_of_sim[i] <= 1'b0; - axi_rand_master[i].add_memory_region(AddrMap[0].start_addr, - AddrMap[xbar_cfg.NoAddrRules-1].end_addr, - axi_pkg::DEVICE_NONBUFFERABLE); - axi_rand_master[i].reset(); - @(posedge rst_n); - axi_rand_master[i].run(NoReads, NoWrites); - end_of_sim[i] <= 1'b1; - end - end - - axi_rand_slave_t axi_rand_slave [TbNumSlv]; - for (genvar i = 0; i < TbNumSlv; i++) begin : gen_rand_slave - initial begin - axi_rand_slave[i] = new( slave_dv[i] ); - axi_rand_slave[i].reset(); - @(posedge rst_n); - axi_rand_slave[i].run(); - end - end - - initial begin : proc_monitor - static tb_axi_xp_pkg::axi_xp_monitor #( - .AxiAddrWidth ( AxiAddrWidth ), - .AxiDataWidth ( AxiDataWidth ), - .AxiIdWidthMasters ( AxiIdWidthMasters ), - .AxiIdWidthSlaves ( AxiIdWidthSlaves ), - .AxiUserWidth ( AxiUserWidth ), - .NumMasters ( TbNumMst ), - .NumSlaves ( TbNumSlv ), - .NumAddrRules ( xbar_cfg.NoAddrRules ), - .rule_t ( rule_t ), - .AddrMap ( AddrMap ), - .TimeTest ( TestTime ) - ) monitor = new( master_monitor_dv, slave_monitor_dv ); - fork - monitor.run(); - do begin - #TestTime; - if(end_of_sim == '1) begin - monitor.print_result(); - $stop(); - end - @(posedge clk); - end while (1'b1); - join - end - - //----------------------------------- - // Clock generator - //----------------------------------- - clk_rst_gen #( - .ClkPeriod ( CyclTime ), - .RstClkCycles ( 5 ) - ) i_clk_gen ( - .clk_o (clk), - .rst_no(rst_n) - ); - - //----------------------------------- - // DUT - //----------------------------------- - axi_xp_intf #( - .ATOPs ( ATOPs ), - .Cfg ( xbar_cfg ), - .NumSlvPorts ( xbar_cfg.NoSlvPorts ), - .NumMstPorts ( xbar_cfg.NoMstPorts ), - .Connectivity ( Connectivity ), - .AxiAddrWidth ( AxiAddrWidth ), - .AxiDataWidth ( AxiDataWidth ), - .AxiIdWidth ( AxiIdWidth ), - .AxiUserWidth ( AxiUserWidth ), - .AxiSlvPortMaxUniqIds ( AxiSlvPortMaxUniqIds ), - .AxiSlvPortMaxTxnsPerId ( AxiSlvPortMaxTxnsPerId ), - .AxiSlvPortMaxTxns ( AxiSlvPortMaxTxns ), - .AxiMstPortMaxUniqIds ( AxiMstPortMaxUniqIds ), - .AxiMstPortMaxTxnsPerId ( AxiMstPortMaxTxnsPerId ), - .NumAddrRules ( xbar_cfg.NoAddrRules ), - .rule_t ( rule_t ) - ) i_xp_dut ( - .clk_i ( clk ), - .rst_ni ( rst_n ), - .test_en_i ( 1'b0 ), - .slv_ports ( master ), - .mst_ports ( slave ), - .addr_map_i ( AddrMap ) - ); - - // logger for master modules - for (genvar i = 0; i < TbNumMst; i++) begin : gen_master_logger - axi_chan_logger #( - .TestTime ( TestTime ), // Time after clock, where sampling happens - .LoggerName( $sformatf("axi_logger_master_%0d", i)), - .aw_chan_t ( aw_chan_mst_t ), // axi AW type - .w_chan_t ( w_chan_t ), // axi W type - .b_chan_t ( b_chan_mst_t ), // axi B type - .ar_chan_t ( ar_chan_mst_t ), // axi AR type - .r_chan_t ( r_chan_mst_t ) // axi R type - ) i_mst_channel_logger ( - .clk_i ( clk ), // Clock - .rst_ni ( rst_n ), // Asynchronous reset active low, when `1'b0` no sampling - .end_sim_i ( &end_of_sim ), - // AW channel - .aw_chan_i ( masters_req[i].aw ), - .aw_valid_i ( masters_req[i].aw_valid ), - .aw_ready_i ( masters_resp[i].aw_ready ), - // W channel - .w_chan_i ( masters_req[i].w ), - .w_valid_i ( masters_req[i].w_valid ), - .w_ready_i ( masters_resp[i].w_ready ), - // B channel - .b_chan_i ( masters_resp[i].b ), - .b_valid_i ( masters_resp[i].b_valid ), - .b_ready_i ( masters_req[i].b_ready ), - // AR channel - .ar_chan_i ( masters_req[i].ar ), - .ar_valid_i ( masters_req[i].ar_valid ), - .ar_ready_i ( masters_resp[i].ar_ready ), - // R channel - .r_chan_i ( masters_resp[i].r ), - .r_valid_i ( masters_resp[i].r_valid ), - .r_ready_i ( masters_req[i].r_ready ) - ); - end - // logger for slave modules - for (genvar i = 0; i < TbNumSlv; i++) begin : gen_slave_logger - axi_chan_logger #( - .TestTime ( TestTime ), // Time after clock, where sampling happens - .LoggerName( $sformatf("axi_logger_slave_%0d",i)), - .aw_chan_t ( aw_chan_slv_t ), // axi AW type - .w_chan_t ( w_chan_t ), // axi W type - .b_chan_t ( b_chan_slv_t ), // axi B type - .ar_chan_t ( ar_chan_slv_t ), // axi AR type - .r_chan_t ( r_chan_slv_t ) // axi R type - ) i_slv_channel_logger ( - .clk_i ( clk ), // Clock - .rst_ni ( rst_n ), // Asynchronous reset active low, when `1'b0` no sampling - .end_sim_i ( &end_of_sim ), - // AW channel - .aw_chan_i ( slaves_req[i].aw ), - .aw_valid_i ( slaves_req[i].aw_valid ), - .aw_ready_i ( slaves_resp[i].aw_ready ), - // W channel - .w_chan_i ( slaves_req[i].w ), - .w_valid_i ( slaves_req[i].w_valid ), - .w_ready_i ( slaves_resp[i].w_ready ), - // B channel - .b_chan_i ( slaves_resp[i].b ), - .b_valid_i ( slaves_resp[i].b_valid ), - .b_ready_i ( slaves_req[i].b_ready ), - // AR channel - .ar_chan_i ( slaves_req[i].ar ), - .ar_valid_i ( slaves_req[i].ar_valid ), - .ar_ready_i ( slaves_resp[i].ar_ready ), - // R channel - .r_chan_i ( slaves_resp[i].r ), - .r_valid_i ( slaves_resp[i].r_valid ), - .r_ready_i ( slaves_req[i].r_ready ) - ); - end - - - for (genvar i = 0; i < TbNumMst; i++) begin : gen_connect_master_monitor - assign master_monitor_dv[i].aw_id = master[i].aw_id ; - assign master_monitor_dv[i].aw_addr = master[i].aw_addr ; - assign master_monitor_dv[i].aw_len = master[i].aw_len ; - assign master_monitor_dv[i].aw_size = master[i].aw_size ; - assign master_monitor_dv[i].aw_burst = master[i].aw_burst ; - assign master_monitor_dv[i].aw_lock = master[i].aw_lock ; - assign master_monitor_dv[i].aw_cache = master[i].aw_cache ; - assign master_monitor_dv[i].aw_prot = master[i].aw_prot ; - assign master_monitor_dv[i].aw_qos = master[i].aw_qos ; - assign master_monitor_dv[i].aw_region = master[i].aw_region; - assign master_monitor_dv[i].aw_atop = master[i].aw_atop ; - assign master_monitor_dv[i].aw_user = master[i].aw_user ; - assign master_monitor_dv[i].aw_valid = master[i].aw_valid ; - assign master_monitor_dv[i].aw_ready = master[i].aw_ready ; - assign master_monitor_dv[i].w_data = master[i].w_data ; - assign master_monitor_dv[i].w_strb = master[i].w_strb ; - assign master_monitor_dv[i].w_last = master[i].w_last ; - assign master_monitor_dv[i].w_user = master[i].w_user ; - assign master_monitor_dv[i].w_valid = master[i].w_valid ; - assign master_monitor_dv[i].w_ready = master[i].w_ready ; - assign master_monitor_dv[i].b_id = master[i].b_id ; - assign master_monitor_dv[i].b_resp = master[i].b_resp ; - assign master_monitor_dv[i].b_user = master[i].b_user ; - assign master_monitor_dv[i].b_valid = master[i].b_valid ; - assign master_monitor_dv[i].b_ready = master[i].b_ready ; - assign master_monitor_dv[i].ar_id = master[i].ar_id ; - assign master_monitor_dv[i].ar_addr = master[i].ar_addr ; - assign master_monitor_dv[i].ar_len = master[i].ar_len ; - assign master_monitor_dv[i].ar_size = master[i].ar_size ; - assign master_monitor_dv[i].ar_burst = master[i].ar_burst ; - assign master_monitor_dv[i].ar_lock = master[i].ar_lock ; - assign master_monitor_dv[i].ar_cache = master[i].ar_cache ; - assign master_monitor_dv[i].ar_prot = master[i].ar_prot ; - assign master_monitor_dv[i].ar_qos = master[i].ar_qos ; - assign master_monitor_dv[i].ar_region = master[i].ar_region; - assign master_monitor_dv[i].ar_user = master[i].ar_user ; - assign master_monitor_dv[i].ar_valid = master[i].ar_valid ; - assign master_monitor_dv[i].ar_ready = master[i].ar_ready ; - assign master_monitor_dv[i].r_id = master[i].r_id ; - assign master_monitor_dv[i].r_data = master[i].r_data ; - assign master_monitor_dv[i].r_resp = master[i].r_resp ; - assign master_monitor_dv[i].r_last = master[i].r_last ; - assign master_monitor_dv[i].r_user = master[i].r_user ; - assign master_monitor_dv[i].r_valid = master[i].r_valid ; - assign master_monitor_dv[i].r_ready = master[i].r_ready ; - end - for (genvar i = 0; i < TbNumSlv; i++) begin : gen_connect_slave_monitor - assign slave_monitor_dv[i].aw_id = slave[i].aw_id ; - assign slave_monitor_dv[i].aw_addr = slave[i].aw_addr ; - assign slave_monitor_dv[i].aw_len = slave[i].aw_len ; - assign slave_monitor_dv[i].aw_size = slave[i].aw_size ; - assign slave_monitor_dv[i].aw_burst = slave[i].aw_burst ; - assign slave_monitor_dv[i].aw_lock = slave[i].aw_lock ; - assign slave_monitor_dv[i].aw_cache = slave[i].aw_cache ; - assign slave_monitor_dv[i].aw_prot = slave[i].aw_prot ; - assign slave_monitor_dv[i].aw_qos = slave[i].aw_qos ; - assign slave_monitor_dv[i].aw_region = slave[i].aw_region; - assign slave_monitor_dv[i].aw_atop = slave[i].aw_atop ; - assign slave_monitor_dv[i].aw_user = slave[i].aw_user ; - assign slave_monitor_dv[i].aw_valid = slave[i].aw_valid ; - assign slave_monitor_dv[i].aw_ready = slave[i].aw_ready ; - assign slave_monitor_dv[i].w_data = slave[i].w_data ; - assign slave_monitor_dv[i].w_strb = slave[i].w_strb ; - assign slave_monitor_dv[i].w_last = slave[i].w_last ; - assign slave_monitor_dv[i].w_user = slave[i].w_user ; - assign slave_monitor_dv[i].w_valid = slave[i].w_valid ; - assign slave_monitor_dv[i].w_ready = slave[i].w_ready ; - assign slave_monitor_dv[i].b_id = slave[i].b_id ; - assign slave_monitor_dv[i].b_resp = slave[i].b_resp ; - assign slave_monitor_dv[i].b_user = slave[i].b_user ; - assign slave_monitor_dv[i].b_valid = slave[i].b_valid ; - assign slave_monitor_dv[i].b_ready = slave[i].b_ready ; - assign slave_monitor_dv[i].ar_id = slave[i].ar_id ; - assign slave_monitor_dv[i].ar_addr = slave[i].ar_addr ; - assign slave_monitor_dv[i].ar_len = slave[i].ar_len ; - assign slave_monitor_dv[i].ar_size = slave[i].ar_size ; - assign slave_monitor_dv[i].ar_burst = slave[i].ar_burst ; - assign slave_monitor_dv[i].ar_lock = slave[i].ar_lock ; - assign slave_monitor_dv[i].ar_cache = slave[i].ar_cache ; - assign slave_monitor_dv[i].ar_prot = slave[i].ar_prot ; - assign slave_monitor_dv[i].ar_qos = slave[i].ar_qos ; - assign slave_monitor_dv[i].ar_region = slave[i].ar_region; - assign slave_monitor_dv[i].ar_user = slave[i].ar_user ; - assign slave_monitor_dv[i].ar_valid = slave[i].ar_valid ; - assign slave_monitor_dv[i].ar_ready = slave[i].ar_ready ; - assign slave_monitor_dv[i].r_id = slave[i].r_id ; - assign slave_monitor_dv[i].r_data = slave[i].r_data ; - assign slave_monitor_dv[i].r_resp = slave[i].r_resp ; - assign slave_monitor_dv[i].r_last = slave[i].r_last ; - assign slave_monitor_dv[i].r_user = slave[i].r_user ; - assign slave_monitor_dv[i].r_valid = slave[i].r_valid ; - assign slave_monitor_dv[i].r_ready = slave[i].r_ready ; - end -endmodule diff --git a/test/tb_axi_xp_pkg.sv b/test/tb_axi_xp_pkg.sv deleted file mode 100644 index 606895467..000000000 --- a/test/tb_axi_xp_pkg.sv +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright (c) 2019 ETH Zurich and 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: -// - Florian Zaruba -// - Wolfgang Roenninger -// - Vikram Jain - -// `axi_xp_monitor` implements an AXI bus monitor that is tuned for the AXI crosspoint. -// It snoops on each of the slaves and master ports of the crosspoint and -// populates FIFOs and ID queues to validate that no AXI beats get -// lost or sent to the wrong destination. It ignores the ID checks for the AW and AR -// channels as they get remapped in the id_remapper - -package tb_axi_xp_pkg; - class axi_xp_monitor #( - parameter int unsigned AxiAddrWidth, - parameter int unsigned AxiDataWidth, - parameter int unsigned AxiIdWidthMasters, - parameter int unsigned AxiIdWidthSlaves, - parameter int unsigned AxiUserWidth, - parameter int unsigned NumMasters, - parameter int unsigned NumSlaves, - parameter int unsigned NumAddrRules, - parameter type rule_t, - parameter rule_t [NumAddrRules-1:0] AddrMap, - // Stimuli application and test time - parameter time TimeTest - ); - typedef logic [AxiIdWidthMasters-1:0] mst_axi_id_t; - typedef logic [AxiIdWidthSlaves-1:0] slv_axi_id_t; - typedef logic [AxiAddrWidth-1:0] axi_addr_t; - - typedef logic [$clog2(NumMasters)-1:0] idx_mst_t; - typedef int unsigned idx_slv_t; // from rule_t - - typedef struct packed { - mst_axi_id_t mst_axi_id; - logic last; - } master_exp_t; - typedef struct packed { - slv_axi_id_t slv_axi_id; - axi_addr_t slv_axi_addr; - axi_pkg::len_t slv_axi_len; - } exp_ax_t; - typedef struct packed { - slv_axi_id_t slv_axi_id; - logic last; - } slave_exp_t; - - typedef rand_id_queue_pkg::rand_id_queue #( - .data_t ( master_exp_t ), - .ID_WIDTH ( AxiIdWidthMasters ) - ) master_exp_queue_t; - typedef rand_id_queue_pkg::rand_id_queue #( - .data_t ( exp_ax_t ), - .ID_WIDTH ( AxiIdWidthSlaves ) - ) ax_queue_t; - - typedef rand_id_queue_pkg::rand_id_queue #( - .data_t ( slave_exp_t ), - .ID_WIDTH ( AxiIdWidthSlaves ) - ) slave_exp_queue_t; - - //----------------------------------------- - // Monitoring virtual interfaces - //----------------------------------------- - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) masters_axi [NumMasters-1:0]; - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slaves_axi [NumSlaves-1:0]; - //----------------------------------------- - // Queues and FIFOs to hold the expected ids - //----------------------------------------- - // Write transactions - ax_queue_t exp_aw_queue [NumSlaves-1:0]; - slave_exp_t exp_w_fifo [NumSlaves-1:0][$]; - slave_exp_t act_w_fifo [NumSlaves-1:0][$]; - master_exp_queue_t exp_b_queue [NumMasters-1:0]; - - // Read transactions - ax_queue_t exp_ar_queue [NumSlaves-1:0]; - master_exp_queue_t exp_r_queue [NumMasters-1:0]; - - //----------------------------------------- - // Bookkeeping - //----------------------------------------- - longint unsigned tests_expected; - longint unsigned tests_conducted; - longint unsigned tests_failed; - semaphore cnt_sem; - - //----------------------------------------- - // Constructor - //----------------------------------------- - function new( - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) axi_masters_vif [NumMasters-1:0], - virtual AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) axi_slaves_vif [NumSlaves-1:0] - ); - begin - this.masters_axi = axi_masters_vif; - this.slaves_axi = axi_slaves_vif; - this.tests_expected = 0; - this.tests_conducted = 0; - this.tests_failed = 0; - for (int unsigned i = 0; i < NumMasters; i++) begin - this.exp_b_queue[i] = new; - this.exp_r_queue[i] = new; - end - for (int unsigned i = 0; i < NumSlaves; i++) begin - this.exp_aw_queue[i] = new; - this.exp_ar_queue[i] = new; - end - this.cnt_sem = new(1); - end - endfunction - - // when start the testing - task cycle_start; - #TimeTest; - endtask - - // when is cycle finished - task cycle_end; - @(posedge masters_axi[0].clk_i); - endtask - - // This task monitors a slave ports of the crossbar. Every time an AW beat is seen - // it populates an id queue at the right master port (if there is no expected decode error), - // populates the expected b response in its own id_queue and in case when the atomic bit [5] - // is set it also injects an expected response in the R channel. - task automatic monitor_mst_aw(input int unsigned i); - idx_slv_t to_slave_idx; - exp_ax_t exp_aw; - slv_axi_id_t exp_aw_id; - bit decerr; - - master_exp_t exp_b; - - if (masters_axi[i].aw_valid && masters_axi[i].aw_ready) begin - // check if it should go to a decerror - decerr = 1'b1; - for (int unsigned j = 0; j < NumAddrRules; j++) begin - if ((masters_axi[i].aw_addr >= AddrMap[j].start_addr) && - (masters_axi[i].aw_addr < AddrMap[j].end_addr)) begin - to_slave_idx = idx_slv_t'(AddrMap[j].idx); - decerr = 1'b0; - end - end - // send the exp aw beat down into the queue of the slave when no decerror - if (!decerr) begin - exp_aw_id = {idx_mst_t'(i), masters_axi[i].aw_id}; - // $display("Test exp aw_id: %b",exp_aw_id); - exp_aw = '{slv_axi_id: exp_aw_id, - slv_axi_addr: masters_axi[i].aw_addr, - slv_axi_len: masters_axi[i].aw_len }; - this.exp_aw_queue[to_slave_idx].push(exp_aw_id, exp_aw); - incr_expected_tests(3); - $display("%0tns > Master %0d: AW to Slave %0d: Axi ID: %b", - $time, i, to_slave_idx, masters_axi[i].aw_id); - end else begin - $display("%0tns > Master %0d: AW to Decerror: Axi ID: %b", - $time, i, to_slave_idx, masters_axi[i].aw_id); - end - // populate the expected b queue anyway - exp_b = '{mst_axi_id: masters_axi[i].aw_id, last: 1'b1}; - this.exp_b_queue[i].push(masters_axi[i].aw_id, exp_b); - incr_expected_tests(1); - $display(" Expect B response."); - // inject expected r beats on this id, if it is an atop - if(masters_axi[i].aw_atop[5]) begin - // push the required r beats into the right fifo (reuse the exp_b variable) - $display(" Expect R response, len: %0d.", masters_axi[i].aw_len); - for (int unsigned j = 0; j <= masters_axi[i].aw_len; j++) begin - exp_b = (j == masters_axi[i].aw_len) ? - '{mst_axi_id: masters_axi[i].aw_id, last: 1'b1} : - '{mst_axi_id: masters_axi[i].aw_id, last: 1'b0}; - this.exp_r_queue[i].push(masters_axi[i].aw_id, exp_b); - incr_expected_tests(1); - end - end - end - endtask : monitor_mst_aw - - // This task monitors a slave port of the crossbar. Every time there is an AW vector it - // gets checked for its contents and if it was expected. The task then pushes an expected - // amount of W beats in the respective fifo. Emphasis of the last flag. - task automatic monitor_slv_aw(input int unsigned i); - exp_ax_t exp_aw; - slave_exp_t exp_slv_w; - // $display("%0t > Was triggered: aw_valid %b, aw_ready: %b", - // $time(), slaves_axi[i].aw_valid, slaves_axi[i].aw_ready); - if (slaves_axi[i].aw_valid && slaves_axi[i].aw_ready) begin - // test if the aw beat was expected - exp_aw = this.exp_aw_queue[i].pop_id(slaves_axi[i].aw_id); - $display("%0tns > Slave %0d: AW Axi ID: %b", - $time, i, slaves_axi[i].aw_id); - if (exp_aw.slv_axi_id != slaves_axi[i].aw_id) begin - incr_failed_tests(1); - $warning("Slave %0d: Unexpected AW with ID: %b", i, slaves_axi[i].aw_id); - end - if (exp_aw.slv_axi_addr != slaves_axi[i].aw_addr) begin - incr_failed_tests(1); - $warning("Slave %0d: Unexpected AW with ID: %b and ADDR: %h, exp: %h", - i, slaves_axi[i].aw_id, slaves_axi[i].aw_addr, exp_aw.slv_axi_addr); - end - if (exp_aw.slv_axi_len != slaves_axi[i].aw_len) begin - incr_failed_tests(1); - $warning("Slave %0d: Unexpected AW with ID: %b and LEN: %h, exp: %h", - i, slaves_axi[i].aw_id, slaves_axi[i].aw_len, exp_aw.slv_axi_len); - end - incr_conducted_tests(3); - - // push the required w beats into the right fifo - incr_expected_tests(slaves_axi[i].aw_len + 1); - for (int unsigned j = 0; j <= slaves_axi[i].aw_len; j++) begin - exp_slv_w = (j == slaves_axi[i].aw_len) ? - '{slv_axi_id: slaves_axi[i].aw_id, last: 1'b1} : - '{slv_axi_id: slaves_axi[i].aw_id, last: 1'b0}; - this.exp_w_fifo[i].push_back(exp_slv_w); - end - end - endtask : monitor_slv_aw - - // This task just pushes every W beat that gets sent on a master port in its respective fifo. - task automatic monitor_slv_w(input int unsigned i); - slave_exp_t act_slv_w; - if (slaves_axi[i].w_valid && slaves_axi[i].w_ready) begin - // $display("%0t > W beat on Slave %0d, last flag: %b", $time, i, slaves_axi[i].w_last); - act_slv_w = '{last: slaves_axi[i].w_last , default:'0}; - this.act_w_fifo[i].push_back(act_slv_w); - end - endtask : monitor_slv_w - - // This task compares the expected and actual W beats on a master port. The reason that - // this is not done in `monitor_slv_w` is that there can be per protocol W beats on the - // channel, before AW is sent to the slave. - task automatic check_slv_w(input int unsigned i); - slave_exp_t exp_w, act_w; - while (this.exp_w_fifo[i].size() != 0 && this.act_w_fifo[i].size() != 0) begin - - exp_w = this.exp_w_fifo[i].pop_front(); - act_w = this.act_w_fifo[i].pop_front(); - // do the check - incr_conducted_tests(1); - if(exp_w.last != act_w.last) begin - incr_failed_tests(1); - $warning("Slave %d: unexpected W beat last flag %b, expected: %b.", - i, act_w.last, exp_w.last); - end - end - endtask : check_slv_w - - // This task checks if a B response is allowed on a slave port of the crossbar. - task automatic monitor_mst_b(input int unsigned i); - master_exp_t exp_b; - mst_axi_id_t axi_b_id; - if (masters_axi[i].b_valid && masters_axi[i].b_ready) begin - incr_conducted_tests(1); - axi_b_id = masters_axi[i].b_id; - $display("%0tns > Master %0d: Got last B with id: %b", - $time, i, axi_b_id); - if (this.exp_b_queue[i].empty()) begin - incr_failed_tests(1); - $warning("Master %d: unexpected B beat with ID: %b detected!", i, axi_b_id); - end else begin - exp_b = this.exp_b_queue[i].pop_id(axi_b_id); - if (axi_b_id != exp_b.mst_axi_id) begin - incr_failed_tests(1); - $warning("Master: %d got unexpected B with ID: %b", i, axi_b_id); - end - end - end - endtask : monitor_mst_b - - // This task monitors the AR channel of a slave port of the crossbar. For each AR it populates - // the corresponding ID queue with the number of r beats indicated on the `ar_len` field. - // Emphasis on the last flag. We will detect reordering, if the last flags do not match, - // as each `random` burst tend to have a different length. - task automatic monitor_mst_ar(input int unsigned i); - mst_axi_id_t mst_axi_id; - axi_addr_t mst_axi_addr; - axi_pkg::len_t mst_axi_len; - - idx_slv_t exp_slv_idx; - slv_axi_id_t exp_slv_axi_id; - exp_ax_t exp_slv_ar; - master_exp_t exp_mst_r; - - logic exp_decerr; - - if (masters_axi[i].ar_valid && masters_axi[i].ar_ready) begin - exp_decerr = 1'b1; - mst_axi_id = masters_axi[i].ar_id; - mst_axi_addr = masters_axi[i].ar_addr; - mst_axi_len = masters_axi[i].ar_len; - exp_slv_axi_id = {idx_mst_t'(i), mst_axi_id}; - exp_slv_idx = '0; - for (int unsigned j = 0; j < NumAddrRules; j++) begin - if ((mst_axi_addr >= AddrMap[j].start_addr) && (mst_axi_addr < AddrMap[j].end_addr)) begin - exp_slv_idx = AddrMap[j].idx; - exp_decerr = 1'b0; - end - end - if (exp_decerr) begin - $display("%0tns > Master %0d: AR to Decerror: Axi ID: %b", - $time, i, mst_axi_id); - end else begin - $display("%0tns > Master %0d: AR to Slave %0d: Axi ID: %b", - $time, i, exp_slv_idx, mst_axi_id); - // push the expected vectors AW for exp_slv - exp_slv_ar = '{slv_axi_id: exp_slv_axi_id, - slv_axi_addr: mst_axi_addr, - slv_axi_len: mst_axi_len }; - //$display("Expected Slv Axi Id is: %b", exp_slv_axi_id); - this.exp_ar_queue[exp_slv_idx].push(exp_slv_axi_id, exp_slv_ar); - incr_expected_tests(1); - end - // push the required r beats into the right fifo - $display(" Expect R response, len: %0d.", masters_axi[i].ar_len); - for (int unsigned j = 0; j <= mst_axi_len; j++) begin - exp_mst_r = (j == mst_axi_len) ? '{mst_axi_id: mst_axi_id, last: 1'b1} : - '{mst_axi_id: mst_axi_id, last: 1'b0}; - this.exp_r_queue[i].push(mst_axi_id, exp_mst_r); - incr_expected_tests(1); - end - end - endtask : monitor_mst_ar - - // This task monitors a master port of the crossbar and checks if a transmitted AR beat was - // expected. - task automatic monitor_slv_ar(input int unsigned i); - exp_ax_t exp_slv_ar; - slv_axi_id_t slv_axi_id; - if (slaves_axi[i].ar_valid && slaves_axi[i].ar_ready) begin - incr_conducted_tests(1); - slv_axi_id = slaves_axi[i].ar_id; - if (this.exp_ar_queue[i].empty()) begin - incr_failed_tests(1); - end else begin - // check that the ids are the same - exp_slv_ar = this.exp_ar_queue[i].pop_id(slv_axi_id); - $display("%0tns > Slave %0d: AR Axi ID: %b", $time, i, slv_axi_id); - if (exp_slv_ar.slv_axi_id != slv_axi_id) begin - incr_failed_tests(1); - $warning("Slave %d: Unexpected AR with ID: %b", i, slv_axi_id); - end - end - end - endtask : monitor_slv_ar - - // This task does the R channel monitoring on a slave port. It compares the last flags, - // which are determined by the sequence of previously sent AR vectors. - task automatic monitor_mst_r(input int unsigned i); - master_exp_t exp_mst_r; - mst_axi_id_t mst_axi_r_id; - logic mst_axi_r_last; - if (masters_axi[i].r_valid && masters_axi[i].r_ready) begin - incr_conducted_tests(1); - mst_axi_r_id = masters_axi[i].r_id; - mst_axi_r_last = masters_axi[i].r_last; - if (mst_axi_r_last) begin - $display("%0tns > Master %0d: Got last R with id: %b", - $time, i, mst_axi_r_id); - end - if (this.exp_r_queue[i].empty()) begin - incr_failed_tests(1); - $warning("Master %d: unexpected R beat with ID: %b detected!", i, mst_axi_r_id); - end else begin - exp_mst_r = this.exp_r_queue[i].pop_id(mst_axi_r_id); - if (mst_axi_r_id != exp_mst_r.mst_axi_id) begin - incr_failed_tests(1); - $warning("Master: %d got unexpected R with ID: %b", i, mst_axi_r_id); - end - if (mst_axi_r_last != exp_mst_r.last) begin - incr_failed_tests(1); - $warning("Master: %d got unexpected R with ID: %b and last flag: %b", - i, mst_axi_r_id, mst_axi_r_last); - end - end - end - endtask : monitor_mst_r - - // Some tasks to manage bookkeeping of the tests conducted. - task incr_expected_tests(input int unsigned times); - cnt_sem.get(); - this.tests_expected += times; - cnt_sem.put(); - endtask : incr_expected_tests - - task incr_conducted_tests(input int unsigned times); - cnt_sem.get(); - this.tests_conducted += times; - cnt_sem.put(); - endtask : incr_conducted_tests - - task incr_failed_tests(input int unsigned times); - cnt_sem.get(); - this.tests_failed += times; - cnt_sem.put(); - endtask : incr_failed_tests - - // This task invokes the various monitoring tasks. It first forks in two, spitting - // the tasks that should continuously run and the ones that get invoked every clock cycle. - // For the tasks every clock cycle all processes that only push something in the fifo's and - // Queues get run. When they are finished the processes that pop something get run. - task run(); - Continous: fork - begin - do begin - cycle_start(); - // at every cycle span some monitoring processes - // execute all processes that put something into the queues - PushMon: fork - proc_mst_aw: begin - for (int unsigned i = 0; i < NumMasters; i++) begin - monitor_mst_aw(i); - end - end - proc_mst_ar: begin - for (int unsigned i = 0; i < NumMasters; i++) begin - monitor_mst_ar(i); - end - end - join : PushMon - // this one pops and pushes something - proc_slv_aw: begin - for (int unsigned i = 0; i < NumSlaves; i++) begin - monitor_slv_aw(i); - end - end - proc_slv_w: begin - for (int unsigned i = 0; i < NumSlaves; i++) begin - monitor_slv_w(i); - end - end - // These only pop somethong from the queses - PopMon: fork - proc_mst_b: begin - for (int unsigned i = 0; i < NumMasters; i++) begin - monitor_mst_b(i); - end - end - proc_slv_ar: begin - for (int unsigned i = 0; i < NumSlaves; i++) begin - monitor_slv_ar(i); - end - end - proc_mst_r: begin - for (int unsigned i = 0; i < NumMasters; i++) begin - monitor_mst_r(i); - end - end - join : PopMon - // check the slave W fifos last - proc_check_slv_w: begin - for (int unsigned i = 0; i < NumSlaves; i++) begin - check_slv_w(i); - end - end - cycle_end(); - end while (1'b1); - end - join - endtask : run - - task print_result(); - $info("Simulation has ended!"); - $display("Tests Expected: %d", this.tests_expected); - $display("Tests Conducted: %d", this.tests_conducted); - $display("Tests Failed: %d", this.tests_failed); - if(tests_failed > 0) begin - $error("Simulation encountered unexpected Transactions!!!!!!"); - end - if(tests_conducted == 0) begin - $error("Simulation did not conduct any tests!"); - end - endtask : print_result - endclass : axi_xp_monitor -endpackage From daf03126e2945518a5a5c8bd0f14a54b14637849 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 15 Sep 2022 14:58:37 +0200 Subject: [PATCH 055/145] authors: Use `scripts/update_authors` --- Bender.yml | 13 ++++++++++--- include/axi/assign.svh | 1 - scripts/run_verilator.sh | 5 +++++ scripts/run_vsim.sh | 1 - scripts/update_authors | 4 ++++ src/axi_cdc.sv | 4 +--- src/axi_cdc_dst.sv | 4 +--- src/axi_cdc_src.sv | 4 +--- src/axi_fifo.sv | 4 +++- src/axi_id_remap.sv | 7 ++++--- src/axi_id_serialize.sv | 3 +-- src/axi_iw_converter.sv | 6 +++--- src/axi_multicut.sv | 2 +- src/axi_sim_mem.sv | 3 +++ src/axi_test.sv | 3 +-- src/axi_throttle.sv | 3 ++- src/axi_to_axi_lite.sv | 1 - src/axi_to_mem.sv | 3 +-- src/axi_to_mem_banked.sv | 1 + src/axi_to_mem_interleaved.sv | 3 ++- src/axi_to_mem_split.sv | 6 +++--- test/axi_synth_bench.sv | 1 + test/tb_axi_dw_downsizer.sv | 1 + test/tb_axi_dw_upsizer.sv | 1 + test/tb_axi_fifo.sv | 4 +--- test/tb_axi_iw_converter.sv | 6 +++--- test/tb_axi_lite_regs.sv | 1 + test/tb_axi_lite_to_axi.sv | 2 +- test/tb_axi_sim_mem.sv | 1 + test/tb_axi_to_mem_banked.sv | 1 + 30 files changed, 58 insertions(+), 41 deletions(-) diff --git a/Bender.yml b/Bender.yml index 97b2d9478..4f4914d90 100644 --- a/Bender.yml +++ b/Bender.yml @@ -1,11 +1,18 @@ package: name: axi authors: - - "Andreas Kurth " # current maintainer + # Alphabetically ordered by last name (maintainers first) + - "Thomas Benz " # current maintainer + - "Michael Rogenmoser " # current maintainer + - "Matheus Cavalcante " + - "Noah Huetter " + - "Andreas Kurth " + - "Stefan Mach " + - "Samuel Riedel " + - "Wolfgang Rönninger " - "Fabian Schuiki " + - "Luca Valente " - "Florian Zaruba " - - "Matheus Cavalcante " - - "Wolfgang Roenninger " dependencies: common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.26.0 } diff --git a/include/axi/assign.svh b/include/axi/assign.svh index 37fdec958..a4ade2387 100644 --- a/include/axi/assign.svh +++ b/include/axi/assign.svh @@ -11,7 +11,6 @@ // // Authors: // - Andreas Kurth -// - Wolfgang Roenninger // Macros to assign AXI Interfaces and Structs diff --git a/scripts/run_verilator.sh b/scripts/run_verilator.sh index a65cd091a..ef21af120 100755 --- a/scripts/run_verilator.sh +++ b/scripts/run_verilator.sh @@ -9,6 +9,11 @@ # 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: +# - Fabian Schuiki +# - Florian Zaruba +# - Andreas Kurth set -e ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh index a6b2678b2..3737ba5d3 100755 --- a/scripts/run_vsim.sh +++ b/scripts/run_vsim.sh @@ -13,7 +13,6 @@ # Authors: # - Andreas Kurth # - Fabian Schuiki -# - Matheus Cavalcante set -euo pipefail ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) diff --git a/scripts/update_authors b/scripts/update_authors index 30ca502aa..5e099b0b5 100755 --- a/scripts/update_authors +++ b/scripts/update_authors @@ -25,6 +25,10 @@ declare -A emails=( \ ["Samuel Riedel"]="sriedel@iis.ee.ethz.ch" \ ["Stefan Mach"]="smach@iis.ee.ethz.ch" \ ["Wolfgang Rönninger"]="wroennin@iis.ee.ethz.ch" \ + ["Thomas Benz"]="tbenz@iis.ee.ethz.ch" \ + ["Michael Rogenmoser"]="michaero@iis.ee.ethz.ch" \ + ["Luca Valente"]="luca.valente@unibo.it" \ + ["Noah Huetter"]="huettern@ethz.ch" \ ) # Iterate over source files (see `done` line for which files are included). diff --git a/src/axi_cdc.sv b/src/axi_cdc.sv index 1e422ed72..152cbef24 100644 --- a/src/axi_cdc.sv +++ b/src/axi_cdc.sv @@ -11,10 +11,8 @@ // // Authors: // - Andreas Kurth -// - Fabian Schuiki -// - Florian Zaruba +// - Luca Valente // - Wolfgang Roenninger -// - Luca Valente `include "axi/assign.svh" diff --git a/src/axi_cdc_dst.sv b/src/axi_cdc_dst.sv index d631e7e85..f252a9800 100644 --- a/src/axi_cdc_dst.sv +++ b/src/axi_cdc_dst.sv @@ -10,10 +10,8 @@ // specific language governing permissions and limitations under the License. // // Authors: +// - Luca Valente // - Andreas Kurth -// - Fabian Schuiki -// - Florian Zaruba -// - Luca Valente `include "axi/assign.svh" `include "axi/typedef.svh" diff --git a/src/axi_cdc_src.sv b/src/axi_cdc_src.sv index 65dcbe497..614a17e45 100644 --- a/src/axi_cdc_src.sv +++ b/src/axi_cdc_src.sv @@ -10,10 +10,8 @@ // specific language governing permissions and limitations under the License. // // Authors: +// - Luca Valente // - Andreas Kurth -// - Fabian Schuiki -// - Florian Zaruba -// - Luca Valente `include "axi/assign.svh" `include "axi/typedef.svh" diff --git a/src/axi_fifo.sv b/src/axi_fifo.sv index 89d43ef6a..8e4cbdfb1 100644 --- a/src/axi_fifo.sv +++ b/src/axi_fifo.sv @@ -10,7 +10,9 @@ // specific language governing permissions and limitations under the License. // // Authors: -// - Noah Huetter +// - Noah Huetter +// - Florian Zaruba +// - Fabian Schuiki // AXI4 Fifo // diff --git a/src/axi_id_remap.sv b/src/axi_id_remap.sv index 677bc14f1..2d50281de 100644 --- a/src/axi_id_remap.sv +++ b/src/axi_id_remap.sv @@ -9,9 +9,10 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // -// Andreas Kurth -// Florian Zaruba -// Wolfgang Roenninger +// Authors: +// - Andreas Kurth +// - Wolfgang Roenninger +// - Florian Zaruba `include "common_cells/registers.svh" diff --git a/src/axi_id_serialize.sv b/src/axi_id_serialize.sv index 4e81a9017..8688cb355 100644 --- a/src/axi_id_serialize.sv +++ b/src/axi_id_serialize.sv @@ -10,8 +10,7 @@ // specific language governing permissions and limitations under the License. // // Authors: -// - Wolfgang Roenninger -// - Andreas Kurth +// - Andreas Kurth `include "axi/assign.svh" `include "axi/typedef.svh" diff --git a/src/axi_iw_converter.sv b/src/axi_iw_converter.sv index e9c5ebb82..f29425eb1 100644 --- a/src/axi_iw_converter.sv +++ b/src/axi_iw_converter.sv @@ -9,9 +9,9 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // -// Andreas Kurth -// Florian Zaruba -// Wolfgang Roenninger +// Authors: +// - Andreas Kurth +// - Wolfgang Roenninger `include "axi/typedef.svh" diff --git a/src/axi_multicut.sv b/src/axi_multicut.sv index 1e42c2da5..64a354a4c 100644 --- a/src/axi_multicut.sv +++ b/src/axi_multicut.sv @@ -11,8 +11,8 @@ // // Authors: // - Wolfgang Roenninger -// - Fabian Schuiki // - Andreas Kurth +// - Fabian Schuiki // - Stefan Mach // Multiple AXI4 cuts. diff --git a/src/axi_sim_mem.sv b/src/axi_sim_mem.sv index 67d278603..b8b6b12c0 100644 --- a/src/axi_sim_mem.sv +++ b/src/axi_sim_mem.sv @@ -3,6 +3,9 @@ // // Authors: // - Andreas Kurth +// - Samuel Riedel +// - Michael Rogenmoser +// - Thomas Benz `include "axi/typedef.svh" diff --git a/src/axi_test.sv b/src/axi_test.sv index b23bf68bd..7c7fab63e 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -10,10 +10,9 @@ // specific language governing permissions and limitations under the License. // // Authors: -// - Wolfgang Roenninger // - Andreas Kurth +// - Wolfgang Roenninger // - Fabian Schuiki -// - Florian Zaruba // - Matheus Cavalcante diff --git a/src/axi_throttle.sv b/src/axi_throttle.sv index ff3c2bcc6..3e7973a08 100644 --- a/src/axi_throttle.sv +++ b/src/axi_throttle.sv @@ -2,7 +2,8 @@ // Solderpad Hardware License, Version 0.51, see LICENSE for details. // SPDX-License-Identifier: SHL-0.51 // -// Thomas Benz +// Authors: +// - Thomas Benz /// Throttles an AXI4+ATOP bus. The maximum number of outstanding transfers have to /// be set as a compile-time parameter, whereas the number of outstanding transfers can be set diff --git a/src/axi_to_axi_lite.sv b/src/axi_to_axi_lite.sv index d610ab99d..a0702bb79 100644 --- a/src/axi_to_axi_lite.sv +++ b/src/axi_to_axi_lite.sv @@ -13,7 +13,6 @@ // - Wolfgang Roenninger // - Andreas Kurth // - Fabian Schuiki -// - Florian Zaruba /// An AXI4+ATOP to AXI4-Lite converter with atomic transaction and burst support. module axi_to_axi_lite #( diff --git a/src/axi_to_mem.sv b/src/axi_to_mem.sv index cbdb311f3..f6954de24 100644 --- a/src/axi_to_mem.sv +++ b/src/axi_to_mem.sv @@ -9,8 +9,7 @@ // specific language governing permissions and limitations under the License. // Authors: -// - Andreas Kurth -// - Wolfgang Roenninger +// - Michael Rogenmoser `include "common_cells/registers.svh" /// AXI4+ATOP slave module which translates AXI bursts into a memory stream. diff --git a/src/axi_to_mem_banked.sv b/src/axi_to_mem_banked.sv index a57962a9d..1de2513c5 100644 --- a/src/axi_to_mem_banked.sv +++ b/src/axi_to_mem_banked.sv @@ -9,6 +9,7 @@ // specific language governing permissions and limitations under the License. // Authors: +// - Michael Rogenmoser // - Wolfgang Rönninger /// AXI4+ATOP to banked SRAM memory slave. Allows for parallel read and write transactions. diff --git a/src/axi_to_mem_interleaved.sv b/src/axi_to_mem_interleaved.sv index e33bcc764..68930be1b 100644 --- a/src/axi_to_mem_interleaved.sv +++ b/src/axi_to_mem_interleaved.sv @@ -8,8 +8,9 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -// Author: +// Authors: // Thomas Benz +// - Michael Rogenmoser /// AXI4+ATOP to SRAM memory slave. Allows for parallel read and write transactions. /// Allows reads to bypass writes, in contrast to `axi_to_mem`, however needs more hardware. diff --git a/src/axi_to_mem_split.sv b/src/axi_to_mem_split.sv index 47827f537..a4e19ec6e 100644 --- a/src/axi_to_mem_split.sv +++ b/src/axi_to_mem_split.sv @@ -7,9 +7,9 @@ // 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. - -// Author: -// Michael Rogenmoser +// +// Authors: +// - Michael Rogenmoser `include "axi/assign.svh" /// AXI4+ATOP to memory-protocol interconnect. Completely separates the read and write channel to diff --git a/test/axi_synth_bench.sv b/test/axi_synth_bench.sv index 14563e137..edc77d4b4 100644 --- a/test/axi_synth_bench.sv +++ b/test/axi_synth_bench.sv @@ -12,6 +12,7 @@ // - Wolfgang Roenninger // - Andreas Kurth // - Fabian Schuiki +// - Michael Rogenmoser /// A synthesis test bench which instantiates various adapter variants. module axi_synth_bench ( diff --git a/test/tb_axi_dw_downsizer.sv b/test/tb_axi_dw_downsizer.sv index ff076ba8c..1edd3b16f 100644 --- a/test/tb_axi_dw_downsizer.sv +++ b/test/tb_axi_dw_downsizer.sv @@ -10,6 +10,7 @@ // // Authors: // - Matheus Cavalcante +// - Andreas Kurth `include "axi/assign.svh" diff --git a/test/tb_axi_dw_upsizer.sv b/test/tb_axi_dw_upsizer.sv index 26f11f048..8a8e03392 100644 --- a/test/tb_axi_dw_upsizer.sv +++ b/test/tb_axi_dw_upsizer.sv @@ -10,6 +10,7 @@ // // Authors: // - Matheus Cavalcante +// - Andreas Kurth `include "axi/assign.svh" diff --git a/test/tb_axi_fifo.sv b/test/tb_axi_fifo.sv index da88a4c81..2e79044e0 100644 --- a/test/tb_axi_fifo.sv +++ b/test/tb_axi_fifo.sv @@ -9,9 +9,7 @@ // specific language governing permissions and limitations under the License. // // Authors: -// - Wolfgang Roenninger -// - Andreas Kurth -// - Noah Huetter +// - Noah Huetter `include "axi/typedef.svh" `include "axi/assign.svh" diff --git a/test/tb_axi_iw_converter.sv b/test/tb_axi_iw_converter.sv index 1b2d5249b..0f2f7893c 100644 --- a/test/tb_axi_iw_converter.sv +++ b/test/tb_axi_iw_converter.sv @@ -8,9 +8,9 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // -// Author: Andreas Kurth -// Florian Zaruba -// Wolfgang Roennigner +// Authors: +// - Andreas Kurth +// - Wolfgang Roenninger `include "axi/assign.svh" `include "axi/typedef.svh" diff --git a/test/tb_axi_lite_regs.sv b/test/tb_axi_lite_regs.sv index 0b90bcac3..32168d483 100644 --- a/test/tb_axi_lite_regs.sv +++ b/test/tb_axi_lite_regs.sv @@ -10,6 +10,7 @@ // // Authors: // - Wolfgang Roenninger +// - Andreas Kurth // Directed Random Verification Testbench for `axi_lite_regs`. diff --git a/test/tb_axi_lite_to_axi.sv b/test/tb_axi_lite_to_axi.sv index 497ce893e..50a9b81fc 100644 --- a/test/tb_axi_lite_to_axi.sv +++ b/test/tb_axi_lite_to_axi.sv @@ -9,8 +9,8 @@ // specific language governing permissions and limitations under the License. // // Authors: -// - Fabian Schuiki // - Andreas Kurth +// - Fabian Schuiki // - Florian Zaruba `include "axi/assign.svh" diff --git a/test/tb_axi_sim_mem.sv b/test/tb_axi_sim_mem.sv index 28c2e7587..d6a899fc1 100644 --- a/test/tb_axi_sim_mem.sv +++ b/test/tb_axi_sim_mem.sv @@ -3,6 +3,7 @@ // // Authors: // - Andreas Kurth +// - Michael Rogenmoser `include "axi/assign.svh" `include "axi/typedef.svh" diff --git a/test/tb_axi_to_mem_banked.sv b/test/tb_axi_to_mem_banked.sv index e519f9f52..0b4302e43 100644 --- a/test/tb_axi_to_mem_banked.sv +++ b/test/tb_axi_to_mem_banked.sv @@ -9,6 +9,7 @@ // specific language governing permissions and limitations under the License. // Authors: +// - Michael Rogenmoser // - Wolfgang Roenninger `include "axi/typedef.svh" From 4dacf05628849a6f8b17021444ddf1bbccfa3ad9 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Tue, 27 Sep 2022 16:05:04 +0200 Subject: [PATCH 056/145] authors: Manual fixes --- Bender.yml | 2 ++ CHANGELOG.md | 1 + include/axi/assign.svh | 1 + scripts/update_authors | 2 ++ src/axi_to_mem_banked.sv | 2 +- src/axi_to_mem_interleaved.sv | 3 ++- src/axi_xp.sv | 3 ++- test/tb_axi_to_mem_banked.sv | 2 +- 8 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Bender.yml b/Bender.yml index 4f4914d90..f0ffdc11c 100644 --- a/Bender.yml +++ b/Bender.yml @@ -5,6 +5,7 @@ package: - "Thomas Benz " # current maintainer - "Michael Rogenmoser " # current maintainer - "Matheus Cavalcante " + - "Tim Fischer " - "Noah Huetter " - "Andreas Kurth " - "Stefan Mach " @@ -12,6 +13,7 @@ package: - "Wolfgang Rönninger " - "Fabian Schuiki " - "Luca Valente " + - "Nils Wistoff " - "Florian Zaruba " dependencies: diff --git a/CHANGELOG.md b/CHANGELOG.md index 518a0a23f..bd9d4a63a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - Improve compatibility with FuseSoC - Improve compatibility with Vivado XSIM +- Use `scripts/update_authors` to update authors, slight manual fixes performed. ### Fixed diff --git a/include/axi/assign.svh b/include/axi/assign.svh index a4ade2387..80667eb09 100644 --- a/include/axi/assign.svh +++ b/include/axi/assign.svh @@ -11,6 +11,7 @@ // // Authors: // - Andreas Kurth +// - Nils Wistoff // Macros to assign AXI Interfaces and Structs diff --git a/scripts/update_authors b/scripts/update_authors index 5e099b0b5..674bad51b 100755 --- a/scripts/update_authors +++ b/scripts/update_authors @@ -29,6 +29,8 @@ declare -A emails=( \ ["Michael Rogenmoser"]="michaero@iis.ee.ethz.ch" \ ["Luca Valente"]="luca.valente@unibo.it" \ ["Noah Huetter"]="huettern@ethz.ch" \ + ["Nils Wistoff"]="nwistoff@iis.ee.ethz.ch" \ + ["Tim Fischer"]="fischeti@iis.ee.ethz.ch" \ ) # Iterate over source files (see `done` line for which files are included). diff --git a/src/axi_to_mem_banked.sv b/src/axi_to_mem_banked.sv index 1de2513c5..81998277f 100644 --- a/src/axi_to_mem_banked.sv +++ b/src/axi_to_mem_banked.sv @@ -9,8 +9,8 @@ // specific language governing permissions and limitations under the License. // Authors: -// - Michael Rogenmoser // - Wolfgang Rönninger +// - Michael Rogenmoser /// AXI4+ATOP to banked SRAM memory slave. Allows for parallel read and write transactions. /// Has higher throughput than `axi_to_mem`, however needs more hardware. diff --git a/src/axi_to_mem_interleaved.sv b/src/axi_to_mem_interleaved.sv index 68930be1b..f306612e0 100644 --- a/src/axi_to_mem_interleaved.sv +++ b/src/axi_to_mem_interleaved.sv @@ -9,7 +9,8 @@ // specific language governing permissions and limitations under the License. // Authors: -// Thomas Benz +// - Wolfgang Roenninger +// - Thomas Benz // - Michael Rogenmoser /// AXI4+ATOP to SRAM memory slave. Allows for parallel read and write transactions. diff --git a/src/axi_xp.sv b/src/axi_xp.sv index 9d133e006..5ccbfb9ff 100644 --- a/src/axi_xp.sv +++ b/src/axi_xp.sv @@ -10,6 +10,7 @@ // specific language governing permissions and limitations under the License. // // Authors: +// - Tim Fischer // - Andreas Kurth // - Vikram Jain @@ -252,4 +253,4 @@ import cf_math_pkg::idx_width; .addr_map_i ); -endmodule \ No newline at end of file +endmodule diff --git a/test/tb_axi_to_mem_banked.sv b/test/tb_axi_to_mem_banked.sv index 0b4302e43..64667998d 100644 --- a/test/tb_axi_to_mem_banked.sv +++ b/test/tb_axi_to_mem_banked.sv @@ -9,8 +9,8 @@ // specific language governing permissions and limitations under the License. // Authors: -// - Michael Rogenmoser // - Wolfgang Roenninger +// - Michael Rogenmoser `include "axi/typedef.svh" `include "axi/assign.svh" From 686b359792f982e867acc6c8cc3574ad6fa930b0 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 30 Aug 2022 17:28:34 +0200 Subject: [PATCH 057/145] axi_to_mem: handle asymmetric backpressure Improve performance for asymmetric backpressure on the memory channel. --- src/axi_to_mem.sv | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/axi_to_mem.sv b/src/axi_to_mem.sv index f6954de24..de70065d6 100644 --- a/src/axi_to_mem.sv +++ b/src/axi_to_mem.sv @@ -352,7 +352,8 @@ module axi_to_mem #( .DataWidth ( DataWidth ), .NumBanks ( NumBanks ), .HideStrb ( HideStrb ), - .MaxTrans ( BufDepth ) + .MaxTrans ( BufDepth ), + .FifoDepth ( 2 ) ) i_mem_to_banks ( .clk_i, .rst_ni, @@ -565,6 +566,8 @@ module mem_to_banks #( parameter bit HideStrb = 1'b0, /// Number of outstanding transactions parameter int unsigned MaxTrans = 32'b1, + /// FIFO depth, must be >=1 + parameter int unsigned FifoDepth = 1, /// Dependent parameter, do not override! Address type. localparam type addr_t = logic [AddrWidth-1:0], /// Dependent parameter, do not override! Input data type. @@ -650,19 +653,23 @@ module mem_to_banks #( assign bank_req[i].strb = strb_i[i*BytesPerBank+:BytesPerBank]; assign bank_req[i].atop = atop_i; assign bank_req[i].we = we_i; - fall_through_register #( - .T ( req_t ) + stream_fifo #( + .FALL_THROUGH ( 1'b1 ), + .DATA_WIDTH ( $bits(req_t) ), + .DEPTH ( FifoDepth ), + .T ( req_t ) ) i_ft_reg ( .clk_i, .rst_ni, - .clr_i ( 1'b0 ), + .flush_i ( 1'b0 ), .testmode_i ( 1'b0 ), + .usage_o (), + .data_i ( bank_req[i] ), .valid_i ( req_valid ), .ready_o ( req_ready[i] ), - .data_i ( bank_req[i] ), + .data_o ( bank_oup[i] ), .valid_o ( bank_req_internal[i] ), - .ready_i ( bank_gnt_internal[i] ), - .data_o ( bank_oup[i] ) + .ready_i ( bank_gnt_internal[i] ) ); assign bank_addr_o[i] = bank_oup[i].addr; assign bank_wdata_o[i] = bank_oup[i].wdata; @@ -709,19 +716,23 @@ module mem_to_banks #( // Handle responses. for (genvar i = 0; unsigned'(i) < NumBanks; i++) begin : gen_resp_regs - fall_through_register #( - .T ( oup_data_t ) + stream_fifo #( + .FALL_THROUGH ( 1'b1 ), + .DATA_WIDTH ( $bits(oup_data_t) ), + .DEPTH ( FifoDepth ), + .T ( oup_data_t ) ) i_ft_reg ( .clk_i, .rst_ni, - .clr_i ( 1'b0 ), + .flush_i ( 1'b0 ), .testmode_i ( 1'b0 ), + .usage_o (), + .data_i ( bank_rdata_i[i] ), .valid_i ( bank_rvalid_i[i] ), .ready_o ( resp_ready[i] ), - .data_i ( bank_rdata_i[i] ), .data_o ( rdata_o[i*BitsPerBank+:BitsPerBank] ), - .ready_i ( rvalid_o & !dead_response[i] ), - .valid_o ( resp_valid[i] ) + .valid_o ( resp_valid[i] ), + .ready_i ( rvalid_o & !dead_response[i] ) ); end assign rvalid_o = &(resp_valid | dead_response); From ccc7e34ed99ffa3bcad577d9ebd68fe2a39a4830 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 30 Aug 2022 17:56:18 +0200 Subject: [PATCH 058/145] axi_to_mem: propagate parameter for output fifo depth --- src/axi_to_mem.sv | 47 +++++++++++++++++++---------------- src/axi_to_mem_banked.sv | 24 +++++++++++------- src/axi_to_mem_interleaved.sv | 36 +++++++++++++++------------ src/axi_to_mem_split.sv | 41 +++++++++++++++++------------- 4 files changed, 85 insertions(+), 63 deletions(-) diff --git a/src/axi_to_mem.sv b/src/axi_to_mem.sv index de70065d6..1b8fd590e 100644 --- a/src/axi_to_mem.sv +++ b/src/axi_to_mem.sv @@ -34,6 +34,8 @@ module axi_to_mem #( parameter int unsigned BufDepth = 1, /// Hide write requests if the strb == '0 parameter bit HideStrb = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OutFifoDepth = 1, /// Dependent parameter, do not override. Memory address type. localparam type addr_t = logic [AddrWidth-1:0], /// Dependent parameter, do not override. Memory data type. @@ -348,12 +350,12 @@ module axi_to_mem #( // Split single memory request to desired number of banks. mem_to_banks #( - .AddrWidth ( AddrWidth ), - .DataWidth ( DataWidth ), - .NumBanks ( NumBanks ), - .HideStrb ( HideStrb ), - .MaxTrans ( BufDepth ), - .FifoDepth ( 2 ) + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .NumBanks ( NumBanks ), + .HideStrb ( HideStrb ), + .MaxTrans ( BufDepth ), + .FifoDepth ( OutFifoDepth ) ) i_mem_to_banks ( .clk_i, .rst_ni, @@ -464,19 +466,21 @@ endmodule /// Interface wrapper for module `axi_to_mem`. module axi_to_mem_intf #( /// See `axi_to_mem`, parameter `AddrWidth`. - parameter int unsigned ADDR_WIDTH = 32'd0, + parameter int unsigned ADDR_WIDTH = 32'd0, /// See `axi_to_mem`, parameter `DataWidth`. - parameter int unsigned DATA_WIDTH = 32'd0, + parameter int unsigned DATA_WIDTH = 32'd0, /// AXI4+ATOP ID width. - parameter int unsigned ID_WIDTH = 32'd0, + parameter int unsigned ID_WIDTH = 32'd0, /// AXI4+ATOP user width. - parameter int unsigned USER_WIDTH = 32'd0, + parameter int unsigned USER_WIDTH = 32'd0, /// See `axi_to_mem`, parameter `NumBanks`. - parameter int unsigned NUM_BANKS = 32'd0, + parameter int unsigned NUM_BANKS = 32'd0, /// See `axi_to_mem`, parameter `BufDepth`. - parameter int unsigned BUF_DEPTH = 32'd1, + parameter int unsigned BUF_DEPTH = 32'd1, /// Hide write requests if the strb == '0 - parameter bit HIDE_STRB = 1'b0, + parameter bit HIDE_STRB = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OUT_FIFO_DEPTH = 32'd1, /// Dependent parameter, do not override. See `axi_to_mem`, parameter `addr_t`. localparam type addr_t = logic [ADDR_WIDTH-1:0], /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_data_t`. @@ -527,14 +531,15 @@ module axi_to_mem_intf #( `AXI_ASSIGN_TO_REQ(req, slv) `AXI_ASSIGN_FROM_RESP(slv, resp) axi_to_mem #( - .axi_req_t ( req_t ), - .axi_resp_t ( resp_t ), - .AddrWidth ( ADDR_WIDTH ), - .DataWidth ( DATA_WIDTH ), - .IdWidth ( ID_WIDTH ), - .NumBanks ( NUM_BANKS ), - .BufDepth ( BUF_DEPTH ), - .HideStrb ( HIDE_STRB ) + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .AddrWidth ( ADDR_WIDTH ), + .DataWidth ( DATA_WIDTH ), + .IdWidth ( ID_WIDTH ), + .NumBanks ( NUM_BANKS ), + .BufDepth ( BUF_DEPTH ), + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) ) i_axi_to_mem ( .clk_i, .rst_ni, diff --git a/src/axi_to_mem_banked.sv b/src/axi_to_mem_banked.sv index 81998277f..e50383d02 100644 --- a/src/axi_to_mem_banked.sv +++ b/src/axi_to_mem_banked.sv @@ -53,6 +53,8 @@ module axi_to_mem_banked #( parameter int unsigned MemLatency = 32'd1, /// Hide write requests if the strb == '0 parameter bit HideStrb = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OutFifoDepth = 1, /// DEPENDENT PARAMETER, DO NOT OVERWRITE! Address type of the memory request. parameter type mem_addr_t = logic [MemAddrWidth-1:0], /// DEPENDENT PARAMETER, DO NOT OVERWRITE! Atomic operation type for the memory request. @@ -183,14 +185,15 @@ module axi_to_mem_banked #( // Careful, request / grant // Only assert grant, if there is a ready axi_to_mem #( - .axi_req_t ( axi_req_t ), - .axi_resp_t( axi_resp_t ), - .AddrWidth ( AxiAddrWidth ), - .DataWidth ( AxiDataWidth ), - .IdWidth ( AxiIdWidth ), - .NumBanks ( BanksPerAxiChannel ), - .BufDepth ( MemLatency ), - .HideStrb ( HideStrb ) + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AxiAddrWidth ), + .DataWidth ( AxiDataWidth ), + .IdWidth ( AxiIdWidth ), + .NumBanks ( BanksPerAxiChannel ), + .BufDepth ( MemLatency ), + .HideStrb ( HideStrb ), + .OutFifoDepth ( OutFifoDepth ) ) i_axi_to_mem ( .clk_i, .rst_ni, @@ -331,6 +334,8 @@ module axi_to_mem_banked_intf #( parameter int unsigned MEM_LATENCY = 32'd1, /// Hide write requests if the strb == '0 parameter bit HIDE_STRB = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OUT_FIFO_DEPTH = 32'd1, // DEPENDENT PARAMETERS, DO NOT OVERWRITE! parameter type mem_addr_t = logic [MEM_ADDR_WIDTH-1:0], parameter type mem_atop_t = logic [5:0], @@ -398,7 +403,8 @@ module axi_to_mem_banked_intf #( .MemAddrWidth ( MEM_ADDR_WIDTH ), .MemDataWidth ( MEM_DATA_WIDTH ), .MemLatency ( MEM_LATENCY ), - .HideStrb ( HIDE_STRB ) + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) ) i_axi_to_mem_banked ( .clk_i, .rst_ni, diff --git a/src/axi_to_mem_interleaved.sv b/src/axi_to_mem_interleaved.sv index f306612e0..a744c1ec0 100644 --- a/src/axi_to_mem_interleaved.sv +++ b/src/axi_to_mem_interleaved.sv @@ -34,6 +34,8 @@ module axi_to_mem_interleaved #( parameter int unsigned BufDepth = 1, /// Hide write requests if the strb == '0 parameter bit HideStrb = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OutFifoDepth = 1, /// Dependent parameter, do not override. Memory address type. parameter type addr_t = logic [AddrWidth-1:0], /// Dependent parameter, do not override. Memory data type. @@ -115,14 +117,15 @@ module axi_to_mem_interleaved #( end axi_to_mem #( - .axi_req_t ( axi_req_t ), - .axi_resp_t ( axi_resp_t ), - .AddrWidth ( AddrWidth ), - .DataWidth ( DataWidth ), - .IdWidth ( IdWidth ), - .NumBanks ( NumBanks ), - .BufDepth ( BufDepth ), - .HideStrb ( HideStrb ) + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumBanks ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ), + .OutFifoDepth( OutFifoDepth ) ) i_axi_to_mem_write ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), @@ -141,14 +144,15 @@ module axi_to_mem_interleaved #( ); axi_to_mem #( - .axi_req_t ( axi_req_t ), - .axi_resp_t ( axi_resp_t ), - .AddrWidth ( AddrWidth ), - .DataWidth ( DataWidth ), - .IdWidth ( IdWidth ), - .NumBanks ( NumBanks ), - .BufDepth ( BufDepth ), - .HideStrb ( HideStrb ) + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumBanks ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ), + .OutFifoDepth ( OutFifoDepth ) ) i_axi_to_mem_read ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), diff --git a/src/axi_to_mem_split.sv b/src/axi_to_mem_split.sv index a4e19ec6e..a51350393 100644 --- a/src/axi_to_mem_split.sv +++ b/src/axi_to_mem_split.sv @@ -34,6 +34,8 @@ module axi_to_mem_split #( parameter int unsigned BufDepth = 0, /// Hide write requests if the strb == '0 parameter bit HideStrb = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OutFifoDepth = 1, /// Dependent parameters, do not override. Number of memory ports. parameter int unsigned NumMemPorts = 2*AxiDataWidth/MemDataWidth, /// Dependent parameter, do not override. Memory address type. @@ -104,14 +106,15 @@ module axi_to_mem_split #( assign busy_o = read_busy || write_busy; axi_to_mem #( - .axi_req_t ( axi_req_t ), - .axi_resp_t ( axi_resp_t ), - .AddrWidth ( AddrWidth ), - .DataWidth ( AxiDataWidth ), - .IdWidth ( IdWidth ), - .NumBanks ( NumMemPorts/2 ), - .BufDepth ( BufDepth ), - .HideStrb ( 1'b0 ) + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( AxiDataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumMemPorts/2 ), + .BufDepth ( BufDepth ), + .HideStrb ( 1'b0 ), + .OutFifoDepth ( OutFifoDepth ) ) i_axi_to_mem_read ( .clk_i, .rst_ni, @@ -130,14 +133,15 @@ module axi_to_mem_split #( ); axi_to_mem #( - .axi_req_t ( axi_req_t ), - .axi_resp_t ( axi_resp_t ), - .AddrWidth ( AddrWidth ), - .DataWidth ( AxiDataWidth ), - .IdWidth ( IdWidth ), - .NumBanks ( NumMemPorts/2 ), - .BufDepth ( BufDepth ), - .HideStrb ( HideStrb ) + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( AxiDataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumMemPorts/2 ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ), + .OutFifoDepth ( OutFifoDepth ) ) i_axi_to_mem_write ( .clk_i, .rst_ni, @@ -174,6 +178,8 @@ module axi_to_mem_split_intf #( parameter int unsigned BUF_DEPTH = 0, /// Hide write requests if the strb == '0 parameter bit HIDE_STRB = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OUT_FIFO_DEPTH = 32'd1, /// Dependent parameters, do not override. Number of memory ports. parameter int unsigned NUM_MEM_PORTS = 2*AXI_DATA_WIDTH/MEM_DATA_WIDTH, /// Dependent parameter, do not override. See `axi_to_mem`, parameter `addr_t`. @@ -230,7 +236,8 @@ module axi_to_mem_split_intf #( .IdWidth ( AXI_ID_WIDTH ), .MemDataWidth ( MEM_DATA_WIDTH ), // must divide `AxiDataWidth` without remainder .BufDepth ( BUF_DEPTH ), - .HideStrb ( HIDE_STRB ) + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) ) i_axi_to_mem_split ( .clk_i, .rst_ni, From 8ff9ce31d2aa9d9f5ab486413ee3203902b66862 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Wed, 28 Sep 2022 11:22:58 +0200 Subject: [PATCH 059/145] changelog: Update `axi_to_mem` performance updates --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd9d4a63a..17d1a1791 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - Improve compatibility with FuseSoC - Improve compatibility with Vivado XSIM +- Performance improvements to `axi_to_mem` - Use `scripts/update_authors` to update authors, slight manual fixes performed. ### Fixed From 2f395b176bee1c769c80f060a4345fda965bb04b Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Wed, 28 Sep 2022 14:57:56 +0200 Subject: [PATCH 060/145] Release v0.38.0 --- CHANGELOG.md | 17 ++++++++++++++--- README.md | 2 ++ VERSION | 2 +- axi.core | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17d1a1791..90cce6ae5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased ### Added -- Add `axi_dumper` and `axi_dumper_interpret` script to dump log from an AXI bus for debugging purposes. + +### Changed + +### Fixed + + +## 0.38.0 - 2022-09-28 + +### Added +- Add `axi_dumper` and `axi_dumper_interpret` script to dump log from an AXI bus for debugging + purposes. - Add FuseSoC and Vivado XSIM limited test to CI - `assign.svh`: Add macros to assign flat buses using the Vivado naming style. - `axi_lfsr` and `axi_lite_lfsr`: Add AXI4 and AXI4 Lite LFSR Subordinate devices. -- `axi_xp`: Crosspoint with homomorphous slave and master ports. +- `axi_xp`: Add crosspoint with homomorphous slave and master ports. ### Changed - Improve compatibility with FuseSoC @@ -20,7 +30,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Performance improvements to `axi_to_mem` - Use `scripts/update_authors` to update authors, slight manual fixes performed. -### Fixed +`v0.38.0` is fully **backward-compatible** to `v0.36.0` and `v0.37.0`. ## 0.37.0 - 2022-08-30 @@ -55,6 +65,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. `v0.37.0` is fully **backward-compatible** to `v0.36.0`. + ## 0.36.0 - 2022-07-07 ### Added diff --git a/README.md b/README.md index ed678dfba..85cf211f1 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | | [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. Additional banked, interleaved, split variant. | | | [`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. | | ### Simulation-Only Modules @@ -68,6 +69,7 @@ In addition to the modules above, which are available in synthesis and simulatio |------------------------------------------------------|--------------------------------------------------------------------------------------------------------| | [`axi_chan_logger`](src/axi_test.sv) | Logs the transactions of an AXI4(+ATOPs) port to files. | | [`axi_driver`](src/axi_test.sv) | Low-level driver for AXI4(+ATOPs) that can send and receive individual beats on any channel. | +| [`axi_dumper`](src/axi_dumper.sv) | Dumps log to file to be interpreted by `axi_dumper_interpret` script for debugging purposes. | | [`axi_lite_driver`](src/axi_test.sv) | Low-level driver for AXI4-Lite that can send and receive individual beats on any channel. | | [`axi_lite_rand_master`](src/axi_test.sv) | AXI4-Lite master component that issues random transactions within user-defined constraints. | | [`axi_lite_rand_slave`](src/axi_test.sv) | AXI4-Lite slave component that responds to transactions with constrainable random delays and data. | diff --git a/VERSION b/VERSION index 0f1a7dfc7..ca75280b0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.37.0 +0.38.0 diff --git a/axi.core b/axi.core index 64c8ea524..cc4e98609 100644 --- a/axi.core +++ b/axi.core @@ -1,6 +1,6 @@ CAPI=2: -name : pulp-platform.org::axi:0.37.0 +name : pulp-platform.org::axi:0.38.0 filesets: rtl: From c97c4ca02b625902618070b73ffea8059c24f1e8 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Mon, 29 Aug 2022 07:22:04 +0200 Subject: [PATCH 061/145] axi_demux: Replace `w_fifo` with a write counter --- CHANGELOG.md | 1 + src/axi_demux.sv | 86 +++++++++++++++++++++++++++--------------------- 2 files changed, 50 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90cce6ae5..c6f5aaed5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added ### Changed +- `axi_demux`: Replace write FIFO (`w_fifo`) with a write credit counter. ### Fixed diff --git a/src/axi_demux.sv b/src/axi_demux.sv index cae7401eb..37e2d5912 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -74,7 +74,8 @@ module axi_demux #( input axi_resp_t [NoMstPorts-1:0] mst_resps_i ); - localparam int unsigned IdCounterWidth = MaxTrans > 1 ? $clog2(MaxTrans) : 1; + localparam int unsigned IdCounterWidth = cf_math_pkg::idx_width(MaxTrans); + typedef logic [IdCounterWidth-1:0] id_cnt_t; //-------------------------------------- // Typedefs for the FIFOs / Queues @@ -175,14 +176,14 @@ module axi_demux #( // AW ID counter select_t lookup_aw_select; logic aw_select_occupied, aw_id_cnt_full; - logic aw_push; // Upon an ATOP load, inject IDs from the AW into the AR channel logic atop_inject; - // W FIFO: stores the decision to which master W beats should go - logic w_fifo_pop; - logic w_fifo_full, w_fifo_empty; - select_t w_select; + // W select counter: stores the decision to which master W beats should go + select_t w_select, w_select_q; + logic w_select_valid; + id_cnt_t w_open; + logic w_cnt_up, w_cnt_down; // Register which locks the AW valid signal logic lock_aw_valid_d, lock_aw_valid_q, load_aw_lock; @@ -274,9 +275,9 @@ module axi_demux #( lock_aw_valid_d = lock_aw_valid_q; load_aw_lock = 1'b0; // AW ID counter and W FIFO - aw_push = 1'b0; + w_cnt_up = 1'b0; // ATOP injection into ar counter - atop_inject = 1'b0; + atop_inject = 1'b0; // we had an arbitration decision, the valid is locked, wait for the transaction if (lock_aw_valid_q) begin aw_valid = 1'b1; @@ -289,19 +290,23 @@ module axi_demux #( atop_inject = slv_aw_chan_select.aw_chan.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; end end else begin - // An AW can be handled if `i_aw_id_counter` and `i_w_fifo` are not full. An ATOP that + // An AW can be handled if `i_aw_id_counter` and `i_counter_open_w` are not full. An ATOP that // requires an R response can be handled if additionally `i_ar_id_counter` is not full (this // only applies if ATOPs are supported at all). - if (!aw_id_cnt_full && !w_fifo_full && + if (!aw_id_cnt_full && (w_open != {IdCounterWidth{1'b1}}) && (!(ar_id_cnt_full && slv_aw_chan_select.aw_chan.atop[axi_pkg::ATOP_R_RESP]) || !AtopSupport)) begin - // there is a valid AW vector make the id lookup and go further, if it passes - if (slv_aw_valid && (!aw_select_occupied || - (slv_aw_chan_select.aw_select == lookup_aw_select))) begin + /// There is a valid AW vector make the id lookup and go further, if it passes. + /// Also stall if previous transmitted AWs still have active W's in flight. + /// This prevents deadlocking of the W channel. The counters are there for the + /// Handling of the B responses. + if (slv_aw_valid && + ((w_open == '0) || (w_select == slv_aw_chan_select.aw_select)) && + (!aw_select_occupied || (slv_aw_chan_select.aw_select == lookup_aw_select))) begin // connect the handshake aw_valid = 1'b1; // push arbitration to the W FIFO regardless, do not wait for the AW transaction - aw_push = 1'b1; + w_cnt_up = 1'b1; // on AW transaction if (aw_ready) begin slv_aw_ready = 1'b1; @@ -345,32 +350,36 @@ module axi_demux #( .inject_i ( 1'b0 ), .push_axi_id_i ( slv_aw_chan_select.aw_chan.id[0+:AxiLookBits] ), .push_mst_select_i ( slv_aw_chan_select.aw_select ), - .push_i ( aw_push ), + .push_i ( w_cnt_up ), .pop_axi_id_i ( slv_b_chan.id[0+:AxiLookBits] ), .pop_i ( slv_b_valid & slv_b_ready ) ); // pop from ID counter on outward transaction end - // FIFO to save W selection - fifo_v3 #( - .FALL_THROUGH ( FallThrough ), - .DEPTH ( MaxTrans ), - .dtype ( select_t ) - ) i_w_fifo ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .flush_i ( 1'b0 ), - .testmode_i( test_i ), - .full_o ( w_fifo_full ), - .empty_o ( w_fifo_empty ), - .usage_o ( ), - .data_i ( slv_aw_chan_select.aw_select ), - .push_i ( aw_push ), // controlled from proc_aw_chan - .data_o ( w_select ), // where the w beat should go - .pop_i ( w_fifo_pop ) // controlled from proc_w_chan + // This counter steers the demultiplexer of the W channel. + // `w_select` determines, which handshaking is connected. + // AWs are only forwarded, if the counter is empty, or `w_select_q` is the same as + // `slv_aw_chan_select.aw_select`. + counter #( + .WIDTH ( IdCounterWidth ), + .STICKY_OVERFLOW ( 1'b0 ) + ) i_counter_open_w ( + .clk_i, + .rst_ni, + .clear_i ( 1'b0 ), + .en_i ( w_cnt_up ^ w_cnt_down ), + .load_i ( 1'b0 ), + .down_i ( w_cnt_down ), + .d_i ( '0 ), + .q_o ( w_open ), + .overflow_o ( /*not used*/ ) ); + `FFLARN(w_select_q, slv_aw_chan_select.aw_select, w_cnt_up, select_t'(0), clk_i, rst_ni) + assign w_select = (|w_open) ? w_select_q : slv_aw_chan_select.aw_select; + assign w_select_valid = w_cnt_up | (|w_open); + //-------------------------------------- // W Channel //-------------------------------------- @@ -571,8 +580,8 @@ module axi_demux #( .idx_o ( ) ); - assign ar_ready = ar_valid & mst_resps_i[slv_ar_chan_select.ar_select].ar_ready; - assign aw_ready = aw_valid & mst_resps_i[slv_aw_chan_select.aw_select].aw_ready; + assign ar_ready = ar_valid & mst_resps_i[slv_ar_chan_select.ar_select].ar_ready; + assign aw_ready = aw_valid & mst_resps_i[slv_aw_chan_select.aw_select].aw_ready; // process that defines the individual demuxes and assignments for the arbitration // as mst_reqs_o has to be drivem from the same always comb block! @@ -580,7 +589,7 @@ module axi_demux #( // default assignments mst_reqs_o = '0; slv_w_ready = 1'b0; - w_fifo_pop = 1'b0; + w_cnt_down = 1'b0; for (int unsigned i = 0; i < NoMstPorts; i++) begin // AW channel @@ -593,10 +602,10 @@ module axi_demux #( // W channel mst_reqs_o[i].w = slv_w_chan; mst_reqs_o[i].w_valid = 1'b0; - if (!w_fifo_empty && (w_select == i)) begin + if (w_select_valid && (w_select == select_t'(i))) begin mst_reqs_o[i].w_valid = slv_w_valid; slv_w_ready = mst_resps_i[i].w_ready; - w_fifo_pop = slv_w_valid & mst_resps_i[i].w_ready & slv_w_chan.last; + w_cnt_down = slv_w_valid & mst_resps_i[i].w_ready & slv_w_chan.last; end // B channel @@ -658,6 +667,9 @@ module axi_demux #( internal_aw_select: assert property( @(posedge clk_i) (aw_valid |-> slv_aw_chan_select.aw_select < NoMstPorts)) else $fatal(1, "slv_aw_chan_select.aw_select illegal while aw_valid."); + w_underflow: assert property( @(posedge clk_i) + ((w_open == '0) && (w_cnt_up ^ w_cnt_down) |-> !w_cnt_down)) else + $fatal(1, "W counter underflowed!"); `ASSUME(NoAtopAllowed, !AtopSupport && slv_req_i.aw_valid |-> slv_req_i.aw.atop == '0) `endif `endif From c8c86eb1d6c0ffa8c3135976e548d7f9cc473a22 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Wed, 28 Sep 2022 16:49:45 +0200 Subject: [PATCH 062/145] axi_demux: Remove fallthrough parameter as it is no longer required --- src/axi_burst_splitter.sv | 1 - src/axi_demux.sv | 2 -- src/axi_id_serialize.sv | 1 - src/axi_isolate.sv | 1 - src/axi_to_mem_banked.sv | 1 - src/axi_xbar.sv | 1 - 6 files changed, 7 deletions(-) diff --git a/src/axi_burst_splitter.sv b/src/axi_burst_splitter.sv index a46ce2c5e..4fe6f7e78 100644 --- a/src/axi_burst_splitter.sv +++ b/src/axi_burst_splitter.sv @@ -78,7 +78,6 @@ module axi_burst_splitter #( .NoMstPorts ( 2 ), .MaxTrans ( MaxTxns ), .AxiLookBits ( IdWidth ), - .FallThrough ( 1'b1 ), .SpillAw ( 1'b0 ), .SpillW ( 1'b0 ), .SpillB ( 1'b0 ), diff --git a/src/axi_demux.sv b/src/axi_demux.sv index 37e2d5912..4ab4aadcf 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -51,7 +51,6 @@ module axi_demux #( parameter int unsigned MaxTrans = 32'd8, parameter int unsigned AxiLookBits = 32'd3, parameter bit UniqueIds = 1'b0, - parameter bit FallThrough = 1'b0, parameter bit SpillAw = 1'b1, parameter bit SpillW = 1'b0, parameter bit SpillB = 1'b0, @@ -870,7 +869,6 @@ module axi_demux_intf #( .MaxTrans ( MAX_TRANS ), .AxiLookBits ( AXI_LOOK_BITS ), .UniqueIds ( UNIQUE_IDS ), - .FallThrough ( FALL_THROUGH ), .SpillAw ( SPILL_AW ), .SpillW ( SPILL_W ), .SpillB ( SPILL_B ), diff --git a/src/axi_id_serialize.sv b/src/axi_id_serialize.sv index 8688cb355..df21c8b1d 100644 --- a/src/axi_id_serialize.sv +++ b/src/axi_id_serialize.sv @@ -160,7 +160,6 @@ module axi_id_serialize #( .NoMstPorts ( AxiMstPortMaxUniqIds ), .MaxTrans ( AxiSlvPortMaxTxns ), .AxiLookBits ( AxiSlvPortIdWidth ), - .FallThrough ( 1'b1 ), .SpillAw ( 1'b1 ), .SpillW ( 1'b0 ), .SpillB ( 1'b0 ), diff --git a/src/axi_isolate.sv b/src/axi_isolate.sv index da6bbc8bc..57c2529a0 100644 --- a/src/axi_isolate.sv +++ b/src/axi_isolate.sv @@ -107,7 +107,6 @@ module axi_isolate #( // We don't need many bits here as the common case will be to go for the pass-through. .AxiLookBits ( 1 ), .UniqueIds ( 1'b0 ), - .FallThrough ( 1'b1 ), .SpillAw ( 1'b0 ), .SpillW ( 1'b0 ), .SpillB ( 1'b0 ), diff --git a/src/axi_to_mem_banked.sv b/src/axi_to_mem_banked.sv index e50383d02..cc7c6198d 100644 --- a/src/axi_to_mem_banked.sv +++ b/src/axi_to_mem_banked.sv @@ -151,7 +151,6 @@ module axi_to_mem_banked #( .MaxTrans ( MemLatency+2 ), // allow multiple Ax vectors to not starve W channel .AxiLookBits ( 32'd1 ), // select is fixed, do not need it .UniqueIds ( 1'b0 ), - .FallThrough ( 1'b1 ), .SpillAw ( 1'b1 ), .SpillW ( 1'b1 ), .SpillB ( 1'b1 ), diff --git a/src/axi_xbar.sv b/src/axi_xbar.sv index 764084619..0971ef6f5 100644 --- a/src/axi_xbar.sv +++ b/src/axi_xbar.sv @@ -164,7 +164,6 @@ import cf_math_pkg::idx_width; .MaxTrans ( Cfg.MaxMstTrans ), .AxiLookBits ( Cfg.AxiIdUsedSlvPorts ), .UniqueIds ( Cfg.UniqueIds ), - .FallThrough ( Cfg.FallThrough ), .SpillAw ( Cfg.LatencyMode[9] ), .SpillW ( Cfg.LatencyMode[8] ), .SpillB ( Cfg.LatencyMode[7] ), From d30f8e03df56480c97871a8305f162b34c1fe7ba Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 29 Sep 2022 06:57:14 +0200 Subject: [PATCH 063/145] ci: Bump synopsys version --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a7ff3ac81..066fdc0ee 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - SYNOPSYS_DC: synopsys-2019.12 dc_shell -64bit + SYNOPSYS_DC: synopsys-2022.03 dc_shell -64bit before_script: - export PATH=~/.cargo/bin:$PATH From 361c78e0f38d5dacb0ba8c2f55cace8696cb5847 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 29 Sep 2022 14:36:48 +0200 Subject: [PATCH 064/145] Add a simple makefile to elaborate and run simulations (#269) * Makefile: Add a simple makefile to elaborate and run simulations * Makefile: Various updates --- .gitignore | 1 + Makefile | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 Makefile diff --git a/.gitignore b/.gitignore index c9eb4f507..0797ab037 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /build /Bender.lock /Bender.local +*.log diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..bbb83f1b9 --- /dev/null +++ b/Makefile @@ -0,0 +1,89 @@ +# 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 + +# select IIS-internal tool commands if we run on IIS machines +ifneq (,$(wildcard /etc/iis.version)) + VSIM ?= questa-2022.3 vsim + SYNOPSYS_DC ?= synopsys-2021.03 dcnxt_shell +else + VSIM ?= vsim + SYNOPSYS_DC ?= dc_shell +endif + +TBS ?= axi_addr_test \ + axi_atop_filter \ + axi_cdc axi_delayer \ + axi_dw_downsizer \ + axi_dw_upsizer \ + axi_fifo \ + axi_isolate \ + axi_iw_converter \ + axi_lite_regs \ + axi_lite_to_apb \ + axi_lite_to_axi \ + axi_lite_mailbox \ + axi_lite_xbar \ + axi_modify_address \ + axi_serializer \ + axi_sim_mem \ + axi_to_axi_lite \ + axi_to_mem_banked \ + axi_xbar + +SIM_TARGETS := $(addsuffix .log,$(addprefix sim-,$(TBS))) + + +.SHELL: bash + +.PHONY: help all sim_all clean + + +help: + @echo "" + @echo "elab.log: elaborates all files using Synopsys DC" + @echo "compile.log: compile files using Questasim" + @echo "sim-#TB#.log: simulates a given testbench, available TBs are:" + @echo "$(addprefix ###############-#,$(TBS))" | sed -e 's/ /\n/g' | sed -e 's/#/ /g' + @echo "sim_all: simulates all available testbenches" + @echo "" + @echo "clean: cleans generated files" + @echo "" + + +all: compile.log elab.log sim_all + + +sim_all: $(SIM_TARGETS) + + +build: + mkdir -p $@ + + +elab.log: Bender.yml | build + export SYNOPSYS_DC="$(SYNOPSYS_DC)"; cd build && ../scripts/synth.sh | tee ../$@ + + +compile.log: Bender.yml | build + export VSIM="$(VSIM)"; cd build && ../scripts/compile_vsim.sh | tee ../$@ + (! grep -n "Error:" $@) + + +sim-%.log: compile.log + export VSIM="$(VSIM)"; cd build && ../scripts/run_vsim.sh --random-seed $* | tee ../$@ + (! grep -n "Error:" $@) + (! grep -n "Fatal:" $@) + + +clean: + rm -rf build + rm -f *.log From 24a758f1218c90f595ac7e4c754b05aaaa1c9d95 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 29 Sep 2022 07:42:47 +0200 Subject: [PATCH 065/145] patch: Apply `0001-pulp_platform_axi-rename-axi-lite-responses-to-_rsp.patch` --- include/axi/typedef.svh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/axi/typedef.svh b/include/axi/typedef.svh index a2a860e50..7be902722 100644 --- a/include/axi/typedef.svh +++ b/include/axi/typedef.svh @@ -204,7 +204,7 @@ `AXI_LITE_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t) \ `AXI_LITE_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t) \ `AXI_LITE_TYPEDEF_REQ_T(__name``_req_t, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \ - `AXI_LITE_TYPEDEF_RESP_T(__name``_resp_t, __name``_b_chan_t, __name``_r_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(__name``_rsp_t, __name``_b_chan_t, __name``_r_chan_t) //////////////////////////////////////////////////////////////////////////////////////////////////// From dc2ad4fdde90cb96f57657f26a390ee90b13daee Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 29 Sep 2022 07:47:11 +0200 Subject: [PATCH 066/145] patch: Apply `0002-axi-vcs-compatibility-of-axi_isolate.patch` --- src/axi_isolate.sv | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/axi_isolate.sv b/src/axi_isolate.sv index 57c2529a0..6385b2633 100644 --- a/src/axi_isolate.sv +++ b/src/axi_isolate.sv @@ -46,13 +46,13 @@ module axi_isolate #( /// Support atomic operations (ATOPs) parameter bit AtopSupport = 1'b1, /// Address width of all AXI4+ATOP ports - parameter int unsigned AxiAddrWidth = 32'd0, + parameter int signed AxiAddrWidth = 32'd0, /// Data width of all AXI4+ATOP ports - parameter int unsigned AxiDataWidth = 32'd0, + parameter int signed AxiDataWidth = 32'd0, /// ID width of all AXI4+ATOP ports - parameter int unsigned AxiIdWidth = 32'd0, + parameter int signed AxiIdWidth = 32'd0, /// User signal width of all AXI4+ATOP ports - parameter int unsigned AxiUserWidth = 32'd0, + parameter int signed AxiUserWidth = 32'd0, /// Request struct type of all AXI4+ATOP ports parameter type axi_req_t = logic, /// Response struct type of all AXI4+ATOP ports From 48146b248536d52e4cb77835346d4c8b74edee93 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 29 Sep 2022 08:08:08 +0200 Subject: [PATCH 067/145] patch: Apply `0003-axi-add-patch-for-axi_demux-vcs-compatibility.patch` --- src/axi_demux.sv | 143 ++++++++++++++++++++++++++--------------------- 1 file changed, 80 insertions(+), 63 deletions(-) diff --git a/src/axi_demux.sv b/src/axi_demux.sv index 4ab4aadcf..2be03d037 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -170,7 +170,11 @@ module axi_demux #( //-------------------------------------- // comes from spill register at input aw_chan_select_t slv_aw_chan_select; - logic slv_aw_valid, slv_aw_ready; + aw_chan_t slv_aw_chan; + select_t slv_aw_select; + + logic slv_aw_valid, aw_valid_chan, aw_valid_sel; + logic slv_aw_ready, slv_aw_ready_chan, slv_aw_ready_sel; // AW ID counter select_t lookup_aw_select; @@ -205,7 +209,8 @@ module axi_demux #( //-------------------------------------- // comes from spill register at input ar_chan_select_t slv_ar_chan_select; - logic slv_ar_valid, slv_ar_ready; + logic slv_ar_valid, ar_valid_chan, ar_valid_sel; + logic slv_ar_ready, slv_ar_ready_chan, slv_ar_ready_sel; // AR ID counter select_t lookup_ar_select; @@ -234,32 +239,35 @@ module axi_demux #( // AW Channel //-------------------------------------- // spill register at the channel input - `ifdef TARGET_VSIM - // Workaround for bug in Questa 2020.2 and 2021.1: Flatten the struct into a logic vector before - // instantiating `spill_register`. - typedef logic [$bits(aw_chan_select_t)-1:0] aw_chan_select_flat_t; - `else - // Other tools, such as VCS, have problems with `$bits()`, so the workaround cannot be used - // generally. - typedef aw_chan_select_t aw_chan_select_flat_t; - `endif - aw_chan_select_flat_t slv_aw_chan_select_in_flat, - slv_aw_chan_select_out_flat; - assign slv_aw_chan_select_in_flat = {slv_req_i.aw, slv_aw_select_i}; spill_register #( - .T ( aw_chan_select_flat_t ), + .T ( aw_chan_t ), .Bypass ( ~SpillAw ) // because module param indicates if we want a spill reg - ) i_aw_spill_reg ( + ) i_aw_channel_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_aw_ready_chan ), + .data_i ( slv_req_i.aw ), + .valid_o ( aw_valid_chan ), + .ready_i ( slv_aw_ready ), + .data_o ( slv_aw_chan ) + ); + spill_register #( + .T ( select_t ), + .Bypass ( ~SpillAw ) // because module param indicates if we want a spill reg + ) i_aw_select_spill_reg ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .valid_i ( slv_req_i.aw_valid ), - .ready_o ( slv_resp_o.aw_ready ), - .data_i ( slv_aw_chan_select_in_flat ), - .valid_o ( slv_aw_valid ), + .ready_o ( slv_aw_ready_sel ), + .data_i ( slv_aw_select_i ), + .valid_o ( aw_valid_sel ), .ready_i ( slv_aw_ready ), - .data_o ( slv_aw_chan_select_out_flat ) + .data_o ( slv_aw_select ) ); - assign slv_aw_chan_select = slv_aw_chan_select_out_flat; + assign slv_resp_o.aw_ready = slv_aw_ready_chan & slv_aw_ready_sel; + assign slv_aw_valid = aw_valid_chan & aw_valid_sel; + assign slv_aw_chan_select = {slv_aw_chan, slv_aw_select}; // Control of the AW handshake always_comb begin @@ -286,7 +294,7 @@ module axi_demux #( lock_aw_valid_d = 1'b0; load_aw_lock = 1'b1; // inject the ATOP if necessary - atop_inject = slv_aw_chan_select.aw_chan.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; + atop_inject = slv_aw_chan.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; end end else begin // An AW can be handled if `i_aw_id_counter` and `i_counter_open_w` are not full. An ATOP that @@ -300,8 +308,8 @@ module axi_demux #( /// This prevents deadlocking of the W channel. The counters are there for the /// Handling of the B responses. if (slv_aw_valid && - ((w_open == '0) || (w_select == slv_aw_chan_select.aw_select)) && - (!aw_select_occupied || (slv_aw_chan_select.aw_select == lookup_aw_select))) begin + ((w_open == '0) || (w_select == slv_aw_select)) && + (!aw_select_occupied || (slv_aw_select == lookup_aw_select))) begin // connect the handshake aw_valid = 1'b1; // push arbitration to the W FIFO regardless, do not wait for the AW transaction @@ -309,7 +317,7 @@ module axi_demux #( // on AW transaction if (aw_ready) begin slv_aw_ready = 1'b1; - atop_inject = slv_aw_chan_select.aw_chan.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; + atop_inject = slv_aw_chan.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; // no AW transaction this cycle, lock the decision end else begin lock_aw_valid_d = 1'b1; @@ -330,7 +338,7 @@ module axi_demux #( // master port as all write transactions with the same ID, or both. This means that the // signals that are driven by the ID counters if this parameter is not set can instead be // derived from existing signals. The ID counters can therefore be omitted. - assign lookup_aw_select = slv_aw_chan_select.aw_select; + assign lookup_aw_select = slv_aw_select; assign aw_select_occupied = 1'b0; assign aw_id_cnt_full = 1'b0; end else begin : gen_aw_id_counter @@ -341,15 +349,15 @@ module axi_demux #( ) i_aw_id_counter ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), - .lookup_axi_id_i ( slv_aw_chan_select.aw_chan.id[0+:AxiLookBits] ), + .lookup_axi_id_i ( slv_aw_chan.id[0+:AxiLookBits] ), .lookup_mst_select_o ( lookup_aw_select ), .lookup_mst_select_occupied_o ( aw_select_occupied ), .full_o ( aw_id_cnt_full ), .inject_axi_id_i ( '0 ), .inject_i ( 1'b0 ), - .push_axi_id_i ( slv_aw_chan_select.aw_chan.id[0+:AxiLookBits] ), - .push_mst_select_i ( slv_aw_chan_select.aw_select ), - .push_i ( w_cnt_up ), + .push_axi_id_i ( slv_aw_chan.id[0+:AxiLookBits] ), + .push_mst_select_i ( slv_aw_select ), + .push_i ( w_cnt_up ), .pop_axi_id_i ( slv_b_chan.id[0+:AxiLookBits] ), .pop_i ( slv_b_valid & slv_b_ready ) ); @@ -438,28 +446,37 @@ module axi_demux #( // AR Channel //-------------------------------------- // Workaround for bug in Questa (see comments on AW channel for details). - `ifdef TARGET_VSIM - typedef logic [$bits(ar_chan_select_t)-1:0] ar_chan_select_flat_t; - `else - typedef ar_chan_select_t ar_chan_select_flat_t; - `endif - ar_chan_select_flat_t slv_ar_chan_select_in_flat, - slv_ar_chan_select_out_flat; - assign slv_ar_chan_select_in_flat = {slv_req_i.ar, slv_ar_select_i}; + ar_chan_t slv_ar_chan; + select_t slv_ar_select; spill_register #( - .T ( ar_chan_select_flat_t ), + .T ( ar_chan_t ), .Bypass ( ~SpillAr ) - ) i_ar_spill_reg ( + ) i_ar_chan_spill_reg ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .valid_i ( slv_req_i.ar_valid ), - .ready_o ( slv_resp_o.ar_ready ), - .data_i ( slv_ar_chan_select_in_flat ), - .valid_o ( slv_ar_valid ), + .ready_o ( slv_ar_ready_chan ), + .data_i ( slv_req_i.ar ), + .valid_o ( ar_valid_chan ), .ready_i ( slv_ar_ready ), - .data_o ( slv_ar_chan_select_out_flat ) + .data_o ( slv_ar_chan ) ); - assign slv_ar_chan_select = slv_ar_chan_select_out_flat; + spill_register #( + .T ( select_t ), + .Bypass ( ~SpillAr ) + ) i_ar_sel_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_ar_ready_sel ), + .data_i ( slv_ar_select_i ), + .valid_o ( ar_valid_sel ), + .ready_i ( slv_ar_ready ), + .data_o ( slv_ar_select ) + ); + assign slv_resp_o.ar_ready = slv_ar_ready_chan & slv_ar_ready_sel; + assign slv_ar_valid = ar_valid_chan & ar_valid_sel; + assign slv_ar_chan_select = {slv_ar_chan, slv_ar_select}; // control of the AR handshake always_comb begin @@ -490,7 +507,7 @@ module axi_demux #( if (!ar_id_cnt_full) begin // There is a valid AR, so look the ID up. if (slv_ar_valid && (!ar_select_occupied || - (slv_ar_chan_select.ar_select == lookup_ar_select))) begin + (slv_ar_select == lookup_ar_select))) begin // connect the AR handshake ar_valid = 1'b1; // on transaction @@ -516,7 +533,7 @@ module axi_demux #( // master port as all read transactions with the same ID, or both. This means that the // signals that are driven by the ID counters if this parameter is not set can instead be // derived from existing signals. The ID counters can therefore be omitted. - assign lookup_ar_select = slv_ar_chan_select.ar_select; + assign lookup_ar_select = slv_ar_select; assign ar_select_occupied = 1'b0; assign ar_id_cnt_full = 1'b0; end else begin : gen_ar_id_counter @@ -527,14 +544,14 @@ module axi_demux #( ) i_ar_id_counter ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), - .lookup_axi_id_i ( slv_ar_chan_select.ar_chan.id[0+:AxiLookBits] ), + .lookup_axi_id_i ( slv_ar_chan.id[0+:AxiLookBits] ), .lookup_mst_select_o ( lookup_ar_select ), .lookup_mst_select_occupied_o ( ar_select_occupied ), .full_o ( ar_id_cnt_full ), - .inject_axi_id_i ( slv_aw_chan_select.aw_chan.id[0+:AxiLookBits] ), + .inject_axi_id_i ( slv_aw_chan.id[0+:AxiLookBits] ), .inject_i ( atop_inject ), - .push_axi_id_i ( slv_ar_chan_select.ar_chan.id[0+:AxiLookBits] ), - .push_mst_select_i ( slv_ar_chan_select.ar_select ), + .push_axi_id_i ( slv_ar_chan.id[0+:AxiLookBits] ), + .push_mst_select_i ( slv_ar_select ), .push_i ( ar_push ), .pop_axi_id_i ( slv_r_chan.id[0+:AxiLookBits] ), .pop_i ( slv_r_valid & slv_r_ready & slv_r_chan.last ) @@ -579,8 +596,8 @@ module axi_demux #( .idx_o ( ) ); - assign ar_ready = ar_valid & mst_resps_i[slv_ar_chan_select.ar_select].ar_ready; - assign aw_ready = aw_valid & mst_resps_i[slv_aw_chan_select.aw_select].aw_ready; + assign ar_ready = ar_valid & mst_resps_i[slv_ar_select].ar_ready; + assign aw_ready = aw_valid & mst_resps_i[slv_aw_select].aw_ready; // process that defines the individual demuxes and assignments for the arbitration // as mst_reqs_o has to be drivem from the same always comb block! @@ -592,16 +609,16 @@ module axi_demux #( for (int unsigned i = 0; i < NoMstPorts; i++) begin // AW channel - mst_reqs_o[i].aw = slv_aw_chan_select.aw_chan; + mst_reqs_o[i].aw = slv_aw_chan; mst_reqs_o[i].aw_valid = 1'b0; - if (aw_valid && (slv_aw_chan_select.aw_select == i)) begin + if (aw_valid && (slv_aw_select == i)) begin mst_reqs_o[i].aw_valid = 1'b1; end // W channel mst_reqs_o[i].w = slv_w_chan; mst_reqs_o[i].w_valid = 1'b0; - if (w_select_valid && (w_select == select_t'(i))) begin + if (w_select_valid && (w_select == i)) begin mst_reqs_o[i].w_valid = slv_w_valid; slv_w_ready = mst_resps_i[i].w_ready; w_cnt_down = slv_w_valid & mst_resps_i[i].w_ready & slv_w_chan.last; @@ -611,9 +628,9 @@ module axi_demux #( mst_reqs_o[i].b_ready = mst_b_readies[i]; // AR channel - mst_reqs_o[i].ar = slv_ar_chan_select.ar_chan; + mst_reqs_o[i].ar = slv_ar_chan; mst_reqs_o[i].ar_valid = 1'b0; - if (ar_valid && (slv_ar_chan_select.ar_select == i)) begin + if (ar_valid && (slv_ar_select == i)) begin mst_reqs_o[i].ar_valid = 1'b1; end @@ -659,13 +676,13 @@ module axi_demux #( $fatal(1, "slv_aw_chan_select unstable with valid set."); ar_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) |=> $stable(slv_ar_chan_select)) else - $fatal(1, "slv_aw_chan_select unstable with valid set."); + $fatal(1, "slv_ar_chan_select unstable with valid set."); internal_ar_select: assert property( @(posedge clk_i) - (ar_valid |-> slv_ar_chan_select.ar_select < NoMstPorts)) - else $fatal(1, "slv_ar_chan_select.ar_select illegal while ar_valid."); + (ar_valid |-> slv_ar_select < NoMstPorts)) + else $fatal(1, "slv_ar_select illegal while ar_valid."); internal_aw_select: assert property( @(posedge clk_i) - (aw_valid |-> slv_aw_chan_select.aw_select < NoMstPorts)) - else $fatal(1, "slv_aw_chan_select.aw_select illegal while aw_valid."); + (aw_valid |-> slv_aw_select < NoMstPorts)) + else $fatal(1, "slv_aw_select illegal while aw_valid."); w_underflow: assert property( @(posedge clk_i) ((w_open == '0) && (w_cnt_up ^ w_cnt_down) |-> !w_cnt_down)) else $fatal(1, "W counter underflowed!"); From 5edbd81a12ae81f03951883d42e03a9af5380a0c Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 29 Sep 2022 08:11:35 +0200 Subject: [PATCH 068/145] axi_demux: Fix formatting --- src/axi_demux.sv | 126 +++++++++++++++++++++++------------------------ 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/src/axi_demux.sv b/src/axi_demux.sv index 2be03d037..d6f9ce2db 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -240,30 +240,30 @@ module axi_demux #( //-------------------------------------- // spill register at the channel input spill_register #( - .T ( aw_chan_t ), - .Bypass ( ~SpillAw ) // because module param indicates if we want a spill reg + .T ( aw_chan_t ), + .Bypass ( ~SpillAw ) // because module param indicates if we want a spill reg ) i_aw_channel_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.aw_valid ), - .ready_o ( slv_aw_ready_chan ), - .data_i ( slv_req_i.aw ), - .valid_o ( aw_valid_chan ), - .ready_i ( slv_aw_ready ), - .data_o ( slv_aw_chan ) + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_aw_ready_chan ), + .data_i ( slv_req_i.aw ), + .valid_o ( aw_valid_chan ), + .ready_i ( slv_aw_ready ), + .data_o ( slv_aw_chan ) ); spill_register #( - .T ( select_t ), - .Bypass ( ~SpillAw ) // because module param indicates if we want a spill reg + .T ( select_t ), + .Bypass ( ~SpillAw ) // because module param indicates if we want a spill reg ) i_aw_select_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.aw_valid ), - .ready_o ( slv_aw_ready_sel ), - .data_i ( slv_aw_select_i ), - .valid_o ( aw_valid_sel ), - .ready_i ( slv_aw_ready ), - .data_o ( slv_aw_select ) + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_aw_ready_sel ), + .data_i ( slv_aw_select_i ), + .valid_o ( aw_valid_sel ), + .ready_i ( slv_aw_ready ), + .data_o ( slv_aw_select ) ); assign slv_resp_o.aw_ready = slv_aw_ready_chan & slv_aw_ready_sel; assign slv_aw_valid = aw_valid_chan & aw_valid_sel; @@ -347,19 +347,19 @@ module axi_demux #( .CounterWidth ( IdCounterWidth ), .mst_port_select_t ( select_t ) ) i_aw_id_counter ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), .lookup_axi_id_i ( slv_aw_chan.id[0+:AxiLookBits] ), - .lookup_mst_select_o ( lookup_aw_select ), - .lookup_mst_select_occupied_o ( aw_select_occupied ), - .full_o ( aw_id_cnt_full ), - .inject_axi_id_i ( '0 ), - .inject_i ( 1'b0 ), + .lookup_mst_select_o ( lookup_aw_select ), + .lookup_mst_select_occupied_o ( aw_select_occupied ), + .full_o ( aw_id_cnt_full ), + .inject_axi_id_i ( '0 ), + .inject_i ( 1'b0 ), .push_axi_id_i ( slv_aw_chan.id[0+:AxiLookBits] ), .push_mst_select_i ( slv_aw_select ), - .push_i ( w_cnt_up ), - .pop_axi_id_i ( slv_b_chan.id[0+:AxiLookBits] ), - .pop_i ( slv_b_valid & slv_b_ready ) + .push_i ( w_cnt_up ), + .pop_axi_id_i ( slv_b_chan.id[0+:AxiLookBits] ), + .pop_i ( slv_b_valid & slv_b_ready ) ); // pop from ID counter on outward transaction end @@ -449,30 +449,30 @@ module axi_demux #( ar_chan_t slv_ar_chan; select_t slv_ar_select; spill_register #( - .T ( ar_chan_t ), - .Bypass ( ~SpillAr ) + .T ( ar_chan_t ), + .Bypass ( ~SpillAr ) ) i_ar_chan_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.ar_valid ), - .ready_o ( slv_ar_ready_chan ), - .data_i ( slv_req_i.ar ), - .valid_o ( ar_valid_chan ), - .ready_i ( slv_ar_ready ), - .data_o ( slv_ar_chan ) + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_ar_ready_chan ), + .data_i ( slv_req_i.ar ), + .valid_o ( ar_valid_chan ), + .ready_i ( slv_ar_ready ), + .data_o ( slv_ar_chan ) ); spill_register #( - .T ( select_t ), - .Bypass ( ~SpillAr ) + .T ( select_t ), + .Bypass ( ~SpillAr ) ) i_ar_sel_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.ar_valid ), - .ready_o ( slv_ar_ready_sel ), - .data_i ( slv_ar_select_i ), - .valid_o ( ar_valid_sel ), - .ready_i ( slv_ar_ready ), - .data_o ( slv_ar_select ) + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_ar_ready_sel ), + .data_i ( slv_ar_select_i ), + .valid_o ( ar_valid_sel ), + .ready_i ( slv_ar_ready ), + .data_o ( slv_ar_select ) ); assign slv_resp_o.ar_ready = slv_ar_ready_chan & slv_ar_ready_sel; assign slv_ar_valid = ar_valid_chan & ar_valid_sel; @@ -542,19 +542,19 @@ module axi_demux #( .CounterWidth ( IdCounterWidth ), .mst_port_select_t ( select_t ) ) i_ar_id_counter ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .lookup_axi_id_i ( slv_ar_chan.id[0+:AxiLookBits] ), - .lookup_mst_select_o ( lookup_ar_select ), - .lookup_mst_select_occupied_o ( ar_select_occupied ), - .full_o ( ar_id_cnt_full ), - .inject_axi_id_i ( slv_aw_chan.id[0+:AxiLookBits] ), - .inject_i ( atop_inject ), - .push_axi_id_i ( slv_ar_chan.id[0+:AxiLookBits] ), - .push_mst_select_i ( slv_ar_select ), - .push_i ( ar_push ), - .pop_axi_id_i ( slv_r_chan.id[0+:AxiLookBits] ), - .pop_i ( slv_r_valid & slv_r_ready & slv_r_chan.last ) + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .lookup_axi_id_i ( slv_ar_chan.id[0+:AxiLookBits] ), + .lookup_mst_select_o ( lookup_ar_select ), + .lookup_mst_select_occupied_o ( ar_select_occupied ), + .full_o ( ar_id_cnt_full ), + .inject_axi_id_i ( slv_aw_chan.id[0+:AxiLookBits] ), + .inject_i ( atop_inject ), + .push_axi_id_i ( slv_ar_chan.id[0+:AxiLookBits] ), + .push_mst_select_i ( slv_ar_select ), + .push_i ( ar_push ), + .pop_axi_id_i ( slv_r_chan.id[0+:AxiLookBits] ), + .pop_i ( slv_r_valid & slv_r_ready & slv_r_chan.last ) ); end From ea9a7fd99334a5ed8ecdf16efc30157c9dcd68f8 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 29 Sep 2022 08:17:37 +0200 Subject: [PATCH 069/145] patch: Apply `0007-vendor-add-AtopSupport-parameter-to-axi_id_serialize.patch` --- src/axi_id_serialize.sv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/axi_id_serialize.sv b/src/axi_id_serialize.sv index df21c8b1d..b9a11c021 100644 --- a/src/axi_id_serialize.sv +++ b/src/axi_id_serialize.sv @@ -26,6 +26,7 @@ /// This module contains one [`axi_serializer`](module.axi_serializer) per master port ID (given by /// the `AxiMstPortMaxUniqIds parameter`). module axi_id_serialize #( + parameter bit AtopSupport = 1'b1, /// ID width of the AXI4+ATOP slave port parameter int unsigned AxiSlvPortIdWidth = 32'd0, /// Maximum number of transactions that can be in flight at the slave port. Reads and writes are @@ -149,6 +150,7 @@ module axi_id_serialize #( slv_resp_t [AxiMstPortMaxUniqIds-1:0] to_serializer_resps; axi_demux #( + .AtopSupport ( AtopSupport ), .AxiIdWidth ( AxiSlvPortIdWidth ), .aw_chan_t ( slv_aw_t ), .w_chan_t ( w_t ), From 1f0438cca2a0694113800d1c4fd282da833b7626 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 29 Sep 2022 08:19:16 +0200 Subject: [PATCH 070/145] axi_id_serialize: Cleanup parameter map --- src/axi_id_serialize.sv | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/axi_id_serialize.sv b/src/axi_id_serialize.sv index b9a11c021..b3d28e839 100644 --- a/src/axi_id_serialize.sv +++ b/src/axi_id_serialize.sv @@ -26,7 +26,6 @@ /// This module contains one [`axi_serializer`](module.axi_serializer) per master port ID (given by /// the `AxiMstPortMaxUniqIds parameter`). module axi_id_serialize #( - parameter bit AtopSupport = 1'b1, /// ID width of the AXI4+ATOP slave port parameter int unsigned AxiSlvPortIdWidth = 32'd0, /// Maximum number of transactions that can be in flight at the slave port. Reads and writes are @@ -47,6 +46,8 @@ module axi_id_serialize #( parameter int unsigned AxiDataWidth = 32'd0, /// User width of both AXI4+ATOP ports parameter int unsigned AxiUserWidth = 32'd0, + /// Enable support for AXI4+ATOP atomics + parameter bit AtopSupport = 1'b1, /// Request struct type of the AXI4+ATOP slave port parameter type slv_req_t = logic, /// Response struct type of the AXI4+ATOP slave port @@ -150,7 +151,6 @@ module axi_id_serialize #( slv_resp_t [AxiMstPortMaxUniqIds-1:0] to_serializer_resps; axi_demux #( - .AtopSupport ( AtopSupport ), .AxiIdWidth ( AxiSlvPortIdWidth ), .aw_chan_t ( slv_aw_t ), .w_chan_t ( w_t ), @@ -162,6 +162,7 @@ module axi_id_serialize #( .NoMstPorts ( AxiMstPortMaxUniqIds ), .MaxTrans ( AxiSlvPortMaxTxns ), .AxiLookBits ( AxiSlvPortIdWidth ), + .AtopSupport ( AtopSupport ), .SpillAw ( 1'b1 ), .SpillW ( 1'b0 ), .SpillB ( 1'b0 ), From 12b717727e78c0529bcdc05208658603e118c033 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 29 Sep 2022 08:26:46 +0200 Subject: [PATCH 071/145] patch: Apply `0009-axi-Compatibility-axi_id_remap.patch` --- src/axi_id_remap.sv | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/axi_id_remap.sv b/src/axi_id_remap.sv index 2d50281de..ebcce76a4 100644 --- a/src/axi_id_remap.sv +++ b/src/axi_id_remap.sv @@ -285,13 +285,21 @@ module axi_id_remap #( aw_id_d = wr_push_oup_id; end end - priority casez ({mst_req_o.ar_valid, mst_resp_i.ar_ready, - mst_req_o.aw_valid, mst_resp_i.aw_ready}) - 4'b1010: state_d = HoldAx; - 4'b10??: state_d = HoldAR; - 4'b??10: state_d = HoldAW; - default: state_d = Ready; - endcase + if ({mst_req_o.ar_valid, mst_resp_i.ar_ready, + mst_req_o.aw_valid, mst_resp_i.aw_ready} == 4'b1010) begin + state_d = HoldAx; + end else if ({mst_req_o.ar_valid, mst_resp_i.ar_ready} == 2'b10) begin + state_d = HoldAR; + end else if ({mst_req_o.aw_valid, mst_resp_i.aw_ready} == 2'b10) begin + state_d = HoldAW; + end else state_d = Ready; + // priority casez ({mst_req_o.ar_valid, mst_resp_i.ar_ready, + // mst_req_o.aw_valid, mst_resp_i.aw_ready}) + // 4'b1010: state_d = HoldAx; + // 4'b10??: state_d = HoldAR; + // 4'b??10: state_d = HoldAW; + // default: state_d = Ready; + // endcase if (mst_req_o.ar_valid && mst_resp_i.ar_ready) begin ar_prio_d = 1'b0; // Reset AR priority, because handshake was successful in this cycle. From e4600cc42acfb3a1ecb08284cae03259f772e965 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 29 Sep 2022 08:28:53 +0200 Subject: [PATCH 072/145] axi_id_remap: Remove commented code fragment --- src/axi_id_remap.sv | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/axi_id_remap.sv b/src/axi_id_remap.sv index ebcce76a4..85dc66e0d 100644 --- a/src/axi_id_remap.sv +++ b/src/axi_id_remap.sv @@ -292,14 +292,9 @@ module axi_id_remap #( state_d = HoldAR; end else if ({mst_req_o.aw_valid, mst_resp_i.aw_ready} == 2'b10) begin state_d = HoldAW; - end else state_d = Ready; - // priority casez ({mst_req_o.ar_valid, mst_resp_i.ar_ready, - // mst_req_o.aw_valid, mst_resp_i.aw_ready}) - // 4'b1010: state_d = HoldAx; - // 4'b10??: state_d = HoldAR; - // 4'b??10: state_d = HoldAW; - // default: state_d = Ready; - // endcase + end else begin + state_d = Ready; + end if (mst_req_o.ar_valid && mst_resp_i.ar_ready) begin ar_prio_d = 1'b0; // Reset AR priority, because handshake was successful in this cycle. From c408885cfce0787926fd8a21fc22f1f880e846c3 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Fri, 30 Sep 2022 09:51:26 +0200 Subject: [PATCH 073/145] tools: Update to newest version --- .gitlab-ci.yml | 6 +++--- Makefile | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 066fdc0ee..15844b86e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - SYNOPSYS_DC: synopsys-2022.03 dc_shell -64bit + SYNOPSYS_DC: synopsys-2022.03 dcnxt_shell before_script: - export PATH=~/.cargo/bin:$PATH @@ -28,7 +28,7 @@ vsim: fi parallel: matrix: - - VSIM_VER: ['10.7b', '10.7e', '2021.3'] + - VSIM_VER: ['10.7b', '2022.3'] synopsys_dc: stage: build @@ -71,7 +71,7 @@ fuse_xsim: fi parallel: matrix: - - VSIM_VER: ['10.7b', '10.7e', '2021.3'] + - VSIM_VER: ['10.7b', '2022.3'] axi_addr_test: <<: *run_vsim diff --git a/Makefile b/Makefile index bbb83f1b9..5d97a9542 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ # select IIS-internal tool commands if we run on IIS machines ifneq (,$(wildcard /etc/iis.version)) VSIM ?= questa-2022.3 vsim - SYNOPSYS_DC ?= synopsys-2021.03 dcnxt_shell + SYNOPSYS_DC ?= synopsys-2022.03 dcnxt_shell else VSIM ?= vsim SYNOPSYS_DC ?= dc_shell @@ -71,6 +71,7 @@ build: elab.log: Bender.yml | build export SYNOPSYS_DC="$(SYNOPSYS_DC)"; cd build && ../scripts/synth.sh | tee ../$@ + (! grep -n "Error:" $@) compile.log: Bender.yml | build From 0439976856ab09ada6d171141f87ffc979c7a380 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Fri, 30 Sep 2022 11:02:18 +0200 Subject: [PATCH 074/145] typedef: Undo `resp` to `rsp` rename, add more flexible macro --- include/axi/typedef.svh | 65 +++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/include/axi/typedef.svh b/include/axi/typedef.svh index 7be902722..5eec91171 100644 --- a/include/axi/typedef.svh +++ b/include/axi/typedef.svh @@ -104,6 +104,28 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// All AXI4+ATOP Channels and Request/Response Structs in One Macro - Custom Type Name Version +// +// This can be used whenever the user is not interested in "precise" control of the naming of the +// individual channels. +// +// Usage Example: +// `AXI_TYPEDEF_ALL_CT(axi, axi_req_t, axi_rsp_t, addr_t, id_t, data_t, strb_t, user_t) +// +// This defines `axi_req_t` and `axi_rsp_t` request/response structs as well as `axi_aw_chan_t`, +// `axi_w_chan_t`, `axi_b_chan_t`, `axi_ar_chan_t`, and `axi_r_chan_t` channel structs. +`define AXI_TYPEDEF_ALL_CT(__name, __req, __rsp, __addr_t, __id_t, __data_t, __strb_t, __user_t) \ + `AXI_TYPEDEF_AW_CHAN_T(__name``_aw_chan_t, __addr_t, __id_t, __user_t) \ + `AXI_TYPEDEF_W_CHAN_T(__name``_w_chan_t, __data_t, __strb_t, __user_t) \ + `AXI_TYPEDEF_B_CHAN_T(__name``_b_chan_t, __id_t, __user_t) \ + `AXI_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t, __id_t, __user_t) \ + `AXI_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t, __id_t, __user_t) \ + `AXI_TYPEDEF_REQ_T(__req, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \ + `AXI_TYPEDEF_RESP_T(__rsp, __name``_b_chan_t, __name``_r_chan_t) +//////////////////////////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////////////////////////// // All AXI4+ATOP Channels and Request/Response Structs in One Macro // @@ -115,14 +137,8 @@ // // This defines `axi_req_t` and `axi_resp_t` request/response structs as well as `axi_aw_chan_t`, // `axi_w_chan_t`, `axi_b_chan_t`, `axi_ar_chan_t`, and `axi_r_chan_t` channel structs. -`define AXI_TYPEDEF_ALL(__name, __addr_t, __id_t, __data_t, __strb_t, __user_t) \ - `AXI_TYPEDEF_AW_CHAN_T(__name``_aw_chan_t, __addr_t, __id_t, __user_t) \ - `AXI_TYPEDEF_W_CHAN_T(__name``_w_chan_t, __data_t, __strb_t, __user_t) \ - `AXI_TYPEDEF_B_CHAN_T(__name``_b_chan_t, __id_t, __user_t) \ - `AXI_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t, __id_t, __user_t) \ - `AXI_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t, __id_t, __user_t) \ - `AXI_TYPEDEF_REQ_T(__name``_req_t, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \ - `AXI_TYPEDEF_RESP_T(__name``_resp_t, __name``_b_chan_t, __name``_r_chan_t) +`define AXI_TYPEDEF_ALL(__name, __addr_t, __id_t, __data_t, __strb_t, __user_t) \ + `AXI_TYPEDEF_ALL_CT(__name, __name``_req_t, __name``_resp_t, __addr_t, __id_t, __data_t, __strb_t, __user_t) //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -185,6 +201,29 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// All AXI4-Lite Channels and Request/Response Structs in One Macro - Custom Type Name Version +// +// This can be used whenever the user is not interested in "precise" control of the naming of the +// individual channels. +// +// Usage Example: +// `AXI_LITE_TYPEDEF_ALL_CT(axi_lite, axi_lite_req_t, axi_lite_rsp_t, addr_t, data_t, strb_t) +// +// This defines `axi_lite_req_t` and `axi_lite_resp_t` request/response structs as well as +// `axi_lite_aw_chan_t`, `axi_lite_w_chan_t`, `axi_lite_b_chan_t`, `axi_lite_ar_chan_t`, and +// `axi_lite_r_chan_t` channel structs. +`define AXI_LITE_TYPEDEF_ALL_CT(__name, __req, __rsp, __addr_t, __data_t, __strb_t) \ + `AXI_LITE_TYPEDEF_AW_CHAN_T(__name``_aw_chan_t, __addr_t) \ + `AXI_LITE_TYPEDEF_W_CHAN_T(__name``_w_chan_t, __data_t, __strb_t) \ + `AXI_LITE_TYPEDEF_B_CHAN_T(__name``_b_chan_t) \ + `AXI_LITE_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t) \ + `AXI_LITE_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t) \ + `AXI_LITE_TYPEDEF_REQ_T(__req, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \ + `AXI_LITE_TYPEDEF_RESP_T(__rsp, __name``_b_chan_t, __name``_r_chan_t) +//////////////////////////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////////////////////////// // All AXI4-Lite Channels and Request/Response Structs in One Macro // @@ -197,14 +236,8 @@ // This defines `axi_lite_req_t` and `axi_lite_resp_t` request/response structs as well as // `axi_lite_aw_chan_t`, `axi_lite_w_chan_t`, `axi_lite_b_chan_t`, `axi_lite_ar_chan_t`, and // `axi_lite_r_chan_t` channel structs. -`define AXI_LITE_TYPEDEF_ALL(__name, __addr_t, __data_t, __strb_t) \ - `AXI_LITE_TYPEDEF_AW_CHAN_T(__name``_aw_chan_t, __addr_t) \ - `AXI_LITE_TYPEDEF_W_CHAN_T(__name``_w_chan_t, __data_t, __strb_t) \ - `AXI_LITE_TYPEDEF_B_CHAN_T(__name``_b_chan_t) \ - `AXI_LITE_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t) \ - `AXI_LITE_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t) \ - `AXI_LITE_TYPEDEF_REQ_T(__name``_req_t, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \ - `AXI_LITE_TYPEDEF_RESP_T(__name``_rsp_t, __name``_b_chan_t, __name``_r_chan_t) +`define AXI_LITE_TYPEDEF_ALL(__name, __addr_t, __data_t, __strb_t) \ + `AXI_LITE_TYPEDEF_ALL_CT(__name, __name``_req_t, __name``_resp_t, __addr_t, __data_t, __strb_t) //////////////////////////////////////////////////////////////////////////////////////////////////// From 803aab93b3427f1821220a76dcd3a3703b6bfc63 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Fri, 30 Sep 2022 11:03:27 +0200 Subject: [PATCH 075/145] axi_demux: Cleanup and simplification --- src/axi_demux.sv | 50 +++++++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/src/axi_demux.sv b/src/axi_demux.sv index d6f9ce2db..25e5b9223 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -76,17 +76,6 @@ module axi_demux #( localparam int unsigned IdCounterWidth = cf_math_pkg::idx_width(MaxTrans); typedef logic [IdCounterWidth-1:0] id_cnt_t; - //-------------------------------------- - // Typedefs for the FIFOs / Queues - //-------------------------------------- - typedef struct packed { - aw_chan_t aw_chan; - select_t aw_select; - } aw_chan_select_t; - typedef struct packed { - ar_chan_t ar_chan; - select_t ar_select; - } ar_chan_select_t; // pass through if only one master port if (NoMstPorts == 32'h1) begin : gen_no_demux @@ -169,11 +158,10 @@ module axi_demux #( // Write Transaction //-------------------------------------- // comes from spill register at input - aw_chan_select_t slv_aw_chan_select; aw_chan_t slv_aw_chan; select_t slv_aw_select; - logic slv_aw_valid, aw_valid_chan, aw_valid_sel; + logic slv_aw_valid, slv_aw_valid_chan, slv_aw_valid_sel; logic slv_aw_ready, slv_aw_ready_chan, slv_aw_ready_sel; // AW ID counter @@ -208,7 +196,6 @@ module axi_demux #( // Read Transaction //-------------------------------------- // comes from spill register at input - ar_chan_select_t slv_ar_chan_select; logic slv_ar_valid, ar_valid_chan, ar_valid_sel; logic slv_ar_ready, slv_ar_ready_chan, slv_ar_ready_sel; @@ -248,7 +235,7 @@ module axi_demux #( .valid_i ( slv_req_i.aw_valid ), .ready_o ( slv_aw_ready_chan ), .data_i ( slv_req_i.aw ), - .valid_o ( aw_valid_chan ), + .valid_o ( slv_aw_valid_chan ), .ready_i ( slv_aw_ready ), .data_o ( slv_aw_chan ) ); @@ -261,13 +248,12 @@ module axi_demux #( .valid_i ( slv_req_i.aw_valid ), .ready_o ( slv_aw_ready_sel ), .data_i ( slv_aw_select_i ), - .valid_o ( aw_valid_sel ), + .valid_o ( slv_aw_valid_sel ), .ready_i ( slv_aw_ready ), .data_o ( slv_aw_select ) ); assign slv_resp_o.aw_ready = slv_aw_ready_chan & slv_aw_ready_sel; - assign slv_aw_valid = aw_valid_chan & aw_valid_sel; - assign slv_aw_chan_select = {slv_aw_chan, slv_aw_select}; + assign slv_aw_valid = slv_aw_valid_chan & slv_aw_valid_sel; // Control of the AW handshake always_comb begin @@ -301,7 +287,7 @@ module axi_demux #( // requires an R response can be handled if additionally `i_ar_id_counter` is not full (this // only applies if ATOPs are supported at all). if (!aw_id_cnt_full && (w_open != {IdCounterWidth{1'b1}}) && - (!(ar_id_cnt_full && slv_aw_chan_select.aw_chan.atop[axi_pkg::ATOP_R_RESP]) || + (!(ar_id_cnt_full && slv_aw_chan.atop[axi_pkg::ATOP_R_RESP]) || !AtopSupport)) begin /// There is a valid AW vector make the id lookup and go further, if it passes. /// Also stall if previous transmitted AWs still have active W's in flight. @@ -367,7 +353,7 @@ module axi_demux #( // This counter steers the demultiplexer of the W channel. // `w_select` determines, which handshaking is connected. // AWs are only forwarded, if the counter is empty, or `w_select_q` is the same as - // `slv_aw_chan_select.aw_select`. + // `slv_aw_select`. counter #( .WIDTH ( IdCounterWidth ), .STICKY_OVERFLOW ( 1'b0 ) @@ -383,8 +369,8 @@ module axi_demux #( .overflow_o ( /*not used*/ ) ); - `FFLARN(w_select_q, slv_aw_chan_select.aw_select, w_cnt_up, select_t'(0), clk_i, rst_ni) - assign w_select = (|w_open) ? w_select_q : slv_aw_chan_select.aw_select; + `FFLARN(w_select_q, slv_aw_select, w_cnt_up, select_t'(0), clk_i, rst_ni) + assign w_select = (|w_open) ? w_select_q : slv_aw_select; assign w_select_valid = w_cnt_up | (|w_open); //-------------------------------------- @@ -445,7 +431,6 @@ module axi_demux #( //-------------------------------------- // AR Channel //-------------------------------------- - // Workaround for bug in Questa (see comments on AW channel for details). ar_chan_t slv_ar_chan; select_t slv_ar_select; spill_register #( @@ -476,7 +461,6 @@ module axi_demux #( ); assign slv_resp_o.ar_ready = slv_ar_ready_chan & slv_ar_ready_sel; assign slv_ar_valid = ar_valid_chan & ar_valid_sel; - assign slv_ar_chan_select = {slv_ar_chan, slv_ar_select}; // control of the AR handshake always_comb begin @@ -671,12 +655,18 @@ module axi_demux #( ar_valid_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) |=> ar_valid) else $fatal(1, "ar_valid was deasserted, when ar_ready = 0 in last cycle."); - aw_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) - |=> $stable(slv_aw_chan_select)) else - $fatal(1, "slv_aw_chan_select unstable with valid set."); - ar_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) - |=> $stable(slv_ar_chan_select)) else - $fatal(1, "slv_ar_chan_select unstable with valid set."); + slv_aw_chan: assert property( @(posedge clk_i) (aw_valid && !aw_ready) + |=> $stable(slv_aw_chan)) else + $fatal(1, "slv_aw_chan unstable with valid set."); + slv_aw_select: assert property( @(posedge clk_i) (aw_valid && !aw_ready) + |=> $stable(slv_aw_select)) else + $fatal(1, "slv_aw_select unstable with valid set."); + slv_ar_chan: assert property( @(posedge clk_i) (ar_valid && !ar_ready) + |=> $stable(slv_ar_chan)) else + $fatal(1, "slv_ar_chan unstable with valid set."); + slv_ar_select: assert property( @(posedge clk_i) (ar_valid && !ar_ready) + |=> $stable(slv_ar_select)) else + $fatal(1, "slv_ar_select unstable with valid set."); internal_ar_select: assert property( @(posedge clk_i) (ar_valid |-> slv_ar_select < NoMstPorts)) else $fatal(1, "slv_ar_select illegal while ar_valid."); From f57bf3428c22b6907dc5d5e42797f30285a25bbc Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Fri, 30 Sep 2022 12:54:30 +0200 Subject: [PATCH 076/145] axi_demux: Fix assertions --- src/axi_demux.sv | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/axi_demux.sv b/src/axi_demux.sv index 25e5b9223..184e9b23c 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -655,16 +655,16 @@ module axi_demux #( ar_valid_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) |=> ar_valid) else $fatal(1, "ar_valid was deasserted, when ar_ready = 0 in last cycle."); - slv_aw_chan: assert property( @(posedge clk_i) (aw_valid && !aw_ready) + slv_aw_chan_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) |=> $stable(slv_aw_chan)) else $fatal(1, "slv_aw_chan unstable with valid set."); - slv_aw_select: assert property( @(posedge clk_i) (aw_valid && !aw_ready) + slv_aw_select_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) |=> $stable(slv_aw_select)) else $fatal(1, "slv_aw_select unstable with valid set."); - slv_ar_chan: assert property( @(posedge clk_i) (ar_valid && !ar_ready) + slv_ar_chan_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) |=> $stable(slv_ar_chan)) else $fatal(1, "slv_ar_chan unstable with valid set."); - slv_ar_select: assert property( @(posedge clk_i) (ar_valid && !ar_ready) + slv_ar_select_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) |=> $stable(slv_ar_select)) else $fatal(1, "slv_ar_select unstable with valid set."); internal_ar_select: assert property( @(posedge clk_i) From c13946f418b0efc0961f341a1da1862349e9df1a Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Fri, 30 Sep 2022 13:48:55 +0200 Subject: [PATCH 077/145] chan_compare: Non-synthesizable module comparing two AXI channels of the same type --- Bender.yml | 1 + CHANGELOG.md | 1 + README.md | 1 + axi.core | 2 + src/axi_chan_compare.sv | 212 ++++++++++++++++++++++++++++++++++++++++ src_files.yml | 2 + 6 files changed, 219 insertions(+) create mode 100644 src/axi_chan_compare.sv diff --git a/Bender.yml b/Bender.yml index f0ffdc11c..952d5139e 100644 --- a/Bender.yml +++ b/Bender.yml @@ -84,6 +84,7 @@ sources: - target: simulation files: + - src/axi_chan_compare.sv - src/axi_dumper.sv - src/axi_sim_mem.sv - src/axi_test.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index c6f5aaed5..ae12b1c2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased ### Added +- Add `axi_channel_compare.sv`: Non-synthesizable module comparing two AXI channels of the same type ### Changed - `axi_demux`: Replace write FIFO (`w_fifo`) with a write credit counter. diff --git a/README.md b/README.md index 85cf211f1..489eee09b 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ In addition to the modules above, which are available in synthesis and simulatio | Name | Description | |------------------------------------------------------|--------------------------------------------------------------------------------------------------------| +| [`axi_chan_compare`](src/axi_chan_compare.sv) | Non-synthesizable module comparing two AXI channels of the same type | | [`axi_chan_logger`](src/axi_test.sv) | Logs the transactions of an AXI4(+ATOPs) port to files. | | [`axi_driver`](src/axi_test.sv) | Low-level driver for AXI4(+ATOPs) that can send and receive individual beats on any channel. | | [`axi_dumper`](src/axi_dumper.sv) | Dumps log to file to be interpreted by `axi_dumper_interpret` script for debugging purposes. | diff --git a/axi.core b/axi.core index cc4e98609..198fd5013 100644 --- a/axi.core +++ b/axi.core @@ -57,6 +57,8 @@ filesets: - src/axi_iw_converter.sv - src/axi_lite_xbar.sv - src/axi_xbar.sv + - src/axi_chan_compare.sv + - src/axi_dumper.sv - src/axi_sim_mem.sv - src/axi_test.sv # Level 5 diff --git a/src/axi_chan_compare.sv b/src/axi_chan_compare.sv new file mode 100644 index 000000000..4de4ea741 --- /dev/null +++ b/src/axi_chan_compare.sv @@ -0,0 +1,212 @@ +// Copyright 2018 ETH Zurich and 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 +// - Paul Scheffler +// - Tim Fischer + +/// Non-synthesizable module comparing two AXI channels of the same type +module axi_chan_compare #( + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, + parameter type req_t = logic, + parameter type resp_t = logic +)( + input logic clk_a_i, + input logic clk_b_i, + input req_t axi_a_req, + input resp_t axi_a_res, + input req_t axi_b_req, + input resp_t axi_b_res +); + + function automatic void print_aw ( + input aw_chan_t aw_expected, + input aw_chan_t aw_received + ); + // verilog_lint: waive-start line-length + $display("AW | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("id: | %64d | %64d", aw_expected.id, aw_received.id); + $display("addr: | %64x | %64x", aw_expected.addr, aw_received.addr); + $display("len: | %64d | %64d", aw_expected.len, aw_received.len); + $display("size: | %64d | %64d", aw_expected.size, aw_received.size); + $display("burst: | %64d | %64d", aw_expected.burst, aw_received.burst); + $display("lock: | %64d | %64d", aw_expected.lock, aw_received.lock); + $display("cache: | %64d | %64d", aw_expected.cache, aw_received.cache); + $display("prot: | %64d | %64d", aw_expected.prot, aw_received.prot); + $display("qos: | %64d | %64d", aw_expected.qos, aw_received.qos); + $display("region: | %64d | %64d", aw_expected.region, aw_received.region); + $display("user: | %64d | %64d", aw_expected.user, aw_received.user); + $display("atop: | %64d | %64d", aw_expected.atop, aw_received.atop); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + function automatic void print_ar ( + input ar_chan_t ar_expected, + input ar_chan_t ar_received + ); + // verilog_lint: waive-start line-length + $display("AR | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("id: | %64d | %64d", ar_expected.id, ar_received.id); + $display("addr: | %64x | %64x", ar_expected.addr, ar_received.addr); + $display("len: | %64d | %64d", ar_expected.len, ar_received.len); + $display("size: | %64d | %64d", ar_expected.size, ar_received.size); + $display("burst: | %64d | %64d", ar_expected.burst, ar_received.burst); + $display("lock: | %64d | %64d", ar_expected.lock, ar_received.lock); + $display("cache: | %64d | %64d", ar_expected.cache, ar_received.cache); + $display("prot: | %64d | %64d", ar_expected.prot, ar_received.prot); + $display("qos: | %64d | %64d", ar_expected.qos, ar_received.qos); + $display("region: | %64d | %64d", ar_expected.region, ar_received.region); + $display("user: | %64d | %64d", ar_expected.user, ar_received.user); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + function automatic void print_w ( + input w_chan_t w_expected, + input w_chan_t w_received + ); + // verilog_lint: waive-start line-length + $display("W | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("data: | %64x | %64x", w_expected.data, w_received.data); + $display("strb: | %64d | %64d", w_expected.strb, w_received.strb); + $display("last: | %64d | %64d", w_expected.last, w_received.last); + $display("user: | %64d | %64d", w_expected.user, w_received.user); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + function automatic void print_b ( + input b_chan_t b_expected, + input b_chan_t b_received + ); + // verilog_lint: waive-start line-length + $display("B | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("id: | %64d | %64d", b_expected.id, b_received.id); + $display("resp: | %64d | %64d", b_expected.resp, b_received.resp); + $display("user: | %64d | %64d", b_expected.user, b_received.user); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + function automatic void print_r ( + input r_chan_t r_expected, + input r_chan_t r_received + ); + // verilog_lint: waive-start line-length + $display("R | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("id: | %64d | %64d", r_expected.id, r_received.id); + $display("data: | %64x | %64x", r_expected.data, r_received.data); + $display("resp: | %64d | %64d", r_expected.resp, r_received.resp); + $display("last: | %64d | %64d", r_expected.last, r_received.last); + $display("user: | %64d | %64d", r_expected.user, r_received.user); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + // queues + aw_chan_t aw_queue [$]; + w_chan_t w_queue [$]; + b_chan_t b_queue [$]; + ar_chan_t ar_queue [$]; + r_chan_t r_queue [$]; + + // requests generated at axi A: enqueue elements + always_ff @(posedge clk_a_i) begin : proc_enqueue_a + // aw + if (axi_a_req.aw_valid & axi_a_res.aw_ready) + aw_queue.push_back(axi_a_req.aw); + // w + if (axi_a_req.w_valid & axi_a_res.w_ready) + w_queue.push_back(axi_a_req.w); + // ar + if (axi_a_req.ar_valid & axi_a_res.ar_ready) + ar_queue.push_back(axi_a_req.ar); + end + + // responses generated at axi B: enqueue elements + always_ff @(posedge clk_b_i) begin : proc_enqueue_b + // b + if (axi_b_res.b_valid & axi_b_req.b_ready) + b_queue.push_back(axi_b_res.b); + // r + if (axi_b_res.r_valid & axi_b_req.r_ready) + r_queue.push_back(axi_b_res.r); + end + + // requests arriving at axi B from A: dequeue elements and check + always_ff @(posedge clk_b_i) begin : proc_dequeue_and_check_b + // aw + if (axi_b_req.aw_valid & axi_b_res.aw_ready) begin + automatic aw_chan_t aw; + if (aw_queue.size() == 0) $error("AW queue is empty!"); + aw = aw_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + if (axi_b_req.aw !== aw) begin + $error("AW mismatch!"); + print_aw(aw, axi_b_req.aw); + end + end + // w + if (axi_b_req.w_valid & axi_b_res.w_ready) begin + automatic w_chan_t w; + if (w_queue.size() == 0) $error("W queue is empty!"); + w = w_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + if (axi_b_req.w !== w) begin + $error("W mismatch!"); + print_w(w, axi_b_req.w); + end + end + // ar + if (axi_b_req.ar_valid & axi_b_res.ar_ready) begin + automatic ar_chan_t ar; + if (ar_queue.size() == 0) $error("AR queue is empty!"); + ar = ar_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + if (axi_b_req.ar !== ar) begin + $error("AR mismatch!"); + print_ar(ar, axi_b_req.ar); + end + end + end + + // responses arriving at axi A from B: dequeue elements and check + always_ff @(posedge clk_a_i) begin : proc_dequeue_and_check_a + // b + if (axi_a_res.b_valid & axi_a_req.b_ready) begin + automatic b_chan_t b; + if (b_queue.size() == 0) $error("B queue is empty!"); + b = b_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + if (axi_a_res.b !== b) begin + $error("B mismatch!"); + print_b(b, axi_a_res.b); + end + end + // r + if (axi_a_res.r_valid & axi_a_req.r_ready) begin + automatic r_chan_t r; + if (r_queue.size() == 0) $error("R queue is empty!"); + r = r_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + if (axi_a_res.r !== r) begin + $error("R mismatch!"); + print_r(r, axi_a_res.r); + end + end + end + +endmodule : axi_chan_compare diff --git a/src_files.yml b/src_files.yml index 5fc0c4ced..60367172d 100644 --- a/src_files.yml +++ b/src_files.yml @@ -61,6 +61,8 @@ axi: axi_sim: files: + - src/axi_chan_compare.sv + - src/axi_dumper.sv - src/axi_sim_mem.sv - src/axi_test.sv flags: From c7c87abea3a514d7e5879ab3db4c2119d93c920e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 29 Jun 2020 13:21:51 +0200 Subject: [PATCH 078/145] tb_axi_xbar: Add parameters, make more configurable for ci --- test/tb_axi_xbar.sv | 283 +++++++++++++++++++++++++------------------- 1 file changed, 159 insertions(+), 124 deletions(-) diff --git a/test/tb_axi_xbar.sv b/test/tb_axi_xbar.sv index d8d75900d..db7ef0235 100644 --- a/test/tb_axi_xbar.sv +++ b/test/tb_axi_xbar.sv @@ -9,6 +9,7 @@ // specific language governing permissions and limitations under the License. // // Authors: +// - Wolfgang Roenninger // - Florian Zaruba // - Andreas Kurth @@ -22,51 +23,68 @@ `include "axi/typedef.svh" `include "axi/assign.svh" +/// Testbench for the module `axi_xbar`. module tb_axi_xbar #( - parameter bit TbEnAtop = 1'b1, // enable atomic operations (ATOPs) - parameter bit TbEnExcl = 1'b0, // enable exclusive accesses - parameter bit TbUniqueIds = 1'b0, // restrict to only unique IDs - parameter int unsigned TbNumMst = 32'd6, // how many AXI masters there are - parameter int unsigned TbNumSlv = 32'd8 // how many AXI slaves there are + /// Number of AXI masters connected to the xbar. (Number of slave ports) + parameter int unsigned TbNumMasters = 32'd6, + /// Number of AXI slaves connected to the xbar. (Number of master ports) + parameter int unsigned TbNumSlaves = 32'd8, + /// Number of write transactions per master. + parameter int unsigned TbNumWrites = 32'd1000, + /// Number of read transactions per master. + parameter int unsigned TbNumReads = 32'd1000, + /// AXI4+ATOP ID wisth of the masters connected to the slave ports of the DUT. + /// The ID width of the salves is calulated depending on the xbar configuration. + parameter int unsigned TbAxiIdWidthMasters = 32'd4, + /// The used ID width of the DUT. + /// Has to be `TbAxiIdWidthMasters >= TbAxiIdUsed`. + parameter int unsigned TbAxiIdUsed = 32'd3, + /// Data width of the AXI channels. + parameter int unsigned TbAxiDataWidth = 32'd64, + /// Pipeline stages in the xbar itself. (Between Demux and mux) + parameter int unsigned TbPipeline = 32'd1, + /// Enable ATOP generation + parameter bit TbEnAtop = 1'b1, + /// Enable exclusive accesses + parameter bit TbEnExcl = 1'b0, + /// Restrict to only unique IDs + parameter bit TbUniqueIds = 1'b0 + ); - // Random master no Transactions - localparam int unsigned NoWrites = 80; // How many writes per master - localparam int unsigned NoReads = 80; // How many reads per master - // timing parameters + + // TB timing parameters localparam time CyclTime = 10ns; localparam time ApplTime = 2ns; localparam time TestTime = 8ns; - // axi configuration - localparam int unsigned AxiIdWidthMasters = 4; - localparam int unsigned AxiIdUsed = 3; // Has to be <= AxiIdWidthMasters - localparam int unsigned AxiIdWidthSlaves = AxiIdWidthMasters + $clog2(TbNumMst); - localparam int unsigned AxiAddrWidth = 32; // Axi Address Width - localparam int unsigned AxiDataWidth = 64; // Axi Data Width - localparam int unsigned AxiStrbWidth = AxiDataWidth / 8; - localparam int unsigned AxiUserWidth = 5; - // in the bench can change this variables which are set here freely + // AXI configuration which is automatically derived. + localparam int unsigned TbAxiIdWidthSlaves = TbAxiIdWidthMasters + $clog2(TbNumMasters); + localparam int unsigned TbAxiAddrWidth = 32'd32; + localparam int unsigned TbAxiStrbWidth = TbAxiDataWidth / 8; + localparam int unsigned TbAxiUserWidth = 5; + // In the bench can change this variables which are set here freely, localparam axi_pkg::xbar_cfg_t xbar_cfg = '{ - NoSlvPorts: TbNumMst, - NoMstPorts: TbNumSlv, + NoSlvPorts: TbNumMasters, + NoMstPorts: TbNumSlaves, MaxMstTrans: 10, MaxSlvTrans: 6, FallThrough: 1'b0, LatencyMode: axi_pkg::CUT_ALL_AX, - AxiIdWidthSlvPorts: AxiIdWidthMasters, - AxiIdUsedSlvPorts: AxiIdUsed, + PipelineStages: TbPipeline, + AxiIdWidthSlvPorts: TbAxiIdWidthMasters, + AxiIdUsedSlvPorts: TbAxiIdUsed, UniqueIds: TbUniqueIds, - AxiAddrWidth: AxiAddrWidth, - AxiDataWidth: AxiDataWidth, - NoAddrRules: 8 + AxiAddrWidth: TbAxiAddrWidth, + AxiDataWidth: TbAxiDataWidth, + NoAddrRules: TbNumSlaves }; - typedef logic [AxiIdWidthMasters-1:0] id_mst_t; - typedef logic [AxiIdWidthSlaves-1:0] id_slv_t; - typedef logic [AxiAddrWidth-1:0] addr_t; - typedef axi_pkg::xbar_rule_32_t rule_t; // Has to be the same width as axi addr - typedef logic [AxiDataWidth-1:0] data_t; - typedef logic [AxiStrbWidth-1:0] strb_t; - typedef logic [AxiUserWidth-1:0] user_t; + typedef logic [TbAxiIdWidthMasters-1:0] id_mst_t; + typedef logic [TbAxiIdWidthSlaves-1:0] id_slv_t; + typedef logic [TbAxiAddrWidth-1:0] addr_t; + typedef axi_pkg::xbar_rule_32_t rule_t; // Has to be the same width as axi addr + typedef logic [TbAxiDataWidth-1:0] data_t; + typedef logic [TbAxiStrbWidth-1:0] strb_t; + typedef logic [TbAxiUserWidth-1:0] user_t; `AXI_TYPEDEF_AW_CHAN_T(aw_chan_mst_t, addr_t, id_mst_t, user_t) `AXI_TYPEDEF_AW_CHAN_T(aw_chan_slv_t, addr_t, id_slv_t, user_t) @@ -84,42 +102,45 @@ module tb_axi_xbar #( `AXI_TYPEDEF_REQ_T(slv_req_t, aw_chan_slv_t, w_chan_t, ar_chan_slv_t) `AXI_TYPEDEF_RESP_T(slv_resp_t, b_chan_slv_t, r_chan_slv_t) - localparam rule_t [xbar_cfg.NoAddrRules-1:0] AddrMap = '{ - '{idx: 32'd7 % TbNumSlv, start_addr: 32'h0001_0000, end_addr: 32'h0001_1000}, - '{idx: 32'd6 % TbNumSlv, start_addr: 32'h0000_9000, end_addr: 32'h0001_0000}, - '{idx: 32'd5 % TbNumSlv, start_addr: 32'h0000_8000, end_addr: 32'h0000_9000}, - '{idx: 32'd4 % TbNumSlv, start_addr: 32'h0000_7000, end_addr: 32'h0000_8000}, - '{idx: 32'd3 % TbNumSlv, start_addr: 32'h0000_6300, end_addr: 32'h0000_7000}, - '{idx: 32'd2 % TbNumSlv, start_addr: 32'h0000_4000, end_addr: 32'h0000_6300}, - '{idx: 32'd1 % TbNumSlv, start_addr: 32'h0000_3000, end_addr: 32'h0000_4000}, - '{idx: 32'd0 % TbNumSlv, start_addr: 32'h0000_0000, end_addr: 32'h0000_3000} - }; + // Each slave has its own address range: + localparam rule_t [xbar_cfg.NoAddrRules-1:0] AddrMap = addr_map_gen(); + + function rule_t [xbar_cfg.NoAddrRules-1:0] addr_map_gen (); + for (int unsigned i = 0; i < xbar_cfg.NoAddrRules; i++) begin + addr_map_gen[i] = rule_t'{ + idx: unsigned'(i), + start_addr: i * 32'h0000_2000, + end_addr: (i+1) * 32'h0000_2000, + default: '0 + }; + end + endfunction typedef axi_test::axi_rand_master #( // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidthMasters ), - .UW ( AxiUserWidth ), + .AW ( TbAxiAddrWidth ), + .DW ( TbAxiDataWidth ), + .IW ( TbAxiIdWidthMasters ), + .UW ( TbAxiUserWidth ), // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ), + .TA ( ApplTime ), + .TT ( TestTime ), // Maximum number of read and write transactions in flight - .MAX_READ_TXNS ( 20 ), - .MAX_WRITE_TXNS ( 20 ), - .AXI_EXCLS ( TbEnExcl ), - .AXI_ATOPS ( TbEnAtop ), + .MAX_READ_TXNS ( 20 ), + .MAX_WRITE_TXNS ( 20 ), + .AXI_EXCLS ( TbEnExcl ), + .AXI_ATOPS ( TbEnAtop ), .UNIQUE_IDS ( TbUniqueIds ) ) axi_rand_master_t; typedef axi_test::axi_rand_slave #( // AXI interface parameters - .AW ( AxiAddrWidth ), - .DW ( AxiDataWidth ), - .IW ( AxiIdWidthSlaves ), - .UW ( AxiUserWidth ), + .AW ( TbAxiAddrWidth ), + .DW ( TbAxiDataWidth ), + .IW ( TbAxiIdWidthSlaves ), + .UW ( TbAxiUserWidth ), // Stimuli application and test time - .TA ( ApplTime ), - .TT ( TestTime ) + .TA ( ApplTime ), + .TT ( TestTime ) ) axi_rand_slave_t; // ------------- @@ -128,72 +149,72 @@ module tb_axi_xbar #( logic clk; // DUT signals logic rst_n; - logic [TbNumMst-1:0] end_of_sim; + logic [TbNumMasters-1:0] end_of_sim; // master structs - mst_req_t [TbNumMst-1:0] masters_req; - mst_resp_t [TbNumMst-1:0] masters_resp; + mst_req_t [TbNumMasters-1:0] masters_req; + mst_resp_t [TbNumMasters-1:0] masters_resp; // slave structs - slv_req_t [TbNumSlv-1:0] slaves_req; - slv_resp_t [TbNumSlv-1:0] slaves_resp; + slv_req_t [TbNumSlaves-1:0] slaves_req; + slv_resp_t [TbNumSlaves-1:0] slaves_resp; // ------------------------------- // AXI Interfaces // ------------------------------- AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master [TbNumMst-1:0] (); + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( TbAxiIdWidthMasters ), + .AXI_USER_WIDTH ( TbAxiUserWidth ) + ) master [TbNumMasters-1:0] (); AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master_dv [TbNumMst-1:0] (clk); + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( TbAxiIdWidthMasters ), + .AXI_USER_WIDTH ( TbAxiUserWidth ) + ) master_dv [TbNumMasters-1:0] (clk); AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthMasters ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) master_monitor_dv [TbNumMst-1:0] (clk); - for (genvar i = 0; i < TbNumMst; i++) begin : gen_conn_dv_masters + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( TbAxiIdWidthMasters ), + .AXI_USER_WIDTH ( TbAxiUserWidth ) + ) master_monitor_dv [TbNumMasters-1:0] (clk); + for (genvar i = 0; i < TbNumMasters; i++) begin : gen_conn_dv_masters `AXI_ASSIGN (master[i], master_dv[i]) `AXI_ASSIGN_TO_REQ(masters_req[i], master[i]) - `AXI_ASSIGN_TO_RESP(masters_resp[i], master[i]) + `AXI_ASSIGN_FROM_RESP(master[i], masters_resp[i]) end AXI_BUS #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave [TbNumSlv-1:0] (); + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( TbAxiIdWidthSlaves ), + .AXI_USER_WIDTH ( TbAxiUserWidth ) + ) slave [TbNumSlaves-1:0] (); AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave_dv [TbNumSlv-1:0](clk); + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( TbAxiIdWidthSlaves ), + .AXI_USER_WIDTH ( TbAxiUserWidth ) + ) slave_dv [TbNumSlaves-1:0](clk); AXI_BUS_DV #( - .AXI_ADDR_WIDTH ( AxiAddrWidth ), - .AXI_DATA_WIDTH ( AxiDataWidth ), - .AXI_ID_WIDTH ( AxiIdWidthSlaves ), - .AXI_USER_WIDTH ( AxiUserWidth ) - ) slave_monitor_dv [TbNumSlv-1:0](clk); - for (genvar i = 0; i < TbNumSlv; i++) begin : gen_conn_dv_slaves + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidth ), + .AXI_ID_WIDTH ( TbAxiIdWidthSlaves ), + .AXI_USER_WIDTH ( TbAxiUserWidth ) + ) slave_monitor_dv [TbNumSlaves-1:0](clk); + for (genvar i = 0; i < TbNumSlaves; i++) begin : gen_conn_dv_slaves `AXI_ASSIGN(slave_dv[i], slave[i]) - `AXI_ASSIGN_TO_REQ(slaves_req[i], slave[i]) + `AXI_ASSIGN_FROM_REQ(slave[i], slaves_req[i]) `AXI_ASSIGN_TO_RESP(slaves_resp[i], slave[i]) end // ------------------------------- // AXI Rand Masters and Slaves // ------------------------------- // Masters control simulation run time - axi_rand_master_t axi_rand_master [TbNumMst]; - for (genvar i = 0; i < TbNumMst; i++) begin : gen_rand_master + axi_rand_master_t axi_rand_master [TbNumMasters]; + for (genvar i = 0; i < TbNumMasters; i++) begin : gen_rand_master initial begin axi_rand_master[i] = new( master_dv[i] ); end_of_sim[i] <= 1'b0; @@ -202,13 +223,13 @@ module tb_axi_xbar #( axi_pkg::DEVICE_NONBUFFERABLE); axi_rand_master[i].reset(); @(posedge rst_n); - axi_rand_master[i].run(NoReads, NoWrites); + axi_rand_master.run(TbNumReads, TbNumWrites); end_of_sim[i] <= 1'b1; end end axi_rand_slave_t axi_rand_slave [TbNumSlv]; - for (genvar i = 0; i < TbNumSlv; i++) begin : gen_rand_slave + for (genvar i = 0; i < TbNumSlaves; i++) begin : gen_rand_slave initial begin axi_rand_slave[i] = new( slave_dv[i] ); axi_rand_slave[i].reset(); @@ -219,13 +240,13 @@ module tb_axi_xbar #( initial begin : proc_monitor static tb_axi_xbar_pkg::axi_xbar_monitor #( - .AxiAddrWidth ( AxiAddrWidth ), - .AxiDataWidth ( AxiDataWidth ), - .AxiIdWidthMasters ( AxiIdWidthMasters ), - .AxiIdWidthSlaves ( AxiIdWidthSlaves ), - .AxiUserWidth ( AxiUserWidth ), - .NoMasters ( TbNumMst ), - .NoSlaves ( TbNumSlv ), + .AxiAddrWidth ( TbAxiAddrWidth ), + .AxiDataWidth ( TbAxiDataWidth ), + .AxiIdWidthMasters ( TbAxiIdWidthMasters ), + .AxiIdWidthSlaves ( TbAxiIdWidthSlaves ), + .AxiUserWidth ( TbAxiUserWidth ), + .NoMasters ( TbNumMasters ), + .NoSlaves ( TbNumSlaves ), .NoAddrRules ( xbar_cfg.NoAddrRules ), .rule_t ( rule_t ), .AddrMap ( AddrMap ), @@ -258,23 +279,37 @@ module tb_axi_xbar #( //----------------------------------- // DUT //----------------------------------- - axi_xbar_intf #( - .AXI_USER_WIDTH ( AxiUserWidth ), - .Cfg ( xbar_cfg ), - .rule_t ( rule_t ) + axi_xbar #( + .Cfg ( xbar_cfg ), + .slv_aw_chan_t( aw_chan_mst_t ), + .mst_aw_chan_t( aw_chan_slv_t ), + .w_chan_t ( w_chan_t ), + .slv_b_chan_t ( b_chan_mst_t ), + .mst_b_chan_t ( b_chan_slv_t ), + .slv_ar_chan_t( ar_chan_mst_t ), + .mst_ar_chan_t( ar_chan_slv_t ), + .slv_r_chan_t ( r_chan_mst_t ), + .mst_r_chan_t ( r_chan_slv_t ), + .slv_req_t ( mst_req_t ), + .slv_resp_t ( mst_resp_t ), + .mst_req_t ( slv_req_t ), + .mst_resp_t ( slv_resp_t ), + .rule_t ( rule_t ) ) i_xbar_dut ( - .clk_i ( clk ), - .rst_ni ( rst_n ), - .test_i ( 1'b0 ), - .slv_ports ( master ), - .mst_ports ( slave ), - .addr_map_i ( AddrMap ), - .en_default_mst_port_i ( '0 ), - .default_mst_port_i ( '0 ) + .clk_i ( clk ), + .rst_ni ( rst_n ), + .test_i ( 1'b0 ), + .slv_ports_req_i ( masters_req ), + .slv_ports_resp_o ( masters_resp ), + .mst_ports_req_o ( slaves_req ), + .mst_ports_resp_i ( slaves_resp ), + .addr_map_i ( AddrMap ), + .en_default_mst_port_i ( '0 ), + .default_mst_port_i ( '0 ) ); // logger for master modules - for (genvar i = 0; i < TbNumMst; i++) begin : gen_master_logger + for (genvar i = 0; i < TbNumMasters; i++) begin : gen_master_logger axi_chan_logger #( .TestTime ( TestTime ), // Time after clock, where sampling happens .LoggerName( $sformatf("axi_logger_master_%0d", i)), @@ -310,7 +345,7 @@ module tb_axi_xbar #( ); end // logger for slave modules - for (genvar i = 0; i < TbNumSlv; i++) begin : gen_slave_logger + for (genvar i = 0; i < TbNumSlaves; i++) begin : gen_slave_logger axi_chan_logger #( .TestTime ( TestTime ), // Time after clock, where sampling happens .LoggerName( $sformatf("axi_logger_slave_%0d",i)), @@ -347,7 +382,7 @@ module tb_axi_xbar #( end - for (genvar i = 0; i < TbNumMst; i++) begin : gen_connect_master_monitor + for (genvar i = 0; i < TbNumMasters; i++) begin : gen_connect_master_monitor assign master_monitor_dv[i].aw_id = master[i].aw_id ; assign master_monitor_dv[i].aw_addr = master[i].aw_addr ; assign master_monitor_dv[i].aw_len = master[i].aw_len ; @@ -394,7 +429,7 @@ module tb_axi_xbar #( assign master_monitor_dv[i].r_valid = master[i].r_valid ; assign master_monitor_dv[i].r_ready = master[i].r_ready ; end - for (genvar i = 0; i < TbNumSlv; i++) begin : gen_connect_slave_monitor + for (genvar i = 0; i < TbNumSlaves; i++) begin : gen_connect_slave_monitor assign slave_monitor_dv[i].aw_id = slave[i].aw_id ; assign slave_monitor_dv[i].aw_addr = slave[i].aw_addr ; assign slave_monitor_dv[i].aw_len = slave[i].aw_len ; From 10d815c34b877277ddb5b46bfc45876e482c9d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 29 Jun 2020 13:28:53 +0200 Subject: [PATCH 079/145] axi_demux: Remove W select FIFO, add counter and register --- CHANGELOG.md | 5 +++++ src/axi_demux.sv | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae12b1c2c..97402a3be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -323,6 +323,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `axi_test::rand_axi_lite_slave` and `axi_test::rand_axi_lite_master`: Change type of address and data width parameters (`AW` and `DW`) from `int` to `int unsigned`. Same rationale as for `AXI_BUS` (et al.) above. +- `axi_demux`: Remove FIFO between AW and W channel, is now a register plus counter. + Prevents AWs to be emmitted downstream to a different master port as long as Ws + are still in flight to another. This prevents deadlock, if there is stalling + downstream. + ### Fixed - `axi_demux`: Break combinatorial simulation loop. diff --git a/src/axi_demux.sv b/src/axi_demux.sv index 184e9b23c..9e05406da 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -817,7 +817,6 @@ module axi_demux_intf #( parameter int unsigned MAX_TRANS = 32'd8, parameter int unsigned AXI_LOOK_BITS = 32'd3, parameter bit UNIQUE_IDS = 1'b0, - parameter bit FALL_THROUGH = 1'b0, parameter bit SPILL_AW = 1'b1, parameter bit SPILL_W = 1'b0, parameter bit SPILL_B = 1'b0, From 3076c142686acaed4ba5671d952f30f1691d8bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 29 Jun 2020 13:34:41 +0200 Subject: [PATCH 080/145] axi_xbar: Add pipeline stages in cross --- CHANGELOG.md | 3 ++- src/axi_pkg.sv | 1 + src/axi_xbar.sv | 19 +++++++++++++++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97402a3be..7f7791662 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -327,7 +327,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. Prevents AWs to be emmitted downstream to a different master port as long as Ws are still in flight to another. This prevents deadlock, if there is stalling downstream. - +- `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` + in the crossed connections in the xbar between the demuxes and muxes. ### Fixed - `axi_demux`: Break combinatorial simulation loop. diff --git a/src/axi_pkg.sv b/src/axi_pkg.sv index 92ede558c..0820dd708 100644 --- a/src/axi_pkg.sv +++ b/src/axi_pkg.sv @@ -399,6 +399,7 @@ package axi_pkg; int unsigned MaxSlvTrans; bit FallThrough; xbar_latency_e LatencyMode; + int unsigned PipelineStages; int unsigned AxiIdWidthSlvPorts; int unsigned AxiIdUsedSlvPorts; bit UniqueIds; diff --git a/src/axi_xbar.sv b/src/axi_xbar.sv index 0971ef6f5..7fd4f537d 100644 --- a/src/axi_xbar.sv +++ b/src/axi_xbar.sv @@ -204,8 +204,23 @@ import cf_math_pkg::idx_width; for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_xbar_slv_cross for (genvar j = 0; j < Cfg.NoMstPorts; j++) begin : gen_xbar_mst_cross if (Connectivity[i][j]) begin : gen_connection - assign mst_reqs[j][i] = slv_reqs[i][j]; - assign slv_resps[i][j] = mst_resps[j][i]; + axi_multicut #( + .NoCuts ( Cfg.PipelineStages ), + .aw_chan_t ( slv_aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( slv_b_chan_t ), + .ar_chan_t ( slv_ar_chan_t ), + .r_chan_t ( slv_r_chan_t ), + .req_t ( slv_req_t ), + .resp_t ( slv_resp_t ) + ) i_axi_multicut_xbar_pipeline ( + .clk_i, + .rst_ni, + .slv_req_i ( slv_reqs[i][j] ), + .slv_resp_o ( slv_resps[i][j] ), + .mst_req_o ( mst_reqs[j][i] ), + .mst_resp_i ( mst_resps[j][i] ) + ); end else begin : gen_no_connection assign mst_reqs[j][i] = '0; From 78f29996ace21682a4ad5145ddf67717c2e03fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 29 Jun 2020 13:35:42 +0200 Subject: [PATCH 081/145] scripts:run_vsim: Add parametized axi_xbar simulation call --- scripts/run_vsim.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh index 3737ba5d3..66a6b745f 100755 --- a/scripts/run_vsim.sh +++ b/scripts/run_vsim.sh @@ -202,6 +202,29 @@ exec_test() { done done ;; + axi_xbar) + for GEN_ATOP in 0 1; do + for NUM_MST in 1 2 4 6; do + for NUM_SLV in 2 7 9; do + for MST_ID_USE in 3 5; do + MST_ID=5 + for DATA_WIDTH in 64 256; do + for PIPE in 0 1; do + call_vsim tb_axi_xbar -t 1ns -voptargs="+acc" \ + -gTbNumMasters=$NUM_MST \ + -gTbNumSlaves=$NUM_SLV \ + -gTbAxiIdWidthMasters=$MST_ID \ + -gTbAxiIdUsed=$MST_ID_USE \ + -gTbAxiDataWidth=$DATA_WIDTH \ + -gTbPipeline=$PIPE \ + -gTbEnAtop=$GEN_ATOP + done + done + done + done + done + done + ;; *) call_vsim tb_$1 -t 1ns -coverage -voptargs="+acc +cover=bcesfx" ;; From 67df42ebfd5854382e13adbd01591c2fc1cd973d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 29 Jun 2020 13:48:20 +0200 Subject: [PATCH 082/145] axi_pkg: Add documentation for xbar_cfg_t --- CHANGELOG.md | 1 + src/axi_pkg.sv | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f7791662..7ce215fc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -329,6 +329,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. downstream. - `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` in the crossed connections in the xbar between the demuxes and muxes. +- `axi_pkg`: Add documentation to `xbar_cfg_t`. ### Fixed - `axi_demux`: Break combinatorial simulation loop. diff --git a/src/axi_pkg.sv b/src/axi_pkg.sv index 0820dd708..7ef3bbcd7 100644 --- a/src/axi_pkg.sv +++ b/src/axi_pkg.sv @@ -393,18 +393,44 @@ package axi_pkg; /// Configuration for `axi_xbar`. typedef struct packed { + /// Number of slave ports of the crossbar. + /// This many master modules are connected to it. int unsigned NoSlvPorts; + /// Number of master ports of the crossbar. + /// This many slave modules are connected to it. int unsigned NoMstPorts; + /// Maximum number of open transactions each master connected to the crossbar can have in + /// flight at the same time. int unsigned MaxMstTrans; + /// Maximum number of open transactions each slave connected to the crossbar can have in + /// flight at the same time. int unsigned MaxSlvTrans; + /// Determine if the internal FIFOs of the crossbar are instantiated in fallthrough mode. + /// 0: No fallthrough + /// 1: Fallthrough bit FallThrough; + /// The Latency mode of the xbar. This determines if the channels on the ports have + /// a spill register instantiated. + /// Example configurations are provided with the enum `xbar_latency_e`. xbar_latency_e LatencyMode; + /// This is the number of `axi_multicut` stages instantiated in the line cross of the channels. + /// Having multiple stages can potentially add a large number of FFs! int unsigned PipelineStages; + /// AXI ID width of the salve ports. The ID width of the master ports is determined + /// Automatically. See `axi_mux` for details. int unsigned AxiIdWidthSlvPorts; + /// The used ID portion to determine if a different salve is used for the same ID. + /// See `axi_demux` for details. int unsigned AxiIdUsedSlvPorts; + /// Are IDs unique? bit UniqueIds; + /// AXI4+ATOP address field width. int unsigned AxiAddrWidth; + /// AXI4+ATOP data field width. int unsigned AxiDataWidth; + /// The number of address rules defined for routing of the transactions. + /// Each master port can have multiple rules, should have however at least one. + /// If a transaction can not be routed the xbar will answer with an `axi_pkg::RESP_DECERR`. int unsigned NoAddrRules; } xbar_cfg_t; From 18d2a4fea5556487eec66aba5369254d0d46d0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Tue, 30 Jun 2020 14:24:23 +0200 Subject: [PATCH 083/145] axi_xbar: Tone down runtime of ci --- scripts/run_vsim.sh | 4 ++-- test/tb_axi_xbar.sv | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh index 66a6b745f..7a14b428a 100755 --- a/scripts/run_vsim.sh +++ b/scripts/run_vsim.sh @@ -204,8 +204,8 @@ exec_test() { ;; axi_xbar) for GEN_ATOP in 0 1; do - for NUM_MST in 1 2 4 6; do - for NUM_SLV in 2 7 9; do + for NUM_MST in 1 6; do + for NUM_SLV in 2 9; do for MST_ID_USE in 3 5; do MST_ID=5 for DATA_WIDTH in 64 256; do diff --git a/test/tb_axi_xbar.sv b/test/tb_axi_xbar.sv index db7ef0235..8261e5216 100644 --- a/test/tb_axi_xbar.sv +++ b/test/tb_axi_xbar.sv @@ -30,12 +30,12 @@ module tb_axi_xbar #( /// Number of AXI slaves connected to the xbar. (Number of master ports) parameter int unsigned TbNumSlaves = 32'd8, /// Number of write transactions per master. - parameter int unsigned TbNumWrites = 32'd1000, + parameter int unsigned TbNumWrites = 32'd100, /// Number of read transactions per master. - parameter int unsigned TbNumReads = 32'd1000, + parameter int unsigned TbNumReads = 32'd100, /// AXI4+ATOP ID wisth of the masters connected to the slave ports of the DUT. /// The ID width of the salves is calulated depending on the xbar configuration. - parameter int unsigned TbAxiIdWidthMasters = 32'd4, + parameter int unsigned TbAxiIdWidthMasters = 32'd5, /// The used ID width of the DUT. /// Has to be `TbAxiIdWidthMasters >= TbAxiIdUsed`. parameter int unsigned TbAxiIdUsed = 32'd3, From a5dce2119c9cfd82f160c8e8403993ae637b88c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Thu, 2 Jul 2020 09:52:45 +0200 Subject: [PATCH 084/145] CHANGELOG: Format xbar pipeline additions --- CHANGELOG.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ce215fc9..2d40aa47b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -323,12 +323,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `axi_test::rand_axi_lite_slave` and `axi_test::rand_axi_lite_master`: Change type of address and data width parameters (`AW` and `DW`) from `int` to `int unsigned`. Same rationale as for `AXI_BUS` (et al.) above. -- `axi_demux`: Remove FIFO between AW and W channel, is now a register plus counter. - Prevents AWs to be emmitted downstream to a different master port as long as Ws - are still in flight to another. This prevents deadlock, if there is stalling - downstream. -- `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` - in the crossed connections in the xbar between the demuxes and muxes. +- `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents + AWs from being issued to one master port while Ws from another burst are ongoing to another + master port. This is required to prevents deadlocks due to circular waits downstream. +- `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` + in the crossed connections in the xbar between the demuxes and muxes. - `axi_pkg`: Add documentation to `xbar_cfg_t`. ### Fixed From f689d32d0e3c687d758e1432655285c7031027ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Thu, 2 Jul 2020 10:12:18 +0200 Subject: [PATCH 085/145] axi_demux: Remove docstring flag from comments --- src/axi_demux.sv | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/axi_demux.sv b/src/axi_demux.sv index 9e05406da..fc061ff09 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -289,10 +289,10 @@ module axi_demux #( if (!aw_id_cnt_full && (w_open != {IdCounterWidth{1'b1}}) && (!(ar_id_cnt_full && slv_aw_chan.atop[axi_pkg::ATOP_R_RESP]) || !AtopSupport)) begin - /// There is a valid AW vector make the id lookup and go further, if it passes. - /// Also stall if previous transmitted AWs still have active W's in flight. - /// This prevents deadlocking of the W channel. The counters are there for the - /// Handling of the B responses. + // There is a valid AW vector make the id lookup and go further, if it passes. + // Also stall if previous transmitted AWs still have active W's in flight. + // This prevents deadlocking of the W channel. The counters are there for the + // Handling of the B responses. if (slv_aw_valid && ((w_open == '0) || (w_select == slv_aw_select)) && (!aw_select_occupied || (slv_aw_select == lookup_aw_select))) begin From 696acdb61ab68eb5ac4d2a404fa4dc7ab03a67f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Fri, 4 Dec 2020 15:37:40 +0100 Subject: [PATCH 086/145] axi_xbar: Add port/param documentation --- src/axi_xbar.sv | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/axi_xbar.sv b/src/axi_xbar.sv index 7fd4f537d..bffa890fe 100644 --- a/src/axi_xbar.sv +++ b/src/axi_xbar.sv @@ -13,45 +13,88 @@ // - Andreas Kurth // - Florian Zaruba -// axi_xbar: Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. -// See `doc/axi_xbar.md` for the documentation, including the definition of parameters and ports. +/// axi_xbar: Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. +/// See `doc/axi_xbar.md` for the documentation, including the definition of parameters and ports. module axi_xbar import cf_math_pkg::idx_width; #( + /// Configuration struct for the crossbar see `axi_pkg` for fields and definitions. parameter axi_pkg::xbar_cfg_t Cfg = '0, + /// Enable atomic operations support. parameter bit ATOPs = 1'b1, + /// Connectivity matrix parameter bit [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts-1:0] Connectivity = '1, + /// AXI4+ATOP AW channel struct type for the slave ports. parameter type slv_aw_chan_t = logic, + /// AXI4+ATOP AW channel struct type for the master ports. parameter type mst_aw_chan_t = logic, + /// AXI4+ATOP W channel struct type for all ports. parameter type w_chan_t = logic, + /// AXI4+ATOP B channel struct type for the slave ports. parameter type slv_b_chan_t = logic, + /// AXI4+ATOP B channel struct type for the master ports. parameter type mst_b_chan_t = logic, + /// AXI4+ATOP AR channel struct type for the slave ports. parameter type slv_ar_chan_t = logic, + /// AXI4+ATOP AR channel struct type for the master ports. parameter type mst_ar_chan_t = logic, + /// AXI4+ATOP R channel struct type for the slave ports. parameter type slv_r_chan_t = logic, + /// AXI4+ATOP R channel struct type for the master ports. parameter type mst_r_chan_t = logic, + /// AXI4+ATOP request struct type for the slave ports. parameter type slv_req_t = logic, + /// AXI4+ATOP response struct type for the slave ports. parameter type slv_resp_t = logic, + /// AXI4+ATOP request struct type for the master ports. parameter type mst_req_t = logic, + /// AXI4+ATOP response struct type for the master ports parameter type mst_resp_t = logic, + /// Address rule type for the address decoders from `common_cells:addr_decode`. + /// Example types are provided in `axi_pkg`. + /// Required struct fields: + /// ``` + /// typedef struct packed { + /// int unsigned idx; + /// axi_addr_t start_addr; + /// axi_addr_t end_addr; + /// } rule_t; + /// ``` parameter type rule_t = axi_pkg::xbar_rule_64_t `ifdef VCS , localparam int unsigned MstPortsIdxWidth = (Cfg.NoMstPorts == 32'd1) ? 32'd1 : unsigned'($clog2(Cfg.NoMstPorts)) `endif ) ( + /// Clock, positive edge triggered. input logic clk_i, + /// Asynchronous reset, active low. input logic rst_ni, + /// Testmode enable, active high. input logic test_i, + /// AXI4+ATOP requests to the slave ports. input slv_req_t [Cfg.NoSlvPorts-1:0] slv_ports_req_i, + /// AXI4+ATOP responses of the slave ports. output slv_resp_t [Cfg.NoSlvPorts-1:0] slv_ports_resp_o, + /// AXI4+ATOP requests of the master ports. output mst_req_t [Cfg.NoMstPorts-1:0] mst_ports_req_o, + /// AXI4+ATOP responses to the master ports. input mst_resp_t [Cfg.NoMstPorts-1:0] mst_ports_resp_i, + /// Address map array input for the crossbar. This map is global for the whole module. + /// It is used for routing the transactions to the respective master ports. + /// Each master port can have multiple different rules. input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, + /// Enable default master port. input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, `ifdef VCS + /// Enables a default master port for each slave port. When this is enabled unmapped + /// transactions get issued at the master port given by `default_mst_port_i`. + /// When not used, tie to `'0`. input logic [Cfg.NoSlvPorts-1:0][MstPortsIdxWidth-1:0] default_mst_port_i `else + /// Enables a default master port for each slave port. When this is enabled unmapped + /// transactions get issued at the master port given by `default_mst_port_i`. + /// When not used, tie to `'0`. input logic [Cfg.NoSlvPorts-1:0][idx_width(Cfg.NoMstPorts)-1:0] default_mst_port_i `endif ); From b556516d7e13dd9240278eaeb766da4a9e381cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 29 Jun 2020 13:21:51 +0200 Subject: [PATCH 087/145] tb_axi_xbar: Add parameters, make more configurable for ci --- test/tb_axi_xbar.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tb_axi_xbar.sv b/test/tb_axi_xbar.sv index 8261e5216..6055864f9 100644 --- a/test/tb_axi_xbar.sv +++ b/test/tb_axi_xbar.sv @@ -30,9 +30,9 @@ module tb_axi_xbar #( /// Number of AXI slaves connected to the xbar. (Number of master ports) parameter int unsigned TbNumSlaves = 32'd8, /// Number of write transactions per master. - parameter int unsigned TbNumWrites = 32'd100, + parameter int unsigned TbNumWrites = 32'd200, /// Number of read transactions per master. - parameter int unsigned TbNumReads = 32'd100, + parameter int unsigned TbNumReads = 32'd200, /// AXI4+ATOP ID wisth of the masters connected to the slave ports of the DUT. /// The ID width of the salves is calulated depending on the xbar configuration. parameter int unsigned TbAxiIdWidthMasters = 32'd5, From bd41988c13d32faad00dd33ac1315717805cdb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 29 Jun 2020 13:34:41 +0200 Subject: [PATCH 088/145] axi_xbar: Add pipeline stages in cross --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d40aa47b..9b2ce0f71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -329,6 +329,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` in the crossed connections in the xbar between the demuxes and muxes. - `axi_pkg`: Add documentation to `xbar_cfg_t`. +- `axi_demux`: Remove FIFO between AW and W channel, is now a register plus counter. + Prevents AWs to be emmitted downstream to a different master port as long as Ws + are still in flight to another. This prevents deadlock, if there is stalling + downstream. +- `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` + in the crossed connections in the xbar between the demuxes and muxes. ### Fixed - `axi_demux`: Break combinatorial simulation loop. From ba318e9dc074fa00a937c47ed6a49a1ad521d997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Mon, 29 Jun 2020 13:48:20 +0200 Subject: [PATCH 089/145] axi_pkg: Add documentation for xbar_cfg_t --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b2ce0f71..37b087e6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -335,6 +335,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. downstream. - `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` in the crossed connections in the xbar between the demuxes and muxes. +- `axi_pkg`: Add documentation to `xbar_cfg_t`. ### Fixed - `axi_demux`: Break combinatorial simulation loop. From da5d955910e10af8809621274d9592fb3934aa77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Thu, 2 Jul 2020 09:52:45 +0200 Subject: [PATCH 090/145] CHANGELOG: Format xbar pipeline additions --- CHANGELOG.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37b087e6e..2d40aa47b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -329,13 +329,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` in the crossed connections in the xbar between the demuxes and muxes. - `axi_pkg`: Add documentation to `xbar_cfg_t`. -- `axi_demux`: Remove FIFO between AW and W channel, is now a register plus counter. - Prevents AWs to be emmitted downstream to a different master port as long as Ws - are still in flight to another. This prevents deadlock, if there is stalling - downstream. -- `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` - in the crossed connections in the xbar between the demuxes and muxes. -- `axi_pkg`: Add documentation to `xbar_cfg_t`. ### Fixed - `axi_demux`: Break combinatorial simulation loop. From 522c2adc89e9c022495858208c93048399cf9485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Thu, 2 Jul 2020 13:08:14 +0200 Subject: [PATCH 091/145] axi_xbar: Add suggestions from PR xbar_pipeline --- src/axi_xbar.sv | 3 ++- test/tb_axi_xbar.sv | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/axi_xbar.sv b/src/axi_xbar.sv index bffa890fe..95b27ff2c 100644 --- a/src/axi_xbar.sv +++ b/src/axi_xbar.sv @@ -99,7 +99,8 @@ import cf_math_pkg::idx_width; `endif ); - typedef logic [Cfg.AxiAddrWidth-1:0] addr_t; + // Address tpye for inidvidual address signals + typedef logic [Cfg.AxiAddrWidth-1:0] addr_t; // to account for the decoding error slave `ifdef VCS localparam int unsigned MstPortsIdxWidthOne = diff --git a/test/tb_axi_xbar.sv b/test/tb_axi_xbar.sv index 6055864f9..d83db6e73 100644 --- a/test/tb_axi_xbar.sv +++ b/test/tb_axi_xbar.sv @@ -33,15 +33,15 @@ module tb_axi_xbar #( parameter int unsigned TbNumWrites = 32'd200, /// Number of read transactions per master. parameter int unsigned TbNumReads = 32'd200, - /// AXI4+ATOP ID wisth of the masters connected to the slave ports of the DUT. - /// The ID width of the salves is calulated depending on the xbar configuration. + /// AXI4+ATOP ID width of the masters connected to the slave ports of the DUT. + /// The ID width of the slaves is calculated depending on the xbar configuration. parameter int unsigned TbAxiIdWidthMasters = 32'd5, /// The used ID width of the DUT. /// Has to be `TbAxiIdWidthMasters >= TbAxiIdUsed`. parameter int unsigned TbAxiIdUsed = 32'd3, /// Data width of the AXI channels. parameter int unsigned TbAxiDataWidth = 32'd64, - /// Pipeline stages in the xbar itself. (Between Demux and mux) + /// Pipeline stages in the xbar itself (between demux and mux). parameter int unsigned TbPipeline = 32'd1, /// Enable ATOP generation parameter bit TbEnAtop = 1'b1, From 3379f5af433aa062038e981e63a3fc72d30dd9ed Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Fri, 30 Sep 2022 13:37:53 +0200 Subject: [PATCH 092/145] axi_xbar: Various fixes --- src/axi_xbar.sv | 16 ++++++++-------- test/tb_axi_xbar.sv | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/axi_xbar.sv b/src/axi_xbar.sv index 95b27ff2c..52769a66c 100644 --- a/src/axi_xbar.sv +++ b/src/axi_xbar.sv @@ -249,14 +249,14 @@ import cf_math_pkg::idx_width; for (genvar j = 0; j < Cfg.NoMstPorts; j++) begin : gen_xbar_mst_cross if (Connectivity[i][j]) begin : gen_connection axi_multicut #( - .NoCuts ( Cfg.PipelineStages ), - .aw_chan_t ( slv_aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( slv_b_chan_t ), - .ar_chan_t ( slv_ar_chan_t ), - .r_chan_t ( slv_r_chan_t ), - .req_t ( slv_req_t ), - .resp_t ( slv_resp_t ) + .NoCuts ( Cfg.PipelineStages ), + .aw_chan_t ( slv_aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( slv_b_chan_t ), + .ar_chan_t ( slv_ar_chan_t ), + .r_chan_t ( slv_r_chan_t ), + .axi_req_t ( slv_req_t ), + .axi_resp_t ( slv_resp_t ) ) i_axi_multicut_xbar_pipeline ( .clk_i, .rst_ni, diff --git a/test/tb_axi_xbar.sv b/test/tb_axi_xbar.sv index d83db6e73..b182e9185 100644 --- a/test/tb_axi_xbar.sv +++ b/test/tb_axi_xbar.sv @@ -223,12 +223,12 @@ module tb_axi_xbar #( axi_pkg::DEVICE_NONBUFFERABLE); axi_rand_master[i].reset(); @(posedge rst_n); - axi_rand_master.run(TbNumReads, TbNumWrites); + axi_rand_master[i].run(TbNumReads, TbNumWrites); end_of_sim[i] <= 1'b1; end end - axi_rand_slave_t axi_rand_slave [TbNumSlv]; + axi_rand_slave_t axi_rand_slave [TbNumSlaves]; for (genvar i = 0; i < TbNumSlaves; i++) begin : gen_rand_slave initial begin axi_rand_slave[i] = new( slave_dv[i] ); From e4b5fb56e3ad461104e10ac2a6ad84d935e21bef Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Fri, 30 Sep 2022 13:55:44 +0200 Subject: [PATCH 093/145] changelog: Update changelog with xbar changes --- CHANGELOG.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d40aa47b..6301e4c8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add `axi_channel_compare.sv`: Non-synthesizable module comparing two AXI channels of the same type ### Changed -- `axi_demux`: Replace write FIFO (`w_fifo`) with a write credit counter. +- `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents + AWs from being issued to one master port while Ws from another burst are ongoing to another + master port. This is required to prevents deadlocks due to circular waits downstream. +- `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` + in the crossed connections in the xbar between the demuxes and muxes. +- `axi_pkg`: Add documentation to `xbar_cfg_t`. ### Fixed @@ -323,12 +328,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `axi_test::rand_axi_lite_slave` and `axi_test::rand_axi_lite_master`: Change type of address and data width parameters (`AW` and `DW`) from `int` to `int unsigned`. Same rationale as for `AXI_BUS` (et al.) above. -- `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents - AWs from being issued to one master port while Ws from another burst are ongoing to another - master port. This is required to prevents deadlocks due to circular waits downstream. -- `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` - in the crossed connections in the xbar between the demuxes and muxes. -- `axi_pkg`: Add documentation to `xbar_cfg_t`. ### Fixed - `axi_demux`: Break combinatorial simulation loop. From 07cd725c7c047c0bd0c78f3f668fd4d588944832 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 6 Oct 2022 14:14:36 +0200 Subject: [PATCH 094/145] tb_xbar: Use the interface version of the xbar again --- test/tb_axi_xbar.sv | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/test/tb_axi_xbar.sv b/test/tb_axi_xbar.sv index b182e9185..6056be919 100644 --- a/test/tb_axi_xbar.sv +++ b/test/tb_axi_xbar.sv @@ -183,7 +183,7 @@ module tb_axi_xbar #( for (genvar i = 0; i < TbNumMasters; i++) begin : gen_conn_dv_masters `AXI_ASSIGN (master[i], master_dv[i]) `AXI_ASSIGN_TO_REQ(masters_req[i], master[i]) - `AXI_ASSIGN_FROM_RESP(master[i], masters_resp[i]) + `AXI_ASSIGN_TO_RESP(masters_resp[i], master[i]) end AXI_BUS #( @@ -206,7 +206,7 @@ module tb_axi_xbar #( ) slave_monitor_dv [TbNumSlaves-1:0](clk); for (genvar i = 0; i < TbNumSlaves; i++) begin : gen_conn_dv_slaves `AXI_ASSIGN(slave_dv[i], slave[i]) - `AXI_ASSIGN_FROM_REQ(slave[i], slaves_req[i]) + `AXI_ASSIGN_TO_REQ(slaves_req[i], slave[i]) `AXI_ASSIGN_TO_RESP(slaves_resp[i], slave[i]) end // ------------------------------- @@ -279,33 +279,19 @@ module tb_axi_xbar #( //----------------------------------- // DUT //----------------------------------- - axi_xbar #( - .Cfg ( xbar_cfg ), - .slv_aw_chan_t( aw_chan_mst_t ), - .mst_aw_chan_t( aw_chan_slv_t ), - .w_chan_t ( w_chan_t ), - .slv_b_chan_t ( b_chan_mst_t ), - .mst_b_chan_t ( b_chan_slv_t ), - .slv_ar_chan_t( ar_chan_mst_t ), - .mst_ar_chan_t( ar_chan_slv_t ), - .slv_r_chan_t ( r_chan_mst_t ), - .mst_r_chan_t ( r_chan_slv_t ), - .slv_req_t ( mst_req_t ), - .slv_resp_t ( mst_resp_t ), - .mst_req_t ( slv_req_t ), - .mst_resp_t ( slv_resp_t ), - .rule_t ( rule_t ) + axi_xbar_intf #( + .AXI_USER_WIDTH ( TbAxiUserWidth ), + .Cfg ( xbar_cfg ), + .rule_t ( rule_t ) ) i_xbar_dut ( - .clk_i ( clk ), - .rst_ni ( rst_n ), - .test_i ( 1'b0 ), - .slv_ports_req_i ( masters_req ), - .slv_ports_resp_o ( masters_resp ), - .mst_ports_req_o ( slaves_req ), - .mst_ports_resp_i ( slaves_resp ), - .addr_map_i ( AddrMap ), - .en_default_mst_port_i ( '0 ), - .default_mst_port_i ( '0 ) + .clk_i ( clk ), + .rst_ni ( rst_n ), + .test_i ( 1'b0 ), + .slv_ports ( master ), + .mst_ports ( slave ), + .addr_map_i ( AddrMap ), + .en_default_mst_port_i ( '0 ), + .default_mst_port_i ( '0 ) ); // logger for master modules From 66e5200ca8e22576e9c15a3b9deab0197c0dd95e Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 6 Oct 2022 14:15:07 +0200 Subject: [PATCH 095/145] sim: Correctly set the parameter overwrite --- scripts/run_vsim.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh index 7a14b428a..ad1312415 100755 --- a/scripts/run_vsim.sh +++ b/scripts/run_vsim.sh @@ -172,7 +172,7 @@ exec_test() { for Atop in 0 1; do for Exclusive in 0 1; do for UniqueIds in 0 1; do - call_vsim tb_axi_xbar -gTbNumMst=$NumMst -gTbNumSlv=$NumSlv \ + call_vsim tb_axi_xbar -gTbNumMasters=$NumMst -gTbNumSlaves=$NumSlv \ -gTbEnAtop=$Atop -gTbEnExcl=$Exclusive \ -gTbUniqueIds=$UniqueIds done From 96cdede2f98f1517a40d60509cedee85183536f4 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Tue, 4 Oct 2022 18:55:26 +0200 Subject: [PATCH 096/145] 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 097/145] 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 From 6bccfd6efd976027b0f7cbc1fd2082de9a5fff9d Mon Sep 17 00:00:00 2001 From: Nicole Narr Date: Wed, 2 Nov 2022 02:53:04 +0100 Subject: [PATCH 098/145] axi_from_mem: Add `axi_lite_from_mem` and the `axi_from_mem` wrapper --- Bender.yml | 2 + CHANGELOG.md | 1 + README.md | 2 + axi.core | 2 + src/axi_from_mem.sv | 124 ++++++++++++++++++++ src/axi_lite_from_mem.sv | 246 +++++++++++++++++++++++++++++++++++++++ src_files.yml | 2 + 7 files changed, 379 insertions(+) create mode 100644 src/axi_from_mem.sv create mode 100644 src/axi_lite_from_mem.sv diff --git a/Bender.yml b/Bender.yml index e6e094fa2..68d78ba94 100644 --- a/Bender.yml +++ b/Bender.yml @@ -49,6 +49,7 @@ sources: - src/axi_isolate.sv - src/axi_join.sv - src/axi_lite_demux.sv + - src/axi_lite_from_mem.sv - src/axi_lite_join.sv - src/axi_lite_lfsr.sv - src/axi_lite_mailbox.sv @@ -66,6 +67,7 @@ sources: - src/axi_cdc.sv - src/axi_err_slv.sv - src/axi_dw_converter.sv + - src/axi_from_mem.sv - src/axi_id_serialize.sv - src/axi_lfsr.sv - src/axi_multicut.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a4677001..526e6f224 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - 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. +- Add `axi_lite_from_mem` and `axi_from_mem` acting like SRAMs making AXI4 requests downstream. ### Changed - `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents diff --git a/README.md b/README.md index b6729e4fc..27c62820a 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_dw_upsizer`](src/axi_dw_upsizer.sv) | A data width converter between a narrow AXI master and a wider AXI slave. | | | [`axi_err_slv`](src/axi_err_slv.sv) | Always responds with an AXI decode/slave error for transactions which are sent to it. | | | [`axi_fifo`](src/axi_fifo.sv) | A Fifo for each AXI4 channel to buffer requests. | | +| [`axi_from_mem`](src/axi_from_mem.sv) | This module acts like an SRAM and makes AXI4 requests downstream. | | | [`axi_id_prepend`](src/axi_id_prepend.sv) | This module prepends/strips the MSB from the AXI IDs. | | | [`axi_id_remap`](src/axi_id_remap.sv) | Remap AXI IDs from wide IDs at the slave port to narrower IDs at the master port. | [Doc][doc.axi_id_remap] | | [`axi_id_serialize`](src/axi_id_serialize.sv) | Reduce AXI IDs by serializing transactions when necessary. | [Doc][doc.axi_id_serialize] | @@ -41,6 +42,7 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_join`](src/axi_join.sv) | A connector that joins two AXI interfaces. | | | [`axi_lfsr`](src/axi_lfsr.sv) | AXI4-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | | [`axi_lite_demux`](src/axi_lite_demux.sv) | Demultiplexes an AXI4-Lite bus from one slave port to multiple master ports. | [Doc](doc/axi_lite_demux.md) | +| [`axi_lite_from_mem`](src/axi_lite_from_mem.sv) | This module acts like an SRAM and makes AXI4-Lite requests downstream. | | | [`axi_lite_join`](src/axi_lite_join.sv) | A connector that joins two AXI-Lite interfaces. | | | [`axi_lite_lfsr`](src/axi_lite_lfsr.sv) | AXI4-Lite-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | | [`axi_lite_mailbox`](src/axi_lite_mailbox.sv) | A AXI4-Lite Mailbox with two slave ports and usage triggered irq. | [Doc](doc/axi_lite_mailbox.md) | diff --git a/axi.core b/axi.core index de34be2ea..5f17230c2 100644 --- a/axi.core +++ b/axi.core @@ -31,6 +31,7 @@ filesets: - src/axi_isolate.sv - src/axi_join.sv - src/axi_lite_demux.sv + - src/axi_lite_from_mem.sv - src/axi_lite_join.sv - src/axi_lite_lfsr.sv - src/axi_lite_mailbox.sv @@ -48,6 +49,7 @@ filesets: - src/axi_cdc.sv - src/axi_err_slv.sv - src/axi_dw_converter.sv + - src/axi_from_mem.sv - src/axi_id_serialize.sv - src/axi_lfsr.sv - src/axi_multicut.sv diff --git a/src/axi_from_mem.sv b/src/axi_from_mem.sv new file mode 100644 index 000000000..23ed2d3af --- /dev/null +++ b/src/axi_from_mem.sv @@ -0,0 +1,124 @@ +// Copyright 2022 ETH Zurich and 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: +// - Christopher Reinwardt +// - Nicole Narr + +/// Protocol adapter which translates memory requests to the AXI4-Lite protocol. +/// +/// This module acts like an SRAM and makes AXI4-Lite requests downstream. +/// +/// Supports multiple outstanding requests and will have responses for reads **and** writes. +/// Response latency is not fixed and for sure **not 1** and depends on the AXI4-Lite memory system. +/// The `mem_rsp_valid_o` can have multiple cycles of latency from the corresponding `mem_gnt_o`. +/// (Was called `mem_to_axi_lite` - originating from https://github.com/pulp-platform/snitch) +module axi_lite_from_mem #( + /// Memory request address width. + parameter int unsigned MemAddrWidth = 32'd0, + /// AXI4-Lite address width. + parameter int unsigned AxiAddrWidth = 32'd0, + /// Data width in bit of the memory request data **and** the Axi4-Lite data channels. + parameter int unsigned DataWidth = 32'd0, + /// How many requests can be in flight at the same time. (Depth of the response mux FIFO). + parameter int unsigned MaxRequests = 32'd0, + /// Protection signal the module should emit on the AXI4-Lite transactions. + parameter axi_pkg::prot_t AxiProt = 3'b000, + /// AXI4-Lite request struct definition. + parameter type axi_req_t = logic, + /// AXI4-Lite response struct definition. + parameter type axi_rsp_t = logic, + /// Dependent parameter do **not** overwrite! + /// + /// Memory address type, derived from `MemAddrWidth`. + parameter type mem_addr_t = logic[MemAddrWidth-1:0], + /// Dependent parameter do **not** overwrite! + /// + /// AXI4-Lite address type, derived from `AxiAddrWidth`. + parameter type axi_addr_t = logic[AxiAddrWidth-1:0], + /// Dependent parameter do **not** overwrite! + /// + /// Data type for read and write data, derived from `DataWidth`. + /// This is the same for the memory request side **and** the AXI4-Lite `W` and `R` channels. + parameter type data_t = logic[DataWidth-1:0], + /// Dependent parameter do **not** overwrite! + /// + /// Byte enable / AXI4-Lite strobe type, derived from `DataWidth`. + parameter type strb_t = logic[DataWidth/8-1:0] +) ( + /// Clock input, positive edge triggered. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Memory slave port, request is active. + input logic mem_req_i, + /// Memory slave port, request address. + /// + /// Byte address, will be extended or truncated to match `AxiAddrWidth`. + input mem_addr_t mem_addr_i, + /// Memory slave port, request is a write. + /// + /// `0`: Read request. + /// `1`: Write request. + input logic mem_we_i, + /// Memory salve port, write data for request. + input data_t mem_wdata_i, + /// Memory slave port, write byte enable for request. + /// + /// Active high. + input strb_t mem_be_i, + /// Memory request is granted. + output logic mem_gnt_o, + /// Memory slave port, response is valid. For each request, regardless if read or write, + /// this will be active once for one cycle. + output logic mem_rsp_valid_o, + /// Memory slave port, response read data. This is forwarded directly from the AXI4-Lite + /// `R` channel. Only valid for responses generated by a read request. + output data_t mem_rsp_rdata_o, + /// Memory request encountered an error. This is forwarded from the AXI4-Lite error response. + output logic mem_rsp_error_o, + /// AXI4-Lite master port, request output. + output axi_req_t axi_req_o, + /// AXI4-Lite master port, response input. + input axi_rsp_t axi_rsp_i +); + `include "common_cells/registers.svh" + + // Response FIFO control signals. + logic fifo_full, fifo_empty; + // Bookkeeping for sent write beats. + logic aw_sent_q, aw_sent_d; + logic w_sent_q, w_sent_d; + + // Control for translating request to the AXI4-Lite `AW`, `W` and `AR` channels. + always_comb begin + // Default assignments. + axi_req_o.aw = '0; + axi_req_o.aw.addr = axi_addr_t'(mem_addr_i); + axi_req_o.aw.prot = AxiProt; + axi_req_o.aw_valid = 1'b0; + axi_req_o.w = '0; + axi_req_o.w.data = mem_wdata_i; + axi_req_o.w.strb = mem_be_i; + axi_req_o.w_valid = 1'b0; + axi_req_o.ar = '0; + axi_req_o.ar.addr = axi_addr_t'(mem_addr_i); + axi_req_o.ar.prot = AxiProt; + axi_req_o.ar_valid = 1'b0; + // This is also the push signal for the response FIFO. + mem_gnt_o = 1'b0; + // Bookkeeping about sent write channels. + aw_sent_d = aw_sent_q; + w_sent_d = w_sent_q; + + // Control for Request to AXI4-Lite translation. + if (mem_req_i && !fifo_full) begin + if (!mem_we_i) begin + // It is a read request. + axi_req_o.ar_valid = 1'b1; + mem_gnt_o = axi_rsp_i.ar_ready; + end else begin + // Is is a write request, decouple `AW` and `W` channels. + unique case ({aw_sent_q, w_sent_q}) + 2'b00 : begin + // None of the AXI4-Lite writes have been sent jet. + axi_req_o.aw_valid = 1'b1; + axi_req_o.w_valid = 1'b1; + unique case ({axi_rsp_i.aw_ready, axi_rsp_i.w_ready}) + 2'b01 : begin // W is sent, still needs AW. + w_sent_d = 1'b1; + end + 2'b10 : begin // AW is sent, still needs W. + aw_sent_d = 1'b1; + end + 2'b11 : begin // Both are transmitted, grant the write request. + mem_gnt_o = 1'b1; + end + default : /* do nothing */; + endcase + end + 2'b10 : begin + // W has to be sent. + axi_req_o.w_valid = 1'b1; + if (axi_rsp_i.w_ready) begin + aw_sent_d = 1'b0; + mem_gnt_o = 1'b1; + end + end + 2'b01 : begin + // AW has to be sent. + axi_req_o.aw_valid = 1'b1; + if (axi_rsp_i.aw_ready) begin + w_sent_d = 1'b0; + mem_gnt_o = 1'b1; + end + end + default : begin + // Failsafe go to IDLE. + aw_sent_d = 1'b0; + w_sent_d = 1'b0; + end + endcase + end + end + end + + `FFARN(aw_sent_q, aw_sent_d, 1'b0, clk_i, rst_ni) + `FFARN(w_sent_q, w_sent_d, 1'b0, clk_i, rst_ni) + + // Select which response should be forwarded. `1` write response, `0` read response. + logic rsp_sel; + + fifo_v3 #( + .FALL_THROUGH ( 1'b0 ), // No fallthrough for one cycle delay before ready on AXI. + .DEPTH ( MaxRequests ), + .dtype ( logic ) + ) i_fifo_rsp_mux ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( fifo_full ), + .empty_o ( fifo_empty ), + .usage_o ( /*not used*/ ), + .data_i ( mem_we_i ), + .push_i ( mem_gnt_o ), + .data_o ( rsp_sel ), + .pop_i ( mem_rsp_valid_o ) + ); + + // Response selection control. + // If something is in the FIFO, the corresponding channel is ready. + assign axi_req_o.b_ready = !fifo_empty && rsp_sel; + assign axi_req_o.r_ready = !fifo_empty && !rsp_sel; + // Read data is directly forwarded. + assign mem_rsp_rdata_o = axi_rsp_i.r.data; + // Error is taken from the respective channel. + assign mem_rsp_error_o = rsp_sel ? + (axi_rsp_i.b.resp inside {axi_pkg::RESP_SLVERR, axi_pkg::RESP_DECERR}) : + (axi_rsp_i.r.resp inside {axi_pkg::RESP_SLVERR, axi_pkg::RESP_DECERR}); + // Mem response is valid if the handshaking on the respective channel occurs. + // Can not happen at the same time as ready is set from the FIFO. + // This serves as the pop signal for the FIFO. + assign mem_rsp_valid_o = (axi_rsp_i.b_valid && axi_req_o.b_ready) || + (axi_rsp_i.r_valid && axi_req_o.r_ready); + + // pragma translate_off + `ifndef SYNTHESIS + `ifndef VERILATOR + initial begin : proc_assert + assert (MemAddrWidth > 32'd0) else $fatal(1, "MemAddrWidth has to be greater than 0!"); + assert (AxiAddrWidth > 32'd0) else $fatal(1, "AxiAddrWidth has to be greater than 0!"); + assert (DataWidth inside {32'd32, 32'd64}) else + $fatal(1, "DataWidth has to be either 32 or 64 bit!"); + assert (MaxRequests > 32'd0) else $fatal(1, "MaxRequests has to be greater than 0!"); + assert (AxiAddrWidth == $bits(axi_req_o.aw.addr)) else + $fatal(1, "AxiAddrWidth has to match axi_req_o.aw.addr!"); + assert (AxiAddrWidth == $bits(axi_req_o.ar.addr)) else + $fatal(1, "AxiAddrWidth has to match axi_req_o.ar.addr!"); + assert (DataWidth == $bits(axi_req_o.w.data)) else + $fatal(1, "DataWidth has to match axi_req_o.w.data!"); + assert (DataWidth/8 == $bits(axi_req_o.w.strb)) else + $fatal(1, "DataWidth / 8 has to match axi_req_o.w.strb!"); + assert (DataWidth == $bits(axi_rsp_i.r.data)) else + $fatal(1, "DataWidth has to match axi_rsp_i.r.data!"); + end + default disable iff (~rst_ni); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> mem_req_i) else + $fatal(1, "It is not allowed to deassert the request if it was not granted!"); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> $stable(mem_addr_i)) else + $fatal(1, "mem_addr_i has to be stable if request is not granted!"); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> $stable(mem_we_i)) else + $fatal(1, "mem_we_i has to be stable if request is not granted!"); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> $stable(mem_wdata_i)) else + $fatal(1, "mem_wdata_i has to be stable if request is not granted!"); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> $stable(mem_be_i)) else + $fatal(1, "mem_be_i has to be stable if request is not granted!"); + `endif + `endif + // pragma translate_on +endmodule diff --git a/src_files.yml b/src_files.yml index 3703488ea..8ee8a9c15 100644 --- a/src_files.yml +++ b/src_files.yml @@ -30,6 +30,7 @@ axi: - src/axi_isolate.sv - src/axi_join.sv - src/axi_lite_demux.sv + - src/axi_lite_from_mem.sv - src/axi_lite_join.sv - src/axi_lite_lfsr.sv - src/axi_lite_mailbox.sv @@ -46,6 +47,7 @@ axi: # Level 3 - src/axi_cdc.sv - src/axi_err_slv.sv + - src/axi_from_mem.sv - src/axi_dw_converter.sv - src/axi_id_serialize.sv - src/axi_lfsr.sv From ded97ffe6917fb30674eb8a640dc68c2d64b38ea Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 10 Nov 2022 19:24:16 +0100 Subject: [PATCH 099/145] Add rw_split and rw_join modules --- Bender.yml | 2 + CHANGELOG.md | 1 + README.md | 2 + axi.core | 2 + src/axi_rw_join.sv | 100 +++++++++++++++++++++++++++++++++++++++++++ src/axi_rw_split.sv | 102 ++++++++++++++++++++++++++++++++++++++++++++ src_files.yml | 2 + 7 files changed, 211 insertions(+) create mode 100644 src/axi_rw_join.sv create mode 100644 src/axi_rw_split.sv diff --git a/Bender.yml b/Bender.yml index 68d78ba94..5c1f43a14 100644 --- a/Bender.yml +++ b/Bender.yml @@ -59,6 +59,8 @@ sources: - src/axi_lite_to_axi.sv - src/axi_modify_address.sv - src/axi_mux.sv + - src/axi_rw_join.sv + - src/axi_rw_split.sv - src/axi_serializer.sv - src/axi_slave_compare.sv - src/axi_throttle.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index 526e6f224..4fe0d6192 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add `axi_bus_compare` and `axi_slave_compare`; two synthesizable verification IPs meant to be used to compare two AXI buses on an FPGA. - Add `axi_lite_from_mem` and `axi_from_mem` acting like SRAMs making AXI4 requests downstream. +- Add `axi_rw_join` and `axi_rw_split` to split/join AXI buses. ### Changed - `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents diff --git a/README.md b/README.md index 27c62820a..012703744 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,8 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_multicut`](src/axi_multicut.sv) | AXI register which can be used to relax timing pressure on long AXI buses. | | | [`axi_mux`](src/axi_mux.sv) | Multiplexes the AXI4 slave ports down to one master port. | [Doc](doc/axi_mux.md) | | [`axi_pkg`](src/axi_pkg.sv) | Contains AXI definitions, common structs, and useful helper functions. | | +| [`axi_rw_join`](src/axi_rw_join.sv) | Joins a read and a write slave into one single read / write master. | | +| [`axi_rw_split`](src/axi_rw_split.sv) | Splits a single read / write slave into one read and one write master. | | | [`axi_serializer`](src/axi_serializer.sv) | Serializes transactions with different IDs to the same ID. | | | [`axi_throttle`](src/axi_throttle.sv) | Limits the maximum number of outstanding transfers sent to the downstream logic. | | | [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | diff --git a/axi.core b/axi.core index 5f17230c2..f67237729 100644 --- a/axi.core +++ b/axi.core @@ -41,6 +41,8 @@ filesets: - src/axi_lite_to_axi.sv - src/axi_modify_address.sv - src/axi_mux.sv + - src/axi_rw_join.sv + - src/axi_rw_split.sv - src/axi_serializer.sv - src/axi_slave_compare.sv - src/axi_throttle.sv diff --git a/src/axi_rw_join.sv b/src/axi_rw_join.sv new file mode 100644 index 000000000..eb7957a72 --- /dev/null +++ b/src/axi_rw_join.sv @@ -0,0 +1,100 @@ +// Copyright (c) 2022 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: +// - Tobias Senti + +`include "axi/assign.svh" + +/// Joins a read and a write slave into one single read / write master +/// +/// Connects the ar and r channel of the read slave to the read / write master +/// and the aw, w and b channel of the write slave to the read / write master +module axi_rw_join #( + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + // Read Slave + input axi_req_t slv_read_req_i, + output axi_resp_t slv_read_resp_o, + + // Write Slave + input axi_req_t slv_write_req_i, + output axi_resp_t slv_write_resp_o, + + // Read / Write Master + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i +); + + //-------------------------------------- + // Read channel data + //-------------------------------------- + + // Assign Read Structs + `AXI_ASSIGN_AR_STRUCT ( mst_req_o.ar , slv_read_req_i.ar ) + `AXI_ASSIGN_R_STRUCT ( slv_read_resp_o.r , mst_resp_i.r ) + + // Read B channel data + assign slv_read_resp_o.b = 'b0; + + + //-------------------------------------- + // Read channel handshakes + //-------------------------------------- + + // Read AR channel handshake + assign mst_req_o.ar_valid = slv_read_req_i.ar_valid; + assign slv_read_resp_o.ar_ready = mst_resp_i.ar_ready; + + // Read R channel handshake + assign slv_read_resp_o.r_valid = mst_resp_i.r_valid; + assign mst_req_o.r_ready = slv_read_req_i.r_ready; + + // Read AW, W and B handshake + assign slv_read_resp_o.aw_ready = 1'b0; + assign slv_read_resp_o.w_ready = 1'b0; + assign slv_read_resp_o.b_valid = 1'b0; + + //-------------------------------------- + // Write channel data + //-------------------------------------- + + // Assign Write Structs + `AXI_ASSIGN_AW_STRUCT ( mst_req_o.aw , slv_write_req_i.aw ) + `AXI_ASSIGN_W_STRUCT ( mst_req_o.w , slv_write_req_i.w ) + `AXI_ASSIGN_B_STRUCT ( slv_write_resp_o.b , mst_resp_i.b ) + + // Write R channel data + assign slv_write_resp_o.r = '0; + + + //-------------------------------------- + // Write channel handshakes + //-------------------------------------- + + // Write AR and R channel handshake + assign slv_write_resp_o.ar_ready = 1'b0; + assign slv_write_resp_o.r_valid = 1'b0; + + // Write AW channel handshake + assign mst_req_o.aw_valid = slv_write_req_i.aw_valid; + assign slv_write_resp_o.aw_ready = mst_resp_i.aw_ready; + + // Write W channel handshake + assign mst_req_o.w_valid = slv_write_req_i.w_valid; + assign slv_write_resp_o.w_ready = mst_resp_i.w_ready; + + // Write B channel handshake + assign slv_write_resp_o.b_valid = mst_resp_i.b_valid; + assign mst_req_o.b_ready = slv_write_req_i.b_ready; + +endmodule : axi_rw_join diff --git a/src/axi_rw_split.sv b/src/axi_rw_split.sv new file mode 100644 index 000000000..59a873854 --- /dev/null +++ b/src/axi_rw_split.sv @@ -0,0 +1,102 @@ +// Copyright (c) 2022 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: +// - Tobias Senti + +`include "axi/assign.svh" + +/// Splits a single read / write slave into one read and one write master +/// +/// Connects the ar and r channel of the read / write slave to the read master +/// and the aw, w and b channel of the read / write slave to the write master +module axi_rw_split #( + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + // Read / Write Slave + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, + + // Read Master + output axi_req_t mst_read_req_o, + input axi_resp_t mst_read_resp_i, + + // Write Master + output axi_req_t mst_write_req_o, + input axi_resp_t mst_write_resp_i +); + + //-------------------------------------- + // Read channel data + //-------------------------------------- + + // Assign Read channel structs + `AXI_ASSIGN_AR_STRUCT ( mst_read_req_o.ar , slv_req_i.ar ) + `AXI_ASSIGN_R_STRUCT ( slv_resp_o.r , mst_read_resp_i.r ) + + // Read AW and W channel data + assign mst_read_req_o.aw = '0; + assign mst_read_req_o.w = '0; + + + //-------------------------------------- + // Read channel handshakes + //-------------------------------------- + + // Read AR channel handshake + assign mst_read_req_o.ar_valid = slv_req_i.ar_valid; + assign slv_resp_o.ar_ready = mst_read_resp_i.ar_ready; + + // Read R channel handshake + assign slv_resp_o.r_valid = mst_read_resp_i.r_valid; + assign mst_read_req_o.r_ready = slv_req_i.r_ready; + + // Read AW, W and B handshake + assign mst_read_req_o.aw_valid = 1'b0; + assign mst_read_req_o.w_valid = 1'b0; + assign mst_read_req_o.b_ready = 1'b0; + + + //-------------------------------------- + // Write channel data + //-------------------------------------- + + // Assign Write channel structs + `AXI_ASSIGN_AW_STRUCT ( mst_write_req_o.aw , slv_req_i.aw ) + `AXI_ASSIGN_W_STRUCT ( mst_write_req_o.w , slv_req_i.w ) + `AXI_ASSIGN_B_STRUCT ( slv_resp_o.b , mst_write_resp_i.b ) + + // Write AR channel data + assign mst_write_req_o.ar = 'b0; + + + //-------------------------------------- + // Write channel handshakes + //-------------------------------------- + + // Write AR and R channel handshake + assign mst_write_req_o.ar_valid = 1'b0; + assign mst_write_req_o.r_ready = 1'b0; + + // Write AW channel handshake + assign mst_write_req_o.aw_valid = slv_req_i.aw_valid; + assign slv_resp_o.aw_ready = mst_write_resp_i.aw_ready; + + // Write W channel handshake + assign mst_write_req_o.w_valid = slv_req_i.w_valid; + assign slv_resp_o.w_ready = mst_write_resp_i.w_ready; + + // Write B channel handshake + assign slv_resp_o.b_valid = mst_write_resp_i.b_valid; + assign mst_write_req_o.b_ready = slv_req_i.b_ready; + +endmodule : axi_rw_split diff --git a/src_files.yml b/src_files.yml index 8ee8a9c15..7c3743e41 100644 --- a/src_files.yml +++ b/src_files.yml @@ -40,6 +40,8 @@ axi: - src/axi_lite_to_axi.sv - src/axi_modify_address.sv - src/axi_mux.sv + - src/axi_rw_join.sv + - src/axi_rw_split.sv - src/axi_serializer.sv - src/axi_slave_compare.sv - src/axi_throttle.sv From c7d995b6e153dd43ed003635cc946371c17ed871 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Fri, 11 Nov 2022 10:27:20 +0100 Subject: [PATCH 100/145] Add assertions, use split module in `axi_to_mem_split` --- src/axi_rw_join.sv | 12 +++++++++++- src/axi_rw_split.sv | 11 ++++++++++- src/axi_to_mem_split.sv | 34 +++++++++++++--------------------- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/axi_rw_join.sv b/src/axi_rw_join.sv index eb7957a72..6b7191b38 100644 --- a/src/axi_rw_join.sv +++ b/src/axi_rw_join.sv @@ -13,6 +13,7 @@ // - Tobias Senti `include "axi/assign.svh" +`include "common_cells/assertions.svh" /// Joins a read and a write slave into one single read / write master /// @@ -22,6 +23,8 @@ module axi_rw_join #( parameter type axi_req_t = logic, parameter type axi_resp_t = logic ) ( + input logic clk_i, + input logic rst_ni, // Read Slave input axi_req_t slv_read_req_i, output axi_resp_t slv_read_resp_o, @@ -44,7 +47,7 @@ module axi_rw_join #( `AXI_ASSIGN_R_STRUCT ( slv_read_resp_o.r , mst_resp_i.r ) // Read B channel data - assign slv_read_resp_o.b = 'b0; + assign slv_read_resp_o.b = '0; //-------------------------------------- @@ -64,6 +67,10 @@ module axi_rw_join #( assign slv_read_resp_o.w_ready = 1'b0; assign slv_read_resp_o.b_valid = 1'b0; + // check for AW and W never to be valid + `ASSERT_NEVER(slv_read_req_aw_valid, slv_read_req_i.aw_valid, clk_i, !rst_ni) + `ASSERT_NEVER(slv_read_req_w_valid, slv_read_req_i.w_valid, clk_i, !rst_ni) + //-------------------------------------- // Write channel data //-------------------------------------- @@ -85,6 +92,9 @@ module axi_rw_join #( assign slv_write_resp_o.ar_ready = 1'b0; assign slv_write_resp_o.r_valid = 1'b0; + // check for AR to never be valid + `ASSERT_NEVER(slv_write_req_ar_valid, slv_write_req_i.ar_valid, clk_i, !rst_ni) + // Write AW channel handshake assign mst_req_o.aw_valid = slv_write_req_i.aw_valid; assign slv_write_resp_o.aw_ready = mst_resp_i.aw_ready; diff --git a/src/axi_rw_split.sv b/src/axi_rw_split.sv index 59a873854..6fa96dfa9 100644 --- a/src/axi_rw_split.sv +++ b/src/axi_rw_split.sv @@ -13,6 +13,7 @@ // - Tobias Senti `include "axi/assign.svh" +`include "common_cells/assertions.svh" /// Splits a single read / write slave into one read and one write master /// @@ -22,6 +23,8 @@ module axi_rw_split #( parameter type axi_req_t = logic, parameter type axi_resp_t = logic ) ( + input logic clk_i, + input logic rst_ni, // Read / Write Slave input axi_req_t slv_req_i, output axi_resp_t slv_resp_o, @@ -65,6 +68,9 @@ module axi_rw_split #( assign mst_read_req_o.w_valid = 1'b0; assign mst_read_req_o.b_ready = 1'b0; + // check for B never to be valid + `ASSERT_NEVER(mst_read_resp_b_valid, mst_read_resp_i.b_valid, clk_i, !rst_ni) + //-------------------------------------- // Write channel data @@ -76,7 +82,7 @@ module axi_rw_split #( `AXI_ASSIGN_B_STRUCT ( slv_resp_o.b , mst_write_resp_i.b ) // Write AR channel data - assign mst_write_req_o.ar = 'b0; + assign mst_write_req_o.ar = '0; //-------------------------------------- @@ -87,6 +93,9 @@ module axi_rw_split #( assign mst_write_req_o.ar_valid = 1'b0; assign mst_write_req_o.r_ready = 1'b0; + // check for R never to be valid + `ASSERT_NEVER(mst_read_resp_r_valid, mst_read_resp_i.r_valid, clk_i, !rst_ni) + // Write AW channel handshake assign mst_write_req_o.aw_valid = slv_req_i.aw_valid; assign slv_resp_o.aw_ready = mst_write_resp_i.aw_ready; diff --git a/src/axi_to_mem_split.sv b/src/axi_to_mem_split.sv index a51350393..107d3fe75 100644 --- a/src/axi_to_mem_split.sv +++ b/src/axi_to_mem_split.sv @@ -81,27 +81,19 @@ module axi_to_mem_split #( logic read_busy, write_busy; - always_comb begin: proc_axi_rw_split - `AXI_SET_R_STRUCT(axi_resp_o.r, axi_read_resp.r) - axi_resp_o.r_valid = axi_read_resp.r_valid; - axi_resp_o.ar_ready = axi_read_resp.ar_ready; - `AXI_SET_B_STRUCT(axi_resp_o.b, axi_write_resp.b) - axi_resp_o.b_valid = axi_write_resp.b_valid; - axi_resp_o.aw_ready = axi_write_resp.aw_ready; - axi_resp_o.w_ready = axi_write_resp.w_ready; - - axi_write_req = '0; - `AXI_SET_AW_STRUCT(axi_write_req.aw, axi_req_i.aw) - axi_write_req.aw_valid = axi_req_i.aw_valid; - `AXI_SET_W_STRUCT(axi_write_req.w, axi_req_i.w) - axi_write_req.w_valid = axi_req_i.w_valid; - axi_write_req.b_ready = axi_req_i.b_ready; - - axi_read_req = '0; - `AXI_SET_AR_STRUCT(axi_read_req.ar, axi_req_i.ar) - axi_read_req.ar_valid = axi_req_i.ar_valid; - axi_read_req.r_ready = axi_req_i.r_ready; - end + axi_rw_split #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) + ) i_axi_rw_split ( + .clk_i, + .rst_ni, + .slv_req_i ( axi_req_i ), + .slv_resp_o ( axi_resp_o ), + .mst_read_req_o ( axi_read_req ), + .mst_read_resp_i ( axi_read_resp ), + .mst_write_req_o ( axi_write_req ), + .mst_write_resp_i ( axi_write_resp ) + ); assign busy_o = read_busy || write_busy; From fe4e020f2c06a242d4901b23cedbf2eeab22bfe6 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Tue, 6 Dec 2022 07:55:42 +0100 Subject: [PATCH 101/145] axi_pkg: Add functions which return channel widths --- CHANGELOG.md | 2 + src/axi_pkg.sv | 105 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 96 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fe0d6192..686794c1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. to compare two AXI buses on an FPGA. - Add `axi_lite_from_mem` and `axi_from_mem` acting like SRAMs making AXI4 requests downstream. - Add `axi_rw_join` and `axi_rw_split` to split/join AXI buses. +- Add `#_width` functions returning the width of the AXI channels. ### Changed - `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents @@ -21,6 +22,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` in the crossed connections in the xbar between the demuxes and muxes. - `axi_pkg`: Add documentation to `xbar_cfg_t`. +- `axi_pkg`: Define `localparams` to define AXI type widths. ### Fixed diff --git a/src/axi_pkg.sv b/src/axi_pkg.sv index 7ef3bbcd7..583846491 100644 --- a/src/axi_pkg.sv +++ b/src/axi_pkg.sv @@ -19,26 +19,49 @@ //! AXI Package /// Contains all necessary type definitions, constants, and generally useful functions. package axi_pkg; - /// AXI Transaction Burst Type. - typedef logic [1:0] burst_t; + /// AXI Transaction Burst Width. + parameter int unsigned BurstWidth = 32'd2; + /// AXI Transaction Response Width. + parameter int unsigned RespWidth = 32'd2; + /// AXI Transaction Cacheability Width. + parameter int unsigned CacheWidth = 32'd4; + /// AXI Transaction Protection Width. + parameter int unsigned ProtWidth = 32'd3; + /// AXI Transaction Quality of Service Width. + parameter int unsigned QosWidth = 32'd4; + /// AXI Transaction Region Width. + parameter int unsigned RegionWidth = 32'd4; + /// AXI Transaction Length Width. + parameter int unsigned LenWidth = 32'd8; + /// AXI Transaction Size Width. + parameter int unsigned SizeWidth = 32'd3; + /// AXI Lock Width. + parameter int unsigned LockWidth = 32'd1; + /// AXI5 Atomic Operation Width. + parameter int unsigned AtopWidth = 32'd6; + /// AXI5 Non-Secure Address Identifier. + parameter int unsigned NsaidWidth = 32'd4; + + /// AXI Transaction Burst Width. + typedef logic [BurstWidth-1:0] burst_t; /// AXI Transaction Response Type. - typedef logic [1:0] resp_t; + typedef logic [RespWidth-1:0] resp_t; /// AXI Transaction Cacheability Type. - typedef logic [3:0] cache_t; + typedef logic [CacheWidth-1:0] cache_t; /// AXI Transaction Protection Type. - typedef logic [2:0] prot_t; + typedef logic [ProtWidth-1:0] prot_t; /// AXI Transaction Quality of Service Type. - typedef logic [3:0] qos_t; + typedef logic [QosWidth-1:0] qos_t; /// AXI Transaction Region Type. - typedef logic [3:0] region_t; + typedef logic [RegionWidth-1:0] region_t; /// AXI Transaction Length Type. - typedef logic [7:0] len_t; + typedef logic [LenWidth-1:0] len_t; /// AXI Transaction Size Type. - typedef logic [2:0] size_t; + typedef logic [SizeWidth-1:0] size_t; /// AXI5 Atomic Operation Type. - typedef logic [5:0] atop_t; // atomic operations + typedef logic [AtopWidth-1:0] atop_t; // atomic operations /// AXI5 Non-Secure Address Identifier. - typedef logic [3:0] nsaid_t; + typedef logic [NsaidWidth-1:0] nsaid_t; /// In a fixed burst: /// - The address is the same for every transfer in the burst. @@ -290,6 +313,65 @@ package axi_pkg; endcase endfunction + /// AW Width: Returns the width of the AW channel payload + function automatic int unsigned aw_width(int unsigned addr_width, int unsigned id_width, + int unsigned user_width ); + // Sum the individual bit widths of the signals + return (id_width + addr_width + LenWidth + SizeWidth + BurstWidth + LockWidth + CacheWidth + + ProtWidth + QosWidth + RegionWidth + AtopWidth + user_width ); + endfunction + + /// W Width: Returns the width of the W channel payload + function automatic int unsigned w_width(int unsigned data_width, int unsigned user_width ); + // Sum the individual bit widths of the signals + return (data_width + data_width / 32'd8 + 32'd1 + user_width); + // ^- StrobeWidth ^- LastWidth + endfunction + + /// B Width: Returns the width of the B channel payload + function automatic int unsigned b_width(int unsigned id_width, int unsigned user_width ); + // Sum the individual bit widths of the signals + return (id_width + RespWidth + user_width); + endfunction + + /// AR Width: Returns the width of the AR channel payload + function automatic int unsigned ar_width(int unsigned addr_width, int unsigned id_width, + int unsigned user_width ); + // Sum the individual bit widths of the signals + return (id_width + addr_width + LenWidth + SizeWidth + BurstWidth + LockWidth + CacheWidth + + ProtWidth + QosWidth + RegionWidth + user_width ); + endfunction + + /// R Width: Returns the width of the R channel payload + function automatic int unsigned r_width(int unsigned data_width, int unsigned id_width, + int unsigned user_width ); + // Sum the individual bit widths of the signals + return (id_width + data_width + RespWidth + 32'd1 + user_width); + // ^- LastWidth + endfunction + + /// Request Width: Returns the width of the request channel + function automatic int unsigned req_width(int unsigned addr_width, int unsigned data_width, + int unsigned id_width, int unsigned aw_user_width, + int unsigned ar_user_width, int unsigned w_user_width ); + // Sum the individual bit widths of the signals and their handshakes + // v- valids + return (aw_width(addr_width, id_width, aw_user_width) + 32'd1 + + w_width(data_width, w_user_width) + 32'd1 + + ar_width(addr_width, id_width, ar_user_width) + 32'd1 + 32'd1 + 32'd1 ); + // ^- R, ^- B ready + endfunction + + /// Response Width: Returns the width of the response channel + function automatic int unsigned rsp_width(int unsigned data_width, int unsigned id_width, + int unsigned r_user_width, int unsigned b_user_width ); + // Sum the individual bit widths of the signals and their handshakes + // v- valids + return (r_width(data_width, id_width, r_user_width) + 32'd1 + + b_width(id_width, b_user_width) + 32'd1 + 32'd1 + 32'd1 + 32'd1); + // ^- AW, ^- AR, ^- W ready + endfunction + // ATOP[5:0] /// - Sends a single data value with an address. /// - The target swaps the value at the addressed location with the data value that is supplied in @@ -447,4 +529,5 @@ package axi_pkg; logic [31:0] start_addr; logic [31:0] end_addr; } xbar_rule_32_t; + endpackage From 9c1dba88d211163bb54e655f6a3085d6c3055758 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Tue, 29 Nov 2022 11:42:13 +0100 Subject: [PATCH 102/145] mem_to_banks: Move from axi to common_cells --- Bender.yml | 2 +- CHANGELOG.md | 2 + src/axi_to_mem.sv | 212 ++-------------------------------------------- 3 files changed, 10 insertions(+), 206 deletions(-) diff --git a/Bender.yml b/Bender.yml index 5c1f43a14..dd3365ef2 100644 --- a/Bender.yml +++ b/Bender.yml @@ -17,7 +17,7 @@ package: - "Florian Zaruba " dependencies: - common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.26.0 } + common_cells: { git: "https://github.com/pulp-platform/common_cells.git", rev: mem_to_banks-tbenz } common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.3 } tech_cells_generic: { git: "https://github.com/pulp-platform/tech_cells_generic.git", version: 0.2.2 } diff --git a/CHANGELOG.md b/CHANGELOG.md index 686794c1b..27d771590 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` in the crossed connections in the xbar between the demuxes and muxes. - `axi_pkg`: Add documentation to `xbar_cfg_t`. +- Move `mem_to_banks` to `common_cells`. +- Update `common_cells` from version `v1.26.0` to `v1.xx.yy`. - `axi_pkg`: Define `localparams` to define AXI type widths. ### Fixed diff --git a/src/axi_to_mem.sv b/src/axi_to_mem.sv index 1b8fd590e..03d2caf26 100644 --- a/src/axi_to_mem.sv +++ b/src/axi_to_mem.sv @@ -350,12 +350,13 @@ module axi_to_mem #( // Split single memory request to desired number of banks. mem_to_banks #( - .AddrWidth ( AddrWidth ), - .DataWidth ( DataWidth ), - .NumBanks ( NumBanks ), - .HideStrb ( HideStrb ), - .MaxTrans ( BufDepth ), - .FifoDepth ( OutFifoDepth ) + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .NumBanks ( NumBanks ), + .HideStrb ( HideStrb ), + .MaxTrans ( BufDepth ), + .FifoDepth ( OutFifoDepth ), + .atop_t ( axi_pkg::atop_t ) ) i_mem_to_banks ( .clk_i, .rst_ni, @@ -557,202 +558,3 @@ module axi_to_mem_intf #( .mem_rdata_i ); endmodule - -/// Split memory access over multiple parallel banks, where each bank has its own req/gnt -/// request and valid response direction. -module mem_to_banks #( - /// Input address width. - parameter int unsigned AddrWidth = 32'd0, - /// Input data width, must be a power of two. - parameter int unsigned DataWidth = 32'd0, - /// Number of banks at output, must evenly divide `DataWidth`. - parameter int unsigned NumBanks = 32'd0, - /// Remove transactions that have zero strobe - parameter bit HideStrb = 1'b0, - /// Number of outstanding transactions - parameter int unsigned MaxTrans = 32'b1, - /// FIFO depth, must be >=1 - parameter int unsigned FifoDepth = 1, - /// Dependent parameter, do not override! Address type. - localparam type addr_t = logic [AddrWidth-1:0], - /// Dependent parameter, do not override! Input data type. - localparam type inp_data_t = logic [DataWidth-1:0], - /// Dependent parameter, do not override! Input write strobe type. - localparam type inp_strb_t = logic [DataWidth/8-1:0], - /// Dependent parameter, do not override! Output data type. - localparam type oup_data_t = logic [DataWidth/NumBanks-1:0], - /// Dependent parameter, do not override! Output write strobe type. - localparam type oup_strb_t = logic [DataWidth/NumBanks/8-1:0] -) ( - /// Clock input. - input logic clk_i, - /// Asynchronous reset, active low. - input logic rst_ni, - /// Memory request to split, request is valid. - input logic req_i, - /// Memory request to split, request can be granted. - output logic gnt_o, - /// Memory request to split, request address, byte-wise. - input addr_t addr_i, - /// Memory request to split, request write data. - input inp_data_t wdata_i, - /// Memory request to split, request write strobe. - input inp_strb_t strb_i, - /// Memory request to split, request Atomic signal from AXI4+ATOP. - input axi_pkg::atop_t atop_i, - /// Memory request to split, request write enable, active high. - input logic we_i, - /// Memory request to split, response is valid. Required for read and write requests - output logic rvalid_o, - /// Memory request to split, response read data. - output inp_data_t rdata_o, - /// Memory bank request, request is valid. - output logic [NumBanks-1:0] bank_req_o, - /// Memory bank request, request can be granted. - input logic [NumBanks-1:0] bank_gnt_i, - /// Memory bank request, request address, byte-wise. Will be different for each bank. - output addr_t [NumBanks-1:0] bank_addr_o, - /// Memory bank request, request write data. - output oup_data_t [NumBanks-1:0] bank_wdata_o, - /// Memory bank request, request write strobe. - output oup_strb_t [NumBanks-1:0] bank_strb_o, - /// Memory bank request, request Atomic signal from AXI4+ATOP. - output axi_pkg::atop_t [NumBanks-1:0] bank_atop_o, - /// Memory bank request, request write enable, active high. - output logic [NumBanks-1:0] bank_we_o, - /// Memory bank request, response is valid. Required for read and write requests - input logic [NumBanks-1:0] bank_rvalid_i, - /// Memory bank request, response read data. - input oup_data_t [NumBanks-1:0] bank_rdata_i -); - - localparam DataBytes = $bits(inp_strb_t); - localparam BitsPerBank = $bits(oup_data_t); - localparam BytesPerBank = $bits(oup_strb_t); - - typedef struct packed { - addr_t addr; - oup_data_t wdata; - oup_strb_t strb; - axi_pkg::atop_t atop; - logic we; - } req_t; - - logic req_valid; - logic [NumBanks-1:0] req_ready, - resp_valid, resp_ready; - req_t [NumBanks-1:0] bank_req, - bank_oup; - logic [NumBanks-1:0] bank_req_internal, bank_gnt_internal, zero_strobe, dead_response; - logic dead_write_fifo_full; - - function automatic addr_t align_addr(input addr_t addr); - return (addr >> $clog2(DataBytes)) << $clog2(DataBytes); - endfunction - - // Handle requests. - assign req_valid = req_i & gnt_o; - for (genvar i = 0; unsigned'(i) < NumBanks; i++) begin : gen_reqs - assign bank_req[i].addr = align_addr(addr_i) + i * BytesPerBank; - assign bank_req[i].wdata = wdata_i[i*BitsPerBank+:BitsPerBank]; - assign bank_req[i].strb = strb_i[i*BytesPerBank+:BytesPerBank]; - assign bank_req[i].atop = atop_i; - assign bank_req[i].we = we_i; - stream_fifo #( - .FALL_THROUGH ( 1'b1 ), - .DATA_WIDTH ( $bits(req_t) ), - .DEPTH ( FifoDepth ), - .T ( req_t ) - ) i_ft_reg ( - .clk_i, - .rst_ni, - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .usage_o (), - .data_i ( bank_req[i] ), - .valid_i ( req_valid ), - .ready_o ( req_ready[i] ), - .data_o ( bank_oup[i] ), - .valid_o ( bank_req_internal[i] ), - .ready_i ( bank_gnt_internal[i] ) - ); - assign bank_addr_o[i] = bank_oup[i].addr; - assign bank_wdata_o[i] = bank_oup[i].wdata; - assign bank_strb_o[i] = bank_oup[i].strb; - assign bank_atop_o[i] = bank_oup[i].atop; - assign bank_we_o[i] = bank_oup[i].we; - - assign zero_strobe[i] = (bank_oup[i].strb == '0); - - if (HideStrb) begin - assign bank_req_o[i] = (bank_oup[i].we && zero_strobe[i]) ? 1'b0 : bank_req_internal[i]; - assign bank_gnt_internal[i] = (bank_oup[i].we && zero_strobe[i]) ? 1'b1 : bank_gnt_i[i]; - end else begin - assign bank_req_o[i] = bank_req_internal[i]; - assign bank_gnt_internal[i] = bank_gnt_i[i]; - end - end - - // Grant output if all our requests have been granted. - assign gnt_o = (&req_ready) & (&resp_ready) & !dead_write_fifo_full; - - if (HideStrb) begin : gen_dead_write_fifo - fifo_v3 #( - .FALL_THROUGH ( 1'b1 ), - .DEPTH ( MaxTrans+1 ), - .DATA_WIDTH ( NumBanks ) - ) i_dead_write_fifo ( - .clk_i, - .rst_ni, - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .full_o ( dead_write_fifo_full ), - .empty_o (), - .usage_o (), - .data_i ( bank_we_o & zero_strobe ), - .push_i ( req_i & gnt_o ), - .data_o ( dead_response ), - .pop_i ( rvalid_o ) - ); - end else begin - assign dead_response = '0; - assign dead_write_fifo_full = 1'b0; - end - - // Handle responses. - for (genvar i = 0; unsigned'(i) < NumBanks; i++) begin : gen_resp_regs - stream_fifo #( - .FALL_THROUGH ( 1'b1 ), - .DATA_WIDTH ( $bits(oup_data_t) ), - .DEPTH ( FifoDepth ), - .T ( oup_data_t ) - ) i_ft_reg ( - .clk_i, - .rst_ni, - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .usage_o (), - .data_i ( bank_rdata_i[i] ), - .valid_i ( bank_rvalid_i[i] ), - .ready_o ( resp_ready[i] ), - .data_o ( rdata_o[i*BitsPerBank+:BitsPerBank] ), - .valid_o ( resp_valid[i] ), - .ready_i ( rvalid_o & !dead_response[i] ) - ); - end - assign rvalid_o = &(resp_valid | dead_response); - - // Assertions - // pragma translate_off - `ifndef VERILATOR - initial begin - assume (DataWidth != 0 && (DataWidth & (DataWidth - 1)) == 0) - else $fatal(1, "Data width must be a power of two!"); - assume (DataWidth % NumBanks == 0) - else $fatal(1, "Data width must be evenly divisible over banks!"); - assume ((DataWidth / NumBanks) % 8 == 0) - else $fatal(1, "Data width of each bank must be divisible into 8-bit bytes!"); - end - `endif - // pragma translate_on -endmodule From f21b1bf4393849d9a44e1ed34b665872f2a7c388 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Tue, 6 Dec 2022 07:59:34 +0100 Subject: [PATCH 103/145] Bender.yml: Update `common_cells` to `1.27.0` --- Bender.yml | 2 +- CHANGELOG.md | 2 +- axi.core | 2 +- ips_list.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Bender.yml b/Bender.yml index dd3365ef2..7a348ac14 100644 --- a/Bender.yml +++ b/Bender.yml @@ -17,7 +17,7 @@ package: - "Florian Zaruba " dependencies: - common_cells: { git: "https://github.com/pulp-platform/common_cells.git", rev: mem_to_banks-tbenz } + common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.27.0 } common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.3 } tech_cells_generic: { git: "https://github.com/pulp-platform/tech_cells_generic.git", version: 0.2.2 } diff --git a/CHANGELOG.md b/CHANGELOG.md index 27d771590..e6bb2ed08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. in the crossed connections in the xbar between the demuxes and muxes. - `axi_pkg`: Add documentation to `xbar_cfg_t`. - Move `mem_to_banks` to `common_cells`. -- Update `common_cells` from version `v1.26.0` to `v1.xx.yy`. +- Update `common_cells` from version `v1.26.0` to `v1.27.0`. - `axi_pkg`: Define `localparams` to define AXI type widths. ### Fixed diff --git a/axi.core b/axi.core index f67237729..e75ed636f 100644 --- a/axi.core +++ b/axi.core @@ -71,7 +71,7 @@ filesets: - src/axi_xp.sv file_type : systemVerilogSource depend : - - ">=pulp-platform.org::common_cells:1.26.0" + - ">=pulp-platform.org::common_cells:1.27.0" benchs: files: - test/tb_axi_dw_pkg.sv diff --git a/ips_list.yml b/ips_list.yml index 07b712ffc..da1a0adc6 100644 --- a/ips_list.yml +++ b/ips_list.yml @@ -1,5 +1,5 @@ common_cells: - commit: v1.26.0 + commit: v1.27.0 group: pulp-platform common_verification: From 0996e83947fb0582314d7368d772a166e7d54a54 Mon Sep 17 00:00:00 2001 From: Luca Valente Date: Tue, 24 Jan 2023 17:10:30 +0100 Subject: [PATCH 104/145] Add option for aligned accesses of given `ax_size`. --- src/axi_test.sv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/axi_test.sv b/src/axi_test.sv index 7c7fab63e..ae74cf1ac 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -698,6 +698,7 @@ package axi_test; parameter int RESP_MIN_WAIT_CYCLES = 0, parameter int RESP_MAX_WAIT_CYCLES = 20, // AXI feature usage + parameter int SIZE_ALIGN = 0, parameter int AXI_MAX_BURST_LEN = 0, // maximum number of beats in burst; 0 = AXI max (256) parameter int TRAFFIC_SHAPING = 0, parameter bit AXI_EXCLS = 1'b0, @@ -926,7 +927,7 @@ package axi_test; end end - ax_beat.ax_addr = addr; + ax_beat.ax_addr = axi_pkg::aligned_addr(addr, axi_pkg::size_t'(SIZE_ALIGN) ); rand_success = std::randomize(id); assert(rand_success); rand_success = std::randomize(qos); assert(rand_success); // The random ID *must* be legalized with `legalize_id()` before the beat is sent! This is From 67d7db912856d741bae0d29e3fdc31fec24e6e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Tue, 23 Jun 2020 10:07:03 +0200 Subject: [PATCH 105/145] axi_lite_dw_downsizer: Add AXI4-Lite data width downsize conversion module --- .ci/Memora.yml | 39 ++- .gitlab-ci.yml | 15 +- Bender.yml | 2 + CHANGELOG.md | 3 + README.md | 91 ++--- scripts/run_vsim.sh | 7 + src/axi_lite_dw_converter.sv | 567 +++++++++++++++++++++++++++++++ test/axi_synth_bench.sv | 46 +++ test/tb_axi_lite_dw_converter.sv | 411 ++++++++++++++++++++++ 9 files changed, 1118 insertions(+), 63 deletions(-) create mode 100644 src/axi_lite_dw_converter.sv create mode 100644 test/tb_axi_lite_dw_converter.sv diff --git a/.ci/Memora.yml b/.ci/Memora.yml index 176afb4ff..deda96773 100644 --- a/.ci/Memora.yml +++ b/.ci/Memora.yml @@ -149,6 +149,32 @@ artifacts: outputs: - build/axi_iw_converter-%.tested + axi_lite_dw_converter-%: + inputs: + - Bender.yml + - include + - scripts/run_vsim.sh + - src/axi_pkg.sv + - src/axi_intf.sv + - src/axi_test.sv + - src/axi_lite_dw_converter.sv + - test/tb_axi_lite_dw_converter.sv + outputs: + - build/axi_lite_dw_converter-%.tested + + axi_lite_mailbox-%: + inputs: + - Bender.yml + - include + - scripts/run_vsim.sh + - src/axi_pkg.sv + - src/axi_intf.sv + - src/axi_test.sv + - src/axi_lite_mailbox.sv + - test/tb_axi_lite_mailbox.sv + outputs: + - build/axi_lite_mailbox-%.tested + axi_lite_regs-%: inputs: - Bender.yml @@ -188,19 +214,6 @@ artifacts: outputs: - build/axi_lite_to_axi-%.tested - axi_lite_mailbox-%: - inputs: - - Bender.yml - - include - - scripts/run_vsim.sh - - src/axi_pkg.sv - - src/axi_intf.sv - - src/axi_test.sv - - src/axi_lite_mailbox.sv - - test/tb_axi_lite_mailbox.sv - outputs: - - build/axi_lite_mailbox-%.tested - axi_lite_xbar-%: inputs: - Bender.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 15844b86e..2a34c9572 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -118,6 +118,16 @@ axi_iw_converter: variables: TEST_MODULE: axi_iw_converter +axi_lite_dw_converter: + <<: *run_vsim + variables: + TEST_MODULE: axi_lite_dw_converter + +axi_lite_mailbox: + <<: *run_vsim + variables: + TEST_MODULE: axi_lite_mailbox + axi_lite_regs: <<: *run_vsim variables: @@ -133,11 +143,6 @@ axi_lite_to_axi: variables: TEST_MODULE: axi_lite_to_axi -axi_lite_mailbox: - <<: *run_vsim - variables: - TEST_MODULE: axi_lite_mailbox - axi_lite_xbar: <<: *run_vsim variables: diff --git a/Bender.yml b/Bender.yml index 7a348ac14..5f74c1f1b 100644 --- a/Bender.yml +++ b/Bender.yml @@ -49,6 +49,7 @@ sources: - src/axi_isolate.sv - src/axi_join.sv - src/axi_lite_demux.sv + - src/axi_lite_dw_converter.sv - src/axi_lite_from_mem.sv - src/axi_lite_join.sv - src/axi_lite_lfsr.sv @@ -110,6 +111,7 @@ sources: - test/tb_axi_dw_upsizer.sv - test/tb_axi_fifo.sv - test/tb_axi_isolate.sv + - test/tb_axi_lite_dw_converter.sv - test/tb_axi_lite_mailbox.sv - test/tb_axi_lite_regs.sv - test/tb_axi_iw_converter.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index e6bb2ed08..2b4e834cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add `axi_lite_from_mem` and `axi_from_mem` acting like SRAMs making AXI4 requests downstream. - Add `axi_rw_join` and `axi_rw_split` to split/join AXI buses. - Add `#_width` functions returning the width of the AXI channels. +- Add `axi_lite_dw_converter`: Convert the data width of AXI4-Lite transactions. Emmits the + appropriate amount of downstream transactions to perform the whole requested access. + ### Changed - `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents diff --git a/README.md b/README.md index 012703744..807fad8ca 100644 --- a/README.md +++ b/README.md @@ -19,51 +19,52 @@ The **design and microarchitecture** of the modules in this repository is descri In addition to the documents linked in the following table, we are setting up [documentation auto-generated from inline docstrings](https://pulp-platform.github.io/axi/master). (Replace `master` in that URL with a tag to get the documentation for a specific version.) -| Name | Description | Doc | -|------------------------------------------------------|------------------------------------------------------------------------------------------------------|--------------------------------| -| [`axi_atop_filter`](src/axi_atop_filter.sv) | Filters atomic operations (ATOPs), i.e., write transactions that have a non-zero `aw_atop` value. | | -| [`axi_burst_splitter`](src/axi_burst_splitter.sv) | Split AXI4 burst transfers into single-beat transactions. | | -| [`axi_cdc`](src/axi_cdc.sv) | AXI clock domain crossing based on a Gray FIFO implementation. | | -| [`axi_cut`](src/axi_cut.sv) | Breaks all combinatorial paths between its input and output. | | -| [`axi_delayer`](src/axi_delayer.sv) | Synthesizable module which can (randomly) delays AXI channels. | | -| [`axi_demux`](src/axi_demux.sv) | Demultiplexes an AXI bus from one slave port to multiple master ports. | [Doc](doc/axi_demux.md) | -| [`axi_dw_converter`](src/axi_dw_converter.sv) | A data width converter between AXI interfaces of any data width. | | -| [`axi_dw_downsizer`](src/axi_dw_downsizer.sv) | A data width converter between a wide AXI master and a narrower AXI slave. | | -| [`axi_dw_upsizer`](src/axi_dw_upsizer.sv) | A data width converter between a narrow AXI master and a wider AXI slave. | | -| [`axi_err_slv`](src/axi_err_slv.sv) | Always responds with an AXI decode/slave error for transactions which are sent to it. | | -| [`axi_fifo`](src/axi_fifo.sv) | A Fifo for each AXI4 channel to buffer requests. | | -| [`axi_from_mem`](src/axi_from_mem.sv) | This module acts like an SRAM and makes AXI4 requests downstream. | | -| [`axi_id_prepend`](src/axi_id_prepend.sv) | This module prepends/strips the MSB from the AXI IDs. | | -| [`axi_id_remap`](src/axi_id_remap.sv) | Remap AXI IDs from wide IDs at the slave port to narrower IDs at the master port. | [Doc][doc.axi_id_remap] | -| [`axi_id_serialize`](src/axi_id_serialize.sv) | Reduce AXI IDs by serializing transactions when necessary. | [Doc][doc.axi_id_serialize] | -| [`axi_intf`](src/axi_intf.sv) | This file defines the interfaces we support. | | -| [`axi_isolate`](src/axi_isolate.sv) | A module that can isolate downstream slaves from receiving new AXI4 transactions. | | -| [`axi_iw_converter`](src/axi_iw_converter.sv) | Convert between any two AXI ID widths. | [Doc][doc.axi_iw_converter] | -| [`axi_join`](src/axi_join.sv) | A connector that joins two AXI interfaces. | | -| [`axi_lfsr`](src/axi_lfsr.sv) | AXI4-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | -| [`axi_lite_demux`](src/axi_lite_demux.sv) | Demultiplexes an AXI4-Lite bus from one slave port to multiple master ports. | [Doc](doc/axi_lite_demux.md) | -| [`axi_lite_from_mem`](src/axi_lite_from_mem.sv) | This module acts like an SRAM and makes AXI4-Lite requests downstream. | | -| [`axi_lite_join`](src/axi_lite_join.sv) | A connector that joins two AXI-Lite interfaces. | | -| [`axi_lite_lfsr`](src/axi_lite_lfsr.sv) | AXI4-Lite-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | -| [`axi_lite_mailbox`](src/axi_lite_mailbox.sv) | A AXI4-Lite Mailbox with two slave ports and usage triggered irq. | [Doc](doc/axi_lite_mailbox.md) | -| [`axi_lite_mux`](src/axi_lite_mux.sv) | Multiplexes AXI4-Lite slave ports down to one master port. | [Doc](doc/axi_lite_mux.md) | -| [`axi_lite_regs`](src/axi_lite_regs.sv) | AXI4-Lite registers with optional read-only and protection features. | [Doc][doc.axi_lite_regs] | -| [`axi_lite_to_apb`](src/axi_lite_to_apb.sv) | AXI4-Lite to APB4 protocol converter. | | -| [`axi_lite_to_axi`](src/axi_lite_to_axi.sv) | AXI4-Lite to AXI4 protocol converter. | | -| [`axi_lite_xbar`](src/axi_lite_xbar.sv) | Fully-connected AXI4-Lite crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_lite_xbar.md) | -| [`axi_modify_address`](src/axi_modify_address.sv) | A connector that allows addresses of AXI requests to be changed. | | -| [`axi_multicut`](src/axi_multicut.sv) | AXI register which can be used to relax timing pressure on long AXI buses. | | -| [`axi_mux`](src/axi_mux.sv) | Multiplexes the AXI4 slave ports down to one master port. | [Doc](doc/axi_mux.md) | -| [`axi_pkg`](src/axi_pkg.sv) | Contains AXI definitions, common structs, and useful helper functions. | | -| [`axi_rw_join`](src/axi_rw_join.sv) | Joins a read and a write slave into one single read / write master. | | -| [`axi_rw_split`](src/axi_rw_split.sv) | Splits a single read / write slave into one read and one write master. | | -| [`axi_serializer`](src/axi_serializer.sv) | Serializes transactions with different IDs to the same ID. | | -| [`axi_throttle`](src/axi_throttle.sv) | Limits the maximum number of outstanding transfers sent to the downstream logic. | | -| [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | -| [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | -| [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. Additional banked, interleaved, split variant. | | -| [`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. | | +| Name | Description | Doc | +|---------------------------------------------------------|------------------------------------------------------------------------------------------------------|----------------------------------| +| [`axi_atop_filter`](src/axi_atop_filter.sv) | Filters atomic operations (ATOPs), i.e., write transactions that have a non-zero `aw_atop` value. | | +| [`axi_burst_splitter`](src/axi_burst_splitter.sv) | Split AXI4 burst transfers into single-beat transactions. | | +| [`axi_cdc`](src/axi_cdc.sv) | AXI clock domain crossing based on a Gray FIFO implementation. | | +| [`axi_cut`](src/axi_cut.sv) | Breaks all combinatorial paths between its input and output. | | +| [`axi_delayer`](src/axi_delayer.sv) | Synthesizable module which can (randomly) delays AXI channels. | | +| [`axi_demux`](src/axi_demux.sv) | Demultiplexes an AXI bus from one slave port to multiple master ports. | [Doc](doc/axi_demux.md) | +| [`axi_dw_converter`](src/axi_dw_converter.sv) | A data width converter between AXI interfaces of any data width. | | +| [`axi_dw_downsizer`](src/axi_dw_downsizer.sv) | A data width converter between a wide AXI master and a narrower AXI slave. | | +| [`axi_dw_upsizer`](src/axi_dw_upsizer.sv) | A data width converter between a narrow AXI master and a wider AXI slave. | | +| [`axi_err_slv`](src/axi_err_slv.sv) | Always responds with an AXI decode/slave error for transactions which are sent to it. | | +| [`axi_fifo`](src/axi_fifo.sv) | A Fifo for each AXI4 channel to buffer requests. | | +| [`axi_from_mem`](src/axi_from_mem.sv) | This module acts like an SRAM and makes AXI4 requests downstream. | | +| [`axi_id_prepend`](src/axi_id_prepend.sv) | This module prepends/strips the MSB from the AXI IDs. | | +| [`axi_id_remap`](src/axi_id_remap.sv) | Remap AXI IDs from wide IDs at the slave port to narrower IDs at the master port. | [Doc][doc.axi_id_remap] | +| [`axi_id_serialize`](src/axi_id_serialize.sv) | Reduce AXI IDs by serializing transactions when necessary. | [Doc][doc.axi_id_serialize] | +| [`axi_intf`](src/axi_intf.sv) | This file defines the interfaces we support. | | +| [`axi_isolate`](src/axi_isolate.sv) | A module that can isolate downstream slaves from receiving new AXI4 transactions. | | +| [`axi_iw_converter`](src/axi_iw_converter.sv) | Convert between any two AXI ID widths. | [Doc][doc.axi_iw_converter] | +| [`axi_join`](src/axi_join.sv) | A connector that joins two AXI interfaces. | | +| [`axi_lfsr`](src/axi_lfsr.sv) | AXI4-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | +| [`axi_lite_demux`](src/axi_lite_demux.sv) | Demultiplexes an AXI4-Lite bus from one slave port to multiple master ports. | [Doc](doc/axi_lite_demux.md) | +| [`axi_lite_dw_converter`](src/axi_lite_dw_converter.sv) | A data width converter between two AXI-Lite busses | [Doc][doc.axi_lite_dw_converter] | +| [`axi_lite_from_mem`](src/axi_lite_from_mem.sv) | This module acts like an SRAM and makes AXI4-Lite requests downstream. | | +| [`axi_lite_join`](src/axi_lite_join.sv) | A connector that joins two AXI-Lite interfaces. | | +| [`axi_lite_lfsr`](src/axi_lite_lfsr.sv) | AXI4-Lite-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | +| [`axi_lite_mailbox`](src/axi_lite_mailbox.sv) | A AXI4-Lite Mailbox with two slave ports and usage triggered irq. | [Doc](doc/axi_lite_mailbox.md) | +| [`axi_lite_mux`](src/axi_lite_mux.sv) | Multiplexes AXI4-Lite slave ports down to one master port. | [Doc](doc/axi_lite_mux.md) | +| [`axi_lite_regs`](src/axi_lite_regs.sv) | AXI4-Lite registers with optional read-only and protection features. | [Doc][doc.axi_lite_regs] | +| [`axi_lite_to_apb`](src/axi_lite_to_apb.sv) | AXI4-Lite to APB4 protocol converter. | | +| [`axi_lite_to_axi`](src/axi_lite_to_axi.sv) | AXI4-Lite to AXI4 protocol converter. | | +| [`axi_lite_xbar`](src/axi_lite_xbar.sv) | Fully-connected AXI4-Lite crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_lite_xbar.md) | +| [`axi_modify_address`](src/axi_modify_address.sv) | A connector that allows addresses of AXI requests to be changed. | | +| [`axi_multicut`](src/axi_multicut.sv) | AXI register which can be used to relax timing pressure on long AXI buses. | | +| [`axi_mux`](src/axi_mux.sv) | Multiplexes the AXI4 slave ports down to one master port. | [Doc](doc/axi_mux.md) | +| [`axi_pkg`](src/axi_pkg.sv) | Contains AXI definitions, common structs, and useful helper functions. | | +| [`axi_rw_join`](src/axi_rw_join.sv) | Joins a read and a write slave into one single read / write master. | | +| [`axi_rw_split`](src/axi_rw_split.sv) | Splits a single read / write slave into one read and one write master. | | +| [`axi_serializer`](src/axi_serializer.sv) | Serializes transactions with different IDs to the same ID. | | +| [`axi_throttle`](src/axi_throttle.sv) | Limits the maximum number of outstanding transfers sent to the downstream logic. | | +| [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | +| [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | +| [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. Additional banked, interleaved, split variant. | | +| [`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 diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh index ad1312415..b9656ddf6 100755 --- a/scripts/run_vsim.sh +++ b/scripts/run_vsim.sh @@ -225,6 +225,13 @@ exec_test() { done done ;; + axi_lite_dw_converter) + for DWSLV in 32 64 128; do + for DWMST in 16 32 64; do + call_vsim tb_axi_lite_dw_converter -gTbAxiDataWidthSlv=$DWSLV -gTbAxiDataWidthMst=$DWMST + done + done + ;; *) call_vsim tb_$1 -t 1ns -coverage -voptargs="+acc +cover=bcesfx" ;; diff --git a/src/axi_lite_dw_converter.sv b/src/axi_lite_dw_converter.sv new file mode 100644 index 000000000..f70940674 --- /dev/null +++ b/src/axi_lite_dw_converter.sv @@ -0,0 +1,567 @@ +// Copyright 2020 ETH Zurich and 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: +// - Wolfgang Rönninger + +/// # AXI4-Lite data width downsize module. +/// +/// ## Down Conversion +/// +/// The module will be in this mode if `AxiSlvPortDataWidth > AxiMstPortDataWidth`. +/// The module does multiple transactions on the master port for each transaction of the salve port. +/// +/// The address on the master port will be aligned to the bus width, regardless what the input +/// address was. The number of transactions generated on the master port is equal to the +/// `DownsizeFactor = AxiSlvPortDataWidth / AxiMstPortDataWidth`. +/// +/// Eg: `AxiAddrWidth == 32'd16`, `AxiSlvPortDataWidth == 32'64`, `AxiMstPortDataWidth == 32'd32' +/// This is for write transactions. Reads are accessing the whole width of the slave port. +/// +/// +/// | EG NUM | SLV ADDR | SLV W DATA | SLV W STRB | MST ADDR | MST W DATA | MST W STRB | +/// |--------|----------|--------------------|------------|----------|------------|------------| +/// | 1 | 0x0000 | 0xBEEFBEEFAAAABBBB | 0xAB | 0x0000 | 0xAAAABBBB | 0xB | +/// | 1 | | | | 0x0004 | 0xBEEFBEEF | 0xA | +/// | | | | | | | | +/// | 2 | 0x0000 | 0xBEEFBEEFAAAABBBB | 0xF0 | 0x0000 | 0xAAAABBBB | 0x0 | +/// | 2 | | | | 0x0004 | 0xBEEFBEEF | 0xF | +/// | | | | | | | | +/// | 3 | 0x0004 | 0xBEEFBEEFAAAABBBB | 0xF0 | 0x0000 | 0xAAAABBBB | 0x0 | +/// | 3 | | | | 0x0004 | 0xBEEFBEEF | 0xF | +/// | | | | | | | | +/// | 4 | 0x0004 | 0xBEEFBEEFAAAABBBB | 0x0F | 0x0000 | 0xAAAABBBB | 0xF | +/// | 4 | | | | 0x0004 | 0xBEEFBEEF | 0x0 | +/// | | | | | | | | +/// | 5 | 0x0005 | 0xBEEFBE0000000000 | 0xE0 | 0x0000 | 0x00000000 | 0x0 | +/// | 5 | | | | 0x0004 | 0xBEEFBE00 | 0xE | +/// +/// Response field is aggregated (OR'ed) between the multiple requests made on the master port. +/// If one of the requests on the master port errors, the error response of the request +/// on the slave port will also signal an error. +/// +/// ## Up conversion +/// +/// The module will be in this mode if `AxiSlvPortDataWidth < AxiMstPortDataWidth`. +/// This mode will generate the same amount of transactions on the master port as on the slave port. +/// Data is replicated to match the bus width. Write strobes are silenced for the byte lanes not +/// written. +/// +/// ## Pass Through +/// +/// The module will be in this mode if `AxiSlvPortDataWidth == AxiMstPortDataWidth`. +/// Here the module passes through the slave port to the master port. +`include "common_cells/registers.svh" +module axi_lite_dw_converter #( + /// AXI4-Lite address width of the ports. + parameter int unsigned AxiAddrWidth = 32'd0, + /// AXI4-Lite data width of the slave port. + parameter int unsigned AxiSlvPortDataWidth = 32'd0, + /// AXI4-Lite data width of the master port. + parameter int unsigned AxiMstPortDataWidth = 32'd0, + /// AXI4-Lite AW channel struct type. This is for both ports the same. + parameter type axi_lite_aw_t = logic, + /// AXI4-Lite W channel struct type of the slave port. + parameter type axi_lite_slv_w_t = logic, + /// AXI4-Lite W channel struct type of the master port. + parameter type axi_lite_mst_w_t = logic, + /// AXI4-Lite B channel struct type. This is for both ports. + parameter type axi_lite_b_t = logic, + /// AXI4-Lite AR channel struct type. This is for both ports. + parameter type axi_lite_ar_t = logic, + /// AXI4-Lite R channel struct type of the slave port. + parameter type axi_lite_slv_r_t = logic, + /// AXI4-Lite R channel struct type of the master port. + parameter type axi_lite_mst_r_t = logic, + /// AXI4-Lite request struct of the slave port. + parameter type axi_lite_slv_req_t = logic, + /// AXI4-Lite response struct of the slave port. + parameter type axi_lite_slv_res_t = logic, + /// AXI4-Lite request struct of the master port. + parameter type axi_lite_mst_req_t = logic, + /// AXI4-Lite response struct of the master port. + parameter type axi_lite_mst_res_t = logic +) ( + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchrounous reset, active low. + input logic rst_ni, + /// Salve port, AXI4-Lite request. + input axi_lite_slv_req_t slv_req_i, + /// Salve port, AXI4-Lite response. + output axi_lite_slv_res_t slv_res_o, + /// Master port, AXI4-Lite request. + output axi_lite_mst_req_t mst_req_o, + /// Master port, AXI4-Lite response. + input axi_lite_mst_res_t mst_res_i +); + // Strobe parameter for the two AXI4-Lite ports. + localparam int unsigned AxiSlvPortStrbWidth = AxiSlvPortDataWidth / 32'd8; + localparam int unsigned AxiMstPortStrbWidth = AxiMstPortDataWidth / 32'd8; + typedef logic [AxiAddrWidth-1:0] addr_t; + + // AXI4-Lite downsizer + if (AxiSlvPortDataWidth > AxiMstPortDataWidth) begin : gen_downsizer + // The Downsize factor determines how often the data channel has to be multiplexed. + localparam int unsigned DownsizeFactor = AxiSlvPortDataWidth / AxiMstPortDataWidth; + // Selection width for choosing the byte lanes. + localparam int unsigned SelWidth = $clog2(DownsizeFactor); + // Type for the selection signal. + typedef logic [SelWidth-1:0] sel_t; + // Offset determines, which part of the address corresponds to the `w_chan_sel` signal. + localparam int unsigned SelOffset = $clog2(AxiMstPortStrbWidth); + + // Calculate the output address for the master port. + // `address`: The address as seen on the salve port. + // `sel`: T The current selection. + // `l_zero`: If set, the lowest bits are zero, for all generated addresses after the first. + function automatic addr_t out_address(input addr_t address, input sel_t sel); + out_address = address; + out_address[SelOffset+:SelWidth] = sel; + out_address[SelOffset-1:0] = SelOffset'(0); + endfunction : out_address + + // Write channels. + // Input spill register of the AW channel. + axi_lite_aw_t aw_chan_spill; + logic aw_chan_spill_valid, aw_chan_spill_ready; + + spill_register #( + .T ( axi_lite_aw_t ), + .Bypass ( 1'b0 ) + ) i_spill_register_aw ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_res_o.aw_ready ), + .data_i ( slv_req_i.aw ), + .valid_o ( aw_chan_spill_valid ), + .ready_i ( aw_chan_spill_ready ), + .data_o ( aw_chan_spill ) + ); + + sel_t aw_sel_q, aw_sel_d; + logic aw_sel_load; + // AW channel output assignment + always_comb begin : proc_aw_chan_oup + mst_req_o.aw = aw_chan_spill; + mst_req_o.aw.addr = out_address(aw_chan_spill.addr, aw_sel_q); + end + // Slave port aw is valid, if there is something in the spill register. + assign mst_req_o.aw_valid = aw_chan_spill_valid; + assign aw_chan_spill_ready = mst_res_i.aw_ready & (&aw_sel_q); + + assign aw_sel_load = mst_req_o.aw_valid & mst_res_i.aw_ready; + assign aw_sel_d = sel_t'(aw_sel_q + 1'b1); + `FFLARN(aw_sel_q, aw_sel_d, aw_sel_load, '0, clk_i, rst_ni) + + // Input spill register of the W channel. + axi_lite_slv_w_t w_chan_spill; + logic w_chan_spill_valid, w_chan_spill_ready; + spill_register #( + .T ( axi_lite_slv_w_t ), + .Bypass ( 1'b0 ) + ) i_spill_register_w ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.w_valid ), + .ready_o ( slv_res_o.w_ready ), + .data_i ( slv_req_i.w ), + .valid_o ( w_chan_spill_valid ), + .ready_i ( w_chan_spill_ready ), + .data_o ( w_chan_spill ) + ); + + // Data multiplexer on the W channel + sel_t w_sel_q, w_sel_d; + logic w_sel_load; + // W channel output assignment + assign mst_req_o.w = axi_lite_mst_w_t'{ + data: w_chan_spill.data[w_sel_q*AxiMstPortDataWidth+:AxiMstPortDataWidth], + strb: w_chan_spill.strb[w_sel_q*AxiMstPortStrbWidth+:AxiMstPortStrbWidth], + default: '0 + }; + assign mst_req_o.w_valid = w_chan_spill_valid; + assign w_chan_spill_ready = mst_res_i.w_ready & (&w_sel_q); + + assign w_sel_load = mst_req_o.w_valid & mst_res_i.w_ready; + assign w_sel_d = sel_t'(w_sel_q + 1'b1); + `FFLARN(w_sel_q, w_sel_d, w_sel_load, '0, clk_i, rst_ni) + + // B response aggregation + // Slave port B output is the aggregated error of the last few B responses. + sel_t b_sel_q, b_sel_d; + axi_pkg::resp_t b_resp_q, b_resp_d; + logic b_resp_load; + + assign slv_res_o.b = axi_lite_b_t'{ + resp: b_resp_q | mst_res_i.b.resp, + default: '0 + }; + // Output is valid, if it is the last b response for the wide W, we have something + // in the B FIFO and the B response is valid from the master port. + assign slv_res_o.b_valid = mst_res_i.b_valid & (&b_sel_q); + + // Assign the b_channel ready output. The master port is ready if something is in the + // B FIFO. Except, if it is the last one which should do a response on the slave port. + assign mst_req_o.b_ready = (&b_sel_q) ? slv_req_i.b_ready : 1'b1; + // B channel error response retention FF + assign b_sel_d = sel_t'(b_sel_q + 1'b1); + assign b_resp_d = (&b_sel_q) ? axi_pkg::RESP_OKAY : (b_resp_q | mst_res_i.b.resp); + assign b_resp_load = mst_res_i.b_valid & mst_req_o.b_ready; + `FFLARN(b_sel_q, b_sel_d, b_resp_load, '0, clk_i, rst_ni) + `FFLARN(b_resp_q, b_resp_d, b_resp_load, axi_pkg::RESP_OKAY, clk_i, rst_ni) + + // Read channels. + // Input spill register of the AW channel. + axi_lite_ar_t ar_chan_spill; + logic ar_chan_spill_valid, ar_chan_spill_ready; + + spill_register #( + .T ( axi_lite_ar_t ), + .Bypass ( 1'b0 ) + ) i_spill_register_ar ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_res_o.ar_ready ), + .data_i ( slv_req_i.ar ), + .valid_o ( ar_chan_spill_valid ), + .ready_i ( ar_chan_spill_ready ), + .data_o ( ar_chan_spill ) + ); + + sel_t ar_sel_q, ar_sel_d; + logic ar_sel_load; + // AR channel output assignment + always_comb begin : proc_ar_chan_oup + mst_req_o.ar = ar_chan_spill; + mst_req_o.ar.addr = out_address(ar_chan_spill.addr, ar_sel_q); + end + // Slave port aw is valid, if there is something in the spill register. + assign mst_req_o.ar_valid = ar_chan_spill_valid; + assign ar_chan_spill_ready = mst_res_i.ar_ready & (&ar_sel_q); + + assign ar_sel_load = mst_req_o.ar_valid & mst_res_i.ar_ready; + assign ar_sel_d = sel_t'(ar_sel_q + 1'b1); + `FFLARN(ar_sel_q, ar_sel_d, ar_sel_load, '0, clk_i, rst_ni) + + // Responses have to be aggregated, one FF less, as the last data is feed directly through. + sel_t r_sel_q, r_sel_d; + logic r_sel_load; + axi_lite_mst_r_t [DownsizeFactor-2:0] r_chan_mst_q; + logic [DownsizeFactor-2:0] r_chan_mst_load; + for (genvar i = 0; unsigned'(i) < (DownsizeFactor-1); i++) begin : gen_r_chan_ff + assign r_chan_mst_load[i] = (sel_t'(i) == r_sel_q) & mst_res_i.r_valid & mst_req_o.r_ready; + `FFLARN(r_chan_mst_q[i], mst_res_i.r, r_chan_mst_load[i], axi_lite_mst_r_t'{default: '0}, clk_i, rst_ni) + end + assign r_sel_load = mst_res_i.r_valid & mst_req_o.r_ready; + assign r_sel_d = sel_t'(r_sel_q + 1'b1); + `FFLARN(r_sel_q, r_sel_d, r_sel_load, '0, clk_i, rst_ni) + + always_comb begin : proc_r_chan_oup + slv_res_o.r = axi_lite_slv_r_t'{ + resp: mst_res_i.r.resp, + default: '0 + }; + // Response is the OR of all responses + for (int unsigned i = 0; i < (DownsizeFactor-1); i++) begin + slv_res_o.r.resp = slv_res_o.r.resp | r_chan_mst_q[i].resp; + slv_res_o.r.data[i*AxiMstPortDataWidth+:AxiMstPortDataWidth] = r_chan_mst_q[i].data; + end + // The highest bits of the data can be directly the master port. + slv_res_o.r.data[(DownsizeFactor-1)*AxiMstPortDataWidth+:AxiMstPortDataWidth] = + mst_res_i.r.data; + end + + assign slv_res_o.r_valid = (&r_sel_q) ? mst_res_i.r_valid : 1'b0; + assign mst_req_o.r_ready = (&r_sel_q) ? slv_req_i.r_ready : 1'b1; + + end else if (AxiMstPortDataWidth > AxiSlvPortDataWidth) begin : gen_upsizer + // The upsize factor determines the amount of replication. + localparam int unsigned UpsizeFactor = AxiMstPortDataWidth / AxiSlvPortDataWidth; + + // Selection type and offset for the address + localparam int unsigned SelOffset = $clog2(AxiSlvPortStrbWidth); + localparam int unsigned SelWidth = $clog2(UpsizeFactor); + typedef logic [SelWidth-1:0] sel_t; + + // AW channel can be passed through, however block handshake if FIFO is full. + assign mst_req_o.aw = slv_req_i.aw; + // Lock the valid on the master port if it has been given. + logic lock_aw_q, lock_aw_d, load_aw_lock; + // W channel needs a FIFO to determine the silencing of the strobe signal. + logic w_full, w_empty, w_push, w_pop; + sel_t aw_sel, w_sel; + + // AW channel handshake control + always_comb begin : proc_aw_handshake + // default assignment + load_aw_lock = 1'b0; // the FF is toggling back and forth when loaded. + mst_req_o.aw_valid = 1'b0; + slv_res_o.aw_ready = 1'b0; + w_push = 1'b0; + + if (lock_aw_q) begin + mst_req_o.aw_valid = 1'b1; + slv_res_o.aw_ready = mst_res_i.aw_ready; + if (mst_res_i.aw_ready) begin + load_aw_lock = 1'b1; + end + end else begin + // Only connect handshake if there is space in the FIFO + if (!w_full) begin + mst_req_o.aw_valid = slv_req_i.aw_valid; + slv_res_o.aw_ready = mst_res_i.aw_ready; + // If there is a valid on the slave port, push the FIFO + if (slv_req_i.aw_valid) begin + w_push = 1'b1; + // When no transaction, lock AW + if (!mst_res_i.aw_ready) begin + load_aw_lock = 1'b1; + end + end + end + end + end + assign lock_aw_d = ~lock_aw_q; + `FFLARN(lock_aw_q, lock_aw_d, load_aw_lock, 1'b0, clk_i, rst_ni) + + // The selection comes from part of the AW address. + assign aw_sel = sel_t'(slv_req_i.aw.addr >> SelOffset); + + fifo_v3 #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( UpsizeFactor ), + .dtype ( sel_t ) + ) i_fifo_w_sel ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( w_full ), + .empty_o ( w_empty ), + .usage_o ( /*not used*/ ), + .data_i ( aw_sel ), + .push_i ( w_push ), + .data_o ( w_sel ), + .pop_i ( w_pop ) + ); + // Pop if there is a W transaction on the master port. + assign w_pop = mst_req_o.w_valid & mst_res_i.w_ready; + + // Replicate Data but silence strobe signal. + assign mst_req_o.w = axi_lite_mst_w_t'{ + data: {UpsizeFactor{slv_req_i.w.data}}, + strb: {AxiMstPortStrbWidth{1'b0}} | (slv_req_i.w.strb << (w_sel * AxiSlvPortStrbWidth)), + default: '0 + }; + + // Connect W handshake if the selection is in the FIFO + assign mst_req_o.w_valid = slv_req_i.w_valid & ~w_empty; + assign slv_res_o.w_ready = mst_res_i.w_ready & ~w_empty; + + + // B channel can be passed through + assign slv_res_o.b = mst_res_i.b; + assign slv_res_o.b_valid = mst_res_i.b_valid; + assign mst_req_o.b_ready = slv_req_i.b_ready; + + + // AR channel can be passed through, however block handshake if FIFO is full. + assign mst_req_o.ar = slv_req_i.ar; + // Lock the valid on the master port if it has been given. + logic lock_ar_q, lock_ar_d, load_ar_lock; + // W channel needs a FIFO to determine the silencing of the strobe signal. + logic r_full, r_empty, r_push, r_pop; + sel_t ar_sel, r_sel; + + // AW channel handshake control + always_comb begin : proc_ar_handshake + // default assignment + load_ar_lock = 1'b0; // the FF is toggling back and forth when loaded. + mst_req_o.ar_valid = 1'b0; + slv_res_o.ar_ready = 1'b0; + r_push = 1'b0; + + if (lock_ar_q) begin + mst_req_o.ar_valid = 1'b1; + slv_res_o.ar_ready = mst_res_i.ar_ready; + if (mst_res_i.ar_ready) begin + load_ar_lock = 1'b1; + end + end else begin + // Only connect handshake if there is space in the FIFO + if (!r_full) begin + mst_req_o.ar_valid = slv_req_i.ar_valid; + slv_res_o.ar_ready = mst_res_i.ar_ready; + // If there is a valid on the slave port, push the FIFO + if (slv_req_i.ar_valid) begin + r_push = 1'b1; + // When no transaction, lock AW + if (!mst_res_i.ar_ready) begin + load_ar_lock = 1'b1; + end + end + end + end + end + assign lock_ar_d = ~lock_ar_q; + `FFLARN(lock_ar_q, lock_ar_d, load_ar_lock, 1'b0, clk_i, rst_ni) + + // The selection comes from part of the AW address. + assign ar_sel = sel_t'(slv_req_i.ar.addr >> SelOffset); + + fifo_v3 #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( UpsizeFactor ), + .dtype ( sel_t ) + ) i_fifo_r_sel ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( r_full ), + .empty_o ( r_empty ), + .usage_o ( /*not used*/ ), + .data_i ( ar_sel ), + .push_i ( r_push ), + .data_o ( r_sel ), + .pop_i ( r_pop ) + ); + // Pop if there is a R transaction on the slave port. + assign r_pop = slv_res_o.r_valid & slv_req_i.r_ready; + + // R channel has to be cut out + assign slv_res_o.r = axi_lite_slv_r_t'{ + data: mst_res_i.r.data[(r_sel*AxiSlvPortDataWidth)+:AxiSlvPortDataWidth], + resp: mst_res_i.r.resp, + default: '0 + }; + // Connect R handshake if there is something in the FIFO. + assign slv_res_o.r_valid = mst_res_i.r_valid & ~r_empty; + assign mst_req_o.r_ready = slv_req_i.r_ready & ~r_empty; + + end else begin : gen_passthrough + assign mst_req_o = slv_req_i; + assign slv_res_o = mst_res_i; + end + + // Assertions, check params + // pragma translate_off + `ifndef VERILATOR + initial begin + assume (AxiAddrWidth > 0) else $fatal(1, "AXI address width has to be > 0"); + assume (AxiSlvPortDataWidth > 8) else $fatal(1, "AxiSlvPortDataWidth has to be > 8"); + assume (AxiMstPortDataWidth > 8) else $fatal(1, "AxiMstPortDataWidth has to be > 8"); + assume ($onehot(AxiSlvPortDataWidth)) else $fatal(1, "AxiSlvPortDataWidth must be power of 2"); + assume ($onehot(AxiMstPortDataWidth)) else $fatal(1, "AxiMstPortDataWidth must be power of 2"); + end + default disable iff (~rst_ni); + stable_aw: assert property (@(posedge clk_i) + (mst_req_o.aw_valid && !mst_res_i.aw_ready) |=> $stable(mst_req_o.aw)) else + $fatal(1, "AW must remain stable until handshake happened."); + stable_w: assert property (@(posedge clk_i) + (mst_req_o.w_valid && !mst_res_i.w_ready) |=> $stable(mst_req_o.w)) else + $fatal(1, "W must remain stable until handshake happened."); + stable_b: assert property (@(posedge clk_i) + (slv_res_o.b_valid && !slv_req_i.b_ready) |=> $stable(slv_res_o.b)) else + $fatal(1, "B must remain stable until handshake happened."); + stable_ar: assert property (@(posedge clk_i) + (mst_req_o.ar_valid && !mst_res_i.ar_ready) |=> $stable(mst_req_o.ar)) else + $fatal(1, "AR must remain stable until handshake happened."); + stable_r: assert property (@(posedge clk_i) + (slv_res_o.r_valid && !slv_req_i.r_ready) |=> $stable(slv_res_o.r)) else + $fatal(1, "R must remain stable until handshake happened."); + `endif + // pragma translate_on +endmodule + +/// Interface wrapper for `axi_lite_dw_converter`. +`include "axi/typedef.svh" +`include "axi/assign.svh" +module axi_lite_dw_converter_intf #( + /// AXI4-Lite address width of the ports. + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + /// AXI4-Lite data width of the slave port. + parameter int unsigned AXI_SLV_PORT_DATA_WIDTH = 32'd0, + /// AXI4-Lite data width of the master port. + parameter int unsigned AXI_MST_PORT_DATA_WIDTH = 32'd0 +) ( + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchrounous reset, active low. + input logic rst_ni, + /// Slave port interface. + AXI_LITE.Slave slv, + /// Master port interface. + AXI_LITE.Master mst +); + // AXI configuration + localparam int unsigned AxiStrbWidthSlv = AXI_SLV_PORT_DATA_WIDTH / 32'd8; + localparam int unsigned AxiStrbWidthMst = AXI_MST_PORT_DATA_WIDTH / 32'd8; + // Type definitions + typedef logic [AXI_ADDR_WIDTH-1:0] lite_addr_t; + typedef logic [AXI_SLV_PORT_DATA_WIDTH-1:0] lite_data_slv_t; + typedef logic [AxiStrbWidthSlv-1:0] lite_strb_slv_t; + typedef logic [AXI_MST_PORT_DATA_WIDTH-1:0] lite_data_mst_t; + typedef logic [AxiStrbWidthMst-1:0] lite_strb_mst_t; + + + `AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_lite_t, lite_addr_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_slv_t, lite_data_slv_t, lite_strb_slv_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_mst_t, lite_data_mst_t, lite_strb_mst_t) + `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_lite_t) + + `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_lite_t, lite_addr_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_slv_t, lite_data_slv_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_mst_t, lite_data_mst_t) + + + `AXI_LITE_TYPEDEF_REQ_T(req_lite_slv_t, aw_chan_lite_t, w_chan_lite_slv_t, ar_chan_lite_t) + `AXI_LITE_TYPEDEF_RESP_T(res_lite_slv_t, b_chan_lite_t, r_chan_lite_slv_t) + + `AXI_LITE_TYPEDEF_REQ_T(req_lite_mst_t, aw_chan_lite_t, w_chan_lite_mst_t, ar_chan_lite_t) + `AXI_LITE_TYPEDEF_RESP_T(res_lite_mst_t, b_chan_lite_t, r_chan_lite_mst_t) + + req_lite_slv_t slv_req; + res_lite_slv_t slv_res; + req_lite_mst_t mst_req; + res_lite_mst_t mst_res; + + `AXI_LITE_ASSIGN_TO_REQ(slv_req, slv) + `AXI_LITE_ASSIGN_FROM_RESP(slv, slv_res) + `AXI_LITE_ASSIGN_FROM_REQ(mst, mst_req) + `AXI_LITE_ASSIGN_TO_RESP(mst_res, mst) + + axi_lite_dw_converter #( + .AxiAddrWidth ( AXI_ADDR_WIDTH ), + .AxiSlvPortDataWidth ( AXI_SLV_PORT_DATA_WIDTH ), + .AxiMstPortDataWidth ( AXI_MST_PORT_DATA_WIDTH ), + .axi_lite_aw_t ( aw_chan_lite_t ), + .axi_lite_slv_w_t ( w_chan_lite_slv_t ), + .axi_lite_mst_w_t ( w_chan_lite_mst_t ), + .axi_lite_b_t ( b_chan_lite_t ), + .axi_lite_ar_t ( ar_chan_lite_t ), + .axi_lite_slv_r_t ( r_chan_lite_slv_t ), + .axi_lite_mst_r_t ( r_chan_lite_mst_t ), + .axi_lite_slv_req_t ( req_lite_slv_t ), + .axi_lite_slv_res_t ( res_lite_slv_t ), + .axi_lite_mst_req_t ( req_lite_mst_t ), + .axi_lite_mst_res_t ( res_lite_mst_t ) + ) i_axi_lite_dw_converter ( + .clk_i, + .rst_ni, + .slv_req_i ( slv_req ), + .slv_res_o ( slv_res ), + .mst_req_o ( mst_req ), + .mst_res_i ( mst_res ) + ); +endmodule diff --git a/test/axi_synth_bench.sv b/test/axi_synth_bench.sv index edc77d4b4..334846abc 100644 --- a/test/axi_synth_bench.sv +++ b/test/axi_synth_bench.sv @@ -192,7 +192,18 @@ module axi_synth_bench ( end end + // AXI4-Lite DW converter + for (genvar i = 0; i < 3; i++) begin + for (genvar j = 0; j < 3; j++) begin + localparam int unsigned SLV_DW[3] = {32, 64, 128}; + localparam int unsigned MST_DW[3] = {16, 32, 64}; + synth_axi_lite_dw_converter #( + .AXI_SLV_PORT_DATA_WIDTH (SLV_DW[i]), + .AXI_MST_PORT_DATA_WIDTH (MST_DW[j]) + ) i_axi_lite_dw_converter (.*); + end + end endmodule @@ -680,6 +691,7 @@ module synth_axi_iw_converter # ( ); endmodule + module synth_axi_to_mem_banked #( parameter int unsigned AxiDataWidth = 32'd0, parameter int unsigned BankNum = 32'd0, @@ -747,3 +759,37 @@ module synth_axi_to_mem_banked #( ); endmodule + + +module synth_axi_lite_dw_converter #( + parameter int unsigned AXI_SLV_PORT_DATA_WIDTH = 32'd0, + parameter int unsigned AXI_MST_PORT_DATA_WIDTH = 32'd0 +) ( + input logic clk_i, + input logic rst_ni +); + + localparam int unsigned AXI_ADDR_WIDTH = 32'd64; + + AXI_LITE #( + .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ), + .AXI_DATA_WIDTH ( AXI_SLV_PORT_DATA_WIDTH ) + ) slv_intf (); + + AXI_LITE #( + .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ), + .AXI_DATA_WIDTH ( AXI_MST_PORT_DATA_WIDTH ) + ) mst_intf (); + + axi_lite_dw_converter_intf #( + .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ), + .AXI_SLV_PORT_DATA_WIDTH ( AXI_SLV_PORT_DATA_WIDTH ), + .AXI_MST_PORT_DATA_WIDTH ( AXI_MST_PORT_DATA_WIDTH ) + ) i_axi_lite_dw_converter_intf ( + .clk_i, + .rst_ni, + .slv ( slv_intf ), + .mst ( mst_intf ) + ); + +endmodule diff --git a/test/tb_axi_lite_dw_converter.sv b/test/tb_axi_lite_dw_converter.sv new file mode 100644 index 000000000..68afd2062 --- /dev/null +++ b/test/tb_axi_lite_dw_converter.sv @@ -0,0 +1,411 @@ +// Copyright (c) 2020 ETH Zurich and 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. + +// Author: Wolfgang Roenninger + + +`include "axi/typedef.svh" +`include "axi/assign.svh" +/// Testbench for the AXI4-Lite data width conversion module. +module tb_axi_lite_dw_converter #( + /// Address width of the AXI4-Lite ports. + parameter int unsigned TbAxiAddrWidth = 32'd32, + /// Data Width of the slave port. + parameter int unsigned TbAxiDataWidthSlv = 32'd32, + /// Data Width of the master port. + parameter int unsigned TbAxiDataWidthMst = 32'd128, + /// Number of write transactions. + parameter int unsigned NumWrites = 32'd10000, + /// Number of read tranactions. + parameter int unsigned NumReads = 32'd10000 +); + // Random master no Transactions + // timing parameters + localparam time CyclTime = 10ns; + localparam time ApplTime = 2ns; + localparam time TestTime = 8ns; + // AXI configuration + localparam int unsigned TbAxiStrbWidthSlv = TbAxiDataWidthSlv / 32'd8; + localparam int unsigned TbAxiStrbWidthMst = TbAxiDataWidthMst / 32'd8; + // Type definitions + typedef logic [TbAxiAddrWidth-1:0] addr_t; + typedef logic [TbAxiDataWidthSlv-1:0] data_slv_t; + typedef logic [TbAxiStrbWidthSlv-1:0] strb_slv_t; + typedef logic [TbAxiDataWidthMst-1:0] data_mst_t; + typedef logic [TbAxiStrbWidthMst-1:0] strb_mst_t; + + + `AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_lite_t, addr_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_slv_t, data_slv_t, strb_slv_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_mst_t, data_mst_t, strb_mst_t) + `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_lite_t) + + `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_lite_t, addr_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_slv_t, data_slv_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_mst_t, data_mst_t) + + + `AXI_LITE_TYPEDEF_REQ_T(req_lite_slv_t, aw_chan_lite_t, w_chan_lite_slv_t, ar_chan_lite_t) + `AXI_LITE_TYPEDEF_RESP_T(res_lite_slv_t, b_chan_lite_t, r_chan_lite_slv_t) + + `AXI_LITE_TYPEDEF_REQ_T(req_lite_mst_t, aw_chan_lite_t, w_chan_lite_mst_t, ar_chan_lite_t) + `AXI_LITE_TYPEDEF_RESP_T(res_lite_mst_t, b_chan_lite_t, r_chan_lite_mst_t) + + + typedef axi_test::axi_lite_rand_master #( + // AXI interface parameters + .AW ( TbAxiAddrWidth ), + .DW ( TbAxiDataWidthSlv ), + // Stimuli application and test time + .TA ( ApplTime ), + .TT ( TestTime ), + .MIN_ADDR ( 0 ), + .MAX_ADDR ( '1 ), + .MAX_READ_TXNS ( 100 ), + .MAX_WRITE_TXNS ( 100 ) + ) rand_lite_master_t; + typedef axi_test::axi_lite_rand_slave #( + // AXI interface parameters + .AW ( TbAxiAddrWidth ), + .DW ( TbAxiDataWidthMst ), + // Stimuli application and test time + .TA ( ApplTime ), + .TT ( TestTime ) + ) rand_lite_slave_t; + + // ------------- + // DUT signals + // ------------- + logic clk; + // DUT signals + logic rst_n; + logic end_of_sim; + + // master structs + req_lite_slv_t master_req; + res_lite_slv_t master_res; + + // slave structs + req_lite_mst_t slave_req; + res_lite_mst_t slave_res; + + // ------------------------------- + // AXI Interfaces + // ------------------------------- + AXI_LITE #( + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidthSlv ) + ) master (); + AXI_LITE_DV #( + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidthSlv ) + ) master_dv (clk); + `AXI_LITE_ASSIGN(master, master_dv) + `AXI_LITE_ASSIGN_TO_REQ(master_req, master) + `AXI_LITE_ASSIGN_TO_RESP(master_res, master) + + AXI_LITE #( + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidthMst ) + ) slave (); + AXI_LITE_DV #( + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidthMst ) + ) slave_dv (clk); + `AXI_LITE_ASSIGN(slave_dv, slave) + `AXI_LITE_ASSIGN_TO_REQ(slave_req, slave) + `AXI_LITE_ASSIGN_TO_RESP(slave_res, slave) + // ------------------------------- + // AXI Rand Masters and Slaves + // ------------------------------- + // Masters control simulation run time + initial begin : proc_generate_traffic + automatic rand_lite_master_t lite_axi_master = new ( master_dv, "MST_0"); + automatic data_slv_t data = '0; + automatic axi_pkg::resp_t resp = '0; + end_of_sim <= 1'b0; + lite_axi_master.reset(); + @(posedge rst_n); + lite_axi_master.write(32'h0000_1100, axi_pkg::prot_t'('0), data_slv_t'(64'hDEADBEEFDEADBEEF), + strb_slv_t'(8'hFF), resp); + lite_axi_master.read(32'h0000_e100, axi_pkg::prot_t'('0), data, resp); + lite_axi_master.run(NumReads, NumWrites); + end_of_sim <= 1'b1; + end + + initial begin : proc_recieve_traffic + automatic rand_lite_slave_t lite_axi_slave = new( slave_dv , "SLV_0"); + lite_axi_slave.reset(); + @(posedge rst_n); + lite_axi_slave.run(); + end + + // FIFOs for sampling the channels + // Salve Port + aw_chan_lite_t fifo_slv_aw[$]; + w_chan_lite_slv_t fifo_slv_w[$]; + b_chan_lite_t fifo_slv_b[$]; + ar_chan_lite_t fifo_slv_ar[$]; + r_chan_lite_slv_t fifo_slv_r[$]; + + aw_chan_lite_t fifo_mst_aw[$]; + w_chan_lite_mst_t fifo_mst_w[$]; + b_chan_lite_t fifo_mst_b[$]; + ar_chan_lite_t fifo_mst_ar[$]; + r_chan_lite_mst_t fifo_mst_r[$]; + // Sampling processes + initial begin : proc_sample + forever begin + @(posedge clk); + #TestTime; + if (rst_n) begin + if (master_req.aw_valid && master_res.aw_ready) begin + fifo_slv_aw.push_back(master_req.aw); + end + if (master_req.w_valid && master_res.w_ready) begin + fifo_slv_w.push_back(master_req.w); + end + if (master_res.b_valid && master_req.b_ready) begin + fifo_slv_b.push_back(master_res.b); + end + if (master_req.ar_valid && master_res.ar_ready) begin + fifo_slv_ar.push_back(master_req.ar); + end + if (master_res.r_valid && master_req.r_ready) begin + fifo_slv_r.push_back(master_res.r); + end + if (slave_req.aw_valid && slave_res.aw_ready) begin + fifo_mst_aw.push_back(slave_req.aw); + end + if (slave_req.w_valid && slave_res.w_ready) begin + fifo_mst_w.push_back(slave_req.w); + end + if (slave_res.b_valid && slave_req.b_ready) begin + fifo_mst_b.push_back(slave_res.b); + end + if (slave_req.ar_valid && slave_res.ar_ready) begin + fifo_mst_ar.push_back(slave_req.ar); + end + if (slave_res.r_valid && slave_req.r_ready) begin + fifo_mst_r.push_back(slave_res.r); + end + end + end + end + + if (TbAxiDataWidthMst < TbAxiDataWidthSlv) begin : gen_down_gm + localparam int unsigned DataDivFactor = TbAxiDataWidthSlv / TbAxiDataWidthMst; + localparam int unsigned AddrShift = $clog2(TbAxiDataWidthMst/8); + localparam int unsigned SelWidth = $clog2(DataDivFactor); + typedef logic [SelWidth-1:0] sel_t; + aw_chan_lite_t fifo_exp_aw[$]; + w_chan_lite_mst_t fifo_exp_w[$]; + ar_chan_lite_t fifo_exp_ar[$]; + + // Golden model for down conversion + initial begin : proc_write_exp_gen + automatic aw_chan_lite_t chan_aw, exp_aw; + automatic w_chan_lite_slv_t chan_w; + automatic w_chan_lite_mst_t exp_w; + automatic int unsigned num_vectors; // Number of expected transactions generated + automatic addr_t exp_addr; + automatic sel_t w_sel; + + forever begin + wait((fifo_slv_aw.size() > 0) && (fifo_slv_w.size() > 0)); + chan_aw = fifo_slv_aw.pop_front(); + chan_w = fifo_slv_w.pop_front(); + exp_addr = chan_aw.addr; + exp_addr = exp_addr >> (AddrShift + SelWidth); + for (int unsigned i = 0; i < DataDivFactor; i++) begin + // Generate expected AW vector + exp_aw = aw_chan_lite_t'{ + addr: ((exp_addr << SelWidth) + i) << AddrShift, + prot: chan_aw.prot + }; + fifo_exp_aw.push_back(exp_aw); + // Generate expected W vector + exp_w = w_chan_lite_mst_t'{ + data: chan_w.data[i*TbAxiDataWidthMst+:TbAxiDataWidthMst], + strb: chan_w.strb[i*TbAxiStrbWidthMst+:TbAxiStrbWidthMst] + }; + fifo_exp_w.push_back(exp_w); + end + end + end + + initial begin : proc_aw_exp_check + automatic aw_chan_lite_t chan_aw, exp_aw; + forever begin + wait((fifo_exp_aw.size() > 0) && (fifo_mst_aw.size() > 0)); + chan_aw = fifo_mst_aw.pop_front(); + exp_aw = fifo_exp_aw.pop_front(); + assert (chan_aw.addr == exp_aw.addr) else + $error("Master port> AW.addr is not expected: EXP: %h ACT:%h", + exp_aw.addr, chan_aw.addr); + assert (chan_aw.prot == exp_aw.prot) else + $error("Master port> AW.prot is not expected: EXP: %h ACT:%h", + exp_aw.prot, chan_aw.prot); + end + end + initial begin : proc_w_exp_check + automatic w_chan_lite_mst_t chan_w, exp_w; + forever begin + wait((fifo_exp_w.size() > 0) && (fifo_mst_w.size() > 0)); + chan_w = fifo_mst_w.pop_front(); + exp_w = fifo_exp_w.pop_front(); + assert (chan_w.data == exp_w.data) else + $error("Master port> W.data is not expected: EXP: %h ACT:%h", + exp_w.data, chan_w.data); + assert (chan_w.strb == exp_w.strb) else + $error("Master port> W.strb is not expected: EXP: %h ACT:%h", + exp_w.strb, chan_w.strb); + end + end + initial begin : proc_b_exp_check + automatic b_chan_lite_t exp_b, act_b; + forever begin + // wait until there are this many B responses in the sampling FIFO + wait(fifo_mst_b.size() >= DataDivFactor); + exp_b = axi_pkg::RESP_OKAY; + for (int unsigned i = 0; i < DataDivFactor; i++) begin + act_b = fifo_mst_b.pop_front(); + exp_b = exp_b | act_b; + end + // Do the check + wait(fifo_slv_b.size() > 0); + act_b = fifo_slv_b.pop_front(); + assert(exp_b.resp == act_b.resp) else + $error("Slave port> B.resp is not expected: EXP: %h ACT: %h", exp_b.resp, act_b.resp); + end + end + initial begin : proc_ar_exp_check + automatic ar_chan_lite_t chan_ar, exp_ar; + forever begin + wait((fifo_exp_ar.size() > 0) && (fifo_mst_ar.size() > 0)); + chan_ar = fifo_mst_ar.pop_front(); + exp_ar = fifo_exp_ar.pop_front(); + assert (chan_ar.addr == exp_ar.addr) else + $error("Master port> AR.addr is not expected: EXP: %h ACT:%h", + exp_ar.addr, chan_ar.addr); + assert (chan_ar.prot == exp_ar.prot) else + $error("Master port> AR.prot is not expected: EXP: %h ACT:%h", + exp_ar.prot, chan_ar.prot); + end + end + initial begin : proc_r_exp_check + automatic r_chan_lite_slv_t exp_r, act_r; + automatic r_chan_lite_mst_t mst_r; + forever begin + wait(fifo_slv_r.size() > 0); + act_r = fifo_slv_r.pop_front(); + exp_r = r_chan_lite_slv_t'{default: '0}; + // Build the expected R response from the master port fifo. + for (int unsigned i = 0; i < DataDivFactor; i++) begin + mst_r = fifo_mst_r.pop_front(); + exp_r.data[i*TbAxiDataWidthMst+:TbAxiDataWidthMst] = mst_r.data; + exp_r.resp = exp_r.resp | mst_r.resp; + end + assert (act_r.data == exp_r.data) else + $error("Slave port> R.data is not expected: EXP: %h ACT:%h", + exp_r.data, act_r.data); + assert (act_r.resp == exp_r.resp) else + $error("Slave port> R.resp is not expected: EXP: %h ACT:%h", + exp_r.resp, act_r.resp); + end + end + end else if (TbAxiDataWidthMst == TbAxiDataWidthSlv) begin + initial begin : proc_passthrough_check + forever begin + @(posedge clk); + #TestTime; + if (rst_n) begin + assert (master_req == slave_req); + assert (master_res == slave_res); + end + end + end + end else begin + // Upsizer + localparam int unsigned DataMultFactor = TbAxiDataWidthMst / TbAxiDataWidthSlv; + localparam int unsigned AddrShift = $clog2(TbAxiDataWidthSlv/8); + localparam int unsigned SelWidth = $clog2(DataMultFactor); + typedef logic [SelWidth-1:0] sel_t; + + aw_chan_lite_t fifo_exp_aw[$]; + w_chan_lite_mst_t fifo_exp_w[$]; + ar_chan_lite_t fifo_exp_ar[$]; + + + initial begin : proc_aw_check + automatic aw_chan_lite_t aw_chan; + automatic w_chan_lite_slv_t w_chan; + automatic w_chan_lite_mst_t exp_w; + automatic addr_t exp_addr; + automatic sel_t w_sel; + + forever begin + wait((fifo_slv_aw.size() > 0) && (fifo_slv_w.size() > 0)); + aw_chan = fifo_slv_aw.pop_front(); + w_chan = fifo_slv_w.pop_front(); + fifo_exp_aw.push_back(aw_chan); + // Calculate the expected W channel. + + end + end + + + + initial begin : proc_b_check + automatic b_chan_lite_t b_act, b_exp; + forever begin + wait((fifo_slv_r.size() > 0) && (fifo_mst_r.size() > 0)); + b_act = fifo_slv_b.pop_front(); + b_exp = fifo_mst_b.pop_front(); + assert (b_act == b_exp) else $error("Slave port> B.resp is not expected: EXP: %h ACT:%h", + b_exp.resp, b_act.resp); + end + end + + end + + + initial begin : proc_stop_sim + wait (end_of_sim); + repeat (1000) @(posedge clk); + $display("Simulation stopped as all Masters transferred their data, Success.",); + $stop(); + end + + //----------------------------------- + // Clock generator + //----------------------------------- + clk_rst_gen #( + .ClkPeriod ( CyclTime ), + .RstClkCycles( 5 ) + ) i_clk_gen ( + .clk_o (clk), + .rst_no(rst_n) + ); + + //----------------------------------- + // DUT + //----------------------------------- + axi_lite_dw_converter_intf #( + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_SLV_PORT_DATA_WIDTH ( TbAxiDataWidthSlv ), + .AXI_MST_PORT_DATA_WIDTH ( TbAxiDataWidthMst ) + ) i_axi_lite_dw_downsizer_dut ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .slv ( master ), + .mst ( slave ) + ); +endmodule From 41fe87ed3b5568b6ae1f8c9c6f51591d933254a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20R=C3=B6nninger?= Date: Wed, 24 Jun 2020 17:17:47 +0200 Subject: [PATCH 106/145] axi_test:rand_axi_lite_slave: R response field is now random --- CHANGELOG.md | 1 + src/axi_test.sv | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b4e834cf..47c8e0ec8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Move `mem_to_banks` to `common_cells`. - Update `common_cells` from version `v1.26.0` to `v1.27.0`. - `axi_pkg`: Define `localparams` to define AXI type widths. +- `axi_test:axi_rand_lite_slave`: R response field is now random. ### Fixed diff --git a/src/axi_test.sv b/src/axi_test.sv index ae74cf1ac..c867d57ad 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -1729,15 +1729,17 @@ package axi_test; task automatic send_rs(); forever begin - automatic logic rand_success; - automatic addr_t ar_addr; - automatic data_t r_data; + automatic logic rand_success; + automatic addr_t ar_addr; + automatic data_t r_data; + automatic axi_pkg::resp_t r_resp; wait (ar_queue.size() > 0); ar_addr = this.ar_queue.pop_front(); rand_success = std::randomize(r_data); assert(rand_success); + rand_success = std::randomize(r_resp); assert(rand_success); rand_wait(R_MIN_WAIT_CYCLES, R_MAX_WAIT_CYCLES); - $display("%0t %s> Send R with DATA: %h", $time(), this.name, r_data); - this.drv.send_r(r_data, axi_pkg::RESP_OKAY); + $display("%0t %s> Send R with DATA: %h RESP: %h", $time(), this.name, r_data, r_resp); + this.drv.send_r(r_data, r_resp); end endtask : send_rs From c811d99a911d042ac71fce95565ec750429e6aec Mon Sep 17 00:00:00 2001 From: bluew Date: Tue, 25 Apr 2023 18:57:50 +0200 Subject: [PATCH 107/145] .gitignore: Update --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0797ab037..10c4aecdd 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /Bender.lock /Bender.local *.log +*.wlf From d0c6cdb72575ac1df62842a5bdf069d16b93293f Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 25 Apr 2023 19:53:38 +0200 Subject: [PATCH 108/145] axi_lite_dw_converter: Fix b response check in TB --- test/tb_axi_lite_dw_converter.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tb_axi_lite_dw_converter.sv b/test/tb_axi_lite_dw_converter.sv index 68afd2062..3fb4297a9 100644 --- a/test/tb_axi_lite_dw_converter.sv +++ b/test/tb_axi_lite_dw_converter.sv @@ -366,7 +366,7 @@ module tb_axi_lite_dw_converter #( initial begin : proc_b_check automatic b_chan_lite_t b_act, b_exp; forever begin - wait((fifo_slv_r.size() > 0) && (fifo_mst_r.size() > 0)); + wait((fifo_slv_b.size() > 0) && (fifo_mst_b.size() > 0)); b_act = fifo_slv_b.pop_front(); b_exp = fifo_mst_b.pop_front(); assert (b_act == b_exp) else $error("Slave port> B.resp is not expected: EXP: %h ACT:%h", From 8fff69853ca1dd7ad73f560d644ce9d747471a7b Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Wed, 26 Apr 2023 10:52:36 +0200 Subject: [PATCH 109/145] axi_lite_dw_converter: Add full test for upsizer --- test/tb_axi_lite_dw_converter.sv | 73 ++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/test/tb_axi_lite_dw_converter.sv b/test/tb_axi_lite_dw_converter.sv index 3fb4297a9..65319b0ad 100644 --- a/test/tb_axi_lite_dw_converter.sv +++ b/test/tb_axi_lite_dw_converter.sv @@ -148,7 +148,7 @@ module tb_axi_lite_dw_converter #( end // FIFOs for sampling the channels - // Salve Port + // Slave Port aw_chan_lite_t fifo_slv_aw[$]; w_chan_lite_slv_t fifo_slv_w[$]; b_chan_lite_t fifo_slv_b[$]; @@ -339,30 +339,35 @@ module tb_axi_lite_dw_converter #( localparam int unsigned SelWidth = $clog2(DataMultFactor); typedef logic [SelWidth-1:0] sel_t; - aw_chan_lite_t fifo_exp_aw[$]; - w_chan_lite_mst_t fifo_exp_w[$]; - ar_chan_lite_t fifo_exp_ar[$]; + sel_t fifo_exp_r_offs[$]; - - initial begin : proc_aw_check - automatic aw_chan_lite_t aw_chan; - automatic w_chan_lite_slv_t w_chan; - automatic w_chan_lite_mst_t exp_w; + initial begin : proc_aw_w_check + automatic aw_chan_lite_t mst_aw_chan, slv_aw_chan; + automatic w_chan_lite_slv_t slv_w_chan; + automatic w_chan_lite_mst_t mst_w_chan; automatic addr_t exp_addr; automatic sel_t w_sel; + automatic logic [TbAxiDataWidthMst/8-1:0] slv_w_strb_concat; forever begin - wait((fifo_slv_aw.size() > 0) && (fifo_slv_w.size() > 0)); - aw_chan = fifo_slv_aw.pop_front(); - w_chan = fifo_slv_w.pop_front(); - fifo_exp_aw.push_back(aw_chan); - // Calculate the expected W channel. - + wait((fifo_slv_aw.size() > 0) && (fifo_mst_aw.size() > 0) && (fifo_slv_w.size() > 0) && (fifo_mst_w.size() > 0)); + slv_aw_chan = fifo_slv_aw.pop_front(); + slv_w_chan = fifo_slv_w.pop_front(); + mst_aw_chan = fifo_mst_aw.pop_front(); + mst_w_chan = fifo_mst_w.pop_front(); + w_sel = slv_aw_chan.addr[AddrShift+SelWidth-1:AddrShift]; + + assert (slv_aw_chan == mst_aw_chan) else $error("Master port> AW is not expected: EXP: %h ACT: %h", + slv_aw_chan, mst_aw_chan); + assert (slv_w_chan.data == mst_w_chan.data[TbAxiDataWidthSlv-1:0]) else $error("Master port> W.data is not expected: EXP: %h ACT: %h", + slv_w_chan.data, mst_w_chan.data); + + slv_w_strb_concat = slv_w_chan.strb << (w_sel*TbAxiDataWidthSlv/8); + assert (slv_w_strb_concat == mst_w_chan.strb) else $error("Master port> W.strb is not expected: EXP: %h ACT: %h", + slv_w_chan.strb, mst_w_chan.strb); end end - - initial begin : proc_b_check automatic b_chan_lite_t b_act, b_exp; forever begin @@ -374,6 +379,40 @@ module tb_axi_lite_dw_converter #( end end + initial begin : proc_ar_check + automatic ar_chan_lite_t mst_ar_chan, slv_ar_chan; + automatic sel_t r_sel; + + forever begin + wait((fifo_slv_ar.size() > 0) && (fifo_mst_ar.size() > 0)); + slv_ar_chan = fifo_slv_ar.pop_front(); + mst_ar_chan = fifo_mst_ar.pop_front(); + r_sel = slv_ar_chan.addr[AddrShift+SelWidth-1:AddrShift]; + fifo_exp_r_offs.push_back(r_sel); + + assert (slv_ar_chan == mst_ar_chan) else $error("Master port> AR is not expected: EXP: %h ACT: %h", + slv_ar_chan, mst_ar_chan); + end + end + + initial begin : proc_r_check + automatic r_chan_lite_slv_t slv_r_chan; + automatic r_chan_lite_mst_t mst_r_chan; + automatic sel_t r_sel; + + forever begin + wait((fifo_slv_r.size() > 0) && (fifo_mst_r.size() > 0) && (fifo_exp_r_offs.size() > 0)); + slv_r_chan = fifo_slv_r.pop_front(); + mst_r_chan = fifo_mst_r.pop_front(); + r_sel = fifo_exp_r_offs.pop_front(); + + assert (slv_r_chan.resp == mst_r_chan.resp) else $error("Slave port> R.resp is not expected: EXP: %h, ACT: %h", + mst_r_chan.resp, slv_r_chan.resp); + assert (slv_r_chan.data == data_slv_t'(mst_r_chan.data >> (r_sel*TbAxiDataWidthSlv))) else $error("Slave port> R.data is not expected: EXP: %h, ACT: %h", + mst_r_chan.data, slv_r_chan.data); + end + end + end From 1de017353ae77998fc400183ce902d9e8da4913a Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Wed, 26 Apr 2023 11:18:34 +0200 Subject: [PATCH 110/145] axi_test: Remove excessive log for axi_lite_rand_* --- src/axi_test.sv | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/axi_test.sv b/src/axi_test.sv index c867d57ad..6bb951449 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -1561,7 +1561,7 @@ package axi_test; ar_addr = addr_t'($urandom_range(MIN_ADDR, MAX_ADDR)); ar_prot = prot_t'($urandom()); this.ar_queue.push_back(ar_addr); - $display("%0t %s> Send AR with ADDR: %h PROT: %b", $time(), this.name, ar_addr, ar_prot); + // $display("%0t %s> Send AR with ADDR: %h PROT: %b", $time(), this.name, ar_addr, ar_prot); drv.send_ar(ar_addr, ar_prot); end endtask : send_ars @@ -1575,7 +1575,7 @@ package axi_test; ar_addr = this.ar_queue.pop_front(); rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); drv.recv_r(r_data, r_resp); - $display("%0t %s> Recv R with DATA: %h RESP: %0h", $time(), this.name, r_data, r_resp); + // $display("%0t %s> Recv R with DATA: %h RESP: %0h", $time(), this.name, r_data, r_resp); end endtask : recv_rs @@ -1587,7 +1587,7 @@ package axi_test; aw_addr = addr_t'($urandom_range(MIN_ADDR, MAX_ADDR)); aw_prot = prot_t'($urandom()); this.aw_queue.push_back(aw_addr); - $display("%0t %s> Send AW with ADDR: %h PROT: %b", $time(), this.name, aw_addr, aw_prot); + // $display("%0t %s> Send AW with ADDR: %h PROT: %b", $time(), this.name, aw_addr, aw_prot); this.drv.send_aw(aw_addr, aw_prot); this.b_queue.push_back(1'b1); end @@ -1604,7 +1604,7 @@ package axi_test; aw_addr = aw_queue.pop_front(); rand_success = std::randomize(w_data); assert(rand_success); rand_success = std::randomize(w_strb); assert(rand_success); - $display("%0t %s> Send W with DATA: %h STRB: %h", $time(), this.name, w_data, w_strb); + // $display("%0t %s> Send W with DATA: %h STRB: %h", $time(), this.name, w_data, w_strb); this.drv.send_w(w_data, w_strb); w_queue.push_back(1'b1); end @@ -1619,7 +1619,7 @@ package axi_test; go_b = this.w_queue.pop_front(); rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); this.drv.recv_b(b_resp); - $display("%0t %s> Recv B with RESP: %h", $time(), this.name, b_resp); + // $display("%0t %s> Recv B with RESP: %h", $time(), this.name, b_resp); end endtask : recv_bs @@ -1637,26 +1637,26 @@ package axi_test; // write data to a specific address task automatic write(input addr_t w_addr, input prot_t w_prot = prot_t'(0), input data_t w_data, input strb_t w_strb, output axi_pkg::resp_t b_resp); - $display("%0t %s> Write to ADDR: %h, PROT: %b DATA: %h, STRB: %h", - $time(), this.name, w_addr, w_prot, w_data, w_strb); + // $display("%0t %s> Write to ADDR: %h, PROT: %b DATA: %h, STRB: %h", + // $time(), this.name, w_addr, w_prot, w_data, w_strb); fork this.drv.send_aw(w_addr, w_prot); this.drv.send_w(w_data, w_strb); join this.drv.recv_b(b_resp); - $display("%0t %s> Received write response from ADDR: %h RESP: %h", - $time(), this.name, w_addr, b_resp); + // $display("%0t %s> Received write response from ADDR: %h RESP: %h", + // $time(), this.name, w_addr, b_resp); endtask : write // read data from a specific location task automatic read(input addr_t r_addr, input prot_t r_prot = prot_t'(0), output data_t r_data, output axi_pkg::resp_t r_resp); - $display("%0t %s> Read from ADDR: %h PROT: %b", - $time(), this.name, r_addr, r_prot); + // $display("%0t %s> Read from ADDR: %h PROT: %b", + // $time(), this.name, r_addr, r_prot); this.drv.send_ar(r_addr, r_prot); this.drv.recv_r(r_data, r_resp); - $display("%0t %s> Recieved read response from ADDR: %h DATA: %h RESP: %h", - $time(), this.name, r_addr, r_data, r_resp); + // $display("%0t %s> Recieved read response from ADDR: %h DATA: %h RESP: %h", + // $time(), this.name, r_addr, r_data, r_resp); endtask : read endclass @@ -1722,7 +1722,7 @@ package axi_test; automatic prot_t ar_prot; rand_wait(AX_MIN_WAIT_CYCLES, AX_MAX_WAIT_CYCLES); this.drv.recv_ar(ar_addr, ar_prot); - $display("%0t %s> Recv AR with ADDR: %h PROT: %b", $time(), this.name, ar_addr, ar_prot); + // $display("%0t %s> Recv AR with ADDR: %h PROT: %b", $time(), this.name, ar_addr, ar_prot); this.ar_queue.push_back(ar_addr); end endtask : recv_ars @@ -1738,7 +1738,7 @@ package axi_test; rand_success = std::randomize(r_data); assert(rand_success); rand_success = std::randomize(r_resp); assert(rand_success); rand_wait(R_MIN_WAIT_CYCLES, R_MAX_WAIT_CYCLES); - $display("%0t %s> Send R with DATA: %h RESP: %h", $time(), this.name, r_data, r_resp); + // $display("%0t %s> Send R with DATA: %h RESP: %h", $time(), this.name, r_data, r_resp); this.drv.send_r(r_data, r_resp); end endtask : send_rs @@ -1749,7 +1749,7 @@ package axi_test; automatic prot_t aw_prot; rand_wait(AX_MIN_WAIT_CYCLES, AX_MAX_WAIT_CYCLES); this.drv.recv_aw(aw_addr, aw_prot); - $display("%0t %s> Recv AW with ADDR: %h PROT: %b", $time(), this.name, aw_addr, aw_prot); + // $display("%0t %s> Recv AW with ADDR: %h PROT: %b", $time(), this.name, aw_addr, aw_prot); this.aw_queue.push_back(aw_addr); end endtask : recv_aws @@ -1760,7 +1760,7 @@ package axi_test; automatic strb_t w_strb; rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); this.drv.recv_w(w_data, w_strb); - $display("%0t %s> Recv W with DATA: %h SRTB: %h", $time(), this.name, w_data, w_strb); + // $display("%0t %s> Recv W with DATA: %h SRTB: %h", $time(), this.name, w_data, w_strb); this.b_queue.push_back(1'b1); end endtask : recv_ws @@ -1776,7 +1776,7 @@ package axi_test; go_b = this.b_queue.pop_front(); rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); rand_success = std::randomize(b_resp); assert(rand_success); - $display("%0t %s> Send B with RESP: %h", $time(), this.name, b_resp); + // $display("%0t %s> Send B with RESP: %h", $time(), this.name, b_resp); this.drv.send_b(b_resp); end endtask : send_bs From 05c121d5a42d6ef4fda656ce6661fe0a148d2377 Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Thu, 4 May 2023 15:42:44 +0200 Subject: [PATCH 111/145] axi_chan_compare: Add support for remapped IDs --- src/axi_chan_compare.sv | 63 ++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/src/axi_chan_compare.sv b/src/axi_chan_compare.sv index 4de4ea741..b8ac4dd75 100644 --- a/src/axi_chan_compare.sv +++ b/src/axi_chan_compare.sv @@ -15,6 +15,8 @@ /// Non-synthesizable module comparing two AXI channels of the same type module axi_chan_compare #( + /// Ignore ID field if it was remapped + parameter bit IgnoreId = 1'b0, parameter type aw_chan_t = logic, parameter type w_chan_t = logic, parameter type b_chan_t = logic, @@ -155,32 +157,43 @@ module axi_chan_compare #( always_ff @(posedge clk_b_i) begin : proc_dequeue_and_check_b // aw if (axi_b_req.aw_valid & axi_b_res.aw_ready) begin - automatic aw_chan_t aw; + automatic aw_chan_t aw_exp, aw_recv; if (aw_queue.size() == 0) $error("AW queue is empty!"); - aw = aw_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - if (axi_b_req.aw !== aw) begin + aw_exp = aw_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + aw_recv = axi_b_req.aw; + if (IgnoreId) begin + aw_exp.id = 'X; + aw_recv.id = 'X; + end + if (aw_exp.aw !== aw_recv) begin $error("AW mismatch!"); - print_aw(aw, axi_b_req.aw); + print_aw(aw_exp, aw_recv); end end // w if (axi_b_req.w_valid & axi_b_res.w_ready) begin - automatic w_chan_t w; + automatic w_chan_t w_exp, w_recv; if (w_queue.size() == 0) $error("W queue is empty!"); - w = w_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - if (axi_b_req.w !== w) begin + w_exp = w_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + w_recv = axi_b_req.w; + if (w_exp !== w_recv) begin $error("W mismatch!"); - print_w(w, axi_b_req.w); + print_w(w_exp, w_recv); end end // ar if (axi_b_req.ar_valid & axi_b_res.ar_ready) begin - automatic ar_chan_t ar; + automatic ar_chan_t ar_exp, ar_recv; if (ar_queue.size() == 0) $error("AR queue is empty!"); - ar = ar_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - if (axi_b_req.ar !== ar) begin + ar_exp = ar_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + ar_recv = axi_b_req.ar; + if (IgnoreId) begin + ar_exp.id = 'X; + ar_recv.id = 'X; + end + if (ar_exp !== ar_recv) begin $error("AR mismatch!"); - print_ar(ar, axi_b_req.ar); + print_ar(ar_exp, ar_recv); end end end @@ -189,22 +202,32 @@ module axi_chan_compare #( always_ff @(posedge clk_a_i) begin : proc_dequeue_and_check_a // b if (axi_a_res.b_valid & axi_a_req.b_ready) begin - automatic b_chan_t b; + automatic b_chan_t b_exp, b_recv; if (b_queue.size() == 0) $error("B queue is empty!"); - b = b_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - if (axi_a_res.b !== b) begin + b_exp = b_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + b_recv = axi_a_res.b; + if (IgnoreId) begin + b_exp.id = 'X; + b_recv.id = 'X; + end + if (b_exp !== b_recv) begin $error("B mismatch!"); - print_b(b, axi_a_res.b); + print_b(b_exp, b_recv); end end // r if (axi_a_res.r_valid & axi_a_req.r_ready) begin - automatic r_chan_t r; + automatic r_chan_t r_exp, r_recv; if (r_queue.size() == 0) $error("R queue is empty!"); - r = r_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - if (axi_a_res.r !== r) begin + r_exp = r_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + r_recv = axi_a_res.r; + if (IgnoreId) begin + r_exp.id = 'X; + r_recv.id = 'X; + end + if (r_exp !== r_recv) begin $error("R mismatch!"); - print_r(r, axi_a_res.r); + print_r(r_exp, r_recv); end end end From 58d17f3b0975528891d1402127549494685d17e6 Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Thu, 4 May 2023 15:46:11 +0200 Subject: [PATCH 112/145] CHANGELOG: Fix module name --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6bb2ed08..4ba9350fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased ### Added -- Add `axi_channel_compare.sv`: Non-synthesizable module comparing two AXI channels of the same type +- Add `axi_chan_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. - Add `axi_lite_from_mem` and `axi_from_mem` acting like SRAMs making AXI4 requests downstream. From e7775760e3e303f8e83f9f3063f67cbd18a3171a Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Thu, 4 May 2023 16:23:27 +0200 Subject: [PATCH 113/145] axi_chan_compare: Fix comparison --- src/axi_chan_compare.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axi_chan_compare.sv b/src/axi_chan_compare.sv index b8ac4dd75..59c99179c 100644 --- a/src/axi_chan_compare.sv +++ b/src/axi_chan_compare.sv @@ -165,7 +165,7 @@ module axi_chan_compare #( aw_exp.id = 'X; aw_recv.id = 'X; end - if (aw_exp.aw !== aw_recv) begin + if (aw_exp !== aw_recv) begin $error("AW mismatch!"); print_aw(aw_exp, aw_recv); end From ec0b7cf8680e3d20fe6c7b22c247fc024d4a8ce4 Mon Sep 17 00:00:00 2001 From: Cesar Fuguet Date: Wed, 18 Jan 2023 18:28:08 +0100 Subject: [PATCH 114/145] axi_dw_downsizer: initialize response buffers with EXOKAY This modification allows to correctly forward the response status of multi-beat exclusive read or write operations. This RESP_EXOKAY response code has the lowest priority when merging the response code from a multi-beat response. Signed-off-by: Cesar Fuguet --- src/axi_dw_downsizer.sv | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/axi_dw_downsizer.sv b/src/axi_dw_downsizer.sv index 78b053d99..88d774ad0 100644 --- a/src/axi_dw_downsizer.sv +++ b/src/axi_dw_downsizer.sv @@ -397,11 +397,12 @@ module axi_dw_downsizer #( r_state_d = R_PASSTHROUGH; // Save beat - r_req_d.ar = slv_req_i.ar ; - r_req_d.ar_valid = 1'b1 ; - r_req_d.burst_len = slv_req_i.ar.len ; - r_req_d.orig_ar_size = slv_req_i.ar.size; - r_req_d.injected_aw = 1'b0 ; + r_req_d.ar = slv_req_i.ar ; + r_req_d.ar_valid = 1'b1 ; + r_req_d.burst_len = slv_req_i.ar.len ; + r_req_d.orig_ar_size = slv_req_i.ar.size ; + r_req_d.injected_aw = 1'b0 ; + r_req_d.r.resp = axi_pkg::RESP_EXOKAY; case (r_req_d.ar.burst) axi_pkg::BURST_INCR : begin @@ -476,6 +477,7 @@ module axi_dw_downsizer #( r_req_d.burst_len = w_req_q.orig_aw_len ; r_req_d.orig_ar_size = w_req_q.orig_aw_size ; r_req_d.injected_aw = 1'b1 ; + r_req_d.r.resp = axi_pkg::RESP_EXOKAY ; // Default state r_state_d = R_PASSTHROUGH; @@ -794,10 +796,10 @@ module axi_dw_downsizer #( // Can start a new request as soon as w_state_d is W_IDLE if (w_state_d == W_IDLE) begin // Reset channels - w_req_d.aw = '0 ; - w_req_d.aw_valid = 1'b0 ; - w_req_d.aw_throw_error = 1'b0 ; - w_req_d.burst_resp = axi_pkg::RESP_OKAY; + w_req_d.aw = '0 ; + w_req_d.aw_valid = 1'b0 ; + w_req_d.aw_throw_error = 1'b0 ; + w_req_d.burst_resp = axi_pkg::RESP_EXOKAY; if (!forward_b_beat_full) begin if (slv_req_i.aw_valid && slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) begin // ATOP with an R response From 0fa2342d5d3b46d70ce081aa0b6c767d264095f3 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Tue, 28 Mar 2023 12:39:16 +0200 Subject: [PATCH 115/145] axi_test: Towards file-based verification --- src/axi_test.sv | 225 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) diff --git a/src/axi_test.sv b/src/axi_test.sv index 6bb951449..6fd789415 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -2308,6 +2308,231 @@ package axi_test; endclass : axi_scoreboard + + class axi_file_master #( + // AXI interface parameters + parameter int AW = 32, + parameter int DW = 32, + parameter int IW = 8, + parameter int UW = 1, + // Stimuli application and test time + parameter time TA = 0ps, + parameter time TT = 0ps + ); + + typedef axi_test::axi_driver #( + .AW(AW), .DW(DW), .IW(IW), .UW(UW), .TA(TA), .TT(TT) + ) axi_driver_t; + + typedef axi_driver_t::ax_beat_t ax_beat_t; + typedef axi_driver_t::b_beat_t b_beat_t; + typedef axi_driver_t::r_beat_t r_beat_t; + typedef axi_driver_t::w_beat_t w_beat_t; + + axi_driver_t drv; + + int read_fd, write_fd; + + // store holding read/write transactions between aw-b and ar-r + logic b_outst[$]; + logic r_outst[$]; + + // proper decoupling: populate queues from file + ax_beat_t aw_queue[$]; + w_beat_t w_queue[$]; + ax_beat_t ar_queue[$]; + + // populated by read file function + int num_reads; + int num_writes; + + function new( + virtual AXI_BUS_DV #( + .AXI_ADDR_WIDTH(AW), + .AXI_DATA_WIDTH(DW), + .AXI_ID_WIDTH(IW), + .AXI_USER_WIDTH(UW) + ) axi + ); + this.drv = new(axi); + this.reset(); + endfunction + + function void reset(); + drv.reset_master(); + endfunction + + function void parse_write(); + // parsing works + int parse_ok; + + // populate according to file + while (!$feof(this.write_fd)) begin + automatic ax_beat_t current_aw = new; + parse_ok = 1; + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_id ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "0x%x\n", current_aw.ax_addr ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_len ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_size ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_burst ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_lock ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_cache ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_prot ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_qos ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_region) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_atop ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_user ) != -1); + if (parse_ok) begin + this.aw_queue.push_back(current_aw); + this.b_outst.push_back(1'b1); + // $display("%p", current_aw); + end else begin + $warning("Issue parsing AW: %p", current_aw); + end + + // get write data + strobe + for (int i = 0; i <= current_aw.ax_len; i++) begin + automatic w_beat_t current_w = new; + parse_ok = parse_ok & ($fscanf(this.write_fd, "0x%x 0x%x %d\n", current_w.w_data, current_w.w_strb, current_w.w_user) != -1); + current_w.w_last = 1'b0; + if (i == current_aw.ax_len) begin + current_w.w_last = 1'b1; + end + if (parse_ok) begin + this.w_queue.push_back(current_w); + // $display("%p", current_w); + end else begin + $warning("Issue parsing W: %p of AW: %p", current_w, current_aw); + end + end + end + + // debug: print queues + // $display("%p", this.aw_queue); + // $display("%p", this.w_queue); + endfunction + + function void parse_read(); + // parsing works + int parse_ok; + + // populate according to file + while (!$feof(this.read_fd)) begin + automatic ax_beat_t current_ar = new; + parse_ok = 1; + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_id ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "0x%x\n", current_ar.ax_addr ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_len ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_size ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_burst ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_lock ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_cache ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_prot ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_qos ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_region) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_user ) != -1); + if (parse_ok) begin + this.ar_queue.push_back(current_ar); + this.r_outst.push_back(1'b1); + // $display("%p", current_ar); + end else begin + $warning("Issue parsing AR: %p", current_ar); + end + end + + // debug: print queues + // $display("%p", this.ar_queue); + endfunction + + function void load_files( + string read_file_name, + string write_file_name + ); + this.read_fd = $fopen(read_file_name, "r"); + this.write_fd = $fopen(write_file_name, "r"); + + // check if files are opened + if (this.read_fd) begin + $info("File %s opened successfully as %d", read_file_name, this.read_fd); + end else begin + $fatal(1, "File %s not found", read_file_name); + end + if (this.write_fd) begin + $info("File %s opened successfully as %d", write_file_name, this.write_fd); + end else begin + $fatal(1, "File %s not found", write_file_name); + end + + // read files + this.parse_read(); + this.parse_write(); + + // update status + this.num_reads = this.ar_queue.size(); + this.num_writes = this.aw_queue.size(); + endfunction + + task run_aw(); + // send aws while there are some left + while (this.aw_queue.size() > 0) begin + // display("Sending AW: %p", this.aw_queue[0]); + drv.send_aw(this.aw_queue[0]); + void'(this.aw_queue.pop_front()); + end + endtask + + task run_w(); + // send ws while there are some left + while (this.w_queue.size() > 0) begin + // $display("Sending W: %p", this.w_queue[0]); + drv.send_w(this.w_queue[0]); + void'(this.w_queue.pop_front()); + end + endtask + + task run_ar(); + // send ars while there are some left + while (this.ar_queue.size() > 0) begin + // $display("Sending AR: %p", this.ar_queue[0]); + drv.send_ar(this.ar_queue[0]); + void'(this.ar_queue.pop_front()); + end + endtask + + task wait_b(); + automatic b_beat_t b_beat = new; + // wait for bs while there are some left + while (this.b_outst.size() > 0) begin + // $display("Waiting B"); + drv.recv_b(b_beat); + void'(this.b_outst.pop_front()); + end + endtask + + task wait_r(); + automatic r_beat_t r_beat = new; + // wait for rs while there are some left + while (this.r_outst.size() > 0) begin + // $display("Waiting R"); + do begin + drv.recv_r(r_beat); + end while (r_beat.r_last !== 1'b1); + void'(this.r_outst.pop_front()); + end + endtask + + task run(); + fork + this.run_aw(); + this.run_w (); + this.run_ar(); + this.wait_b(); + this.wait_r(); + join + endtask + + endclass + endpackage // non synthesisable axi logger module From 2e3fa1bd1c5a19ad5468b6594183c932a72fedd8 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Mon, 8 May 2023 09:53:42 +0200 Subject: [PATCH 116/145] Fix lint error --- src/axi_pkg.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axi_pkg.sv b/src/axi_pkg.sv index 583846491..3f723e262 100644 --- a/src/axi_pkg.sv +++ b/src/axi_pkg.sv @@ -198,7 +198,7 @@ package axi_pkg; beat_lower_byte(largest_addr_t addr, size_t size, len_t len, burst_t burst, shortint unsigned strobe_width, shortint unsigned i_beat); largest_addr_t _addr = beat_addr(addr, size, len, burst, i_beat); - return _addr - (_addr / strobe_width) * strobe_width; + return shortint'(_addr - (_addr / strobe_width) * strobe_width); endfunction /// Index of highest byte in beat (see A3-51). From 4b20954dc8229a0c06e8b3d70284e966f9da2ec1 Mon Sep 17 00:00:00 2001 From: Cyril Koenig Date: Wed, 29 Mar 2023 14:57:12 +0200 Subject: [PATCH 117/145] axi_pkg: Keep hardcoded value in typedef for vivado ip packager --- src/axi_pkg.sv | 65 +++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/axi_pkg.sv b/src/axi_pkg.sv index 3f723e262..60c452443 100644 --- a/src/axi_pkg.sv +++ b/src/axi_pkg.sv @@ -19,49 +19,50 @@ //! AXI Package /// Contains all necessary type definitions, constants, and generally useful functions. package axi_pkg; - /// AXI Transaction Burst Width. - parameter int unsigned BurstWidth = 32'd2; - /// AXI Transaction Response Width. - parameter int unsigned RespWidth = 32'd2; - /// AXI Transaction Cacheability Width. - parameter int unsigned CacheWidth = 32'd4; - /// AXI Transaction Protection Width. - parameter int unsigned ProtWidth = 32'd3; - /// AXI Transaction Quality of Service Width. - parameter int unsigned QosWidth = 32'd4; - /// AXI Transaction Region Width. - parameter int unsigned RegionWidth = 32'd4; - /// AXI Transaction Length Width. - parameter int unsigned LenWidth = 32'd8; - /// AXI Transaction Size Width. - parameter int unsigned SizeWidth = 32'd3; - /// AXI Lock Width. - parameter int unsigned LockWidth = 32'd1; - /// AXI5 Atomic Operation Width. - parameter int unsigned AtopWidth = 32'd6; - /// AXI5 Non-Secure Address Identifier. - parameter int unsigned NsaidWidth = 32'd4; /// AXI Transaction Burst Width. - typedef logic [BurstWidth-1:0] burst_t; + typedef logic [1:0] burst_t; /// AXI Transaction Response Type. - typedef logic [RespWidth-1:0] resp_t; + typedef logic [1:0] resp_t; /// AXI Transaction Cacheability Type. - typedef logic [CacheWidth-1:0] cache_t; + typedef logic [3:0] cache_t; /// AXI Transaction Protection Type. - typedef logic [ProtWidth-1:0] prot_t; + typedef logic [2:0] prot_t; /// AXI Transaction Quality of Service Type. - typedef logic [QosWidth-1:0] qos_t; + typedef logic [3:0] qos_t; /// AXI Transaction Region Type. - typedef logic [RegionWidth-1:0] region_t; + typedef logic [3:0] region_t; /// AXI Transaction Length Type. - typedef logic [LenWidth-1:0] len_t; + typedef logic [7:0] len_t; /// AXI Transaction Size Type. - typedef logic [SizeWidth-1:0] size_t; + typedef logic [2:0] size_t; /// AXI5 Atomic Operation Type. - typedef logic [AtopWidth-1:0] atop_t; // atomic operations + typedef logic [5:0] atop_t; // atomic operations + /// AXI5 Non-Secure Address Identifier. + typedef logic [3:0] nsaid_t; + + /// AXI Transaction Burst Width. + parameter int unsigned BurstWidth = $size(burst_t); + /// AXI Transaction Response Width. + parameter int unsigned RespWidth = $size(resp_t); + /// AXI Transaction Cacheability Width. + parameter int unsigned CacheWidth = $size(cache_t); + /// AXI Transaction Protection Width. + parameter int unsigned ProtWidth = $size(prot_t); + /// AXI Transaction Quality of Service Width. + parameter int unsigned QosWidth = $size(qos_t); + /// AXI Transaction Region Width. + parameter int unsigned RegionWidth = $size(region_t); + /// AXI Transaction Length Width. + parameter int unsigned LenWidth = $size(len_t); + /// AXI Transaction Size Width. + parameter int unsigned SizeWidth = $size(size_t); + /// AXI Lock Width. + parameter int unsigned LockWidth = 32'd1; + /// AXI5 Atomic Operation Width. + parameter int unsigned AtopWidth = $size(atop_t); /// AXI5 Non-Secure Address Identifier. - typedef logic [NsaidWidth-1:0] nsaid_t; + parameter int unsigned NsaidWidth = $size(nsaid_t); /// In a fixed burst: /// - The address is the same for every transfer in the burst. From 7b0db8e4208427c6238d60d705aecd082fc8f902 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Mon, 8 May 2023 10:25:24 +0200 Subject: [PATCH 118/145] axi_pkg: Remove size function for better compatibility --- src/axi_pkg.sv | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/axi_pkg.sv b/src/axi_pkg.sv index 60c452443..11c0f8ba7 100644 --- a/src/axi_pkg.sv +++ b/src/axi_pkg.sv @@ -19,6 +19,28 @@ //! AXI Package /// Contains all necessary type definitions, constants, and generally useful functions. package axi_pkg; + /// AXI Transaction Burst Width. + parameter int unsigned BurstWidth = 32'd2; + /// AXI Transaction Response Width. + parameter int unsigned RespWidth = 32'd2; + /// AXI Transaction Cacheability Width. + parameter int unsigned CacheWidth = 32'd4; + /// AXI Transaction Protection Width. + parameter int unsigned ProtWidth = 32'd3; + /// AXI Transaction Quality of Service Width. + parameter int unsigned QosWidth = 32'd4; + /// AXI Transaction Region Width. + parameter int unsigned RegionWidth = 32'd4; + /// AXI Transaction Length Width. + parameter int unsigned LenWidth = 32'd8; + /// AXI Transaction Size Width. + parameter int unsigned SizeWidth = 32'd3; + /// AXI Lock Width. + parameter int unsigned LockWidth = 32'd1; + /// AXI5 Atomic Operation Width. + parameter int unsigned AtopWidth = 32'd6; + /// AXI5 Non-Secure Address Identifier. + parameter int unsigned NsaidWidth = 32'd4; /// AXI Transaction Burst Width. typedef logic [1:0] burst_t; @@ -41,29 +63,6 @@ package axi_pkg; /// AXI5 Non-Secure Address Identifier. typedef logic [3:0] nsaid_t; - /// AXI Transaction Burst Width. - parameter int unsigned BurstWidth = $size(burst_t); - /// AXI Transaction Response Width. - parameter int unsigned RespWidth = $size(resp_t); - /// AXI Transaction Cacheability Width. - parameter int unsigned CacheWidth = $size(cache_t); - /// AXI Transaction Protection Width. - parameter int unsigned ProtWidth = $size(prot_t); - /// AXI Transaction Quality of Service Width. - parameter int unsigned QosWidth = $size(qos_t); - /// AXI Transaction Region Width. - parameter int unsigned RegionWidth = $size(region_t); - /// AXI Transaction Length Width. - parameter int unsigned LenWidth = $size(len_t); - /// AXI Transaction Size Width. - parameter int unsigned SizeWidth = $size(size_t); - /// AXI Lock Width. - parameter int unsigned LockWidth = 32'd1; - /// AXI5 Atomic Operation Width. - parameter int unsigned AtopWidth = $size(atop_t); - /// AXI5 Non-Secure Address Identifier. - parameter int unsigned NsaidWidth = $size(nsaid_t); - /// In a fixed burst: /// - The address is the same for every transfer in the burst. /// - The byte lanes that are valid are constant for all beats in the burst. However, within From cdd5c8998c42fbdb44247d5444c4940d819c291e Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Mon, 17 Apr 2023 11:22:41 +0200 Subject: [PATCH 119/145] Use pulp-actions for internal CI --- .github/workflows/gitlab-ci.yml | 77 +++++---------------------------- 1 file changed, 11 insertions(+), 66 deletions(-) diff --git a/.github/workflows/gitlab-ci.yml b/.github/workflows/gitlab-ci.yml index 4118a2a44..b60140d26 100644 --- a/.github/workflows/gitlab-ci.yml +++ b/.github/workflows/gitlab-ci.yml @@ -8,74 +8,19 @@ on: branches-ignore: - gh-pages # deployment target branch (this workflow should not exist on that branch anyway) - v** # such branch names conflict with tags + workflow_dispatch: jobs: gitlab-ci: - if: github.repository == 'pulp-platform/axi' # do not run this job on forks (because Gitlab CI - runs-on: ubuntu-latest # will not trigger on forks) - timeout-minutes: 190 + runs-on: ubuntu-latest + timeout-minutes: 310 steps: - - name: Checkout - uses: actions/checkout@v2 + - name: Check Gitlab CI + uses: pulp-platform/pulp-actions/gitlab-ci@v2 + # Skip on forks or pull requests from forks due to missing secrets. + if: github.repository == 'pulp-platform/axi' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) with: - persist-credentials: false - # Checkout pull request HEAD commit instead of merge commit, because CI runs against HEAD - # commit. - ref: ${{ github.event.pull_request.head.sha }} - - - name: Wait for synchronization (every 5 minutes) - run: | - while [ $(($(date -d "+1 minute" +%-M) % 5)) -ne 0 ]; do - # "+1 minute" because if the current minute is divisible by 5, we likely already missed - # the synchronization. - sleep 10 - done - sleep 90 # the minute above plus 30 seconds to leave some time for the synchronization - shell: bash - - - name: Obtain CI result - run: | - if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then - BRANCH_NAME="$GITHUB_HEAD_REF" - elif [ "$GITHUB_EVENT_NAME" == "push" ]; then - if echo $GITHUB_REF | grep -qE '^refs/heads'; then - BRANCH_NAME="$(echo $GITHUB_REF | cut -d '/' -f3-)" - else - echo "Error: Could not derive branch name from ref '$GITHUB_REF'!" - exit 1 - fi - else - echo "Error: Unsupported event: '$GITHUB_EVENT_NAME'!" - exit 1 - fi - while true; do - resp="$(curl --fail --silent --show-error \ - https://akurth.net/usrv/ig/shields/pipeline/akurth/axi/$BRANCH_NAME)" - if [ $? -ne 0 ]; then - echo "Error: Failed to obtain CI status!" - exit 1 - fi - status="$(echo $resp | jq -r .message)" - if [ "$status" == "passing" ]; then - sha="$(echo $resp | jq -r .sha)" - if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then - github_sha="$(cat "$GITHUB_EVENT_PATH" | jq -r .pull_request.head.sha)" - else - github_sha="$GITHUB_SHA" - fi - if [ "$sha" == "$github_sha" ]; then - echo "CI passed." - exit 0 - else - echo "Error: CI passed, but on a different SHA: '$sha'!" - exit 1 - fi - elif [ "$status" == "running" ]; then - echo "CI is running, waiting .." - else - echo "Error: Unknown or failing status: '$status'!" - exit 1 - fi - sleep 10 - done - shell: bash + domain: iis-git.ee.ethz.ch + repo: github-mirror/axi + token: ${{ secrets.GITLAB_TOKEN }} + poll-count: 1800 From c13bd0d820e9d6721dbcee146f696cc988cc2ccc Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Mon, 8 May 2023 19:08:39 +0200 Subject: [PATCH 120/145] Update CI badge --- .github/workflows/gitlab-ci.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gitlab-ci.yml b/.github/workflows/gitlab-ci.yml index b60140d26..560eaedbb 100644 --- a/.github/workflows/gitlab-ci.yml +++ b/.github/workflows/gitlab-ci.yml @@ -1,4 +1,4 @@ -name: Retrieve CI result from GitLab +name: Internal CI on: push: branches-ignore: diff --git a/README.md b/README.md index 807fad8ca..734154136 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # AXI SystemVerilog Modules for High-Performance On-Chip Communication -[![CI status](https://akurth.net/usrv/ig/shields/pipeline/akurth/axi/master.svg)](https://iis-git.ee.ethz.ch/akurth/axi/commits/master) +[![CI status](https://github.com/pulp-platform/axi/actions/workflows/gitlab-ci.yml/badge.svg?branch=master)](https://github.com/pulp-platform/axi/actions/workflows/gitlab-ci.yml?query=branch%3Amaster) [![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/pulp-platform/axi?color=blue&label=current&sort=semver)](CHANGELOG.md) [![SHL-0.51 license](https://img.shields.io/badge/license-SHL--0.51-green)](LICENSE) From ff78b048401f7cea52e09daec983729fa7416987 Mon Sep 17 00:00:00 2001 From: Paul Scheffler Date: Tue, 2 May 2023 18:06:09 +0200 Subject: [PATCH 121/145] axi_id_serialize: Add optional ID base and map parameters --- src/axi_id_serialize.sv | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/axi_id_serialize.sv b/src/axi_id_serialize.sv index b3d28e839..fc36a217f 100644 --- a/src/axi_id_serialize.sv +++ b/src/axi_id_serialize.sv @@ -55,7 +55,17 @@ module axi_id_serialize #( /// Request struct type of the AXI4+ATOP master port parameter type mst_req_t = logic, /// Response struct type of the AXI4+ATOP master port - parameter type mst_resp_t = logic + parameter type mst_resp_t = logic, + /// A custom offset (modulo `AxiMstPortMaxUniqIds`, ignored for input IDs remapped through + /// `IdMap`) for the assigned output IDs. + parameter int unsigned MstIdBaseOffset = 32'd0, + /// Explicit input-output ID map. If an input ID `id` does not appear in this mapping (default), + /// it is simply mapped to the output ID `id % AxiMstPortMaxUniqIds`. If `id` appears in more + /// than one entry, it is matched to the *last* matching entry's output ID. + /// Number of Entries in the explicit ID map (default: None) + parameter int unsigned IdMapNumEntries = 32'd0, + /// Explicit ID map; index [0] in each entry is the input ID to match, index [1] the output ID. + parameter int unsigned IdMap [IdMapNumEntries-1:0][0:1] = '{default: '{32'b0, 32'b0}} ) ( /// Rising-edge clock of both ports input logic clk_i, @@ -143,9 +153,27 @@ module axi_id_serialize #( /// R channel at master port `AXI_TYPEDEF_R_CHAN_T(mst_r_t, data_t, mst_id_t, user_t) + /// Type for slave ID map + typedef mst_id_t [2**AxiSlvPortIdWidth-1:0] slv_id_map_t; + + /// Resolve target output ID for each possible input ID as a parameter + function automatic slv_id_map_t map_slv_ids(); + slv_id_map_t ret = '0; + // Populate output with default mapping, including `MstIdBaseOffset` + for (int unsigned i = 0; i < 2**AxiSlvPortIdWidth; ++i) + ret[i] = (i + MstIdBaseOffset) % AxiMstPortMaxUniqIds; + // For each explicitly mapped input ID, set the desired output ID + for (int unsigned i = 0; i < IdMapNumEntries; ++i) + ret[IdMap[i][0]] = IdMap[i][1]; + return ret; + endfunction + + /// Input-to-output ID map used + localparam slv_id_map_t SlvIdMap = map_slv_ids(); + select_t slv_aw_select, slv_ar_select; - assign slv_aw_select = select_t'(slv_req_i.aw.id % AxiMstPortMaxUniqIds); // TODO: customizable base - assign slv_ar_select = select_t'(slv_req_i.ar.id % AxiMstPortMaxUniqIds); + assign slv_aw_select = select_t'(SlvIdMap[slv_req_i.aw.id]); + assign slv_ar_select = select_t'(SlvIdMap[slv_req_i.ar.id]); slv_req_t [AxiMstPortMaxUniqIds-1:0] to_serializer_reqs; slv_resp_t [AxiMstPortMaxUniqIds-1:0] to_serializer_resps; From ecd376c653b2d095b8e4343c061cdc477f4dd509 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Thu, 11 May 2023 09:55:23 +0200 Subject: [PATCH 122/145] Fix spyglass lint error --- src/axi_id_serialize.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axi_id_serialize.sv b/src/axi_id_serialize.sv index fc36a217f..b032c7766 100644 --- a/src/axi_id_serialize.sv +++ b/src/axi_id_serialize.sv @@ -65,7 +65,7 @@ module axi_id_serialize #( /// Number of Entries in the explicit ID map (default: None) parameter int unsigned IdMapNumEntries = 32'd0, /// Explicit ID map; index [0] in each entry is the input ID to match, index [1] the output ID. - parameter int unsigned IdMap [IdMapNumEntries-1:0][0:1] = '{default: '{32'b0, 32'b0}} + parameter int unsigned IdMap [IdMapNumEntries-1:0][0:1] = '{default: {32'b0, 32'b0}} ) ( /// Rising-edge clock of both ports input logic clk_i, From 807d33a166c25ca2a1b25b31aeb427fd7ea25f3e Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 16 May 2023 17:20:55 +0200 Subject: [PATCH 123/145] Add axi_demux without spill registers for simplicity --- Bender.yml | 3 +- src/axi_demux.sv | 854 ++++++---------------------------------- src/axi_demux_simple.sv | 631 +++++++++++++++++++++++++++++ 3 files changed, 762 insertions(+), 726 deletions(-) create mode 100644 src/axi_demux_simple.sv diff --git a/Bender.yml b/Bender.yml index 5f74c1f1b..664995bb9 100644 --- a/Bender.yml +++ b/Bender.yml @@ -40,7 +40,7 @@ sources: - src/axi_cdc_src.sv - src/axi_cut.sv - src/axi_delayer.sv - - src/axi_demux.sv + - src/axi_demux_simple.sv - src/axi_dw_downsizer.sv - src/axi_dw_upsizer.sv - src/axi_fifo.sv @@ -68,6 +68,7 @@ sources: - src/axi_to_mem.sv # Level 3 - src/axi_cdc.sv + - src/axi_demux.sv - src/axi_err_slv.sv - src/axi_dw_converter.sv - src/axi_from_mem.sv diff --git a/src/axi_demux.sv b/src/axi_demux.sv index fc061ff09..a5f8389ad 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -73,735 +73,139 @@ module axi_demux #( input axi_resp_t [NoMstPorts-1:0] mst_resps_i ); - localparam int unsigned IdCounterWidth = cf_math_pkg::idx_width(MaxTrans); - typedef logic [IdCounterWidth-1:0] id_cnt_t; - - - // pass through if only one master port - if (NoMstPorts == 32'h1) begin : gen_no_demux - spill_register #( - .T ( aw_chan_t ), - .Bypass ( ~SpillAw ) - ) i_aw_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.aw_valid ), - .ready_o ( slv_resp_o.aw_ready ), - .data_i ( slv_req_i.aw ), - .valid_o ( mst_reqs_o[0].aw_valid ), - .ready_i ( mst_resps_i[0].aw_ready ), - .data_o ( mst_reqs_o[0].aw ) - ); - spill_register #( - .T ( w_chan_t ), - .Bypass ( ~SpillW ) - ) i_w_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.w_valid ), - .ready_o ( slv_resp_o.w_ready ), - .data_i ( slv_req_i.w ), - .valid_o ( mst_reqs_o[0].w_valid ), - .ready_i ( mst_resps_i[0].w_ready ), - .data_o ( mst_reqs_o[0].w ) - ); - spill_register #( - .T ( b_chan_t ), - .Bypass ( ~SpillB ) - ) i_b_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( mst_resps_i[0].b_valid ), - .ready_o ( mst_reqs_o[0].b_ready ), - .data_i ( mst_resps_i[0].b ), - .valid_o ( slv_resp_o.b_valid ), - .ready_i ( slv_req_i.b_ready ), - .data_o ( slv_resp_o.b ) - ); - spill_register #( - .T ( ar_chan_t ), - .Bypass ( ~SpillAr ) - ) i_ar_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.ar_valid ), - .ready_o ( slv_resp_o.ar_ready ), - .data_i ( slv_req_i.ar ), - .valid_o ( mst_reqs_o[0].ar_valid ), - .ready_i ( mst_resps_i[0].ar_ready ), - .data_o ( mst_reqs_o[0].ar ) - ); - spill_register #( - .T ( r_chan_t ), - .Bypass ( ~SpillR ) - ) i_r_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( mst_resps_i[0].r_valid ), - .ready_o ( mst_reqs_o[0].r_ready ), - .data_i ( mst_resps_i[0].r ), - .valid_o ( slv_resp_o.r_valid ), - .ready_i ( slv_req_i.r_ready ), - .data_o ( slv_resp_o.r ) - ); - - // other non degenerate cases - end else begin : gen_demux - - //-------------------------------------- - //-------------------------------------- - // Signal Declarations - //-------------------------------------- - //-------------------------------------- - - //-------------------------------------- - // Write Transaction - //-------------------------------------- - // comes from spill register at input - aw_chan_t slv_aw_chan; - select_t slv_aw_select; - - logic slv_aw_valid, slv_aw_valid_chan, slv_aw_valid_sel; - logic slv_aw_ready, slv_aw_ready_chan, slv_aw_ready_sel; - - // AW ID counter - select_t lookup_aw_select; - logic aw_select_occupied, aw_id_cnt_full; - // Upon an ATOP load, inject IDs from the AW into the AR channel - logic atop_inject; - - // W select counter: stores the decision to which master W beats should go - select_t w_select, w_select_q; - logic w_select_valid; - id_cnt_t w_open; - logic w_cnt_up, w_cnt_down; - - // Register which locks the AW valid signal - logic lock_aw_valid_d, lock_aw_valid_q, load_aw_lock; - logic aw_valid, aw_ready; - - // W channel from spill reg - w_chan_t slv_w_chan; - logic slv_w_valid, slv_w_ready; - - // B channles input into the arbitration - b_chan_t [NoMstPorts-1:0] mst_b_chans; - logic [NoMstPorts-1:0] mst_b_valids, mst_b_readies; - - // B channel to spill register - b_chan_t slv_b_chan; - logic slv_b_valid, slv_b_ready; - - //-------------------------------------- - // Read Transaction - //-------------------------------------- - // comes from spill register at input - logic slv_ar_valid, ar_valid_chan, ar_valid_sel; - logic slv_ar_ready, slv_ar_ready_chan, slv_ar_ready_sel; - - // AR ID counter - select_t lookup_ar_select; - logic ar_select_occupied, ar_id_cnt_full; - logic ar_push; - - // Register which locks the AR valid signel - logic lock_ar_valid_d, lock_ar_valid_q, load_ar_lock; - logic ar_valid, ar_ready; - - // R channles input into the arbitration - r_chan_t [NoMstPorts-1:0] mst_r_chans; - logic [NoMstPorts-1:0] mst_r_valids, mst_r_readies; - - // R channel to spill register - r_chan_t slv_r_chan; - logic slv_r_valid, slv_r_ready; - - //-------------------------------------- - //-------------------------------------- - // Channel Control - //-------------------------------------- - //-------------------------------------- - - //-------------------------------------- - // AW Channel - //-------------------------------------- - // spill register at the channel input - spill_register #( - .T ( aw_chan_t ), - .Bypass ( ~SpillAw ) // because module param indicates if we want a spill reg - ) i_aw_channel_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.aw_valid ), - .ready_o ( slv_aw_ready_chan ), - .data_i ( slv_req_i.aw ), - .valid_o ( slv_aw_valid_chan ), - .ready_i ( slv_aw_ready ), - .data_o ( slv_aw_chan ) - ); - spill_register #( - .T ( select_t ), - .Bypass ( ~SpillAw ) // because module param indicates if we want a spill reg - ) i_aw_select_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.aw_valid ), - .ready_o ( slv_aw_ready_sel ), - .data_i ( slv_aw_select_i ), - .valid_o ( slv_aw_valid_sel ), - .ready_i ( slv_aw_ready ), - .data_o ( slv_aw_select ) - ); - assign slv_resp_o.aw_ready = slv_aw_ready_chan & slv_aw_ready_sel; - assign slv_aw_valid = slv_aw_valid_chan & slv_aw_valid_sel; - - // Control of the AW handshake - always_comb begin - // AXI Handshakes - slv_aw_ready = 1'b0; - aw_valid = 1'b0; - // `lock_aw_valid`, used to be protocol conform as it is not allowed to deassert - // a valid if there was no corresponding ready. As this process has to be able to inject - // an AXI ID into the counter of the AR channel on an ATOP, there could be a case where - // this process waits on `aw_ready` but in the mean time on the AR channel the counter gets - // full. - lock_aw_valid_d = lock_aw_valid_q; - load_aw_lock = 1'b0; - // AW ID counter and W FIFO - w_cnt_up = 1'b0; - // ATOP injection into ar counter - atop_inject = 1'b0; - // we had an arbitration decision, the valid is locked, wait for the transaction - if (lock_aw_valid_q) begin - aw_valid = 1'b1; - // transaction - if (aw_ready) begin - slv_aw_ready = 1'b1; - lock_aw_valid_d = 1'b0; - load_aw_lock = 1'b1; - // inject the ATOP if necessary - atop_inject = slv_aw_chan.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; - end - end else begin - // An AW can be handled if `i_aw_id_counter` and `i_counter_open_w` are not full. An ATOP that - // requires an R response can be handled if additionally `i_ar_id_counter` is not full (this - // only applies if ATOPs are supported at all). - if (!aw_id_cnt_full && (w_open != {IdCounterWidth{1'b1}}) && - (!(ar_id_cnt_full && slv_aw_chan.atop[axi_pkg::ATOP_R_RESP]) || - !AtopSupport)) begin - // There is a valid AW vector make the id lookup and go further, if it passes. - // Also stall if previous transmitted AWs still have active W's in flight. - // This prevents deadlocking of the W channel. The counters are there for the - // Handling of the B responses. - if (slv_aw_valid && - ((w_open == '0) || (w_select == slv_aw_select)) && - (!aw_select_occupied || (slv_aw_select == lookup_aw_select))) begin - // connect the handshake - aw_valid = 1'b1; - // push arbitration to the W FIFO regardless, do not wait for the AW transaction - w_cnt_up = 1'b1; - // on AW transaction - if (aw_ready) begin - slv_aw_ready = 1'b1; - atop_inject = slv_aw_chan.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; - // no AW transaction this cycle, lock the decision - end else begin - lock_aw_valid_d = 1'b1; - load_aw_lock = 1'b1; - end - end - end - end - end - - // lock the valid signal, as the selection gets pushed into the W FIFO on first assertion, - // prevent further pushing - `FFLARN(lock_aw_valid_q, lock_aw_valid_d, load_aw_lock, '0, clk_i, rst_ni) - - if (UniqueIds) begin : gen_unique_ids_aw - // If the `UniqueIds` parameter is set, each write transaction has an ID that is unique among - // all in-flight write transactions, or all write transactions with a given ID target the same - // master port as all write transactions with the same ID, or both. This means that the - // signals that are driven by the ID counters if this parameter is not set can instead be - // derived from existing signals. The ID counters can therefore be omitted. - assign lookup_aw_select = slv_aw_select; - assign aw_select_occupied = 1'b0; - assign aw_id_cnt_full = 1'b0; - end else begin : gen_aw_id_counter - axi_demux_id_counters #( - .AxiIdBits ( AxiLookBits ), - .CounterWidth ( IdCounterWidth ), - .mst_port_select_t ( select_t ) - ) i_aw_id_counter ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .lookup_axi_id_i ( slv_aw_chan.id[0+:AxiLookBits] ), - .lookup_mst_select_o ( lookup_aw_select ), - .lookup_mst_select_occupied_o ( aw_select_occupied ), - .full_o ( aw_id_cnt_full ), - .inject_axi_id_i ( '0 ), - .inject_i ( 1'b0 ), - .push_axi_id_i ( slv_aw_chan.id[0+:AxiLookBits] ), - .push_mst_select_i ( slv_aw_select ), - .push_i ( w_cnt_up ), - .pop_axi_id_i ( slv_b_chan.id[0+:AxiLookBits] ), - .pop_i ( slv_b_valid & slv_b_ready ) - ); - // pop from ID counter on outward transaction - end - - // This counter steers the demultiplexer of the W channel. - // `w_select` determines, which handshaking is connected. - // AWs are only forwarded, if the counter is empty, or `w_select_q` is the same as - // `slv_aw_select`. - counter #( - .WIDTH ( IdCounterWidth ), - .STICKY_OVERFLOW ( 1'b0 ) - ) i_counter_open_w ( - .clk_i, - .rst_ni, - .clear_i ( 1'b0 ), - .en_i ( w_cnt_up ^ w_cnt_down ), - .load_i ( 1'b0 ), - .down_i ( w_cnt_down ), - .d_i ( '0 ), - .q_o ( w_open ), - .overflow_o ( /*not used*/ ) - ); - - `FFLARN(w_select_q, slv_aw_select, w_cnt_up, select_t'(0), clk_i, rst_ni) - assign w_select = (|w_open) ? w_select_q : slv_aw_select; - assign w_select_valid = w_cnt_up | (|w_open); - - //-------------------------------------- - // W Channel - //-------------------------------------- - spill_register #( - .T ( w_chan_t ), - .Bypass ( ~SpillW ) - ) i_w_spill_reg( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.w_valid ), - .ready_o ( slv_resp_o.w_ready ), - .data_i ( slv_req_i.w ), - .valid_o ( slv_w_valid ), - .ready_i ( slv_w_ready ), - .data_o ( slv_w_chan ) - ); - - //-------------------------------------- - // B Channel - //-------------------------------------- - // optional spill register - spill_register #( - .T ( b_chan_t ), - .Bypass ( ~SpillB ) - ) i_b_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_b_valid ), - .ready_o ( slv_b_ready ), - .data_i ( slv_b_chan ), - .valid_o ( slv_resp_o.b_valid ), - .ready_i ( slv_req_i.b_ready ), - .data_o ( slv_resp_o.b ) - ); - - // Arbitration of the different B responses - rr_arb_tree #( - .NumIn ( NoMstPorts ), - .DataType ( b_chan_t ), - .AxiVldRdy( 1'b1 ), - .LockIn ( 1'b1 ) - ) i_b_mux ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .flush_i( 1'b0 ), - .rr_i ( '0 ), - .req_i ( mst_b_valids ), - .gnt_o ( mst_b_readies ), - .data_i ( mst_b_chans ), - .gnt_i ( slv_b_ready ), - .req_o ( slv_b_valid ), - .data_o ( slv_b_chan ), - .idx_o ( ) - ); - - //-------------------------------------- - // AR Channel - //-------------------------------------- - ar_chan_t slv_ar_chan; - select_t slv_ar_select; - spill_register #( - .T ( ar_chan_t ), - .Bypass ( ~SpillAr ) - ) i_ar_chan_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.ar_valid ), - .ready_o ( slv_ar_ready_chan ), - .data_i ( slv_req_i.ar ), - .valid_o ( ar_valid_chan ), - .ready_i ( slv_ar_ready ), - .data_o ( slv_ar_chan ) - ); - spill_register #( - .T ( select_t ), - .Bypass ( ~SpillAr ) - ) i_ar_sel_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.ar_valid ), - .ready_o ( slv_ar_ready_sel ), - .data_i ( slv_ar_select_i ), - .valid_o ( ar_valid_sel ), - .ready_i ( slv_ar_ready ), - .data_o ( slv_ar_select ) - ); - assign slv_resp_o.ar_ready = slv_ar_ready_chan & slv_ar_ready_sel; - assign slv_ar_valid = ar_valid_chan & ar_valid_sel; - - // control of the AR handshake - always_comb begin - // AXI Handshakes - slv_ar_ready = 1'b0; - ar_valid = 1'b0; - // `lock_ar_valid`: Used to be protocol conform as it is not allowed to deassert `ar_valid` - // if there was no corresponding `ar_ready`. There is the possibility that an injection - // of a R response from an `atop` from the AW channel can change the occupied flag of the - // `i_ar_id_counter`, even if it was previously empty. This FF prevents the deassertion. - lock_ar_valid_d = lock_ar_valid_q; - load_ar_lock = 1'b0; - // AR id counter - ar_push = 1'b0; - // The process had an arbitration decision in a previous cycle, the valid is locked, - // wait for the AR transaction. - if (lock_ar_valid_q) begin - ar_valid = 1'b1; - // transaction - if (ar_ready) begin - slv_ar_ready = 1'b1; - ar_push = 1'b1; - lock_ar_valid_d = 1'b0; - load_ar_lock = 1'b1; - end - end else begin - // The process can start handling AR transaction if `i_ar_id_counter` has space. - if (!ar_id_cnt_full) begin - // There is a valid AR, so look the ID up. - if (slv_ar_valid && (!ar_select_occupied || - (slv_ar_select == lookup_ar_select))) begin - // connect the AR handshake - ar_valid = 1'b1; - // on transaction - if (ar_ready) begin - slv_ar_ready = 1'b1; - ar_push = 1'b1; - // no transaction this cycle, lock the valid decision! - end else begin - lock_ar_valid_d = 1'b1; - load_ar_lock = 1'b1; - end - end - end - end - end - - // this ff is needed so that ar does not get de-asserted if an atop gets injected - `FFLARN(lock_ar_valid_q, lock_ar_valid_d, load_ar_lock, '0, clk_i, rst_ni) - - if (UniqueIds) begin : gen_unique_ids_ar - // If the `UniqueIds` parameter is set, each read transaction has an ID that is unique among - // all in-flight read transactions, or all read transactions with a given ID target the same - // master port as all read transactions with the same ID, or both. This means that the - // signals that are driven by the ID counters if this parameter is not set can instead be - // derived from existing signals. The ID counters can therefore be omitted. - assign lookup_ar_select = slv_ar_select; - assign ar_select_occupied = 1'b0; - assign ar_id_cnt_full = 1'b0; - end else begin : gen_ar_id_counter - axi_demux_id_counters #( - .AxiIdBits ( AxiLookBits ), - .CounterWidth ( IdCounterWidth ), - .mst_port_select_t ( select_t ) - ) i_ar_id_counter ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .lookup_axi_id_i ( slv_ar_chan.id[0+:AxiLookBits] ), - .lookup_mst_select_o ( lookup_ar_select ), - .lookup_mst_select_occupied_o ( ar_select_occupied ), - .full_o ( ar_id_cnt_full ), - .inject_axi_id_i ( slv_aw_chan.id[0+:AxiLookBits] ), - .inject_i ( atop_inject ), - .push_axi_id_i ( slv_ar_chan.id[0+:AxiLookBits] ), - .push_mst_select_i ( slv_ar_select ), - .push_i ( ar_push ), - .pop_axi_id_i ( slv_r_chan.id[0+:AxiLookBits] ), - .pop_i ( slv_r_valid & slv_r_ready & slv_r_chan.last ) - ); - end - - //-------------------------------------- - // R Channel - //-------------------------------------- - // optional spill register - spill_register #( - .T ( r_chan_t ), - .Bypass ( ~SpillR ) - ) i_r_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_r_valid ), - .ready_o ( slv_r_ready ), - .data_i ( slv_r_chan ), - .valid_o ( slv_resp_o.r_valid ), - .ready_i ( slv_req_i.r_ready ), - .data_o ( slv_resp_o.r ) - ); - - // Arbitration of the different r responses - rr_arb_tree #( - .NumIn ( NoMstPorts ), - .DataType ( r_chan_t ), - .AxiVldRdy( 1'b1 ), - .LockIn ( 1'b1 ) - ) i_r_mux ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .flush_i( 1'b0 ), - .rr_i ( '0 ), - .req_i ( mst_r_valids ), - .gnt_o ( mst_r_readies ), - .data_i ( mst_r_chans ), - .gnt_i ( slv_r_ready ), - .req_o ( slv_r_valid ), - .data_o ( slv_r_chan ), - .idx_o ( ) - ); - - assign ar_ready = ar_valid & mst_resps_i[slv_ar_select].ar_ready; - assign aw_ready = aw_valid & mst_resps_i[slv_aw_select].aw_ready; - - // process that defines the individual demuxes and assignments for the arbitration - // as mst_reqs_o has to be drivem from the same always comb block! - always_comb begin - // default assignments - mst_reqs_o = '0; - slv_w_ready = 1'b0; - w_cnt_down = 1'b0; - - for (int unsigned i = 0; i < NoMstPorts; i++) begin - // AW channel - mst_reqs_o[i].aw = slv_aw_chan; - mst_reqs_o[i].aw_valid = 1'b0; - if (aw_valid && (slv_aw_select == i)) begin - mst_reqs_o[i].aw_valid = 1'b1; - end - - // W channel - mst_reqs_o[i].w = slv_w_chan; - mst_reqs_o[i].w_valid = 1'b0; - if (w_select_valid && (w_select == i)) begin - mst_reqs_o[i].w_valid = slv_w_valid; - slv_w_ready = mst_resps_i[i].w_ready; - w_cnt_down = slv_w_valid & mst_resps_i[i].w_ready & slv_w_chan.last; - end - - // B channel - mst_reqs_o[i].b_ready = mst_b_readies[i]; - - // AR channel - mst_reqs_o[i].ar = slv_ar_chan; - mst_reqs_o[i].ar_valid = 1'b0; - if (ar_valid && (slv_ar_select == i)) begin - mst_reqs_o[i].ar_valid = 1'b1; - end - - // R channel - mst_reqs_o[i].r_ready = mst_r_readies[i]; - end - end - // unpack the response B and R channels for the arbitration - for (genvar i = 0; i < NoMstPorts; i++) begin : gen_b_channels - assign mst_b_chans[i] = mst_resps_i[i].b; - assign mst_b_valids[i] = mst_resps_i[i].b_valid; - assign mst_r_chans[i] = mst_resps_i[i].r; - assign mst_r_valids[i] = mst_resps_i[i].r_valid; - end - - -// Validate parameters. -// pragma translate_off -`ifndef VERILATOR -`ifndef XSIM - initial begin: validate_params - no_mst_ports: assume (NoMstPorts > 0) else - $fatal(1, "The Number of slaves (NoMstPorts) has to be at least 1"); - AXI_ID_BITS: assume (AxiIdWidth >= AxiLookBits) else - $fatal(1, "AxiIdBits has to be equal or smaller than AxiIdWidth."); - end - default disable iff (!rst_ni); - aw_select: assume property( @(posedge clk_i) (slv_req_i.aw_valid |-> - (slv_aw_select_i < NoMstPorts))) else - $fatal(1, "slv_aw_select_i is %d: AW has selected a slave that is not defined.\ - NoMstPorts: %d", slv_aw_select_i, NoMstPorts); - ar_select: assume property( @(posedge clk_i) (slv_req_i.ar_valid |-> - (slv_ar_select_i < NoMstPorts))) else - $fatal(1, "slv_ar_select_i is %d: AR has selected a slave that is not defined.\ - NoMstPorts: %d", slv_ar_select_i, NoMstPorts); - aw_valid_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) |=> aw_valid) else - $fatal(1, "aw_valid was deasserted, when aw_ready = 0 in last cycle."); - ar_valid_stable: assert property( @(posedge clk_i) - (ar_valid && !ar_ready) |=> ar_valid) else - $fatal(1, "ar_valid was deasserted, when ar_ready = 0 in last cycle."); - slv_aw_chan_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) - |=> $stable(slv_aw_chan)) else - $fatal(1, "slv_aw_chan unstable with valid set."); - slv_aw_select_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) - |=> $stable(slv_aw_select)) else - $fatal(1, "slv_aw_select unstable with valid set."); - slv_ar_chan_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) - |=> $stable(slv_ar_chan)) else - $fatal(1, "slv_ar_chan unstable with valid set."); - slv_ar_select_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) - |=> $stable(slv_ar_select)) else - $fatal(1, "slv_ar_select unstable with valid set."); - internal_ar_select: assert property( @(posedge clk_i) - (ar_valid |-> slv_ar_select < NoMstPorts)) - else $fatal(1, "slv_ar_select illegal while ar_valid."); - internal_aw_select: assert property( @(posedge clk_i) - (aw_valid |-> slv_aw_select < NoMstPorts)) - else $fatal(1, "slv_aw_select illegal while aw_valid."); - w_underflow: assert property( @(posedge clk_i) - ((w_open == '0) && (w_cnt_up ^ w_cnt_down) |-> !w_cnt_down)) else - $fatal(1, "W counter underflowed!"); - `ASSUME(NoAtopAllowed, !AtopSupport && slv_req_i.aw_valid |-> slv_req_i.aw.atop == '0) -`endif -`endif -// pragma translate_on - end -endmodule - -module axi_demux_id_counters #( - // the lower bits of the AXI ID that should be considered, results in 2**AXI_ID_BITS counters - parameter int unsigned AxiIdBits = 2, - parameter int unsigned CounterWidth = 4, - parameter type mst_port_select_t = logic -) ( - input clk_i, // Clock - input rst_ni, // Asynchronous reset active low - // lookup - input logic [AxiIdBits-1:0] lookup_axi_id_i, - output mst_port_select_t lookup_mst_select_o, - output logic lookup_mst_select_occupied_o, - // push - output logic full_o, - input logic [AxiIdBits-1:0] push_axi_id_i, - input mst_port_select_t push_mst_select_i, - input logic push_i, - // inject ATOPs in AR channel - input logic [AxiIdBits-1:0] inject_axi_id_i, - input logic inject_i, - // pop - input logic [AxiIdBits-1:0] pop_axi_id_i, - input logic pop_i -); - localparam int unsigned NoCounters = 2**AxiIdBits; - typedef logic [CounterWidth-1:0] cnt_t; - - // registers, each gets loaded when push_en[i] - mst_port_select_t [NoCounters-1:0] mst_select_q; - - // counter signals - logic [NoCounters-1:0] push_en, inject_en, pop_en, occupied, cnt_full; + axi_req_t slv_req_cut; + axi_resp_t slv_resp_cut; + + logic slv_aw_ready_chan, slv_aw_ready_sel; + logic slv_aw_valid_chan, slv_aw_valid_sel; + + logic slv_ar_ready_chan, slv_ar_ready_sel; + logic slv_ar_valid_chan, slv_ar_valid_sel; + + select_t slv_aw_select, slv_ar_select; + + spill_register #( + .T ( aw_chan_t ), + .Bypass ( ~SpillAw ) + ) i_aw_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_aw_ready_chan ), + .data_i ( slv_req_i.aw ), + .valid_o ( slv_aw_valid_chan ), + .ready_i ( slv_resp_cut.aw_ready ), + .data_o ( slv_req_cut.aw ) + ); + spill_register #( + .T ( select_t ), + .Bypass ( ~SpillAw ) + ) i_aw_select_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_aw_ready_sel ), + .data_i ( slv_aw_select_i ), + .valid_o ( slv_aw_valid_sel ), + .ready_i ( slv_resp_cut.aw_ready ), + .data_o ( slv_aw_select ) + ); - //----------------------------------- - // Lookup - //----------------------------------- - assign lookup_mst_select_o = mst_select_q[lookup_axi_id_i]; - assign lookup_mst_select_occupied_o = occupied[lookup_axi_id_i]; - //----------------------------------- - // Push and Pop - //----------------------------------- - assign push_en = (push_i) ? (1 << push_axi_id_i) : '0; - assign inject_en = (inject_i) ? (1 << inject_axi_id_i) : '0; - assign pop_en = (pop_i) ? (1 << pop_axi_id_i) : '0; - assign full_o = |cnt_full; - // counters - for (genvar i = 0; i < NoCounters; i++) begin : gen_counters - logic cnt_en, cnt_down, overflow; - cnt_t cnt_delta, in_flight; - always_comb begin - unique case ({push_en[i], inject_en[i], pop_en[i]}) - 3'b001 : begin // pop_i = -1 - cnt_en = 1'b1; - cnt_down = 1'b1; - cnt_delta = cnt_t'(1); - end - 3'b010 : begin // inject_i = +1 - cnt_en = 1'b1; - cnt_down = 1'b0; - cnt_delta = cnt_t'(1); - end - // 3'b011, inject_i & pop_i = 0 --> use default - 3'b100 : begin // push_i = +1 - cnt_en = 1'b1; - cnt_down = 1'b0; - cnt_delta = cnt_t'(1); - end - // 3'b101, push_i & pop_i = 0 --> use default - 3'b110 : begin // push_i & inject_i = +2 - cnt_en = 1'b1; - cnt_down = 1'b0; - cnt_delta = cnt_t'(2); - end - 3'b111 : begin // push_i & inject_i & pop_i = +1 - cnt_en = 1'b1; - cnt_down = 1'b0; - cnt_delta = cnt_t'(1); - end - default : begin // do nothing to the counters - cnt_en = 1'b0; - cnt_down = 1'b0; - cnt_delta = cnt_t'(0); - end - endcase - end + assign slv_resp_o.aw_ready = slv_aw_ready_chan & slv_aw_ready_sel; + assign slv_req_cut.aw_valid = slv_aw_valid_chan & slv_aw_valid_sel; + + spill_register #( + .T ( w_chan_t ), + .Bypass ( ~SpillW ) + ) i_w_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.w_valid ), + .ready_o ( slv_resp_o.w_ready ), + .data_i ( slv_req_i.w ), + .valid_o ( slv_req_cut.w_valid ), + .ready_i ( slv_resp_cut.w_ready ), + .data_o ( slv_req_cut.w ) + ); + spill_register #( + .T ( ar_chan_t ), + .Bypass ( ~SpillAr ) + ) i_ar_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_ar_ready_chan ), + .data_i ( slv_req_i.ar ), + .valid_o ( slv_ar_valid_chan ), + .ready_i ( slv_resp_cut.ar_ready ), + .data_o ( slv_req_cut.ar ) + ); + spill_register #( + .T ( select_t ), + .Bypass ( ~SpillAr ) + ) i_ar_sel_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_ar_ready_sel ), + .data_i ( slv_ar_select_i ), + .valid_o ( slv_ar_valid_sel ), + .ready_i ( slv_resp_cut.ar_ready ), + .data_o ( slv_ar_select ) + ); - delta_counter #( - .WIDTH ( CounterWidth ), - .STICKY_OVERFLOW ( 1'b0 ) - ) i_in_flight_cnt ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .clear_i ( 1'b0 ), - .en_i ( cnt_en ), - .load_i ( 1'b0 ), - .down_i ( cnt_down ), - .delta_i ( cnt_delta ), - .d_i ( '0 ), - .q_o ( in_flight ), - .overflow_o ( overflow ) - ); - assign occupied[i] = |in_flight; - assign cnt_full[i] = overflow | (&in_flight); + assign slv_resp_o.ar_ready = slv_ar_ready_chan & slv_ar_ready_sel; + assign slv_req_cut.ar_valid = slv_ar_valid_chan & slv_ar_valid_sel; + + spill_register #( + .T ( b_chan_t ), + .Bypass ( ~SpillB ) + ) i_b_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_resp_cut.b_valid ), + .ready_o ( slv_req_cut.b_ready ), + .data_i ( slv_resp_cut.b ), + .valid_o ( slv_resp_o.b_valid ), + .ready_i ( slv_req_i.b_ready ), + .data_o ( slv_resp_o.b ) + ); + spill_register #( + .T ( r_chan_t ), + .Bypass ( ~SpillR ) + ) i_r_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_resp_cut.r_valid ), + .ready_o ( slv_req_cut.r_ready ), + .data_i ( slv_resp_cut.r ), + .valid_o ( slv_resp_o.r_valid ), + .ready_i ( slv_req_i.r_ready ), + .data_o ( slv_resp_o.r ) + ); - // holds the selection signal for this id - `FFLARN(mst_select_q[i], push_mst_select_i, push_en[i], '0, clk_i, rst_ni) + axi_demux_simple #( + .AxiIdWidth ( AxiIdWidth ), + .AtopSupport( AtopSupport ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( NoMstPorts ), + .MaxTrans ( MaxTrans ), + .AxiLookBits( AxiLookBits ), + .UniqueIds ( UniqueIds ) + ) i_demux_simple ( + .clk_i, + .rst_ni, + .test_i, + + .slv_req_i ( slv_req_cut ), + .slv_aw_select_i ( slv_aw_select ), + .slv_ar_select_i ( slv_ar_select ), + .slv_resp_o ( slv_resp_cut ), + .mst_reqs_o ( mst_reqs_o ), + .mst_resps_i ( mst_resps_i ) + ); -// pragma translate_off -`ifndef VERILATOR -`ifndef XSIM - // Validate parameters. - cnt_underflow: assert property( - @(posedge clk_i) disable iff (~rst_ni) (pop_en[i] |=> !overflow)) else - $fatal(1, "axi_demux_id_counters > Counter: %0d underflowed.\ - The reason is probably a faulty AXI response.", i); -`endif -`endif -// pragma translate_on - end endmodule // interface wrapper diff --git a/src/axi_demux_simple.sv b/src/axi_demux_simple.sv new file mode 100644 index 000000000..f77514c4f --- /dev/null +++ b/src/axi_demux_simple.sv @@ -0,0 +1,631 @@ +// Copyright (c) 2019 ETH Zurich and 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: +// - Wolfgang Roenninger +// - Andreas Kurth + +`include "common_cells/assertions.svh" +`include "common_cells/registers.svh" +`include "axi/assign.svh" + +`ifdef QUESTA +// Derive `TARGET_VSIM`, which is used for tool-specific workarounds in this file, from `QUESTA`, +// which is automatically set in Questa. +`define TARGET_VSIM +`endif + +/// Demultiplex one AXI4+ATOP slave port to multiple AXI4+ATOP master ports. +/// +/// The AW and AR slave channels each have a `select` input to determine to which master port the +/// current request is sent. The `select` can, for example, be driven by an address decoding module +/// to map address ranges to different AXI slaves. +/// +/// ## Design overview +/// +/// ![Block diagram](module.axi_demux.png "Block diagram") +/// +/// Beats on the W channel are routed by demultiplexer according to the selection for the +/// corresponding AW beat. This relies on the AXI property that W bursts must be sent in the same +/// order as AW beats and beats from different W bursts may not be interleaved. +/// +/// Beats on the B and R channel are multiplexed from the master ports to the slave port with +/// a round-robin arbitration tree. +module axi_demux_simple #( + parameter int unsigned AxiIdWidth = 32'd0, + parameter bit AtopSupport = 1'b1, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic, + parameter int unsigned NoMstPorts = 32'd0, + parameter int unsigned MaxTrans = 32'd8, + parameter int unsigned AxiLookBits = 32'd3, + parameter bit UniqueIds = 1'b0, + // Dependent parameters, DO NOT OVERRIDE! + parameter int unsigned SelectWidth = (NoMstPorts > 32'd1) ? $clog2(NoMstPorts) : 32'd1, + parameter type select_t = logic [SelectWidth-1:0] +) ( + input logic clk_i, + input logic rst_ni, + input logic test_i, + // Slave Port + input axi_req_t slv_req_i, + input select_t slv_aw_select_i, + input select_t slv_ar_select_i, + output axi_resp_t slv_resp_o, + // Master Ports + output axi_req_t [NoMstPorts-1:0] mst_reqs_o, + input axi_resp_t [NoMstPorts-1:0] mst_resps_i +); + + localparam int unsigned IdCounterWidth = cf_math_pkg::idx_width(MaxTrans); + typedef logic [IdCounterWidth-1:0] id_cnt_t; + + // pass through if only one master port + if (NoMstPorts == 32'h1) begin : gen_no_demux + `AXI_ASSIGN_REQ_STRUCT(mst_reqs_o[0], slv_req_i) + `AXI_ASSIGN_RESP_STRUCT(slv_resp_o, mst_resps_i[0]) + end else begin + + //-------------------------------------- + //-------------------------------------- + // Signal Declarations + //-------------------------------------- + //-------------------------------------- + + //-------------------------------------- + // Write Transaction + //-------------------------------------- + + // Register which locks the AW valid signal + logic lock_aw_valid_d, lock_aw_valid_q, load_aw_lock; + logic aw_valid, aw_ready; + + // AW ID counter + select_t lookup_aw_select; + logic aw_select_occupied, aw_id_cnt_full; + // Upon an ATOP load, inject IDs from the AW into the AR channel + logic atop_inject; + + // W select counter: stores the decision to which master W beats should go + select_t w_select, w_select_q; + logic w_select_valid; + id_cnt_t w_open; + logic w_cnt_up, w_cnt_down; + + // B channles input into the arbitration + logic [NoMstPorts-1:0] mst_b_valids, mst_b_readies; + + //-------------------------------------- + // Read Transaction + //-------------------------------------- + + // AR ID counter + select_t lookup_ar_select; + logic ar_select_occupied, ar_id_cnt_full; + logic ar_push; + + // Register which locks the AR valid signel + logic lock_ar_valid_d, lock_ar_valid_q, load_ar_lock; + logic ar_valid, ar_ready; + + logic [NoMstPorts-1:0] mst_r_valids, mst_r_readies; + + + + + + + + //-------------------------------------- + // Channel Control + //-------------------------------------- + //-------------------------------------- + + //-------------------------------------- + // AW Channel + //-------------------------------------- + + // Control of the AW handshake + always_comb begin + // AXI Handshakes + slv_resp_o.aw_ready = 1'b0; + aw_valid = 1'b0; + // `lock_aw_valid`, used to be protocol conform as it is not allowed to deassert + // a valid if there was no corresponding ready. As this process has to be able to inject + // an AXI ID into the counter of the AR channel on an ATOP, there could be a case where + // this process waits on `aw_ready` but in the mean time on the AR channel the counter gets + // full. + lock_aw_valid_d = lock_aw_valid_q; + load_aw_lock = 1'b0; + // AW ID counter and W FIFO + w_cnt_up = 1'b0; + // ATOP injection into ar counter + atop_inject = 1'b0; + // we had an arbitration decision, the valid is locked, wait for the transaction + if (lock_aw_valid_q) begin + aw_valid = 1'b1; + // transaction + if (aw_ready) begin + slv_resp_o.aw_ready = 1'b1; + lock_aw_valid_d = 1'b0; + load_aw_lock = 1'b1; + // inject the ATOP if necessary + atop_inject = slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; + end + end else begin + // An AW can be handled if `i_aw_id_counter` and `i_counter_open_w` are not full. An ATOP that + // requires an R response can be handled if additionally `i_ar_id_counter` is not full (this + // only applies if ATOPs are supported at all). + if (!aw_id_cnt_full && (w_open != {IdCounterWidth{1'b1}}) && + (!(ar_id_cnt_full && slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) || + !AtopSupport)) begin + // There is a valid AW vector make the id lookup and go further, if it passes. + // Also stall if previous transmitted AWs still have active W's in flight. + // This prevents deadlocking of the W channel. The counters are there for the + // Handling of the B responses. + if (slv_req_i.aw_valid && + ((w_open == '0) || (w_select == slv_aw_select_i)) && + (!aw_select_occupied || (slv_aw_select_i == lookup_aw_select))) begin + // connect the handshake + aw_valid = 1'b1; + // push arbitration to the W FIFO regardless, do not wait for the AW transaction + w_cnt_up = 1'b1; + // on AW transaction + if (aw_ready) begin + slv_resp_o.aw_ready = 1'b1; + atop_inject = slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; + // no AW transaction this cycle, lock the decision + end else begin + lock_aw_valid_d = 1'b1; + load_aw_lock = 1'b1; + end + end + end + end + end + + // lock the valid signal, as the selection gets pushed into the W FIFO on first assertion, + // prevent further pushing + `FFLARN(lock_aw_valid_q, lock_aw_valid_d, load_aw_lock, '0, clk_i, rst_ni) + + if (UniqueIds) begin : gen_unique_ids_aw + // If the `UniqueIds` parameter is set, each write transaction has an ID that is unique among + // all in-flight write transactions, or all write transactions with a given ID target the same + // master port as all write transactions with the same ID, or both. This means that the + // signals that are driven by the ID counters if this parameter is not set can instead be + // derived from existing signals. The ID counters can therefore be omitted. + assign lookup_aw_select = slv_aw_select_i; + assign aw_select_occupied = 1'b0; + assign aw_id_cnt_full = 1'b0; + end else begin : gen_aw_id_counter + axi_demux_id_counters #( + .AxiIdBits ( AxiLookBits ), + .CounterWidth ( IdCounterWidth ), + .mst_port_select_t ( select_t ) + ) i_aw_id_counter ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .lookup_axi_id_i ( slv_req_i.aw.id[0+:AxiLookBits] ), + .lookup_mst_select_o ( lookup_aw_select ), + .lookup_mst_select_occupied_o ( aw_select_occupied ), + .full_o ( aw_id_cnt_full ), + .inject_axi_id_i ( '0 ), + .inject_i ( 1'b0 ), + .push_axi_id_i ( slv_req_i.aw.id[0+:AxiLookBits] ), + .push_mst_select_i ( slv_aw_select_i ), + .push_i ( w_cnt_up ), + .pop_axi_id_i ( slv_resp_o.b.id[0+:AxiLookBits] ), + .pop_i ( slv_resp_o.b_valid & slv_req_i.b_ready ) + ); + // pop from ID counter on outward transaction + end + + // This counter steers the demultiplexer of the W channel. + // `w_select` determines, which handshaking is connected. + // AWs are only forwarded, if the counter is empty, or `w_select_q` is the same as + // `slv_aw_select_i`. + counter #( + .WIDTH ( IdCounterWidth ), + .STICKY_OVERFLOW ( 1'b0 ) + ) i_counter_open_w ( + .clk_i, + .rst_ni, + .clear_i ( 1'b0 ), + .en_i ( w_cnt_up ^ w_cnt_down ), + .load_i ( 1'b0 ), + .down_i ( w_cnt_down ), + .d_i ( '0 ), + .q_o ( w_open ), + .overflow_o ( /*not used*/ ) + ); + + `FFLARN(w_select_q, slv_aw_select_i, w_cnt_up, select_t'(0), clk_i, rst_ni) + assign w_select = (|w_open) ? w_select_q : slv_aw_select_i; + assign w_select_valid = w_cnt_up | (|w_open); + + //-------------------------------------- + // W Channel + //-------------------------------------- + + //-------------------------------------- + // B Channel + //-------------------------------------- + logic [cf_math_pkg::idx_width(NoMstPorts)-1:0] b_idx; + + // Arbitration of the different B responses + rr_arb_tree #( + .NumIn ( NoMstPorts ), + .DataType ( logic ), + .AxiVldRdy( 1'b1 ), + .LockIn ( 1'b1 ) + ) i_b_mux ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i( 1'b0 ), + .rr_i ( '0 ), + .req_i ( mst_b_valids ), + .gnt_o ( mst_b_readies ), + .data_i ( '0 ), + .gnt_i ( slv_req_i.b_ready ), + .req_o ( slv_resp_o.b_valid ), + .data_o ( ), + .idx_o ( b_idx ) + ); + + always_comb begin + if (slv_resp_o.b_valid) begin + `AXI_SET_B_STRUCT(slv_resp_o.b, mst_resps_i[b_idx].b) + end else begin + slv_resp_o.b = '0; + end + end + + //-------------------------------------- + // AR Channel + //-------------------------------------- + + // control of the AR handshake + always_comb begin + // AXI Handshakes + slv_resp_o.ar_ready = 1'b0; + ar_valid = 1'b0; + // `lock_ar_valid`: Used to be protocol conform as it is not allowed to deassert `ar_valid` + // if there was no corresponding `ar_ready`. There is the possibility that an injection + // of a R response from an `atop` from the AW channel can change the occupied flag of the + // `i_ar_id_counter`, even if it was previously empty. This FF prevents the deassertion. + lock_ar_valid_d = lock_ar_valid_q; + load_ar_lock = 1'b0; + // AR id counter + ar_push = 1'b0; + // The process had an arbitration decision in a previous cycle, the valid is locked, + // wait for the AR transaction. + if (lock_ar_valid_q) begin + ar_valid = 1'b1; + // transaction + if (ar_ready) begin + slv_resp_o.ar_ready = 1'b1; + ar_push = 1'b1; + lock_ar_valid_d = 1'b0; + load_ar_lock = 1'b1; + end + end else begin + // The process can start handling AR transaction if `i_ar_id_counter` has space. + if (!ar_id_cnt_full) begin + // There is a valid AR, so look the ID up. + if (slv_req_i.ar_valid && (!ar_select_occupied || + (slv_ar_select_i == lookup_ar_select))) begin + // connect the AR handshake + ar_valid = 1'b1; + // on transaction + if (ar_ready) begin + slv_resp_o.ar_ready = 1'b1; + ar_push = 1'b1; + // no transaction this cycle, lock the valid decision! + end else begin + lock_ar_valid_d = 1'b1; + load_ar_lock = 1'b1; + end + end + end + end + end + + // this ff is needed so that ar does not get de-asserted if an atop gets injected + `FFLARN(lock_ar_valid_q, lock_ar_valid_d, load_ar_lock, '0, clk_i, rst_ni) + + if (UniqueIds) begin : gen_unique_ids_ar + // If the `UniqueIds` parameter is set, each read transaction has an ID that is unique among + // all in-flight read transactions, or all read transactions with a given ID target the same + // master port as all read transactions with the same ID, or both. This means that the + // signals that are driven by the ID counters if this parameter is not set can instead be + // derived from existing signals. The ID counters can therefore be omitted. + assign lookup_ar_select = slv_ar_select_i; + assign ar_select_occupied = 1'b0; + assign ar_id_cnt_full = 1'b0; + end else begin : gen_ar_id_counter + axi_demux_id_counters #( + .AxiIdBits ( AxiLookBits ), + .CounterWidth ( IdCounterWidth ), + .mst_port_select_t ( select_t ) + ) i_ar_id_counter ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .lookup_axi_id_i ( slv_req_i.ar.id[0+:AxiLookBits] ), + .lookup_mst_select_o ( lookup_ar_select ), + .lookup_mst_select_occupied_o ( ar_select_occupied ), + .full_o ( ar_id_cnt_full ), + .inject_axi_id_i ( slv_req_i.aw.id[0+:AxiLookBits] ), + .inject_i ( atop_inject ), + .push_axi_id_i ( slv_req_i.ar.id[0+:AxiLookBits] ), + .push_mst_select_i ( slv_ar_select_i ), + .push_i ( ar_push ), + .pop_axi_id_i ( slv_resp_o.r.id[0+:AxiLookBits] ), + .pop_i ( slv_resp_o.r_valid & slv_req_i.r_ready & slv_resp_o.r.last ) + ); + end + + //-------------------------------------- + // R Channel + //-------------------------------------- + + logic [cf_math_pkg::idx_width(NoMstPorts)-1:0] r_idx; + + // Arbitration of the different r responses + rr_arb_tree #( + .NumIn ( NoMstPorts ), + .DataType ( logic ), + .AxiVldRdy( 1'b1 ), + .LockIn ( 1'b1 ) + ) i_r_mux ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i( 1'b0 ), + .rr_i ( '0 ), + .req_i ( mst_r_valids ), + .gnt_o ( mst_r_readies ), + .data_i ( '0 ), + .gnt_i ( slv_req_i.r_ready ), + .req_o ( slv_resp_o.r_valid ), + .data_o (), + .idx_o ( r_idx ) + ); + + always_comb begin + if (slv_resp_o.r_valid) begin + `AXI_SET_R_STRUCT(slv_resp_o.r, mst_resps_i[r_idx].r) + end else begin + slv_resp_o.r = '0; + end + end + + assign ar_ready = ar_valid & mst_resps_i[slv_ar_select_i].ar_ready; + assign aw_ready = aw_valid & mst_resps_i[slv_aw_select_i].aw_ready; + + // process that defines the individual demuxes and assignments for the arbitration + // as mst_reqs_o has to be drivem from the same always comb block! + always_comb begin + // default assignments + mst_reqs_o = '0; + slv_resp_o.w_ready = 1'b0; + w_cnt_down = 1'b0; + + for (int unsigned i = 0; i < NoMstPorts; i++) begin + // AW channel + mst_reqs_o[i].aw = slv_req_i.aw; + mst_reqs_o[i].aw_valid = 1'b0; + if (aw_valid && (slv_aw_select_i == i)) begin + mst_reqs_o[i].aw_valid = 1'b1; + end + + // W channel + mst_reqs_o[i].w = slv_req_i.w; + mst_reqs_o[i].w_valid = 1'b0; + if (w_select_valid && (w_select == i)) begin + mst_reqs_o[i].w_valid = slv_req_i.w_valid; + slv_resp_o.w_ready = mst_resps_i[i].w_ready; + w_cnt_down = slv_req_i.w_valid & mst_resps_i[i].w_ready & slv_req_i.w.last; + end + + // B channel + mst_reqs_o[i].b_ready = mst_b_readies[i]; + + // AR channel + mst_reqs_o[i].ar = slv_req_i.ar; + mst_reqs_o[i].ar_valid = 1'b0; + if (ar_valid && (slv_ar_select_i == i)) begin + mst_reqs_o[i].ar_valid = 1'b1; + end + + // R channel + mst_reqs_o[i].r_ready = mst_r_readies[i]; + end + end + // unpack the response B and R channels for the arbitration + for (genvar i = 0; i < NoMstPorts; i++) begin : gen_b_channels + // assign mst_b_chans[i] = mst_resps_i[i].b; + assign mst_b_valids[i] = mst_resps_i[i].b_valid; + // assign mst_r_chans[i] = mst_resps_i[i].r; + assign mst_r_valids[i] = mst_resps_i[i].r_valid; + end + +// Validate parameters. +// pragma translate_off +`ifndef VERILATOR +`ifndef XSIM + initial begin: validate_params + no_mst_ports: assume (NoMstPorts > 0) else + $fatal(1, "The Number of slaves (NoMstPorts) has to be at least 1"); + AXI_ID_BITS: assume (AxiIdWidth >= AxiLookBits) else + $fatal(1, "AxiIdBits has to be equal or smaller than AxiIdWidth."); + end + default disable iff (!rst_ni); + aw_select: assume property( @(posedge clk_i) (slv_req_i.aw_valid |-> + (slv_aw_select_i < NoMstPorts))) else + $fatal(1, "slv_aw_select_i is %d: AW has selected a slave that is not defined.\ + NoMstPorts: %d", slv_aw_select_i, NoMstPorts); + ar_select: assume property( @(posedge clk_i) (slv_req_i.ar_valid |-> + (slv_ar_select_i < NoMstPorts))) else + $fatal(1, "slv_ar_select_i is %d: AR has selected a slave that is not defined.\ + NoMstPorts: %d", slv_ar_select_i, NoMstPorts); + aw_valid_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) |=> aw_valid) else + $fatal(1, "aw_valid was deasserted, when aw_ready = 0 in last cycle."); + ar_valid_stable: assert property( @(posedge clk_i) + (ar_valid && !ar_ready) |=> ar_valid) else + $fatal(1, "ar_valid was deasserted, when ar_ready = 0 in last cycle."); + slv_aw_chan_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) + |=> $stable(slv_req_i.aw)) else + $fatal(1, "slv_aw_chan unstable with valid set."); + slv_aw_select_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) + |=> $stable(slv_aw_select_i)) else + $fatal(1, "slv_aw_select_i unstable with valid set."); + slv_ar_chan_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) + |=> $stable(slv_req_i.ar)) else + $fatal(1, "slv_ar_chan unstable with valid set."); + slv_ar_select_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) + |=> $stable(slv_ar_select_i)) else + $fatal(1, "slv_ar_select_i unstable with valid set."); + internal_ar_select: assert property( @(posedge clk_i) + (ar_valid |-> slv_ar_select_i < NoMstPorts)) + else $fatal(1, "slv_ar_select_i illegal while ar_valid."); + internal_aw_select: assert property( @(posedge clk_i) + (aw_valid |-> slv_aw_select_i < NoMstPorts)) + else $fatal(1, "slv_aw_select_i illegal while aw_valid."); + w_underflow: assert property( @(posedge clk_i) + ((w_open == '0) && (w_cnt_up ^ w_cnt_down) |-> !w_cnt_down)) else + $fatal(1, "W counter underflowed!"); + `ASSUME(NoAtopAllowed, !AtopSupport && slv_req_i.aw_valid |-> slv_req_i.aw.atop == '0) +`endif +`endif +// pragma translate_on + end +endmodule + + +module axi_demux_id_counters #( + // the lower bits of the AXI ID that should be considered, results in 2**AXI_ID_BITS counters + parameter int unsigned AxiIdBits = 2, + parameter int unsigned CounterWidth = 4, + parameter type mst_port_select_t = logic +) ( + input clk_i, // Clock + input rst_ni, // Asynchronous reset active low + // lookup + input logic [AxiIdBits-1:0] lookup_axi_id_i, + output mst_port_select_t lookup_mst_select_o, + output logic lookup_mst_select_occupied_o, + // push + output logic full_o, + input logic [AxiIdBits-1:0] push_axi_id_i, + input mst_port_select_t push_mst_select_i, + input logic push_i, + // inject ATOPs in AR channel + input logic [AxiIdBits-1:0] inject_axi_id_i, + input logic inject_i, + // pop + input logic [AxiIdBits-1:0] pop_axi_id_i, + input logic pop_i +); + localparam int unsigned NoCounters = 2**AxiIdBits; + typedef logic [CounterWidth-1:0] cnt_t; + + // registers, each gets loaded when push_en[i] + mst_port_select_t [NoCounters-1:0] mst_select_q; + + // counter signals + logic [NoCounters-1:0] push_en, inject_en, pop_en, occupied, cnt_full; + + //----------------------------------- + // Lookup + //----------------------------------- + assign lookup_mst_select_o = mst_select_q[lookup_axi_id_i]; + assign lookup_mst_select_occupied_o = occupied[lookup_axi_id_i]; + //----------------------------------- + // Push and Pop + //----------------------------------- + assign push_en = (push_i) ? (1 << push_axi_id_i) : '0; + assign inject_en = (inject_i) ? (1 << inject_axi_id_i) : '0; + assign pop_en = (pop_i) ? (1 << pop_axi_id_i) : '0; + assign full_o = |cnt_full; + // counters + for (genvar i = 0; i < NoCounters; i++) begin : gen_counters + logic cnt_en, cnt_down, overflow; + cnt_t cnt_delta, in_flight; + always_comb begin + unique case ({push_en[i], inject_en[i], pop_en[i]}) + 3'b001 : begin // pop_i = -1 + cnt_en = 1'b1; + cnt_down = 1'b1; + cnt_delta = cnt_t'(1); + end + 3'b010 : begin // inject_i = +1 + cnt_en = 1'b1; + cnt_down = 1'b0; + cnt_delta = cnt_t'(1); + end + // 3'b011, inject_i & pop_i = 0 --> use default + 3'b100 : begin // push_i = +1 + cnt_en = 1'b1; + cnt_down = 1'b0; + cnt_delta = cnt_t'(1); + end + // 3'b101, push_i & pop_i = 0 --> use default + 3'b110 : begin // push_i & inject_i = +2 + cnt_en = 1'b1; + cnt_down = 1'b0; + cnt_delta = cnt_t'(2); + end + 3'b111 : begin // push_i & inject_i & pop_i = +1 + cnt_en = 1'b1; + cnt_down = 1'b0; + cnt_delta = cnt_t'(1); + end + default : begin // do nothing to the counters + cnt_en = 1'b0; + cnt_down = 1'b0; + cnt_delta = cnt_t'(0); + end + endcase + end + + delta_counter #( + .WIDTH ( CounterWidth ), + .STICKY_OVERFLOW ( 1'b0 ) + ) i_in_flight_cnt ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .clear_i ( 1'b0 ), + .en_i ( cnt_en ), + .load_i ( 1'b0 ), + .down_i ( cnt_down ), + .delta_i ( cnt_delta ), + .d_i ( '0 ), + .q_o ( in_flight ), + .overflow_o ( overflow ) + ); + assign occupied[i] = |in_flight; + assign cnt_full[i] = overflow | (&in_flight); + + // holds the selection signal for this id + `FFLARN(mst_select_q[i], push_mst_select_i, push_en[i], '0, clk_i, rst_ni) + +// pragma translate_off +`ifndef VERILATOR +`ifndef XSIM + // Validate parameters. + cnt_underflow: assert property( + @(posedge clk_i) disable iff (~rst_ni) (pop_en[i] |=> !overflow)) else + $fatal(1, "axi_demux_id_counters > Counter: %0d underflowed.\ + The reason is probably a faulty AXI response.", i); +`endif +`endif +// pragma translate_on + end +endmodule + From 1dfae098bccdad358dac2c87a9f65305d6bafb46 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 16 May 2023 18:26:23 +0200 Subject: [PATCH 124/145] Correctly split r/w in axi_to_mem_* for atops --- src/axi_to_mem_banked.sv | 2 +- src/axi_to_mem_interleaved.sv | 43 ++++++++++++++++++----------------- src/axi_to_mem_split.sv | 30 ++++++++++++++++-------- 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/axi_to_mem_banked.sv b/src/axi_to_mem_banked.sv index cc7c6198d..548339f37 100644 --- a/src/axi_to_mem_banked.sv +++ b/src/axi_to_mem_banked.sv @@ -150,7 +150,7 @@ module axi_to_mem_banked #( .NoMstPorts ( 32'd2 ), .MaxTrans ( MemLatency+2 ), // allow multiple Ax vectors to not starve W channel .AxiLookBits ( 32'd1 ), // select is fixed, do not need it - .UniqueIds ( 1'b0 ), + .UniqueIds ( 1'b1 ), // Can be set as ports are statically selected -> reduces HW .SpillAw ( 1'b1 ), .SpillW ( 1'b1 ), .SpillB ( 1'b1 ), diff --git a/src/axi_to_mem_interleaved.sv b/src/axi_to_mem_interleaved.sv index a744c1ec0..fb7e8570e 100644 --- a/src/axi_to_mem_interleaved.sv +++ b/src/axi_to_mem_interleaved.sv @@ -47,6 +47,8 @@ module axi_to_mem_interleaved #( input logic clk_i, /// Asynchronous reset, active low. input logic rst_ni, + /// Testmode enable + input logic test_i, /// The unit is busy handling an AXI4+ATOP request. output logic busy_o, /// AXI4+ATOP slave port, request input. @@ -94,27 +96,26 @@ module axi_to_mem_interleaved #( mem_data_t [NumBanks-1:0] r_mem_rdata, w_mem_rdata; // split AXI bus in read and write - always_comb begin : proc_axi_rw_split - axi_resp_o.r = r_axi_resp.r; - axi_resp_o.r_valid = r_axi_resp.r_valid; - axi_resp_o.ar_ready = r_axi_resp.ar_ready; - axi_resp_o.b = w_axi_resp.b; - axi_resp_o.b_valid = w_axi_resp.b_valid; - axi_resp_o.w_ready = w_axi_resp.w_ready; - axi_resp_o.aw_ready = w_axi_resp.aw_ready; - - w_axi_req = '0; - w_axi_req.aw = axi_req_i.aw; - w_axi_req.aw_valid = axi_req_i.aw_valid; - w_axi_req.w = axi_req_i.w; - w_axi_req.w_valid = axi_req_i.w_valid; - w_axi_req.b_ready = axi_req_i.b_ready; - - r_axi_req = '0; - r_axi_req.ar = axi_req_i.ar; - r_axi_req.ar_valid = axi_req_i.ar_valid; - r_axi_req.r_ready = axi_req_i.r_ready; - end + axi_demux_simple #( + .AxiIdWidth ( IdWidth ), + .AtopSupport ( 1'b1 ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( 2 ), + .MaxTrans ( BufDepth ), + .AxiLookBits ( 1 ), // select is fixed, do not need it + .UniqueIds ( 1'b1 ) // Can be set as ports are statically selected -> reduces HW + ) i_split_read_write ( + .clk_i, + .rst_ni, + .test_i, + .slv_req_i ( axi_req_i ), + .slv_ar_select_i ( 1'b0 ), + .slv_aw_select_i ( 1'b1 ), + .slv_resp_o ( axi_resp_o ), + .mst_reqs_o ( {w_axi_req, r_axi_req} ), + .mst_resps_i ( {w_axi_resp, r_axi_resp} ) + ); axi_to_mem #( .axi_req_t ( axi_req_t ), diff --git a/src/axi_to_mem_split.sv b/src/axi_to_mem_split.sv index 107d3fe75..1d86cc3eb 100644 --- a/src/axi_to_mem_split.sv +++ b/src/axi_to_mem_split.sv @@ -49,6 +49,8 @@ module axi_to_mem_split #( input logic clk_i, /// Asynchronous reset, active low. input logic rst_ni, + /// Testmode enable + input logic test_i, /// The unit is busy handling an AXI4+ATOP request. output logic busy_o, /// AXI4+ATOP slave port, request input. @@ -81,18 +83,26 @@ module axi_to_mem_split #( logic read_busy, write_busy; - axi_rw_split #( - .axi_req_t ( axi_req_t ), - .axi_resp_t ( axi_resp_t ) - ) i_axi_rw_split ( + // split AXI bus in read and write + axi_demux_simple #( + .AxiIdWidth ( IdWidth ), + .AtopSupport ( 1'b1 ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( 2 ), + .MaxTrans ( BufDepth ), + .AxiLookBits ( 1 ), // select is fixed, do not need it + .UniqueIds ( 1'b1 ) // Can be set as ports are statically selected -> reduces HW + ) i_split_read_write ( .clk_i, .rst_ni, - .slv_req_i ( axi_req_i ), - .slv_resp_o ( axi_resp_o ), - .mst_read_req_o ( axi_read_req ), - .mst_read_resp_i ( axi_read_resp ), - .mst_write_req_o ( axi_write_req ), - .mst_write_resp_i ( axi_write_resp ) + .test_i, + .slv_req_i ( axi_req_i ), + .slv_ar_select_i ( 1'b0 ), + .slv_aw_select_i ( 1'b1 ), + .slv_resp_o ( axi_resp_o ), + .mst_reqs_o ( {axi_write_req, axi_read_req} ), + .mst_resps_i ( {axi_write_resp, axi_read_resp} ) ); assign busy_o = read_busy || write_busy; From 9351c3d06bd5109b737715f198898886af389a45 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 16 May 2023 16:31:46 +0200 Subject: [PATCH 125/145] Add a detailed axi_to_mem to expose more signals for ATOPS --- Bender.yml | 9 +- src/axi_to_detailed_mem.sv | 649 +++++++++++++++++++++++++++++++++++++ src/axi_to_mem.sv | 412 ++--------------------- 3 files changed, 685 insertions(+), 385 deletions(-) create mode 100644 src/axi_to_detailed_mem.sv diff --git a/Bender.yml b/Bender.yml index 664995bb9..3c4a29be1 100644 --- a/Bender.yml +++ b/Bender.yml @@ -65,7 +65,7 @@ sources: - src/axi_serializer.sv - src/axi_slave_compare.sv - src/axi_throttle.sv - - src/axi_to_mem.sv + - src/axi_to_detailed_mem.sv # Level 3 - src/axi_cdc.sv - src/axi_demux.sv @@ -76,13 +76,14 @@ sources: - src/axi_lfsr.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv - - src/axi_to_mem_banked.sv - - src/axi_to_mem_interleaved.sv - - src/axi_to_mem_split.sv + - src/axi_to_mem.sv # Level 4 - src/axi_iw_converter.sv - src/axi_lite_xbar.sv - src/axi_xbar.sv + - src/axi_to_mem_banked.sv + - src/axi_to_mem_interleaved.sv + - src/axi_to_mem_split.sv # Level 5 - src/axi_xp.sv diff --git a/src/axi_to_detailed_mem.sv b/src/axi_to_detailed_mem.sv new file mode 100644 index 000000000..7a8b95f0f --- /dev/null +++ b/src/axi_to_detailed_mem.sv @@ -0,0 +1,649 @@ +// Copyright 2020 ETH Zurich and 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: +// - Michael Rogenmoser + +`include "common_cells/registers.svh" +/// AXI4+ATOP slave module which translates AXI bursts into a memory stream. +/// If both read and write channels of the AXI4+ATOP are active, both will have an +/// utilization of 50%. +module axi_to_detailed_mem #( + /// AXI4+ATOP request type. See `include/axi/typedef.svh`. + parameter type axi_req_t = logic, + /// AXI4+ATOP response type. See `include/axi/typedef.svh`. + parameter type axi_resp_t = logic, + /// Address width, has to be less or equal than the width off the AXI address field. + /// Determines the width of `mem_addr_o`. Has to be wide enough to emit the memory region + /// which should be accessible. + parameter int unsigned AddrWidth = 0, + /// AXI4+ATOP data width. + parameter int unsigned DataWidth = 0, + /// AXI4+ATOP ID width. + parameter int unsigned IdWidth = 0, + /// AXI4+ATOP user width. + parameter int unsigned UserWidth = 0, + /// Number of banks at output, must evenly divide `DataWidth`. + parameter int unsigned NumBanks = 0, + /// Depth of memory response buffer. This should be equal to the memory response latency. + parameter int unsigned BufDepth = 1, + /// Hide write requests if the strb == '0 + parameter bit HideStrb = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OutFifoDepth = 1, + /// Dependent parameter, do not override. Memory address type. + localparam type addr_t = logic [AddrWidth-1:0], + /// Dependent parameter, do not override. Memory data type. + localparam type mem_data_t = logic [DataWidth/NumBanks-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + localparam type mem_strb_t = logic [DataWidth/NumBanks/8-1:0], + /// Dependent parameter, do not override. Memory id type. + localparam type mem_id_t = logic [IdWidth-1:0], + /// Dependent parameter, do not override. Memory user type. + localparam type mem_user_t = logic [UserWidth-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// The unit is busy handling an AXI4+ATOP request. + output logic busy_o, + /// AXI4+ATOP slave port, request input. + input axi_req_t axi_req_i, + /// AXI4+ATOP slave port, response output. + output axi_resp_t axi_resp_o, + /// Memory stream master, request is valid for this bank. + output logic [NumBanks-1:0] mem_req_o, + /// Memory stream master, request can be granted by this bank. + input logic [NumBanks-1:0] mem_gnt_i, + /// Memory stream master, byte address of the request. + output addr_t [NumBanks-1:0] mem_addr_o, + /// Memory stream master, write data for this bank. Valid when `mem_req_o`. + output mem_data_t [NumBanks-1:0] mem_wdata_o, + /// Memory stream master, byte-wise strobe (byte enable). + output mem_strb_t [NumBanks-1:0] mem_strb_o, + /// Memory stream master, `axi_pkg::atop_t` signal associated with this request. + output axi_pkg::atop_t [NumBanks-1:0] mem_atop_o, + /// Memory stream master, lock signal. + output logic [NumBanks-1:0] mem_lock_o, + /// Memory stream master, write enable. Then asserted store of `mem_w_data` is requested. + output logic [NumBanks-1:0] mem_we_o, + /// Memory stream master, ID. Response ID is managed internally, ensure in-order responses. + output mem_id_t [NumBanks-1:0] mem_id_o, + /// Memory stream master, user signal. Ax channel user bits used. + output mem_user_t [NumBanks-1:0] mem_user_o, + /// Memory stream master, cache signal. + output axi_pkg::cache_t [NumBanks-1:0] mem_cache_o, + /// Memory stream master, protection signal. + output axi_pkg::prot_t [NumBanks-1:0] mem_prot_o, + /// Memory stream master, response is valid. This module expects always a response valid for a + /// request regardless if the request was a write or a read. + input logic [NumBanks-1:0] mem_rvalid_i, + /// Memory stream master, read response data. + input mem_data_t [NumBanks-1:0] mem_rdata_i, + /// Memory stream master, error response. + input logic [NumBanks-1:0] mem_err_i, + /// Memory stream master, read response exclusive access OK. + input logic [NumBanks-1:0] mem_exokay_i +); + + typedef logic [DataWidth-1:0] axi_data_t; + typedef logic [DataWidth/8-1:0] axi_strb_t; + typedef logic [IdWidth-1:0] axi_id_t; + + typedef struct packed { + addr_t addr; + axi_pkg::atop_t atop; + logic lock; + axi_strb_t strb; + axi_data_t wdata; + logic we; + mem_id_t id; + mem_user_t user; + axi_pkg::cache_t cache; + axi_pkg::prot_t prot; + } mem_req_t; + + typedef struct packed { + addr_t addr; + axi_pkg::atop_t atop; + logic lock; + axi_id_t id; + logic last; + axi_pkg::qos_t qos; + axi_pkg::size_t size; + logic write; + mem_user_t user; + axi_pkg::cache_t cache; + axi_pkg::prot_t prot; + } meta_t; + + axi_data_t mem_rdata, + m2s_resp; + axi_pkg::len_t r_cnt_d, r_cnt_q, + w_cnt_d, w_cnt_q; + logic arb_valid, arb_ready, + rd_valid, rd_ready, + wr_valid, wr_ready, + sel_b, sel_buf_b, + sel_r, sel_buf_r, + sel_valid, sel_ready, + sel_buf_valid, sel_buf_ready, + sel_lock_d, sel_lock_q, + meta_valid, meta_ready, + meta_buf_valid, meta_buf_ready, + meta_sel_d, meta_sel_q, + m2s_req_valid, m2s_req_ready, + m2s_resp_valid, m2s_resp_ready, + mem_req_valid, mem_req_ready, + mem_rvalid; + mem_req_t m2s_req, + mem_req; + meta_t rd_meta, + rd_meta_d, rd_meta_q, + wr_meta, + wr_meta_d, wr_meta_q, + meta, meta_buf; + + assign busy_o = axi_req_i.aw_valid | axi_req_i.ar_valid | axi_req_i.w_valid | + axi_resp_o.b_valid | axi_resp_o.r_valid | + (r_cnt_q > 0) | (w_cnt_q > 0); + + // Handle reads. + always_comb begin + // Default assignments + axi_resp_o.ar_ready = 1'b0; + rd_meta_d = rd_meta_q; + rd_meta = meta_t'{default: '0}; + rd_valid = 1'b0; + r_cnt_d = r_cnt_q; + // Handle R burst in progress. + if (r_cnt_q > '0) begin + rd_meta_d.last = (r_cnt_q == 8'd1); + rd_meta = rd_meta_d; + rd_meta.addr = rd_meta_q.addr + axi_pkg::num_bytes(rd_meta_q.size); + rd_valid = 1'b1; + if (rd_ready) begin + r_cnt_d--; + rd_meta_d.addr = rd_meta.addr; + end + // Handle new AR if there is one. + end else if (axi_req_i.ar_valid) begin + rd_meta_d = '{ + addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.ar.addr, axi_req_i.ar.size)), + atop: '0, + lock: axi_req_i.ar.lock, + id: axi_req_i.ar.id, + last: (axi_req_i.ar.len == '0), + qos: axi_req_i.ar.qos, + size: axi_req_i.ar.size, + write: 1'b0, + user: axi_req_i.ar.user, + cache: axi_req_i.ar.cache, + prot: axi_req_i.ar.prot + }; + rd_meta = rd_meta_d; + rd_meta.addr = addr_t'(axi_req_i.ar.addr); + rd_valid = 1'b1; + if (rd_ready) begin + r_cnt_d = axi_req_i.ar.len; + axi_resp_o.ar_ready = 1'b1; + end + end + end + + // Handle writes. + always_comb begin + // Default assignments + axi_resp_o.aw_ready = 1'b0; + axi_resp_o.w_ready = 1'b0; + wr_meta_d = wr_meta_q; + wr_meta = meta_t'{default: '0}; + wr_valid = 1'b0; + w_cnt_d = w_cnt_q; + // Handle W bursts in progress. + if (w_cnt_q > '0) begin + wr_meta_d.last = (w_cnt_q == 8'd1); + wr_meta = wr_meta_d; + wr_meta.addr = wr_meta_q.addr + axi_pkg::num_bytes(wr_meta_q.size); + if (axi_req_i.w_valid) begin + wr_valid = 1'b1; + if (wr_ready) begin + axi_resp_o.w_ready = 1'b1; + w_cnt_d--; + wr_meta_d.addr = wr_meta.addr; + end + end + // Handle new AW if there is one. + end else if (axi_req_i.aw_valid && axi_req_i.w_valid) begin + wr_meta_d = '{ + addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.aw.addr, axi_req_i.aw.size)), + atop: axi_req_i.aw.atop, + lock: axi_req_i.aw.lock, + id: axi_req_i.aw.id, + last: (axi_req_i.aw.len == '0), + qos: axi_req_i.aw.qos, + size: axi_req_i.aw.size, + write: 1'b1, + user: axi_req_i.aw.user, + cache: axi_req_i.aw.cache, + prot: axi_req_i.aw.prot + }; + wr_meta = wr_meta_d; + wr_meta.addr = addr_t'(axi_req_i.aw.addr); + wr_valid = 1'b1; + if (wr_ready) begin + w_cnt_d = axi_req_i.aw.len; + axi_resp_o.aw_ready = 1'b1; + axi_resp_o.w_ready = 1'b1; + end + end + end + + // Arbitrate between reads and writes. + stream_mux #( + .DATA_T ( meta_t ), + .N_INP ( 32'd2 ) + ) i_ax_mux ( + .inp_data_i ({wr_meta, rd_meta }), + .inp_valid_i ({wr_valid, rd_valid}), + .inp_ready_o ({wr_ready, rd_ready}), + .inp_sel_i ( meta_sel_d ), + .oup_data_o ( meta ), + .oup_valid_o ( arb_valid ), + .oup_ready_i ( arb_ready ) + ); + always_comb begin + meta_sel_d = meta_sel_q; + sel_lock_d = sel_lock_q; + if (sel_lock_q) begin + meta_sel_d = meta_sel_q; + if (arb_valid && arb_ready) begin + sel_lock_d = 1'b0; + end + end else begin + if (wr_valid ^ rd_valid) begin + // If either write or read is valid but not both, select the valid one. + meta_sel_d = wr_valid; + end else if (wr_valid && rd_valid) begin + // If both write and read are valid, decide according to QoS then burst properties. + // Prioritize higher QoS. + if (wr_meta.qos > rd_meta.qos) begin + meta_sel_d = 1'b1; + end else if (rd_meta.qos > wr_meta.qos) begin + meta_sel_d = 1'b0; + // Decide requests with identical QoS. + end else if (wr_meta.qos == rd_meta.qos) begin + // 1. Prioritize individual writes over read bursts. + // Rationale: Read bursts can be interleaved on AXI but write bursts cannot. + if (wr_meta.last && !rd_meta.last) begin + meta_sel_d = 1'b1; + // 2. Prioritize ongoing burst. + // Rationale: Stalled bursts create back-pressure or require costly buffers. + end else if (w_cnt_q > '0) begin + meta_sel_d = 1'b1; + end else if (r_cnt_q > '0) begin + meta_sel_d = 1'b0; + // 3. Otherwise arbitrate round robin to prevent starvation. + end else begin + meta_sel_d = ~meta_sel_q; + end + end + end + // Lock arbitration if valid but not yet ready. + if (arb_valid && !arb_ready) begin + sel_lock_d = 1'b1; + end + end + end + + // Fork arbitrated stream to meta data, memory requests, and R/B channel selection. + stream_fork #( + .N_OUP ( 32'd3 ) + ) i_fork ( + .clk_i, + .rst_ni, + .valid_i ( arb_valid ), + .ready_o ( arb_ready ), + .valid_o ({sel_valid, meta_valid, m2s_req_valid}), + .ready_i ({sel_ready, meta_ready, m2s_req_ready}) + ); + + assign sel_b = meta.write & meta.last; + assign sel_r = ~meta.write | meta.atop[5]; + + stream_fifo #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( 32'd1 + BufDepth ), + .T ( logic[1:0] ) + ) i_sel_buf ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .data_i ({sel_b, sel_r }), + .valid_i ( sel_valid ), + .ready_o ( sel_ready ), + .data_o ({sel_buf_b, sel_buf_r}), + .valid_o ( sel_buf_valid ), + .ready_i ( sel_buf_ready ), + .usage_o ( /* unused */ ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( 32'd1 + BufDepth ), + .T ( meta_t ) + ) i_meta_buf ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .data_i ( meta ), + .valid_i ( meta_valid ), + .ready_o ( meta_ready ), + .data_o ( meta_buf ), + .valid_o ( meta_buf_valid ), + .ready_i ( meta_buf_ready ), + .usage_o ( /* unused */ ) + ); + + // Assemble the actual memory request from meta information and write data. + assign m2s_req = mem_req_t'{ + addr: meta.addr, + atop: meta.atop, + lock: meta.lock, + strb: axi_req_i.w.strb, + wdata: axi_req_i.w.data, + we: meta.write, + id: meta.id, + user: meta.user, + cache: meta.cache, + prot: meta.prot + }; + + // Interface memory as stream. + stream_to_mem #( + .mem_req_t ( mem_req_t ), + .mem_resp_t ( axi_data_t ), + .BufDepth ( BufDepth ) + ) i_stream_to_mem ( + .clk_i, + .rst_ni, + .req_i ( m2s_req ), + .req_valid_i ( m2s_req_valid ), + .req_ready_o ( m2s_req_ready ), + .resp_o ( m2s_resp ), + .resp_valid_o ( m2s_resp_valid ), + .resp_ready_i ( m2s_resp_ready ), + .mem_req_o ( mem_req ), + .mem_req_valid_o ( mem_req_valid ), + .mem_req_ready_i ( mem_req_ready ), + .mem_resp_i ( mem_rdata ), + .mem_resp_valid_i ( mem_rvalid ) + ); + + typedef struct packed { + axi_pkg::atop_t atop; + logic lock; + mem_id_t id; + mem_user_t user; + axi_pkg::cache_t cache; + axi_pkg::prot_t prot; + + } tmp_atop_t; + + tmp_atop_t mem_req_atop; + tmp_atop_t [NumBanks-1:0] banked_req_atop; + + assign mem_req_atop = '{ + atop: mem_req.atop, + lock: mem_req.lock, + id: mem_req.id, + user: mem_req.user, + cache: mem_req.cache, + prot: mem_req.prot + }; + + for (genvar i = 0; i < NumBanks; i++) begin + assign mem_atop_o [i] = banked_req_atop[i].atop; + assign mem_lock_o [i] = banked_req_atop[i].lock; + assign mem_id_o [i] = banked_req_atop[i].id; + assign mem_user_o [i] = banked_req_atop[i].user; + assign mem_cache_o[i] = banked_req_atop[i].cache; + assign mem_prot_o [i] = banked_req_atop[i].prot; + end + + // Split single memory request to desired number of banks. + mem_to_banks #( + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .NumBanks ( NumBanks ), + .HideStrb ( HideStrb ), + .MaxTrans ( BufDepth ), + .FifoDepth ( OutFifoDepth ), + .atop_t ( tmp_atop_t ) + ) i_mem_to_banks ( + .clk_i, + .rst_ni, + .req_i ( mem_req_valid ), + .gnt_o ( mem_req_ready ), + .addr_i ( mem_req.addr ), + .wdata_i ( mem_req.wdata ), + .strb_i ( mem_req.strb ), + .atop_i ( mem_req_atop ), + .we_i ( mem_req.we ), + .rvalid_o ( mem_rvalid ), + .rdata_o ( mem_rdata ), + .bank_req_o ( mem_req_o ), + .bank_gnt_i ( mem_gnt_i ), + .bank_addr_o ( mem_addr_o ), + .bank_wdata_o ( mem_wdata_o ), + .bank_strb_o ( mem_strb_o ), + .bank_atop_o ( banked_req_atop ), + .bank_we_o ( mem_we_o ), + .bank_rvalid_i ( mem_rvalid_i ), + .bank_rdata_i ( mem_rdata_i ) + ); + + // Join memory read data and meta data stream. + logic mem_join_valid, mem_join_ready; + stream_join #( + .N_INP ( 32'd2 ) + ) i_join ( + .inp_valid_i ({m2s_resp_valid, meta_buf_valid}), + .inp_ready_o ({m2s_resp_ready, meta_buf_ready}), + .oup_valid_o ( mem_join_valid ), + .oup_ready_i ( mem_join_ready ) + ); + + // Dynamically fork the joined stream to B and R channels. + stream_fork_dynamic #( + .N_OUP ( 32'd2 ) + ) i_fork_dynamic ( + .clk_i, + .rst_ni, + .valid_i ( mem_join_valid ), + .ready_o ( mem_join_ready ), + .sel_i ({sel_buf_b, sel_buf_r }), + .sel_valid_i ( sel_buf_valid ), + .sel_ready_o ( sel_buf_ready ), + .valid_o ({axi_resp_o.b_valid, axi_resp_o.r_valid}), + .ready_i ({axi_req_i.b_ready, axi_req_i.r_ready }) + ); + + // Compose B responses. + assign axi_resp_o.b = '{ + id: meta_buf.id, + resp: mem_err_i ? axi_pkg::RESP_SLVERR : mem_exokay_i ? axi_pkg::RESP_EXOKAY : axi_pkg::RESP_OKAY, + user: '0 + }; + + // Compose R responses. + assign axi_resp_o.r = '{ + data: m2s_resp, + id: meta_buf.id, + last: meta_buf.last, + resp: mem_err_i ? axi_pkg::RESP_SLVERR : mem_exokay_i ? axi_pkg::RESP_EXOKAY : axi_pkg::RESP_OKAY, + user: '0 + }; + + // Registers + `FFARN(meta_sel_q, meta_sel_d, 1'b0, clk_i, rst_ni) + `FFARN(sel_lock_q, sel_lock_d, 1'b0, clk_i, rst_ni) + `FFARN(rd_meta_q, rd_meta_d, meta_t'{default: '0}, clk_i, rst_ni) + `FFARN(wr_meta_q, wr_meta_d, meta_t'{default: '0}, clk_i, rst_ni) + `FFARN(r_cnt_q, r_cnt_d, '0, clk_i, rst_ni) + `FFARN(w_cnt_q, w_cnt_d, '0, clk_i, rst_ni) + + // Assertions + // pragma translate_off + `ifndef VERILATOR + default disable iff (!rst_ni); + assume property (@(posedge clk_i) + axi_req_i.ar_valid && !axi_resp_o.ar_ready |=> $stable(axi_req_i.ar)) + else $error("AR must remain stable until handshake has happened!"); + assert property (@(posedge clk_i) + axi_resp_o.r_valid && !axi_req_i.r_ready |=> $stable(axi_resp_o.r)) + else $error("R must remain stable until handshake has happened!"); + assume property (@(posedge clk_i) + axi_req_i.aw_valid && !axi_resp_o.aw_ready |=> $stable(axi_req_i.aw)) + else $error("AW must remain stable until handshake has happened!"); + assume property (@(posedge clk_i) + axi_req_i.w_valid && !axi_resp_o.w_ready |=> $stable(axi_req_i.w)) + else $error("W must remain stable until handshake has happened!"); + assert property (@(posedge clk_i) + axi_resp_o.b_valid && !axi_req_i.b_ready |=> $stable(axi_resp_o.b)) + else $error("B must remain stable until handshake has happened!"); + assert property (@(posedge clk_i) axi_req_i.ar_valid && axi_req_i.ar.len > 0 |-> + axi_req_i.ar.burst == axi_pkg::BURST_INCR) + else $error("Non-incrementing bursts are not supported!"); + assert property (@(posedge clk_i) axi_req_i.aw_valid && axi_req_i.aw.len > 0 |-> + axi_req_i.aw.burst == axi_pkg::BURST_INCR) + else $error("Non-incrementing bursts are not supported!"); + assert property (@(posedge clk_i) meta_valid && meta.atop != '0 |-> meta.write) + else $warning("Unexpected atomic operation on read."); + `endif + // pragma translate_on +endmodule + + +`include "axi/assign.svh" +`include "axi/typedef.svh" +/// Interface wrapper for module `axi_to_mem`. +module axi_to_detailed_mem_intf #( + /// See `axi_to_mem`, parameter `AddrWidth`. + parameter int unsigned ADDR_WIDTH = 32'd0, + /// See `axi_to_mem`, parameter `DataWidth`. + parameter int unsigned DATA_WIDTH = 32'd0, + /// AXI4+ATOP ID width. + parameter int unsigned ID_WIDTH = 32'd0, + /// AXI4+ATOP user width. + parameter int unsigned USER_WIDTH = 32'd0, + /// See `axi_to_mem`, parameter `NumBanks`. + parameter int unsigned NUM_BANKS = 32'd0, + /// See `axi_to_mem`, parameter `BufDepth`. + parameter int unsigned BUF_DEPTH = 32'd1, + /// Hide write requests if the strb == '0 + parameter bit HIDE_STRB = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OUT_FIFO_DEPTH = 32'd1, + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `addr_t`. + localparam type addr_t = logic [ADDR_WIDTH-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_data_t`. + localparam type mem_data_t = logic [DATA_WIDTH/NUM_BANKS-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_strb_t`. + localparam type mem_strb_t = logic [DATA_WIDTH/NUM_BANKS/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// See `axi_to_mem`, port `busy_o`. + output logic busy_o, + /// AXI4+ATOP slave interface port. + AXI_BUS.Slave slv, + /// See `axi_to_mem`, port `mem_req_o`. + output logic [NUM_BANKS-1:0] mem_req_o, + /// See `axi_to_mem`, port `mem_gnt_i`. + input logic [NUM_BANKS-1:0] mem_gnt_i, + /// See `axi_to_mem`, port `mem_addr_o`. + output addr_t [NUM_BANKS-1:0] mem_addr_o, + /// See `axi_to_mem`, port `mem_wdata_o`. + output mem_data_t [NUM_BANKS-1:0] mem_wdata_o, + /// See `axi_to_mem`, port `mem_strb_o`. + output mem_strb_t [NUM_BANKS-1:0] mem_strb_o, + /// See `axi_to_mem`, port `mem_atop_o`. + output axi_pkg::atop_t [NUM_BANKS-1:0] mem_atop_o, + /// See `axi_to_mem`, port `mem_lock_o`. + output logic [NUM_BANKS-1:0] mem_lock_o, + /// See `axi_to_mem`, port `mem_we_o`. + output logic [NUM_BANKS-1:0] mem_we_o, + /// See `axi_to_mem`, port `mem_id_o`. + output logic [NUM_BANKS-1:0] mem_id_o, + /// See `axi_to_mem`, port `mem_user_o`. + output logic [NUM_BANKS-1:0] mem_user_o, + /// See `axi_to_mem`, port `mem_rvalid_i`. + input logic [NUM_BANKS-1:0] mem_rvalid_i, + /// See `axi_to_mem`, port `mem_rdata_i`. + input mem_data_t [NUM_BANKS-1:0] mem_rdata_i, + /// See `axi_to_mem`, port `mem_err_i`. + input logic [NUM_BANKS-1:0] mem_err_i, + /// See `axi_to_mem`, port `mem_exokay_i`. + input logic [NUM_BANKS-1:0] mem_exokay_i +); + typedef logic [ID_WIDTH-1:0] id_t; + typedef logic [DATA_WIDTH-1:0] data_t; + typedef logic [DATA_WIDTH/8-1:0] strb_t; + typedef logic [USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + req_t req; + resp_t resp; + `AXI_ASSIGN_TO_REQ(req, slv) + `AXI_ASSIGN_FROM_RESP(slv, resp) + axi_to_detailed_mem #( + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .AddrWidth ( ADDR_WIDTH ), + .DataWidth ( DATA_WIDTH ), + .IdWidth ( ID_WIDTH ), + .UserWidth ( USER_WIDTH ), + .NumBanks ( NUM_BANKS ), + .BufDepth ( BUF_DEPTH ), + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) + ) i_axi_to_detailed_mem ( + .clk_i, + .rst_ni, + .busy_o, + .axi_req_i ( req ), + .axi_resp_o ( resp ), + .mem_req_o, + .mem_gnt_i, + .mem_addr_o, + .mem_wdata_o, + .mem_strb_o, + .mem_atop_o, + .mem_lock_o, + .mem_we_o, + .mem_id_o, + .mem_user_o, + .mem_rvalid_i, + .mem_rdata_i, + .mem_err_i, + .mem_exokay_i + ); +endmodule diff --git a/src/axi_to_mem.sv b/src/axi_to_mem.sv index 03d2caf26..6f8130b02 100644 --- a/src/axi_to_mem.sv +++ b/src/axi_to_mem.sv @@ -74,391 +74,41 @@ module axi_to_mem #( input mem_data_t [NumBanks-1:0] mem_rdata_i ); - typedef logic [DataWidth-1:0] axi_data_t; - typedef logic [DataWidth/8-1:0] axi_strb_t; - typedef logic [IdWidth-1:0] axi_id_t; - - typedef struct packed { - addr_t addr; - axi_pkg::atop_t atop; - axi_strb_t strb; - axi_data_t wdata; - logic we; - } mem_req_t; - - typedef struct packed { - addr_t addr; - axi_pkg::atop_t atop; - axi_id_t id; - logic last; - axi_pkg::qos_t qos; - axi_pkg::size_t size; - logic write; - } meta_t; - - axi_data_t mem_rdata, - m2s_resp; - axi_pkg::len_t r_cnt_d, r_cnt_q, - w_cnt_d, w_cnt_q; - logic arb_valid, arb_ready, - rd_valid, rd_ready, - wr_valid, wr_ready, - sel_b, sel_buf_b, - sel_r, sel_buf_r, - sel_valid, sel_ready, - sel_buf_valid, sel_buf_ready, - sel_lock_d, sel_lock_q, - meta_valid, meta_ready, - meta_buf_valid, meta_buf_ready, - meta_sel_d, meta_sel_q, - m2s_req_valid, m2s_req_ready, - m2s_resp_valid, m2s_resp_ready, - mem_req_valid, mem_req_ready, - mem_rvalid; - mem_req_t m2s_req, - mem_req; - meta_t rd_meta, - rd_meta_d, rd_meta_q, - wr_meta, - wr_meta_d, wr_meta_q, - meta, meta_buf; - - assign busy_o = axi_req_i.aw_valid | axi_req_i.ar_valid | axi_req_i.w_valid | - axi_resp_o.b_valid | axi_resp_o.r_valid | - (r_cnt_q > 0) | (w_cnt_q > 0); - - // Handle reads. - always_comb begin - // Default assignments - axi_resp_o.ar_ready = 1'b0; - rd_meta_d = rd_meta_q; - rd_meta = meta_t'{default: '0}; - rd_valid = 1'b0; - r_cnt_d = r_cnt_q; - // Handle R burst in progress. - if (r_cnt_q > '0) begin - rd_meta_d.last = (r_cnt_q == 8'd1); - rd_meta = rd_meta_d; - rd_meta.addr = rd_meta_q.addr + axi_pkg::num_bytes(rd_meta_q.size); - rd_valid = 1'b1; - if (rd_ready) begin - r_cnt_d--; - rd_meta_d.addr = rd_meta.addr; - end - // Handle new AR if there is one. - end else if (axi_req_i.ar_valid) begin - rd_meta_d = '{ - addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.ar.addr, axi_req_i.ar.size)), - atop: '0, - id: axi_req_i.ar.id, - last: (axi_req_i.ar.len == '0), - qos: axi_req_i.ar.qos, - size: axi_req_i.ar.size, - write: 1'b0 - }; - rd_meta = rd_meta_d; - rd_meta.addr = addr_t'(axi_req_i.ar.addr); - rd_valid = 1'b1; - if (rd_ready) begin - r_cnt_d = axi_req_i.ar.len; - axi_resp_o.ar_ready = 1'b1; - end - end - end - - // Handle writes. - always_comb begin - // Default assignments - axi_resp_o.aw_ready = 1'b0; - axi_resp_o.w_ready = 1'b0; - wr_meta_d = wr_meta_q; - wr_meta = meta_t'{default: '0}; - wr_valid = 1'b0; - w_cnt_d = w_cnt_q; - // Handle W bursts in progress. - if (w_cnt_q > '0) begin - wr_meta_d.last = (w_cnt_q == 8'd1); - wr_meta = wr_meta_d; - wr_meta.addr = wr_meta_q.addr + axi_pkg::num_bytes(wr_meta_q.size); - if (axi_req_i.w_valid) begin - wr_valid = 1'b1; - if (wr_ready) begin - axi_resp_o.w_ready = 1'b1; - w_cnt_d--; - wr_meta_d.addr = wr_meta.addr; - end - end - // Handle new AW if there is one. - end else if (axi_req_i.aw_valid && axi_req_i.w_valid) begin - wr_meta_d = '{ - addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.aw.addr, axi_req_i.aw.size)), - atop: axi_req_i.aw.atop, - id: axi_req_i.aw.id, - last: (axi_req_i.aw.len == '0), - qos: axi_req_i.aw.qos, - size: axi_req_i.aw.size, - write: 1'b1 - }; - wr_meta = wr_meta_d; - wr_meta.addr = addr_t'(axi_req_i.aw.addr); - wr_valid = 1'b1; - if (wr_ready) begin - w_cnt_d = axi_req_i.aw.len; - axi_resp_o.aw_ready = 1'b1; - axi_resp_o.w_ready = 1'b1; - end - end - end - - // Arbitrate between reads and writes. - stream_mux #( - .DATA_T ( meta_t ), - .N_INP ( 32'd2 ) - ) i_ax_mux ( - .inp_data_i ({wr_meta, rd_meta }), - .inp_valid_i ({wr_valid, rd_valid}), - .inp_ready_o ({wr_ready, rd_ready}), - .inp_sel_i ( meta_sel_d ), - .oup_data_o ( meta ), - .oup_valid_o ( arb_valid ), - .oup_ready_i ( arb_ready ) - ); - always_comb begin - meta_sel_d = meta_sel_q; - sel_lock_d = sel_lock_q; - if (sel_lock_q) begin - meta_sel_d = meta_sel_q; - if (arb_valid && arb_ready) begin - sel_lock_d = 1'b0; - end - end else begin - if (wr_valid ^ rd_valid) begin - // If either write or read is valid but not both, select the valid one. - meta_sel_d = wr_valid; - end else if (wr_valid && rd_valid) begin - // If both write and read are valid, decide according to QoS then burst properties. - // Prioritize higher QoS. - if (wr_meta.qos > rd_meta.qos) begin - meta_sel_d = 1'b1; - end else if (rd_meta.qos > wr_meta.qos) begin - meta_sel_d = 1'b0; - // Decide requests with identical QoS. - end else if (wr_meta.qos == rd_meta.qos) begin - // 1. Prioritize individual writes over read bursts. - // Rationale: Read bursts can be interleaved on AXI but write bursts cannot. - if (wr_meta.last && !rd_meta.last) begin - meta_sel_d = 1'b1; - // 2. Prioritize ongoing burst. - // Rationale: Stalled bursts create back-pressure or require costly buffers. - end else if (w_cnt_q > '0) begin - meta_sel_d = 1'b1; - end else if (r_cnt_q > '0) begin - meta_sel_d = 1'b0; - // 3. Otherwise arbitrate round robin to prevent starvation. - end else begin - meta_sel_d = ~meta_sel_q; - end - end - end - // Lock arbitration if valid but not yet ready. - if (arb_valid && !arb_ready) begin - sel_lock_d = 1'b1; - end - end - end - - // Fork arbitrated stream to meta data, memory requests, and R/B channel selection. - stream_fork #( - .N_OUP ( 32'd3 ) - ) i_fork ( + axi_to_detailed_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .IdWidth ( IdWidth ), + .UserWidth ( 1 ), + .NumBanks ( NumBanks ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ), + .OutFifoDepth ( OutFifoDepth ) + ) i_axi_to_detailed_mem ( .clk_i, .rst_ni, - .valid_i ( arb_valid ), - .ready_o ( arb_ready ), - .valid_o ({sel_valid, meta_valid, m2s_req_valid}), - .ready_i ({sel_ready, meta_ready, m2s_req_ready}) - ); - - assign sel_b = meta.write & meta.last; - assign sel_r = ~meta.write | meta.atop[5]; - - stream_fifo #( - .FALL_THROUGH ( 1'b1 ), - .DEPTH ( 32'd1 + BufDepth ), - .T ( logic[1:0] ) - ) i_sel_buf ( - .clk_i, - .rst_ni, - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .data_i ({sel_b, sel_r }), - .valid_i ( sel_valid ), - .ready_o ( sel_ready ), - .data_o ({sel_buf_b, sel_buf_r}), - .valid_o ( sel_buf_valid ), - .ready_i ( sel_buf_ready ), - .usage_o ( /* unused */ ) - ); - - stream_fifo #( - .FALL_THROUGH ( 1'b1 ), - .DEPTH ( 32'd1 + BufDepth ), - .T ( meta_t ) - ) i_meta_buf ( - .clk_i, - .rst_ni, - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .data_i ( meta ), - .valid_i ( meta_valid ), - .ready_o ( meta_ready ), - .data_o ( meta_buf ), - .valid_o ( meta_buf_valid ), - .ready_i ( meta_buf_ready ), - .usage_o ( /* unused */ ) - ); - - // Assemble the actual memory request from meta information and write data. - assign m2s_req = mem_req_t'{ - addr: meta.addr, - atop: meta.atop, - strb: axi_req_i.w.strb, - wdata: axi_req_i.w.data, - we: meta.write - }; - - // Interface memory as stream. - stream_to_mem #( - .mem_req_t ( mem_req_t ), - .mem_resp_t ( axi_data_t ), - .BufDepth ( BufDepth ) - ) i_stream_to_mem ( - .clk_i, - .rst_ni, - .req_i ( m2s_req ), - .req_valid_i ( m2s_req_valid ), - .req_ready_o ( m2s_req_ready ), - .resp_o ( m2s_resp ), - .resp_valid_o ( m2s_resp_valid ), - .resp_ready_i ( m2s_resp_ready ), - .mem_req_o ( mem_req ), - .mem_req_valid_o ( mem_req_valid ), - .mem_req_ready_i ( mem_req_ready ), - .mem_resp_i ( mem_rdata ), - .mem_resp_valid_i ( mem_rvalid ) - ); - - // Split single memory request to desired number of banks. - mem_to_banks #( - .AddrWidth ( AddrWidth ), - .DataWidth ( DataWidth ), - .NumBanks ( NumBanks ), - .HideStrb ( HideStrb ), - .MaxTrans ( BufDepth ), - .FifoDepth ( OutFifoDepth ), - .atop_t ( axi_pkg::atop_t ) - ) i_mem_to_banks ( - .clk_i, - .rst_ni, - .req_i ( mem_req_valid ), - .gnt_o ( mem_req_ready ), - .addr_i ( mem_req.addr ), - .wdata_i ( mem_req.wdata ), - .strb_i ( mem_req.strb ), - .atop_i ( mem_req.atop ), - .we_i ( mem_req.we ), - .rvalid_o ( mem_rvalid ), - .rdata_o ( mem_rdata ), - .bank_req_o ( mem_req_o ), - .bank_gnt_i ( mem_gnt_i ), - .bank_addr_o ( mem_addr_o ), - .bank_wdata_o ( mem_wdata_o ), - .bank_strb_o ( mem_strb_o ), - .bank_atop_o ( mem_atop_o ), - .bank_we_o ( mem_we_o ), - .bank_rvalid_i ( mem_rvalid_i ), - .bank_rdata_i ( mem_rdata_i ) - ); - - // Join memory read data and meta data stream. - logic mem_join_valid, mem_join_ready; - stream_join #( - .N_INP ( 32'd2 ) - ) i_join ( - .inp_valid_i ({m2s_resp_valid, meta_buf_valid}), - .inp_ready_o ({m2s_resp_ready, meta_buf_ready}), - .oup_valid_o ( mem_join_valid ), - .oup_ready_i ( mem_join_ready ) - ); - - // Dynamically fork the joined stream to B and R channels. - stream_fork_dynamic #( - .N_OUP ( 32'd2 ) - ) i_fork_dynamic ( - .clk_i, - .rst_ni, - .valid_i ( mem_join_valid ), - .ready_o ( mem_join_ready ), - .sel_i ({sel_buf_b, sel_buf_r }), - .sel_valid_i ( sel_buf_valid ), - .sel_ready_o ( sel_buf_ready ), - .valid_o ({axi_resp_o.b_valid, axi_resp_o.r_valid}), - .ready_i ({axi_req_i.b_ready, axi_req_i.r_ready }) + .busy_o, + .axi_req_i ( axi_req_i ), + .axi_resp_o ( axi_resp_o ), + .mem_req_o ( mem_req_o ), + .mem_gnt_i ( mem_gnt_i ), + .mem_addr_o ( mem_addr_o ), + .mem_wdata_o ( mem_wdata_o ), + .mem_strb_o ( mem_strb_o ), + .mem_atop_o ( mem_atop_o ), + .mem_lock_o (), + .mem_we_o ( mem_we_o ), + .mem_id_o (), + .mem_user_o (), + .mem_cache_o (), + .mem_prot_o (), + .mem_rvalid_i ( mem_rvalid_i ), + .mem_rdata_i ( mem_rdata_i ), + .mem_err_i ('0), + .mem_exokay_i ('0) ); - // Compose B responses. - assign axi_resp_o.b = '{ - id: meta_buf.id, - resp: axi_pkg::RESP_OKAY, - user: '0 - }; - - // Compose R responses. - assign axi_resp_o.r = '{ - data: m2s_resp, - id: meta_buf.id, - last: meta_buf.last, - resp: axi_pkg::RESP_OKAY, - user: '0 - }; - - // Registers - `FFARN(meta_sel_q, meta_sel_d, 1'b0, clk_i, rst_ni) - `FFARN(sel_lock_q, sel_lock_d, 1'b0, clk_i, rst_ni) - `FFARN(rd_meta_q, rd_meta_d, meta_t'{default: '0}, clk_i, rst_ni) - `FFARN(wr_meta_q, wr_meta_d, meta_t'{default: '0}, clk_i, rst_ni) - `FFARN(r_cnt_q, r_cnt_d, '0, clk_i, rst_ni) - `FFARN(w_cnt_q, w_cnt_d, '0, clk_i, rst_ni) - - // Assertions - // pragma translate_off - `ifndef VERILATOR - default disable iff (!rst_ni); - assume property (@(posedge clk_i) - axi_req_i.ar_valid && !axi_resp_o.ar_ready |=> $stable(axi_req_i.ar)) - else $error("AR must remain stable until handshake has happened!"); - assert property (@(posedge clk_i) - axi_resp_o.r_valid && !axi_req_i.r_ready |=> $stable(axi_resp_o.r)) - else $error("R must remain stable until handshake has happened!"); - assume property (@(posedge clk_i) - axi_req_i.aw_valid && !axi_resp_o.aw_ready |=> $stable(axi_req_i.aw)) - else $error("AW must remain stable until handshake has happened!"); - assume property (@(posedge clk_i) - axi_req_i.w_valid && !axi_resp_o.w_ready |=> $stable(axi_req_i.w)) - else $error("W must remain stable until handshake has happened!"); - assert property (@(posedge clk_i) - axi_resp_o.b_valid && !axi_req_i.b_ready |=> $stable(axi_resp_o.b)) - else $error("B must remain stable until handshake has happened!"); - assert property (@(posedge clk_i) axi_req_i.ar_valid && axi_req_i.ar.len > 0 |-> - axi_req_i.ar.burst == axi_pkg::BURST_INCR) - else $error("Non-incrementing bursts are not supported!"); - assert property (@(posedge clk_i) axi_req_i.aw_valid && axi_req_i.aw.len > 0 |-> - axi_req_i.aw.burst == axi_pkg::BURST_INCR) - else $error("Non-incrementing bursts are not supported!"); - assert property (@(posedge clk_i) meta_valid && meta.atop != '0 |-> meta.write) - else $warning("Unexpected atomic operation on read."); - `endif - // pragma translate_on endmodule From bafbaf769d702ff720d7d682be549e8066242f17 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 4 Jul 2023 13:36:52 +0200 Subject: [PATCH 126/145] Limit fusesoc/XSIM test --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2a34c9572..1ac12d887 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -41,6 +41,8 @@ synopsys_dc: fuse_xsim: stage: build + allow_failure: true + timeout: 5 minutes script: - bender sources - /usr/local/anaconda3/bin/python3 -m pip install fusesoc --user From d810705676ad624a609a8f0bb545d90c981e5211 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Wed, 7 Jun 2023 10:25:50 +0200 Subject: [PATCH 127/145] axi_to_detailed_mem: Fix type bug with workaround --- src/axi_to_detailed_mem.sv | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/axi_to_detailed_mem.sv b/src/axi_to_detailed_mem.sv index 7a8b95f0f..debf08284 100644 --- a/src/axi_to_detailed_mem.sv +++ b/src/axi_to_detailed_mem.sv @@ -397,7 +397,6 @@ module axi_to_detailed_mem #( mem_user_t user; axi_pkg::cache_t cache; axi_pkg::prot_t prot; - } tmp_atop_t; tmp_atop_t mem_req_atop; @@ -423,13 +422,13 @@ module axi_to_detailed_mem #( // Split single memory request to desired number of banks. mem_to_banks #( - .AddrWidth ( AddrWidth ), - .DataWidth ( DataWidth ), - .NumBanks ( NumBanks ), - .HideStrb ( HideStrb ), - .MaxTrans ( BufDepth ), - .FifoDepth ( OutFifoDepth ), - .atop_t ( tmp_atop_t ) + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .NumBanks ( NumBanks ), + .HideStrb ( HideStrb ), + .MaxTrans ( BufDepth ), + .FifoDepth ( OutFifoDepth ), + .AtopWidth ( $bits(tmp_atop_t) ) ) i_mem_to_banks ( .clk_i, .rst_ni, From fcefef54c0c206364c518b2f661783dd749db381 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Wed, 5 Jul 2023 14:24:47 +0200 Subject: [PATCH 128/145] axi_to_detailed_mem: Add AXI qos and region signals --- src/axi_to_detailed_mem.sv | 232 +++++++++++++++++++++---------------- src/axi_to_mem.sv | 2 + 2 files changed, 134 insertions(+), 100 deletions(-) diff --git a/src/axi_to_detailed_mem.sv b/src/axi_to_detailed_mem.sv index debf08284..0a8b955ea 100644 --- a/src/axi_to_detailed_mem.sv +++ b/src/axi_to_detailed_mem.sv @@ -50,48 +50,52 @@ module axi_to_detailed_mem #( localparam type mem_user_t = logic [UserWidth-1:0] ) ( /// Clock input. - input logic clk_i, + input logic clk_i, /// Asynchronous reset, active low. - input logic rst_ni, + input logic rst_ni, /// The unit is busy handling an AXI4+ATOP request. - output logic busy_o, + output logic busy_o, /// AXI4+ATOP slave port, request input. - input axi_req_t axi_req_i, + input axi_req_t axi_req_i, /// AXI4+ATOP slave port, response output. - output axi_resp_t axi_resp_o, + output axi_resp_t axi_resp_o, /// Memory stream master, request is valid for this bank. - output logic [NumBanks-1:0] mem_req_o, + output logic [NumBanks-1:0] mem_req_o, /// Memory stream master, request can be granted by this bank. - input logic [NumBanks-1:0] mem_gnt_i, + input logic [NumBanks-1:0] mem_gnt_i, /// Memory stream master, byte address of the request. - output addr_t [NumBanks-1:0] mem_addr_o, + output addr_t [NumBanks-1:0] mem_addr_o, /// Memory stream master, write data for this bank. Valid when `mem_req_o`. - output mem_data_t [NumBanks-1:0] mem_wdata_o, + output mem_data_t [NumBanks-1:0] mem_wdata_o, /// Memory stream master, byte-wise strobe (byte enable). - output mem_strb_t [NumBanks-1:0] mem_strb_o, + output mem_strb_t [NumBanks-1:0] mem_strb_o, /// Memory stream master, `axi_pkg::atop_t` signal associated with this request. - output axi_pkg::atop_t [NumBanks-1:0] mem_atop_o, + output axi_pkg::atop_t [NumBanks-1:0] mem_atop_o, /// Memory stream master, lock signal. - output logic [NumBanks-1:0] mem_lock_o, + output logic [NumBanks-1:0] mem_lock_o, /// Memory stream master, write enable. Then asserted store of `mem_w_data` is requested. - output logic [NumBanks-1:0] mem_we_o, + output logic [NumBanks-1:0] mem_we_o, /// Memory stream master, ID. Response ID is managed internally, ensure in-order responses. - output mem_id_t [NumBanks-1:0] mem_id_o, + output mem_id_t [NumBanks-1:0] mem_id_o, /// Memory stream master, user signal. Ax channel user bits used. - output mem_user_t [NumBanks-1:0] mem_user_o, + output mem_user_t [NumBanks-1:0] mem_user_o, /// Memory stream master, cache signal. - output axi_pkg::cache_t [NumBanks-1:0] mem_cache_o, + output axi_pkg::cache_t [NumBanks-1:0] mem_cache_o, /// Memory stream master, protection signal. - output axi_pkg::prot_t [NumBanks-1:0] mem_prot_o, + output axi_pkg::prot_t [NumBanks-1:0] mem_prot_o, + /// Memory stream master, QOS signal. + output axi_pkg::qos_t [NumBanks-1:0] mem_qos_o, + /// Memory stream master, region signal. + output axi_pkg::region_t [NumBanks-1:0] mem_region_o, /// Memory stream master, response is valid. This module expects always a response valid for a /// request regardless if the request was a write or a read. - input logic [NumBanks-1:0] mem_rvalid_i, + input logic [NumBanks-1:0] mem_rvalid_i, /// Memory stream master, read response data. - input mem_data_t [NumBanks-1:0] mem_rdata_i, + input mem_data_t [NumBanks-1:0] mem_rdata_i, /// Memory stream master, error response. - input logic [NumBanks-1:0] mem_err_i, + input logic [NumBanks-1:0] mem_err_i, /// Memory stream master, read response exclusive access OK. - input logic [NumBanks-1:0] mem_exokay_i + input logic [NumBanks-1:0] mem_exokay_i ); typedef logic [DataWidth-1:0] axi_data_t; @@ -99,30 +103,34 @@ module axi_to_detailed_mem #( typedef logic [IdWidth-1:0] axi_id_t; typedef struct packed { - addr_t addr; - axi_pkg::atop_t atop; - logic lock; - axi_strb_t strb; - axi_data_t wdata; - logic we; - mem_id_t id; - mem_user_t user; - axi_pkg::cache_t cache; - axi_pkg::prot_t prot; + addr_t addr; + axi_pkg::atop_t atop; + logic lock; + axi_strb_t strb; + axi_data_t wdata; + logic we; + mem_id_t id; + mem_user_t user; + axi_pkg::cache_t cache; + axi_pkg::prot_t prot; + axi_pkg::qos_t qos; + axi_pkg::region_t region; } mem_req_t; typedef struct packed { - addr_t addr; - axi_pkg::atop_t atop; - logic lock; - axi_id_t id; - logic last; - axi_pkg::qos_t qos; - axi_pkg::size_t size; - logic write; - mem_user_t user; - axi_pkg::cache_t cache; - axi_pkg::prot_t prot; + addr_t addr; + axi_pkg::atop_t atop; + logic lock; + axi_id_t id; + logic last; + axi_pkg::qos_t qos; + axi_pkg::size_t size; + logic write; + mem_user_t user; + axi_pkg::cache_t cache; + axi_pkg::prot_t prot; + axi_pkg::qos_t qos; + axi_pkg::region_t region; } meta_t; axi_data_t mem_rdata, @@ -177,17 +185,19 @@ module axi_to_detailed_mem #( // Handle new AR if there is one. end else if (axi_req_i.ar_valid) begin rd_meta_d = '{ - addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.ar.addr, axi_req_i.ar.size)), - atop: '0, - lock: axi_req_i.ar.lock, - id: axi_req_i.ar.id, - last: (axi_req_i.ar.len == '0), - qos: axi_req_i.ar.qos, - size: axi_req_i.ar.size, - write: 1'b0, - user: axi_req_i.ar.user, - cache: axi_req_i.ar.cache, - prot: axi_req_i.ar.prot + addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.ar.addr, axi_req_i.ar.size)), + atop: '0, + lock: axi_req_i.ar.lock, + id: axi_req_i.ar.id, + last: (axi_req_i.ar.len == '0), + qos: axi_req_i.ar.qos, + size: axi_req_i.ar.size, + write: 1'b0, + user: axi_req_i.ar.user, + cache: axi_req_i.ar.cache, + prot: axi_req_i.ar.prot, + qos: axi_req_i.ar.qos, + region: axi_req_i.ar.region }; rd_meta = rd_meta_d; rd_meta.addr = addr_t'(axi_req_i.ar.addr); @@ -234,7 +244,9 @@ module axi_to_detailed_mem #( write: 1'b1, user: axi_req_i.aw.user, cache: axi_req_i.aw.cache, - prot: axi_req_i.aw.prot + prot: axi_req_i.aw.prot, + qos: axi_req_i.aw.qos, + region: axi_req_i.aw.region }; wr_meta = wr_meta_d; wr_meta.addr = addr_t'(axi_req_i.aw.addr); @@ -357,16 +369,18 @@ module axi_to_detailed_mem #( // Assemble the actual memory request from meta information and write data. assign m2s_req = mem_req_t'{ - addr: meta.addr, - atop: meta.atop, - lock: meta.lock, - strb: axi_req_i.w.strb, - wdata: axi_req_i.w.data, - we: meta.write, - id: meta.id, - user: meta.user, - cache: meta.cache, - prot: meta.prot + addr: meta.addr, + atop: meta.atop, + lock: meta.lock, + strb: axi_req_i.w.strb, + wdata: axi_req_i.w.data, + we: meta.write, + id: meta.id, + user: meta.user, + cache: meta.cache, + prot: meta.prot, + qos: meta.qos, + region: meta.region }; // Interface memory as stream. @@ -391,33 +405,39 @@ module axi_to_detailed_mem #( ); typedef struct packed { - axi_pkg::atop_t atop; - logic lock; - mem_id_t id; - mem_user_t user; - axi_pkg::cache_t cache; - axi_pkg::prot_t prot; + axi_pkg::atop_t atop; + logic lock; + mem_id_t id; + mem_user_t user; + axi_pkg::cache_t cache; + axi_pkg::prot_t prot; + axi_pkg::qos_t qos; + axi_pkg::region_t region; } tmp_atop_t; tmp_atop_t mem_req_atop; tmp_atop_t [NumBanks-1:0] banked_req_atop; assign mem_req_atop = '{ - atop: mem_req.atop, - lock: mem_req.lock, - id: mem_req.id, - user: mem_req.user, - cache: mem_req.cache, - prot: mem_req.prot + atop: mem_req.atop, + lock: mem_req.lock, + id: mem_req.id, + user: mem_req.user, + cache: mem_req.cache, + prot: mem_req.prot, + qos: mem_req.qos, + region: mem_req.region }; for (genvar i = 0; i < NumBanks; i++) begin - assign mem_atop_o [i] = banked_req_atop[i].atop; - assign mem_lock_o [i] = banked_req_atop[i].lock; - assign mem_id_o [i] = banked_req_atop[i].id; - assign mem_user_o [i] = banked_req_atop[i].user; - assign mem_cache_o[i] = banked_req_atop[i].cache; - assign mem_prot_o [i] = banked_req_atop[i].prot; + assign mem_atop_o [i] = banked_req_atop[i].atop; + assign mem_lock_o [i] = banked_req_atop[i].lock; + assign mem_id_o [i] = banked_req_atop[i].id; + assign mem_user_o [i] = banked_req_atop[i].user; + assign mem_cache_o [i] = banked_req_atop[i].cache; + assign mem_prot_o [i] = banked_req_atop[i].prot; + assign mem_qos_o [i] = banked_req_atop[i].qos; + assign mem_region_o[i] = banked_req_atop[i].region; end // Split single memory request to desired number of banks. @@ -562,41 +582,49 @@ module axi_to_detailed_mem_intf #( localparam type mem_strb_t = logic [DATA_WIDTH/NUM_BANKS/8-1:0] ) ( /// Clock input. - input logic clk_i, + input logic clk_i, /// Asynchronous reset, active low. - input logic rst_ni, + input logic rst_ni, /// See `axi_to_mem`, port `busy_o`. - output logic busy_o, + output logic busy_o, /// AXI4+ATOP slave interface port. - AXI_BUS.Slave slv, + AXI_BUS.Slave slv, /// See `axi_to_mem`, port `mem_req_o`. - output logic [NUM_BANKS-1:0] mem_req_o, + output logic [NUM_BANKS-1:0] mem_req_o, /// See `axi_to_mem`, port `mem_gnt_i`. - input logic [NUM_BANKS-1:0] mem_gnt_i, + input logic [NUM_BANKS-1:0] mem_gnt_i, /// See `axi_to_mem`, port `mem_addr_o`. - output addr_t [NUM_BANKS-1:0] mem_addr_o, + output addr_t [NUM_BANKS-1:0] mem_addr_o, /// See `axi_to_mem`, port `mem_wdata_o`. - output mem_data_t [NUM_BANKS-1:0] mem_wdata_o, + output mem_data_t [NUM_BANKS-1:0] mem_wdata_o, /// See `axi_to_mem`, port `mem_strb_o`. - output mem_strb_t [NUM_BANKS-1:0] mem_strb_o, + output mem_strb_t [NUM_BANKS-1:0] mem_strb_o, /// See `axi_to_mem`, port `mem_atop_o`. - output axi_pkg::atop_t [NUM_BANKS-1:0] mem_atop_o, + output axi_pkg::atop_t [NUM_BANKS-1:0] mem_atop_o, /// See `axi_to_mem`, port `mem_lock_o`. - output logic [NUM_BANKS-1:0] mem_lock_o, + output logic [NUM_BANKS-1:0] mem_lock_o, /// See `axi_to_mem`, port `mem_we_o`. - output logic [NUM_BANKS-1:0] mem_we_o, + output logic [NUM_BANKS-1:0] mem_we_o, /// See `axi_to_mem`, port `mem_id_o`. - output logic [NUM_BANKS-1:0] mem_id_o, + output logic [NUM_BANKS-1:0] mem_id_o, /// See `axi_to_mem`, port `mem_user_o`. - output logic [NUM_BANKS-1:0] mem_user_o, + output logic [NUM_BANKS-1:0] mem_user_o, + /// See `axi_to_mem`, port `mem_cache_o`. + output axi_pkg::cache_t [NUM_BANKS-1:0] mem_cache_o, + /// See `axi_to_mem`, port `mem_prot_o`. + output axi_pkg::prot_t [NUM_BANKS-1:0] mem_prot_o, + /// See `axi_to_mem`, port `mem_qos_o`. + output axi_pkg::qos_t [NUM_BANKS-1:0] mem_qos_o, + /// See `axi_to_mem`, port `mem_region_o`. + output axi_pkg::region_t [NUM_BANKS-1:0] mem_region_o, /// See `axi_to_mem`, port `mem_rvalid_i`. - input logic [NUM_BANKS-1:0] mem_rvalid_i, + input logic [NUM_BANKS-1:0] mem_rvalid_i, /// See `axi_to_mem`, port `mem_rdata_i`. - input mem_data_t [NUM_BANKS-1:0] mem_rdata_i, + input mem_data_t [NUM_BANKS-1:0] mem_rdata_i, /// See `axi_to_mem`, port `mem_err_i`. - input logic [NUM_BANKS-1:0] mem_err_i, + input logic [NUM_BANKS-1:0] mem_err_i, /// See `axi_to_mem`, port `mem_exokay_i`. - input logic [NUM_BANKS-1:0] mem_exokay_i + input logic [NUM_BANKS-1:0] mem_exokay_i ); typedef logic [ID_WIDTH-1:0] id_t; typedef logic [DATA_WIDTH-1:0] data_t; @@ -640,6 +668,10 @@ module axi_to_detailed_mem_intf #( .mem_we_o, .mem_id_o, .mem_user_o, + .mem_cache_o, + .mem_prot_o, + .mem_qos_o, + .mem_region_o, .mem_rvalid_i, .mem_rdata_i, .mem_err_i, diff --git a/src/axi_to_mem.sv b/src/axi_to_mem.sv index 6f8130b02..e935e69ff 100644 --- a/src/axi_to_mem.sv +++ b/src/axi_to_mem.sv @@ -103,6 +103,8 @@ module axi_to_mem #( .mem_user_o (), .mem_cache_o (), .mem_prot_o (), + .mem_qos_o (), + .mem_region_o (), .mem_rvalid_i ( mem_rvalid_i ), .mem_rdata_i ( mem_rdata_i ), .mem_err_i ('0), From 2255665d6b7a692a3334d80f0b00c361c2f176df Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Wed, 5 Jul 2023 14:43:17 +0200 Subject: [PATCH 129/145] axi_to_detailed_mem: Fix qos in meta struct --- src/axi_to_detailed_mem.sv | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/axi_to_detailed_mem.sv b/src/axi_to_detailed_mem.sv index 0a8b955ea..3e6cfdcab 100644 --- a/src/axi_to_detailed_mem.sv +++ b/src/axi_to_detailed_mem.sv @@ -129,7 +129,6 @@ module axi_to_detailed_mem #( mem_user_t user; axi_pkg::cache_t cache; axi_pkg::prot_t prot; - axi_pkg::qos_t qos; axi_pkg::region_t region; } meta_t; @@ -196,7 +195,6 @@ module axi_to_detailed_mem #( user: axi_req_i.ar.user, cache: axi_req_i.ar.cache, prot: axi_req_i.ar.prot, - qos: axi_req_i.ar.qos, region: axi_req_i.ar.region }; rd_meta = rd_meta_d; @@ -245,7 +243,6 @@ module axi_to_detailed_mem #( user: axi_req_i.aw.user, cache: axi_req_i.aw.cache, prot: axi_req_i.aw.prot, - qos: axi_req_i.aw.qos, region: axi_req_i.aw.region }; wr_meta = wr_meta_d; From 6c61c46e08dcae0c8ee0f9d7650d28623b6875f5 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 20 Jul 2023 10:14:27 +0200 Subject: [PATCH 130/145] Release v0.39.0 --- .github/workflows/doc.yml | 46 +++++++-------------- Bender.yml | 2 + CHANGELOG.md | 68 +++++++++++++++++++++++--------- README.md | 3 ++ VERSION | 2 +- axi.core | 16 +++++--- include/axi/typedef.svh | 1 + scripts/run_verilator.sh | 1 + scripts/run_vsim.sh | 1 + scripts/update_authors | 3 ++ src/axi_chan_compare.sv | 4 +- src/axi_demux.sv | 2 + src/axi_demux_simple.sv | 2 + src/axi_from_mem.sv | 2 +- src/axi_id_serialize.sv | 1 + src/axi_lite_dw_converter.sv | 2 +- src/axi_lite_from_mem.sv | 1 + src/axi_pkg.sv | 2 + src/axi_test.sv | 1 + src/axi_to_detailed_mem.sv | 1 + src/axi_to_mem.sv | 1 + src/axi_to_mem_split.sv | 1 + src_files.yml | 15 ++++--- test/tb_axi_lite_dw_converter.sv | 4 +- test/tb_axi_slave_compare.sv | 2 + 25 files changed, 116 insertions(+), 68 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index d598aeb3b..0f50b87e5 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -17,38 +17,20 @@ jobs: runs-on: ubuntu-latest # will fail) steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: persist-credentials: false - - name: Cache cargo registry - uses: actions/cache@v1 + - name: Install Bender + uses: pulp-platform/pulp-actions/bender-install@v2 with: - path: ~/.cargo/registry - key: ubuntu-latest-cargo-registry-${{ hashFiles('.github/workflows/doc.yml') }} + version: 0.27.2 - - name: Cache cargo index - uses: actions/cache@v1 - with: - path: ~/.cargo/git - key: ubuntu-latest-cargo-index-${{ hashFiles('.github/workflows/doc.yml') }} - - - name: Cache cargo binaries - uses: actions/cache@v1 - with: - path: ~/.cargo/bin - key: ubuntu-latest-cargo-binaries-${{ hashFiles('.github/workflows/doc.yml') }} - - - name: Install Bender and Morty + - name: Install Morty run: | - rustup update stable --no-self-update && rustup default stable - if ! $(which bender); then - cargo install bender --version 0.23.1 - fi - if ! $(which morty); then - cargo install --git https://github.com/zarubaf/morty --rev 4855119c1378d45d9ac35cfa525725d2786e68f3 - fi - shell: bash + sudo mkdir -p /tools/morty && sudo chmod 777 /tools/morty + cd /tools/morty && curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/morty/init -sSf | bash -s -- 0.9.0 + echo "PATH=/tools/morty:$PATH" >> ${GITHUB_ENV} - name: Build documentation run: | @@ -76,14 +58,14 @@ jobs: echo "DOC_TARGET=$DOC_TARGET" >> $GITHUB_ENV - name: Deploy documentation - uses: JamesIves/github-pages-deploy-action@releases/v3 + uses: JamesIves/github-pages-deploy-action@v4 if: > github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository with: - ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} - BRANCH: gh-pages # The branch the action should deploy to. - FOLDER: docs # The folder the action should deploy. - TARGET_FOLDER: ${{ env.DOC_TARGET }} - CLEAN: true # remove files from `TARGET_FOLDER` that are not in `FOLDER` + token: ${{ secrets.GH_PAGES }} + branch: gh-pages # The branch the action should deploy to. + folder: docs # The folder the action should deploy. + target-folder: ${{ env.DOC_TARGET }} + clean: true # remove files from `TARGET_FOLDER` that are not in `FOLDER` # (`rsync --delete`) diff --git a/Bender.yml b/Bender.yml index 3c4a29be1..8f16f74cb 100644 --- a/Bender.yml +++ b/Bender.yml @@ -7,10 +7,12 @@ package: - "Matheus Cavalcante " - "Tim Fischer " - "Noah Huetter " + - "Cyril Koenig " - "Andreas Kurth " - "Stefan Mach " - "Samuel Riedel " - "Wolfgang Rönninger " + - "Paul Scheffler " - "Fabian Schuiki " - "Luca Valente " - "Nils Wistoff " diff --git a/CHANGELOG.md b/CHANGELOG.md index c2bfa2743..ab8b06b78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,30 +8,62 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased ### Added -- Add `axi_chan_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. -- Add `axi_lite_from_mem` and `axi_from_mem` acting like SRAMs making AXI4 requests downstream. -- Add `axi_rw_join` and `axi_rw_split` to split/join AXI buses. -- Add `#_width` functions returning the width of the AXI channels. -- Add `axi_lite_dw_converter`: Convert the data width of AXI4-Lite transactions. Emmits the - appropriate amount of downstream transactions to perform the whole requested access. - +- Synthesizable IPs: + - `axi_bus_compare` and `axi_slave_compare`; two synthesizable verification IPs meant to be used + to compare two AXI buses on an FPGA. + - `axi_lite_from_mem` and `axi_from_mem` acting like SRAMs making AXI4 requests downstream. + - `axi_lite_dw_converter`: Convert the data width of AXI4-Lite transactions. Emits the + appropriate amount of downstream transactions to perform the whole requested access. + - `axi_rw_join` and `axi_rw_split` to split/join the read and write channels of an AXI bus. +- `CT`-macros allowing to instantiate AXI structs with custom channel type names. +- `axi_pkg': Add documentation to `xbar_cfg_t`. +- Testbench IPs: + - `axi_chan_compare.sv`: Non-synthesizable module comparing two AXI channels of the same type + - Add `axi_file_master` to `axi_test`, allowing file-based AXI verification approaches. + - Add `#_width` functions to `axi_test` returning the width of the AXI channels. ### Changed -- `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents - AWs from being issued to one master port while Ws from another burst are ongoing to another - master port. This is required to prevents deadlocks due to circular waits downstream. -- `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` - in the crossed connections in the xbar between the demuxes and muxes. -- `axi_pkg`: Add documentation to `xbar_cfg_t`. -- Move `mem_to_banks` to `common_cells`. -- Update `common_cells` from version `v1.26.0` to `v1.27.0`. +- Synthesizable IPs: + - `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents + AWs from being issued to one master port while Ws from another burst are ongoing to another + master port. This is required to prevents deadlocks due to circular waits downstream. Removes + `FallThrough` parameter from `axi_demux`. + - Split the `axi_demux` logic and timing decoupling. A new module called `axi_demux_simple` contains + the core logic. + - `axi_dw_downsizer` uses `axi_pkg::RESP_EXOKAY` as a default value. + - Simplify the `casez` in `axi_id_remap`. + - Add optional explicit mapping to the `axi_id_serialize` module. + - Expand `axi_to_mem` to `axi_to_detailed_mem` exposing all of AXI's side-signals; namely `id`, `user`, + `cache`, `prot`, `qos`, `region`, `atop`. Add possibility to inject `err` and `exokay`. + - `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` + in the crossed connections in the `xbar` between the *demuxes* and *muxes*. Improve inline + documentation. + - Move `mem_to_banks` to `common_cells`. +- `axi_pkg`: Improve for better compatibility with *Vivado*. +- `axi_test: + - `axi_lite_rand_slave`: `R` response field is now randomized. + - Remove excessive prints from random master and slave. + - Properly size-align the address. - `axi_pkg`: Define `localparams` to define AXI type widths. -- `axi_test:axi_rand_lite_slave`: R response field is now random. +- Update `common_cells` from version `v1.26.0` to `v1.27.0`. +- Tooling: + - Use `pulp-platform/pulp-actions/gitlab-ci@v2` in the GitHub CI to communicate with the internal CI. + - Bump `DC Shell version` from `2019.12` to `2022.03` + - No longer check *ModelSim* versions `10.7e` and `2021.3`, add `2022.3`. + - More thorough verification runs for the `xbar`. + - Start transitioning from shell script to Makefile to run simulations. +- Use `scripts/update_authors` to update authors, slight manual fixes performed. ### Fixed +- `axi_to_mem_banked`: Reduce hardware by properly setting `UniqueIds`. +- `axi_to_mem_interleaved` and `axi_to_mem_split` properly instantiates a demultiplexer now. + Adds `test_i` port for DFT. +### Breaking Changes +There are breaking changes between `v0.38.0` and `v0.39.0`: +- `axi_demux`: `FallThrough` parameter was removed. +- `axi_xbar`: `axi_pkg::xbar_cfg_t` added `PipelineStages` parameter. +- `axi_to_mem_interleaved` and `axi_to_mem_split`: Added `test_i` input port. ## 0.38.0 - 2022-09-28 diff --git a/README.md b/README.md index 734154136..962185172 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_cdc`](src/axi_cdc.sv) | AXI clock domain crossing based on a Gray FIFO implementation. | | | [`axi_cut`](src/axi_cut.sv) | Breaks all combinatorial paths between its input and output. | | | [`axi_delayer`](src/axi_delayer.sv) | Synthesizable module which can (randomly) delays AXI channels. | | +| [`axi_demux_simple`](src/axi_demux_simple.sv) | Demux without spill registers. | [Doc](doc/axi_demux.md) | | [`axi_demux`](src/axi_demux.sv) | Demultiplexes an AXI bus from one slave port to multiple master ports. | [Doc](doc/axi_demux.md) | | [`axi_dw_converter`](src/axi_dw_converter.sv) | A data width converter between AXI interfaces of any data width. | | | [`axi_dw_downsizer`](src/axi_dw_downsizer.sv) | A data width converter between a wide AXI master and a narrower AXI slave. | | @@ -59,6 +60,7 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_rw_join`](src/axi_rw_join.sv) | Joins a read and a write slave into one single read / write master. | | | [`axi_rw_split`](src/axi_rw_split.sv) | Splits a single read / write slave into one read and one write master. | | | [`axi_serializer`](src/axi_serializer.sv) | Serializes transactions with different IDs to the same ID. | | +| [`axi_slave_compare`](src/axi_slave_compare.sv) | Compares two slave devices. | | | [`axi_throttle`](src/axi_throttle.sv) | Limits the maximum number of outstanding transfers sent to the downstream logic. | | | [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | | [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | @@ -85,6 +87,7 @@ In addition to the modules above, which are available in synthesis and simulatio | [`axi_chan_logger`](src/axi_test.sv) | Logs the transactions of an AXI4(+ATOPs) port to files. | | [`axi_driver`](src/axi_test.sv) | Low-level driver for AXI4(+ATOPs) that can send and receive individual beats on any channel. | | [`axi_dumper`](src/axi_dumper.sv) | Dumps log to file to be interpreted by `axi_dumper_interpret` script for debugging purposes. | +| [`axi_file_master`](src/axi_test.sv) | AXI4 master for file-based testbenches | | [`axi_lite_driver`](src/axi_test.sv) | Low-level driver for AXI4-Lite that can send and receive individual beats on any channel. | | [`axi_lite_rand_master`](src/axi_test.sv) | AXI4-Lite master component that issues random transactions within user-defined constraints. | | [`axi_lite_rand_slave`](src/axi_test.sv) | AXI4-Lite slave component that responds to transactions with constrainable random delays and data. | diff --git a/VERSION b/VERSION index ca75280b0..4ef2eb086 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.38.0 +0.39.0 diff --git a/axi.core b/axi.core index e75ed636f..d3bf2f735 100644 --- a/axi.core +++ b/axi.core @@ -1,6 +1,6 @@ CAPI=2: -name : pulp-platform.org::axi:0.38.0 +name : pulp-platform.org::axi:0.39.0 filesets: rtl: @@ -22,7 +22,7 @@ filesets: - src/axi_cdc_src.sv - src/axi_cut.sv - src/axi_delayer.sv - - src/axi_demux.sv + - src/axi_demux_simple.sv - src/axi_dw_downsizer.sv - src/axi_dw_upsizer.sv - src/axi_fifo.sv @@ -31,6 +31,7 @@ filesets: - src/axi_isolate.sv - src/axi_join.sv - src/axi_lite_demux.sv + - src/axi_lite_dw_converter.sv - src/axi_lite_from_mem.sv - src/axi_lite_join.sv - src/axi_lite_lfsr.sv @@ -46,9 +47,10 @@ filesets: - src/axi_serializer.sv - src/axi_slave_compare.sv - src/axi_throttle.sv - - src/axi_to_mem.sv + - src/axi_to_detailed_mem.sv # Level 3 - src/axi_cdc.sv + - src/axi_demux.sv - src/axi_err_slv.sv - src/axi_dw_converter.sv - src/axi_from_mem.sv @@ -56,13 +58,14 @@ filesets: - src/axi_lfsr.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv - - src/axi_to_mem_banked.sv - - src/axi_to_mem_interleaved.sv - - src/axi_to_mem_split.sv + - src/axi_to_mem.sv # Level 4 - src/axi_iw_converter.sv - src/axi_lite_xbar.sv - src/axi_xbar.sv + - src/axi_to_mem_banked.sv + - src/axi_to_mem_interleaved.sv + - src/axi_to_mem_split.sv - src/axi_chan_compare.sv - src/axi_dumper.sv - src/axi_sim_mem.sv @@ -87,6 +90,7 @@ filesets: - test/tb_axi_fifo.sv - test/tb_axi_isolate.sv - test/tb_axi_iw_converter.sv + - test/tb_axi_lite_dw_converter.sv - test/tb_axi_lite_mailbox.sv - test/tb_axi_lite_regs.sv - test/tb_axi_lite_to_apb.sv diff --git a/include/axi/typedef.svh b/include/axi/typedef.svh index 5eec91171..648c3fed7 100644 --- a/include/axi/typedef.svh +++ b/include/axi/typedef.svh @@ -11,6 +11,7 @@ // // Authors: // - Andreas Kurth +// - Thomas Benz // - Florian Zaruba // - Wolfgang Roenninger diff --git a/scripts/run_verilator.sh b/scripts/run_verilator.sh index ef21af120..dd9be3c0b 100755 --- a/scripts/run_verilator.sh +++ b/scripts/run_verilator.sh @@ -14,6 +14,7 @@ # - Fabian Schuiki # - Florian Zaruba # - Andreas Kurth +# - Thomas Benz set -e ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh index b9656ddf6..9812c5ed5 100755 --- a/scripts/run_vsim.sh +++ b/scripts/run_vsim.sh @@ -13,6 +13,7 @@ # Authors: # - Andreas Kurth # - Fabian Schuiki +# - Wolfgang Roenninger set -euo pipefail ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) diff --git a/scripts/update_authors b/scripts/update_authors index 674bad51b..79fcc24eb 100755 --- a/scripts/update_authors +++ b/scripts/update_authors @@ -19,6 +19,7 @@ declare -A hide=( \ # Map each author name to an email address. declare -A emails=( \ ["Andreas Kurth"]="akurth@iis.ee.ethz.ch" \ + ["Cyril Koenig"]="cykoenig@iis.ee.ethz.ch" \ ["Fabian Schuiki"]="fschuiki@iis.ee.ethz.ch" \ ["Florian Zaruba"]="zarubaf@iis.ee.ethz.ch" \ ["Matheus Cavalcante"]="matheusd@iis.ee.ethz.ch" \ @@ -30,6 +31,8 @@ declare -A emails=( \ ["Luca Valente"]="luca.valente@unibo.it" \ ["Noah Huetter"]="huettern@ethz.ch" \ ["Nils Wistoff"]="nwistoff@iis.ee.ethz.ch" \ + ["Nicole Narr"]="narrn@ethz.ch" \ + ["Paul Scheffler"]="paulsc@iis.ee.ethz.ch" \ ["Tim Fischer"]="fischeti@iis.ee.ethz.ch" \ ) diff --git a/src/axi_chan_compare.sv b/src/axi_chan_compare.sv index 59c99179c..e2339aedf 100644 --- a/src/axi_chan_compare.sv +++ b/src/axi_chan_compare.sv @@ -9,9 +9,9 @@ // specific language governing permissions and limitations under the License. // // Authors: -// - Thomas Benz +// - Thomas Benz // - Paul Scheffler -// - Tim Fischer +// - Tim Fischer /// Non-synthesizable module comparing two AXI channels of the same type module axi_chan_compare #( diff --git a/src/axi_demux.sv b/src/axi_demux.sv index a5f8389ad..899d83521 100644 --- a/src/axi_demux.sv +++ b/src/axi_demux.sv @@ -9,7 +9,9 @@ // specific language governing permissions and limitations under the License. // // Authors: +// - Michael Rogenmoser // - Wolfgang Roenninger +// - Thomas Benz // - Andreas Kurth `include "common_cells/assertions.svh" diff --git a/src/axi_demux_simple.sv b/src/axi_demux_simple.sv index f77514c4f..1c11f794a 100644 --- a/src/axi_demux_simple.sv +++ b/src/axi_demux_simple.sv @@ -10,6 +10,8 @@ // // Authors: // - Wolfgang Roenninger +// - Michael Rogenmoser +// - Thomas Benz // - Andreas Kurth `include "common_cells/assertions.svh" diff --git a/src/axi_from_mem.sv b/src/axi_from_mem.sv index 23ed2d3af..466a384e3 100644 --- a/src/axi_from_mem.sv +++ b/src/axi_from_mem.sv @@ -10,7 +10,7 @@ // // Authors: // - Christopher Reinwardt -// - Nicole Narr `include "axi/typedef.svh" diff --git a/src/axi_id_serialize.sv b/src/axi_id_serialize.sv index b032c7766..9a787dd25 100644 --- a/src/axi_id_serialize.sv +++ b/src/axi_id_serialize.sv @@ -11,6 +11,7 @@ // // Authors: // - Andreas Kurth +// - Paul Scheffler `include "axi/assign.svh" `include "axi/typedef.svh" diff --git a/src/axi_lite_dw_converter.sv b/src/axi_lite_dw_converter.sv index f70940674..d8055481f 100644 --- a/src/axi_lite_dw_converter.sv +++ b/src/axi_lite_dw_converter.sv @@ -9,7 +9,7 @@ // specific language governing permissions and limitations under the License. // Authors: -// - Wolfgang Rönninger +// - Wolfgang Roenninger /// # AXI4-Lite data width downsize module. /// diff --git a/src/axi_lite_from_mem.sv b/src/axi_lite_from_mem.sv index 6b5446c6b..e13793b37 100644 --- a/src/axi_lite_from_mem.sv +++ b/src/axi_lite_from_mem.sv @@ -10,6 +10,7 @@ // // Authors: // - Wolfgang Roenninger +// - Nicole Narr /// Protocol adapter which translates memory requests to the AXI4-Lite protocol. /// diff --git a/src/axi_pkg.sv b/src/axi_pkg.sv index 11c0f8ba7..dd60c451e 100644 --- a/src/axi_pkg.sv +++ b/src/axi_pkg.sv @@ -12,8 +12,10 @@ // Authors: // - Andreas Kurth // - Florian Zaruba +// - Thomas Benz // - Wolfgang Roenninger // - Fabian Schuiki +// - Cyril Koenig // - Matheus Cavalcante //! AXI Package diff --git a/src/axi_test.sv b/src/axi_test.sv index 6fd789415..ea621d91e 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -13,6 +13,7 @@ // - Andreas Kurth // - Wolfgang Roenninger // - Fabian Schuiki +// - Thomas Benz // - Matheus Cavalcante diff --git a/src/axi_to_detailed_mem.sv b/src/axi_to_detailed_mem.sv index 3e6cfdcab..691a1f5c8 100644 --- a/src/axi_to_detailed_mem.sv +++ b/src/axi_to_detailed_mem.sv @@ -10,6 +10,7 @@ // Authors: // - Michael Rogenmoser +// - Thomas Benz `include "common_cells/registers.svh" /// AXI4+ATOP slave module which translates AXI bursts into a memory stream. diff --git a/src/axi_to_mem.sv b/src/axi_to_mem.sv index e935e69ff..37992d449 100644 --- a/src/axi_to_mem.sv +++ b/src/axi_to_mem.sv @@ -10,6 +10,7 @@ // Authors: // - Michael Rogenmoser +// - Thomas Benz `include "common_cells/registers.svh" /// AXI4+ATOP slave module which translates AXI bursts into a memory stream. diff --git a/src/axi_to_mem_split.sv b/src/axi_to_mem_split.sv index 1d86cc3eb..28ce40831 100644 --- a/src/axi_to_mem_split.sv +++ b/src/axi_to_mem_split.sv @@ -10,6 +10,7 @@ // // Authors: // - Michael Rogenmoser +// - Thomas Benz `include "axi/assign.svh" /// AXI4+ATOP to memory-protocol interconnect. Completely separates the read and write channel to diff --git a/src_files.yml b/src_files.yml index 7c3743e41..16cc1ce00 100644 --- a/src_files.yml +++ b/src_files.yml @@ -21,7 +21,7 @@ axi: - src/axi_cdc_src.sv - src/axi_cut.sv - src/axi_delayer.sv - - src/axi_demux.sv + - src/axi_demux_simple.sv - src/axi_dw_downsizer.sv - src/axi_dw_upsizer.sv - src/axi_fifo.sv @@ -30,6 +30,7 @@ axi: - src/axi_isolate.sv - src/axi_join.sv - src/axi_lite_demux.sv + - src/axi_lite_dw_converter.sv - src/axi_lite_from_mem.sv - src/axi_lite_join.sv - src/axi_lite_lfsr.sv @@ -45,23 +46,25 @@ axi: - src/axi_serializer.sv - src/axi_slave_compare.sv - src/axi_throttle.sv - - src/axi_to_mem.sv + - src/axi_to_detailed_mem.sv # Level 3 - src/axi_cdc.sv + - src/axi_demux.sv - src/axi_err_slv.sv - - src/axi_from_mem.sv - src/axi_dw_converter.sv + - src/axi_from_mem.sv - src/axi_id_serialize.sv - src/axi_lfsr.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv - - src/axi_to_mem_banked.sv - - src/axi_to_mem_interleaved.sv - - src/axi_to_mem_split.sv + - src/axi_to_mem.sv # Level 4 - src/axi_iw_converter.sv - src/axi_lite_xbar.sv - src/axi_xbar.sv + - src/axi_to_mem_banked.sv + - src/axi_to_mem_interleaved.sv + - src/axi_to_mem_split.sv # Level 5 - src/axi_xp.sv diff --git a/test/tb_axi_lite_dw_converter.sv b/test/tb_axi_lite_dw_converter.sv index 65319b0ad..e376cb1d6 100644 --- a/test/tb_axi_lite_dw_converter.sv +++ b/test/tb_axi_lite_dw_converter.sv @@ -8,7 +8,9 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -// Author: Wolfgang Roenninger +// Authors: +// - Wolfgang Roenninger +// - Michael Rogenmoser `include "axi/typedef.svh" diff --git a/test/tb_axi_slave_compare.sv b/test/tb_axi_slave_compare.sv index 45809e5d2..2fa47bdc7 100644 --- a/test/tb_axi_slave_compare.sv +++ b/test/tb_axi_slave_compare.sv @@ -2,7 +2,9 @@ // SPDX-License-Identifier: SHL-0.51 // // Authors: +// - Andreas Kurth // - Thomas Benz +// - Michael Rogenmoser `include "axi/assign.svh" `include "axi/typedef.svh" From bfee21757bf090ec8e358456314b0b0fd3c90809 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 20 Jul 2023 10:18:59 +0200 Subject: [PATCH 131/145] Release v0.39.0: Add release date --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab8b06b78..328a8e818 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased +## 0.39.0 - 2023-07-20 + ### Added - Synthesizable IPs: - `axi_bus_compare` and `axi_slave_compare`; two synthesizable verification IPs meant to be used From c879a54ec54e73534d280fb4237f9dc351799b79 Mon Sep 17 00:00:00 2001 From: Mattia Sinigaglia Date: Thu, 20 Jul 2023 12:21:17 +0200 Subject: [PATCH 132/145] propagation of the cdc grey SYNC_STAGES parameter to the interfaces --- src/axi_cdc.sv | 24 ++++++++++++------ src/axi_cdc_dst.sv | 59 +++++++++++++++++++++++++++----------------- src/axi_cdc_src.sv | 61 ++++++++++++++++++++++++++++------------------ 3 files changed, 90 insertions(+), 54 deletions(-) diff --git a/src/axi_cdc.sv b/src/axi_cdc.sv index 152cbef24..81ad58818 100644 --- a/src/axi_cdc.sv +++ b/src/axi_cdc.sv @@ -30,7 +30,9 @@ module axi_cdc #( parameter type axi_req_t = logic, // encapsulates request channels parameter type axi_resp_t = logic, // encapsulates request channels /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LogDepth = 1 + parameter int unsigned LogDepth = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SyncStages = 2 ) ( // slave side - clocked by `src_clk_i` input logic src_clk_i, @@ -63,7 +65,8 @@ module axi_cdc #( .r_chan_t ( r_chan_t ), .axi_req_t ( axi_req_t ), .axi_resp_t ( axi_resp_t ), - .LogDepth ( LogDepth ) + .LogDepth ( LogDepth ), + .SyncStages ( SyncStages ) ) i_axi_cdc_src ( .src_clk_i, .src_rst_ni, @@ -94,7 +97,8 @@ module axi_cdc #( .r_chan_t ( r_chan_t ), .axi_req_t ( axi_req_t ), .axi_resp_t ( axi_resp_t ), - .LogDepth ( LogDepth ) + .LogDepth ( LogDepth ), + .SyncStages ( SyncStages ) ) i_axi_cdc_dst ( .dst_clk_i, .dst_rst_ni, @@ -129,7 +133,9 @@ module axi_cdc_intf #( parameter int unsigned AXI_DATA_WIDTH = 0, parameter int unsigned AXI_USER_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // slave side - clocked by `src_clk_i` input logic src_clk_i, @@ -171,7 +177,8 @@ module axi_cdc_intf #( .r_chan_t ( r_chan_t ), .axi_req_t ( req_t ), .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc ( .src_clk_i, .src_rst_ni, @@ -189,7 +196,9 @@ module axi_lite_cdc_intf #( parameter int unsigned AXI_ADDR_WIDTH = 0, parameter int unsigned AXI_DATA_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // slave side - clocked by `src_clk_i` input logic src_clk_i, @@ -229,7 +238,8 @@ module axi_lite_cdc_intf #( .r_chan_t ( r_chan_t ), .axi_req_t ( req_t ), .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc ( .src_clk_i, .src_rst_ni, diff --git a/src/axi_cdc_dst.sv b/src/axi_cdc_dst.sv index f252a9800..c3e76bfc1 100644 --- a/src/axi_cdc_dst.sv +++ b/src/axi_cdc_dst.sv @@ -24,6 +24,8 @@ module axi_cdc_dst #( /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. parameter int unsigned LogDepth = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SyncStages = 2, parameter type aw_chan_t = logic, parameter type w_chan_t = logic, parameter type b_chan_t = logic, @@ -63,7 +65,8 @@ module axi_cdc_dst #( // Other tools, such as VCS, have problems with type parameters constructed through `$bits()`. .T ( aw_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_aw ( .async_data_i ( async_data_slave_aw_data_i ), .async_wptr_i ( async_data_slave_aw_wptr_i ), @@ -81,7 +84,8 @@ module axi_cdc_dst #( `else .T ( w_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_w ( .async_data_i ( async_data_slave_w_data_i ), .async_wptr_i ( async_data_slave_w_wptr_i ), @@ -99,7 +103,8 @@ module axi_cdc_dst #( `else .T ( b_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_b ( .src_clk_i ( dst_clk_i ), .src_rst_ni ( dst_rst_ni ), @@ -117,7 +122,8 @@ module axi_cdc_dst #( `else .T ( ar_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_ar ( .dst_clk_i, .dst_rst_ni, @@ -135,7 +141,8 @@ module axi_cdc_dst #( `else .T ( r_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_r ( .src_clk_i ( dst_clk_i ), .src_rst_ni ( dst_rst_ni ), @@ -156,7 +163,9 @@ module axi_cdc_dst_intf #( parameter int unsigned AXI_DATA_WIDTH = 0, parameter int unsigned AXI_USER_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // asynchronous slave port AXI_BUS_ASYNC_GRAY.Slave src, @@ -183,14 +192,15 @@ module axi_cdc_dst_intf #( resp_t dst_resp; axi_cdc_dst #( - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .axi_req_t ( req_t ), - .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc_dst ( .async_data_slave_aw_data_i ( src.aw_data ), .async_data_slave_aw_wptr_i ( src.aw_wptr ), @@ -223,7 +233,9 @@ module axi_lite_cdc_dst_intf #( parameter int unsigned AXI_ADDR_WIDTH = 0, parameter int unsigned AXI_DATA_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // asynchronous slave port AXI_LITE_ASYNC_GRAY.Slave src, @@ -248,14 +260,15 @@ module axi_lite_cdc_dst_intf #( resp_t dst_resp; axi_cdc_dst #( - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .axi_req_t ( req_t ), - .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc_dst ( .async_data_slave_aw_data_i ( src.aw_data ), .async_data_slave_aw_wptr_i ( src.aw_wptr ), diff --git a/src/axi_cdc_src.sv b/src/axi_cdc_src.sv index 614a17e45..36e4e6119 100644 --- a/src/axi_cdc_src.sv +++ b/src/axi_cdc_src.sv @@ -23,7 +23,9 @@ /// the FIFO; see the header of `cdc_fifo_gray` for instructions. module axi_cdc_src #( /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LogDepth = 1, + parameter int unsigned LogDepth = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SyncStages = 2, parameter type aw_chan_t = logic, parameter type w_chan_t = logic, parameter type b_chan_t = logic, @@ -62,7 +64,8 @@ module axi_cdc_src #( `else .T ( aw_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_aw ( .src_clk_i, .src_rst_ni, @@ -80,7 +83,8 @@ module axi_cdc_src #( `else .T ( w_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_w ( .src_clk_i, .src_rst_ni, @@ -98,7 +102,8 @@ module axi_cdc_src #( `else .T ( b_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_b ( .dst_clk_i ( src_clk_i ), .dst_rst_ni ( src_rst_ni ), @@ -116,7 +121,8 @@ module axi_cdc_src #( `else .T ( ar_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_ar ( .src_clk_i, .src_rst_ni, @@ -134,7 +140,8 @@ module axi_cdc_src #( `else .T ( r_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_r ( .dst_clk_i ( src_clk_i ), .dst_rst_ni ( src_rst_ni ), @@ -155,7 +162,9 @@ module axi_cdc_src_intf #( parameter int unsigned AXI_DATA_WIDTH = 0, parameter int unsigned AXI_USER_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // synchronous slave port - clocked by `src_clk_i` input logic src_clk_i, @@ -185,14 +194,15 @@ module axi_cdc_src_intf #( `AXI_ASSIGN_FROM_RESP(src, src_resp) axi_cdc_src #( - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .axi_req_t ( req_t ), - .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc_src ( .src_clk_i, .src_rst_ni, @@ -222,7 +232,9 @@ module axi_lite_cdc_src_intf #( parameter int unsigned AXI_ADDR_WIDTH = 0, parameter int unsigned AXI_DATA_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // synchronous slave port - clocked by `src_clk_i` input logic src_clk_i, @@ -250,14 +262,15 @@ module axi_lite_cdc_src_intf #( `AXI_LITE_ASSIGN_FROM_RESP(src, src_resp) axi_cdc_src #( - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .axi_req_t ( req_t ), - .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc_src ( .src_clk_i, .src_rst_ni, From 69a66d85943f00c93ee58b943fad5710c9d1ad00 Mon Sep 17 00:00:00 2001 From: bluew Date: Tue, 9 May 2023 15:45:34 +0200 Subject: [PATCH 133/145] axi_to_mem_interleaved: Add interface version --- src/axi_to_mem_interleaved.sv | 116 ++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/axi_to_mem_interleaved.sv b/src/axi_to_mem_interleaved.sv index fb7e8570e..9a5f87805 100644 --- a/src/axi_to_mem_interleaved.sv +++ b/src/axi_to_mem_interleaved.sv @@ -247,3 +247,119 @@ module axi_to_mem_interleaved #( end endmodule : axi_to_mem_interleaved + +`include "axi/typedef.svh" +`include "axi/assign.svh" +/// AXI4+ATOP interface wrapper for `axi_to_mem_interleaved` +module axi_to_mem_interleaved_intf #( + /// AXI4+ATOP ID width + parameter int unsigned AXI_ID_WIDTH = 32'd0, + /// AXI4+ATOP address width + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + /// AXI4+ATOP data width + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + /// AXI4+ATOP user width + parameter int unsigned AXI_USER_WIDTH = 32'd0, + /// Number of memory banks / macros + parameter int unsigned MEM_NUM_BANKS = 32'd4, + /// Read latency of the connected memory in cycles + parameter int unsigned BUF_DEPTH = 32'd1, + /// Hide write requests if the strb == '0 + parameter bit HIDE_STRB = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OUT_FIFO_DEPTH = 32'd1, + + /// Dependent parameter, do not override. Memory address type. + parameter type mem_addr_t = logic [AXI_ADDR_WIDTH-1:0], + /// Dependent parameter, do not override. Memory atomic type. + parameter type mem_atop_t = axi_pkg::atop_t, + /// Dependent parameter, do not override. Memory data type. + parameter type mem_data_t = logic [AXI_DATA_WIDTH/MEM_NUM_BANKS-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + parameter type mem_strb_t = logic [AXI_DATA_WIDTH/MEM_NUM_BANKS/8-1:0] +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Status output, busy flag of `axi_to_mem` + output logic busy_o, + /// AXI4+ATOP slave port + AXI_BUS.Slave slv, + /// Memory bank request + output logic [MEM_NUM_BANKS-1:0] mem_req_o, + /// Memory request grant + input logic [MEM_NUM_BANKS-1:0] mem_gnt_i, + /// Request address + output mem_addr_t [MEM_NUM_BANKS-1:0] mem_addr_o, + /// Write data + output mem_data_t [MEM_NUM_BANKS-1:0] mem_wdata_o, + /// Write data byte enable, active high + output mem_strb_t [MEM_NUM_BANKS-1:0] mem_strb_o, + /// Atomic operation + output mem_atop_t [MEM_NUM_BANKS-1:0] mem_atop_o, + /// Write request enable, active high + output logic [MEM_NUM_BANKS-1:0] mem_we_o, + /// Read data valid response, active high + input logic [MEM_NUM_BANKS-1:0] mem_rvalid_i, + /// Read data response + input mem_data_t [MEM_NUM_BANKS-1:0] mem_rdata_i +); + typedef logic [AXI_ID_WIDTH-1:0] id_t; + typedef logic [AXI_ADDR_WIDTH-1:0] addr_t; + typedef logic [AXI_DATA_WIDTH-1:0] data_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t; + typedef logic [AXI_USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) + + axi_req_t mem_axi_req; + axi_resp_t mem_axi_resp; + + `AXI_ASSIGN_TO_REQ(mem_axi_req, slv) + `AXI_ASSIGN_FROM_RESP(slv, mem_axi_resp) + + axi_to_mem_interleaved #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AXI_ADDR_WIDTH ), + .DataWidth ( AXI_DATA_WIDTH ), + .IdWidth ( AXI_ID_WIDTH ), + .NumBanks ( MEM_NUM_BANKS ), + .BufDepth ( BUF_DEPTH ), + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) + ) i_axi_to_mem_interleaved ( + .clk_i, + .rst_ni, + .busy_o, + .axi_req_i ( mem_axi_req ), + .axi_resp_o ( mem_axi_resp ), + .mem_req_o, + .mem_gnt_i, + .mem_addr_o, + .mem_wdata_o, + .mem_strb_o, + .mem_atop_o, + .mem_we_o, + .mem_rvalid_i, + .mem_rdata_i + ); + +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (AXI_ADDR_WIDTH >= 1) else $fatal(1, "AXI address width must be at least 1!"); + assert (AXI_DATA_WIDTH >= 1) else $fatal(1, "AXI data width must be at least 1!"); + assert (AXI_ID_WIDTH >= 1) else $fatal(1, "AXI ID width must be at least 1!"); + assert (AXI_USER_WIDTH >= 1) else $fatal(1, "AXI user width must be at least 1!"); + end +`endif +// pragma translate_on + +endmodule // axi_to_mem_interleaved_intf From 8c88c76df01479f6fa2089c1ebaf87615aaf9812 Mon Sep 17 00:00:00 2001 From: Luca Colagrande Date: Mon, 12 Jun 2023 09:32:15 +0200 Subject: [PATCH 134/145] axi_burst_splitter: Expose id_queue module's FULL_BW parameter --- src/axi_burst_splitter.sv | 14 +++++++++++--- src/axi_to_axi_lite.sv | 6 +++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/axi_burst_splitter.sv b/src/axi_burst_splitter.sv index 4fe6f7e78..4f3626ebc 100644 --- a/src/axi_burst_splitter.sv +++ b/src/axi_burst_splitter.sv @@ -30,6 +30,8 @@ module axi_burst_splitter #( parameter int unsigned MaxReadTxns = 32'd0, // Maximum number of AXI write bursts outstanding at the same time parameter int unsigned MaxWriteTxns = 32'd0, + // Internal ID queue can work in two bandwidth modes: see id_queue.sv for details + parameter bit FullBW = 0, // AXI Bus Types parameter int unsigned AddrWidth = 32'd0, parameter int unsigned DataWidth = 32'd0, @@ -139,7 +141,8 @@ module axi_burst_splitter #( axi_burst_splitter_ax_chan #( .chan_t ( aw_chan_t ), .IdWidth ( IdWidth ), - .MaxTxns ( MaxWriteTxns ) + .MaxTxns ( MaxWriteTxns ), + .FullBW ( FullBW ) ) i_axi_burst_splitter_aw_chan ( .clk_i, .rst_ni, @@ -233,7 +236,8 @@ module axi_burst_splitter #( axi_burst_splitter_ax_chan #( .chan_t ( ar_chan_t ), .IdWidth ( IdWidth ), - .MaxTxns ( MaxReadTxns ) + .MaxTxns ( MaxReadTxns ), + .FullBW ( FullBW ) ) i_axi_burst_splitter_ar_chan ( .clk_i, .rst_ni, @@ -346,6 +350,7 @@ module axi_burst_splitter_ax_chan #( parameter type chan_t = logic, parameter int unsigned IdWidth = 0, parameter int unsigned MaxTxns = 0, + parameter bit FullBW = 0, parameter type id_t = logic[IdWidth-1:0] ) ( input logic clk_i, @@ -371,6 +376,7 @@ module axi_burst_splitter_ax_chan #( logic cnt_alloc_req, cnt_alloc_gnt; axi_burst_splitter_counters #( .MaxTxns ( MaxTxns ), + .FullBW ( FullBW ), .IdWidth ( IdWidth ) ) i_axi_burst_splitter_counters ( .clk_i, @@ -459,6 +465,7 @@ endmodule /// Internal module of [`axi_burst_splitter`](module.axi_burst_splitter) to order transactions. module axi_burst_splitter_counters #( parameter int unsigned MaxTxns = 0, + parameter bit FullBW = 0, parameter int unsigned IdWidth = 0, parameter type id_t = logic [IdWidth-1:0] ) ( @@ -517,7 +524,8 @@ module axi_burst_splitter_counters #( id_queue #( .ID_WIDTH ( $bits(id_t) ), .CAPACITY ( MaxTxns ), - .data_t ( cnt_idx_t ) + .data_t ( cnt_idx_t ), + .FULL_BW ( FullBW ) ) i_idq ( .clk_i, .rst_ni, diff --git a/src/axi_to_axi_lite.sv b/src/axi_to_axi_lite.sv index a0702bb79..ad2e41414 100644 --- a/src/axi_to_axi_lite.sv +++ b/src/axi_to_axi_lite.sv @@ -22,6 +22,7 @@ module axi_to_axi_lite #( parameter int unsigned AxiUserWidth = 32'd0, parameter int unsigned AxiMaxWriteTxns = 32'd0, parameter int unsigned AxiMaxReadTxns = 32'd0, + parameter bit FullBW = 0, // ID Queue in Full BW mode in axi_burst_splitter parameter bit FallThrough = 1'b1, // FIFOs in Fall through mode in ID reflect parameter type full_req_t = logic, parameter type full_resp_t = logic, @@ -61,6 +62,7 @@ module axi_to_axi_lite #( axi_burst_splitter #( .MaxReadTxns ( AxiMaxReadTxns ), .MaxWriteTxns ( AxiMaxWriteTxns ), + .FullBW ( FullBW ), .AddrWidth ( AxiAddrWidth ), .DataWidth ( AxiDataWidth ), .IdWidth ( AxiIdWidth ), @@ -255,7 +257,8 @@ module axi_to_axi_lite_intf #( parameter int unsigned AXI_MAX_WRITE_TXNS = 32'd1, /// Maximum number of outstanding reads. parameter int unsigned AXI_MAX_READ_TXNS = 32'd1, - parameter bit FALL_THROUGH = 1'b1 + parameter bit FALL_THROUGH = 1'b1, + parameter bit FULL_BW = 0 ) ( input logic clk_i, input logic rst_ni, @@ -304,6 +307,7 @@ module axi_to_axi_lite_intf #( .AxiMaxWriteTxns ( AXI_MAX_WRITE_TXNS ), .AxiMaxReadTxns ( AXI_MAX_READ_TXNS ), .FallThrough ( FALL_THROUGH ), // FIFOs in Fall through mode in ID reflect + .FullBW ( FULL_BW ), .full_req_t ( full_req_t ), .full_resp_t ( full_resp_t ), .lite_req_t ( lite_req_t ), From 91185f71cb48acc972907bca230297a05d858bb4 Mon Sep 17 00:00:00 2001 From: Luca Valente Date: Tue, 20 Jun 2023 21:50:14 +0200 Subject: [PATCH 135/145] `axi_scoreboard`: avoid false negs for mem-mapped misaligned reads. If the access is misaligned the subordinate does not need to fill the don't care bytes with the the stored values. --- src/axi_test.sv | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/axi_test.sv b/src/axi_test.sv index ea621d91e..c9fca2697 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -2046,6 +2046,7 @@ package axi_test; byte_t act_data; byte_t exp_data[$]; byte_t tst_data[$]; + int first_byte_to_check; forever begin wait (this.ar_sample[id].size() > 0); ar_beat = this.ar_sample[id].pop_front(); @@ -2060,6 +2061,10 @@ package axi_test; ar_beat.ax_burst, i); beat_address = axi_pkg::aligned_addr(beat_address, ar_beat.ax_size); bus_address = axi_pkg::aligned_addr(beat_address, BUS_SIZE); + if(i!=0) + first_byte_to_check = 0; + else + first_byte_to_check = ar_beat.ax_addr - beat_address; if (!this.memory_q.exists(bus_address)) begin for (int unsigned j = 0; j < axi_pkg::num_bytes(BUS_SIZE); j++) begin this.memory_q[bus_address+j].push_back(8'bxxxxxxxx); @@ -2068,7 +2073,7 @@ package axi_test; // Assert that the correct data is read. if (this.check_en[ReadCheck] && (r_beat.r_resp inside {axi_pkg::RESP_OKAY, axi_pkg::RESP_EXOKAY})) begin - for (int unsigned j = 0; j < axi_pkg::num_bytes(ar_beat.ax_size); j++) begin + for (int unsigned j = first_byte_to_check; j < axi_pkg::num_bytes(ar_beat.ax_size); j++) begin idx_data = 8*BUS_SIZE'(beat_address+j); act_data = r_beat.r_data[idx_data+:8]; exp_data = this.memory_q[beat_address+j]; From 27df7132352b6d262db529b06fb1637aa04b66e7 Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Wed, 24 May 2023 21:53:14 +0200 Subject: [PATCH 136/145] axi_chan_compare: Adds parameter to allow reordered transactions --- src/axi_chan_compare.sv | 65 ++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/src/axi_chan_compare.sv b/src/axi_chan_compare.sv index e2339aedf..1e4e82347 100644 --- a/src/axi_chan_compare.sv +++ b/src/axi_chan_compare.sv @@ -17,6 +17,11 @@ module axi_chan_compare #( /// Ignore ID field if it was remapped parameter bit IgnoreId = 1'b0, + /// Allow reordered responses of different IDs, + /// not compatible with `IgnoreId` + parameter bit AllowReordering = 1'b0, + /// AXI ID Width + parameter int unsigned IdWidth = 1, parameter type aw_chan_t = logic, parameter type w_chan_t = logic, parameter type b_chan_t = logic, @@ -123,34 +128,40 @@ module axi_chan_compare #( // verilog_lint: waive-stop line-length endfunction + localparam NumIds = (AllowReordering)? 2**IdWidth : 1; + // queues - aw_chan_t aw_queue [$]; - w_chan_t w_queue [$]; - b_chan_t b_queue [$]; - ar_chan_t ar_queue [$]; - r_chan_t r_queue [$]; + aw_chan_t aw_queue [NumIds-1:0][$]; + w_chan_t w_queue [$]; + b_chan_t b_queue [NumIds-1:0][$]; + ar_chan_t ar_queue [NumIds-1:0][$]; + r_chan_t r_queue [NumIds-1:0][$]; // requests generated at axi A: enqueue elements always_ff @(posedge clk_a_i) begin : proc_enqueue_a // aw if (axi_a_req.aw_valid & axi_a_res.aw_ready) - aw_queue.push_back(axi_a_req.aw); + if (AllowReordering) aw_queue[axi_a_req.aw.id].push_back(axi_a_req.aw); + else aw_queue[0].push_back(axi_a_req.aw); // w if (axi_a_req.w_valid & axi_a_res.w_ready) w_queue.push_back(axi_a_req.w); // ar if (axi_a_req.ar_valid & axi_a_res.ar_ready) - ar_queue.push_back(axi_a_req.ar); + if (AllowReordering) ar_queue[axi_a_req.ar.id].push_back(axi_a_req.ar); + else ar_queue[0].push_back(axi_a_req.ar); end // responses generated at axi B: enqueue elements always_ff @(posedge clk_b_i) begin : proc_enqueue_b // b if (axi_b_res.b_valid & axi_b_req.b_ready) - b_queue.push_back(axi_b_res.b); + if (AllowReordering) b_queue[axi_b_res.b.id].push_back(axi_b_res.b); + else b_queue[0].push_back(axi_b_res.b); // r if (axi_b_res.r_valid & axi_b_req.r_ready) - r_queue.push_back(axi_b_res.r); + if (AllowReordering) r_queue[axi_b_res.r.id].push_back(axi_b_res.r); + else r_queue[0].push_back(axi_b_res.r); end // requests arriving at axi B from A: dequeue elements and check @@ -158,8 +169,13 @@ module axi_chan_compare #( // aw if (axi_b_req.aw_valid & axi_b_res.aw_ready) begin automatic aw_chan_t aw_exp, aw_recv; - if (aw_queue.size() == 0) $error("AW queue is empty!"); - aw_exp = aw_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + if (AllowReordering) begin + if (aw_queue[axi_b_req.aw.id].size() == 0) $error("AW queue is empty!"); + aw_exp = aw_queue[axi_b_req.aw.id].pop_front(); // verilog_lint: waive always-ff-non-blocking + end else begin + if (aw_queue[0].size() == 0) $error("AW queue is empty!"); + aw_exp = aw_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking + end aw_recv = axi_b_req.aw; if (IgnoreId) begin aw_exp.id = 'X; @@ -184,8 +200,13 @@ module axi_chan_compare #( // ar if (axi_b_req.ar_valid & axi_b_res.ar_ready) begin automatic ar_chan_t ar_exp, ar_recv; - if (ar_queue.size() == 0) $error("AR queue is empty!"); - ar_exp = ar_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + if (AllowReordering) begin + if (ar_queue[axi_b_req.ar.id].size() == 0) $error("AR queue is empty!"); + ar_exp = ar_queue[axi_b_req.ar.id].pop_front(); // verilog_lint: waive always-ff-non-blocking + end else begin + if (ar_queue[0].size() == 0) $error("AR queue is empty!"); + ar_exp = ar_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking + end ar_recv = axi_b_req.ar; if (IgnoreId) begin ar_exp.id = 'X; @@ -203,8 +224,13 @@ module axi_chan_compare #( // b if (axi_a_res.b_valid & axi_a_req.b_ready) begin automatic b_chan_t b_exp, b_recv; - if (b_queue.size() == 0) $error("B queue is empty!"); - b_exp = b_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + if (AllowReordering) begin + if (b_queue[axi_a_res.b.id].size() == 0) $error("B queue is empty!"); + b_exp = b_queue[axi_a_res.b.id].pop_front(); // verilog_lint: waive always-ff-non-blocking + end else begin + if (b_queue[0].size() == 0) $error("B queue is empty!"); + b_exp = b_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking + end b_recv = axi_a_res.b; if (IgnoreId) begin b_exp.id = 'X; @@ -218,8 +244,13 @@ module axi_chan_compare #( // r if (axi_a_res.r_valid & axi_a_req.r_ready) begin automatic r_chan_t r_exp, r_recv; - if (r_queue.size() == 0) $error("R queue is empty!"); - r_exp = r_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + if (AllowReordering) begin + if (r_queue[axi_a_res.r.id].size() == 0) $error("R queue is empty!"); + r_exp = r_queue[axi_a_res.r.id].pop_front(); // verilog_lint: waive always-ff-non-blocking + end else begin + if (r_queue[0].size() == 0) $error("R queue is empty!"); + r_exp = r_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking + end r_recv = axi_a_res.r; if (IgnoreId) begin r_exp.id = 'X; From 75acfc643f5ee06d2aa54a4f3b0179ad791fe218 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Tue, 25 Jul 2023 15:06:57 +0200 Subject: [PATCH 137/145] include/axi: Add support for signal highlighter and add flat port instantation macros --- include/axi/assign.svh | 42 +++++++++++++++ include/axi/port.svh | 120 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 include/axi/port.svh diff --git a/include/axi/assign.svh b/include/axi/assign.svh index 80667eb09..739038f6c 100644 --- a/include/axi/assign.svh +++ b/include/axi/assign.svh @@ -652,4 +652,46 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros instantiating signal highlighter from common verification +// https://github.com/pulp-platform/common_verification.git +// `AXI_HIGHLIGHT(__name, __aw_t, __w_t, __b_t, __ar_t, __r_t, __req, __resp) +`define AXI_HIGHLIGHT(__name, __aw_t, __w_t, __b_t, __ar_t, __r_t, __req, __resp) \ + signal_highlighter #( \ + .T ( __aw_t ) \ + ) i_signal_highlighter_``__name``_aw ( \ + .ready_i ( __resp.aw_ready ), \ + .valid_i ( __req.aw_valid ), \ + .data_i ( __req.aw ) \ + ); \ + signal_highlighter #( \ + .T ( __w_t ) \ + ) i_signal_highlighter_``__name``_w ( \ + .ready_i ( __resp.w_ready ), \ + .valid_i ( __req.w_valid ), \ + .data_i ( __req.w ) \ + ); \ + signal_highlighter #( \ + .T ( __b_t ) \ + ) i_signal_highlighter_``__name``_b ( \ + .ready_i ( __req.b_ready ), \ + .valid_i ( __resp.b_valid ), \ + .data_i ( __resp.b ) \ + ); \ + signal_highlighter #( \ + .T ( __ar_t ) \ + ) i_signal_highlighter_``__name``_ar ( \ + .ready_i ( __resp.ar_ready ), \ + .valid_i ( __req.ar_valid ), \ + .data_i ( __req.ar ) \ + ); \ + signal_highlighter #( \ + .T ( __r_t ) \ + ) i_signal_highlighter_``__name``_r ( \ + .ready_i ( __req.r_ready ), \ + .valid_i ( __resp.r_valid ), \ + .data_i ( __resp.r ) \ + ); +//////////////////////////////////////////////////////////////////////////////////////////////////// + `endif diff --git a/include/axi/port.svh b/include/axi/port.svh new file mode 100644 index 000000000..c3d7a4a7f --- /dev/null +++ b/include/axi/port.svh @@ -0,0 +1,120 @@ +// Copyright (c) 2014-2023 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 + +// Macros to add AXI ports + +`ifndef AXI_PORT_SVH_ +`define AXI_PORT_SVH_ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros creating flat AXI ports +// `AXI_M_PORT(__name, __addr_t, __data_t, __strb_t, __id_t, __aw_user_t, __w_user_t, __b_user_t, __ar_user_t, __r_user_t) +`define AXI_M_PORT(__name, __addr_t, __data_t, __strb_t, __id_t, __aw_user_t, __w_user_t, __b_user_t, __ar_user_t, __r_user_t) \ + output logic m_axi_``__name``_awvalid, \ + output __id_t m_axi_``__name``_awid, \ + output __addr_t m_axi_``__name``_awaddr, \ + output axi_pkg::len_t m_axi_``__name``_awlen, \ + output axi_pkg::size_t m_axi_``__name``_awsize, \ + output axi_pkg::burst_t m_axi_``__name``_awburst, \ + output logic m_axi_``__name``_awlock, \ + output axi_pkg::cache_t m_axi_``__name``_awcache, \ + output axi_pkg::prot_t m_axi_``__name``_awprot, \ + output axi_pkg::qos_t m_axi_``__name``_awqos, \ + output axi_pkg::region_t m_axi_``__name``_awregion, \ + output __aw_user_t m_axi_``__name``_awuser, \ + output logic m_axi_``__name``_wvalid, \ + output __data_t m_axi_``__name``_wdata, \ + output __strb_t m_axi_``__name``_wstrb, \ + output logic m_axi_``__name``_wlast, \ + output __w_user_t m_axi_``__name``_wuser, \ + output logic m_axi_``__name``_bready, \ + output logic m_axi_``__name``_arvalid, \ + output __id_t m_axi_``__name``_arid, \ + output __addr_t m_axi_``__name``_araddr, \ + output axi_pkg::len_t m_axi_``__name``_arlen, \ + output axi_pkg::size_t m_axi_``__name``_arsize, \ + output axi_pkg::burst_t m_axi_``__name``_arburst, \ + output logic m_axi_``__name``_arlock, \ + output axi_pkg::cache_t m_axi_``__name``_arcache, \ + output axi_pkg::prot_t m_axi_``__name``_arprot, \ + output axi_pkg::qos_t m_axi_``__name``_arqos, \ + output axi_pkg::region_t m_axi_``__name``_arregion, \ + output __ar_user_t m_axi_``__name``_aruser, \ + output logic m_axi_``__name``_rready, \ + input logic m_axi_``__name``_awready, \ + input logic m_axi_``__name``_arready, \ + input logic m_axi_``__name``_wready, \ + input logic m_axi_``__name``_bvalid, \ + input __id_t m_axi_``__name``_bid, \ + input axi_pkg::resp_t m_axi_``__name``_bresp, \ + input __b_user_t m_axi_``__name``_buser, \ + input logic m_axi_``__name``_rvalid, \ + input __id_t m_axi_``__name``_rid, \ + input __data_t m_axi_``__name``_rdata, \ + input axi_pkg::resp_t m_axi_``__name``_rresp, \ + input logic m_axi_``__name``_rlast, \ + input __r_user_t m_axi_``__name``_ruser, \ +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros creating flat AXI ports +// `AXI_S_PORT(__name, __addr_t, __data_t, __strb_t, __id_t, __aw_user_t, __w_user_t, __b_user_t, __ar_user_t, __r_user_t) +`define AXI_S_PORT(__name, __addr_t, __data_t, __strb_t, __id_t, __aw_user_t, __w_user_t, __b_user_t, __ar_user_t, __r_user_t) \ + input logic s_axi_``__name``_awvalid, \ + input __id_t s_axi_``__name``_awid, \ + input __addr_t s_axi_``__name``_awaddr, \ + input axi_pkg::len_t s_axi_``__name``_awlen, \ + input axi_pkg::size_t s_axi_``__name``_awsize, \ + input axi_pkg::burst_t s_axi_``__name``_awburst, \ + input logic s_axi_``__name``_awlock, \ + input axi_pkg::cache_t s_axi_``__name``_awcache, \ + input axi_pkg::prot_t s_axi_``__name``_awprot, \ + input axi_pkg::qos_t s_axi_``__name``_awqos, \ + input axi_pkg::region_t s_axi_``__name``_awregion, \ + input __aw_user_t s_axi_``__name``_awuser, \ + input logic s_axi_``__name``_wvalid, \ + input __data_t s_axi_``__name``_wdata, \ + input __strb_t s_axi_``__name``_wstrb, \ + input logic s_axi_``__name``_wlast, \ + input __w_user_t s_axi_``__name``_wuser, \ + input logic s_axi_``__name``_bready, \ + input logic s_axi_``__name``_arvalid, \ + input __id_t s_axi_``__name``_arid, \ + input __addr_t s_axi_``__name``_araddr, \ + input axi_pkg::len_t s_axi_``__name``_arlen, \ + input axi_pkg::size_t s_axi_``__name``_arsize, \ + input axi_pkg::burst_t s_axi_``__name``_arburst, \ + input logic s_axi_``__name``_arlock, \ + input axi_pkg::cache_t s_axi_``__name``_arcache, \ + input axi_pkg::prot_t s_axi_``__name``_arprot, \ + input axi_pkg::qos_t s_axi_``__name``_arqos, \ + input axi_pkg::region_t s_axi_``__name``_arregion, \ + input __ar_user_t s_axi_``__name``_aruser, \ + input logic s_axi_``__name``_rready, \ + output logic s_axi_``__name``_awready, \ + output logic s_axi_``__name``_arready, \ + output logic s_axi_``__name``_wready, \ + output logic s_axi_``__name``_bvalid, \ + output __id_t s_axi_``__name``_bid, \ + output axi_pkg::resp_t s_axi_``__name``_bresp, \ + output __b_user_t s_axi_``__name``_buser, \ + output logic s_axi_``__name``_rvalid, \ + output __id_t s_axi_``__name``_rid, \ + output __data_t s_axi_``__name``_rdata, \ + output axi_pkg::resp_t s_axi_``__name``_rresp, \ + output logic s_axi_``__name``_rlast, \ + output __r_user_t s_axi_``__name``_ruser, \ +//////////////////////////////////////////////////////////////////////////////////////////////////// + +`endif From 7bfa7fb5af44adf4395e2b87e33d9838e0f9b160 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Thu, 27 Jul 2023 09:44:56 +0200 Subject: [PATCH 138/145] Limit page deployment of CI to master branch --- .github/workflows/doc.yml | 5 +++-- .github/workflows/gitlab-ci.yml | 4 ---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 0f50b87e5..2996e519b 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -60,8 +60,9 @@ jobs: - name: Deploy documentation uses: JamesIves/github-pages-deploy-action@v4 if: > - github.event_name == 'push' - || github.event.pull_request.head.repo.full_name == github.repository + (github.event_name == 'push' + || github.event.pull_request.head.repo.full_name == github.repository) && + github.ref == 'refs/heads/master' with: token: ${{ secrets.GH_PAGES }} branch: gh-pages # The branch the action should deploy to. diff --git a/.github/workflows/gitlab-ci.yml b/.github/workflows/gitlab-ci.yml index 560eaedbb..96456f43e 100644 --- a/.github/workflows/gitlab-ci.yml +++ b/.github/workflows/gitlab-ci.yml @@ -4,10 +4,6 @@ on: branches-ignore: - gh-pages # deployment target branch (this workflow should not exist on that branch anyway) - v** # such branch names conflict with tags - pull_request: - branches-ignore: - - gh-pages # deployment target branch (this workflow should not exist on that branch anyway) - - v** # such branch names conflict with tags workflow_dispatch: jobs: From 9e1b7d27bca357de59cf978e1a81c860d3422973 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Thu, 27 Jul 2023 16:21:35 +0200 Subject: [PATCH 139/145] Properly handle response code in axi_to_detailed_mem Fixes #319 --- Bender.yml | 2 +- src/axi_to_detailed_mem.sv | 71 +++++++++++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/Bender.yml b/Bender.yml index 8f16f74cb..2f121e0bf 100644 --- a/Bender.yml +++ b/Bender.yml @@ -19,7 +19,7 @@ package: - "Florian Zaruba " dependencies: - common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.27.0 } + common_cells: { git: "https://github.com/pulp-platform/common_cells.git", rev: "be16fe6efa97af39ca258e398f37b7910e636e02" } common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.3 } tech_cells_generic: { git: "https://github.com/pulp-platform/tech_cells_generic.git", version: 0.2.2 } diff --git a/src/axi_to_detailed_mem.sv b/src/axi_to_detailed_mem.sv index 691a1f5c8..3a656b0dc 100644 --- a/src/axi_to_detailed_mem.sv +++ b/src/axi_to_detailed_mem.sv @@ -122,6 +122,7 @@ module axi_to_detailed_mem #( addr_t addr; axi_pkg::atop_t atop; logic lock; + axi_strb_t strb; axi_id_t id; logic last; axi_pkg::qos_t qos; @@ -133,7 +134,13 @@ module axi_to_detailed_mem #( axi_pkg::region_t region; } meta_t; - axi_data_t mem_rdata, + typedef struct packed { + axi_data_t data; + logic [NumBanks-1:0] err; + logic [NumBanks-1:0] exokay; + } mem_rsp_t; + + mem_rsp_t mem_rdata, m2s_resp; axi_pkg::len_t r_cnt_d, r_cnt_q, w_cnt_d, w_cnt_q; @@ -188,6 +195,7 @@ module axi_to_detailed_mem #( addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.ar.addr, axi_req_i.ar.size)), atop: '0, lock: axi_req_i.ar.lock, + strb: '0, id: axi_req_i.ar.id, last: (axi_req_i.ar.len == '0), qos: axi_req_i.ar.qos, @@ -224,6 +232,7 @@ module axi_to_detailed_mem #( wr_meta.addr = wr_meta_q.addr + axi_pkg::num_bytes(wr_meta_q.size); if (axi_req_i.w_valid) begin wr_valid = 1'b1; + wr_meta.strb = axi_req_i.w.strb; if (wr_ready) begin axi_resp_o.w_ready = 1'b1; w_cnt_d--; @@ -236,6 +245,7 @@ module axi_to_detailed_mem #( addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.aw.addr, axi_req_i.aw.size)), atop: axi_req_i.aw.atop, lock: axi_req_i.aw.lock, + strb: axi_req_i.w.strb, id: axi_req_i.aw.id, last: (axi_req_i.aw.len == '0), qos: axi_req_i.aw.qos, @@ -384,7 +394,7 @@ module axi_to_detailed_mem #( // Interface memory as stream. stream_to_mem #( .mem_req_t ( mem_req_t ), - .mem_resp_t ( axi_data_t ), + .mem_resp_t ( mem_rsp_t ), .BufDepth ( BufDepth ) ) i_stream_to_mem ( .clk_i, @@ -438,10 +448,20 @@ module axi_to_detailed_mem #( assign mem_region_o[i] = banked_req_atop[i].region; end + logic [NumBanks*2-1:0] tmp_ersp; + logic [NumBanks-1:0][1:0] bank_ersp; + for (genvar i = 0; i < NumBanks; i++) begin + assign mem_rdata.err[i] = tmp_ersp[i*2]; + assign mem_rdata.exokay[i] = tmp_ersp[i*2+1]; + assign bank_ersp[i][0] = mem_err_i[i]; + assign bank_ersp[i][1] = mem_exokay_i[i]; + end + // Split single memory request to desired number of banks. - mem_to_banks #( + mem_to_banks_detailed #( .AddrWidth ( AddrWidth ), .DataWidth ( DataWidth ), + .ErspWidth ( 2 ), .NumBanks ( NumBanks ), .HideStrb ( HideStrb ), .MaxTrans ( BufDepth ), @@ -458,7 +478,8 @@ module axi_to_detailed_mem #( .atop_i ( mem_req_atop ), .we_i ( mem_req.we ), .rvalid_o ( mem_rvalid ), - .rdata_o ( mem_rdata ), + .rdata_o ( mem_rdata.data ), + .ersp_o ( tmp_ersp ), .bank_req_o ( mem_req_o ), .bank_gnt_i ( mem_gnt_i ), .bank_addr_o ( mem_addr_o ), @@ -467,7 +488,8 @@ module axi_to_detailed_mem #( .bank_atop_o ( banked_req_atop ), .bank_we_o ( mem_we_o ), .bank_rvalid_i ( mem_rvalid_i ), - .bank_rdata_i ( mem_rdata_i ) + .bank_rdata_i ( mem_rdata_i ), + .bank_ersp_i ( bank_ersp ) ); // Join memory read data and meta data stream. @@ -496,19 +518,50 @@ module axi_to_detailed_mem #( .ready_i ({axi_req_i.b_ready, axi_req_i.r_ready }) ); + // Accumulate errors if memory responds + localparam NumBytesPerBank = DataWidth/NumBanks/8; + logic collect_b_err_d, collect_b_err_q; + logic collect_b_exokay_d, collect_b_exokay_q; + logic [NumBanks-1:0] meta_buf_bank_strb, meta_buf_size_enable; + logic resp_b_err, resp_b_exokay, resp_r_err, resp_r_exokay; + for (genvar i = 0; i < NumBanks; i++) begin + assign meta_buf_bank_strb[i] = meta_buf.strb[i*NumBytesPerBank +: NumBytesPerBank]; + assign meta_buf_size_enable[i] = i*NumBytesPerBank + NumBytesPerBank > (meta_buf.addr % DataWidth/8) && + i*NumBytesPerBank < ((meta_buf.addr % DataWidth/8) + 1< Date: Fri, 28 Jul 2023 17:38:30 +0200 Subject: [PATCH 140/145] axi_to_detailed_mem: Expand explanation --- Bender.yml | 2 +- src/axi_to_detailed_mem.sv | 41 +++++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/Bender.yml b/Bender.yml index 2f121e0bf..ab90f8c83 100644 --- a/Bender.yml +++ b/Bender.yml @@ -19,7 +19,7 @@ package: - "Florian Zaruba " dependencies: - common_cells: { git: "https://github.com/pulp-platform/common_cells.git", rev: "be16fe6efa97af39ca258e398f37b7910e636e02" } + common_cells: { git: "https://github.com/pulp-platform/common_cells.git", rev: "99fcffdc3f8a5f3f590f6c9f687f65a2e9f2e0fe" } # updated mem_to_banks_detailed common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.3 } tech_cells_generic: { git: "https://github.com/pulp-platform/tech_cells_generic.git", version: 0.2.2 } diff --git a/src/axi_to_detailed_mem.sv b/src/axi_to_detailed_mem.sv index 3a656b0dc..9da63c013 100644 --- a/src/axi_to_detailed_mem.sv +++ b/src/axi_to_detailed_mem.sv @@ -448,11 +448,11 @@ module axi_to_detailed_mem #( assign mem_region_o[i] = banked_req_atop[i].region; end - logic [NumBanks*2-1:0] tmp_ersp; + logic [NumBanks-1:0][1:0] tmp_ersp; logic [NumBanks-1:0][1:0] bank_ersp; for (genvar i = 0; i < NumBanks; i++) begin - assign mem_rdata.err[i] = tmp_ersp[i*2]; - assign mem_rdata.exokay[i] = tmp_ersp[i*2+1]; + assign mem_rdata.err[i] = tmp_ersp[i][0]; + assign mem_rdata.exokay[i] = tmp_ersp[i][1]; assign bank_ersp[i][0] = mem_err_i[i]; assign bank_ersp[i][1] = mem_exokay_i[i]; end @@ -518,21 +518,34 @@ module axi_to_detailed_mem #( .ready_i ({axi_req_i.b_ready, axi_req_i.r_ready }) ); - // Accumulate errors if memory responds localparam NumBytesPerBank = DataWidth/NumBanks/8; - logic collect_b_err_d, collect_b_err_q; - logic collect_b_exokay_d, collect_b_exokay_q; + logic [NumBanks-1:0] meta_buf_bank_strb, meta_buf_size_enable; logic resp_b_err, resp_b_exokay, resp_r_err, resp_r_exokay; + + // Collect `err` and `exokay` from all banks + // To ensure correct propagation, `err` is grouped with `OR` and `exokay` is grouped with `AND`. for (genvar i = 0; i < NumBanks; i++) begin - assign meta_buf_bank_strb[i] = meta_buf.strb[i*NumBytesPerBank +: NumBytesPerBank]; + // Set active write banks based on strobe + assign meta_buf_bank_strb[i] = |meta_buf.strb[i*NumBytesPerBank +: NumBytesPerBank]; + // Set active read banks based on size and address offset assign meta_buf_size_enable[i] = i*NumBytesPerBank + NumBytesPerBank > (meta_buf.addr % DataWidth/8) && i*NumBytesPerBank < ((meta_buf.addr % DataWidth/8) + 1< Date: Tue, 8 Aug 2023 13:56:04 +0200 Subject: [PATCH 141/145] Bump common_cells version --- Bender.yml | 2 +- src/axi_to_detailed_mem.sv | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Bender.yml b/Bender.yml index ab90f8c83..5435d12af 100644 --- a/Bender.yml +++ b/Bender.yml @@ -19,7 +19,7 @@ package: - "Florian Zaruba " dependencies: - common_cells: { git: "https://github.com/pulp-platform/common_cells.git", rev: "99fcffdc3f8a5f3f590f6c9f687f65a2e9f2e0fe" } # updated mem_to_banks_detailed + common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.31.0 } common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.3 } tech_cells_generic: { git: "https://github.com/pulp-platform/tech_cells_generic.git", version: 0.2.2 } diff --git a/src/axi_to_detailed_mem.sv b/src/axi_to_detailed_mem.sv index 9da63c013..8f5c11d21 100644 --- a/src/axi_to_detailed_mem.sv +++ b/src/axi_to_detailed_mem.sv @@ -459,14 +459,14 @@ module axi_to_detailed_mem #( // Split single memory request to desired number of banks. mem_to_banks_detailed #( - .AddrWidth ( AddrWidth ), - .DataWidth ( DataWidth ), - .ErspWidth ( 2 ), - .NumBanks ( NumBanks ), - .HideStrb ( HideStrb ), - .MaxTrans ( BufDepth ), - .FifoDepth ( OutFifoDepth ), - .AtopWidth ( $bits(tmp_atop_t) ) + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .RUserWidth ( 2 ), + .NumBanks ( NumBanks ), + .HideStrb ( HideStrb ), + .MaxTrans ( BufDepth ), + .FifoDepth ( OutFifoDepth ), + .WUserWidth ( $bits(tmp_atop_t) ) ) i_mem_to_banks ( .clk_i, .rst_ni, @@ -475,21 +475,21 @@ module axi_to_detailed_mem #( .addr_i ( mem_req.addr ), .wdata_i ( mem_req.wdata ), .strb_i ( mem_req.strb ), - .atop_i ( mem_req_atop ), + .wuser_i ( mem_req_atop ), .we_i ( mem_req.we ), .rvalid_o ( mem_rvalid ), .rdata_o ( mem_rdata.data ), - .ersp_o ( tmp_ersp ), + .ruser_o ( tmp_ersp ), .bank_req_o ( mem_req_o ), .bank_gnt_i ( mem_gnt_i ), .bank_addr_o ( mem_addr_o ), .bank_wdata_o ( mem_wdata_o ), .bank_strb_o ( mem_strb_o ), - .bank_atop_o ( banked_req_atop ), + .bank_wuser_o ( banked_req_atop ), .bank_we_o ( mem_we_o ), .bank_rvalid_i ( mem_rvalid_i ), .bank_rdata_i ( mem_rdata_i ), - .bank_ersp_i ( bank_ersp ) + .bank_ruser_i ( bank_ersp ) ); // Join memory read data and meta data stream. @@ -528,9 +528,9 @@ module axi_to_detailed_mem #( for (genvar i = 0; i < NumBanks; i++) begin // Set active write banks based on strobe assign meta_buf_bank_strb[i] = |meta_buf.strb[i*NumBytesPerBank +: NumBytesPerBank]; - // Set active read banks based on size and address offset - assign meta_buf_size_enable[i] = i*NumBytesPerBank + NumBytesPerBank > (meta_buf.addr % DataWidth/8) && - i*NumBytesPerBank < ((meta_buf.addr % DataWidth/8) + 1< addr) && (bank.start < addr+size) + assign meta_buf_size_enable[i] = ((i*NumBytesPerBank + NumBytesPerBank) > (meta_buf.addr % DataWidth/8)) && + ((i*NumBytesPerBank) < ((meta_buf.addr % DataWidth/8) + 1< Date: Tue, 8 Aug 2023 14:51:27 +0200 Subject: [PATCH 142/145] axi_to_detailed_mem: Ensure CI run with memora --- .ci/Memora.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/Memora.yml b/.ci/Memora.yml index deda96773..c8fd967de 100644 --- a/.ci/Memora.yml +++ b/.ci/Memora.yml @@ -294,6 +294,7 @@ artifacts: - src/axi_intf.sv - src/axi_test.sv - src/axi_demux.sv + - src/axi_to_detailed_mem.sv - src/axi_to_mem.sv - src/axi_to_mem_banked.sv - test/tb_axi_to_mem_banked.sv From 25c1b9197d1e7f9c12fa8debb711e28b4017bee6 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 8 Aug 2023 14:54:42 +0200 Subject: [PATCH 143/145] Update Changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 328a8e818..d3b0caf4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased +### Fixed +- `axi_to_detailed_mem`: Ensure proper propagation or `err` and `exokay` signals. + ## 0.39.0 - 2023-07-20 ### Added From cab37ddf45b98ce80cbd4533ed06574ed4b1f201 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Wed, 16 Aug 2023 09:45:34 +0200 Subject: [PATCH 144/145] Bump common_cells for proper parameter propagation --- Bender.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bender.yml b/Bender.yml index 5435d12af..d5cc6d008 100644 --- a/Bender.yml +++ b/Bender.yml @@ -19,7 +19,7 @@ package: - "Florian Zaruba " dependencies: - common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.31.0 } + common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.31.1 } common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.3 } tech_cells_generic: { git: "https://github.com/pulp-platform/tech_cells_generic.git", version: 0.2.2 } From fccffb5953ec8564218ba05e20adbedec845e014 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 5 Sep 2023 15:41:34 +0200 Subject: [PATCH 145/145] Release v0.39.1 --- CHANGELOG.md | 11 +++++++++++ VERSION | 2 +- axi.core | 4 ++-- ips_list.yml | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3b0caf4b..33d9a0783 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased +## 0.39.1 - 2023-09-05 + +### Added +- `axi_cdc`: Add `SyncStages` parameter. +- `axi_to_mem_interleaved`: Add interface variant. +- `axi_burst_splitter`: Expose `id_queue`'s `FULL_BW` parameter. +- `axi_chan_compare`: Add parameter to allow reordered transactions. +- Add `AXI_HIGHLIGHT` macro to highlight AXI signals. +- Add flat port instantiation macros. + ### Fixed +- `axi_test`: Avoid false negatives for misaligned reads in `axi_scoreboard`. - `axi_to_detailed_mem`: Ensure proper propagation or `err` and `exokay` signals. ## 0.39.0 - 2023-07-20 diff --git a/VERSION b/VERSION index 4ef2eb086..d2e2400ee 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.39.0 +0.39.1 diff --git a/axi.core b/axi.core index d3bf2f735..a44922d2c 100644 --- a/axi.core +++ b/axi.core @@ -1,6 +1,6 @@ CAPI=2: -name : pulp-platform.org::axi:0.39.0 +name : pulp-platform.org::axi:0.39.1 filesets: rtl: @@ -74,7 +74,7 @@ filesets: - src/axi_xp.sv file_type : systemVerilogSource depend : - - ">=pulp-platform.org::common_cells:1.27.0" + - ">=pulp-platform.org::common_cells:1.31.1" benchs: files: - test/tb_axi_dw_pkg.sv diff --git a/ips_list.yml b/ips_list.yml index da1a0adc6..7d797dea5 100644 --- a/ips_list.yml +++ b/ips_list.yml @@ -1,5 +1,5 @@ common_cells: - commit: v1.27.0 + commit: v1.31.1 group: pulp-platform common_verification: