Wraps a module with an output pulse interface inside a ready/valid output handshake interface. The connected module must have at least one pipeline stage from input to output. No combinational paths allowed else the input and output handshake logic will form a loop.
When we have a module that cannot be fully pipelined due to a data dependency (e.g.: because of a backwards loop in the pipeline, or simply by the iterative nature of the implemented algorithm), and thus cannot accept a new input every cycle (i.e.: it has an initiation interval greater than 1), then we design the connected module to accept a new input with a one-cycle valid pulse, and to signal the updated output 1 or more cycles later with a corresponding one-cycle output pulse.
This Pulse to Pipeline module converts that output pulse interface into a pipeline output with a ready/valid handshake, and once read out, signals the corresponding input interface that new data can be accepted.
Note that the connected module result can only be read out once, even if
held steady until the next update. This is necessary to meet some Kahn
Process Network criteria, and to allow for the connected module to use its
output registers as part of its computations, rather than have extra
buffers (which we instead hold here as the output_buffer
).
We assume here that the connected module is not C-Slowed, though that is allowed. You will have to keep track of the separate computation streams yourself in the enclosing module.
An output buffer is necessary to cut the backwards combinational path from
ready_out
to module_ready
, as well as to get the best performance and
expected behaviour, depending on the connected module. Set
OUTPUT_BUFFER_TYPE
(and maybe FIFO_BUFFER_DEPTH
, FIFO_BUFFER_RAMSTYLE
,
and OUTPUT_BUFFER_CIRCULAR
) as required.
output_buffer
waits for the next
pipeline stage to read out the previous result.output_buffer
. OUTPUT_BUFFER_CIRCULAR
to a non-zero value for cases where you
always want the latest data available to the output, regardless of
intermediate changes which will be lost.`default_nettype none module Pulse_to_Pipeline #( parameter WORD_WIDTH = 0, parameter OUTPUT_BUFFER_TYPE = "", // "HALF", "SKID", "FIFO" parameter OUTPUT_BUFFER_CIRCULAR = 0, // non-zero to enable parameter FIFO_BUFFER_DEPTH = 0, // Only for "FIFO" parameter FIFO_BUFFER_RAMSTYLE = "" // Only for "FIFO" ) ( input wire clock, input wire clear, // Pipeline output output wire valid_out, input wire ready_out, output wire [WORD_WIDTH-1:0] data_out, // Pulse interface from connected module input wire [WORD_WIDTH-1:0] module_data_out, input wire module_data_out_valid, // Signal that the module can accept the next input output reg module_ready ); initial begin module_ready = 1'b0; end
Express the usual conditions to complete a ready/valid handshake. The "internal" signals connect to a Skid Buffer that deals with the final output handshake to the downstream pipeline. When we have transferred the module's output data to the Skid Buffer, we signal the module is ready for the next input.
reg valid_out_internal = 1'b0; wire ready_out_internal; reg output_handshake_done = 1'b0; always @(*) begin output_handshake_done = (valid_out_internal == 1'b1) && (ready_out_internal == 1'b1); module_ready = (output_handshake_done == 1'b1); end
The output ready/valid handshake starts when the connected module updates its output data.
wire valid_out_latched; Pulse_Latch #( .RESET_VALUE (1'b0) ) generate_valid_out_latched ( .clock (clock), .clear (output_handshake_done), .pulse_in (module_data_out_valid), .level_out (valid_out_latched) );
Pass the module pulse directly, and also latch it. This removes a cycle of latency and still allows the output handshake to complete later if the downstream logic is not ready.
always @(*) begin valid_out_internal = (valid_out_latched == 1'b1) || (module_data_out_valid == 1'b1); end
Buffer the output handshake to cut the backwards combinational path from
ready_out
to module_ready
. A Half-Buffer blocks module_ready
until
it is read out, and the data is the first buffered one since last read out.
A Skid Buffer allows overlap by raising module_ready
while holding
1 previous result, but holding 2 results will cause it to block. A FIFO
buffer will allow overlap and hold up to FIFO_BUFFER_DEPTH
values before
blocking.
generate if (OUTPUT_BUFFER_TYPE == "HALF") begin : gen_half Pipeline_Half_Buffer #( .WORD_WIDTH (WORD_WIDTH), .CIRCULAR_BUFFER (OUTPUT_BUFFER_CIRCULAR) ) output_buffer ( .clock (clock), .clear (clear), .input_valid (valid_out_internal), .input_ready (ready_out_internal), .input_data (module_data_out), .output_valid (valid_out), .output_ready (ready_out), .output_data (data_out) ); end else if (OUTPUT_BUFFER_TYPE == "SKID") begin : gen_skid Pipeline_Skid_Buffer #( .WORD_WIDTH (WORD_WIDTH), .CIRCULAR_BUFFER (OUTPUT_BUFFER_CIRCULAR) ) output_buffer ( .clock (clock), .clear (clear), .input_valid (valid_out_internal), .input_ready (ready_out_internal), .input_data (module_data_out), .output_valid (valid_out), .output_ready (ready_out), .output_data (data_out) ); end else if (OUTPUT_BUFFER_TYPE == "FIFO") begin : gen_fifo Pipeline_FIFO_Buffer #( .WORD_WIDTH (WORD_WIDTH), .DEPTH (FIFO_BUFFER_DEPTH), .RAMSTYLE (FIFO_BUFFER_RAMSTYLE), .CIRCULAR_BUFFER (OUTPUT_BUFFER_CIRCULAR) ) output_buffer ( .clock (clock), .clear (clear), .input_valid (valid_out_internal), .input_ready (ready_out_internal), .input_data (module_data_out), .output_valid (valid_out), .output_ready (ready_out), .output_data (data_out) ); end endgenerate endmodule