summaryrefslogtreecommitdiff
path: root/rtl/wb2axip/axi_addr.v
diff options
context:
space:
mode:
Diffstat (limited to 'rtl/wb2axip/axi_addr.v')
-rw-r--r--rtl/wb2axip/axi_addr.v235
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