-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'chips/cwhitehead-msft-gen2-axi-modules'…
… into user/anjpar/caliptra_ss_fuse_ctrl
- Loading branch information
Showing
9 changed files
with
2,063 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
--- | ||
provides: [axi_sub] | ||
schema_version: 2.4.0 | ||
requires: | ||
- libs | ||
targets: | ||
tb: | ||
directories: [$COMPILE_ROOT/rtl] | ||
files: | ||
- $COMPILE_ROOT/rtl/axi_if.sv | ||
- $COMPILE_ROOT/rtl/axi_pkg.sv | ||
- $COMPILE_ROOT/rtl/axi_addr.v | ||
- $COMPILE_ROOT/rtl/skidbuffer.v | ||
- $COMPILE_ROOT/rtl/axi_sub_rd.sv | ||
rtl: | ||
directories: [$COMPILE_ROOT/rtl] | ||
files: | ||
- $COMPILE_ROOT/rtl/axi_if.sv | ||
- $COMPILE_ROOT/rtl/axi_pkg.sv | ||
- $COMPILE_ROOT/rtl/axi_addr.v | ||
- $COMPILE_ROOT/rtl/skidbuffer.v | ||
- $COMPILE_ROOT/rtl/axi_sub_rd.sv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Oops, something went wrong.