// Copyright 2018 ETH Zurich and University of Bologna. // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law // or agreed to in writing, software, hardware and materials distributed under // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // // ---------------------------- // AXI to SRAM Adapter // ---------------------------- // Author: Florian Zaruba (zarubaf@iis.ee.ethz.ch) // // Description: Manages AXI transactions // Supports all burst accesses but only on aligned addresses and with full data width. // Assertions should guide you if there is something unsupported happening. // module axi2sram_sp_external #( parameter AXI_ID_WIDTH = 5, parameter AXI_ADDR_WIDTH = 32, parameter AXI_DATA_WIDTH = 32, parameter FAST_READ_BURST = 1 )( input clk, input resetn, input [AXI_ADDR_WIDTH-1:0] s_araddr , input [1 :0] s_arburst, input [3 :0] s_arcache, input [AXI_ID_WIDTH-1 :0] s_arid , input [7 :0] s_arlen , input s_arlock , input [2 :0] s_arprot , output reg s_arready, input [2 :0] s_arsize , input s_arvalid, input [AXI_ADDR_WIDTH-1:0] s_awaddr , input [1 :0] s_awburst, input [3 :0] s_awcache, input [AXI_ID_WIDTH-1 :0] s_awid , input [7 :0] s_awlen , input s_awlock , input [2 :0] s_awprot , output reg s_awready, input [2 :0] s_awsize , input s_awvalid, output reg [AXI_ID_WIDTH-1 :0] s_bid , input s_bready , output reg [1 :0] s_bresp , output reg s_bvalid , output reg [AXI_DATA_WIDTH-1:0] s_rdata , output reg [AXI_ID_WIDTH-1 :0] s_rid , output reg s_rlast , input s_rready , output reg [1 :0] s_rresp , output reg s_rvalid , input [AXI_DATA_WIDTH-1:0] s_wdata , input s_wlast , output reg s_wready , input [AXI_DATA_WIDTH/8-1:0] s_wstrb , input s_wvalid , output reg req_o, output reg we_o, output reg [AXI_ADDR_WIDTH-1:0] addr_o, output reg bank_o, output reg [AXI_DATA_WIDTH/8-1:0] be_o, output reg [AXI_DATA_WIDTH-1:0] data_o, input [AXI_DATA_WIDTH-1:0] data_i ); // AXI has the following rules governing the use of bursts: // - for wrapping bursts, the burst length must be 2, 4, 8, or 16 // - a burst must not cross a 4KB address boundary // - early termination of bursts is not supported. localparam LOG_NR_BYTES = $clog2(AXI_DATA_WIDTH/8); reg [AXI_ID_WIDTH-1:0] ax_req_d_id; reg [AXI_ADDR_WIDTH-1:0] ax_req_d_addr; reg [7:0] ax_req_d_len; reg [2:0] ax_req_d_size; reg [1:0] ax_req_d_burst; reg [AXI_ID_WIDTH-1:0] ax_req_q_id; reg [AXI_ADDR_WIDTH-1:0] ax_req_q_addr; reg [7:0] ax_req_q_len; reg [2:0] ax_req_q_size; reg [1:0] ax_req_q_burst; reg [2:0] state_d; reg [2:0] state_q; localparam IDLE = 3'h0; localparam READ = 3'h1; localparam WRITE = 3'h2; localparam SEND_B = 3'h3; localparam WAIT_WVALID = 3'h4; localparam READ_ADDR = 3'h6; localparam FIXED = 2'b00; localparam INCR = 2'b01; localparam WRAP = 2'b10; localparam [AXI_ADDR_WIDTH-1:0] ADDR_INCR = {{(AXI_ADDR_WIDTH-1){1'b0}}, 1'b1} << LOG_NR_BYTES; reg [AXI_ADDR_WIDTH-1:0] req_addr_d, req_addr_q; reg [7:0] cnt_d, cnt_q; reg [AXI_ADDR_WIDTH-1:0] wrap_mask_d, wrap_mask_q; reg req_bank_d, req_bank_q; reg [AXI_DATA_WIDTH-1:0] read_data_q; reg read_capture; wire [AXI_ADDR_WIDTH-1:0] ar_burst_bytes = ({{(AXI_ADDR_WIDTH-8){1'b0}}, s_arlen} + {{(AXI_ADDR_WIDTH-1){1'b0}}, 1'b1}) << LOG_NR_BYTES; wire [AXI_ADDR_WIDTH-1:0] aw_burst_bytes = ({{(AXI_ADDR_WIDTH-8){1'b0}}, s_awlen} + {{(AXI_ADDR_WIDTH-1){1'b0}}, 1'b1}) << LOG_NR_BYTES; wire [AXI_ADDR_WIDTH-1:0] ar_wrap_mask = ar_burst_bytes - {{(AXI_ADDR_WIDTH-1){1'b0}}, 1'b1}; wire [AXI_ADDR_WIDTH-1:0] aw_wrap_mask = aw_burst_bytes - {{(AXI_ADDR_WIDTH-1){1'b0}}, 1'b1}; reg [AXI_ADDR_WIDTH-1:0] next_addr; reg [AXI_ADDR_WIDTH-1:0] seq_addr; always @ (*) begin seq_addr = req_addr_q + ADDR_INCR; case (ax_req_q_burst) FIXED: next_addr = req_addr_q; INCR: next_addr = seq_addr; WRAP: next_addr = (req_addr_q & ~wrap_mask_q) | (seq_addr & wrap_mask_q); default: next_addr = seq_addr; endcase // Transaction attributes // default assignments state_d = state_q; ax_req_d_id = ax_req_q_id; ax_req_d_addr = ax_req_q_addr; ax_req_d_len = ax_req_q_len; ax_req_d_size = ax_req_q_size; ax_req_d_burst = ax_req_q_burst; req_addr_d = req_addr_q; cnt_d = cnt_q; wrap_mask_d = wrap_mask_q; req_bank_d = req_bank_q; read_capture = 1'b0; // Memory default assignments data_o = s_wdata; be_o = s_wstrb; we_o = 1'b0; req_o = 1'b0; addr_o = 'h0; bank_o = req_bank_q; // AXI assignments // request s_awready = 1'b0; s_arready = 1'b0; // read response channel s_rvalid = 1'b0; s_rdata = 'h0; s_rresp = 'h0; s_rlast = 'h0; s_rid = ax_req_q_id; // slave write data channel s_wready = 1'b0; // write response channel s_bvalid = 1'b0; s_bresp = 1'b0; s_bid = 1'b0; case (state_q) IDLE: begin // Wait for a read or write // ------------ // Read // ------------ if (s_arvalid) begin s_arready = 1'b1; // sample ax ax_req_d_id = s_arid; ax_req_d_addr = s_araddr; ax_req_d_len = s_arlen; ax_req_d_size = s_arsize; ax_req_d_burst = s_arburst; wrap_mask_d = ar_wrap_mask; req_bank_d = s_araddr[22]; state_d = READ_ADDR; // we can request the first address, this saves us time req_o = 1'b1; addr_o = s_araddr; bank_o = s_araddr[22]; // save the address req_addr_d = s_araddr; // save the ar_len cnt_d = 0; // ------------ // Write // ------------ end else if (s_awvalid) begin s_awready = 1'b1; s_wready = 1'b1; addr_o = s_awaddr; // sample ax ax_req_d_id = s_awid; ax_req_d_addr = s_awaddr; ax_req_d_len = s_awlen; ax_req_d_size = s_awsize; ax_req_d_burst = s_awburst; wrap_mask_d = aw_wrap_mask; req_bank_d = s_awaddr[22]; bank_o = s_awaddr[22]; // we've got our first w_valid so start the write process if (s_wvalid) begin req_o = 1'b1; we_o = 1'b1; state_d = (s_wlast) ? SEND_B : WRITE; cnt_d = 1; // we still have to wait for the first w_valid to arrive end else state_d = WAIT_WVALID; end end // ~> we are still missing a w_valid WAIT_WVALID: begin s_wready = 1'b1; addr_o = ax_req_q_addr; // we can now make our first request if (s_wvalid) begin req_o = 1'b1; we_o = 1'b1; state_d = (s_wlast) ? SEND_B : WRITE; cnt_d = 1; end end READ: begin // send the response s_rvalid = 1'b1; s_rdata = read_data_q; s_rid = ax_req_q_id; s_rlast = (cnt_q == ax_req_q_len + 1); if (FAST_READ_BURST && !s_rlast) begin req_o = 1'b1; addr_o = next_addr; end // check that the master is ready, the slave must not wait on this if (s_rready) begin // we sent the last byte -> go back to idle if (s_rlast) begin state_d = IDLE; // we already got everything end else begin if (FAST_READ_BURST) begin read_capture = 1'b1; req_addr_d = next_addr; cnt_d = cnt_q + 1; state_d = READ; end else begin req_addr_d = next_addr; state_d = READ_ADDR; end end end end READ_ADDR: begin // Fill the read data register after the async-SRAM address has // been stable for one full sys_clk cycle. req_o = 1'b1; addr_o = req_addr_q; read_capture = 1'b1; cnt_d = cnt_q + 1; state_d = READ; end // ~> we already wrote the first word here WRITE: begin s_wready = 1'b1; state_d = WRITE; // consume a word here if (s_wvalid) begin req_o = 1'b1; we_o = 1'b1; // ---------------------------- // Next address generation // ---------------------------- // handle the correct burst type addr_o = next_addr; // save the request address for the next cycle req_addr_d = next_addr; // we can decrease the counter as the master has consumed the read data cnt_d = cnt_q + 1; if (s_wlast) state_d = SEND_B; end end // ~> send a write acknowledge back SEND_B: begin s_bvalid = 1'b1; s_bid = ax_req_q_id; if (s_bready) state_d = IDLE; end endcase end // -------------- // Registers // -------------- always @(posedge clk or negedge resetn) begin if (~resetn) begin state_q <= IDLE; ax_req_q_addr <= 32'h0; ax_req_q_burst <= 2'h0; ax_req_q_id <= 'h0; ax_req_q_len <= 8'h0; ax_req_q_size <= 3'h0; req_addr_q <= 'h0; cnt_q <= 8'h0; wrap_mask_q <= 'h0; req_bank_q <= 1'b0; read_data_q <= 'h0; end else begin state_q <= state_d; ax_req_q_addr <= ax_req_d_addr; ax_req_q_burst <= ax_req_d_burst; ax_req_q_id <= ax_req_d_id; ax_req_q_len <= ax_req_d_len; ax_req_q_size <= ax_req_d_size; req_addr_q <= req_addr_d; cnt_q <= cnt_d; wrap_mask_q <= wrap_mask_d; req_bank_q <= req_bank_d; if (read_capture) read_data_q <= data_i; end end // always @(posedge clk or negedge resetn) begin // if (~resetn) begin // s_rdata <= 'h0; // end else begin // if(req_o == 1'b1 && we_o == 1'b0) // s_rdata <= data_i; // else // s_rdata <= s_rdata; // end // end endmodule