Verilog实现带暂停和刷新的四级流水线加法器-FPGA常见问题论坛-FPGA CPLD-ChipDebug

Verilog实现带暂停和刷新的四级流水线加法器

 

图解

先放一张图:

图片[1]-Verilog实现带暂停和刷新的四级流水线加法器-FPGA常见问题论坛-FPGA CPLD-ChipDebug

原理就是把32位的数分成4部分,每部分8位。每个周期都只计算一个部分,共四个周期计算出结果。比如a+b:

第一个周期,a的最低8位与b的最低8位相加,得出的结果(包括进位)传递给第一级流水线寄存器,尚还没有计算的a的高24位与b的高24位也原样传递给第一级流水线寄存器;

第二个周期,a的次低8位与b的次低8位相加,得出的结果传递给第二级流水线寄存器(与低位的结果拼接),尚还没有计算的a的高16位与b的高16位也原样传递给第二级流水线寄存器;

第三个周期,a的次高8位与b的次高8位相加,得出的结果传递给第三级流水线寄存器(与低位的结果拼接),尚还没有计算的a的高8位与b的高8位也原样传递给第三级流水线寄存器;

第四个周期,a的最高8位与b的最高8位相加,得出的结果传递给第四级流水线寄存器(与低位的结果拼接),此时已全部计算完成,得到整个输出结果和进位结果。

四级流水线加法器,从低位到高位,每周期计算8位,共需4周期出结果。

流水线暂停仿真图

流水线暂停:简单说就是把要暂停的阶段前面所有流水线寄存器的使能信号关掉,使时钟跳变时,暂停的阶段前面所有流水线寄存器中的内容保持(这样数据就不往后传),达到了暂停的效果。而暂停阶段后面的流水线仍在流动。

一个暂停仿真图:

图片[2]-Verilog实现带暂停和刷新的四级流水线加法器-FPGA常见问题论坛-FPGA CPLD-ChipDebug

流水线刷新仿真图

流水线的刷新一般和暂停一起使用,当然也可以单独使用。

(1)与暂停一起使用,作用:清空暂停阶段的后续一个流水线寄存器,避免重复无意义的运算。

上文说到,若要暂停某一阶段,则其前面的所有阶段都要一起暂停才行,但是其后面的阶段仍在流动。这会引发什么问题呢?假设要暂停第二阶段(a的次低8位与b的次低8位相加),则其前面的流水线寄存器(第一级流水线寄存器)使能信号置0,在暂停的过程中,每一次时钟上升沿(使用posedge clk)时,流水线的第二阶段都会把第一级流水线寄存器中的数据拿来运算,并在下一时钟上升沿传递给第二级流水线寄存器,因为前面没有新的数据传过来,所以流水线的第二阶段会一直做重复的计算,算完之后往后传递(第二级流水线寄存器),所以我们不能让他的结果继续往后传了(其实让他传也没影响,但我们习惯于把它刷掉,防止占用一些资源),故把第二级流水线寄存器的clear信号置1,每次时钟上升沿,第二级流水线寄存器的值全清0,因此第三阶段取到的值也都是0。

(2)单独使用,作用:刷新指定流水线寄存器,处于该流水线寄存器的值全部清0,即当前运行到此流水线寄存器前一阶段的那条加法的数据被清空(无效数据)了,它最后也就没有结果产生了。

一个单独使用刷新的仿真图:

图片[3]-Verilog实现带暂停和刷新的四级流水线加法器-FPGA常见问题论坛-FPGA CPLD-ChipDebug

Verilog源代码

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/04/19 20:41:31
// Design Name: 
// Module Name: stallable_pipeline_adder
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
 
    //32位4级流水线加法器
