乱记-4

一开会,大领导参与了,心情似乎一般除了提几句流量报告应该改进的话,其他的也没有说什么。app只能做数字报其他内容并入bd,我窃以为是斩断了大boss的移动梦,难怪乎心情不好。自己则诌了两个工作内容忽悠过去。会议也创纪录的不到11点就结束,大家真的是没什么好说了?!今天听到rtx群里有某领导说,部门不明确,正在八卦传闻,说不定变化比预想的来的快呢。

上想着去车公庙7点多出门已晚,打地铁一想到要被夹成肉饼就恐惧,跑到后门坐224到田面转。果然如预想的一路上塞得和狗屎一样,手机都快划烂了都没到田面。路上一个中年大叔差点和背包青年差点打起来,盖青年不愿意传递后门上车乘客传递过来的车卡,大叔就觉得纳闷,大神说了几句,于是乎两个人扛上了。我和后面几个人劝了下大叔,何必为点小事打起来,最后大家不了了之。

到田面竟意思8点多,转车,出了几个站才发觉上错车。下车,换地铁,到世界之窗换线,出战竟然发现风景不一样,细看站牌发现方向反了,下楼来过。

在车公庙进楼时候,赫然遭遇了技术男,和他打招呼,怪别扭的,心想ljh竟然还没和他说啊。接下来的工作安排、计划之类,我实在是想得太多了。到他办公室,果然他说太晚了还没说。两个人出到外面聊,最后一句话是,领导有点摇摆。他当然说是领导各种担忧,我总疑心是nd派系斗争又牵扯进来。又想着,哎!我也太积极了吧。

收到xie的QQ留言提到yc修改的东西,心想先搞定赚钱的东西吧。保持平常心。其实内心深处还是有点怕去适应新的环境啊又想着不成也好,省得变动。人啊!贱!

午打网页版cs,战绩垫底,技术是一如既往烂到底。之前在成都工作,下了班或者周末无事忙的时候亦是这样消磨时间的,一眨眼10年都过去了。

