-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial pass at AXI sub -- read channel only
- Loading branch information
1 parent
56b2ade
commit d770c8f
Showing
5 changed files
with
1,305 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,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 |
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,155 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use 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. | ||
// | ||
// Description: | ||
// Signals for a standard AXI4 compliant interface | ||
// | ||
|
||
interface axi_if #(parameter AW = 32, parameter DW = 32, parameter IW = 3, parameter UW = 32); | ||
|
||
import axi_pkg::*; | ||
|
||
// AXI AR | ||
logic [AW-1:0] araddr; | ||
logic [1:0] arburst; | ||
logic [2:0] arsize; | ||
logic [7:0] arlen; | ||
logic [UW-1:0] aruser; | ||
logic [IW-1:0] arid; | ||
logic arlock; | ||
logic arvalid; | ||
logic arready; | ||
|
||
// AXI R | ||
logic [DW-1:0] rdata; | ||
logic [1:0] rresp; | ||
logic [IW-1:0] rid; | ||
logic rlast; | ||
logic rvalid; | ||
logic rready; | ||
|
||
// AXI AW | ||
logic [AW-1:0] awaddr; | ||
logic [1:0] awburst; | ||
logic [2:0] awsize; | ||
logic [7:0] awlen; | ||
logic [UW-1:0] awuser; | ||
logic [IW-1:0] awid; | ||
logic awlock; | ||
logic awvalid; | ||
logic awready; | ||
|
||
// AXI W | ||
logic [DW-1:0] wdata; | ||
logic [DW/8-1:0] wstrb; | ||
logic wvalid; | ||
logic wready; | ||
|
||
// AXI B | ||
logic [1:0] bresp; | ||
logic [IW-1:0] bid; | ||
logic bvalid; | ||
logic bready; | ||
|
||
// Modport for read manager | ||
modport r_mgr ( | ||
// AR | ||
output araddr, | ||
output arburst, | ||
output arsize, | ||
output arlen, | ||
output aruser, | ||
output arid, | ||
output arlock, | ||
output arvalid, | ||
input arready, | ||
// R | ||
input rdata, | ||
input rresp, | ||
input rid, | ||
input rlast, | ||
input rvalid, | ||
output rready, | ||
); | ||
|
||
// Modport for write manager | ||
modport w_mgr ( | ||
// AW | ||
output awaddr, | ||
output awburst, | ||
output awsize, | ||
output awlen, | ||
output awuser, | ||
output awid, | ||
output awlock, | ||
output awvalid, | ||
input awready, | ||
// W | ||
output wdata, | ||
output wstrb, | ||
output wvalid, | ||
input wready, | ||
// B | ||
input bresp, | ||
input bid, | ||
input bvalid, | ||
output bready | ||
); | ||
|
||
// Modport for read subordinate | ||
modport r_sub ( | ||
// AR | ||
input araddr, | ||
input arburst, | ||
input arsize, | ||
input arlen, | ||
input aruser, | ||
input arid, | ||
input arlock, | ||
input arvalid, | ||
output arready, | ||
// R | ||
output rdata, | ||
output rresp, | ||
output rid, | ||
output rlast, | ||
output rvalid, | ||
input rready, | ||
); | ||
|
||
// Modport for write subordinate | ||
modport w_sub ( | ||
// AW | ||
input awaddr, | ||
input awburst, | ||
input awsize, | ||
input awlen, | ||
input awuser, | ||
input awid, | ||
input awlock, | ||
input awvalid, | ||
output awready, | ||
// W | ||
input wdata, | ||
input wstrb, | ||
input wvalid, | ||
output wready, | ||
// B | ||
output bresp, | ||
output bid, | ||
output bvalid, | ||
input bready | ||
); | ||
|
||
endinterface |
Oops, something went wrong.