/* * Copyright 2021 Dennis Potter * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ module srdl2sv_amba3ahblite #( parameter bit FLOP_REGISTER_IF = 0, parameter BUS_BITS = 32, parameter NO_BYTE_ENABLE = 0 ) ( // Bus protocol input HCLK, input HRESETn, input HSEL, input [31:0] HADDR, input HWRITE, input [ 2:0] HSIZE, input [ 3:0] HPROT, // Might be used in the future together with an RDL UDP input [ 1:0] HTRANS, input [BUS_BITS-1:0] HWDATA, output logic HREADYOUT, output logic HRESP, output logic [BUS_BITS-1:0] HRDATA, // Interface to internal logic srdl2sv_widget_if.widget widget_if ); localparam BUS_BYTES = BUS_BITS/8; localparam BUS_BYTES_W = $clog2(BUS_BYTES); /*********************** * Define enums ***********************/ typedef enum logic [2:0] { SINGLE = 3'b000, INCR = 3'b001, WRAP4 = 3'b010, INCR4 = 3'b011, WRAP8 = 3'b100, INCR8 = 3'b101, WRAP16 = 3'b110, INCR16 = 3'b111 } HBURST_t; typedef enum logic [1:0] { IDLE = 2'b00, BUSY = 2'b01, NONSEQ = 2'b10, SEQ = 2'b11 } HTRANS_t; typedef enum logic { OKAY = 1'b0, ERROR = 1'b1 } HRESP_t; typedef enum logic { READ = 1'b0, WRITE = 1'b1 } OP_t; typedef enum logic [1:0] { FSM_IDLE = 2'b00, FSM_TRANS = 2'b01, FSM_ERR_0 = 2'b10, FSM_ERR_1 = 2'b11 } fsm_t; /**************************** * Determine current address ****************************/ logic [31:0] HADDR_q; logic [2:0] HSIZE_q; OP_t operation_q; wire addr_err = HADDR % (32'b1 << HSIZE) != 32'b0; always_ff @ (posedge HCLK) begin case (HTRANS) IDLE: ;// Do nothing BUSY: ;// Do nothing NONSEQ: begin // When a transfer is extended it has the side-effecxt // of extending the address phase of the next transfer if (HREADYOUT) begin HADDR_q <= HADDR; HSIZE_q <= HSIZE; operation_q <= HWRITE ? WRITE : READ; end end SEQ: begin if (HREADYOUT) begin HADDR_q <= HADDR; HSIZE_q <= HSIZE; end end endcase end /**************************** * Statemachine ****************************/ logic [BUS_BITS-1:0] HRDATA_temp; fsm_t fsm_next, fsm_q; always_comb begin // Defaults HREADYOUT = 1'b1; HRESP = OKAY; // When reading back, the data of the bit that was accessed over the bus // should be at byte 0 of the HRDATA bus and bits that were not accessed // should be masked with 0s. HRDATA_temp = widget_if.r_data >> (8*HADDR_q[BUS_BYTES_W-1:0]); for (int i = 0; i < BUS_BYTES; i++) if (i < (1 << HSIZE_q)) HRDATA[8*(i+1)-1 -: 8] = HRDATA_temp[8*(i+1)-1 -: 8]; else HRDATA[8*(i+1)-1 -: 8] = 8'b0; widget_if_w_vld_next = 0; widget_if_r_vld_next = 0; fsm_next = fsm_q; case (fsm_q) default: // FSM_IDLE begin if (HSEL && HTRANS > BUSY) begin if (addr_err) // In case the address is illegal, switch to an error state fsm_next = FSM_ERR_0; else if (HTRANS == NONSEQ) // If NONSEQ, go to NONSEQ state fsm_next = FSM_TRANS; else if (HTRANS == SEQ) // If a SEQ is provided, something is wrong fsm_next = FSM_ERR_0; end end FSM_TRANS: begin HREADYOUT = widget_if.rdy; widget_if_w_vld_next = operation_q == WRITE; widget_if_r_vld_next = operation_q == READ; if (HTRANS == BUSY) begin // Wait fsm_next = FSM_TRANS; end else if (widget_if.err && widget_if.rdy) begin HREADYOUT = 0; HRESP = ERROR; fsm_next = FSM_ERR_1; end else if (HTRANS == NONSEQ) begin // Another unrelated access is coming fsm_next = FSM_TRANS; end else if (HTRANS == SEQ) begin // Another part of the burst is coming fsm_next = FSM_TRANS; end else if (HTRANS == IDLE) begin // All done, wrapping things up! fsm_next = widget_if.rdy ? FSM_IDLE : FSM_TRANS; end end FSM_ERR_0: begin HREADYOUT = 0; if (HTRANS == BUSY) begin // Slaves must always provide a zero wait state OKAY response // to BUSY transfers and the transfer must be ignored by the slave. HRESP = OKAY; fsm_next = FSM_ERR_0; end else begin HRESP = ERROR; fsm_next = FSM_ERR_1; end end FSM_ERR_1: begin if (HTRANS == BUSY) begin // Slaves must always provide a zero wait state OKAY response // to BUSY transfers and the transfer must be ignored by the slave. HREADYOUT = 0; HRESP = OKAY; fsm_next = FSM_ERR_0; end else begin HREADYOUT = 1; HRESP = ERROR; fsm_next = FSM_IDLE; end end endcase end always_ff @ (posedge HCLK or negedge HRESETn) if (!HRESETn) fsm_q <= FSM_IDLE; else fsm_q <= fsm_next; /*** * Determine the number of active bytes ***/ logic [BUS_BYTES-1:0] HSIZE_bitfielded; logic [BUS_BYTES-1:0] widget_if_byte_en_next; logic widget_if_w_vld_next; logic widget_if_r_vld_next; generate if (NO_BYTE_ENABLE) begin assign widget_if_byte_en_next = {BUS_BYTES{1'b1}}; end else begin always_comb begin for (int i = 0; i < BUS_BYTES; i++) HSIZE_bitfielded[i] = i < (1 << HSIZE_q); // Shift if not the full bus is accessed widget_if_byte_en_next = HSIZE_bitfielded << (HADDR_q % BUS_BYTES); end end endgenerate /*** * Drive interface to registers ***/ generate if (FLOP_REGISTER_IF) begin always_ff @ (posedge HCLK or negedge HRESETn) if (!HRESETn) begin widget_if.w_vld <= 1'b0; widget_if.r_vld <= 1'b0; end else begin widget_if.w_vld <= widget_if_w_vld_next; widget_if.r_vld <= widget_if_r_vld_next; end always_ff @ (posedge HCLK) begin widget_if.addr <= {HADDR_q[31:BUS_BYTES_W], {BUS_BYTES_W{1'b0}}}; widget_if.w_data <= HWDATA << (8*HADDR_q[BUS_BYTES_W-1:0]); widget_if.byte_en <= widget_if_byte_en_next; end end else begin assign widget_if.w_vld = widget_if_w_vld_next; assign widget_if.r_vld = widget_if_r_vld_next; assign widget_if.addr = {HADDR_q[31:BUS_BYTES_W], {BUS_BYTES_W{1'b0}}}; assign widget_if.w_data = HWDATA << (8*HADDR_q[BUS_BYTES_W-1:0]); assign widget_if.byte_en = widget_if_byte_en_next; end endgenerate endmodule