cpu/other_projects/axi_lite_memory.v
2021-08-11 00:18:46 -06:00

152 lines
4.9 KiB
Verilog

module axi_lite_memory(
// Global
input ACLK,
input ARESETn,
// Write address
input AWVALID,
input [ADDR_WIDTH-1:0] AWADDR,
input [2:0] AWPROT,
output reg AWREADY,
// Write data
input WVALID,
input [DATA_WIDTH-1:0] WDATA,
input [(DATA_WIDTH/8)-1:0] WSTRB,
output reg WREADY,
// Write response
output reg BVALID,
input BREADY,
output reg [1:0] BRESP,
// Read address
input ARVALID,
input [ADDR_WIDTH-1:0] ARADDR,
input [2:0] ARPROT, // IGNORED
output reg ARREADY,
// Read data
output reg RVALID,
output reg [DATA_WIDTH-1:0] RDATA,
output reg [1:0] RRESP,
input RREADY,
// Wishbone write
output reg [ADDR_WIDTH-1:0] WB_WADDR,
output reg [2:0] WB_WPROT,
output reg [DATA_WIDTH-1:0] WB_WDATA,
output reg [(DATA_WIDTH/8)-1:0] WB_WSTRB,
output reg WB_WVALID = 0,
input WB_WREADY,
// Wishbone read
output reg [ADDR_WIDTH-1:0] WB_RADDR,
input [DATA_WIDTH-1:0] WB_RDATA,
input WB_RVALID,
output reg WB_RREADY
);
parameter DATA_WIDTH = 32; // Only 32 allowed for now (AXI-Lite allows 32 or 64). 64 might work but I haven't investigated it yet.
parameter ADDR_WIDTH = 12; // No minimum requirement. Typically at least 12b (4KB)
parameter SYNC_DEPTH = 1; // Minimum recommended: 2. Larger synchronizer depth allows for larger delay between AXI address and data without stalling.
reg [ADDR_WIDTH-1:0] sync_awaddr [0:SYNC_DEPTH-1];
reg [2:0] sync_awprot [0:SYNC_DEPTH-1];
reg [$clog2(SYNC_DEPTH)+1:0] sync_aw_fill = 0;
reg [DATA_WIDTH-1:0] sync_wdata [0:SYNC_DEPTH-1];
reg [(DATA_WIDTH/8)-1:0] sync_wstrb [0:SYNC_DEPTH-1];
reg [$clog2(SYNC_DEPTH)+1:0] sync_w_fill = 0;
reg [1:0] sync_bresp [0:SYNC_DEPTH-1]; // TODO: make this not be the same sync_depth?
reg [$clog2(SYNC_DEPTH)+1:0] sync_b_fill = 0;
localparam RESP_OKAY = 2'b00,
RESP_EXOKAY = 2'b01,
RESP_SLVERR = 2'b10,
RESP_DECERR = 2'b11;
always @(*) begin
AWREADY = sync_aw_fill < SYNC_DEPTH;
WREADY = sync_w_fill < SYNC_DEPTH;
BRESP = RESP_OKAY; // TODO: add support for responses other than OKAY
BVALID = sync_b_fill > 0;
end
always @(posedge ACLK or negedge ARESETn) begin: clk_update
integer i;
// Write direction
integer event_wb_write;
integer event_aw;
integer event_w;
integer event_b;
// Read direction
integer event_wb_read;
integer event_ar;
integer event_r;
// Write direction
event_wb_write = 0;
event_aw = 0;
event_w = 0;
event_b = 0;
// Read direction
event_wb_read = 0;
event_ar = 0;
event_r = 0;
if (ARESETn == 0) begin
// TODO: deal with reset
sync_aw_fill <= 0;
sync_w_fill <= 0;
sync_b_fill <= 0;
end else begin
if (AWREADY && AWVALID) begin
event_aw = 1;
for (i=0; i<sync_aw_fill; i=i+1) begin
sync_awaddr[i+1] <= sync_awaddr[i];
sync_awprot[i+1] <= sync_awprot[i];
end
sync_awaddr[0] <= AWADDR;
sync_awprot[0] <= AWPROT;
end
if (WREADY && WVALID) begin
event_w = 1;
for (i=0; i<sync_w_fill; i=i+1) begin
sync_wdata[i+1] <= sync_wstrb[i];
sync_wstrb[i+1] <= sync_wstrb[i];
end
sync_wdata[sync_w_fill] <= WDATA;
sync_wstrb[sync_w_fill] <= WSTRB;
end
if (BREADY && BVALID) begin
event_b = 1;
end
if (WB_WREADY || !WB_WVALID) begin
event_wb_write = 1;
if (sync_aw_fill > 0 && sync_w_fill > 0) begin
WB_WVALID <= 1'b1;
WB_WADDR <= sync_awaddr[sync_aw_fill-1];
WB_WPROT <= sync_awprot[sync_aw_fill-1];
WB_WDATA <= sync_wdata[sync_w_fill-1];
WB_WSTRB <= sync_wstrb[sync_w_fill-1];
end else begin
WB_WVALID <= 1'b0;
end
end
sync_aw_fill <= sync_aw_fill + event_aw - event_wb_write;
sync_w_fill <= sync_w_fill + event_w - event_wb_write;
sync_b_fill <= sync_b_fill - event_b + event_wb_write; // TODO: is this right?
end
end
endmodule