diff options
Diffstat (limited to 'rtl/wb2axip/axi_addr.v')
| -rw-r--r-- | rtl/wb2axip/axi_addr.v | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/rtl/wb2axip/axi_addr.v b/rtl/wb2axip/axi_addr.v new file mode 100644 index 0000000..8d8ac75 --- /dev/null +++ b/rtl/wb2axip/axi_addr.v @@ -0,0 +1,235 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: axi_addr.v +// {{{ +// Project: WB2AXIPSP: bus bridges and other odds and ends +// +// Purpose: The AXI (full) standard has some rather complicated addressing +// modes, where the address can either be FIXED, INCRementing, or +// even where it can WRAP around some boundary. When in either INCR or +// WRAP modes, the next address must always be aligned. In WRAP mode, +// the next address calculation needs to wrap around a given value, and +// that value is dependent upon the burst size (i.e. bytes per beat) and +// length (total numbers of beats). Since this calculation can be +// non-trivial, and since it needs to be done multiple times, the logic +// below captures it for every time it might be needed. +// +// 20200918 - modified to accommodate (potential) AXI3 burst lengths +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// Copyright (C) 2019-2024, Gisselquist Technology, LLC +// {{{ +// This file is part of the WB2AXIP project. +// +// The WB2AXIP project contains free software and gateware, licensed under the +// Apache License, Version 2.0 (the "License"). You may not use this project, +// or this file, except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the 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. +// +//////////////////////////////////////////////////////////////////////////////// +// +// +`default_nettype none +// }}} +module axi_addr #( + // {{{ + parameter AW = 32, + DW = 32, + // parameter [0:0] OPT_AXI3 = 1'b0, + localparam LENB = 8 + // }}} + ) ( + // {{{ + input wire [AW-1:0] i_last_addr, + input wire [2:0] i_size, // 1b, 2b, 4b, 8b, etc + input wire [1:0] i_burst, // fixed, incr, wrap, reserved + input wire [LENB-1:0] i_len, + output wire [AW-1:0] o_next_addr + // }}} + ); + + // Parameter/register declarations + // {{{ + localparam DSZ = $clog2(DW)-3; + localparam [1:0] FIXED = 2'b00; + // localparam [1:0] INCREMENT = 2'b01; + // localparam [1:0] WRAP = 2'b10; + localparam IN_AW = (AW >= 12) ? 12 : AW; + localparam [IN_AW-1:0] ONE = 1; + + reg [IN_AW-1:0] wrap_mask, increment; + reg [IN_AW-1:0] crossblk_addr, aligned_addr, unaligned_addr; + // }}} + + // Address increment + // {{{ + always @(*) + if (DSZ == 0) + increment = 1; + else if (DSZ == 1) + increment = (i_size[0]) ? 2 : 1; + else if (DSZ == 2) + increment = (i_size[1]) ? 4 : ((i_size[0]) ? 2 : 1); + else if (DSZ == 3) + case(i_size[1:0]) + 2'b00: increment = 1; + 2'b01: increment = 2; + 2'b10: increment = 4; + 2'b11: increment = 8; + endcase + else + increment = (1<<i_size); + // }}} + + // wrap_mask + // {{{ + // The wrap_mask is used to determine which bits remain stable across + // the burst, and which are allowed to change. It is only used during + // wrapped addressing. + always @(*) + begin + // Start with the default, minimum mask + + /* + // Here's the original code. It works, but it's + // not economical (uses too many LUTs) + // + if (i_len[3:0] == 1) + wrap_mask = (1<<(i_size+1)); + else if (i_len[3:0] == 3) + wrap_mask = (1<<(i_size+2)); + else if (i_len[3:0] == 7) + wrap_mask = (1<<(i_size+3)); + else if (i_len[3:0] == 15) + wrap_mask = (1<<(i_size+4)); + wrap_mask = wrap_mask - 1; + */ + + // Here's what we *want* + // + // wrap_mask[i_size:0] = -1; + // + // On the other hand, since we're already guaranteed that our + // addresses are aligned, do we really care about + // wrap_mask[i_size-1:0] ? + + // What we want: + // + // wrap_mask[i_size+3:i_size] |= i_len[3:0] + // + // We could simplify this to + // + // wrap_mask = wrap_mask | (i_len[3:0] << (i_size)); + // Verilator lint_off WIDTH + if (DSZ < 2) + wrap_mask = ONE | ({{(IN_AW-4){1'b0}},i_len[3:0]} << (i_size[0])); + else if (DSZ < 4) + wrap_mask = ONE | ({{(IN_AW-4){1'b0}},i_len[3:0]} << (i_size[1:0])); + else + wrap_mask = ONE | ({{(IN_AW-4){1'b0}},i_len[3:0]} << (i_size)); + // Verilator lint_on WIDTH + end + // }}} + + // unaligned_addr + always @(*) + unaligned_addr = i_last_addr[IN_AW-1:0] + increment[IN_AW-1:0]; + + // aligned_addr + // {{{ + always @(*) + if (i_burst != FIXED) + begin + // Align subsequent beats in any burst + // {{{ + aligned_addr = unaligned_addr; + // We use the bus size here to simplify the logic + // required in case the bus is smaller than the + // maximum. This depends upon AxSIZE being less than + // $clog2(DATA_WIDTH/8). + if (DSZ < 2) + begin + // {{{ + // Align any subsequent address + if (i_size[0]) + aligned_addr[0] = 0; + // }}} + end else if (DSZ < 4) + begin + // {{{ + // Align any subsequent address + case(i_size[1:0]) + 2'b00: aligned_addr = unaligned_addr; + 2'b01: aligned_addr[ 0] = 0; + 2'b10: aligned_addr[(AW-1>1) ? 1 : (AW-1):0]= 0; + 2'b11: aligned_addr[(AW-1>2) ? 2 : (AW-1):0]= 0; + endcase + // }}} + end else begin + // {{{ + // Align any subsequent address + case(i_size) + 3'b001: aligned_addr[ 0] = 0; + 3'b010: aligned_addr[(AW-1>1) ? 1 : (AW-1):0]=0; + 3'b011: aligned_addr[(AW-1>2) ? 2 : (AW-1):0]=0; + 3'b100: aligned_addr[(AW-1>3) ? 3 : (AW-1):0]=0; + 3'b101: aligned_addr[(AW-1>4) ? 4 : (AW-1):0]=0; + 3'b110: aligned_addr[(AW-1>5) ? 5 : (AW-1):0]=0; + 3'b111: aligned_addr[(AW-1>6) ? 6 : (AW-1):0]=0; + default: aligned_addr = unaligned_addr; + endcase + // }}} + end + // }}} + end else + aligned_addr = i_last_addr[IN_AW-1:0]; + // }}} + + // crossblk_addr from aligned_addr, for WRAP addressing + // {{{ + always @(*) + if (i_burst[1]) + begin + // WRAP! + crossblk_addr[IN_AW-1:0] = (i_last_addr[IN_AW-1:0] & ~wrap_mask) + | (aligned_addr & wrap_mask); + end else + crossblk_addr[IN_AW-1:0] = aligned_addr; + // }}} + + // o_next_addr: Guarantee only the bottom 12 bits change + // {{{ + // This is really a logic simplification. AXI bursts aren't allowed + // to cross 4kB boundaries. Given that's the case, we don't have to + // suffer from the propagation across all AW bits, and can limit any + // address propagation to just the lower 12 bits + generate if (AW > 12) + begin : WIDE_ADDRESS + assign o_next_addr = { i_last_addr[AW-1:12], + crossblk_addr[11:0] }; + end else begin : NARROW_ADDRESS + assign o_next_addr = crossblk_addr[AW-1:0]; + end endgenerate + // }}} + + // Make Verilator happy + // {{{ + // Verilator lint_off UNUSED + wire unused; + assign unused = (LENB <= 4) ? &{1'b0, i_len[0] } + : &{ 1'b0, i_len[LENB-1:4], i_len[0] }; + // Verilator lint_on UNUSED + // }}} +endmodule |
