// 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 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 ); 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