module stallable_pipeline_adder(
    input clk,
    input rst,                 //复位
    input [3:0] stop,          //暂停(注意当暂停某一级时,当前级与之前的级都被暂停,其后的级继续流动 
            // 0000不暂停,0001暂停第一级,0011暂停第二级,0111暂停第三级,1111暂停第四级)
    input [3:0] refresh,       //刷新 (刷新哪一级,哪一级的数据就变无效,通过拉低 pipeX_valid信号)
            //(0000不刷新,0001刷新第一级,0010刷新第二级,0100刷新第三级,1000刷新第四级)
    input validin,             //输入是否有效
    input [31:0] cin_a,        //输入a
    input [31:0] cin_b,        //输入b
    input c_in,                //初始进位输入
    input out_allow,           //是否允许输出
    output validout,           //当前输出是否有效
    output [31:0] sum_out,   //流水线结果输出
    output c_out             //流水线进位输出
 );
 
    //各级流水线间临时寄存器(保存未计算的高位输入)
    reg [23:0] temp_a_t1,temp_b_t1; //第一、二级间
    reg [15:0] temp_a_t2,temp_b_t2; //第二、三级间
    reg [7:0]  temp_a_t3,temp_b_t3; //第三、四级间
 
    //各级流水线输出寄存器
    reg [7:0]  sum_out_t1; //第一级 
    reg [15:0] sum_out_t2; //第二级
    reg [23:0] sum_out_t3; //第三级
    reg [31:0] sum_out_t4; //第四级
 
    //各级流水线进位输出寄存器
    reg c_out_t1, c_out_t2, c_out_t3, c_out_t4;
    
    // 记录每一级当前数据是否有效
    reg pipe1_valid;
    reg pipe2_valid;
    reg pipe3_valid;
    reg pipe4_valid;
 
    //pipeline level 1 , calculate 7:0 bits of input, store 31:8 bits of input
    
    wire pipe1_allowin;       //表示pipe1能否被上一级刷新
    wire pipe1_ready_go;      //表示pipe1是否可以用于刷新下一级
    wire pipe1_to_pipe2_valid;//表示pipe1能否进入pipe2
 
    assign pipe1_ready_go = !stop[0];  //如果没有暂停,那么就可以Go
    // 如果pipe1中的值已经无效,或者这一轮一定会传给下一个,那么就可以进行接收
    assign pipe1_allowin = !pipe1_valid || pipe1_ready_go && pipe2_allowin;
    // 如果pipe1有效,并且pipe1可以进行传输,那么pipe1_to_pipe2_valid可以进行。
    assign pipe1_to_pipe2_valid = pipe1_valid && pipe1_ready_go;
 
    always@(posedge clk)begin
      if(rst)begin
         pipe1_valid <= 1'b0; //如果需要复位,那么pipe1_valid变为0,表示pipe1中的值不再有效
      end
      else if(refresh[0])begin
         pipe1_valid <= 1'b0; //如果需要刷新,那么pipe1_valid变为0,表示pipe1中的值不再有效
      end
      //既不复位也不刷新,当pipe1允许输入,若当前输入有效,pipe1_valid为1,值有效,否则0,值无效
      else if(pipe1_allowin)begin
         pipe1_valid <= validin;
      end
      //如果输入有效,pipe1能接收,则进行低位运算并保留高位
      if(validin && pipe1_allowin) begin
        {c_out_t1,sum_out_t1}  <= cin_a[7:0] + cin_b[7:0] + c_in;
        temp_a_t1 <= cin_a[31:8];
        temp_b_t1 <= cin_b[31:8];
      end
    end
 
    //pipeline level 2 , calculate 15:8 bits , store 31:16 bits
    wire pipe2_allowin;       
    wire pipe2_ready_go;      
    wire pipe2_to_pipe3_valid;
 
    assign pipe2_ready_go = !stop[1]; 
    assign pipe2_allowin = !pipe2_valid || pipe2_ready_go && pipe3_allowin;
    assign pipe2_to_pipe3_valid = pipe2_valid && pipe2_ready_go;
    always@(posedge clk)begin
      if(rst)begin
         pipe2_valid <= 1'b0;
      end
      else if(refresh[1])begin
         pipe2_valid <= 1'b0;
      end
      else if(pipe2_allowin)begin
         pipe2_valid <=  pipe1_to_pipe2_valid;
      end
      if(pipe1_to_pipe2_valid && pipe2_allowin) begin
        {c_out_t2,sum_out_t2} <= {{1'b0,temp_a_t1[7:0]} + {1'b0,temp_b_t1[7:0]} + c_out_t1, sum_out_t1};
        temp_a_t2<=temp_a_t1[23:8];
        temp_b_t2<=temp_b_t1[23:8];
      end
    end
    
    
    //pipeline level 3 , calculate 23:16 bits , store 31:24 bits
    
    wire pipe3_allowin;       
    wire pipe3_ready_go;      
    wire pipe3_to_pipe4_valid;
 
    assign pipe3_ready_go = !stop[2]; 
    assign pipe3_allowin = !pipe3_valid || pipe3_ready_go && pipe4_allowin;
    assign pipe3_to_pipe4_valid = pipe3_valid && pipe3_ready_go;
 
    always@(posedge clk)begin
      if(rst)begin
         pipe3_valid <= 1'b0;
      end
      else if(refresh[2])begin
         pipe3_valid <= 1'b0;
      end
      else if(pipe3_allowin)begin
         pipe3_valid <=  pipe2_to_pipe3_valid;
      end
      if(pipe2_to_pipe3_valid && pipe3_allowin)begin
        {c_out_t3,sum_out_t3} <= {{1'b0,temp_a_t2[7:0]} + {1'b0,temp_b_t2[7:0]} + c_out_t2, sum_out_t2};
        temp_a_t3<=temp_a_t2[15:8];
        temp_b_t3<=temp_b_t2[15:8];
       end
    end
    
    //pipeline level 4 , calculate 31:24 bits,and then output
    wire pipe4_allowin;       
    wire pipe4_ready_go;      
 
    assign pipe4_ready_go = !stop[3]; 
    assign pipe4_allowin = !pipe4_valid || pipe4_ready_go && out_allow;
 
    always@(posedge clk)begin
      if(rst)begin
         pipe4_valid <= 1'b0;
      end
      else if(refresh[3])begin
         pipe4_valid <= 1'b0;
      end
      else if(pipe4_allowin)begin
         pipe4_valid <=  pipe3_to_pipe4_valid;
      end
      if(pipe3_to_pipe4_valid && pipe4_allowin)begin
        {c_out_t4,sum_out_t4} <= {{1'b0,temp_a_t3[7:0]} + {1'b0,temp_b_t3[7:0]} + c_out_t3, sum_out_t3};
      end
    end
    
    //output
 
    assign validout = pipe4_valid && pipe4_ready_go;
    assign sum_out = sum_out_t4;
    assign c_out = c_out_t4;
