axi: Add AXI RAM interface and AXI dual port RAM

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2026-03-19 18:12:37 -07:00
parent 9a352ae302
commit 5652bb0016
10 changed files with 1399 additions and 0 deletions

View File

@@ -0,0 +1,244 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2019-2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* AXI4 dual-port RAM
*/
module taxi_axi_dp_ram #
(
// Width of address bus in bits
parameter ADDR_W = 16,
// Extra pipeline register on output port A
parameter logic A_PIPELINE_OUTPUT = 1'b0,
// Extra pipeline register on output port B
parameter logic B_PIPELINE_OUTPUT = 1'b0,
// Interleave read and write burst cycles on port A
parameter logic A_INTERLEAVE = 1'b0,
// Interleave read and write burst cycles on port B
parameter logic B_INTERLEAVE = 1'b0
)
(
/*
* Port A
*/
input wire logic a_clk,
input wire logic a_rst,
taxi_axi_if.wr_slv s_axi_wr_a,
taxi_axi_if.rd_slv s_axi_rd_a,
/*
* Port B
*/
input wire logic b_clk,
input wire logic b_rst,
taxi_axi_if.wr_slv s_axi_wr_b,
taxi_axi_if.rd_slv s_axi_rd_b
);
// extract parameters
localparam DATA_W = s_axi_wr_a.DATA_W;
localparam STRB_W = s_axi_wr_a.STRB_W;
localparam A_ID_W = s_axi_wr_a.ID_W;
localparam B_ID_W = s_axi_wr_b.ID_W;
localparam VALID_ADDR_W = ADDR_W - $clog2(STRB_W);
localparam BYTE_LANES = STRB_W;
localparam BYTE_W = DATA_W/BYTE_LANES;
// check configuration
if (BYTE_W * STRB_W != DATA_W)
$fatal(0, "Error: AXI data width not evenly divisible (instance %m)");
if (2**$clog2(BYTE_LANES) != BYTE_LANES)
$fatal(0, "Error: AXI word width must be even power of two (instance %m)");
wire [A_ID_W-1:0] ram_a_cmd_id;
wire [ADDR_W-1:0] ram_a_cmd_addr;
wire [DATA_W-1:0] ram_a_cmd_wr_data;
wire [STRB_W-1:0] ram_a_cmd_wr_strb;
wire ram_a_cmd_wr_en;
wire ram_a_cmd_rd_en;
wire ram_a_cmd_last;
wire ram_a_cmd_ready;
logic [A_ID_W-1:0] ram_a_rd_resp_id_reg = 'd0;
logic [DATA_W-1:0] ram_a_rd_resp_data_reg = 'd0;
logic ram_a_rd_resp_last_reg = 1'b0;
logic ram_a_rd_resp_valid_reg = 1'b0;
wire ram_a_rd_resp_ready;
wire [B_ID_W-1:0] ram_b_cmd_id;
wire [ADDR_W-1:0] ram_b_cmd_addr;
wire [DATA_W-1:0] ram_b_cmd_wr_data;
wire [STRB_W-1:0] ram_b_cmd_wr_strb;
wire ram_b_cmd_wr_en;
wire ram_b_cmd_rd_en;
wire ram_b_cmd_last;
wire ram_b_cmd_ready;
logic [B_ID_W-1:0] ram_b_rd_resp_id_reg = 'd0;
logic [DATA_W-1:0] ram_b_rd_resp_data_reg = 'd0;
logic ram_b_rd_resp_last_reg = 1'b0;
logic ram_b_rd_resp_valid_reg = 1'b0;
wire ram_b_rd_resp_ready;
taxi_axi_ram_if_rdwr #(
.DATA_W(DATA_W),
.ADDR_W(ADDR_W),
.STRB_W(STRB_W),
.ID_W(A_ID_W),
.AUSER_W(s_axi_wr_a.AWUSER_W > s_axi_rd_a.ARUSER_W ? s_axi_wr_a.AWUSER_W : s_axi_rd_a.ARUSER_W),
.WUSER_W(s_axi_wr_a.WUSER_W),
.RUSER_W(s_axi_rd_a.RUSER_W),
.PIPELINE_OUTPUT(A_PIPELINE_OUTPUT),
.INTERLEAVE(A_INTERLEAVE)
)
a_if (
.clk(a_clk),
.rst(a_rst),
/*
* AXI4 slave interface
*/
.s_axi_wr(s_axi_wr_a),
.s_axi_rd(s_axi_rd_a),
/*
* RAM interface
*/
.ram_cmd_id(ram_a_cmd_id),
.ram_cmd_addr(ram_a_cmd_addr),
.ram_cmd_lock(),
.ram_cmd_cache(),
.ram_cmd_prot(),
.ram_cmd_qos(),
.ram_cmd_region(),
.ram_cmd_auser(),
.ram_cmd_wr_data(ram_a_cmd_wr_data),
.ram_cmd_wr_strb(ram_a_cmd_wr_strb),
.ram_cmd_wr_user(),
.ram_cmd_wr_en(ram_a_cmd_wr_en),
.ram_cmd_rd_en(ram_a_cmd_rd_en),
.ram_cmd_last(ram_a_cmd_last),
.ram_cmd_ready(ram_a_cmd_ready),
.ram_rd_resp_id(ram_a_rd_resp_id_reg),
.ram_rd_resp_data(ram_a_rd_resp_data_reg),
.ram_rd_resp_last(ram_a_rd_resp_last_reg),
.ram_rd_resp_user('0),
.ram_rd_resp_valid(ram_a_rd_resp_valid_reg),
.ram_rd_resp_ready(ram_a_rd_resp_ready)
);
taxi_axi_ram_if_rdwr #(
.DATA_W(DATA_W),
.ADDR_W(ADDR_W),
.STRB_W(STRB_W),
.ID_W(B_ID_W),
.AUSER_W(s_axi_wr_b.AWUSER_W > s_axi_rd_b.ARUSER_W ? s_axi_wr_b.AWUSER_W : s_axi_rd_b.ARUSER_W),
.WUSER_W(s_axi_wr_b.WUSER_W),
.RUSER_W(s_axi_rd_b.RUSER_W),
.PIPELINE_OUTPUT(B_PIPELINE_OUTPUT),
.INTERLEAVE(B_INTERLEAVE)
)
b_if (
.clk(b_clk),
.rst(b_rst),
/*
* AXI4 slave interface
*/
.s_axi_wr(s_axi_wr_b),
.s_axi_rd(s_axi_rd_b),
/*
* RAM interface
*/
.ram_cmd_id(ram_b_cmd_id),
.ram_cmd_addr(ram_b_cmd_addr),
.ram_cmd_lock(),
.ram_cmd_cache(),
.ram_cmd_prot(),
.ram_cmd_qos(),
.ram_cmd_region(),
.ram_cmd_auser(),
.ram_cmd_wr_data(ram_b_cmd_wr_data),
.ram_cmd_wr_strb(ram_b_cmd_wr_strb),
.ram_cmd_wr_user(),
.ram_cmd_wr_en(ram_b_cmd_wr_en),
.ram_cmd_rd_en(ram_b_cmd_rd_en),
.ram_cmd_last(ram_b_cmd_last),
.ram_cmd_ready(ram_b_cmd_ready),
.ram_rd_resp_id(ram_b_rd_resp_id_reg),
.ram_rd_resp_data(ram_b_rd_resp_data_reg),
.ram_rd_resp_last(ram_b_rd_resp_last_reg),
.ram_rd_resp_user('0),
.ram_rd_resp_valid(ram_b_rd_resp_valid_reg),
.ram_rd_resp_ready(ram_b_rd_resp_ready)
);
// verilator lint_off MULTIDRIVEN
// (* RAM_STYLE="BLOCK" *)
logic [DATA_W-1:0] mem[2**VALID_ADDR_W] = '{default: '0};
// verilator lint_on MULTIDRIVEN
wire [VALID_ADDR_W-1:0] addr_a_valid = VALID_ADDR_W'(ram_a_cmd_addr >> (ADDR_W - VALID_ADDR_W));
wire [VALID_ADDR_W-1:0] addr_b_valid = VALID_ADDR_W'(ram_b_cmd_addr >> (ADDR_W - VALID_ADDR_W));
assign ram_a_cmd_ready = !ram_a_rd_resp_valid_reg || ram_a_rd_resp_ready;
always_ff @(posedge a_clk) begin
ram_a_rd_resp_valid_reg <= ram_a_rd_resp_valid_reg && !ram_a_rd_resp_ready;
if (ram_a_cmd_rd_en && ram_a_cmd_ready) begin
ram_a_rd_resp_id_reg <= ram_a_cmd_id;
ram_a_rd_resp_data_reg <= mem[addr_a_valid];
ram_a_rd_resp_last_reg <= ram_a_cmd_last;
ram_a_rd_resp_valid_reg <= 1'b1;
end else if (ram_a_cmd_wr_en && ram_a_cmd_ready) begin
for (integer i = 0; i < BYTE_LANES; i = i + 1) begin
if (ram_a_cmd_wr_strb[i]) begin
mem[addr_a_valid][BYTE_W*i +: BYTE_W] <= ram_a_cmd_wr_data[BYTE_W*i +: BYTE_W];
end
end
end
if (a_rst) begin
ram_a_rd_resp_valid_reg <= 1'b0;
end
end
assign ram_b_cmd_ready = !ram_b_rd_resp_valid_reg || ram_b_rd_resp_ready;
always_ff @(posedge b_clk) begin
ram_b_rd_resp_valid_reg <= ram_b_rd_resp_valid_reg && !ram_b_rd_resp_ready;
if (ram_b_cmd_rd_en && ram_b_cmd_ready) begin
ram_b_rd_resp_id_reg <= ram_b_cmd_id;
ram_b_rd_resp_data_reg <= mem[addr_b_valid];
ram_b_rd_resp_last_reg <= ram_b_cmd_last;
ram_b_rd_resp_valid_reg <= 1'b1;
end else if (ram_b_cmd_wr_en && ram_b_cmd_ready) begin
for (integer i = 0; i < BYTE_LANES; i = i + 1) begin
if (ram_b_cmd_wr_strb[i]) begin
mem[addr_b_valid][BYTE_W*i +: BYTE_W] <= ram_b_cmd_wr_data[BYTE_W*i +: BYTE_W];
end
end
end
if (b_rst) begin
ram_b_rd_resp_valid_reg <= 1'b0;
end
end
endmodule
`resetall