<think>我们正在讨论4位移位相减除法的恢复余数算法。根据引用[1]和[3],我们知道恢复余数除法(也称为加减交替法)是一种二进制除法的实现方法。它的基本思想是通过不断移位和相减来得到商和余数,并在余数为负时恢复余数(即加回除数)并调整商。4位除法的意思是被除数和除数都是4位二进制数(除数不为0)。我们假设被除数是8位(因为除法过程中被除数需要扩展,但这里我们考虑被除数和除数都是4位,但实际算法中,被除数需要扩展为8位,即高4位为0,低4位为被除数,这样可以得到4位的商和4位的余数)。算法步骤:1.初始化:将8位的被除数(高4位为0,低4位为实际被除数)放入一个16位的寄存器(高8位为余数,低8位为被除数/商,但这里我们只需要4位商,所以实际上我们使用一个8位寄存器:高4位存余数,低4位存被除数,并在运算过程中逐步移位得到商)。更准确地说,通常我们使用一个组合寄存器(余数/被除数/商寄存器),初始时,余数部分为0,被除数部分为被除数,总位数=余数位数+被除数位数=4+4=8位。2.循环4次(因为被除数是4位):a.将整个寄存器(余数和被除数)左移1位(这样最高位移出,最低位补0)。b.用移位后的余数部分(高4位)减去除数(4位),结果存回余数部分。c.检查余数部分的符号(最高位):-如果余数为正或0,则将商的最低位设为1(因为移位后最低位是空出来的,现在用来存放商)。-如果余数为负,则恢复余数(将余数加回除数),并将商的最低位设为0。3.循环结束后,寄存器的高4位就是余数,低4位就是商。注意:在恢复余数法中,当余数为负时,我们需要恢复余数(即加上除数),然后再继续下一步。这样做的目的是保证每一步的余数都是非负的。下面我们用一个具体的例子来说明。假设被除数是7(0111),除数是2(0010)。我们期望商为3(0011),余数为1(0001)。初始化:寄存器(8位)初始为00000111(高4位余数,低4位被除数)。步骤1:左移:整个寄存器左移1位:00001110(移出0,最低位补0)。余数部分(高4位)减除数:0000-0010=1110(即-2,用补码表示,最高位为1,表示负数)。因为余数为负,所以恢复余数:1110+0010=0000,并将商的最低位设为0(此时寄存器低4位是1110,但最低位是空出来的,我们设为0,所以变成11100?这里注意,实际上我们左移后,最低位是0,然后我们根据减法结果设置最低位。但恢复余数后,我们实际上没有设置商位,而是将余数恢复后,商位设为0。所以此时寄存器为:0000(余数恢复后)和1110(被除数移位后),但商位还没有设置。我们需要在恢复后设置最低位为0。所以整个寄存器应该是00001110,然后设置最低位为0?不对,左移后,最低位是空出来的,我们根据减法结果设置这个空位。实际上,在恢复余数法中,我们这样操作:左移后,寄存器为:00001110(高4位0000,低4位1110,注意这里低4位包含被除数的移位和商位的预留位置,但此时商位还没有确定)。然后做减法:0000-0010=1110(负),所以我们需要恢复余数(加上除数0010)得到0000,然后因为这一步的商应该是0,所以我们将商的最低位设为0。那么整个寄存器如何变化?实际上,我们在左移后,低4位的最低位是0(因为左移后补0),然后我们根据减法结果决定是否恢复余数和设置商位。在恢复余数后,余数部分回到0000,然后我们将整个寄存器的低4位的最低位设置为0(其实它已经是0了,因为左移补0,但我们没有改变它)。所以寄存器还是00001110?这显然不对,因为下一步移位时,我们移出的应该是被除数位,而商位应该单独记录。更标准的做法是:我们有一个8位的寄存器,初始为00000111(高4位余数,低4位被除数)。第1步:左移1位:整个寄存器变成00001110(高4位0000,低4位1110)。注意,这个1110中,最高位的1是被除数移上来的,最低位的0是补的(用于商位)。用高4位(0000)减除数(0010)得到1110(负),所以这一步商0,并且需要恢复余数(将高4位加回除数:1110+0010=0000)。然后,我们设置商位为0(实际上左移后的最低位已经是0,所以我们不需要改变)。所以寄存器高4位恢复为0000,低4位为1110(注意,低4位中,111是剩余的被除数,0是商位0)。但是,这里我们还没有设置商位?实际上,左移后,最低位就是商位,我们根据减法结果设置这个商位。因为余数为负,所以商位设为0(已经为0),然后恢复余数(高4位恢复为0000)。第2步:左移:整个寄存器左移1位:00011100(高4位0001,低4位1100)。用高4位(0001)减除数(0010)得到1111(负),所以商0,并恢复余数(1111+0010=0001)。所以高4位恢复为0001,低4位为1100(最低位是0,因为商0)。注意:左移后最低位补0,所以商0时就是0,商1时我们将其改为1。第3步:左移:00111000(高4位0011,低4位1000)。减除数:0011-0010=0001(正),所以商1。此时,我们将低4位的最低位设为1(所以低4位变成1001)。寄存器变为:高4位0001,低4位1001。第4步:左移:0011001?(这里高4位0001左移,低4位1001左移,整个寄存器左移1位:00110010?注意,高4位左移,最高位移入低4位的最高位,低4位的最低位补0。所以左移后:高4位0011(实际上是0001左移,低4位最高位1移入高4位的最低位,所以高4位变成0011?不对,应该是:整个寄存器是8位,左移一位:00110010(高4位0011,低4位0010)?我们来仔细分解:初始:高4位0001,低4位1001->整个寄存器00011001(8位)左移1位:00110010(最高位0移出,最低位补0,所以整个变成00110010,即高4位0011,低4位0010)然后,用高4位0011减除数0010:0011-0010=0001(正),所以商1,将低4位的最低位设为1(所以低4位变成0011)。所以最后寄存器为00010011(高4位0001,低4位0011)。循环结束。商在低4位:0011(即3),余数在高4位:0001(即1),正确。但是,上面的步骤中,第1步和第2步都商0,第3步和第4步商1,所以商是0011。然而,在恢复余数法中,我们每次在余数为负时,需要恢复余数(即加上除数),然后再进行下一步。这样会导致额外的加法操作,使得运算时间不确定(最坏情况下每一步都需要恢复余数)。下面我们尝试用Verilog来实现一个4位的恢复余数除法器。模块定义:输入:clk(时钟),rst(复位),start(开始信号),dividend(4位被除数),divisor(4位除数)输出:busy(忙信号),done(完成信号),quotient(4位商),remainder(4位余数)内部状态机或计数器:需要一个计数器cnt(2位,因为4步循环),以及一个状态寄存器state(可能包括空闲状态和计算状态)。一个8位的移位寄存器reg_s(高4位存余数,低4位存被除数/商)。算法步骤:空闲状态下,当start有效时,初始化reg_s为{4'b0,dividend},计数器cnt置0。然后进入计算状态,每个时钟周期执行一步:1.左移reg_s一位(低位补0)。2.用reg_s的高4位减去除数,结果存入临时变量diff。3.判断diff的最高位(符号位):如果为0(非负),则reg_s的高4位更新为diff,并且reg_s的最低位设为1(商1)。如果为1(负),则reg_s的高4位保持不变(即不更新为diff,因为要恢复余数,所以这一步相当于没有减,所以不需要恢复?不对,实际上我们已经减了,所以需要加回来,但是这里我们不更新高4位,就等于没有减?不对,我们左移后减了,如果为负,我们需要恢复,即加回除数,但是这里我们并没有更新高4位,所以实际上高4位还是左移后的值(即没有减之前的值)。所以,在负的情况下,我们不需要更新高4位(因为减操作无效,我们恢复),并且最低位设为0(因为左移后最低位是0,所以不用动?不对,我们需要设置商位为0,而左移后最低位是0,所以保持不变即可。但是,注意:在非负的情况下,我们更新高4位为diff,并且设置最低位为1;在负的情况下,我们不做更新(相当于高4位还是左移后的值,即没有减),并且最低位保持0(因为左移后补0,所以已经是0)。然而,恢复余数法在负的情况下需要恢复余数,也就是在减法后,如果发现负,需要立即加回除数。但是在上面的步骤2中,我们减了,然后判断为负,所以我们需要将高4位恢复为减之前的值(即左移后的高4位)。所以,实际上,在负的情况下,我们不更新高4位(因为减操作无效),所以高4位还是左移后的值(也就是相当于没有减,所以不需要恢复)。这样理解对吗?不对,因为我们在减法后得到了一个负的diff,所以我们需要将高4位恢复为左移后的值(即不减),所以实际上我们不需要做任何操作(因为高4位还没有被覆盖,我们只是计算了diff,但还没有更新到高4位)。所以,在负的情况下,我们不需要更新高4位,并且商0(最低位设为0,但左移后最低位已经是0,所以不需要改变)。因此,步骤3可以这样操作:左移后,计算diff=高4-除数如果diff>=0(即最高位为0),则更新高4位为diff,并将reg_s的最低位设为1。否则(diff<0),则高4位不变(即还是左移后的高4位),最低位保持0(也就是不设置,因为已经是0)。这样就不需要额外的恢复操作,因为当diff<0时,我们根本没有更新高4位,所以高4位还是左移后的值,相当于没有减。所以,恢复余数法在硬件实现中,可以通过不更新负的差值来避免恢复操作。这样,每一步都只做一次减法,然后根据减法结果决定是否更新余数以及设置商位。因此,我们可以这样写:always@(posedgeclk)beginif(rst)begin//复位endelseif(start)begin//初始化reg_s<={4'b0,dividend};cnt<=0;state<=CALC;endelseif(state==CALC)begin//左移一位reg_s<={reg_s[6:0],1'b0};//左移,低位补0//计算差值diff=reg_s[7:4]-divisor;if(diff[3]==1'b0)begin//非负reg_s[7:4]<=diff;//更新余数reg_s[0]<=1'b1;//设置商位为1endelsebegin//负的情况,不更新余数(即保持左移后的余数,也就是reg_s[7:4]不变,所以不需要写),且商位为0(已经为0,所以也不需要写)//所以这里什么都不做?但是我们已经左移了,并且商位已经补0,所以确实不需要操作。endcnt<=cnt+1;if(cnt==3)begin//4步完成state<=IDLE;done<=1;quotient<=reg_s[3:0];//注意,最后一步设置后,商在低4位remainder<=reg_s[7:4];//余数在高4位endendend但是注意,上面的代码中,我们在每个时钟周期先左移,然后计算diff,然后根据diff更新。但是,左移后的reg_s的高4位是用于减法的,而我们在同一个时钟周期内更新了reg_s(包括左移和可能的更新高4位和设置最低位),这会有问题吗?实际上,我们是在同一个时钟沿完成整个操作。所以,我们使用组合逻辑计算diff,然后在时钟上升沿更新reg_s。但是,在更新reg_s时,我们同时进行了左移和更新高4位以及设置最低位,这需要仔细设计。更好的做法是:在计算状态,每个时钟周期执行一步,我们使用一个组合逻辑计算差值,然后在时钟上升沿更新寄存器。但是,上面的代码中,我们先将reg_s左移(并低位补0)得到一个新的值,然后在这个新值的高4位的基础上减除数,然后根据结果决定是否更新高4位和设置最低位。注意,左移后的值我们还没有写入reg_s,我们是在同一个时钟周期内计算并更新。所以,我们可以这样:always@(posedgeclk)beginif(state==CALC)begin//先左移:将reg_s左移1位,低位补0,得到一个新的移位后的值reg_s_temp={reg_s[6:0],1'b0};diff=reg_s_temp[7:4]-divisor;if(diff[3]==1'b0)beginreg_s<={diff,reg_s_temp[3:1],1'b1};//高4位更新为diff,低4位中,移位移出的被除数位保留(reg_s_temp[3:1]),最低位设为1endelsebeginreg_s<=reg_s_temp;//因为左移后低位补0(即商0),而且高4位不变(即reg_s_temp[7:4]就是左移后的值,没有减),所以直接赋值end//计数器递增endend或者,我们可以将左移操作和更新合并。注意:在负的情况下,我们实际上没有改变左移后的值,所以整个寄存器就是左移后的值(即高4位是左移后的余数,低4位是左移后的被除数,且最低位是0(商0))。在非负的情况下,我们将高4位替换为diff,低4位中,我们将最低位设为1(而左移后低4位的最低位是0,所以我们需要将其改为1)。因此,我们可以这样写:always@(posedgeclk)beginif(state==CALC)begin//先计算左移后的值(但不实际移位,而是用中间变量)reg_s_temp={reg_s[6:0],1'b0};//左移1位,低位补0diff=reg_s_temp[7:4]-divisor;if(diff[3]==1'b0)begin//非负:更新高4位为diff,低4位的最低位设为1(注意reg_s_temp[3:0]是左移后的低4位,其最低位是0,所以我们需要将其改为1)reg_s<={diff,reg_s_temp[3:1],1'b1};endelsebegin//负:整个寄存器就是左移后的值(reg_s_temp),其中低4位的最低位已经是0(商0)reg_s<=reg_s_temp;endcnt<=cnt+1;//...完成判断endend这样实现即可。注意:在非负的情况下,我们更新了高4位,并且设置了商位为1;在负的情况下,我们使用左移后的值(高4位没有减,商位为0)。这就是恢复余数法的硬件实现。注意,这种方法每一步都需要一次减法,但只有在非负的情况下才更新余数(即减去除数),在负的情况下不更新(相当于没有减),所以不需要额外的恢复操作(即不需要加回来,因为我们没有更新)。但是,这种方法实际上就是“不恢复余数法”(也称为加减交替法)?不对,恢复余数法在负的情况下需要恢复(即加回除数),然后再继续下一步。而这里我们并没有恢复,而是直接使用左移后的值(相当于没有减)。所以,这实际上是“不恢复余数法”?实际上,在恢复余数法中,如果余数为负,我们恢复余数(加回除数)后,余数回到左移后的值,然后我们下一步仍然用这个值继续左移。而我们这里在负的情况下,我们并没有减操作(因为减了之后发现负,我们不更新,所以还是左移后的值),所以实际上我们跳过了减操作,下一步仍然用这个值继续左移。这和不恢复余数法(加减交替法)是一样的:在余数为负时,下一步的减法操作改为加法操作(因为恢复余数后,下一步应该还是减法,但加减交替法在余数为负时,下一步做加法)。而我们这里在负的情况下,下一步仍然是减法(因为我们没有改变除数,也没有改变操作),所以实际上我们并没有实现加减交替,而是相当于在负的情况下,这一步的减法操作无效,下一步继续减。但是,我们注意,在负的情况下,我们没有更新余数,所以下一步左移的余数就是上一步左移后的余数(没有减)。那么下一步我们继续用这个余数减除数,相当于连续两次减除数(第一步减了发现负,然后不更新,第二步又减)。这显然不对。所以,我们需要重新审视:恢复余数法在负的情况下,恢复余数(加回除数)后,余数回到左移后的值,然后下一步继续左移并减除数。而我们这里在负的情况下,没有加回,所以余数还是减了之后的值(负值),但我们没有更新,所以寄存器的高4位还是左移后的值(即没有减),所以下一步我们继续用这个值减除数,相当于没有减。这样,在负的情况下,我们实际上跳过了这一步的减法操作,下一步继续减。这样,整个算法在负的情况下,这一步相当于只做了左移,没有做减法,下一步继续左移并减除数。而恢复余数法在负的情况下,做了左移、减法、加法(恢复)、下一步左移和减法。所以,我们这里节省了一次加法操作,但是下一步还是减法,所以没有交替。实际上,标准的恢复余数法在恢复后,下一步仍然是减法,所以并没有交替。而我们这里在负的情况下,没有更新余数(即没有减),所以这一步相当于无效,下一步继续减。这样,在连续负的情况下,我们连续多次减除数,直到非负为止?不对,因为每一步都是独立的,每一步都是左移然后减除数,然后判断。所以,我们这里实现的实际上是“不恢复余数法”的简化版本,即当余数为负时,我们不做任何操作(不更新余数,商0),然后继续下一步。这种方法和恢复余数法是等价的,因为恢复余数法在负的情况下,恢复余数后,下一步的操作和我们这里在负的情况下不做操作然后下一步操作是一样的:都是下一步从恢复后的余数(即左移后的余数)开始左移并减除数。因此,我们这里实现的算法和恢复余数法是等价的,但避免了恢复操作(加法操作),所以效率更高。这实际上就是“不恢复余数法”(也称为加减交替法)?但这里我们并没有交替加减,而是一直减。实际上,不恢复余数法是这样:当余数为负时,下一步改为加法(加上除数)。而我们这里下一步仍然是减法。所以不是标准的不恢复余数法。标准的不恢复余数法(加减交替法):步骤:1.左移。2.根据上一步的商(或上一步的余数符号)决定这一步的操作:如果上一步的余数为负,则这一步加上除数;否则减去除数。3.设置商位:如果操作后的余数为非负,则商1;否则商0。注意:第一步默认是减法。而恢复余数法在负的情况下恢复后,下一步仍然是减法(没有交替)。我们这里实现的算法:每一步都是先左移,然后减除数,然后如果非负则更新余数和设置商1,否则不更新余数(即余数保持左移后的值)并设置商0(实际商0已经由左移时补0完成)。下一步仍然减除数。所以,我们这里实现的算法实际上就是恢复余数法,但通过不在负的情况下更新余数,从而避免了恢复操作(加法操作)。所以,它和恢复余数法等价,但硬件实现更简单(只需要一个减法器,不需要加法器)。因此,我们可以称其为恢复余数法的一种实现方式。总结:4位移位相减除法恢复余数算法的实现步骤(每个时钟周期一步):1.左移1位(整个8位寄存器),低位补0。2.用移位后的高4位减去除数(4位),得到差值。3.如果差值非负(符号位为0),则用差值更新高4位,并将最低位(商位)设为1。4.如果差值为负,则不更新高4位(保持移位后的值),最低位保持0(左移时已补0,所以无需操作)。5.循环4次。最后,商在低4位,余数在高4位。注意:余数的位数和除数相同(4位),所以余数范围是0到除数-1。下面给出一个Verilog代码的简单框架(不包括状态机和计数器,只包含核心计算部分):modulediv_restoring(inputclk,inputrst,inputstart,input[3:0]dividend,input[3:0]divisor,outputregbusy,outputregdone,outputreg[3:0]quotient,outputreg[3:0]remainder);reg[1:0]cnt;reg[7:0]reg_s;reg[2:0]state;localparamIDLE=0,CALC=1;always@(posedgeclkorposedgerst)beginif(rst)beginstate<=IDLE;busy<=0;done<=0;cnt<=0;reg_s<=0;quotient<=0;remainder<=0;endelsebegincase(state)IDLE:beginif(start)beginreg_s<={4'b0,dividend};cnt<=0;busy<=1;done<=0;state<=CALC;endendCALC:begin//左移1位,低位补0reg_s<={reg_s[6:0],1'b0};//先左移,低位补0//计算差值if(reg_s[7:4]>=divisor)begin//非负reg_s[7:4]<=reg_s[7:4]-divisor;reg_s[0]<=1'b1;//设置商位为1end//否则,为负,我们不需要做任何事(高4位不变,商位已经是0)cnt<=cnt+1;if(cnt==3)begin//4次移位完成state<=IDLE;busy<=0;done<=1;quotient<=reg_s[3:0];remainder<=reg_s[7:4];endendendcaseendendendmodule但是注意,上面的代码中,在CALC状态,我们先将reg_s左移(并低位补0)赋值给reg_s,然后立即判断reg_s[7:4](也就是移位后的高4位)是否大于等于除数,然后更新reg_s[7:4]和reg_s[0]。这是非阻塞赋值,所以更新发生在时钟周期结束时。但是,在同一个always块中,非阻塞赋值的更新是同时发生的,所以这里存在冲突:我们先赋值了reg_s为左移后的值,然后又更新了reg_s[7:4]和reg_s[0]。实际上,综合器会如何处理?为了避免这个问题,我们可以使用临时变量:wire[3:0]rem_shifted=reg_s[3:0];//左移后的高4位实际上是reg_s[3:0]?不对,左移后,原来的高4位[7:4]变成了[6:3]?不对,我们左移整个8位,所以左移后,最高位(第7位)被移出,第6位变成第7位,...,第0位变成第1位,最低位补0。所以,移位后的高4位是{reg_s[6:4],reg_s[3]}?不对,这样很乱。所以,我们最好将左移和更新分开,使用组合逻辑计算新的reg_s值,然后在一个时钟沿赋值。修改如下:在CALC状态,我们计算一个临时变量next_reg_s:reg[7:0]next_reg_s;always@(*)begin//先左移1位next_reg_s={reg_s[6:0],1'b0};if(next_reg_s[7:4]>=divisor)beginnext_reg_s[7:4]=next_reg_s[7:4]-divisor;next_reg_s[0]=1'b1;end//否则,next_reg_s保持不变(即左移后的值,且最低位为0)endalways@(posedgeclk)beginif(state==CALC)beginreg_s<=next_reg_s;cnt<=cnt+1;//...完成判断endend这样更清晰。但是,为了简单,我们也可以在一个always块中,先计算左移后的值,然后根据条件更新:always@(posedgeclk)beginif(state==CALC)begin//计算左移后的值(临时变量)reg[7:0]temp={reg_s[6:0],1'b0};if(temp[7:4]>=divisor)begintemp[7:4]=temp[7:4]-divisor;temp[0]=1'b1;endreg_s<=temp;cnt<=cnt+1;//...endend这样写也是可以的,因为temp是组合逻辑,在时钟沿到来时计算并赋值。因此,我们采用这种方式。最后,注意除数为0的情况,需要特殊处理(比如溢出标志)。这里我们不考虑。以上是4位恢复余数除法器的实现。相关问题:1.恢复余数法相对于不恢复余数法(加减交替法)有什么优缺点?2.在除法器中,如何判断余数的正负?使用补码还是无符号数?3.如何扩展这个4位除法器使其支持有符号除法?</think>### 4位移位相减除法恢复余数算法实现 #### 算法原理 恢复余数法(Restoring Division)是一种基于二进制移位和减法实现的除法算法,核心步骤如下: 1. **初始化**: - 使用8位寄存器(高4位存余数,低4位存被除数/商),初始余数为0:`余数 = 0000, 被除数 = xxxx` 2. **循环4次(4位移位)**: a. **左移1位**:整个寄存器左移,低位补0。 b. **减除数**:用移位后的高4位(余数部分)减去除数。 c. **判断符号位**: - 若结果 ≥ 0(符号位为0):更新余数为差值,商位设1(最低位置1)。 - 若结果 < 0(符号位为1):**恢复余数**(放弃本次减法),商位设0(低位已补0)。 3. **输出结果**: - 商在寄存器低4位,余数在高4位。 #### 示例:7 ÷ 2(0111 ÷ 0010) | 步骤 | 操作 | 寄存器(高4位余数 + 低4位被除数/商) | 说明 | |------|---------------|--------------------------------------|--------------------------| | 初始 | - | `0000 0111` | 余数=0, 被除数=7 | | 1 | 左移 | `0000 1110` | | | | 减除数(0010) | `0000-0010=1110` (负) | 恢复余数,商位=0 | | | **恢复后** | `0000 1110` | 余数不变,商位保持0 | | 2 | 左移 | `0001 1100` | | | | 减除数(0010) | `0001-0010=1111` (负) | 恢复余数,商位=0 | | | **恢复后** | `0001 1100` | 余数不变,商位保持0 | | 3 | 左移 | `0011 1000` | | | | 减除数(0010) | `0011-0010=0001` (正) | 更新余数=0001,商位=1 | | | **更新后** | `0001 1001` | 低4位变为1001 (商位1) | | 4 | 左移 | `0011 0010` | | | | 减除数(0010) | `0011-0010=0001` (正) | 更新余数=0001,商位=1 | | | **结果** | `0001 0011` | 商=0011(3), 余数=0001(1) | > **关键点**: > - 余数恢复通过**放弃减法结果**实现(不更新寄存器高4位)。 > - 商位设置:正差时置1,负差时保持0(左移时已补0)[^1]。 --- #### Verilog实现代码 ```verilog module div_restoring ( input clk, rst, start, input [3:0] dividend, divisor, output reg busy, done, output reg [3:0] quotient, remainder ); reg [1:0] cnt; // 4步计数器 reg [7:0] reg_s; // 8位寄存器:{余数, 被除数/商} reg state; // 状态机 localparam IDLE = 0, CALC = 1; always @(posedge clk or posedge rst) begin if (rst) begin state <= IDLE; busy <= 0; done <= 0; cnt <= 0; reg_s <= 0; end else begin case (state) IDLE: begin if (start) begin reg_s <= {4'b0, dividend}; // 初始化:余数=0 cnt <= 0; busy <= 1; done <= 0; state <= CALC; end end CALC: begin reg [7:0] temp = {reg_s[6:0], 1'b0}; // 左移1位,低位补0 if (temp[7:4] >= divisor) begin // 差值≥0 temp[7:4] = temp[7:4] - divisor; // 更新余数 temp[0] = 1'b1; // 商位置1 end reg_s <= temp; cnt <= cnt + 1; if (cnt == 3) begin // 完成4次循环 quotient <= reg_s[3:0]; // 输出商(低4位) remainder <= reg_s[7:4]; // 输出余数(高4位) busy <= 0; done <= 1; state <= IDLE; end end endcase end end endmodule ``` #### 代码说明 1. **输入/输出**: - 输入:4位被除数(`dividend`)、除数(`divisor`)、启动信号(`start`)。 - 输出:4位商(`quotient`)、余数(`remainder`)、状态信号(`busy`, `done`)。 2. **关键逻辑**: - 每个时钟周期左移寄存器,计算`余数 - 除数`。 - 若结果非负:更新余数并设商位为1;否则保持余数不变(恢复),商位为0。 - 循环4次后输出结果[^3]。 3. **注意事项**: - 不支持除数为0(需额外检查溢出)。 - 余数为正数且满足`0 ≤ 余数 < 除数`[^1]。 --- ### 相关问题 1. **恢复余数法的最坏时间复杂度是什么?** > 最坏情况下每一步都需恢复余数(如连续负差),需额外操作,导致延迟不确定[^1]。 2. **如何优化恢复余数法的性能?** > 采用**不恢复余数法**(加减交替法):负差时不恢复,下一步改做加法,减少操作步骤[^1][^3]。 3. **如何扩展该算法支持有符号除法?** > 先取绝对值运算,再根据符号位调整商和余数符号。例如: > - 被除数符号 → 商符号 > - 余数符号同被除数[^4]。 4. **Verilog实现中如何处理除数为零?** > 添加溢出检测:初始化时若`divisor==0`,直接置溢出标志并跳过计算[^2]。 [^1]: 恢复余数法需在负差时恢复原值,控制逻辑复杂且时间不确定。 [^2]: 实际应用中需增加除数为零的检测逻辑。 [^3]: 硬件实现时减法器复用,节省资源。 [^4]: 有符号除法需额外处理符号位扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值