diff --git a/tests/common/axi4_lite.sv b/tests/common/axi4_lite.sv index 750442e..31c6a4d 100644 --- a/tests/common/axi4_lite.sv +++ b/tests/common/axi4_lite.sv @@ -1,5 +1,205 @@ -module axi4_lite( - input wire aclk +// TODO: improve throughput. Currently limited to 1 write every 3 cycles and read every 2 cycles (plus wb slave latency) + +module axi4l_wb_bridge #( + parameter ADDR_WIDTH = 8, + parameter DATA_WIDTH = 32 +)( + // Global + 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; + wdata_valid <= 0; + w_complete <= 0; + raddr_valid <= 0; + 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 + ) 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_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 \ No newline at end of file