首先我们看小学时代的一个公式:被除数
=除数 X商 +余数
应因小学我们没学过负数,这里假定被除数与除数都是自然数。能不能
为负数呢?下面在说。
|
循环型触发器的原理其实也很简单就是:判断被除数有多少个除数,满
足的条件是商小于除数。如果用 C语言写的话
|
Q
= 0;
while(rmin
<< dsor)
{
rmin =
rmin – dsor;
Q ++;
|
这里它们都是正数哈,则计算的结果:
Q是商,rmin是余数。
但是我们发现,当 rmin和 dsor大小不一样时,计算所花费的步骤将不
一样,这里我们要做的是一个,时钟消耗一样的除法器。
假设
|
被除数 A,除数 B,商 Q=Q3 Q2 Q1 Q0,余数 R都是四位二进制数
|
A-Bx2^3=R
>= 0 ?
A-Bx2^2=R
>= 0 ?
A-Bx2^1=R
>= 0 ?
A-Bx2^0=R>= 0 ?
|
下一操作 A=R
下一操作 A=R
下一操作 A=R
下一操作 A=R
|
下一操作 A不变
下一操作 A不变
下一操作 A不变
下一操作 A不变
|
10
– 3 x 2^3 =
-14 < 0
10
– 3 x 2^2 =
-2 < 0
|
10
– 3 x 2^1 = 4
4 – 3
x 2^0 = 1
|
第一步:判断 Q3是否为一, Q3是第四位权重为
2^3=8。为一的话说明
A至少有 8个 B,即 A – 8B
>= 0,并把余数 R赋给 A进行下一步操作
;
第二步:判断 Q2是否为一, Q2是第四位权重为
2^2=4。为一的话说明
A至少有 4个 B,即 A – 4B
>= 0,并把余数 R赋给 A进行下一步操作
;
第三步:判断 Q1是否为一, Q1是第四位权重为
2^1=2。为一的话说明
A至少有 2个 B,即 A – 2B
>= 0,并把余数 R赋给 A进行下一步操作
;
第三步:判断 Q1是否为一, Q1是第四位权重为
2^0=1。为一的话说明
A至少有 1个 B,即 A – B
>= 0,并把余数 R赋给 A进行下一步操作 ;
思考中……
|
Veirlog算法分析:
值得注意的是我们发现
A-Bx2^3=R公式中在每一操作中
A在变,Bx2^
3也在变。我们希望恒数多一点,
例 2:
|
2^0
x 10
2^1
x 10
2^2
x 10
2^3
x 4
|
3
x 2^3
3
x 2^3
3
x 2^3
3
x 2^3
|
则商 Q=1100=3余数 R / 2^3=1;
我们将例 1中的等式分别乘于 1
2 4 8,就能发现被减数不变了。
|
4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8
:
begin
|
temp
= ospace + {divisor_n,7'd0};
if(temp[15])
ospace <= {ospace[14:0],1'b0};
else
ospace <= {temp[14:0],1'b1};
i
<= i + 1'b1;
|
end
因为被除数除数均为 8位二进制数所以操作次数为
8次。
|
temp
= ospace + {divisor_n,7'd0};
|
相当于上面的 A-Bx2^3=R只不过是 Bx2^3变成了
8而已。
if(temp[15])
ospace <= {ospace[14:0],1'b0};
else ospace
<= {temp[14:0],1'b1};
|
判断余数的正负 temp[15]为符号位,这里就不在细解了位操作了,慢慢
消化下面的代码就通了。
|
算法是活的,代码也是活的,只要抓到老鼠的(结果正确),吃饭少的
(资源消耗小)都是好猫。
|
正负数的处理了:在带余数除法中,我们习惯将被除数与除数都化为正
数来计算。得出的余数应与被除数同号。
余数可以为负数吗??带余除法定理不是规定余数恒正??纳尼 ~~~~~
大家可以在仿真看$display输出。
我的另一篇文章对带余除法进行了一定的探讨,是我的想法,如果其中
有什么不对的,多多指教哈。
|
Verilog
HDL代码为:
module
divisor
(
|
input[7:0]
divisor ,
input[7:0]
dividend ,
|
output[7:0]
quotient , //商
output[7:0]
reminder , //余数
//signe
|
reg[15:0]
ospace ; //操作空间
reg[15:0]
temp ;
|
reg[8:0]
divisor_n ; //divisor的负数
|
always
@(posedge clk or negedge rst_n)
if(!rst_n)
|
ospace
<= 16'd0 ;
temp
<= 16'd0 ;
divisor_n
<= 8'd0 ;
neg
<= 1'b0 ;
neg1
<= 1'b0 ;
done_r
<= 1'b0 ;
i
<= 4'd0;
|
neg
<= dividend[7] ^ divisor[7] ;
|
ospace
<= dividend[7] ? {8'd0,(~dividend + 1'b1)} : {8'd0, dividend};
divisor_n
<= divisor[7] ? {1'b1 ,divisor} : {1'b1 ,(~divisor + 1'b1)};
i
<= i + 1'b1;
|
4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8
:
|
temp
= ospace + {divisor_n,7'd0}; // divisor * 2^7
|
if(temp[15])
ospace <= {ospace[14:0],1'b0};
|
else
ospace <= {temp[14:0],1'b1};
|
done_r <= 1'b1;
i <= i + 1'b1;
end
|
done_r <= 1'b0;
i <= 4'd0;
end
|
assign
quotient = neg ? (~ospace[7:0]+1'b1) : ospace[7:0];
assign
reminder = neg1 ? (~ospace[15:8]+1'b1) : ospace[15:8];
assign
done = done_r;
|
module
divisor_vlg_tst();
|
reg
[7:0] dividend;
reg
[7:0] divisor;
reg
rst_n;
|
wire
done;
wire
[7:0]
wire
[7:0]
|
//
assign statements (if any)
divisor
i1 (
|
.dividend(dividend),
.divisor(divisor),
.done(done),
|
.quotient(quotient),
.reminder(reminder),
.rst_n(rst_n),
|
#20
rst_n = 1'b1;
$display("%d\n",-4/3);
$display("%d\n",-4%3);
$display("%d\n",4/-3);
$display("%d\n",4%-3);
$display("%d\n",-2/-120);
$display("%d\n",-2%-120);
forever
|
always
@(posedge clk or negedge rst_n)
|
dividend <= 8'd0;
divisor <= 8'd0;
start <= 1'b0;
i <= 4'd0;
end
|
if(done)
begin start <= 1'b0; i <= i + 1'b1;
|
else
begin dividend <= 8'd125; divisor <= 8'd11;
start<=1'b1;
|
if(done)
begin start <= 1'b0; i <= i + 1'b1;
|
else
begin dividend <= 8'd158; divisor <= 8'd18;
start<=1'b1;
|
if(done)
begin start <= 1'b0; i <= i + 1'b1;
|
else
begin dividend <= 8'd254;
divisor <= 8'd136; start<=1'b1;
|
if(done)
begin start <= 1'b0; i <= i + 1'b1;
|
else
begin dividend <= 8'd248; divisor <= 8'd77;
start<=1'b1;
|
if(done)
begin start <= 1'b0; i <= i + 1'b1;
|
else
begin dividend <= 8'd17; divisor <= 8'd18;
start<=1'b1;
|
if(done)
begin start <= 1'b0; i <= i + 1'b1;
|
else
begin dividend <= 8'd125; divisor <= 8'd11;
start<=1'b1;
|
|