From 6bccfd6efd976027b0f7cbc1fd2082de9a5fff9d Mon Sep 17 00:00:00 2001 From: Nicole Narr Date: Wed, 2 Nov 2022 02:53:04 +0100 Subject: [PATCH] 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