2.1打破流水线
打破流水的方法与上一章介绍的为提高吞吐量使用的展开循环的方法是一个相反的操作。通过上一章的介绍,我们知道当展开循环创建流水的时候,需要消耗更多的面积,因为我们需要更多的资源来保存中间值或者需要复制一些需要并行运行的运算结构。相反,当我们需要最小化一个设计的面积的时候,我们需要执行相反的操作。也就是打破流水,让逻辑资源得到重复利用。该方法通常用于优化在各个流水阶段复制逻辑的高度流水化的设计。
我们来看一个定点小数乘法的例子代码如下所示。A为正常整型数据格式,小数点固定位于最低比特的右边;而输入B的小数点固定位于最高比特的左边。换句话说,用B从0到1来对A进行量化。
module mult8(
output [7:0] product,
input [7:0] A,
input [7:0] B,
input clk);
reg [15:0] prod16;
assign product=prod16[15:8];
always @ (posedge clk)
prod16<=A*B;
endmodule
当上述代码被执行时,每个时钟周期都会产生一个新的结果。从上述代码中我们看不到明显的流水线结构,只是看到一系列明显的寄存器而已。但是需要注意的是,乘法器本身是一个相当长的逻辑链,这个逻辑链可以通过插入寄存器轻松地将其流水线化。我们就是需要将这个乘法器“折叠”(阻断其流水化)起来。可以通过使用一系列移位寄存器和加法操作来实现将乘法“折叠”起来,如下述代码所示:
module mult8(
output done,
output reg [7:0] product,
input [7:0] A,
input [7:0] B,
input clk,
input start);
reg [4:0] multcounter;//计数统计移位和加法数目
reg [7:0] shiftB;//B的移位寄存器
reg [7:0] shiftA;//A的移位寄存器
wire adden;//加法使能
assign adden=shiftB[7]&!done;
assign done=multcounter[3];
always @ (posedge clk) begin
if (start) multcounter<=0; //移位加法操作实现乘法计数器累加
else if (!done) multcounter<=multcounter+1;
if (start) shiftB<=B; //移位寄存器B移位
else shiftB[7:0]<={shiftB[6:0],1’b0};
if (start) shiftA<=A; //移位寄存器A移位
else shiftA[7:0]<={shiftA[7],shiftA[7:1]};
if (start) product<=0; //计算乘法结果
else if (adden)product<=product+shiftA;
end
endmodule
通过上述修改后,如图2-1所示,新的乘法结构是一个累加器加上移位后的输入A,根据移位后输入B的比特将累加器的结果寄存输出。如此,我就完全地去除了在产生单个时钟周期乘法所必需的逻辑树,并且由一些移位寄存器和一个加法来代替了一个乘法操作。这是一个非常紧凑的乘法结构,但是需要8个时钟周期完成一个乘法操作。另外需要注意的是,该乘法无须任何的特殊顺序控制。只需要一个简单的计数器来告诉我们什么时候结束移位和加法。后面我们会详细介绍到,其实这些控制并非是如此的微不足道。
图2-1:移位累加乘法器