Skip to content

Commit

Permalink
Add AXI sub wrapper and simplex arbiter
Browse files Browse the repository at this point in the history
  • Loading branch information
calebofearth committed Jul 25, 2024
1 parent b481ac2 commit eaca0f7
Show file tree
Hide file tree
Showing 2 changed files with 331 additions and 0 deletions.
196 changes: 196 additions & 0 deletions src/axi/rtl/axi_sub.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// 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.
//

// -------------------------------------------------------------
// AXI Subordinate
// -------------------------------------------------------------
// Description:
// Subordinate to convert AXI protocol transactions into internal component accesses
// Includes an arbiter to squash duplex AXI transactions into simplex component operations
// May optionally include an Exclusive Access monitor (AxLOCK signal)
//
// Limitations:
// - When multiple ID tracking is enabled, write responses are returned in the
// same order they are received, regardless of ID.
//
// -------------------------------------------------------------

module axi_sub import axi_pkg::*; #(
parameter AW = 32, // Address Width
parameter DW = 32, // Data Width
BC = DW/8, // Byte Count
BW = $clog2(BC), // Byte count Width
parameter UW = 32, // User Width
parameter IW = 1, // ID Width
ID_NUM = 1 << IW, // Don't override

parameter EX_EN = 0, // Enable exclusive access tracking w/ AxLOCK
parameter C_LAT = 0 // Component latency in clock cycles from (dv&&!hld) -> rdata
// Must be const per component
// For registers, typically 0
// For SRAM, 1 or more
) (
input clk,
input rst_n,

// AXI INF
axi_if.w_sub s_axi_w_if,
axi_if.r_sub s_axi_r_if,

//COMPONENT INF
output logic dv,
output logic [AW-1:0] addr, // Byte address
output logic [UW-1:0] user,
output logic [DW-1:0] wdata, // Requires: Component dwidth == AXI dwidth
output logic [BC-1:0] wstrb, // Requires: Component dwidth == AXI dwidth
input logic [DW-1:0] rdata, // Requires: Component dwidth == AXI dwidth
output logic last, // Asserted with final 'dv' of a burst
input logic hld,
input logic err

);

// Exclusive Access Signals
logic [ID_NUM-1:0] ex_clr;
logic [ID_NUM-1:0] ex_active;
axi_ex_ctx_t [ID_NUM-1:0] ex_ctx;

//Read Subordinate INF
logic r_dv;
logic [AW-1:0] r_addr; // Byte address
logic [UW-1:0] r_user;
logic r_last; // Asserted with final 'dv' of a burst
logic r_hld;
logic r_err;

logic [DW-1:0] r_rdata; // Requires: Component dwidth == AXI dwidth

//Write Subordinate INF
logic w_dv;
logic [AW-1:0] w_addr; // Byte address
logic [UW-1:0] w_user;
logic [DW-1:0] w_wdata; // Requires: Component dwidth == AXI dwidth
logic [BC-1:0] w_wstrb; // Requires: Component dwidth == AXI dwidth
logic w_last; // Asserted with final 'dv' of a burst
logic w_hld;
logic w_err;


axi_sub_wr #(
.AW (AW ),
.DW (DW ),
.UW (UW ),
.IW (IW ),

.EX_EN(EX_EN)
) i_axi_sub_wr (
.clk (clk ),
.rst_n(rst_n),

// AXI INF
.s_axi_if(s_axi_w_if),

// Exclusive Access Signals
.ex_clr (ex_clr ),
.ex_active(ex_active),
.ex_ctx (ex_ctx ),

//COMPONENT INF
.dv (w_dv ),
.addr (w_addr ),
.user (w_user ),
.wdata(w_wdata),
.wstrb(w_wstrb),
.last (w_last ),
.hld (w_hld ),
.err (w_err )

);

axi_sub_rd #(
.AW(AW),
.DW(DW),
.UW(UW),
.IW(IW),

.EX_EN(EX_EN),
.C_LAT(C_LAT)
) i_axi_sub_rd (
.clk (clk ),
.rst_n(rst_n),

// AXI INF
.s_axi_if(s_axi_r_if),

// Exclusive Access Signals
.ex_clr (ex_clr ),
.ex_active(ex_active),
.ex_ctx (ex_ctx ),

//COMPONENT INF
.dv (r_dv ),
.addr (r_addr ),
.user (r_user ),
.last (r_last ),
.hld (r_hld ),
.err (r_err ),

.rdata(r_rdata)
);

axi_sub_arb #(
.AW(AW),
.DW(DW),
.UW(UW),
.IW(IW),

