关于除法电路

2015年12月07日 15:15    发布者:designapp
除法,这个小学4年纪就开始学习和使用的方法却一直是我这个ASIC工程师心中的痛。我一直在思考如何能找到一个简单(硬件资源少)而快捷(时钟排数少)的通用除法电路。

其实简单的说除法可以用迭代的减法来实现,但是对于硬件,这恐怕要花很多时间。我也一直没有找到实现任意除法的好方法。但是对于某些除数固定的除法还是有一些办法的。

1)最容易想到的就是ROM查找表,但是ROM毕竟不是我们的目标,虽然ROM有时是不错的方法。

2)我开始仔细考虑这个问题是在做264解码时必须要处理QP的问题。这是一个除以6的计算,由于被除数不会大于52(6bit),所以我简化了一个组合逻辑来实现。代码如下:

always@(idata)
begin
case(idata)
3'b000: begin
oquotient = 3'b000;
if (idata==2'b11)
begin
oquotient = 1'b1;
end
else
begin
oquotient = 1'b0;
end
end
3'b001: begin
oquotient = 2'b00;
if (idata==1'b1)
begin
oquotient = 2'b10;
end
else
begin
oquotient = 2'b01;
end
end
3'b010: begin
oquotient = 3'b001;
if (idata!=2'b00)
begin
oquotient = 1'b1;
end
else
begin
oquotient = 1'b0;
end
end
3'b011: begin
oquotient = 3'b010;
if (idata==2'b11)
begin
oquotient = 1'b1;
end
else
begin
oquotient = 1'b0;
end
end
3'b100: begin
oquotient = 2'b01;
if (idata==1'b1)
begin
oquotient = 2'b10;
end
else
begin
oquotient = 2'b01;
end
end
3'b101: begin
oquotient = 3'b011;
if (idata!=2'b00)
begin
oquotient = 1'b1;
end
else
begin
oquotient = 1'b0;
end
end
3'b110: begin
oquotient = 3'b100;
if (idata==2'b11)
begin
oquotient = 1'b1;
end
else
begin
oquotient = 1'b0;
end
end
3'b111: begin
oquotient = 2'b10;
if (idata==1'b1)
begin
oquotient = 2'b10;
end
else
begin
oquotient = 2'b01;
end
end
default: oquotient = 4'd0;
endcase
end
//always@(idata)
//begin
// case(idata)
// 3'b000: rem_temp = 2'b00;
// 3'b001: rem_temp = 2'b01;
// 3'b010: rem_temp = 2'b10;
// 3'b011: rem_temp = 2'b00;
// 3'b100: rem_temp = 2'b01;
// 3'b101: rem_temp = 2'b10;
// 3'b110: rem_temp = 2'b00;
// 3'b111: rem_temp = 2'b01;
// default:rem_temp = 2'b00;
// endcase
//end
//
//always@(idataor rem_temp)
//begin
// oremainder = idata;
// case(idata)
// 2'b00,2'b11: oremainder = rem_temp;
// 2'b01: oremainder = {(~(rem_temp | rem_temp)),rem_temp};
// 2'b10: oremainder = {rem_temp,(~(rem_temp | rem_temp))};
// default: oremainder = rem_temp;
// endcase
//end

可见这个逻辑并不是很大,求商的逻辑不过是一个3bit的case里面套一级选择(if),FPGA里不过就是一个ALU。时序也很好,百兆的钟都没有问题。我由此想到了,对于除数固定,被除数有一定范围限制的除法,还是可以用一定简化了逻辑实现的。

3)以下我们讨论的除法就由此先做一个前提的约束:被除数是8bit,除数我们将分别讨论3、5、7、11等质数的情况,其它的和数的除法可以分解成质数。

4) 除数为3(二进制2‘b11)

我最早的想法其实很简单,除以3是很困难的,但是除以4对于硬件确实非常简单的。所以也许可以通过对1/4进行一些调整来达到1/3的目的。这显然是要在1/4上加一点什么,加什么呢?我突然想到了无穷级数:

1/4 1/16 1/64 .........
其无穷级数求和和刚好是1/3。这似乎就简单了。不就是1/4 +1/16 +1/64 + .........
有一件事情是可能的,我们不能求无穷的加和,但是如果我们只要整数位,那也许就不需要无穷的加完。
temp = {number,6'd0} + {number,4'd0} + {number,2'd0} + number;
result1 = temp + ((number==1'b1 && number!=7'd0)? temp : temp&temp);

number是8bit被除数,temp是number若干(这里这用了前4个)移位值的和,但是我们明白,这是不精确的,所以对此和进行一些调整。调整的方向一定是加一点什么(因为我们少加了很多数),result1就是这个调整的逻辑。

从整体上看看这个逻辑:4个14位数的加法,一个选择逻辑和单bit加。逻辑不算太小(14bit加法电路还是不小的),但是也不算太大(毕竟就是4个加法)。时序由加法电路来限制,综合的好应该到百兆是没有问题的。

5)除数为5(3'b101)

1/5显然和1/4比较近。我们仍然可以比较方便的写出这个无穷级数来:

1 - 1/4 + 1/16 - 1/64 ...........
这个的和是4/5不是1/5,但是再除个4就好了(这很方便的)。
temp={number,6'd0} - {number,4'd0} + {number,2'd0} - number + number - number;
result1 = temp + temp;
result2 = result1;

temp是number的6个移位值的和,result1是调整后的值,result2是result/4的商。这个逻辑怎么要加6个值的和呢?其实就是近似问题,如果加的个数少,那么后面那个调整电路就会复杂些。

6)除数为7(3'b111)

这个和1/3其实是类似的,我就不赘述了。

7)除数为11(4'b1011)

这个有点烦,和11近的2^n是8或16,这个级数似乎不好找。但是我一觉醒来突然明白了一个事情:任何一个小数都是可以化为2进制表示的,而其2进制表示其实就是一个2^n的数列的和,只不过是换了一种形式吧了。

于是1/11就是0.0001011101 | 0001011101 | 0001011101 | ...........
temp = {number,6'd0} + {number,4'd0} + {number,3'd0} + {number,2'd0} + number + number;
result1 = temp + (temp&temp&temp&temp);

精度仍然只取了前6个有效的(是1)的数,然后在result1上做了一些补足的调整。

8)总结:其实到这里我们就已经清除了一件事----所有除数固定的除法都可以用上述确定的过程来实现。具体说就是:第一,将除数n变成乘以1/n,然后用2进制来表示这个1/n。第二,根据被除数的位数来选取合适的1/n的有效位数。第三,再根据具体的结果做一些调整。

1/N取多少有效位合适,取决与被除数的范围(被除数较大,就要多取几位)、逻辑大小的控制(加法越多,可能你的门数和时序多要付出代价)、一级最后那个调整的复杂程度(总不能太复杂吧)。

好了,到这里就先告一段落吧,但是我仍然没有从根本上真正解决任意除法的问题。我心中的通看来还要持续。不知有谁能最终来替我排解。