2-10 进制码,也称为 BCD 码,它的编码方式则是通过一个 4 位二进制来表示一个 10 进制数,部分十进制对应的 BCD 码如下

十进制数 | BCD 码

13 --> 0001_0011

14 --> 0001_0100

19 --> 0001_1001

20 --> 0010_0000

99 --> 1001_1001

对于任意的三位十进制整数存在以下公式:

(ABC)   = A*10 + B*10 + C*10

显然,对于任意一个三位数分离它的百位、十位和个位可以通过整除来实现,verilog 语法同时也支持相乘(*)、相除(/)和取模(%)的运算符,Quartus 综合器发现这些运算符时会通过调用 FPGA 内部的嵌入式乘法器来实现这些运算。由于在数字电路中乘除法和浮点数的实现较为复杂,所以其他的综合器或许并不直接提供支持或者会出现仿真与实际不一致的结果,在 verilog 代码中使用乘除法或者取模会使得代码失去可移植性,这里并不建议使用。

这里介绍一种通用的算法用于分离十进数的各个数位,这种称为 移位加 3 算法 的算法 只使用加减和移位运算,它可以满足代码移植性的要求,移位加三算法的流程如下(这里假设要分离的只有 3 个数位):

  1. 将二进制数左移一位(未满 4 位在前面填 0)
  2. 如果移动了 8 位,那么二进制数就在 百位、十位和个位列,计算结束
  3. 在任何一个 BCD 列中,如果任何一个二进制数 大于或者等于 5,就把这个数 加上 3
  4. 回到步骤 1

下图展示了十六进制数 0x3F 其转化 BCD 码的流程:

我的 FPGA 学习历程(07)—— BCD 编码:移位加 3 算法-LMLPHP

根据上图,可以很容易的编写对应的 verilog 代码:

module bin2bcd8
(
input wire [:] binary,
output wire [:] b,
output wire [:] c,
output wire [:] d
); //***********************//
/*
* z 作为存储 BCD 码和 二进制码的寄存器
* 如果输入为 8 位,那么 z 需要的长度为
* 0xFF = 255 ---> 10-0101-0101 +++ ????-????
* 总共 18 位
*/
reg [:] z;
//***********************// always @ (*)
begin
z = 'b0; //置 0
z[:] = binary; //读入低 8 位
repeat () //重复 8 次
begin
if(z[: ]>) //大于 4 就加 3
z[: ] = z[: ] + 'b11;
if(z[:]>)
z[:] = z[:] + 'b11;
z[:] = z[:]; //左移一位
end
end
    assign b = z[:];                     //输出 BCD 码
assign c = z[:];
assign d = z[:] ; endmodule

部分仿真结果如下图:

我的 FPGA 学习历程(07)—— BCD 编码:移位加 3 算法-LMLPHP

同理,16 位二进制数的转化流程如下,和上图不同的是移动次数增加了,这次共需要移动 16 次,各位可以试着写出对应的 verilog 代码。

我的 FPGA 学习历程(07)—— BCD 编码:移位加 3 算法-LMLPHP

05-11 11:10