[When all questions are answered, and we finalize this, we need to put
this on the wiki.  This is one of the things that I want an IDE to
automate.]

This is a tutorial, you might say, on coding processing pipelines
in Verilog.  The objective is to provide templates that people can fill
in with their code, where the underlying pipeline mechanics are designed
to be fast and efficient.  I'm describing what I call a "packing"
pipeline; this means that if something is holding up the pipeline
further down, but there are bubbles (stages that have nothing to do),
earlier stages will be able to progress until the pipeline is completely
full.

We design pipelines in logical sections.  A top-level module for a
section will have an interface that conforms to the fifo protocol.
A section contains multiple stages, however many are logical for the
function being computed.  However for performance, there may be places
where you'd want to break the pipeline more finely.

There are five major kinds of pipeline modules:

(1) HEADER stages are designed to break the combinatorial chains
associated with busy signals that control pipeline flow.  Each stage
pays attention to the same busy signal, and together with internal busy
signals, fanouts and levels of logic accumulate, and we want to limit
that.  Think of a header stage as a fifo that holds zero or one entry.

(2) DUMMY stages are intended to break combinatorial
chains associate with the data flowing through the pipeline.  These are
practically manditory following a header stage, but they're also very
useful in some cases with stages that do I/O with logic outside of the
pipeline.  These are sometimes referred to as "drive" stages.

(3) PROCESS stages simply perform some slice of the function being
computed.  The developer breaks logic into logical pipeline stages,
and each of those corresponds to one of these physical stages.

(4) IO stages are also PROCESS stages, but they're ones that do I/O
with something external to the pipeline and thus may become "busy."
This status must be fed back up the pipeline to hold up other stages.

(5) FOOTER stages aren't pipeline stages.  They just cap off the
pipeline so that the output of the top-level module uses fifo protocol.


Pipeline stages typically have multiple signals as inputs and outputs.
However, for the sake of generality, we will treat each stage as having
one data word in and one data word out.  Each is likely to be a different
length.



module HEADER_stage(
    input clock,
    input reset_,

    // Input channel
    input [INSIZE] input_data,
    input input_enq,
    output im_busy,

    // Output channel
    output [INSIZE] output_data,
    output output_valid,
    input next_advance
);

// Holding register for when we're busy
reg [INSIZE] hold_data;
reg holding;
assign im_busy = holding;

// Select input or holding data for next stage
assign output_data = holding ? hold_data : input_data;
// Indicate if we're offering data to the next stage
assign output_valid = holding || input_enq;


always @(posedge clock or negedge reset_) begin
    if (!_reset) begin
        holding <= 0;
        hold_data <= 0;
    end else begin
        // If we're not holding, grab data, whether or not we need it
        if (!holding) hold_data <= input_data;

        // Compute whether or not we're going to hold data
        holding <= !next_advance && output_valid;
    end
end

endmodule



module DUMMY_stage(
    input clock,
    input reset_,

    // Input channel
    input [INSIZE] input_data,
    input input_valid,
    output i_advance,

    // Output channel
    output reg [INSIZE] output_data,
    output reg output_valid,
    input next_advance
);

assign i_advance = next_advance;

always @(posedge clock or negedge reset_) begin
    if (!reset_) begin
        output_valid <= 0;
        output_data <= 0;
    end else begin
        // If next stage advances, so can I.
        if (next_advance) begin
            output_valid <= input_valid;
            output_data <= input_data;
        end
    end
end

endmodule



module PROCESS_stage(
    input clock,
    input reset_,

    // Input channel
    input [INSIZE] input_data,
    input input_valid,
    output i_advance,

    // Output channel
    output reg [OUTSIZE] output_data,
    output reg output_valid,
    input next_advance
);

assign i_advance = next_advance;


// Instantiate module that performs computation from input
// to output
wire [OUTSIZE] my_data;
wire my_valid;
my_combinatorial_logic mcl (.input_data(input_data),
    .output_data(my_data), .output_valid(my_valid));


always @(posedge clock or negedge reset_) begin
    if (!reset_) begin
        output_valid <= 0;
        output_data <= 0;
    end else begin
        // If next stage advances, so can I.
        if (next_advance) begin
            output_valid <= my_valid && input_valid;
            output_data <= my_data;
        end
    end
end

endmodule



module IO_stage(
    input clock,
    input reset_,

    // Input channel
    input [INSIZE] input_data,
    input input_valid,
    output i_advance,

    // Output channel
    output reg [OUTSIZE] output_data,
    output reg output_valid,
    input next_advance,

    // External connections
    input [IOINSIZE] io_input_data;
    output [IOOUTSIZE] io_output_data;
    input its_busy,   // other logic is busy
    output im_busy    // I am busy for other logic
);


wire expecting_io;
// I can't do any I/O if I have no expectation of it
assign im_busy = !input_valid || !next_advance || !expecting_io;


// Report to previous stage that I cannot advance if I/O is
// holding me up
wire my_busy = input_valid && expecting_io && its_busy;
assign i_advance = next_advance && !my_busy;


// Instantiate module that performs computation from input
// to output
wire [OUTSIZE] my_data;
wire my_valid;
my_combinatorial_logic mcl (.input_data(input_data),
    .output_data(my_data), .output_valid(my_valid),
    .io_input_data(io_input_data), .io_output_data(io_output_data),
    .expecting(expecting_io),
    .clock(clock), .reset_(reset_));



always @(posedge clock or negedge reset_) begin
    if (!reset_) begin
        output_valid <= 0;
        output_data <= 0;
    end else begin
        // If next stage advances, so can I.
        if (next_advance) begin
            output_valid <= my_valid && input_valid;
            output_data <= my_data;
        end
    end
end

endmodule



module FOOTER_stage(
    // Input channel
    input [INSIZE] input_data,
    input input_valid,
    output i_advance,

    // Output channel
    output reg [INSIZE] output_data,
    output reg output_enq,
    input output_busy
);


assign output_enq = input_valid;
assign output_data = input_data;
assign i_advance = !output_busy || !input_valid;

endmodule



-- 
Timothy Normand Miller
http://www.cse.ohio-state.edu/~millerti
Open Graphics Project
_______________________________________________
Open-graphics mailing list
[email protected]
http://lists.duskglow.com/mailman/listinfo/open-graphics
List service provided by Duskglow Consulting, LLC (www.duskglow.com)

Reply via email to