forked from pulp-platform/axi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
axi_pkg.sv
423 lines (399 loc) · 19.5 KB
/
axi_pkg.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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
// Copyright (c) 2014-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 <akurth@iis.ee.ethz.ch>
// - Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Matheus Cavalcante <matheusd@iis.ee.ethz.ch>
//! AXI Package
/// Contains all necessary type definitions, constants, and generally useful functions.
package axi_pkg;
/// AXI Transaction Burst Type.
typedef logic [1:0] burst_t;
/// AXI Transaction Response Type.
typedef logic [1:0] resp_t;
/// AXI Transaction Cacheability Type.
typedef logic [3:0] cache_t;
/// AXI Transaction Protection Type.
typedef logic [2:0] prot_t;
/// AXI Transaction Quality of Service Type.
typedef logic [3:0] qos_t;
/// AXI Transaction Region Type.
typedef logic [3:0] region_t;
/// AXI Transaction Length Type.
typedef logic [7:0] len_t;
/// AXI Transaction Size Type.
typedef logic [2:0] size_t;
/// AXI5 Atomic Operation Type.
typedef logic [5:0] atop_t; // atomic operations
/// AXI5 Non-Secure Address Identifier.
typedef logic [3:0] nsaid_t;
/// In a fixed burst:
/// - The address is the same for every transfer in the burst.
/// - The byte lanes that are valid are constant for all beats in the burst. However, within
/// those byte lanes, the actual bytes that have `wstrb` asserted can differ for each beat in
/// the burst.
/// This burst type is used for repeated accesses to the same location such as when loading or
/// emptying a FIFO.
localparam BURST_FIXED = 2'b00;
/// In an incrementing burst, the address for each transfer in the burst is an increment of the
/// address for the previous transfer. The increment value depends on the size of the transfer.
/// For example, the address for each transfer in a burst with a size of 4 bytes is the previous
/// address plus four.
/// This burst type is used for accesses to normal sequential memory.
localparam BURST_INCR = 2'b01;
/// A wrapping burst is similar to an incrementing burst, except that the address wraps around to
/// a lower address if an upper address limit is reached.
/// The following restrictions apply to wrapping bursts:
/// - The start address must be aligned to the size of each transfer.
/// - The length of the burst must be 2, 4, 8, or 16 transfers.
localparam BURST_WRAP = 2'b10;
/// Normal access success. Indicates that a normal access has been successful. Can also indicate
/// that an exclusive access has failed.
localparam RESP_OKAY = 2'b00;
/// Exclusive access okay. Indicates that either the read or write portion of an exclusive access
/// has been successful.
localparam RESP_EXOKAY = 2'b01;
/// Slave error. Used when the access has reached the slave successfully, but the slave wishes to
/// return an error condition to the originating master.
localparam RESP_SLVERR = 2'b10;
/// Decode error. Generated, typically by an interconnect component, to indicate that there is no
/// slave at the transaction address.
localparam RESP_DECERR = 2'b11;
/// When this bit is asserted, the interconnect, or any component, can delay the transaction
/// reaching its final destination for any number of cycles.
localparam CACHE_BUFFERABLE = 4'b0001;
/// When HIGH, Modifiable indicates that the characteristics of the transaction can be modified.
/// When Modifiable is LOW, the transaction is Non-modifiable.
localparam CACHE_MODIFIABLE = 4'b0010;
/// When this bit is asserted, read allocation of the transaction is recommended but is not
/// mandatory.
localparam CACHE_RD_ALLOC = 4'b0100;
/// When this bit is asserted, write allocation of the transaction is recommended but is not
/// mandatory.
localparam CACHE_WR_ALLOC = 4'b1000;
/// Maximum number of bytes per burst, as specified by `size` (see Table A3-2).
function automatic shortint unsigned num_bytes(size_t size);
return 1 << size;
endfunction
/// An overly long address type.
/// It lets us define functions that work generically for shorter addresses. We rely on the
/// synthesizer to optimize the unused bits away.
typedef logic [127:0] largest_addr_t;
/// Aligned address of burst (see A3-51).
function automatic largest_addr_t aligned_addr(largest_addr_t addr, size_t size);
return (addr >> size) << size;
endfunction
/// Warp boundary of a `BURST_WRAP` transfer (see A3-51).
/// This is the lowest address accessed within a wrapping burst.
/// This address is aligned to the size and length of the burst.
/// The length of a `BURST_WRAP` has to be 2, 4, 8, or 16 transfers.
function automatic largest_addr_t wrap_boundary (largest_addr_t addr, size_t size, len_t len);
largest_addr_t wrap_addr;
// pragma translate_off
`ifndef VERILATOR
assume (len == len_t'(4'b1) || len == len_t'(4'b11) || len == len_t'(4'b111) ||
len == len_t'(4'b1111)) else
$error("AXI BURST_WRAP with not allowed len of: %0h", len);
`endif
// pragma translate_on
// In A3-51 the wrap boundary is defined as:
// `Wrap_Boundary = (INT(Start_Address / (Number_Bytes × Burst_Length))) ×
// (Number_Bytes × Burst_Length)`
// Whereas the aligned address is defined as:
// `Aligned_Address = (INT(Start_Address / Number_Bytes)) × Number_Bytes`
// This leads to the wrap boundary using the same calculation as the aligned address, difference
// being the additional dependency on the burst length. The addition in the case statement
// is equal to the multiplication with `Burst_Length` as a shift (used by `aligned_addr`) is
// equivalent with multiplication and division by a power of two, which conveniently are the
// only allowed values for `len` of a `BURST_WRAP`.
unique case (len)
4'b1 : wrap_addr = (addr >> (unsigned'(size) + 1)) << (unsigned'(size) + 1); // multiply `Number_Bytes` by `2`
4'b11 : wrap_addr = (addr >> (unsigned'(size) + 2)) << (unsigned'(size) + 2); // multiply `Number_Bytes` by `4`
4'b111 : wrap_addr = (addr >> (unsigned'(size) + 3)) << (unsigned'(size) + 3); // multiply `Number_Bytes` by `8`
4'b1111 : wrap_addr = (addr >> (unsigned'(size) + 4)) << (unsigned'(size) + 4); // multiply `Number_Bytes` by `16`
default : wrap_addr = '0;
endcase
return wrap_addr;
endfunction
/// Address of beat (see A3-51).
function automatic largest_addr_t
beat_addr(largest_addr_t addr, size_t size, len_t len, burst_t burst, shortint unsigned i_beat);
largest_addr_t ret_addr = addr;
largest_addr_t wrp_bond = '0;
if (burst == BURST_WRAP) begin
// do not trigger the function if there is no wrapping burst, to prevent assumptions firing
wrp_bond = wrap_boundary(addr, size, len);
end
if (i_beat != 0 && burst != BURST_FIXED) begin
// From A3-51:
// For an INCR burst, and for a WRAP burst for which the address has not wrapped, this
// equation determines the address of any transfer after the first transfer in a burst:
// `Address_N = Aligned_Address + (N – 1) × Number_Bytes` (N counts from 1 to len!)
ret_addr = aligned_addr(addr, size) + i_beat * num_bytes(size);
// From A3-51:
// For a WRAP burst, if Address_N = Wrap_Boundary + (Number_Bytes × Burst_Length), then:
// * Use this equation for the current transfer:
// `Address_N = Wrap_Boundary`
// * Use this equation for any subsequent transfers:
// `Address_N = Start_Address + ((N – 1) × Number_Bytes) – (Number_Bytes × Burst_Length)`
// This means that the address calculation of a `BURST_WRAP` fundamentally works the same
// as for a `BURST_INC`, the difference is when the calculated address increments
// over the wrap threshold, the address wraps around by subtracting the accessed address
// space from the normal `BURST_INCR` address. The lower wrap boundary is equivalent to
// The wrap trigger condition minus the container size (`num_bytes(size) * (len + 1)`).
if (burst == BURST_WRAP && ret_addr >= wrp_bond + (num_bytes(size) * (len + 1))) begin
ret_addr = ret_addr - (num_bytes(size) * (len + 1));
end
end
return ret_addr;
endfunction
/// Index of lowest byte in beat (see A3-51).
function automatic shortint unsigned
beat_lower_byte(largest_addr_t addr, size_t size, len_t len, burst_t burst,
shortint unsigned strobe_width, shortint unsigned i_beat);
largest_addr_t _addr = beat_addr(addr, size, len, burst, i_beat);
return _addr - (_addr / strobe_width) * strobe_width;
endfunction
/// Index of highest byte in beat (see A3-51).
function automatic shortint unsigned
beat_upper_byte(largest_addr_t addr, size_t size, len_t len, burst_t burst,
shortint unsigned strobe_width, shortint unsigned i_beat);
if (i_beat == 0) begin
return aligned_addr(addr, size) + (num_bytes(size) - 1) - (addr / strobe_width) * strobe_width;
end else begin
return beat_lower_byte(addr, size, len, burst, strobe_width, i_beat) + num_bytes(size) - 1;
end
endfunction
/// Is the bufferable bit set?
function automatic logic bufferable(cache_t cache);
return |(cache & CACHE_BUFFERABLE);
endfunction
/// Is the modifiable bit set?
function automatic logic modifiable(cache_t cache);
return |(cache & CACHE_MODIFIABLE);
endfunction
/// Memory Type.
typedef enum logic [3:0] {
DEVICE_NONBUFFERABLE,
DEVICE_BUFFERABLE,
NORMAL_NONCACHEABLE_NONBUFFERABLE,
NORMAL_NONCACHEABLE_BUFFERABLE,
WTHRU_NOALLOCATE,
WTHRU_RALLOCATE,
WTHRU_WALLOCATE,
WTHRU_RWALLOCATE,
WBACK_NOALLOCATE,
WBACK_RALLOCATE,
WBACK_WALLOCATE,
WBACK_RWALLOCATE
} mem_type_t;
/// Create an `AR_CACHE` field from a `mem_type_t` type.
function automatic logic [3:0] get_arcache(mem_type_t mtype);
unique case (mtype)
DEVICE_NONBUFFERABLE : return 4'b0000;
DEVICE_BUFFERABLE : return 4'b0001;
NORMAL_NONCACHEABLE_NONBUFFERABLE : return 4'b0010;
NORMAL_NONCACHEABLE_BUFFERABLE : return 4'b0011;
WTHRU_NOALLOCATE : return 4'b1010;
WTHRU_RALLOCATE : return 4'b1110;
WTHRU_WALLOCATE : return 4'b1010;
WTHRU_RWALLOCATE : return 4'b1110;
WBACK_NOALLOCATE : return 4'b1011;
WBACK_RALLOCATE : return 4'b1111;
WBACK_WALLOCATE : return 4'b1011;
WBACK_RWALLOCATE : return 4'b1111;
endcase // mtype
endfunction
/// Create an `AW_CACHE` field from a `mem_type_t` type.
function automatic logic [3:0] get_awcache(mem_type_t mtype);
unique case (mtype)
DEVICE_NONBUFFERABLE : return 4'b0000;
DEVICE_BUFFERABLE : return 4'b0001;
NORMAL_NONCACHEABLE_NONBUFFERABLE : return 4'b0010;
NORMAL_NONCACHEABLE_BUFFERABLE : return 4'b0011;
WTHRU_NOALLOCATE : return 4'b0110;
WTHRU_RALLOCATE : return 4'b0110;
WTHRU_WALLOCATE : return 4'b1110;
WTHRU_RWALLOCATE : return 4'b1110;
WBACK_NOALLOCATE : return 4'b0111;
WBACK_RALLOCATE : return 4'b0111;
WBACK_WALLOCATE : return 4'b1111;
WBACK_RWALLOCATE : return 4'b1111;
endcase // mtype
endfunction
/// RESP precedence: DECERR > SLVERR > OKAY > EXOKAY. This is not defined in the AXI standard but
/// depends on the implementation. We consistently use the precedence above. Rationale:
/// - EXOKAY means an exclusive access was successful, whereas OKAY means it was not. Thus, if
/// OKAY and EXOKAY are to be merged, OKAY precedes because the exclusive access was not fully
/// successful.
/// - Both DECERR and SLVERR mean (part of) a transaction were unsuccessful, whereas OKAY means an
/// entire transaction was successful. Thus both DECERR and SLVERR precede OKAY.
/// - DECERR means (part of) a transactions could not be routed to a slave component, whereas
/// SLVERR means the transaction reached a slave component but lead to an error condition there.
/// Thus DECERR precedes SLVERR because DECERR happens earlier in the handling of a transaction.
function automatic resp_t resp_precedence(resp_t resp_a, resp_t resp_b);
unique case (resp_a)
RESP_OKAY: begin
// Any response except EXOKAY precedes OKAY.
if (resp_b == RESP_EXOKAY) begin
return resp_a;
end else begin
return resp_b;
end
end
RESP_EXOKAY: begin
// Any response precedes EXOKAY.
return resp_b;
end
RESP_SLVERR: begin
// Only DECERR precedes SLVERR.
if (resp_b == RESP_DECERR) begin
return resp_b;
end else begin
return resp_a;
end
end
RESP_DECERR: begin
// No response precedes DECERR.
return resp_a;
end
endcase
endfunction
// ATOP[5:0]
/// - Sends a single data value with an address.
/// - The target swaps the value at the addressed location with the data value that is supplied in
/// the transaction.
/// - The original data value at the addressed location is returned.
/// - Outbound data size is 1, 2, 4, or 8 bytes.
/// - Inbound data size is the same as the outbound data size.
localparam ATOP_ATOMICSWAP = 6'b110000;
/// - Sends two data values, the compare value and the swap value, to the addressed location.
/// The compare and swap values are of equal size.
/// - The data value at the addressed location is checked against the compare value:
/// - If the values match, the swap value is written to the addressed location.
/// - If the values do not match, the swap value is not written to the addressed location.
/// - The original data value at the addressed location is returned.
/// - Outbound data size is 2, 4, 8, 16, or 32 bytes.
/// - Inbound data size is half of the outbound data size because the outbound data contains both
/// compare and swap values, whereas the inbound data has only the original data value.
localparam ATOP_ATOMICCMP = 6'b110001;
// ATOP[5:4]
/// Perform no atomic operation.
localparam ATOP_NONE = 2'b00;
/// - Sends a single data value with an address and the atomic operation to be performed.
/// - The target performs the operation using the sent data and value at the addressed location as
/// operands.
/// - The result is stored in the address location.
/// - A single response is given without data.
/// - Outbound data size is 1, 2, 4, or 8 bytes.
localparam ATOP_ATOMICSTORE = 2'b01;
/// Sends a single data value with an address and the atomic operation to be performed.
/// - The original data value at the addressed location is returned.
/// - The target performs the operation using the sent data and value at the addressed location as
/// operands.
/// - The result is stored in the address location.
/// - Outbound data size is 1, 2, 4, or 8 bytes.
/// - Inbound data size is the same as the outbound data size.
localparam ATOP_ATOMICLOAD = 2'b10;
// ATOP[3]
/// For AtomicStore and AtomicLoad transactions `AWATOP[3]` indicates the endianness that is
/// required for the atomic operation. The value of `AWATOP[3]` applies to arithmetic operations
/// only and is ignored for bitwise logical operations.
/// When deasserted, this bit indicates that the operation is little-endian.
localparam ATOP_LITTLE_END = 1'b0;
/// When asserted, this bit indicates that the operation is big-endian.
localparam ATOP_BIG_END = 1'b1;
// ATOP[2:0]
/// The value in memory is added to the sent data and the result stored in memory.
localparam ATOP_ADD = 3'b000;
/// Every set bit in the sent data clears the corresponding bit of the data in memory.
localparam ATOP_CLR = 3'b001;
/// Bitwise exclusive OR of the sent data and value in memory.
localparam ATOP_EOR = 3'b010;
/// Every set bit in the sent data sets the corresponding bit of the data in memory.
localparam ATOP_SET = 3'b011;
/// The value stored in memory is the maximum of the existing value and sent data. This operation
/// assumes signed data.
localparam ATOP_SMAX = 3'b100;
/// The value stored in memory is the minimum of the existing value and sent data. This operation
/// assumes signed data.
localparam ATOP_SMIN = 3'b101;
/// The value stored in memory is the maximum of the existing value and sent data. This operation
/// assumes unsigned data.
localparam ATOP_UMAX = 3'b110;
/// The value stored in memory is the minimum of the existing value and sent data. This operation
/// assumes unsigned data.
localparam ATOP_UMIN = 3'b111;
// ATOP[5] == 1'b1 indicated that an atomic transaction has a read response
// Ussage eg: if (req_i.aw.atop[axi_pkg::ATOP_R_RESP]) begin
localparam ATOP_R_RESP = 32'd5;
// `xbar_latency_e` and `xbar_cfg_t` are documented in `doc/axi_xbar.md`.
/// Slice on Demux AW channel.
localparam logic [9:0] DemuxAw = (1 << 9);
/// Slice on Demux W channel.
localparam logic [9:0] DemuxW = (1 << 8);
/// Slice on Demux B channel.
localparam logic [9:0] DemuxB = (1 << 7);
/// Slice on Demux AR channel.
localparam logic [9:0] DemuxAr = (1 << 6);
/// Slice on Demux R channel.
localparam logic [9:0] DemuxR = (1 << 5);
/// Slice on Mux AW channel.
localparam logic [9:0] MuxAw = (1 << 4);
/// Slice on Mux W channel.
localparam logic [9:0] MuxW = (1 << 3);
/// Slice on Mux B channel.
localparam logic [9:0] MuxB = (1 << 2);
/// Slice on Mux AR channel.
localparam logic [9:0] MuxAr = (1 << 1);
/// Slice on Mux R channel.
localparam logic [9:0] MuxR = (1 << 0);
/// Latency configuration for `axi_xbar`.
typedef enum logic [9:0] {
NO_LATENCY = 10'b000_00_000_00,
CUT_SLV_AX = DemuxAw | DemuxAr,
CUT_MST_AX = MuxAw | MuxAr,
CUT_ALL_AX = DemuxAw | DemuxAr | MuxAw | MuxAr,
CUT_SLV_PORTS = DemuxAw | DemuxW | DemuxB | DemuxAr | DemuxR,
CUT_MST_PORTS = MuxAw | MuxW | MuxB | MuxAr | MuxR,
CUT_ALL_PORTS = 10'b111_11_111_11
} xbar_latency_e;
/// Configuration for `axi_xbar`.
typedef struct packed {
int unsigned NoSlvPorts;
int unsigned NoMstPorts;
int unsigned MaxMstTrans;
int unsigned MaxSlvTrans;
bit FallThrough;
xbar_latency_e LatencyMode;
int unsigned AxiIdWidthSlvPorts;
int unsigned AxiIdUsedSlvPorts;
bit UniqueIds;
int unsigned AxiAddrWidth;
int unsigned AxiDataWidth;
int unsigned NoAddrRules;
} xbar_cfg_t;
/// Commonly used rule types for `axi_xbar` (64-bit addresses).
typedef struct packed {
int unsigned idx;
logic [63:0] start_addr;
logic [63:0] end_addr;
} xbar_rule_64_t;
/// Commonly used rule types for `axi_xbar` (32-bit addresses).
typedef struct packed {
int unsigned idx;
logic [31:0] start_addr;
logic [31:0] end_addr;
} xbar_rule_32_t;
endpackage