.EX_EN(EX_EN),
.C_LAT(C_LAT)
) i_axi_sub_arb (
.clk (clk ),
.rst_n (rst_n ),

//Read Subordinate INF
.r_dv (r_dv ),
.r_addr (r_addr ),
.r_user (r_user ),
.r_last (r_last ),
.r_hld (r_hld ),
.r_err (r_err ),
.r_rdata(r_rdata),

//Write Subordinate INF
.w_dv (w_dv ),
.w_addr (w_addr ),
.w_user (w_user ),
.w_wdata(w_wdata),
.w_wstrb(w_wstrb),
.w_last (w_last ),
.w_hld (w_hld ),
.w_err (w_err ),

//COMPONENT INF
.dv (dv ),
.addr (addr ),
.user (user ),
.wdata (wdata ),
.wstrb (wstrb ),
.last (last ),
.hld (hld ),
.err (err ),
.rdata (rdata )
);

endmodule
135 changes: 135 additions & 0 deletions src/axi/rtl/axi_sub_arb.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// 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.
//

// -------------------------------------------------------------
// AXI Subordinate Arbiter
// -------------------------------------------------------------
// Description:
// Arbitrate between Reads and Writes coming from AXI subordinate modules.
// Always give precedence to Writes.
//
// -------------------------------------------------------------

module axi_sub_arb import axi_pkg::*; #(
parameter AW = 32, // Address Width
parameter DW = 32, // Data Width
BC = DW/8, // Byte Count
BW = $clog2(BC), // Byte count Width
parameter UW = 32, // User Width
parameter IW = 1, // ID Width
ID_NUM = 1 << IW, // Don't override

parameter EX_EN = 0, // Enable exclusive access tracking w/ AxLOCK
parameter C_LAT = 0 // Component latency in clock cycles from (dv&&!hld) -> rdata
// Must be const per component
// For registers, typically 0
// For SRAM, 1 or more
) (
input clk,
input rst_n,

//Read Subordinate INF
input logic r_dv,
input logic [AW-1:0] r_addr, // Byte address
input logic [UW-1:0] r_user,
input logic r_last, // Asserted with final 'dv' of a burst
output logic r_hld,
output logic r_err,

output logic [DW-1:0] r_rdata, // Requires: Component dwidth == AXI dwidth

//Write Subordinate INF
input logic w_dv,
input logic [AW-1:0] w_addr, // Byte address
input logic [UW-1:0] w_user,
input logic [DW-1:0] w_wdata, // Requires: Component dwidth == AXI dwidth
input logic [BC-1:0] w_wstrb, // Requires: Component dwidth == AXI dwidth
input logic w_last, // Asserted with final 'dv' of a burst
output logic w_hld,
output logic w_err,

//COMPONENT INF
output logic dv,
output logic [AW-1:0] addr, // Byte address
output logic [UW-1:0] user,
output logic [DW-1:0] wdata, // Requires: Component dwidth == AXI dwidth
output logic [BC-1:0] wstrb, // Requires: Component dwidth == AXI dwidth
output logic last, // Asserted with final 'dv' of a burst
input logic hld,
input logic err, // FIXME Does this assert with dv or with rdata for reads (when C_LAT > 0)?

input logic [DW-1:0] rdata // Requires: Component dwidth == AXI dwidth
);

logic r_pri; // Priority to reads
logic r_win;

// Switch priority to current arb winner so that priority persists
// in case
// a) it was granted during a hold
// b) it was granted at start of multi-beat burst
// Otherwise, always give priority to other channel at end of a burst
// to arbitrate fairly
always_ff@(posedge clk or negedge rst_n) begin
if (!rst_n)
r_pri <= 1'b0;
// Toggle priority at end of burst
else if (w_dv && !w_hld && w_last)
r_pri <= 1'b1;
else if (r_dv && !r_hld && r_last)
r_pri <= 1'b0;
// Keep priority when burst starts
else if (w_dv && !r_win && !w_last)
r_pri <= 1'b0;
else if (r_dv && r_win && !r_last)
r_pri <= 1'b1;
end

always_comb begin
// case ({r_pri,r_dv,w_dv}) inside
// 3'b000: r_win = 0;
// 3'b001: r_win = 0;
// 3'b010: r_win = 1;
// 3'b011: r_win = 0;
// 3'b100: r_win = 1;
// 3'b101: r_win = 0;
// 3'b110: r_win = 1;
// 3'b111: r_win = 1;
// endcase
if (r_pri) r_win = r_dv || !w_dv;
else r_win = w_dv || !r_dv;
end

always_comb begin
dv = r_dv || w_dv;
addr = r_win ? r_addr : w_addr;
user = r_win ? r_user : w_user;
last = r_win ? r_last : w_last;
r_hld = hld || !r_win;
w_hld = hld || r_win;
r_err = err;
w_err = err;
wdata = w_wdata;
wstrb = w_wstrb;
r_rdata = rdata;
end

`CALIPTRA_ASSERT_NEVER(AXI_SUB_ARB_CONFLICT, r_dv && !r_hld && w_dv && !w_hld, clk, !rst_n)
// This arbiter can't deal with an err signal that asserts at
// a delay from the dv signal (as in the case of the read channel).
`CALIPTRA_ASSERT(AXI_SUB_ARB_LAT_ERR, C_LAT == 0, clk, !rst_n)


endmodule

0 comments on commit eaca0f7

Please sign in to comment.