mirror of
https://gitlab.com/brendanhaines/cpu.git
synced 2024-12-27 11:36:47 -07:00
209 lines
6.1 KiB
Systemverilog
209 lines
6.1 KiB
Systemverilog
// TODO: improve throughput. Currently limited to 1 write every 3 cycles and read every 2 cycles (plus wb slave latency)
|
|
|
|
module axil_wb_bridge #(
|
|
parameter ADDR_WIDTH = 8,
|
|
parameter DATA_WIDTH = 32
|
|
)(
|
|
input logic clk,
|
|
input logic reset,
|
|
|
|
///// AXI4-Lite /////
|
|
|
|
// 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
|
|
);
|
|
|
|
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;
|
|
waddr_legal <= 1; // unnecessary
|
|
wdata_valid <= 0;
|
|
wdata_legal <= 1; // unnecessary
|
|
w_complete <= 0;
|
|
raddr_valid <= 0;
|
|
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;
|
|
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
|
|
|
|
endmodule |