From 17f18773906ac22839f627abdd533aca3eeb38e0 Mon Sep 17 00:00:00 2001 From: Dennis Date: Sun, 12 Sep 2021 17:13:44 -0700 Subject: [PATCH] Add very basic, incomplete statemachine for AHB protocol --- .../widgets/srdl2sv_amba3ahblite.sv | 298 ++++++++++++------ .../widgets/srdl2sv_amba3ahblite.yaml | 20 +- 2 files changed, 213 insertions(+), 105 deletions(-) diff --git a/srdl2sv/components/widgets/srdl2sv_amba3ahblite.sv b/srdl2sv/components/widgets/srdl2sv_amba3ahblite.sv index 4257467..e937e27 100644 --- a/srdl2sv/components/widgets/srdl2sv_amba3ahblite.sv +++ b/srdl2sv/components/widgets/srdl2sv_amba3ahblite.sv @@ -26,19 +26,9 @@ module srdl2sv_amba3ahblite import srdl2sv_if_pkg::*; #( - parameter FLOP_IN = 0, // Set to '1' to flop input from the AHB bus. This is meant - // to help meet timing. Don't use this to synchronize the input. - parameter SYNC_IO = 0 // Set to '1' to in case HCLK and bus_clk are asynchronous - // By default, mainly for it to directly work in simulations, - // it will double-flops based on always_ff-blocks. To replace - // this module with a proper MTBF-optimized double flop cell, - // the contents of the synchronizer module `srdl2sv_sync.sv` - // shall be updated + parameter bit FLOP_REGISTER_IF = 0 ) ( - // Register clock - input reg_clk, - // Outputs to internal logic output b2r_t b2r, @@ -46,8 +36,8 @@ module srdl2sv_amba3ahblite input r2b_t r2b, // Bus protocol - input HRESETn, input HCLK, + input HRESETn, input HSEL, input [31:0] HADDR, input HWRITE, @@ -57,118 +47,240 @@ module srdl2sv_amba3ahblite input [ 1:0] HTRANS, input [31:0] HWDATA, input HREADY, + input HMASTLOCK, output logic HREADYOUT, output logic HRESP, output logic [31:0] HRDATA ); - // TODO: Add synchronizer logic - - /*** - * Translate HWRITE & HSEL into a write/read operation for the register logic - ***/ - logic r_vld_next; - logic w_vld_next; + /*********************** + * 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 [2:0] { + FSM_IDLE = 3'b000, + FSM_NONSEQ= 3'b001, + FSM_SEQ = 3'b010, + FSM_WAIT = 3'b011, + FSM_ERR_0 = 3'b100, + FSM_ERR_1 = 3'b101 + } fsm_t; + + /**************************** + * Determine current address + ****************************/ + logic [31:0] addr_q; + OP_t operation_q; + + wire addr_err = (HADDR % HSIZE) != 0; + + 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 (r2b.rdy) + begin + addr_q <= HADDR; + operation_q <= HWRITE ? WRITE : READ; + end + end + SEQ: + begin + if (r2b.rdy) + begin + addr_q <= addr_q; // TODO + end + end + endcase + end + + /**************************** + * Statemachine + ****************************/ + fsm_t fsm_next, fsm_q; always_comb begin - w_vld_next = 1'b0; - r_vld_next = 1'b0; + // Defaults + HREADYOUT = 1'b1; + HRESP = 1'b0; + HRDATA = r2b.data; - if (HWRITE) - w_vld_next = HSEL; - else - r_vld_next = HSEL; + b2r_w_vld_next = 0; + b2r_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_NONSEQ; + else if (HTRANS == SEQ) + // If a SEQ is provided, something is wrong + fsm_next = FSM_ERR_0; + end + end + FSM_NONSEQ: + begin + HREADYOUT = r2b.rdy; + + b2r_w_vld_next = operation_q == WRITE; + b2r_r_vld_next = operation_q == READ; + + if (r2b.err && r2b.rdy) + begin + fsm_next = FSM_ERR_0; + end + else if (HTRANS == BUSY) + begin + // Wait + fsm_next = FSM_NONSEQ; + end + else if (HTRANS == NONSEQ) + begin + // Another unrelated access is coming + fsm_next = FSM_NONSEQ; + end + else if (HTRANS == SEQ) + begin + // Entering a burst + fsm_next = r2b.rdy ? FSM_SEQ : FSM_NONSEQ; + end + else if (HTRANS == IDLE) + begin + // All done, wrapping things up! + fsm_next = r2b.rdy ? FSM_IDLE : FSM_NONSEQ; + end + end + FSM_SEQ: + begin + end + FSM_WAIT: + begin + 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 = 0; + fsm_next = FSM_ERR_0; + end + else + begin + HRESP = 1; + fsm_next = FSM_ERR_1; + end + end + FSM_ERR_1: + begin + HREADYOUT = 1; + HRESP = 1; + + fsm_next = FSM_IDLE; + 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 [3:0] b2r_byte_en_next; + logic b2r_w_vld_next; + logic b2r_r_vld_next; - always_comb - begin - case (HTRANS) - 3'b000 : b2r_byte_en_next = 4'b0001; - 3'b001 : b2r_byte_en_next = 4'b0011; - 3'b010 : b2r_byte_en_next = 4'b1111; - // TODO: Implement larger sizes - default: b2r_byte_en_next = 4'b1111; - endcase - end + //always_comb + //begin + // case (HTRANS) + // 3'b000 : b2r_byte_en_next = 4'b0001; + // 3'b001 : b2r_byte_en_next = 4'b0011; + // 3'b010 : b2r_byte_en_next = 4'b1111; + // default: b2r_byte_en_next = 4'b1111; + // endcase + //end /*** - * Flop or sync input if required + * Drive interface to registers ***/ generate - if (FLOP_IN) - begin - always_ff @(posedge HCLK or negedge HRESETn) + if (FLOP_REGISTER_IF) + begin + always_ff @ (posedge HCLK or negedge HRESETn) if (!HRESETn) begin - b2r.r_vld <= 1'b0; b2r.w_vld <= 1'b0; + b2r.r_vld <= 1'b0; end else begin - b2r.r_vld <= r_vld_next; - b2r.w_vld <= w_vld_next; + b2r.w_vld <= b2r_w_vld_next; + b2r.r_vld <= b2r_r_vld_next; end - always_ff @(posedge HCLK) - if (HWRITE) - begin - b2r.data <= HWDATA; - b2r.byte_en <= b2r_byte_en_next; - end - end - else + always_ff @ (posedge HCLK) begin - assign b2r.r_vld = r_vld_next; - assign b2r.w_vld = w_vld_next; - assign b2r.data = HWDATA; + b2r.addr <= addr_q; + b2r.data <= HWDATA; + b2r.byte_en <= b2r_byte_en_next; end + end + else + begin + assign b2r.w_vld = b2r_w_vld_next; + assign b2r.r_vld = b2r_r_vld_next; + assign b2r.addr = addr_q; + assign b2r.data = HWDATA; + assign b2r.byte_en = b2r_byte_en_next; + end endgenerate - /*** - * Keep track of an ungoing transaction - ***/ - logic reg_busy_q; - - always_ff @(posedge HCLK or negedge HRESETn) - if (!HRESETn) - reg_busy_q <= 1'b0; - else if ((b2r.r_vld || b2r.w_vld) && !r2b.rdy) - reg_busy_q <= 1'b1; - else if (r2b.rdy) - reg_busy_q <= 1'b0; - - assign HREADYOUT = !reg_busy_q; - - /*** - * Return to AHB bus once the register block is ready - ***/ - // Return actual data - logic ongoing_read_q; - - always_ff @(posedge HCLK or negedge HRESETn) - if (!HRESETn) - ongoing_read_q <= 1'b0; - else if (b2r.r_vld && !r2b.rdy) - ongoing_read_q <= 1'b1; - else if (r2b.rdy) - ongoing_read_q <= 1'b0; - - always_ff @(posedge HCLK) - if ((b2r.r_vld || ongoing_read_q) && r2b.rdy) - HRDATA <= r2b.data; - - // Did an error occur while reading? - always_ff @(posedge HCLK or negedge HRESETn) - if (!HRESETn) - HRESP <= 1'b0; - else - HRESP <= r2b.err; - endmodule diff --git a/srdl2sv/components/widgets/srdl2sv_amba3ahblite.yaml b/srdl2sv/components/widgets/srdl2sv_amba3ahblite.yaml index dd1f2b8..2c9a161 100644 --- a/srdl2sv/components/widgets/srdl2sv_amba3ahblite.yaml +++ b/srdl2sv/components/widgets/srdl2sv_amba3ahblite.yaml @@ -9,18 +9,11 @@ module_instantiation: * - b2r.* -> Signals from bus to registers * - H* -> Signals as defined in AMBA3 AHB Lite * specification - * - bus_clk -> Clock that that drives signals on bus - * - reg_clk -> Clock that drives register flops - * - bus_rst_n -> Asynchronous reset that resets only the bus (but - * (not the registers). The deassertion of this - * reset shall be synchronized to bus_clk + * - clk -> Clock that drives registers and the bus *******************************************************************/ srdl2sv_amba3ahblite srdl2sv_amba3ahblite_inst - (// Register clock - .reg_clk, - - // Outputs to internal logic + (// Outputs to internal logic .b2r, // Inputs from internal logic @@ -28,7 +21,7 @@ module_instantiation: // Bus protocol .HRESETn, - .HCLK, + .HCLK (clk), .HADDR, .HWRITE, .HSIZE, @@ -38,6 +31,7 @@ module_instantiation: .HWDATA, .HREADY, .HSEL, + .HMASTLOCK, .HREADYOUT, .HRESP, @@ -48,9 +42,9 @@ module_instantiation: - name: 'r2b' signal_type: 'r2b_t' input_ports: - - name: 'HRESETn' + - name: 'clk' signal_type: '' - - name: 'HCLK' + - name: 'HRESETn' signal_type: '' - name: 'HADDR' signal_type: '[31:0]' @@ -70,6 +64,8 @@ module_instantiation: signal_type: '' - name: 'HSEL' signal_type: '' + - name: 'HMASTLOCK' + signal_type: '' output_ports: - name: 'HREADYOUT' signal_type: ''