在FPGA中计算两个数据相加和C语言中的加法不太一样,在FPGA中是二进制相加,要考虑数据的进位、数据时单比特还是多比特,数据若位宽过大引起的时延该怎么解决,本文就对以上问题进行梳理
另外我想挖个新坑,把HDLBits中的内容整理一下,就从加法器进行入手,等写好了就过来填坑
首先区别,什么是半加器,什么是全加器,从下面图中可以看到
半加器:没有来自上一级的进位(cin), {cout,sum} = a + b
全加器:有来自上一级的进位(cin), {cout,sum} = a + b + cin
并且可以使用2个半加器构成一个全加器,即第一个半加器计算 sum = a + b
,第二个半加器计算 cin + sum
此处考虑多比特全加器
由上图可知,多比特全加器,可以将数据从低到高一次使用单比特的全加器求解,他还有一个名字,叫:等波纹进位加法器
,这个名字就很形象,全加器一级一级地如同波纹传递着上一级的cin
举例:
但是此时出现一个新的问题,如果是100bit的数据进行相加,那就需要100个单比特的全加器,这样会导致时延
那如何解决这个问题呢?
为了解决等波纹进位带来的时延,提出一种基于并行处理的思想的超前进位加法器
核心思想:使用并行求解 C o u t C_{out} Cout
牛客刷题 - VL12 4bit超前进位加法器电路
最开始接触到流水线是在FFT算法实现中,这里——> FFT算法实现
再挖个坑
,写一篇详细介绍流水线的文章
填坑
:流水线
通过分析发现,8bit的全加器是一种单向的、并且前面输出的cout要作为后面的cin,因此可以使用流水线来提高工作频率
实现的效果
:第一级流水线在每个时钟上升沿到来时,只负责计算数据低4位的全加结果,以及将高4位打一拍;第二级流水线只负责将第一级流水线的结果进行相加和拼接
// 用流水线实现8bit加法
module water_add8 (
input clk,
input rst_n,
input [7:0] a,
input [7:0] b,
input cin,
output [7:0] sum,
output cout
);
reg cout_low4_1;
reg [3:0] sum_low4_4;
reg [3:0] a_high4_4;
reg [3:0] b_high4_4;
reg cout_reg;
reg [7:0] sum_reg;
// 第一级流水线
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cout_low4_1 <= 1'd0;
sum_low4_4 <= 4'd0;
end
else
{cout_low4_1, sum_low4_4[3:0]} <= a[3:0] + b[3:0] + cin;
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
a_high4_4 <= 1'd0;
b_high4_4 <= 4'd0;
end
else
a_high4_4 <= a[7:4];
b_high4_4 <= b[7:4];
end
// 第二级流水线
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cout_reg <= 1'd0;
sum_reg <= 8'd0;
end
else
{cout_reg, sum_reg[7:0]} <= {5'd0+a_high4_4[3:0]+b_high4_4[3:0]+cout_low4_1,sum_low4_4[3:0]}; //{{{1'b0,ina_reg[3:0]}+{1'b0,inb_reg[3:0]}+cout1},sum1[3:0]}
end
assign cout = cout_reg;
assign sum = sum_reg;
endmodule
`timescale 1ns/1ns
module tb_add8_2;
reg clk;
reg rst_n;
reg cin;
reg [7:0] a;
reg [7:0] b;
wire [7:0] sum;
wire cout;
initial begin
clk=0;
rst_n=0;
cin=1;
a=2;
b=3;
#20
rst_n=1;
end
initial begin
#30
data_ina();
end
initial begin
#30;
data_inb();
end
always #10 clk = ~clk;
task data_ina();
integer i;
begin
for(i=0;i<256;i=i+1)
begin
@(posedge clk)
a<=i;
end
end
endtask
task data_inb();
integer j;
begin
for(j=1;j<254;j=j+1)
begin
@(posedge clk)
b<=j;
cin<=b[0];
end
end
endtask
water_add8 water_add8_inst(
.clk(clk),
.rst_n(rst_n),
.a(a),
.b(b),
.cin(cin),
.sum(sum),
.cout(cout)
);
endmodule
HDLBits-进位选择加法器这里提到过这种思想,刷了题这不就用上了哇哈哈哈
结构:使用3个全加器模块,1个用来求解数据低16位,另外2个分别在cin=0、cin=1的情况下求解出sum,然后通过二选一选择器得到高16位的输出,最后将低16位和高16位的输出拼接即可
module top_module(
input [31:0] a,
input [31:0] b,
output [31:0] sum
);
wire cout1;
wire [15:0] sum0;
wire [15:0] sum1;
wire [15:0] sum2;
wire [15:0] sum_mux;
always @(*) begin
case(cout1)
1'd0: sum_mux = sum1;
1'd1: sum_mux = sum2;
default: sum_mux = sum1;
endcase
end
// 上面的组合逻辑还可以使用条件判断
// assign sum_mux = (cout1)?sum2:sum1;
assign sum = {sum_mux,sum0};