cpu/lib/axil_wb_bridge.sv

209 lines
6.1 KiB
Systemverilog
Raw Normal View History

// TODO: improve throughput. Currently limited to 1 write every 3 cycles and read every 2 cycles (plus wb slave latency)
2022-12-01 01:13:23 -07:00
module axil_wb_bridge #(
parameter ADDR_WIDTH = 8,
parameter DATA_WIDTH = 32
)(
input logic clk,
input logic reset,
2023-01-23 18:26:14 -07:00
///// AXI4-Lite Slave /////
// Write address
input logic axil_awvalid,
output logic axil_awready,
input logic [ADDR_WIDTH-1:0] axil_awaddr,
input logic [2:0] axil_awprot,
// Write data
input logic axil_wvalid,
output logic axil_wready,
input logic [DATA_WIDTH-1:0] axil_wdata,
input logic [DATA_WIDTH/8 - 1:0] axil_wstrb,
// Write response
output logic axil_bvalid,
input logic axil_bready,
output logic [1:0] axil_bresp,
// Read address
input logic axil_arvalid,
output logic axil_arready,
input logic [ADDR_WIDTH-1:0] axil_araddr,
input logic [2:0] axil_arprot,
// Read data
output logic axil_rvalid,
input logic axil_rready,
output logic [DATA_WIDTH-1:0] axil_rdata,
output logic [1:0] axil_rresp,
///// Wishbone /////
output logic [ADDR_WIDTH-1:0] wb_adr_o,
input logic [DATA_WIDTH-1:0] wb_dat_i,
output logic [DATA_WIDTH-1:0] wb_dat_o,
output logic wb_we_o,
output logic wb_sel_o,
output logic wb_stb_o,
input logic wb_ack_i,
output logic wb_cyc_o
2021-08-11 00:18:46 -06:00
);
localparam AXIL_RESP_OKAY = 2'b00;
// localparam AXIL_RESP_EXOKAY = 2'b01; // Only valid for full AXI, not AXI-Lite
localparam AXIL_RESP_SLVERR = 2'b10;
localparam AXIL_RESP_DECERR = 2'b11; // Indicates there is no slave at the transaction address
localparam STATE_IDLE = 0;
localparam STATE_READ = 1;
localparam STATE_WRITE = 2;
logic [ADDR_WIDTH-1:0] waddr;
logic [ADDR_WIDTH-1:0] raddr;
logic [DATA_WIDTH-1:0] wdata;
logic [DATA_WIDTH-1:0] rdata;
logic waddr_legal;
logic raddr_legal;
logic wdata_legal;
logic waddr_valid = 0;
logic raddr_valid = 0;
logic wdata_valid = 0;
logic rdata_valid = 0;
logic w_complete = 0;
logic [1:0] state_wb = STATE_IDLE;
always_ff @(posedge clk) begin
if (reset) begin
waddr_valid <= 0;
2022-12-07 20:03:13 -07:00
waddr_legal <= 1; // unnecessary
wdata_valid <= 0;
2022-12-07 20:03:13 -07:00
wdata_legal <= 1; // unnecessary
w_complete <= 0;
raddr_valid <= 0;
2022-12-07 20:03:13 -07:00
raddr_legal <= 1; // unnecessary
rdata_valid <= 0;
state_wb <= STATE_IDLE;
end else begin
// axil write address
if (axil_awvalid && axil_awready) begin
waddr <= axil_awaddr;
waddr_valid <= 1;
waddr_legal <= 1;
// TODO: use write protection
end
// axil write data
if (axil_wvalid && axil_wready) begin
wdata <= axil_wdata;
wdata_valid <= 1;
if (&axil_wstrb) begin
// wishbone only supports full width writes
wdata_legal <= 1;
// end else if (!|axil_wstrb) begin
// // TODO: optionally discard and ignore access with axil_wstrb = 4'b0000
end else begin
// invalid write strobe combination
wdata_legal <= 0;
end
end
if (
((axil_wvalid && axil_wready) || wdata_valid) &&
((axil_awvalid && axil_awready) || waddr_valid) &&
!w_complete
// TODO: check for wdata_legal (and combinatorial wdata_legal)
) begin
if (state_wb == STATE_IDLE) begin
state_wb <= STATE_WRITE;
end
end
// axil write response
if (axil_bvalid && axil_bready) begin
// reset write logic for next transfer
waddr_valid <= 0;
wdata_valid <= 0;
w_complete <= 0;
end
// axil read address
if (axil_arvalid && axil_arready) begin
raddr <= axil_araddr;
raddr_valid <= 1;
raddr_legal <= 1;
// TODO: use read protection
if (state_wb == STATE_IDLE) begin
// if write and read arrive on the same cycle, read takes priority
state_wb <= STATE_READ;
end
end
// axil read data
if (axil_rvalid && axil_rready) begin
// read is complete. Get ready for next read
raddr_valid <= 0;
rdata_valid <= 0;
end
// wb
case (state_wb)
STATE_IDLE: begin
// do nothing
end
STATE_WRITE: begin
if (wb_cyc_o && wb_sel_o && wb_stb_o && wb_ack_i) begin
w_complete <= 1;
state_wb <= STATE_IDLE;
// TODO: switch directly to STATE_READ if we can
end
end
STATE_READ: begin
if (wb_cyc_o && wb_sel_o && wb_stb_o && wb_ack_i) begin
rdata <= wb_dat_i;
rdata_valid <= 1;
state_wb <= STATE_IDLE;
// TODO: switch directly to STATE_WRITE if we can
// This will also ensure continuous reads don't inhibit all writes
end
end
default: begin
// bh_assert_equal(state_wb, 0, "Wishbone master encountered unknown state");
state_wb <= STATE_IDLE;
end
endcase
end
end
always_comb begin
// axil write address
axil_awready = !waddr_valid;
// axil write data
axil_wready = !wdata_valid;
// axil write response
axil_bvalid = waddr_valid && wdata_valid && w_complete;
axil_bresp = (waddr_legal && wdata_legal) ? AXIL_RESP_OKAY : AXIL_RESP_SLVERR;
// axil read address
axil_arready = !raddr_valid;
// axil read data
axil_rvalid = rdata_valid;
2022-12-07 20:03:13 -07:00
axil_rdata = rdata;
axil_rresp = raddr_legal ? AXIL_RESP_OKAY : AXIL_RESP_SLVERR;
// wishbone
wb_we_o = state_wb == STATE_WRITE;
wb_dat_o = wdata;
wb_adr_o = (state_wb == STATE_WRITE) ? waddr : raddr;
wb_cyc_o = state_wb == STATE_WRITE || state_wb == STATE_READ;
wb_sel_o = 1; // TODO: figure out if this is right
wb_stb_o = 1; // TODO: figure out if this is right
end
2021-08-11 00:18:46 -06:00
endmodule