// // Copyright 2016-2017 Ettus Research // Copyright 2018 Ettus Research, a National Instruments Company // // SPDX-License-Identifier: LGPL-3.0-or-later // // An AXI4-Lite read/write register port adapter // // Converts memory mapped flow controlled AXI4-Lite transactions into a much // simpler non flow controlled write and read register bus. // // WRITE Transaction: // - Transaction completes in one cycle // - Valid, Strobe, Address and Data asserted in same cycle // __ __ __ __ // clk __| |__| |__| |__| |__ // _____ // reg_wr_req ________| |___________ // _____ // reg_wr_keep XXXXXXXX|_____|XXXXXXXXXXX // _____ // reg_wr_addr XXXXXXXX|_____|XXXXXXXXXXX // _____ // reg_wr_data XXXXXXXX|_____|XXXXXXXXXXX //; // READ Transaction: // - Transaction request completes in one cycle, with valid and address assertion // - Transaction response must complete in at least one cycle with resp and data // - resp must be asserted between 1 and pow(2, TIMEOUT) cycles otherwise the read will timeout // __ __ __ __ __ // clk __| |__| |__| |__| |__| |__ // _____ // reg_rd_req ________| |_________________ // _____ // reg_rd_addr XXXXXXXX|_____|XXXXXXXXXXXXXXXXX // _____ // reg_rd_resp ____________________| |_____ // _____ // reg_rd_data XXXXXXXXXXXXXXXXXXXX|_____|XXXXX module axil_regport_master #( parameter DWIDTH = 32, // Width of the AXI4-Lite data bus (must be 32 or 64) parameter AWIDTH = 32, // Width of the address bus parameter WRBASE = 32'h0, // Write address base parameter RDBASE = 32'h0, // Read address base parameter TIMEOUT = 10 // log2(timeout). Read will timeout after (2^TIMEOUT - 1) cycles )( // Clock and reset input s_axi_aclk, input s_axi_aresetn, input reg_clk, // AXI4-Lite: Write address port (domain: s_axi_aclk) input [AWIDTH-1:0] s_axi_awaddr, input s_axi_awvalid, output reg s_axi_awready, // AXI4-Lite: Write data port (domain: s_axi_aclk) input [DWIDTH-1:0] s_axi_wdata, input [DWIDTH/8-1:0] s_axi_wstrb, input s_axi_wvalid, output reg s_axi_wready, // AXI4-Lite: Write response port (domain: s_axi_aclk) output reg [1:0] s_axi_bresp, output reg s_axi_bvalid, input s_axi_bready, // AXI4-Lite: Read address port (domain: s_axi_aclk) input [AWIDTH-1:0] s_axi_araddr, input s_axi_arvalid, output reg s_axi_arready, // AXI4-Lite: Read data port (domain: s_axi_aclk) output reg [DWIDTH-1:0] s_axi_rdata, output reg [1:0] s_axi_rresp, output reg s_axi_rvalid, input s_axi_rready, // Register port: Write port (domain: reg_clk) output reg_wr_req, output [AWIDTH-1:0] reg_wr_addr, output [DWIDTH-1:0] reg_wr_data, output [DWIDTH/8-1:0] reg_wr_keep, // Register port: Read port (domain: reg_clk) output reg_rd_req, output [AWIDTH-1:0] reg_rd_addr, input reg_rd_resp, input [DWIDTH-1:0] reg_rd_data ); //NOTE: clog2 only works when assigned to a parameter // localparam does not work parameter ADDR_LSB = $clog2(DWIDTH/8); //Do not modify //---------------------------------------------------------- // Write state machine //---------------------------------------------------------- reg [AWIDTH-1:0] wr_addr_cache; wire wr_fifo_valid, wr_fifo_ready; wire [AWIDTH-1:0] wr_addr_rel = (s_axi_awaddr - WRBASE); // Generate s_axi_awready and latch write address always @(posedge s_axi_aclk) begin if (!s_axi_aresetn) begin s_axi_awready <= 1'b0; wr_addr_cache <= {AWIDTH{1'b0}}; end else begin if (~s_axi_awready && s_axi_awvalid && s_axi_wvalid && wr_fifo_ready) begin s_axi_awready <= 1'b1; wr_addr_cache <= {wr_addr_rel[AWIDTH-1:ADDR_LSB], {ADDR_LSB{1'b0}}}; end else begin s_axi_awready <= 1'b0; end end end // Generate s_axi_wready always @(posedge s_axi_aclk) begin if (!s_axi_aresetn) begin s_axi_wready <= 1'b0; end else begin if (~s_axi_wready && s_axi_wvalid && s_axi_awvalid) s_axi_wready <= 1'b1; else s_axi_wready <= 1'b0; end end // Generate write response assign wr_fifo_valid = s_axi_awready && s_axi_awvalid && s_axi_wready && s_axi_wvalid && ~s_axi_bvalid; always @(posedge s_axi_aclk) begin if (!s_axi_aresetn) begin s_axi_bvalid <= 1'b0; s_axi_bresp <= 2'b0; end else begin if (wr_fifo_valid && wr_fifo_ready) begin // indicates a valid write response is available s_axi_bvalid <= 1'b1; s_axi_bresp <= 2'b0; // 'OKAY' response end else begin if (s_axi_bready && s_axi_bvalid) s_axi_bvalid <= 1'b0; end end end axi_fifo_2clk #( .WIDTH(DWIDTH/8 + AWIDTH + DWIDTH), .SIZE(0) ) wr_fifo_2clk_i ( .reset(~s_axi_aresetn), .i_aclk(s_axi_aclk), .i_tdata({s_axi_wstrb, wr_addr_cache, s_axi_wdata}), .i_tvalid(wr_fifo_valid), .i_tready(wr_fifo_ready), .o_aclk(reg_clk), .o_tdata({reg_wr_keep, reg_wr_addr, reg_wr_data}), .o_tvalid(reg_wr_req), .o_tready(1'b1) ); //---------------------------------------------------------- // Read state machine //---------------------------------------------------------- reg [TIMEOUT-1:0] read_pending_ctr = {TIMEOUT{1'b0}}; wire read_timed_out = (read_pending_ctr == {{(TIMEOUT-1){1'b0}}, 1'b1}); wire read_pending = (read_pending_ctr != {TIMEOUT{1'b0}}); wire [AWIDTH-1:0] rd_addr_rel = (s_axi_araddr - RDBASE); wire rdreq_fifo_ready, rdresp_fifo_valid; wire [DWIDTH-1:0] rdresp_fifo_data; // Generate s_axi_arready and latch read address always @(posedge s_axi_aclk) begin if (!s_axi_aresetn) begin s_axi_arready <= 1'b0; read_pending_ctr <= {TIMEOUT{1'b0}}; end else begin if (~s_axi_arready && s_axi_arvalid && rdreq_fifo_ready) begin s_axi_arready <= 1'b1; read_pending_ctr <= {TIMEOUT{1'b1}}; end else begin s_axi_arready <= 1'b0; end if (read_pending) begin if (rdresp_fifo_valid && ~s_axi_rvalid) read_pending_ctr <= {TIMEOUT{1'b0}}; else read_pending_ctr <= read_pending_ctr - 1'b1; end end end // Perform read transaction always @(posedge s_axi_aclk) begin if (!s_axi_aresetn) begin s_axi_rvalid <= 1'b0; s_axi_rresp <= 2'b00; s_axi_rdata <= 0; end else begin if (read_pending && rdresp_fifo_valid && ~s_axi_rvalid) begin // Valid read data is available at the read data bus s_axi_rvalid <= 1'b1; s_axi_rresp <= 2'b00; // 'OKAY' response s_axi_rdata <= rdresp_fifo_data; end else if (read_pending && read_timed_out && ~s_axi_rvalid) begin // Read timed out. Assert error. s_axi_rvalid <= 1'b1; s_axi_rresp <= 2'b10; // 'SLVERR' response s_axi_rdata <= {DWIDTH{1'b1}}; end else if (s_axi_rvalid && s_axi_rready) begin // Read data is accepted by the master s_axi_rvalid <= 1'b0; end end end axi_fifo_2clk #( .WIDTH(AWIDTH), .SIZE(0) ) readreq_fifo_2clk_i ( .reset(~s_axi_aresetn), .i_aclk(s_axi_aclk), .i_tdata({rd_addr_rel[AWIDTH-1:ADDR_LSB], {ADDR_LSB{1'b0}}}), .i_tvalid(s_axi_arready && s_axi_arvalid), .i_tready(rdreq_fifo_ready), .o_aclk(reg_clk), .o_tdata(reg_rd_addr), .o_tvalid(reg_rd_req), .o_tready(1'b1) ); axi_fifo_2clk #( .WIDTH(DWIDTH), .SIZE(0) ) rdresp_fifo_2clk_i ( .reset(~s_axi_aresetn), .i_aclk(reg_clk), .i_tdata(reg_rd_data), .i_tvalid(reg_rd_resp), .i_tready(/* lossy */), .o_aclk(s_axi_aclk), .o_tdata(rdresp_fifo_data), .o_tvalid(rdresp_fifo_valid), .o_tready(~read_pending || (s_axi_rvalid && (s_axi_rresp == 2'b00))) ); endmodule