Skip to content

Commit

Permalink
Initial pass at AXI sub -- read channel only
Browse files Browse the repository at this point in the history
  • Loading branch information
calebofearth committed May 31, 2024
1 parent 56b2ade commit d770c8f
Show file tree
Hide file tree
Showing 5 changed files with 1,305 additions and 0 deletions.
235 changes: 235 additions & 0 deletions src/axi/rtl/axi_addr.v
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
155 changes: 155 additions & 0 deletions src/axi/rtl/axi_if.sv
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
Loading

0 comments on commit d770c8f

Please sign in to comment.