Skid Buffer

A skid buffer is used to break apart a combinatorial path between a sender and receiver interface by registering the data and handshake signals. The skid buffer uses two buffer entries, a main buffer and a “skid” buffer, that allows it to maintain the full throughput, thus pipelining the data path. Because there is a one clock cycle latency through the skid buffer for backpressuring the sender if it is backpressured by the receiver, the “skid” buffer is needed to absorb one extra transaction from the sender, before it can actually backpressure the sender. Skid buffers are commonly used for pipelining and resolving timing issues by breaking long combinatorial paths between a sender and receiver. Because there are only two buffer registers, it is also a very lightweight solution.

This skid buffer implementation also provides two additional output status signals, o_accept and o_transmit, which indicate when a transaction is successfully accepted from the input interface or successfully transmitted to the output interface. These two additional signals are provided so that a parent module can keep track of when the skid buffer is accepting or transmitting data. For example, this could be useful if the parent module needs that information for some of its own control logic or if it wants to keep track of how many transcations have been accepted and transmitted by the skid buffer.

Parameters

  • DATA_WIDTH : data width in bits

Ports

  • i_clock : input clock

  • i_aresetn : asynchronous active-low reset

  • i_clear : synchronous clear

  • i_data : input data

  • i_input_valid : valid signal from input interface. Used with o_input_ready to handshake on input interface

  • i_output_ready : ready signal from output interface. Used with o_output_valid to handshake on output interface

  • o_data : output data

  • o_output_valid : valid signal from output interface. Used with i_output_ready to handshake on output interface

  • o_input_ready : ready signal from input interface. Used with i_input_valid to handshake on input interface

  • o_accept : accept status signal that indicates whenever input data is accepted by the skid buffer

  • o_transmit : transmit status signal that indicates whenever output data is transmitted by the skid buffer

Source Code

skid_buffer.sv
  1`ifndef LIBSV_FIFOS_SKID_BUFFER
  2`define LIBSV_FIFOS_SKID_BUFFER
  3
  4module skid_buffer #(
  5    parameter int DATA_WIDTH  /* verilator public_flat_rd */ = 32
  6) (
  7    input  logic                  i_clock,
  8    input  logic                  i_aresetn,
  9    input  logic                  i_clear,
 10    input  logic [DATA_WIDTH-1:0] i_data,
 11    input  logic                  i_input_valid,
 12    input  logic                  i_output_ready,
 13    output logic [DATA_WIDTH-1:0] o_data,
 14    output logic                  o_output_valid,
 15    output logic                  o_input_ready,
 16    output logic                  o_accept,
 17    output logic                  o_transmit
 18);
 19
 20    // CONTROL PATH -----------------------------
 21
 22    typedef enum logic [2:0] {
 23        EMPTY = 3'b001,
 24        BUSY  = 3'b010,
 25        FULL  = 3'b100
 26    } state_t;
 27
 28    state_t state, next_state;  // state variables
 29    logic accept, transmit;  // handshake flags on each interface
 30    logic [DATA_WIDTH-1:0] buffer;  // the "skid" buffer
 31
 32    assign o_accept   = accept;
 33    assign o_transmit = transmit;
 34
 35    always_comb begin : next_state_logic
 36        accept     = i_input_valid && o_input_ready;  // check for input handshake
 37        transmit   = o_output_valid && i_output_ready;  // check for output handshake
 38        next_state = EMPTY;
 39        unique case (state)
 40            EMPTY: begin
 41                next_state = EMPTY;
 42                if (accept) next_state = BUSY;
 43            end
 44            BUSY: begin
 45                next_state = BUSY;
 46                if (accept && !transmit) next_state = FULL;
 47                else if (!accept && transmit) next_state = EMPTY;
 48            end
 49            FULL: begin
 50                next_state = FULL;
 51                if (transmit) next_state = BUSY;
 52            end
 53            default: next_state = EMPTY;
 54        endcase
 55    end : next_state_logic
 56
 57    always_ff @(posedge i_clock, negedge i_aresetn) begin : update_state_logic
 58        if (!i_aresetn || i_clear) begin
 59            state          <= EMPTY;
 60            o_input_ready  <= 1'b0;
 61            o_output_valid <= 1'b0;
 62        end else begin
 63            state          <= next_state;
 64            o_input_ready  <= next_state != FULL;
 65            o_output_valid <= next_state != EMPTY;
 66        end
 67    end : update_state_logic
 68
 69    logic buffer_write_en, o_data_write_en;
 70    always_comb begin : write_en_logic
 71        buffer_write_en = state == BUSY && accept && !transmit;
 72        o_data_write_en = (state == EMPTY && accept && !transmit)
 73                      || (state == BUSY && accept && transmit)
 74                      || (state == FULL && !accept && transmit);
 75    end : write_en_logic
 76
 77    // END OF CONTROL PATH ----------------------
 78
 79    // DATA PATH --------------------------------
 80
 81    always_ff @(posedge i_clock, negedge i_aresetn) begin : o_data_and_buffer_logic
 82        if (!i_aresetn || i_clear) begin
 83            o_data <= '0;
 84            buffer <= '0;
 85        end else begin
 86
 87            if (o_data_write_en) begin
 88                if (state == FULL) o_data <= buffer;
 89                else o_data <= i_data;
 90            end
 91
 92            if (buffer_write_en) begin
 93                buffer <= i_data;
 94            end
 95
 96        end
 97    end : o_data_and_buffer_logic
 98
 99    // END OF DATA PATH -------------------------
100
101endmodule
102
103`endif  /* LIBSV_FIFOS_SKID_BUFFER */