236 lines
6.7 KiB
Verilog
236 lines
6.7 KiB
Verilog
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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
|