在FPGA开发中,我们习惯与二进制打交道,逻辑运算、状态机跳转,无一不是0和1的舞蹈。然而,当系统需要与人交互——比如在数码管上显示当前计数值,或者向上位机发送测量结果时,如何让它在数码管上显示为"123456",而不是让人去心算十六进制?
如果要显示“123”,我们无法直接将二进制 1111011 扔给数码管驱动。我们需要一种将二进制数值转化为十进制各位数值(即BCD码)的机制。这就是经典的二进制转BCD码问题。
通过一个标准的“移位加3”算法模块,实现二进制转BCD码。bin_to_bcd 模块展示了如何用最小的硬件代价(移位寄存器 + 少量比较器)解决复杂的数值转换问题。避开了耗资源的除法器,是FPGA“以时间换空间”设计思想的典范。
一、 算法核心:为什么是“加3”?
如果直接将二进制数除以10来获取各位数值,在硬件上代价昂贵。除法器通常占用大量资源且时序难以收敛。因此,工程师们发明了Double Dabble(移位加3)算法。 其核心思想利用了二进制左移一位等于“乘2”的特性,模拟十进制的进位过程:
移位:将二进制数逐位左移进入BCD寄存器。
修正:每次移位后,如果BCD当前位大于等于5(即 0101),在下次移位前必须加3。
为什么是加3?
因为BCD码的范围是0-9(0000-1001)。当数值大于等于5时,比如 0101 (5),如果直接左移(乘2),会变成 1010 (10),这在BCD中是非法码。
通过预先加3,比如 0101 + 0011 = 1000 (8),再左移变成 10000 (16),高位产生进位,低位归0,完美符合十进制 5 * 2 = 10 的运算逻辑。这就是算法的精髓。
二、 模块架构解析
实现的 bin_to_bcd 模块,采用了一种紧凑的状态机设计,支持将20位二进制数转换为6位BCD码(最大支持百万级数值)。
1. 数据结构:44位移位寄存器
代码中最核心的信号是 data_shift。这是一个44位的寄存器:
高24位:预留给BCD码(6位 * 4bit)。
低20位:存放待转换的二进制数。
reg [43:0] data_shift;
// 结构: [十万位][万位]...[个位] [二进制原数据]
2. 状态流转:移位与判断
为了节省资源,模块没有使用庞大的组合逻辑加法树,而是采用了时序迭代的方式。通过 shift_flag 信号,将每个比特的处理过程分为两个节拍:
节拍1(判断/加3):检查BCD的每一位是否大于4。如果是,则执行加3操作。
节拍2(移位):将整个44位寄存器左移一位,将二进制数的最高位移入BCD的最低位。
这种“判断-移位”交替进行的逻辑,由 cnt_shift 计数器控制,循环20次,完成所有数据的移位。
3. 逻辑实现细节
在源码中,我们可以看到经典的判断逻辑:
// 对每个BCD位独立判断
if (data_shift[23:20] > 4)
data_shift[23:20] <= data_shift[23:20] + 2'd3;
这段代码在时钟的驱动下,周而复始地修正数值。当计数器 cnt_shift 达到21时,意味着20位数据全部处理完毕,此时 data_shift 的高24位就是我们要的BCD码结果。
三、 仿真验证
仿真不仅验证了逻辑的正确性,更展示了算法的执行过程。
1. 测试策略
测试平台 tb_bin_to_bcd 采用了“穷举边界 + 典型值”的策略:
边界值:测试了0、1、9(不进位边界)、10(进位边界)。
全位测试:从个位到十万位,依次测试了99、999、9999直至最大值999,999。
随机典型值:如123456、987654等,确保中间过程无误。
2. 自动化检查
在仿真代码中,我们定义了一个 test_and_check 任务,自动计算预期结果并与模块输出比对:
// 等待足够长的转换周期
repeat(100) @(posedge i_clk);
// 比对结果
if (bcd_out !== expected) ... else ...
这种自动化任务大大简化了测试代码的编写,能够直观地在控制台打印出 [PASS] 或 [FAIL] 信息。
3. 仿真结果分析
观察仿真波形,可以清晰地看到转换过程:
当输入为 20'd123456 时,经过一段固定的时钟延迟,输出端 o_hunderd_thousand 等依次稳定输出 1, 2, 3, 4, 5, 6。
特别值得注意的是处理延迟。由于采用了串行移位算法,对于20位数据,转换过程大约需要消耗一定个数时钟周期。这在低速显示应用(如数码管刷新)中完全可以忽略不计,但在高频吞吐场景下需注意吞吐率的设计。
四、 应用场景与总结
这个模块虽然逻辑简单,但在实际工程中应用极其广泛:
数码管动态显示:这是最典型的应用。FPGA内部计数器运行在二进制,通过该模块实时转换为BCD码,直接驱动数码管的段选信号,实现计数显示。
测量仪器接口:在电压表、频率计等设计中,测量结果通常是二进制数值,通过BCD转换后可送往单片机或LCD屏幕进行十进制显示。
低速数据通信:在某些工业总线协议中,数据格式要求为十进制ASCII码,BCD码往往是二进制转ASCII的中间桥梁。
五、如何获取?
关注公众号
我是小灰灰的FPGA,关注我,带你从入门到精通数字逻辑设计。
后台回复(建议复制粘贴):bin_to_bcd
250