-
Notifications
You must be signed in to change notification settings - Fork 268
/
axi_lite_from_mem.sv
247 lines (238 loc) · 10.3 KB
/
axi_lite_from_mem.sv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
// 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:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Nicole Narr <narrn@ethz.ch>
/// 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