1、流水线技术
硬件描述语言的一个突出优点就是指令执行的并行性。多条语句能够在相同时钟周期内并行处理多个信号数据。但是当数据串行输入时,指令执行的并行性并不能体现出其优势。而且很多时候有些计算并不能在一个或两个时钟周期内执行完毕,如果每次输入的串行数据都需要等待上一次计算执行完毕后才能开启下一次的计算,那效率是相当低的。流水线就是解决多周期下串行数据计算效率低的问题。
流水线(pipe_line)的设计方法已在高性能的、需要经常进行大规模运算的系统中得到广泛的应用,如cpu(中央处理器)等。流水线处理方法之所以能够很大程度上提高数据流的处理速度,是因为它进行了处理模块的复制,也能很好的体现了面积换速度的思想。
如果某个设计可以分为若干步骤进行处理,而且整个数据处理过程是单向的,即没有反馈运算或者迭代运算,前一个步骤的输入即使下一个步骤的输出,就可以考虑采用流水线设计方法来提高系统的工作频率。
2、流水线设计的概念
专业词汇解释:
首次延迟(latency):将(从输入到输出)最长的路径进行初始化所需要的时间总量。
吞吐延迟:执行一次重复性操作所需要的时间总量。
吞吐率:单位时间内通过某通信信道(a communication channel)或某个节点成功交付数据的平均速率,通常以每秒比特数(bps, bits per second )为单位。
所谓流水线设计实际上是把规模较大、层次较多的组合逻辑电路分为几个级,在每一级插入寄存器组并暂存中间数据。
K级流水线就是从组合逻辑的输入到输出恰好有K个寄存器组(分为K级,每一级都有一个寄存器组),上一级的输出是下一级的输入而又无反馈的电路。
上图表示如何将组合逻辑设计转换为相同组合逻辑功能的流水线设计。
组合逻辑包括两级,延迟分析如下:
第一级延迟:max(T1,T3),即T1和T3两个延迟中的最大值。
第二级延迟:T2,即T2的延迟。
总传播延迟:max(T1,T3)+ T2
从输入到输出的每一级插入寄存器后,流水线设计的延迟分析如下:
第一级延迟:max(T1,T3)+ Tco,即第一级寄存器所具有的总的延迟为T1和T3两个延迟中的最大值加上寄存器的Tco(触发时间)。
第二级延迟:T2 + Tco,即第二级寄存器延迟为T2的时延加上Tco
总传输延迟:max(max(T1,T3)+ Tco,(T2 + Tco))
流水线的设计需要两个时钟周期来获取第一个计算结果,而只需要一个时钟周期来获取随后的计算结果。 获取第一个结果所需的两个时钟周期为首次延迟,而执行后面操作所需的一个时钟周期便为吞吐延迟。
3、流水线设计的优缺点
优点:提高吞吐率(throughput)。
假设T1、T2、T3具有相同的传递延迟Tpd。组合逻辑设计和流水线设计的延迟对比如下:
首次延迟 | 吞吐延迟 | |
---|---|---|
组合逻辑设计 | 2*Tpd | 2*Tpd |
流水线设计 | 2*(Tpd+Tco) | Tpd+Tco |
对于FPGA来说,器件的延迟如T1、T2、T3相对于触发器的Tco要长的多,并且寄存器的建立时间Tsu也要比器件的延迟快的多。如果FPGA硬件能提供快速的Tco,则流水线设计相对于同样功能的组合逻辑设计能提供更大的吞吐量。
缺点:消耗的寄存器资源更多。
将复杂的组合逻辑转换成流水线设计的过程中,为了保证中间的计算结果都在同一时钟内得到,必须在各级之间加入更多的寄存器。
4、实验目的
开发平台:vivado2020.1
开发板:Xilinx Artix-7 xc7a100tfgg676-2
目的:编写一个8位的4级流水线加法器。
5、程序设计
8位4级流水线的RTL代码如下:
//8位4级流水线加法器
module addr_4(
input sys_rst_n,
input sys_clk,
input cin,
input [7:0] a,
input [7:0] b,
//8位流水线加法器
output reg cout,
output reg [7:0] sum,
//8位单周期加法器
output reg cout1,
output reg [7:0] sum1
);
//-----------------------------单周期加法器---------------------------------------//
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n)
begin
cout1<=1'b0;
sum1<=8'd0;
end
else
{cout1,sum1} <= a+b+cin;
end
//-------------------------------流水线加法器--------------------------------------//
reg cin_temp;
reg [7:0] a_temp,b_temp;
reg firstc,secondc,thirdc;
reg [1:0] firsts;
reg [3:0] seconds;
reg [5:0] thirds;
reg [5:0] a_temp1,b_temp1;
reg [3:0] a_temp2,b_temp2;
reg [1:0] a_temp3,b_temp3;
//将输入的值先缓存
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
cin_temp <= 1'b0;
a_temp <= 7'b0;
b_temp <= 7'b0;
end
else begin
cin_temp <= cin;
a_temp <= a;
b_temp <= b;
end
end
//第一级
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
firstc <= 1'b0;
firsts <= 2'b0;
a_temp1 <= 6'b0;
b_temp1 <= 6'b0;
end
else begin
{firstc,firsts} <= a_temp[1:0] + b_temp[1:0] + cin_temp;
a_temp1 <= a_temp[7:2];
b_temp1 <= b_temp[7:2];
end
end
//第二级
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
secondc <= 1'b0;
seconds <= 4'b0;
a_temp2 <= 4'b0;
b_temp2 <= 4'b0;
end
else begin
{secondc,seconds[3:2]} <= a_temp1[1:0] + b_temp1[1:0] + firstc;
a_temp2 <= a_temp1[5:2];
b_temp2 <= b_temp1[5:2];
seconds[1:0] <= firsts;
end
end
//第三级
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
thirdc <= 1'b0;
thirds <= 6'b0;
a_temp3 <= 2'b0;
b_temp3 <= 2'b0;
end
else begin
{thirdc,thirds[5:4]} <= a_temp2[1:0] + b_temp2[1:0] + secondc;
a_temp3 <= a_temp2[3:2];
b_temp3 <= b_temp2[3:2];
thirds[3:0] <= seconds;
end
end
//第四级
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
cout <= 1'b0;
sum <= 2'b0;
end
else begin
{cout,sum[7:6]} <= a_temp3[1:0] + b_temp3[1:0] + thirdc;
sum[5:0] <= thirds;
end
end
endmodule
Testbech编写如下:
`timescale 1ns / 1ns
//4级流水线加法器
module add_4_tb;
addr_4 addr4_inst(
.sys_rst_n(sys_rst_n) ,
.sys_clk (sys_clk) ,
.cout (cout) ,
.sum (sum) ,
.a (a) ,
.b (b) ,
.cin (cin) ,
.sum1 (sum1) ,
.cout1(cout1)
);
reg sys_rst_n;
reg sys_clk;
wire cout;
wire [7:0] sum;
reg [7:0] a;
reg [7:0] b;
reg cin;
wire [7:0] sum1;
wire cout1;
initial begin
sys_rst_n = 0;
sys_clk = 0;
@(posedge sys_clk) begin
sys_rst_n = 1;
a = 8'd120;
b = 8'd100;
cin = 1;
end
$write("sum is %d ,cout is %d\n",sum,cout);
$write("sum1 is %d,cout1 is %d\n",sum1,cout1);
$write("time is : %t\n",$time);
@(posedge sys_clk) begin
sys_rst_n = 1;
a = 8'd10;
b = 8'd255;
cin = 1;
end
$write("sum is %d ,cout is %d\n",sum,cout);
$write("sum1 is %d,cout1 is %d\n",sum1,cout1);
$write("time is : %t\n",$time);
@(posedge sys_clk) begin
sys_rst_n = 1;
a = 8'd92;
b = 8'd33;
cin = 0;
end
$write("sum is %d ,cout is %d\n",sum,cout);
$write("sum1 is %d,cout1 is %d\n",sum1,cout1);
$write("time is : %t\n",$time);
#20
$write("sum is %d ,cout is %d\n",sum,cout);
$write("sum1 is %d,cout1 is %d\n",sum1,cout1);
$write("time is : %t\n",$time);
#20
$write("sum is %d ,cout is %d\n",sum,cout);
$write("sum1 is %d,cout1 is %d\n",sum1,cout1);
$write("time is : %t\n",$time);
#20
$write("sum is %d ,cout is %d\n",sum,cout);
$write("sum1 is %d,cout1 is %d\n",sum1,cout1);
$write("time is : %t\n",$time);
#20
$write("sum is %d ,cout is %d\n",sum,cout);
$write("sum1 is %d,cout1 is %d\n",sum1,cout1);
$write("time is : %t\n",$time);
#20
$write("sum is %d ,cout is %d\n",sum,cout);
$write("sum1 is %d,cout1 is %d\n",sum1,cout1);
$write("time is : %t\n",$time);
#20
$stop;
end
always #10 sys_clk = ~sys_clk;
endmodule
得到仿真结果如下:
5、引用
1、verilog数字系统设计教程
2、深入浅出玩转fpga