用Verilog实现一个除法器的功能

image.png

最简单的方法就是减法实现除法器(比如十进制中的a/b,可先比较a与b的大小,如果a>b,则商加1,a<=a-b,再进行比较大小,直到a<b,商不变,余数为a)。但这种方式通常速度比较慢,实际上更快速的方法是模拟手算除法的过程:

  • image.png

image.png

思考

:::info

  • 为什么我们需要对被除数的高位补0呢?

对被除数的高位进行补0来存放商;

  • 那么我们为什么要对除数的低位补0呢?

为了和被除数的低位进行匹配。
>> 通过运算我们可以发现,一个16bit的数/一个8bit的数,只有在被除数从高向低数的第8位才有可能有商的值出现,所以被除数需要向左移位逐步进行比较,我们可以将得到的商存放在一个临时声明的变量中,也可以将他直接放在被除数的低位。 :::

代码

以下就是代码的实现:

  1. module divider(
  2. input [15:0] A,
  3. input [7:0] B,
  4. output [15:0] result,
  5. output [15:0] rmd
  6. );
  7. reg [31:0] a_reg;
  8. reg [31:0] b_reg;
  9. integer i;
  10. always @(*) begin
  11. a_reg = {16'h0, A};
  12. b_reg = {B, 16'h0};
  13. for(i = 0; i < 16; i = i+1) begin
  14. a_reg = a_reg << 1;
  15. if(a_reg >= b_reg) begin
  16. a_reg = a_reg - b_reg + 1;
  17. end
  18. else begin
  19. a_reg = a_reg;
  20. end
  21. end
  22. end
  23. assign result = a_reg[15:0];
  24. assign rmd = a_reg[31:16];
  25. endmodule

result(商)和rmd(余数)这两个变量一定要给定位宽,否则DUT出来的数据是不正常的

Testbench验证

同样的,我们也搭建一个简单的tb进行验证:
步骤就是首先定义端口;
接下来调用我们的硬件DUT模块;
最后在Initial过程块中给定激励。

  1. module divider_tb;
  2. bit [15:0] A;
  3. bit [7:0] B;
  4. bit [15:0] result;
  5. bit [15:0] rmd;
  6. divider dut(
  7. .A(A),
  8. .B(B),
  9. .result(result),
  10. .rmd(rmd)
  11. );
  12. initial begin
  13. A = 16'd27;
  14. B = 8'd5;
  15. #20;
  16. A = 16'd47;
  17. B = 8'd7;
  18. #20;
  19. $finish();
  20. end
  21. endmodule

Reference

[1] https://www.cnblogs.com/lyc-seu/p/12507760.html
[2] https://www.bilibili.com/video/BV1iF411V7Nj?spm_id_from=333.337.search-card.all.click&vd_source=5df87fe7e859dc91e7029ea8f59c1811