endmodule

 

仿真顶层文件代码

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/04/28 00:06:05
// Design Name: 
// Module Name: sim
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
 
 
module sim();
    reg clk,rst; 
    reg [3:0] stop,refresh;     
    reg validin;
    reg [31:0] cin_a,cin_b;       
    reg c_in;
    reg out_allow;     
    wire validout;
    wire [31:0] sum_out;
    wire c_out;   
 
  stallable_pipeline_adder U1(.clk(clk),.rst(rst),.stop(stop),.refresh(refresh),
                .validin(validin), .cin_a(cin_a),.cin_b(cin_b),.c_in(c_in),
                .out_allow(out_allow),.validout(validout),.sum_out(sum_out),.c_out(c_out) );
    initial begin
        clk = 1;
        rst = 1;
        stop = 0;
        refresh = 0;
        validin = 1;
        cin_a = 0;
        cin_b = 0;
        c_in = 0;
        out_allow = 1;
        #10 rst = 0;
        #390 stop = 4'b0111;  //第10周期(即第400单位时)暂停流水线第二级,持续两周期
        #80 stop = 4'b0000;
        #360 refresh = 4'b0010; //第20周期刷新流水线第三级
        #40 refresh = 4'b0000;
    end
    
    always #20 clk = ~clk;  //20单位翻转一次,40单位构成一个周期
    //每个周期,输入改变
    always #40 cin_a = {$random} % 66666; //产生0--666665之间的随机数
    always #40 cin_b = {$random} % 66666;
endmodule

 

计算机组成原理实验一要求,在此记录一下,如有疑问或错误之处,欢迎留言指出!

参考资料:CQU计算机组成原理实验一指导书

 

请登录后发表评论