写在前面的话
我们的数据在运算或者存储的时候,一般都是以二进制的格式存在的。但是在很多情况下,我们需要将运算结果显示到某种显示设备上,如果直接以二进制的形式来显示的话,会非常不便于我们查看。因此,我们需要首先将二进制数转换为十进制数再进行显示。二进制到十进制的转换有很多种方法。本节,梦翼师兄和大家一起学习一种国外目前最为流行的转换方法-逐步移位法。通过这种方式,我们不但可以在没有周期差的情况下实现数据格式的转换,同时我们的资源占用量也是相当小的。
基本概念
BCD码(Binary-Coded Decimal)也称二进码十进数或二-十进制代码。用4位二进制数来表示1位十进制数中的0~9这10个数码。BCD码这种编码形式利用了四个位元来储存一个十进制的数码,使二进制和十进制之间的转换得以快捷的进行。这种编码技巧在FPGA中经常用到,如矩阵键盘输入的数据需要在数码管上显示的时候,矩阵键盘输入的数字是二进制数,而数码管上需要显示的是十进制数,所以需要将二进制数转换成BCD码,这在我们以后的设计中会经常遇到。
7.3.3逐步移位法原理
在本设计中,我们使用逐步移位法来实现二进制数向BCD码的转换,在设计之前,我们先来了解一下二进制数向BCD码转换的原理-逐步移位法:
变量定义:
B:需要转换的二进制数位宽
D:转换后的BCD码位宽(其中BCD码的位宽计算如下:根据二进制数的位宽,求出它的无符号数能表示的最大值和最小值,如数据位宽是8位则数据范围大小就是0~255,我们取最大值255,每一个数字对应4位BCD码,三个数字就对应3x4=12位的BCD码)
N:需要转换的二进制数位宽加上转换后的BCD码位宽
逐步移位法的规则:
- 准备一个N比特的移位寄存器;
- 二进数逐步左移;
- 每次左移之后每个BCD位做大四加三的调整;
- 二进数全部移完,得到结果。
- 设计任务
我们本次的设计任务是将一个8位的二进制数转换成BCD码
分析如下:输入二进制数据的位宽是B=8位,用无符号数来表示的话,输入数据的范围大小就是0~255,我们取最大值255,其中每一个数字需要4位的BCD码来表示,所以BCD码的长度就是D=3x4=12比特。
总结:
- 准备一个N=B+D=8+12=20比特的移位寄存器;
- 二进数逐步左移;
- 每次左移之后每个BCD位做大四加三的调整;
- 二进数全部移完,得到结果。
现在,我们列一个表格来说明逐步移位法:
第几次移位 |
BCD[11:8] |
BCD[7:4] |
BCD[3:0] |
Bin[7:0] |
Start |
|
|
|
10100101 |
1 |
|
|
1 |
01001010 |
2 |
|
|
10 |
10010100 |
3 |
|
|
101 |
00101000 |
3 |
|
|
1000 |
00101000 |
4 |
|
1 |
0000 |
01010000 |
5 |
|
10 |
0000 |
10100000 |
6 |
|
100 |
0001 |
01000000 |
7 |
|
1000 |
0010 |
10000000 |
7 |
|
1011 |
0010 |
10000000 |
8 |
1 |
0110 |
0101 |
00000000 |
BCD |
1 |
6 |
5 |
|
由上表知:
Bin = 10100101 = 165;
BCD = 0001_0110_0101 = 1_6_5 = 165;
由此可知,逐步位移法是可以把二进制数转变成BCD码的。
顶层框图设计
我们掌握了上面的基本概念和明确了设计任务后,就可以开始设计我们的电路了,梦翼师兄的思路是首先建立一个bin_to_bcd的顶层模块,这个模块的主要功能是将输入的二进制数据进行扩展,然后将扩展后的数据输入到下一层的模块进行移位,最后将最后一次移位的结果取高12位输出即可;然后建立一个bcd_modify模块,这个模块的功能是将输入的数据进行移位,并将输入的数据的高12位分成3组分别输入到下一层的比较模块进行比较,每一次比较结束进行一次移位并输出数据;最后建立一个cmp模块,这个模块的主要功能是将输入的数据进行大四加三的调整,然后输出。各个模块的框图设计如下:
bin_to_bcd 顶层框架设计
bcd_modify 模块的框架设计:
cmp模块的框架设计:
代码实现
设计好上面的模块框图后,接着我们使用Verilog语言,把上述的电路结构描述出来:
bin_to_bcd顶层架构的代码如下:
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056
* The module function : 二进制数转8421BCD码顶层模块 *****************************************************/ 01 module bin_to_bcd( 02 bin, //二进制数输入 03 bcd //BCD码输出 04 ); 05 06 input [7:0] bin; //二进制数输入 07 output [11:0] bcd; //BCD码输出 08 09 wire [19:0] bcd_reg_0,bcd_reg_1,bcd_reg_2,bcd_reg_3,bcd_reg_4, 10 bcd_reg_5,bcd_reg_6,bcd_reg_7,bcd_reg_8; //8次移位结果输出 11 12 assign bcd_reg_0={12'b000000000000,bin}; //把输入的8位二进制数转换成20位 13 14 //第一次移位 15 bcd_modify b1(.data_in(bcd_reg_0),.data_out(bcd_reg_1)); 16 //第二次移位 17 bcd_modify b2(.data_in(bcd_reg_1),.data_out(bcd_reg_2)); 18 //第三次移位 19 bcd_modify b3(.data_in(bcd_reg_2),.data_out(bcd_reg_3)); 20 //第四次移位 21 bcd_modify b4(.data_in(bcd_reg_3),.data_out(bcd_reg_4)); 22 //第五次移位 23 bcd_modify b5(.data_in(bcd_reg_4),.data_out(bcd_reg_5)); 24 //第六次移位 25 bcd_modify b6(.data_in(bcd_reg_5),.data_out(bcd_reg_6)); 26 //第七次移位 27 bcd_modify b7(.data_in(bcd_reg_6),.data_out(bcd_reg_7)); 28 //第八次移位 29 bcd_modify b8(.data_in(bcd_reg_7),.data_out(bcd_reg_8)); 30 31 assign bcd={bcd_reg_8[19:8]}; //取高12位为输出结果 32 33 endmodule |
第9~10行我们定义了9个位宽是20的寄存器,第一个寄存器bcd_reg_0我们存放的是输入数据扩展之后的数据,也就是在第12行我们把输入的8位二进制数转换成了20位;第15~29行我们把bcd_modify模块例化了8次,前一个例化模块的输出总是当前模块的输入,比如第23行,输入的数据是bcd_reg_4,在进行了一次移位之后,输出的数据是bcd_reg_5,然后bcd_reg5又作为下一个第25行例化模块的输入,依次向下一级一级传递,进行了8次移位之后,第31行直接取第8次移位之后的结果的高12位作为转换后的BCD码输出。
bcd_modify模块的代码如下:
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * The module function : 移位模块 *****************************************************/ 01 module bcd_modify( 02 data_in, //需要移位比较数据输入 03 data_out //移位比较完成数据输出 04 ); 05 06 input [19:0]data_in; //需要移位比较数据输入 07 output [19:0]data_out; //移位比较完成数据输出 08 09 wire [3:0]bcd_reg2,bcd_reg3,bcd_reg1; //3次移位结果输出 10 11 //data_in[19:16]进行大四加三比较 12 cmp c1(.cmp_in(data_in[19:16]),.cmp_out(bcd_reg1)); 13 //data_in[15:12]进行大四加三比较 14 cmp c2(.cmp_in(data_in[15:12]),.cmp_out(bcd_reg2)); 15 //data_in[11:8]进行大四加三比较 16 cmp c3(.cmp_in(data_in[11:8]), .cmp_out(bcd_reg3)); 17 18 //data_in[19:8]全部比较完之后,左移一位 19 assign data_out={bcd_reg1[2:0],bcd_reg2,bcd_reg3,data_in[7:0],1'b0}; 20 21 endmodule |
第9行我们定义了3个位宽是4的寄存器,作用是存放比较之后的数据;第11~16行把cmp模块例化了3次,我们把输入数据的高12位分成3组分别送进了这3个例化模块的输入端口,作用是进行大四加三的调整;第19行将第12行~16行输出的数据存放到输出寄存器中进行一次左移操作。
cmp模块的代码如下:
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * The module function : 大四加三处理模块 *****************************************************/ 01 module cmp( 02 cmp_in, //比较器数据输入 03 cmp_out //比较器数据输出 04 ); 05 06 input [3:0]cmp_in; //比较器数据输入 07 output reg [3:0]cmp_out; //比较器数据输出 08 09 always @ (*) 10 begin 11 if (cmp_in > 4) 12 cmp_out = cmp_in + 3; //输入数据大四加三处理 13 else 14 cmp_out = cmp_in; //输入数据小于四不做任何处理 15 end 16 17 endmodule |
这个模块挺简单的,第9~15行只是将输入的数据和4进行了比较,大于4输出就是输入数据加三,小于4输入数据不做任何处理直接输出即可。
编写的测试代码如下:
01 `timescale 1ns/1ps //仿真时间单位是ns,仿真时间精度是ns 02 module bcd_tb; 03 04 reg [7:0]bin; //仿真激励二进制输入数据 05 06 wire [11:0]bcd; //仿真输出BCD码 07 08 bin_to_bcd u1(.bin(bin),.bcd(bcd)); //把激励信号送进BCD转换器 09 10 initial begin 11 bin=8'b0; //bin信号初始化 12 #100 bin=8'b1010_1101; //输入数据 173 13 #100 bin=8'b0000_1101; //输入数据 13 14 #100 bin=8'b1010_0100; //输入数据 164 15 #100 bin=8'b1000_0000; //输入数据 128 16 #100 bin=8'b1111_1111; //输入数据 255 17 end 18 19 endmodule |
仿真分析
如图所示,输入数据bin(无符号十进制表达)的值等于bcd(十六进制表达)输出的值,所以本次设计是成功的。