verilog中两种写流水线的方法,哪种更好?

Two ways to write pipeline in verilog, which one is better?

学习了两种pipeline的写法(unblocking和blocking),不知道哪个更好?

我个人认为第二个很乏味,我不明白为什么需要那么多wire

另外,在verilog中有没有像FSM这样的流水线的标准写法(模板)? 提前致谢。

module simplePipeline
#(
    parameter WIDTH = 100
)
(
    input clk,
    input [WIDTH - 1 : 0] datain,
    output [WIDTH - 1: 0] dataout
);

    reg [WIDTH - 1 : 0] piprData1;
    reg [WIDTH - 1 : 0] piprData2;
    reg [WIDTH - 1 : 0] piprData3;

    always @(posedge clk) begin
        piprData1 <= datain;
    end

    always @(posedge clk) begin
        piprData2 <= piprData1;
    end

    always @(posedge clk) begin
        piprData3 <= piprData2;
    end

    assign dataout = piprData3;

endmodule

module blockingPipeline#2
(
    parameter WIDTH = 100
)
(
    input                   clk,
    input                   rst,
    input                   validIn,
    input [WIDTH - 1 : 0]   dataIn,
    input                   outAllow,
    output wire                 validOut,
    output wire [WIDTH - 1 : 0]   dataOut
);

    reg                 pipe1Valid;
    reg [WIDTH - 1 : 0] pipe1Data;
    reg                 pipe2Valid;
    reg [WIDTH - 1 : 0] pipe2Data;
    reg                 pipe3Valid;
    reg [WIDTH - 1 : 0] pipe3Data;


    /*------------------------PIPE1 LOGIC------------------------*/
    wire                pipe1AllowIn;
    wire                pipe1ReadyGo;
    wire                pipe1ToPipe2Valid;

    assign pipe1ReadyGo = 1'b1

    assign pipe1AllowIn = ! pipe1Valid || pipe1ReadyGo && pipe2AllowIn;

    assign pipe1ToPipe2Valid = pipe1Valid && pipe1ReadyGo

    always @(posedge clk)begin

        if( rst ) begin
            pipe1Vali <= 1'b0;
        end

        else if(pipe1AllowIn)begin
            pipe1Valid <= validIn;
        end

        ifvalidIn && pipe1AllowIn)begin
           pipe1Data <= dataIn;
        end

    end

    /*------------------------PIPE2 LOGIC------------------------*/
    wire                pipe2AllowIn;
    wire                pipe2ReadyGo;
    wire                pipe2ToPipe3Valid;

    assign pipe2ReadyGo = 1'b1;
    assign pipe2AllowIn = ! pipe2Valid || pipe2ReadyGo && pipe3AllowIn;
    assign pipe2ToPipe3Valid = pipe2Valid && pipe3ReadyGo;

    always @(posedge clk)begin
        if( rst ) begin
            pipe2Valid <= 1'b0;
        end
        else if(pipe2AllowIn)begin
            pipe2Valid <= pipe1ToPipe2Valid;
        end
        if(pipe1ToPipe2Valid && pipe2AllowIn)begin
            pipe2Data <= pipe1Data;
        end
    end


    /*------------------------PIPE3 LOGIC------------------------*/
    wire                pipe3AllowIn;
    wire                pipe3ReadyGo;

    assign pipe3ReadyGo = 1'b1;
    assign pipe3AllowIn = ! pipe3Valid || pipe3ReadyGo && outAllow;

    always @(posedge clk)begin
        if( rst ) begin
            pipe3Valid <= 1'b0;
        end
        else if(pipe3AllowIn)begin
            pipe3Valid <= pipe2ToPipe3Valid;
        end
        if(pipe2ToPipe3Valid && pipe3AllowIn)begin
            pipe3Data <= pipe2Data;
        end
    end

    assign validOut = pipe3Valid && pipe3ReadyGo;
    assign dataOut = pipe3Data;

endmodule

作为良好实践,第二个似乎 "better" 从某种意义上说,当您设计硬件电路部件时,您可能希望提供很好的控制可能性。一般来说,由于您的代码将永远在硅片中实现(或在您的 FPGA 中进行痛苦的重新配置),因此没有足够的控制能力将是一个真正的问题,因为您之后无法真正添加一些。

举个例子,您在评论中已经提到您必须停止管道,因此您当然需要更多的电线来完成它。有时您还需要重置管道,这就是 rst 信号的目的。

然而,更多的信号意味着更多的硅表面(或使用更多的 FPGA 资源),这通常需要更高的价格。有人可能会争辩说,只有一根或两根电线不会对芯片产生如此大的影响,这是事实,但如果你在电路上重复使用你的管道一千次,它会更重要。

对我来说,第一个实现可能只与电路级非常强的优化要求相关,例如 ASIC,您完全知道整体所需的行为。

第一个版本的问题是似乎根本没有时钟门。除非您的时钟在更高级别上得到很好的门控,或者每个周期都使用管道,否则您将通过(不必要地)在每个周期切换管道的每个阶段来浪费大量功率。