fpga流水线理解

`timescale 1ns / 1ps
module mul_addtree(mul_a, mul_b, mul_out, clk);
parameter MUL_WIDTH = 8;
parameter MUL_RESULT = 16;

input [MUL_WIDTH-1 : 0] mul_a;
input [MUL_WIDTH-1 : 0] mul_b;
input clk;
output [MUL_RESULT-1 : 0] mul_out;
reg [MUL_RESULT-1 : 0] mul_out = 0;
reg [MUL_RESULT-1 : 0] stored0 = 0;
reg [MUL_RESULT-1 : 0] stored1 = 0;
reg [MUL_RESULT-1 : 0] stored2 = 0;
reg [MUL_RESULT-1 : 0] stored3 = 0;
reg [MUL_RESULT-1 : 0] stored4 = 0;
reg [MUL_RESULT-1 : 0] stored5 = 0;
reg [MUL_RESULT-1 : 0] stored6 = 0;
reg [MUL_RESULT-1 : 0] stored7 = 0;
reg [MUL_RESULT-1 : 0] add01 = 0;
reg [MUL_RESULT-1 : 0] add23 = 0;
reg [MUL_RESULT-1 : 0] add45 = 0;
reg [MUL_RESULT-1 : 0] add67 = 0;
reg [MUL_RESULT-1 : 0] add0123 = 0;
reg [MUL_RESULT-1 : 0] add4567 = 0;

always @ (posedge clk)
      begin  //??????
         stored7 <= mul_b[7]?{1'b0,mul_a,7'b0}: 8'b0;
         stored6 <= mul_b[6]?{2'b0,mul_a,6'b0}: 8'b0;
         stored5 <= mul_b[5]?{3'b0,mul_a,5'b0}: 8'b0;
         stored4 <= mul_b[4]?{4'b0,mul_a,4'b0}: 8'b0;
         stored3 <= mul_b[3]?{5'b0,mul_a,3'b0}: 8'b0;
         stored2 <= mul_b[2]?{6'b0,mul_a,2'b0}: 8'b0;
         stored1 <= mul_b[1]?{7'b0,mul_a,1'b0}: 8'b0;
         stored0 <= mul_b[0]?{8'b0,mul_a}: 8'b0;  //这里是无符号乘法所以高8位补0,若为有符号数补码乘法,应该做最高位的符号扩展,即: {8{mul_a[8]},mul_a}
         add01 <= stored1 + stored0;
         add23 <= stored3 + stored2;
         add45 <= stored5 + stored4;
         add67 <= stored7 + stored6;
         add0123 <= add01 + add23;
         add4567 <= add45 + add67;
         mul_out <= add0123 + add4567;
      end

endmodule

流水线不会减小相应模块1的延时,但是会减少系统其他模块2的延时,提高系统的吞吐率。以乘法操作为例,假设做一次乘法需要3ms并且是在一个时钟周期中完成,则会导致时钟周期也只能为3ms,同时系统中还有一个并行或者级联模块2,操作时间只需要1ms,则模块2每个周期都要浪费2ms去等待模块1。若改用流水线设计,将模块1在一个周期做的事情分配到3个周期去做,每个周期只做一部分,这时时钟周期就可以减少到1ms,此时提高了系统的吞吐率,模块1的延时没改变,但是模块2的延时减小。
在纯粹的modelsim仿真中流水线设计没什么意义,因为没有引入实际电路的延时。
上面提到的乘法操作需要3ms就是实际电路的延时。电路延时本质是由门延时引入的(见崔葛瑾数电P116),门延时的存在引出触发器的建立时间和保持时间,导致触发器都是由系统时钟触发的,建立时间与保持时间约束了系统时钟周期不能太小。深入浅出玩转fpga P124对这些约束关系有一定的描述

你可能感兴趣的:(fpga)