// 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