当前位置:   article > 正文

Verilog -- 任意整数除以三求商和余数_verilog除3的方法

verilog除3的方法

Verilog – 任意整数除以三求商和余数

@(verilog)

1. 问题简介

问题:输入一个16bit的数,现在要求它除以3得到的商和余数,如何优化?

来源:@笑着刻印在那一张泛黄 提供,面试真题。

2. 思路

一开始联想到之前写过的另一篇博文序列模三检测器,但是这只能解决余数的问题,没法得到商。

后面的想法是直接使用任意整数除法器来实现,由于除数是3,比较特殊,实际上除3只需要考虑三个序列,也就是11,100,101。因为在手算除三的过程中,如果用二进制算,从高位依次往低位计算,就可以发现这一规律,例如:

     0 0 1 1 0 0 1 1 0  (7d'102)
    ------------------
11 | 1 0 0 1 1 0 1 0 0  (8d'308)
       1 1
     --------
         1 1
         1 1
    ----------------
           0 1 0 1
               1 1
          -----------
               1 0 0
                 1 1
           ----------
                   1 0  (余数)
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

整理一下,就是说把被除数从高位到低位排列,从前到后依次找11,100,101这三个序列,遇到这三中序列商就写1,否则商就写0,并且写完后左移一位。做完之后移除这个序列。不同的是遇到11,不做其他操作,遇到100,将3’b100-2’b11=1’b1 插入原序列最高位;遇到101则插入2’b10。

如果写成状态机,则可列出状态转移表:

State\Input01
00/+0/01/+0/1
110/+0/100/+1/0
101/+1/110/+1/10

表中的值表示next_state/商操作/余数,其中商操作+1就表示商序列加入一个1,+0就表示加入一个0.
此外,需要一个计数器来控制状态转移次数,理论上只要计数到被除数的位宽-1即可,但我在实际代码中为了规避最后返回IDLE使得余数不正确的问题,将它计数到了被除数位宽(可能有其他写法)。最后得到商和余数,余数就是计数器结束时的下一个状态表示的二进制数

3. 代码


`timescale 1ns/1ps

module divide_by_three
#(
parameter DATAWIDTH = 16
)(
input                                   clk,
input                                   rst_n,
input                                   vld_in,
input             [DATAWIDTH-1:0]       data_in,
output  reg       [DATAWIDTH-1:0]       quotient, 
output  reg       [1:0]                 reminder,
output  reg                             vld_out
);


reg [1:0]current_state;
reg [1:0]next_state;

reg [$clog2(DATAWIDTH):0] cnt;
reg [DATAWIDTH-1:0] data_reg;

parameter IDLE = 2'b11;

always @(posedge clk or negedge rst_n) 
  if(!rst_n) current_state <= IDLE;
  else current_state <= next_state;


always @(*) 
  case(current_state)
    IDLE:  if(vld_in) next_state = 2'b0;
           else       next_state = IDLE;

    2'b00: if (cnt == DATAWIDTH)          next_state = IDLE; // cnt = 16 not 15, for the calc of remainder
           else if(data_reg[DATAWIDTH-1]) next_state = 2'b1;
           else                           next_state = 2'b0;

    2'b01: if (cnt == DATAWIDTH)          next_state = IDLE;
           else if(data_reg[DATAWIDTH-1]) next_state = 2'b0;
           else                          next_state = 2'b10;

    2'b10: if (cnt == DATAWIDTH)          next_state = IDLE;
           else if(data_reg[DATAWIDTH-1]) next_state = 2'b10;
           else                          next_state = 2'b1;
    default: next_state = IDLE;
endcase

always @(posedge clk or negedge rst_n) 
  if(!rst_n) begin
    {cnt,data_reg,reminder,quotient,vld_out} <= 0;
  end else begin
    case(current_state)
      IDLE: begin
              {vld_out,cnt} <= 0;
              if(vld_in) data_reg <= data_in;
              else data_reg <= data_reg;
            end
      2'b00,2'b01,2'b10: begin
        if(cnt == DATAWIDTH-1) begin
          cnt <= cnt + 1;        // without this,remainder will be next_state=IDLE=2'b11'
          reminder <= next_state;
          vld_out <= 1;
        end else begin
          cnt <= cnt + 1; 
          vld_out <= 0;
          data_reg <= {data_reg[DATAWIDTH-2:0],1'b0};
        end
        if(data_reg[DATAWIDTH-1]) 
          quotient <= {quotient[DATAWIDTH-2:0],current_state[1]|current_state[0]};
        else 
          quotient <= {quotient[DATAWIDTH-2:0],current_state[1]};
      end
    endcase
  end

  
endmodule



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

testbench:


`timescale 1ns/1ps

module divide_by_three_tb();

parameter DATAWIDTH = 16;

reg                   clk;
reg                   rst_n;
reg                   vld_in;
reg   [DATAWIDTH-1:0] data_in;

wire   [DATAWIDTH-1:0] quotient;
wire   [1:0]           reminder;
wire   [1:0]           vld_out;

reg   [DATAWIDTH-1:0] quotient_ref;
reg   [1:0]           reminder_ref;


always #1 clk = ~clk;
initial begin
  clk = 0;
  vld_in = 0;
  data_in = 0;
  rst_n = 1;
  #4 rst_n = 0; #2 rst_n = 1;
  
  repeat(10) begin
    @(posedge clk);
    vld_in <= 1;
    data_in = $urandom()%100;
    quotient_ref = data_in/3;
    reminder_ref = data_in%3;
    @(posedge clk);
    vld_in <= 0;
    wait(vld_out==1);
  end
end

divide_by_three #(  .DATAWIDTH ( DATAWIDTH ))
U_DIVIDE_BY_THREE_0
(  .clk      ( clk      ),
   .rst_n    ( rst_n    ),
   .vld_in   ( vld_in   ),
   .data_in  ( data_in  ),
   .quotient ( quotient ),
   .reminder ( reminder ),
   .vld_out  ( vld_out  ));


initial begin
  $fsdbDumpvars();
  $fsdbDumpMDA();
  $dumpvars();
  #1000 $finish;
end

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

仿真波形

对于16位的被除数,需要18个周期得到计算结果。

相比于直接使用任意整数除法器,节省了减法操作,通过找规律化简了状态机。
其实对于输入也可以是之前的序列输入的形式,只需要修改输入的形式,调整下内部的数据存储即可。并且这种写法可以实现每输入一个bit,可以直接同时输出当前序列的商和余数

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/516672
推荐阅读
相关标签
  

闽ICP备14008679号