From ded97ffe6917fb30674eb8a640dc68c2d64b38ea Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 10 Nov 2022 19:24:16 +0100 Subject: [PATCH 1/2] 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 2/2] 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;