diff --git a/.ci/Memora.yml b/.ci/Memora.yml index a4504d448..9bcd20e1d 100644 --- a/.ci/Memora.yml +++ b/.ci/Memora.yml @@ -62,6 +62,21 @@ artifacts: outputs: - build/axi_cdc-%.tested + axi_cdc_isolatable-%: + inputs: + - Bender.yml + - include + - scripts/run_vsim.sh + - src/axi_pkg.sv + - src/axi_intf.sv + - src/axi_test.sv + - src/axi_cdc_dst_clearable.sv + - src/axi_cdc_src_clearable.sv + - src/axi_cdc_isolatable.sv + - test/tb_axi_cdc_isolatable.sv + outputs: + - build/axi_cdc_isolatable-%.tested + axi_delayer-%: inputs: - Bender.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7419b1af5..c7091fdc2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -99,6 +99,11 @@ axi_cdc: variables: TEST_MODULE: axi_cdc +axi_cdc_isolatable: + <<: *run_vsim + variables: + TEST_MODULE: axi_cdc_isolatable + axi_delayer: <<: *run_vsim variables: diff --git a/Bender.yml b/Bender.yml index 2444f928f..abbf67309 100644 --- a/Bender.yml +++ b/Bender.yml @@ -40,7 +40,9 @@ sources: - src/axi_burst_unwrap.sv - src/axi_bus_compare.sv - src/axi_cdc_dst.sv + - src/axi_cdc_dst_clearable.sv - src/axi_cdc_src.sv + - src/axi_cdc_src_clearable.sv - src/axi_cut.sv - src/axi_delayer.sv - src/axi_demux_simple.sv @@ -73,6 +75,7 @@ sources: # Level 3 - src/axi_burst_splitter.sv - src/axi_cdc.sv + - src/axi_cdc_isolatable.sv - src/axi_demux.sv - src/axi_err_slv.sv - src/axi_dw_converter.sv @@ -117,6 +120,7 @@ sources: - test/tb_axi_atop_filter.sv - test/tb_axi_bus_compare.sv - test/tb_axi_cdc.sv + - test/tb_axi_cdc_isolatable.sv - test/tb_axi_delayer.sv - test/tb_axi_dw_downsizer.sv - test/tb_axi_dw_upsizer.sv diff --git a/Makefile b/Makefile index 98c1d3e00..cf1e9f026 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,9 @@ endif TBS ?= axi_addr_test \ axi_atop_filter \ - axi_cdc axi_delayer \ + axi_cdc \ + axi_cdc_isolatable \ + axi_delayer \ axi_dw_downsizer \ axi_dw_upsizer \ axi_fifo \ diff --git a/README.md b/README.md index 6fb7399dd..9141c6768 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_burst_splitter_gran`](src/axi_burst_splitter_gran.sv) | Split AXI4 burst transfers into transactions of runtime-configurable granularity. | | | [`axi_burst_unwrap`](src/axi_burst_unwrap.sv) | Convert AXI4 wrapping burst transfers into up to two incremental bursts. | | | [`axi_cdc`](src/axi_cdc.sv) | AXI clock domain crossing based on a Gray FIFO implementation. | | +| [`axi_cdc_isolatable`](src/axi_cdc_isolatable.sv) | Isolatable AXI clock domain crossing based on a Gray FIFO implementation allowing one-sided resets. | | | [`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) | diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh index aaf8c3f22..744c2869e 100755 --- a/scripts/run_vsim.sh +++ b/scripts/run_vsim.sh @@ -45,7 +45,7 @@ exec_test() { call_vsim tb_axi_atop_filter -gTB_N_TXNS=1000 -gTB_AXI_MAX_WRITE_TXNS=$MAX_TXNS done ;; - axi_cdc|axi_delayer) + axi_cdc|axi_cdc_isolatable|axi_delayer) call_vsim tb_$1 ;; axi_dw_downsizer) diff --git a/src/axi_cdc_dst_clearable.sv b/src/axi_cdc_dst_clearable.sv new file mode 100644 index 000000000..5450583ca --- /dev/null +++ b/src/axi_cdc_dst_clearable.sv @@ -0,0 +1,307 @@ +// 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: +// - Luca Valente +// - Andreas Kurth + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +/// Destination-clock-domain half of the clearable AXI CDC crossing. +/// +/// For each of the five AXI channels, this module instantiates the source or destination half of +/// a clearable CDC FIFO. IMPORTANT: For each AXI channel, you MUST properly constrain three paths +/// through the FIFO; see the header of `cdc_fifo_gray_clearable` for instructions. +module axi_cdc_dst_clearable #( + /// 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, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + // asynchronous slave port + input aw_chan_t [2**LogDepth-1:0] async_data_slave_aw_data_i, + input logic [LogDepth:0] async_data_slave_aw_wptr_i, + output logic [LogDepth:0] async_data_slave_aw_rptr_o, + input w_chan_t [2**LogDepth-1:0] async_data_slave_w_data_i, + input logic [LogDepth:0] async_data_slave_w_wptr_i, + output logic [LogDepth:0] async_data_slave_w_rptr_o, + output b_chan_t [2**LogDepth-1:0] async_data_slave_b_data_o, + output logic [LogDepth:0] async_data_slave_b_wptr_o, + input logic [LogDepth:0] async_data_slave_b_rptr_i, + input ar_chan_t [2**LogDepth-1:0] async_data_slave_ar_data_i, + input logic [LogDepth:0] async_data_slave_ar_wptr_i, + output logic [LogDepth:0] async_data_slave_ar_rptr_o, + output r_chan_t [2**LogDepth-1:0] async_data_slave_r_data_o, + output logic [LogDepth:0] async_data_slave_r_wptr_o, + input logic [LogDepth:0] async_data_slave_r_rptr_i, + // synchronous master port - clocked by `dst_clk_i` + input logic dst_clk_i, + input logic dst_rst_ni, + input logic dst_clear_i, + output axi_req_t dst_req_o, + input axi_resp_t dst_resp_i +); + + cdc_fifo_gray_dst_clearable #( +`ifdef QUESTA + // Workaround for a bug in Questa: Pass flat logic vector instead of struct to type parameter. + .T ( logic [$bits(aw_chan_t)-1:0] ), +`else + // Other tools, such as VCS, have problems with type parameters constructed through `$bits()`. + .T ( aw_chan_t ), +`endif + .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 ), + .async_rptr_o ( async_data_slave_aw_rptr_o ), + .dst_clk_i, + .dst_rst_ni, + .dst_clear_i, + .dst_data_o ( dst_req_o.aw ), + .dst_valid_o ( dst_req_o.aw_valid ), + .dst_ready_i ( dst_resp_i.aw_ready ) + ); + + cdc_fifo_gray_dst_clearable #( +`ifdef QUESTA + .T ( logic [$bits(w_chan_t)-1:0] ), +`else + .T ( w_chan_t ), +`endif + .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 ), + .async_rptr_o ( async_data_slave_w_rptr_o ), + .dst_clk_i, + .dst_rst_ni, + .dst_clear_i, + .dst_data_o ( dst_req_o.w ), + .dst_valid_o ( dst_req_o.w_valid ), + .dst_ready_i ( dst_resp_i.w_ready ) + ); + + cdc_fifo_gray_src_clearable #( +`ifdef QUESTA + .T ( logic [$bits(b_chan_t)-1:0] ), +`else + .T ( b_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) + ) i_cdc_fifo_gray_src_b ( + .src_clk_i ( dst_clk_i ), + .src_rst_ni ( dst_rst_ni ), + .src_clear_i ( dst_clear_i ), + .src_data_i ( dst_resp_i.b ), + .src_valid_i ( dst_resp_i.b_valid ), + .src_ready_o ( dst_req_o.b_ready ), + .async_data_o ( async_data_slave_b_data_o ), + .async_wptr_o ( async_data_slave_b_wptr_o ), + .async_rptr_i ( async_data_slave_b_rptr_i ) + ); + + cdc_fifo_gray_dst_clearable #( +`ifdef QUESTA + .T ( logic [$bits(ar_chan_t)-1:0] ), +`else + .T ( ar_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) + ) i_cdc_fifo_gray_dst_ar ( + .dst_clk_i, + .dst_rst_ni, + .dst_clear_i, + .dst_data_o ( dst_req_o.ar ), + .dst_valid_o ( dst_req_o.ar_valid ), + .dst_ready_i ( dst_resp_i.ar_ready ), + .async_data_i ( async_data_slave_ar_data_i ), + .async_wptr_i ( async_data_slave_ar_wptr_i ), + .async_rptr_o ( async_data_slave_ar_rptr_o ) + ); + + cdc_fifo_gray_src_clearable #( +`ifdef QUESTA + .T ( logic [$bits(r_chan_t)-1:0] ), +`else + .T ( r_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) + ) i_cdc_fifo_gray_src_r ( + .src_clk_i ( dst_clk_i ), + .src_rst_ni ( dst_rst_ni ), + .src_clear_i ( dst_clear_i ), + .src_data_i ( dst_resp_i.r ), + .src_valid_i ( dst_resp_i.r_valid ), + .src_ready_o ( dst_req_o.r_ready ), + .async_data_o ( async_data_slave_r_data_o ), + .async_wptr_o ( async_data_slave_r_wptr_o ), + .async_rptr_i ( async_data_slave_r_rptr_i ) + ); + +endmodule + + +module axi_cdc_dst_clearable_intf #( + parameter int unsigned AXI_ID_WIDTH = 0, + parameter int unsigned AXI_ADDR_WIDTH = 0, + 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, + /// 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, + // synchronous master port - clocked by `dst_clk_i` + input logic dst_clk_i, + input logic dst_rst_ni, + input logic dst_clear_i, + AXI_BUS.Master dst +); + + 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(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 dst_req; + resp_t dst_resp; + + axi_cdc_dst_clearable #( + .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 ), + .async_data_slave_aw_rptr_o ( src.aw_rptr ), + .async_data_slave_w_data_i ( src.w_data ), + .async_data_slave_w_wptr_i ( src.w_wptr ), + .async_data_slave_w_rptr_o ( src.w_rptr ), + .async_data_slave_b_data_o ( src.b_data ), + .async_data_slave_b_wptr_o ( src.b_wptr ), + .async_data_slave_b_rptr_i ( src.b_rptr ), + .async_data_slave_ar_data_i ( src.ar_data ), + .async_data_slave_ar_wptr_i ( src.ar_wptr ), + .async_data_slave_ar_rptr_o ( src.ar_rptr ), + .async_data_slave_r_data_o ( src.r_data ), + .async_data_slave_r_wptr_o ( src.r_wptr ), + .async_data_slave_r_rptr_i ( src.r_rptr ), + .dst_clk_i, + .dst_rst_ni, + .dst_clear_i, + .dst_req_o ( dst_req ), + .dst_resp_i ( dst_resp ) + ); + + `AXI_ASSIGN_FROM_REQ(dst, dst_req) + `AXI_ASSIGN_TO_RESP(dst_resp, dst) + +endmodule + + +module axi_lite_cdc_dst_clearable_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, + /// 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, + // synchronous master port - clocked by `dst_clk_i` + input logic dst_clk_i, + input logic dst_rst_ni, + input logic dst_clear_i, + AXI_LITE.Master dst +); + + 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; + `AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t) + `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) + `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) + `AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + + req_t dst_req; + resp_t dst_resp; + + axi_cdc_dst_clearable #( + .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 ), + .async_data_slave_aw_rptr_o ( src.aw_rptr ), + .async_data_slave_w_data_i ( src.w_data ), + .async_data_slave_w_wptr_i ( src.w_wptr ), + .async_data_slave_w_rptr_o ( src.w_rptr ), + .async_data_slave_b_data_o ( src.b_data ), + .async_data_slave_b_wptr_o ( src.b_wptr ), + .async_data_slave_b_rptr_i ( src.b_rptr ), + .async_data_slave_ar_data_i ( src.ar_data ), + .async_data_slave_ar_wptr_i ( src.ar_wptr ), + .async_data_slave_ar_rptr_o ( src.ar_rptr ), + .async_data_slave_r_data_o ( src.r_data ), + .async_data_slave_r_wptr_o ( src.r_wptr ), + .async_data_slave_r_rptr_i ( src.r_rptr ), + .dst_clk_i, + .dst_rst_ni, + .dst_clear_i, + .dst_req_o ( dst_req ), + .dst_resp_i ( dst_resp ) + ); + + `AXI_LITE_ASSIGN_FROM_REQ(dst, dst_req) + `AXI_LITE_ASSIGN_TO_RESP(dst_resp, dst) + +endmodule diff --git a/src/axi_cdc_isolatable.sv b/src/axi_cdc_isolatable.sv new file mode 100644 index 000000000..76ed28368 --- /dev/null +++ b/src/axi_cdc_isolatable.sv @@ -0,0 +1,546 @@ +// 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: +// - Andreas Kurth +// - Luca Valente +// - Wolfgang Roenninger +// - Luca Rufer +// - Moritz Scherer + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +/// An isolatable clock domain crossing on an AXI interface. This isolatable variant of the AXI CDC +/// allows one-sided power-down or reset without having to reset the other side of the CDC. +/// +/// The module consists of an AXI isolate on the src side and a clearable CDC. When powering down or +/// resetting the dst side, the src side must first assert the `src_isolate_i` signal and wait until +/// the `src_isolated_o` signal is asserted. Assertion of the `src_isolated_o` signal indicates that +/// the AXI interface was isolated with all outstanding transactions completed and the CDC FIFO +/// pointers were cleared. +/// The `src_isolate_i` signal must remain asserted during the power-down and/or reset of the dst +/// side and may only be retracted after the reset on the dst side was completed. +/// After de-asserting the `src_isolate_i` signal, the user shall way for de-assertion of the +/// `src_isolated_o` signal before sending new AXI requests to ensure the AXI interface is no longer +/// isolated. +/// `src_isolate_i` and `src_isolated_o` form a 4-phase handshake signal, which may not be violated. +/// +/// When powering down or resetting the src side, the dst side must first assert the `dst_isolate_i` +/// signal and wait until the `dst_isolated_o` signal is asserted. Assertion of the `dst_isolated_o` +/// signal indicates that all outstanding AXI transactions were completed and the CDC FIFO pointers +/// were cleared. +/// The `dst_isolate_i` signal is required to remain asserted until the power-down or the reset +/// of the src side unless the user can guarantee that no new AXI requests are issued. +/// After de-asserting the `dst_isolate_i` signal, the de-assertion of `dst_isolated_o` indicates +/// that the AXI interface on the src side is no longer isolated. +/// `dst_isolate_i` and `dst_isolated_o` form a 4-phase handshake signal, which may not be violated. +/// +/// For each of the five AXI channels, this module instantiates a clearable CDC FIFO, whose push and +/// pop ports are in separate clock domains. IMPORTANT: For each AXI channel, you MUST properly +/// constrain three paths through the FIFO; see the header of `cdc_fifo_gray_clearable` for +/// instructions. + +module axi_cdc_isolatable + import axi_pkg::*; +#( + /// Address width of all AXI4+ATOP ports + parameter int signed AxiAddrWidth = 32'd0, + /// Data width of all AXI4+ATOP ports + parameter int signed AxiDataWidth = 32'd0, + /// ID width of all AXI4+ATOP ports + parameter int signed AxiIdWidth = 32'd0, + /// User signal width of all AXI4+ATOP ports + parameter int signed AxiUserWidth = 32'd0, + /// Support atomic operations (ATOPs) + parameter bit AtopSupport = 1'b1, + /// Request struct type of all AXI4+ATOP ports + parameter type axi_req_t = logic, + /// Response struct type of all AXI4+ATOP ports + parameter type axi_resp_t = logic, + /// 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 = 3, + /// Gracefully terminate all incoming transactions in case of isolation by returning proper error + /// responses. + parameter bit TerminateTransaction = 1'b0 +) ( + // slave side - clocked by `src_clk_i` + input logic src_clk_i, + input logic src_rst_ni, + input logic src_isolate_i, + output logic src_isolated_o, + input axi_req_t src_req_i, + output axi_resp_t src_resp_o, + // master side - clocked by `dst_clk_i` + input logic dst_clk_i, + input logic dst_rst_ni, + input logic dst_isolate_i, + output logic dst_isolated_o, + output axi_req_t dst_req_o, + input axi_resp_t dst_resp_i +); + + 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(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) + + //--------- + // Isolate + //--------- + + logic axi_src_isolate; + logic axi_src_isolated; + + logic cdc_src_isolate, dst_isolate_sync; + + axi_req_t src_req_iso; + axi_resp_t src_resp_iso; + + // Isolation is triggered from the input signal or by request of the CDC + assign axi_src_isolate = src_isolate_i | cdc_src_isolate | dst_isolate_sync; + + axi_isolate #( + .NumPending (2 ** LogDepth), + .TerminateTransaction(TerminateTransaction), + .AtopSupport (AtopSupport), + .AxiAddrWidth (AxiAddrWidth), + .AxiDataWidth (AxiDataWidth), + .AxiIdWidth (AxiIdWidth), + .AxiUserWidth (AxiUserWidth), + .axi_req_t (axi_req_t), + .axi_resp_t (axi_resp_t) + ) i_src_iso ( + .clk_i (src_clk_i), + .rst_ni (src_rst_ni), + .slv_req_i (src_req_i), + .slv_resp_o(src_resp_o), + .mst_req_o (src_req_iso), + .mst_resp_i(src_resp_iso), + .isolate_i (axi_src_isolate), + .isolated_o(axi_src_isolated) + ); + + //-------------------------------- + // CDC Clear and Reset Controller + //-------------------------------- + + logic src_clear, dst_clear; + + logic src_clear_req, src_clear_ack_q; + logic dst_clear_req, dst_clear_ack_q; + logic dst_isolate_req, dst_isolate_ack_q; + + // Synchronize the clear and reset signaling in both directions + cdc_reset_ctrlr #( + .SYNC_STAGES (SyncStages - 1), + .CLEAR_ON_ASYNC_RESET(1'b0) + ) i_cdc_reset_ctrlr ( + .a_clk_i (src_clk_i), + .a_rst_ni (src_rst_ni), + .a_clear_i (src_clear), + .a_clear_o (src_clear_req), + .a_clear_ack_i (src_clear_ack_q), + .a_isolate_o (cdc_src_isolate), + .a_isolate_ack_i(axi_src_isolated), + .b_clk_i (dst_clk_i), + .b_rst_ni (dst_rst_ni), + .b_clear_i (dst_clear), + .b_clear_o (dst_clear_req), + .b_clear_ack_i (dst_clear_ack_q), + .b_isolate_o (dst_isolate_req), + .b_isolate_ack_i(dst_isolate_ack_q) + ); + + // The CDC can be cleared in one cycle + always_ff @(posedge src_clk_i, negedge src_rst_ni) begin + if (!src_rst_ni) begin + src_clear_ack_q <= 1'b0; + end else begin + src_clear_ack_q <= src_clear_req; + end + end + + always_ff @(posedge dst_clk_i, negedge dst_rst_ni) begin + if (!dst_rst_ni) begin + dst_clear_ack_q <= 1'b0; + end else begin + dst_clear_ack_q <= dst_clear_req; + end + end + + // Isolation is controlled on the src side, dst side is irrelevant + always_ff @(posedge dst_clk_i, negedge dst_rst_ni) begin + if (!dst_rst_ni) begin + dst_isolate_ack_q <= 1'b0; + end else begin + dst_isolate_ack_q <= dst_isolate_req; + end + end + + //-------------------------------- + // Isolate / Clear handshakes + //-------------------------------- + + logic src_isolate_in_q; + logic src_isolated_d, src_isolated_q; + + logic dst_isolate_in_q; + logic dst_isolated_d, dst_isolated_q; + + logic src_isolated_sync_n; + + // Initiate the CDC clear on the rising edge of the isolate inputs + assign src_clear = src_isolate_i && !src_isolate_in_q; + assign dst_clear = dst_isolate_i && !dst_isolate_in_q; + + always_comb begin + src_isolated_d = src_isolated_q; + + if (src_isolate_i) begin + // Isolation (+ CDC clear) complete when iso isolated and CDC clear complete + if (axi_src_isolated && !cdc_src_isolate) begin + src_isolated_d = 1'b1; + end + end else begin + // Isolation revoked when no isolation and no CDC clear ongoing + if (!axi_src_isolated && !cdc_src_isolate) begin + src_isolated_d = 1'b0; + end + end + end + + assign src_isolated_o = src_isolated_q; + + always_comb begin + dst_isolated_d = dst_isolated_q; + + if (dst_isolate_i) begin + // Dst isolation complete when crc reset controller has finished (falling edge of dst isolate) + if (dst_isolate_ack_q && !dst_isolate_req) begin + dst_isolated_d = 1'b1; + end + end else begin + // Dst isolation released once iso on the source side is no longer isolating + if (src_isolated_sync_n) begin + dst_isolated_d = 1'b0; + end + end + end + + assign dst_isolated_o = dst_isolated_q; + + always_ff @(posedge src_clk_i, negedge src_rst_ni) begin + if (!src_rst_ni) begin + src_isolate_in_q <= 1'b0; + src_isolated_q <= 1'b0; + end else begin + src_isolate_in_q <= src_isolate_i; + src_isolated_q <= src_isolated_d; + end + end + + always_ff @(posedge dst_clk_i, negedge dst_rst_ni) begin + if (!dst_rst_ni) begin + dst_isolate_in_q <= 1'b0; + dst_isolated_q <= 1'b0; + end else begin + dst_isolate_in_q <= dst_isolate_i; + dst_isolated_q <= dst_isolated_d; + end + end + + //-------------------------------- + // Destination clear synchronizer + //-------------------------------- + + sync #( + .STAGES(SyncStages) + ) i_dst_isolate_sync ( + .clk_i (src_clk_i), + .rst_ni (src_rst_ni), + .serial_i(dst_isolate_i), + .serial_o(dst_isolate_sync) + ); + + sync #( + .STAGES(SyncStages) + ) i_dst_isolated_sync ( + .clk_i (dst_clk_i), + .rst_ni (dst_rst_ni), + .serial_i(!axi_src_isolated), + .serial_o(src_isolated_sync_n) + ); + + //----- + // CDC + //----- + + aw_chan_t [2**LogDepth-1:0] async_data_aw_data; + w_chan_t [2**LogDepth-1:0] async_data_w_data; + b_chan_t [2**LogDepth-1:0] async_data_b_data; + ar_chan_t [2**LogDepth-1:0] async_data_ar_data; + r_chan_t [2**LogDepth-1:0] async_data_r_data; + logic [LogDepth:0] + async_data_aw_wptr, + async_data_aw_rptr, + async_data_w_wptr, + async_data_w_rptr, + async_data_b_wptr, + async_data_b_rptr, + async_data_ar_wptr, + async_data_ar_rptr, + async_data_r_wptr, + async_data_r_rptr; + + axi_cdc_src_clearable #( + .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), + .LogDepth (LogDepth), + .SyncStages(SyncStages) + ) i_axi_cdc_src ( + .src_clk_i, + .src_rst_ni, + .src_clear_i (src_clear_req), + .src_req_i (src_req_iso), + .src_resp_o (src_resp_iso), + (* async *) .async_data_master_aw_data_o(async_data_aw_data), + (* async *) .async_data_master_aw_wptr_o(async_data_aw_wptr), + (* async *) .async_data_master_aw_rptr_i(async_data_aw_rptr), + (* async *) .async_data_master_w_data_o (async_data_w_data), + (* async *) .async_data_master_w_wptr_o (async_data_w_wptr), + (* async *) .async_data_master_w_rptr_i (async_data_w_rptr), + (* async *) .async_data_master_b_data_i (async_data_b_data), + (* async *) .async_data_master_b_wptr_i (async_data_b_wptr), + (* async *) .async_data_master_b_rptr_o (async_data_b_rptr), + (* async *) .async_data_master_ar_data_o(async_data_ar_data), + (* async *) .async_data_master_ar_wptr_o(async_data_ar_wptr), + (* async *) .async_data_master_ar_rptr_i(async_data_ar_rptr), + (* async *) .async_data_master_r_data_i (async_data_r_data), + (* async *) .async_data_master_r_wptr_i (async_data_r_wptr), + (* async *) .async_data_master_r_rptr_o (async_data_r_rptr) + ); + + axi_cdc_dst_clearable #( + .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), + .LogDepth (LogDepth), + .SyncStages(SyncStages) + ) i_axi_cdc_dst ( + .dst_clk_i, + .dst_rst_ni, + .dst_clear_i (dst_clear_req), + .dst_req_o, + .dst_resp_i, + (* async *) .async_data_slave_aw_wptr_i(async_data_aw_wptr), + (* async *) .async_data_slave_aw_rptr_o(async_data_aw_rptr), + (* async *) .async_data_slave_aw_data_i(async_data_aw_data), + (* async *) .async_data_slave_w_wptr_i (async_data_w_wptr), + (* async *) .async_data_slave_w_rptr_o (async_data_w_rptr), + (* async *) .async_data_slave_w_data_i (async_data_w_data), + (* async *) .async_data_slave_b_wptr_o (async_data_b_wptr), + (* async *) .async_data_slave_b_rptr_i (async_data_b_rptr), + (* async *) .async_data_slave_b_data_o (async_data_b_data), + (* async *) .async_data_slave_ar_wptr_i(async_data_ar_wptr), + (* async *) .async_data_slave_ar_rptr_o(async_data_ar_rptr), + (* async *) .async_data_slave_ar_data_i(async_data_ar_data), + (* async *) .async_data_slave_r_wptr_o (async_data_r_wptr), + (* async *) .async_data_slave_r_rptr_i (async_data_r_rptr), + (* async *) .async_data_slave_r_data_o (async_data_r_data) + ); + + // pragma translate_off +`ifndef SYNTHESIS + assert property (@(posedge src_clk_i) + disable iff (~src_rst_ni) (src_isolate_i && !src_isolated_o) |=> src_isolate_i) + else $error(1, "src isolate signal was de-asserted before isolation was complete!"); + assert property (@(posedge src_clk_i) + disable iff (~src_rst_ni) (!src_isolate_i && src_isolated_o) |=> !src_isolate_i) + else $error(1, "src isolate signal was asserted before previous isolation was complete!"); + assert property (@(posedge dst_clk_i) + disable iff (~dst_rst_ni) (dst_isolate_i && !dst_isolated_o) |=> dst_isolate_i) + else $error(1, "dst isolate signal was de-asserted before isolation was complete!"); + assert property (@(posedge dst_clk_i) + disable iff (~dst_rst_ni) (!dst_isolate_i && dst_isolated_o) |=> !dst_isolate_i) + else $error(1, "dst isolate signal was asserted before previous isolation was complete!"); +`endif + // pragma translate_on + +endmodule + +// interface wrapper +module axi_cdc_isolatable_intf #( + parameter int unsigned AXI_ID_WIDTH = 0, + parameter int unsigned AXI_ADDR_WIDTH = 0, + parameter int unsigned AXI_DATA_WIDTH = 0, + parameter int unsigned AXI_USER_WIDTH = 0, + parameter bit ATOP_SUPPORT = 1'b1, + /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2, + /// Gracefully terminate all incoming transactions in case of isolation by returning proper error + /// responses. + parameter bit TERMINATE_TRANSACTION = 1'b0 +) ( + // slave side - clocked by `src_clk_i` + input logic src_clk_i, + input logic src_rst_ni, + input logic src_isolate_i, + output logic src_isolated_o, + AXI_BUS.Slave src, + // master side - clocked by `dst_clk_i` + input logic dst_clk_i, + input logic dst_rst_ni, + input logic dst_isolate_i, + output logic dst_isolated_o, + AXI_BUS.Master dst +); + + 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(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 src_req, dst_req; + resp_t src_resp, dst_resp; + + `AXI_ASSIGN_TO_REQ(src_req, src) + `AXI_ASSIGN_FROM_RESP(src, src_resp) + + `AXI_ASSIGN_FROM_REQ(dst, dst_req) + `AXI_ASSIGN_TO_RESP(dst_resp, dst) + + axi_cdc_isolatable #( + .AxiAddrWidth (AXI_ADDR_WIDTH), + .AxiDataWidth (AXI_DATA_WIDTH), + .AxiIdWidth (AXI_ID_WIDTH), + .AxiUserWidth (AXI_USER_WIDTH), + .AtopSupport (ATOP_SUPPORT), + .axi_req_t (req_t), + .axi_resp_t (resp_t), + .LogDepth (LOG_DEPTH), + .SyncStages (SYNC_STAGES), + .TerminateTransaction(TERMINATE_TRANSACTION) + ) i_axi_cdc ( + .src_clk_i, + .src_rst_ni, + .src_isolate_i, + .src_isolated_o, + .src_req_i (src_req), + .src_resp_o(src_resp), + .dst_clk_i, + .dst_rst_ni, + .dst_isolate_i, + .dst_isolated_o, + .dst_req_o (dst_req), + .dst_resp_i(dst_resp) + ); + +endmodule + +module axi_lite_cdc_isolatable_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, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2, + /// Gracefully terminate all incoming transactions in case of isolation by returning proper error + /// responses. + parameter bit TERMINATE_TRANSACTION = 1'b0 +) ( + // slave side - clocked by `src_clk_i` + input logic src_clk_i, + input logic src_rst_ni, + input logic src_isolate_i, + output logic src_isolated_o, + AXI_LITE.Slave src, + // master side - clocked by `dst_clk_i` + input logic dst_clk_i, + input logic dst_rst_ni, + input logic dst_isolate_i, + output logic dst_isolated_o, + AXI_LITE.Master dst +); + + 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; + `AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t) + `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) + `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) + `AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + + req_t src_req, dst_req; + resp_t src_resp, dst_resp; + + `AXI_LITE_ASSIGN_TO_REQ(src_req, src) + `AXI_LITE_ASSIGN_FROM_RESP(src, src_resp) + + `AXI_LITE_ASSIGN_FROM_REQ(dst, dst_req) + `AXI_LITE_ASSIGN_TO_RESP(dst_resp, dst) + + axi_cdc_isolatable #( + .AxiAddrWidth (AXI_ADDR_WIDTH), + .AxiDataWidth (AXI_DATA_WIDTH), + .AtopSupport (1'b0), + .axi_req_t (req_t), + .axi_resp_t (resp_t), + .LogDepth (LOG_DEPTH), + .SyncStages (SYNC_STAGES), + .TerminateTransaction(TERMINATE_TRANSACTION) + ) i_axi_cdc ( + .src_clk_i, + .src_rst_ni, + .src_isolate_i, + .src_isolated_o, + .src_req_i (src_req), + .src_resp_o(src_resp), + .dst_clk_i, + .dst_rst_ni, + .dst_isolate_i, + .dst_isolated_o, + .dst_req_o (dst_req), + .dst_resp_i(dst_resp) + ); + +endmodule diff --git a/src/axi_cdc_src_clearable.sv b/src/axi_cdc_src_clearable.sv new file mode 100644 index 000000000..21a9e6cd8 --- /dev/null +++ b/src/axi_cdc_src_clearable.sv @@ -0,0 +1,306 @@ +// 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: +// - Luca Valente +// - Andreas Kurth + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +/// Source-clock-domain half of the clearable AXI CDC crossing. +/// +/// For each of the five AXI channels, this module instantiates the source or destination half of +/// a clearable CDC FIFO. IMPORTANT: For each AXI channel, you MUST properly constrain three paths +/// through the FIFO; see the header of `cdc_fifo_gray_clearable` for instructions. +module axi_cdc_src_clearable #( + /// 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, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + // synchronous slave port - clocked by `src_clk_i` + input logic src_clk_i, + input logic src_rst_ni, + input logic src_clear_i, + input axi_req_t src_req_i, + output axi_resp_t src_resp_o, + // asynchronous master port + output aw_chan_t [2**LogDepth-1:0] async_data_master_aw_data_o, + output logic [LogDepth:0] async_data_master_aw_wptr_o, + input logic [LogDepth:0] async_data_master_aw_rptr_i, + output w_chan_t [2**LogDepth-1:0] async_data_master_w_data_o, + output logic [LogDepth:0] async_data_master_w_wptr_o, + input logic [LogDepth:0] async_data_master_w_rptr_i, + input b_chan_t [2**LogDepth-1:0] async_data_master_b_data_i, + input logic [LogDepth:0] async_data_master_b_wptr_i, + output logic [LogDepth:0] async_data_master_b_rptr_o, + output ar_chan_t [2**LogDepth-1:0] async_data_master_ar_data_o, + output logic [LogDepth:0] async_data_master_ar_wptr_o, + input logic [LogDepth:0] async_data_master_ar_rptr_i, + input r_chan_t [2**LogDepth-1:0] async_data_master_r_data_i, + input logic [LogDepth:0] async_data_master_r_wptr_i, + output logic [LogDepth:0] async_data_master_r_rptr_o +); + + cdc_fifo_gray_src_clearable #( + // Workaround for a bug in Questa (see comment in `axi_cdc_dst` for details). +`ifdef QUESTA + .T ( logic [$bits(aw_chan_t)-1:0] ), +`else + .T ( aw_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) + ) i_cdc_fifo_gray_src_aw ( + .src_clk_i, + .src_rst_ni, + .src_clear_i, + .src_data_i ( src_req_i.aw ), + .src_valid_i ( src_req_i.aw_valid ), + .src_ready_o ( src_resp_o.aw_ready ), + .async_data_o ( async_data_master_aw_data_o ), + .async_wptr_o ( async_data_master_aw_wptr_o ), + .async_rptr_i ( async_data_master_aw_rptr_i ) + ); + + cdc_fifo_gray_src_clearable #( +`ifdef QUESTA + .T ( logic [$bits(w_chan_t)-1:0] ), +`else + .T ( w_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) + ) i_cdc_fifo_gray_src_w ( + .src_clk_i, + .src_rst_ni, + .src_clear_i, + .src_data_i ( src_req_i.w ), + .src_valid_i ( src_req_i.w_valid ), + .src_ready_o ( src_resp_o.w_ready ), + .async_data_o ( async_data_master_w_data_o ), + .async_wptr_o ( async_data_master_w_wptr_o ), + .async_rptr_i ( async_data_master_w_rptr_i ) + ); + + cdc_fifo_gray_dst_clearable #( +`ifdef QUESTA + .T ( logic [$bits(b_chan_t)-1:0] ), +`else + .T ( b_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) + ) i_cdc_fifo_gray_dst_b ( + .dst_clk_i ( src_clk_i ), + .dst_rst_ni ( src_rst_ni ), + .dst_clear_i ( src_clear_i ), + .dst_data_o ( src_resp_o.b ), + .dst_valid_o ( src_resp_o.b_valid ), + .dst_ready_i ( src_req_i.b_ready ), + .async_data_i ( async_data_master_b_data_i ), + .async_wptr_i ( async_data_master_b_wptr_i ), + .async_rptr_o ( async_data_master_b_rptr_o ) + ); + + cdc_fifo_gray_src_clearable #( +`ifdef QUESTA + .T ( logic [$bits(ar_chan_t)-1:0] ), +`else + .T ( ar_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) + ) i_cdc_fifo_gray_src_ar ( + .src_clk_i, + .src_rst_ni, + .src_clear_i, + .src_data_i ( src_req_i.ar ), + .src_valid_i ( src_req_i.ar_valid ), + .src_ready_o ( src_resp_o.ar_ready ), + .async_data_o ( async_data_master_ar_data_o ), + .async_wptr_o ( async_data_master_ar_wptr_o ), + .async_rptr_i ( async_data_master_ar_rptr_i ) + ); + + cdc_fifo_gray_dst_clearable #( +`ifdef QUESTA + .T ( logic [$bits(r_chan_t)-1:0] ), +`else + .T ( r_chan_t ), +`endif + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) + ) i_cdc_fifo_gray_dst_r ( + .dst_clk_i ( src_clk_i ), + .dst_rst_ni ( src_rst_ni ), + .dst_clear_i ( src_clear_i ), + .dst_data_o ( src_resp_o.r ), + .dst_valid_o ( src_resp_o.r_valid ), + .dst_ready_i ( src_req_i.r_ready ), + .async_data_i ( async_data_master_r_data_i ), + .async_wptr_i ( async_data_master_r_wptr_i ), + .async_rptr_o ( async_data_master_r_rptr_o ) + ); + +endmodule + + +module axi_cdc_src_clearable_intf #( + parameter int unsigned AXI_ID_WIDTH = 0, + parameter int unsigned AXI_ADDR_WIDTH = 0, + 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, + /// 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, + input logic src_rst_ni, + input logic src_clear_i, + AXI_BUS.Slave src, + // asynchronous master port + AXI_BUS_ASYNC_GRAY.Master dst +); + + 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(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 src_req; + resp_t src_resp; + + `AXI_ASSIGN_TO_REQ(src_req, src) + `AXI_ASSIGN_FROM_RESP(src, src_resp) + + axi_cdc_src_clearable #( + .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, + .src_clear_i, + .src_req_i ( src_req ), + .src_resp_o ( src_resp ), + .async_data_master_aw_data_o ( dst.aw_data ), + .async_data_master_aw_wptr_o ( dst.aw_wptr ), + .async_data_master_aw_rptr_i ( dst.aw_rptr ), + .async_data_master_w_data_o ( dst.w_data ), + .async_data_master_w_wptr_o ( dst.w_wptr ), + .async_data_master_w_rptr_i ( dst.w_rptr ), + .async_data_master_b_data_i ( dst.b_data ), + .async_data_master_b_wptr_i ( dst.b_wptr ), + .async_data_master_b_rptr_o ( dst.b_rptr ), + .async_data_master_ar_data_o ( dst.ar_data ), + .async_data_master_ar_wptr_o ( dst.ar_wptr ), + .async_data_master_ar_rptr_i ( dst.ar_rptr ), + .async_data_master_r_data_i ( dst.r_data ), + .async_data_master_r_wptr_i ( dst.r_wptr ), + .async_data_master_r_rptr_o ( dst.r_rptr ) + ); + +endmodule + + +module axi_lite_cdc_src_clearable_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, + /// 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, + input logic src_rst_ni, + input logic src_clear_i, + AXI_BUS.Slave src, + // asynchronous master port + AXI_LITE_ASYNC_GRAY.Master dst +); + + 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; + `AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t) + `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) + `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) + `AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + + req_t src_req; + resp_t src_resp; + + `AXI_LITE_ASSIGN_TO_REQ(src_req, src) + `AXI_LITE_ASSIGN_FROM_RESP(src, src_resp) + + axi_cdc_src_clearable #( + .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, + .src_clear_i, + .src_req_i ( src_req ), + .src_resp_o ( src_resp ), + .async_data_master_aw_data_o ( dst.aw_data ), + .async_data_master_aw_wptr_o ( dst.aw_wptr ), + .async_data_master_aw_rptr_i ( dst.aw_rptr ), + .async_data_master_w_data_o ( dst.w_data ), + .async_data_master_w_wptr_o ( dst.w_wptr ), + .async_data_master_w_rptr_i ( dst.w_rptr ), + .async_data_master_b_data_i ( dst.b_data ), + .async_data_master_b_wptr_i ( dst.b_wptr ), + .async_data_master_b_rptr_o ( dst.b_rptr ), + .async_data_master_ar_data_o ( dst.ar_data ), + .async_data_master_ar_wptr_o ( dst.ar_wptr ), + .async_data_master_ar_rptr_i ( dst.ar_rptr ), + .async_data_master_r_data_i ( dst.r_data ), + .async_data_master_r_wptr_i ( dst.r_wptr ), + .async_data_master_r_rptr_o ( dst.r_rptr ) + ); + +endmodule diff --git a/src_files.yml b/src_files.yml index 844c458a9..1e025522b 100644 --- a/src_files.yml +++ b/src_files.yml @@ -19,7 +19,9 @@ axi: - src/axi_burst_unwrap.sv - src/axi_bus_compare.sv - src/axi_cdc_dst.sv + - src/axi_cdc_dst_clearable.sv - src/axi_cdc_src.sv + - src/axi_cdc_src_clearable.sv - src/axi_cut.sv - src/axi_delayer.sv - src/axi_demux_simple.sv @@ -52,6 +54,7 @@ axi: # Level 3 - src/axi_burst_splitter.sv - src/axi_cdc.sv + - src/axi_cdc_isolatable.sv - src/axi_demux.sv - src/axi_err_slv.sv - src/axi_dw_converter.sv diff --git a/test/tb_axi_cdc_isolatable.sv b/test/tb_axi_cdc_isolatable.sv new file mode 100644 index 000000000..085564f2b --- /dev/null +++ b/test/tb_axi_cdc_isolatable.sv @@ -0,0 +1,396 @@ +// Copyright 2019-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 +// - Luca Rufer + +`include "axi/typedef.svh" +`include "axi/assign.svh" + +module tb_axi_cdc_isolatable #( + // AXI Parameters + parameter int unsigned AXI_AW = 32, + parameter int unsigned AXI_DW = 64, + parameter int unsigned AXI_IW = 4, + parameter int unsigned AXI_UW = 2, + parameter int unsigned AXI_MAX_READ_TXNS = 10, + parameter int unsigned AXI_MAX_WRITE_TXNS = 12, + // TB Parameters + parameter time TCLK_UPSTREAM = 10ns, + parameter time TA_UPSTREAM = TCLK_UPSTREAM * 1/4, + parameter time TT_UPSTREAM = TCLK_UPSTREAM * 3/4, + parameter time TCLK_DOWNSTREAM = 3ns, + parameter time TA_DOWNSTREAM = TCLK_DOWNSTREAM * 1/4, + parameter time TT_DOWNSTREAM = TCLK_DOWNSTREAM * 3/4, + parameter int unsigned REQ_MIN_WAIT_CYCLES = 0, + parameter int unsigned REQ_MAX_WAIT_CYCLES = 10, + parameter int unsigned RESP_MIN_WAIT_CYCLES = 0, + parameter int unsigned RESP_MAX_WAIT_CYCLES = REQ_MAX_WAIT_CYCLES/2, + parameter int unsigned N_TXNS = 100, + parameter int unsigned N_UPSTREAM_RSTS = 10, + parameter int unsigned ISOLATE_MIN_WAIT_CYCLES = 0, + parameter int unsigned ISOLATE_MAX_WAIT_CYCLES = 500 +); + + localparam int unsigned N_RD_TXNS = N_TXNS / 2; + localparam int unsigned N_WR_TXNS = N_TXNS / 2; + + // Clocks and Resets + + logic upstream_clk, + upstream_rst_n, + upstream_rst_gen_n, + upstream_test_rst_n; + + clk_rst_gen #( + .ClkPeriod (TCLK_UPSTREAM), + .RstClkCycles (5) + ) i_clk_rst_gen_upstream ( + .clk_o (upstream_clk), + .rst_no (upstream_rst_gen_n) + ); + + assign upstream_rst_n = upstream_rst_gen_n & upstream_test_rst_n; + + logic downstream_clk, + downstream_rst_n, + downstream_rst_gen_n, + downstream_test_rst_n; + + clk_rst_gen #( + .ClkPeriod (TCLK_DOWNSTREAM), + .RstClkCycles (5) + ) i_clk_rst_gen_downstream ( + .clk_o (downstream_clk), + .rst_no (downstream_rst_gen_n) + ); + + assign downstream_rst_n = downstream_rst_gen_n & downstream_test_rst_n; + + // AXI Interfaces + + AXI_BUS_DV #( + .AXI_ADDR_WIDTH (AXI_AW), + .AXI_DATA_WIDTH (AXI_DW), + .AXI_ID_WIDTH (AXI_IW), + .AXI_USER_WIDTH (AXI_UW) + ) upstream_dv ( + .clk_i (upstream_clk) + ); + + AXI_BUS #( + .AXI_ADDR_WIDTH (AXI_AW), + .AXI_DATA_WIDTH (AXI_DW), + .AXI_ID_WIDTH (AXI_IW), + .AXI_USER_WIDTH (AXI_UW) + ) upstream (); + + `AXI_ASSIGN(upstream, upstream_dv) + + AXI_BUS_DV #( + .AXI_ADDR_WIDTH (AXI_AW), + .AXI_DATA_WIDTH (AXI_DW), + .AXI_ID_WIDTH (AXI_IW), + .AXI_USER_WIDTH (AXI_UW) + ) downstream_dv ( + .clk_i (downstream_clk) + ); + + AXI_BUS #( + .AXI_ADDR_WIDTH (AXI_AW), + .AXI_DATA_WIDTH (AXI_DW), + .AXI_ID_WIDTH (AXI_IW), + .AXI_USER_WIDTH (AXI_UW) + ) downstream (); + + `AXI_ASSIGN(downstream_dv, downstream) + + // AXI Channel Structs + + typedef logic [AXI_AW-1:0] addr_t; + typedef logic [AXI_DW-1:0] data_t; + typedef logic [AXI_IW-1:0] id_t; + typedef logic [AXI_DW/8-1:0] strb_t; + typedef logic [AXI_UW-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) + + // Source Isolation signals + logic src_isolate, src_isolated; + + // Dest Isolation signals + logic dst_isolate, dst_isolated; + + axi_cdc_isolatable_intf #( + .AXI_ADDR_WIDTH (AXI_AW), + .AXI_DATA_WIDTH (AXI_DW), + .AXI_ID_WIDTH (AXI_IW), + .AXI_USER_WIDTH (AXI_UW), + .ATOP_SUPPORT (1'b1), + .LOG_DEPTH (2), + .SYNC_STAGES (2), + .TERMINATE_TRANSACTION (0) + ) dut ( + .src_clk_i (upstream_clk), + .src_rst_ni (upstream_rst_n), + .src_isolate_i (src_isolate), + .src_isolated_o (src_isolated), + .src (upstream), + .dst_clk_i (downstream_clk), + .dst_rst_ni (downstream_rst_n), + .dst_isolate_i (dst_isolate), + .dst_isolated_o (dst_isolated), + .dst (downstream) + ); + + semaphore reset_lock; + initial reset_lock = new(1); + + initial begin + src_isolate <= 1'b0; + downstream_test_rst_n <= 1'b1; + wait (upstream_rst_n); + + forever begin + automatic int rand_success; + automatic int unsigned cycles; + automatic bit reset_downstream; + rand_success = std::randomize(cycles) with { + cycles >= ISOLATE_MIN_WAIT_CYCLES; + cycles <= ISOLATE_MAX_WAIT_CYCLES; + }; + assert (rand_success) else $error("Failed to randomize wait cycles!"); + reset_downstream = $random; + + repeat (cycles) @(posedge upstream_clk); + + reset_lock.get(); + + // src_isolate + #(TA_UPSTREAM); + src_isolate <= 1'b1; + #(TT_UPSTREAM - TA_UPSTREAM); + wait (src_isolated); + + // Optional downstream reset + if (reset_downstream) begin + @(posedge downstream_clk); + #(TA_DOWNSTREAM); + downstream_test_rst_n <= 1'b0; + + repeat (5) @(posedge downstream_clk); + #(TA_DOWNSTREAM); + downstream_test_rst_n <= 1'b1; + end + + // Release Isolation + @(posedge upstream_clk); + #(TA_UPSTREAM); + src_isolate <= 1'b0; + #(TT_UPSTREAM - TA_UPSTREAM); + wait (!src_isolated); + + reset_lock.put(); + end + end + + typedef axi_test::axi_rand_master #( + .AW (AXI_AW), + .DW (AXI_DW), + .IW (AXI_IW), + .UW (AXI_UW), + .TA (TA_UPSTREAM), + .TT (TT_UPSTREAM), + .MAX_READ_TXNS (AXI_MAX_READ_TXNS), + .MAX_WRITE_TXNS (AXI_MAX_WRITE_TXNS), + .AX_MIN_WAIT_CYCLES (REQ_MIN_WAIT_CYCLES), + .AX_MAX_WAIT_CYCLES (REQ_MAX_WAIT_CYCLES), + .W_MIN_WAIT_CYCLES (REQ_MIN_WAIT_CYCLES), + .W_MAX_WAIT_CYCLES (REQ_MAX_WAIT_CYCLES), + .RESP_MIN_WAIT_CYCLES (RESP_MIN_WAIT_CYCLES), + .RESP_MAX_WAIT_CYCLES (RESP_MAX_WAIT_CYCLES), + .AXI_MAX_BURST_LEN (16) + ) axi_master_t; + axi_master_t axi_master = new(upstream_dv); + + initial begin + dst_isolate <= 1'b0; + upstream_test_rst_n <= 1'b1; + wait (upstream_rst_n); + + axi_master.run(N_RD_TXNS, N_WR_TXNS); + + for (int unsigned i = 0; i < N_UPSTREAM_RSTS - 1; i++) begin + automatic bit reset_upstream; + automatic bit release_dst_iso_before_reset; + reset_upstream = $random; + release_dst_iso_before_reset = $random; + + reset_lock.get(); + + // Isolate + @(posedge downstream_clk); + #(TA_DOWNSTREAM); + dst_isolate <= 1'b1; + #(TT_DOWNSTREAM - TA_DOWNSTREAM); + wait (dst_isolated); + + // Optional isolate release before reset + if (release_dst_iso_before_reset) begin + @(posedge downstream_clk); + #(TA_DOWNSTREAM); + dst_isolate <= 1'b0; + #(TT_DOWNSTREAM - TA_DOWNSTREAM); + wait (!dst_isolated); + end + + // Optional upstream reset + if (reset_upstream) begin + @(posedge upstream_clk); + #(TA_UPSTREAM); + upstream_test_rst_n <= 1'b0; + + repeat (5) @(posedge upstream_clk); + #(TA_UPSTREAM); + upstream_test_rst_n <= 1'b1; + end + + // Optional isolate release after reset + if (!release_dst_iso_before_reset) begin + @(posedge downstream_clk); + #(TA_DOWNSTREAM); + dst_isolate <= 1'b0; + #(TT_DOWNSTREAM - TA_DOWNSTREAM); + wait (!dst_isolated); + end + + reset_lock.put(); + + axi_master.run(N_RD_TXNS, N_WR_TXNS); + end + end + + typedef axi_test::axi_rand_slave #( + .AW (AXI_AW), + .DW (AXI_DW), + .IW (AXI_IW), + .UW (AXI_UW), + .TA (TA_DOWNSTREAM), + .TT (TT_DOWNSTREAM), + .AX_MIN_WAIT_CYCLES (RESP_MIN_WAIT_CYCLES), + .AX_MAX_WAIT_CYCLES (RESP_MAX_WAIT_CYCLES), + .R_MIN_WAIT_CYCLES (RESP_MIN_WAIT_CYCLES), + .R_MAX_WAIT_CYCLES (RESP_MAX_WAIT_CYCLES), + .RESP_MIN_WAIT_CYCLES (RESP_MIN_WAIT_CYCLES), + .RESP_MAX_WAIT_CYCLES (RESP_MAX_WAIT_CYCLES) + ) axi_slave_t; + axi_slave_t axi_slave = new(downstream_dv); + + initial begin + wait (downstream_rst_n); + axi_slave.run(); + end + + ar_chan_t mst_ar, slv_ar, ar_queue[$]; + aw_chan_t mst_aw, slv_aw, aw_queue[$]; + b_chan_t mst_b, slv_b, b_queue[$]; + r_chan_t mst_r, slv_r, r_queue[$]; + w_chan_t mst_w, slv_w, w_queue[$]; + + `AXI_ASSIGN_TO_AR(mst_ar, upstream) + `AXI_ASSIGN_TO_AR(slv_ar, downstream) + `AXI_ASSIGN_TO_AW(mst_aw, upstream) + `AXI_ASSIGN_TO_AW(slv_aw, downstream) + `AXI_ASSIGN_TO_B(mst_b, upstream) + `AXI_ASSIGN_TO_B(slv_b, downstream) + `AXI_ASSIGN_TO_R(mst_r, upstream) + `AXI_ASSIGN_TO_R(slv_r, downstream) + `AXI_ASSIGN_TO_W(mst_w, upstream) + `AXI_ASSIGN_TO_W(slv_w, downstream) + + logic mst_done = 1'b0; + // Monitor and check upstream + initial begin + automatic b_chan_t exp_b; + automatic r_chan_t exp_r; + automatic int unsigned rd_cnt = 0, wr_cnt = 0; + forever begin + @(posedge upstream_clk); + #(TT_UPSTREAM); + if (upstream.aw_valid && upstream.aw_ready) begin + aw_queue.push_back(mst_aw); + end + if (upstream.w_valid && upstream.w_ready) begin + w_queue.push_back(mst_w); + end + if (upstream.b_valid && upstream.b_ready) begin + exp_b = b_queue.pop_front(); + assert (mst_b == exp_b); + wr_cnt++; + end + if (upstream.ar_valid && upstream.ar_ready) begin + ar_queue.push_back(mst_ar); + end + if (upstream.r_valid && upstream.r_ready) begin + exp_r = r_queue.pop_front(); + assert (mst_r == exp_r); + if (upstream.r_last) begin + rd_cnt++; + end + end + if (rd_cnt == N_UPSTREAM_RSTS * N_RD_TXNS && wr_cnt == N_UPSTREAM_RSTS * N_WR_TXNS) begin + mst_done = 1'b1; + end + end + end + + // Monitor and check downstream + initial begin + automatic ar_chan_t exp_ar; + automatic aw_chan_t exp_aw; + automatic w_chan_t exp_w; + forever begin + @(posedge downstream_clk); + #(TT_DOWNSTREAM); + if (downstream.aw_valid && downstream.aw_ready) begin + exp_aw = aw_queue.pop_front(); + assert (slv_aw == exp_aw); + end + if (downstream.w_valid && downstream.w_ready) begin + exp_w = w_queue.pop_front(); + assert (slv_w == exp_w); + end + if (downstream.b_valid && downstream.b_ready) begin + b_queue.push_back(slv_b); + end + if (downstream.ar_valid && downstream.ar_ready) begin + exp_ar = ar_queue.pop_front(); + assert (slv_ar == exp_ar); + end + if (downstream.r_valid && downstream.r_ready) begin + r_queue.push_back(slv_r); + end + end + end + + // Terminate simulation after all transactions have completed. + initial begin + wait (mst_done); + #(10*TCLK_UPSTREAM); + $finish(); + end + +endmodule