在文章逻辑运算加法器中,介绍了两种加法运算方式,串行进位加法器
和进位选择加法器
,我们给出了逻辑门的实现并给出了C语言描述,本篇文章介绍另外一种加法计算方法:并行进位加法器
首先介绍一个概念,门延迟
。
我们知道,集成电路的主要工作单位是晶体管,晶体管虽然状态的改变非常迅速,但是毕竟有延迟,尤其随着晶体管串行数量的增大,延迟不可小觑。我们把一组电路中最大串行逻辑门的延迟门数量之和称为该电路的延迟门。延迟门是衡量电路性能的标志,下面是基本逻辑门的延迟门数量:
使用一个或门,一个与非门进行与门操作
实现的。我们从前面的文章能知道,串行进位加法器的主要性能瓶颈在于每一项的进位值需要前面的计算结果
。对于n位的两个相加的值
X
X
X和
Y
Y
Y,我们再次给出进位值和计算值的公式:
{
F
i
=
(
X
i
?
Y
i
)
?
C
i
?
1
,
计算值
C
i
=
(
X
i
C
i
?
1
)
+
(
Y
i
C
i
?
1
)
+
(
X
i
Y
i
)
,
进位值
\begin{cases} F_i = (X_i \bigoplus Y_i) \bigoplus C_{i-1},计算值\\ C_i = (X_i C_{i-1}) + (Y_i C_{i-1}) + (X_i Y_i ),进位值 \end{cases}
{Fi?=(Xi??Yi?)?Ci?1?,计算值Ci?=(Xi?Ci?1?)+(Yi?Ci?1?)+(Xi?Yi?),进位值?
用电路表示
F
i
F_i
Fi?的值为:
用电路表示
C
i
C_i
Ci?的值为,可以看到进位的计算的门延迟是2:
根据布尔运算
C
i
=
(
X
i
C
i
?
1
)
+
(
Y
i
C
i
?
1
)
+
(
X
i
Y
i
)
=
X
i
Y
i
+
(
X
i
+
Y
i
)
C
i
?
1
C_i =(X_i C_{i-1}) + (Y_i C_{i-1}) + (X_i Y_i ) = X_i Y_i + (X_i+Y_i)C_{i-1}
Ci?=(Xi?Ci?1?)+(Yi?Ci?1?)+(Xi?Yi?)=Xi?Yi?+(Xi?+Yi?)Ci?1?
也就是说:
C
1
=
X
1
Y
1
+
(
X
1
+
Y
1
)
C
0
C_1 = X_1 Y_1 + (X_1+Y_1)C_{0}
C1?=X1?Y1?+(X1?+Y1?)C0?
C
2
=
X
2
Y
2
+
(
X
2
+
Y
2
)
C
1
=
X
2
Y
2
+
(
X
2
+
Y
2
)
(
X
1
Y
1
+
(
X
1
+
Y
1
)
C
0
)
=
X
2
Y
2
+
(
X
2
+
Y
2
)
(
X
1
Y
1
)
+
(
X
2
+
Y
2
)
(
X
1
+
Y
1
)
C
0
\begin{aligned} C_2 &= X_2 Y_2 + (X_2+Y_2)C_{1}\\ &=X_2 Y_2 + (X_2+Y_2)(X_1 Y_1 + (X_1+Y_1)C_{0})\\ &=X_2 Y_2 + (X_2+Y_2)(X_1 Y_1) + (X_2+Y_2)(X_1+Y_1)C_{0} \end{aligned}
C2??=X2?Y2?+(X2?+Y2?)C1?=X2?Y2?+(X2?+Y2?)(X1?Y1?+(X1?+Y1?)C0?)=X2?Y2?+(X2?+Y2?)(X1?Y1?)+(X2?+Y2?)(X1?+Y1?)C0??
C
3
=
X
3
Y
3
+
(
X
3
+
Y
3
)
C
2
=
X
3
Y
3
+
(
X
3
+
Y
3
)
(
X
2
Y
2
+
(
X
2
+
Y
2
)
(
X
1
Y
1
)
+
(
X
2
+
Y
2
)
(
X
1
+
Y
1
)
C
0
)
=
X
3
Y
3
+
(
X
3
+
Y
3
)
(
X
2
Y
2
)
+
(
X
3
+
Y
3
)
(
X
2
+
Y
2
)
(
X
1
Y
1
)
+
(
X
3
+
Y
3
)
(
X
2
+
Y
2
)
(
X
1
+
Y
1
)
C
0
\begin{aligned} C_3 &= X_3 Y_3 + (X_3+Y_3)C_{2}\\ &=X_3 Y_3 + (X_3+Y_3)(X_2 Y_2 + (X_2+Y_2)(X_1 Y_1) + (X_2+Y_2)(X_1+Y_1)C_{0})\\ &=X_3 Y_3 + (X_3+Y_3)(X_2 Y_2) + (X_3+Y_3)(X_2+Y_2)(X_1 Y_1)+(X_3+Y_3)(X_2+Y_2)(X_1+Y_1)C_{0} \end{aligned}
C3??=X3?Y3?+(X3?+Y3?)C2?=X3?Y3?+(X3?+Y3?)(X2?Y2?+(X2?+Y2?)(X1?Y1?)+(X2?+Y2?)(X1?+Y1?)C0?)=X3?Y3?+(X3?+Y3?)(X2?Y2?)+(X3?+Y3?)(X2?+Y2?)(X1?Y1?)+(X3?+Y3?)(X2?+Y2?)(X1?+Y1?)C0??
我们假设
P
i
=
X
i
+
Y
i
P_i = X_i+Y_i
Pi?=Xi?+Yi?,
G
i
=
X
i
Y
i
G_i = X_iY_i
Gi?=Xi?Yi?,也就是
P
i
P_i
Pi?是一个逻辑或门,
G
i
G_i
Gi?是一个逻辑与门,上面的公式可以写成如下形式:
C
1
=
G
1
+
P
1
C
0
C_1 = G_1 + P_1C_{0}
C1?=G1?+P1?C0?
C
2
=
G
2
+
P
2
G
1
+
P
2
P
1
C
0
C_2= G_2 + P_2G_1+ P_2P_1C_{0}
C2?=G2?+P2?G1?+P2?P1?C0?
C
3
=
G
3
+
P
3
G
2
+
P
3
P
2
G
1
+
P
3
P
2
P
1
C
0
C_3= G_3 + P_3G_2+P_3P_2G_1+P_3P_2P_1C_{0}
C3?=G3?+P3?G2?+P3?P2?G1?+P3?P2?P1?C0?
同理:
C
4
=
G
4
+
P
4
G
3
+
P
4
P
3
G
2
+
P
4
P
3
P
2
G
1
+
P
4
P
3
P
2
P
1
C
0
C_4= G_4 + P_4G_3+P_4P_3G_2+P_4P_3P_2G_1+P_4P_3P_2P_1C_{0}
C4?=G4?+P4?G3?+P4?P3?G2?+P4?P3?P2?G1?+P4?P3?P2?P1?C0?
即然
C
4
、
C
3
、
C
2
、
C
1
C_4、C_3、C_2、C_1
C4?、C3?、C2?、C1?都能使用
C
0
C_0
C0?来表示,那么我们就可以通过设计电路来同时计算
C
4
、
C
3
、
C
2
、
C
1
C_4、C_3、C_2、C_1
C4?、C3?、C2?、C1?的值,然后根据计算后的值进行每一位的加法运算。设计后电路图如下:
上面的电路称为先行进位部件
,简称CLA(Carry Lookahead Unit)
我们把输入的
X
、
Y
X、Y
X、Y和数值位的计算添加进来,然后把
C
i
C_i
Ci?的值也添加到加法器,这样便组成了一个全先行进位加法
,电路图如下:
我们看一下上面电路的门延迟:
并行
着,并且
M
i
=
X
i
?
Y
i
M_i = X_i \bigoplus Y_i
Mi?=Xi??Yi?异或操作门延迟正好也是3,也就是到现在为止,一共的门延迟为3所有的门延迟一共为6
下面,给出CLA电路的C语言描述:
/**
* CLA电路的C语言描述,计算4位的加法值
* in_1:输入X
* in_2:输入Y
* c0:默认的进位项
*/
extern long alu_add_cla(long in_1, long in_2, long* c0);
long alu_add_cla(long in_1, long in_2, long* c0)
{
// 初始结果为0
long result = 0;
// 下面这四组并行,每一组在电路中需要1级门延迟
long x1 = alu_bit(in_1, 0); // 获取输入1的第0位
long y1 = alu_bit(in_2, 0); // 获取输入2的第0位
long p1 = or_gate(x1, y1);
long g1 = and_gate(x1, y1);
long x2 = alu_bit(in_1, 1); // 获取输入1的第1位
long y2 = alu_bit(in_2, 1); // 获取输入2的第1位
long p2 = or_gate(x2, y2);
long g2 = and_gate(x2, y2);
long x3 = alu_bit(in_1, 2); // 获取输入1的第2位
long y3 = alu_bit(in_2, 2); // 获取输入2的第2位
long p3 = or_gate(x3, y3);
long g3 = and_gate(x3, y3);
long x4 = alu_bit(in_1, 3); // 获取输入1的第3位
long y4 = alu_bit(in_2, 3); // 获取输入2的第3位
long p4 = or_gate(x4, y4);
long g4 = and_gate(x4, y4);
// CLA电路实现,需要门延迟2
long c1 = or_gate(g1, and_gate(p1, *c0));
long c2 = or_gate(or_gate(g2, and_gate(p2, g1)),and_gate(and_gate(p2, p1), *c0));
long c3 = or_gate(or_gate(or_gate(g3, and_gate(p3, g2)), and_gate(and_gate(p3, p2), g1)), and_gate(and_gate(and_gate(p3, p2), p1), *c0));
long c4 = or_gate(or_gate(or_gate(or_gate(g4, and_gate(p4, g3)), and_gate(and_gate(p4, p3), g2)), and_gate(and_gate(and_gate(p4, p3), p2), g1)), and_gate(and_gate(and_gate(and_gate(p4, p3), p2), p1), *c0));
// 异或操作,门延迟3,与上面的并行
long m1 = xor_gate(x1, y1);
long m2 = xor_gate(x2, y2);
long m3 = xor_gate(x3, y3);
long m4 = xor_gate(x4, y4);
// 最后的异或计算,门延迟3
long r1 = xor_gate(m1, *c0);
long r2 = xor_gate(m2, c1);
long r3 = xor_gate(m3, c2);
long r4 = xor_gate(m4, c3);
result |= r4<<3;
result |= r3<<2;
result |= r2<<1;
result |= r1;
// 将进位值返回给c0
*c0 = c4;
return result;
}
有了四位的全先行进位加法器我们可以实现多位的加法器了,这里还可以分为两种:
首先看第一种:
单级先行进位加法器是将
n
n
n个全先行进位加法器串起来,可以实现
4
n
4n
4n位的加法运算,我们计算一下对于
4
n
4n
4n位的单级先行进位加法器,门延迟是多少?
一共消耗2n+2个门延迟
下面这是一个16位的加法器示意图:
现在我们可以给出64位加法器的C语言描述了,因为有了之前CLA的定义,非常简单:
/**
* CLA电路的串行加法器
* in_1:输入X
* in_2:输入Y
* bits:计算的位数
* c0:默认的进位项
*/
extern long alu_add_cla_64(long in_1, long in_2,long bits, long* c0);
long alu_add_cla_64(long in_1, long in_2,long bits, long* c0)
{
long result = 0;
for(int i = 0;i<bits;i+=4)
{
result |= alu_add_cla(in_1>>i,in_2>>i,c0)<<i;
}
return result;
}
int main(int argc, const char * argv[])
{
int c= 0;
long r0 = alu_add_cla_64(-398, 283, 64, &c);
printf("r0 = %ld\n",r0);
long r1 = alu_add_cla_64(398, 283, 64, &c);
printf("r1 = %ld\n",r1);
}
输出结果为:
r0 = -115
r1 = 681
最后,我们在研究一下多级先行进位加法器,所谓多级先行进位加法器就是将多个全先行进位加法器并起来,而不是串起来
,先看一下16位的两级先行进位加法器,整个逻辑是下面这个样子:
下面给出16位两级先行进位加法器的电路图:
好了,了解了两级先行进位加法器以后,就可以构建三级的先行进位加法器。三级能够进行64位的加减操作,对于我们目前来说足够了,并且三级的并行操作能使性能达到最优。
三级的先行进位加法器构建如下:
最后,给出三级先行进位加法器的C语言描述,代码比较多,为了展示电路级别的操作,逻辑写的比较复杂,其实实际上很多操作都是并行触发的
/**
* 三级先行进位加法器
* in_1:输入X
* in_2:输入Y
* bits:计算的位数
* c0:默认的进位项
* return:返回计算结果
*/
extern long alu_add_bcla_64(long in_1, long in_2,long bits, long* c0);
long alu_add_bcla_64(long in_1, long in_2,long bits, long* c0)
{
// 计算p、g,一共64位
long out_p = alu_or(in_1, in_2,sizeof(long)*8);
long out_g = alu_and(in_1, in_2,sizeof(long)*8);
// 第二级p、g,一共16位
long out_pm=0;
long out_gm=0;
// 第三极p、g,一共4位
long out_pm2=0;
long out_gm2=0;
// 计算第二级p、g,应该是并行的
for(int i=0;i<16;i++)
{
long p4 =alu_bit(out_p,i*4+3);
long p3 =alu_bit(out_p,i*4+2);
long p2 =alu_bit(out_p,i*4+1);
long p1 =alu_bit(out_p,i*4+0);
long g4 =alu_bit(out_g,i*4+3);
long g3 =alu_bit(out_g,i*4+2);
long g2 =alu_bit(out_g,i*4+1);
long g1 =alu_bit(out_g,i*4+0);
out_pm|=((and_gate(and_gate(and_gate(p4, p3), p2), p1))<<i);
out_gm|=((or_gate(or_gate(or_gate(g4, and_gate(p4, g3)), and_gate(and_gate(p4, p3), g2)), and_gate(and_gate(and_gate(p4, p3), p2), g1)))<<i);
}
// 计算第三极p、g,应该是并行的
for(int i=0;i<4;i++)
{
long p4 =alu_bit(out_pm,i*4+3);
long p3 =alu_bit(out_pm,i*4+2);
long p2 =alu_bit(out_pm,i*4+1);
long p1 =alu_bit(out_pm,i*4+0);
long g4 =alu_bit(out_gm,i*4+3);
long g3 =alu_bit(out_gm,i*4+2);
long g2 =alu_bit(out_gm,i*4+1);
long g1 =alu_bit(out_gm,i*4+0);
out_pm2|=((and_gate(and_gate(and_gate(p4, p3), p2), p1))<<i);
out_gm2|=((or_gate(or_gate(or_gate(g4, and_gate(p4, g3)), and_gate(and_gate(p4, p3), g2)), and_gate(and_gate(and_gate(p4, p3), p2), g1)))<<i);
}
// 根据第三极p、g和c0计算c16、c32、c48、c64,应该是并行的
long p4 =alu_bit(out_pm2,3);
long p3 =alu_bit(out_pm2,2);
long p2 =alu_bit(out_pm2,1);
long p1 =alu_bit(out_pm2,0);
long g4 =alu_bit(out_gm2,3);
long g3 =alu_bit(out_gm2,2);
long g2 =alu_bit(out_gm2,1);
long g1 =alu_bit(out_gm2,0);
long c16 = or_gate(g1, and_gate(p1, *c0));
long c32 = or_gate(or_gate(g2, and_gate(p2, g1)),and_gate(and_gate(p2, p1), *c0));
long c48 = or_gate(or_gate(or_gate(g3, and_gate(p3, g2)), and_gate(and_gate(p3, p2), g1)), and_gate(and_gate(and_gate(p3, p2), p1), *c0));
// c64是最终输出的c0
long c64 = or_gate(or_gate(or_gate(or_gate(g4, and_gate(p4, g3)), and_gate(and_gate(p4, p3), g2)), and_gate(and_gate(and_gate(p4, p3), p2), g1)), and_gate(and_gate(and_gate(and_gate(p4, p3), p2), p1), *c0));
// 准备计算一级的所有c值,下面这些是二级所有的c值
long c_2[4]={*c0,c16,c32,c48};
// 最终输出结果
long result = 0;
// 当前计算第几位
int r = 0;
for(int i=0;i<4;i++)
{
// 根据二级的c值计算一级的c值
long p4 =alu_bit(out_pm,i*4+3);
long p3 =alu_bit(out_pm,i*4+2);
long p2 =alu_bit(out_pm,i*4+1);
long p1 =alu_bit(out_pm,i*4+0);
long g4 =alu_bit(out_gm,i*4+3);
long g3 =alu_bit(out_gm,i*4+2);
long g2 =alu_bit(out_gm,i*4+1);
long g1 =alu_bit(out_gm,i*4+0);
long c1 = or_gate(g1, and_gate(p1, c_2[i]));
long c2 = or_gate(or_gate(g2, and_gate(p2, g1)),and_gate(and_gate(p2, p1), c_2[i]));
long c3 = or_gate(or_gate(or_gate(g3, and_gate(p3, g2)), and_gate(and_gate(p3, p2), g1)), and_gate(and_gate(and_gate(p3, p2), p1), c_2[i]));
// 这个没啥用,其实可以不用计算,但是其实电路中也没啥影响,毕竟都是并行的,都消耗2个门延迟
//long c4 = or_gate(or_gate(or_gate(or_gate(g4, and_gate(p4, g3)), and_gate(and_gate(p4, p3), g2)), and_gate(and_gate(and_gate(p4, p3), p2), g1)), and_gate(and_gate(and_gate(and_gate(p4, p3), p2), p1), c_2[i]));
// 根据1级的c值计算0级所有的c值
long c_1[4]={c_2[i],c1,c2,c3};
for(int j=0;j<4;j++)
{
long p4 =alu_bit(out_p,i*16+j*4+3);
long p3 =alu_bit(out_p,i*16+j*4+2);
long p2 =alu_bit(out_p,i*16+j*4+1);
long p1 =alu_bit(out_p,i*16+j*4+0);
long g4 =alu_bit(out_g,i*16+j*4+3);
long g3 =alu_bit(out_g,i*16+j*4+2);
long g2 =alu_bit(out_g,i*16+j*4+1);
long g1 =alu_bit(out_g,i*16+j*4+0);
long c1 = or_gate(g1, and_gate(p1, c_1[j]));
long c2 = or_gate(or_gate(g2, and_gate(p2, g1)),and_gate(and_gate(p2, p1), c_1[j]));
long c3 = or_gate(or_gate(or_gate(g3, and_gate(p3, g2)), and_gate(and_gate(p3, p2), g1)), and_gate(and_gate(and_gate(p3, p2), p1), c_1[j]));
//long c4 = or_gate(or_gate(or_gate(or_gate(g4, and_gate(p4, g3)), and_gate(and_gate(p4, p3), g2)), and_gate(and_gate(and_gate(p4, p3), p2), g1)), and_gate(and_gate(and_gate(and_gate(p4, p3), p2), p1), c_1[j]));
// 将c值写入result,result就相当于一个64位的电路
result |= c3<<(r+3);
result |= c2<<(r+2);
result |= c1<<(r+1);
result |= c_1[j]<<(r);
r+=4;
}
}
// 这里保存进位值
*c0 = c64;
// 两个异或操作获取位的计算值
return alu_xor(alu_xor(in_1, in_2,sizeof(long)*8), result,sizeof(long)*8);
}
int main(int argc, const char * argv[])
{
long c= 0;
long r0 = alu_add_bcla_64(-398, 283, 64, &c);
printf("r0 = %ld\n",r0);
long r1 = alu_add_bcla_64(398, 283, 64, &c);
printf("r1 = %ld\n",r1);
}
结果为:
r0 = -115
r1 = 681
我们从不断优化的方案可以看出时间和空间的权衡方案,性能和速度的提升几乎必然伴随着电路的复杂度提高,需要更多的空间来安排电路的排线,需要更多的空间来放置更多的逻辑门。本篇文章完成时,荷兰的2纳米光刻机已经开始交付给intel。可预见的是,集成电路还会有大幅提升的空间。