浅谈BCD码同二进制转换
一、BCD码
1、BCD码概述
- BCD码(Binary-Coded Decimal),用4位二进制数来表示1位十进制数中的0~9这10个数码,是一种二进制的数字编码形式,用二进制编码的十进制代码。BCD码这种编码形式利用了四个位元来储存一个十进制的数码,使二进制和十进制之间的转换得以快捷的进行。
- 相对于一般的浮点式记数法,采用BCD码,既可保存数值的精确度,又可免去使计算机作浮点运算时所耗费的时间。
- BCD码也称二进码十进数,BCD码可分为有权码和无权码两类。其中,常见的有权BCD码有8421码、2421码、5421码,无权BCD码有余3码、余3循环码、格雷码。
图片表示
流程详解:
二进制数:1010 0010 = 2 5 + 2 7 + 2 1 = 2 + 32 + 128 = 162 2^5+2^7+2^1=2+32+128=162 25+27+21=2+32+128=162
BCD码是用四位二进制数表示十进制数【0~9】
0001 = 2 0 = 1 2^0=1 20=1
0110= 2 2 + 2 1 = 6 2^2+2^1=6 22+21=6
0010= 2 1 = 2 2^1=2 21=2
2、BCD分类
BCD码可分为有权码和无权码两类。其中,常见的有权BCD码有8421码、2421码、5421码,无权BCD码有余3码、余3循环码、格雷码。
- 有权码,自然二进制代码是按照二进制代码各位权值大小,以自然向下加一,逢二进一的方式来表示数值的大小所生成的代码。
- 有权码和无权码区别是每一位是否有权值。
1、有权码
8421
8421 BCD码是最基本和最常用的BCD码,它和四位自然二进制码相似,各位的权值为8、4、2、1,故称为有权BCD码。和四位自然二进制码不同的是,它只选用了四位二进制码中前10组代码,即用0000~1001分别代表它所对应的十进制数,余下的六组代码不用。
运算规则:
十进制 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
二进制(BCD8421) | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 |
按权相加 | 0 | 2 0 2^0 20 | 2 1 2^1 21 | 2 1 + 2 0 2^1+2^0 21+20 | 2 2 2^2 22 | 2 2 + 2 0 2^2+2^0 22+20 | 2 2 + 2 1 2^2+2^1 22+21 | 2 2 + 2 1 + 2 0 2^2+2^1+2^0 22+21+20 | 2 3 2^3 23 | 2 3 + 2 0 2^3+2^0 23+20 |
实例验证:
十进制数: 4 + 3 = 7 4+3=7 4+3=7
二进制数: 0100 + 0011 = 0111 0100+0011=0111 0100+0011=0111
/*二进制加法:逢二进一 */
0 1 0 0 4
+ 0 0 1 1 3
0 1 1 1 7
5421
十进制 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
二进制(BCD5421) | 0000 | 0001 | 0010 | 0011 | 0100 | 1000 | 1001 | 1010 | 1011 | 1100 |
2421
十进制 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
二进制(BCD2421) | 0000 | 0001 | 0010 | 0011 | 0100 | 1011 | 1100 | 1101 | 1110 | 1111 |
总结:
有权码就是对应数字为对应位的权值,1代表可以取到该值,0表示取不到。
8421:十进制数:5 二进制数:0101 即4和1能取到 4 + 1 = 5 4+1=5 4+1=5
5421: 十进制数:5 二进制数:1000 即5能取到 5 5 5
2421:十进制数:9 二进制数:1111即2和4和2和1都能取到 2 + 4 + 2 + 1 = 9 2+4+2+1=9 2+4+2+1=9
2、无权码
余三码
余3码是8421 BCD码的每个码组加3(0011)形成的。常用于BCD码的运算电路中。
十进制 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
二进制(BCD余三码) | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 |
实例验证:
十进制数:7
二进制数;0111
2 2 + 2 1 + 2 0 = 4 + 2 + 1 = 7 2^2+2^1+2^0=4+2+1=7 22+21+20=4+2+1=7
/*二进制加法:逢二进一 */
0 1 1 1 7
+ 0 0 1 1 3
1 0 1 0 余三码: 7 十进制数:10
余三循环码
余3循环码是无权码,即每个编码中的1和0没有确切的权值,整个编码直接代表一个数值。主要优点是相邻编码只有一位变化,避免了过渡码产生的“噪声”。
十进制 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
二进制(BCD余三循环码) | 0010 | 0110 | 0111 | 0101 | 0100 | 1100 | 1101 | 1111 | 1110 | 1010 |
3、BCD运算问题
我们知道,BCD是用四位二进制数表示十进制数【0~9】的,而四位二进制数可以表示的十进制数为 2 4 = 16 2^4=16 24=16 个即【0~15】,当运算超出范围怎么办?
解决方案:
以 5 + 8 = 13 5+8=13 5+8=13为例
十进制数: 5 + 8 = 13 5+8=13 5+8=13
二进制数: 0101 + 1000 = 1101 0101+1000=1101 0101+1000=1101
差值: 16 − 10 = 6 16-10=6 16−10=6
将所得值加6,产生进位,高位补0
1101 + 0110 = 00010011 1101+0110=00010011 1101+0110=00010011
/*二进制加法:逢二进一 */
1 1 0 1 13
+ 0 1 1 0 6
1 1 产生进位
0 0 0 1 0 0 1 1 高位补0
二、二进制BCD码
1、原理实现
使用逐步移位法来实现二进制数向BCD码的转换;
变量定义:
- B:需要转换的二进制数位宽
- D:转换后的BCD码位宽(其中BCD码的位宽计算如下:根据二进制数的位宽,求出它的无符号数能表示的最大值和最小值,如数据位宽是8位则数据范围大小就是0~255,我们取最大值255,每一个数字对应4位BCD码,三个数字就对应3x4=12位的BCD码)
- N:需要转换的二进制数位宽加上转换后的BCD码位宽
逐步移位法的规则:
- 准备一个N比特的移位寄存器;
- 二进数逐步左移;
- 每次左移之后每个BCD位做大四加三的调整;
- 二进数全部移完,得到结果。
移位流程表
移位次数 | BCD[11:8] | BCD[7:4] | BCD[3:0] | bin[7:0] | 说明 |
---|---|---|---|---|---|
0 | 10100101 | 输入二进制数 | |||
1 | 1 | 01001010 | 左移一位,低位补0 | ||
2 | 10 | 10010100 | 左移两位,低位补0 | ||
3 | 101 | 00101000 | 101=5;大于4加3 | ||
3 | 1000 | 00101000 | 加3不移位 | ||
4 | 1 | 0000 | 01010000 | 1000移位,原始值也移位,低位补0 | |
5 | 10 | 0000 | 10100000 | 循环移位,低位补0 | |
6 | 100 | 0001 | 01000000 | 循环移位,低位补0 | |
7 | 1000 | 0010 | 10000000 | 循环移位,低位补0 | |
7 | 1011 | 0010 | 10000000 | 循环移位,低位补0 | |
8 | 1 | 0110 | 0101 | 00000000 | 循环移位,低位补0 |
BCD | 1 | 6 | 5 | 移位结束,输出BCD码 |
2、模块划分
顶层模块
代码实现
module bin2bcd (
input [7:0] bin ,//二进制输入
output [11:0] bcd //bcd码输出
);
//信号定义
wire [19:0] bcd_reg0 ;
wire [19:0] bcd_reg1 ;
wire [19:0] bcd_reg2 ;
wire [19:0] bcd_reg3 ;
wire [19:0] bcd_reg4 ;
wire [19:0] bcd_reg5 ;
wire [19:0] bcd_reg6 ;
wire [19:0] bcd_reg7 ;
wire [19:0] bcd_reg8 ;//八次移位结果输出
assign bcd_reg0 = {
12'b0000_0000_0000,bin};//将输入的八位二进制转换为二十位
//第一次移位
bcd_modify b1
(.data_in(bcd_reg0),.data_out(bcd_reg1));
//第二次移位
bcd_modify b2
(.data_in(bcd_reg1),.data_out(bcd_reg2));
//第三次移位
bcd_modify b3
(.data_in(bcd_reg2),.data_out(bcd_reg3));
//第四次移位
bcd_modify b4
(.data_in(bcd_reg3),.data_out(bcd_reg4));
//第五次移位
bcd_modify b5
(.data_in(bcd_reg4),.data_out(bcd_reg5));
//第六次移位
bcd_modify b6
(.data_in(bcd_reg5),.data_out(bcd_reg6));
//第七次移位
bcd_modify b7
(.data_in(bcd_reg6),.data_out(bcd_reg7));
//第八次移位
bcd_modify b8
(.data_in(bcd_reg7),.data_out(bcd_reg8));
//BCD输出
assign bcd = {
bcd_reg8[19:8]};//取高12位为输出结果
endmodule
移位模块
代码实现
//移位处理模块
module bcd_modify (
input [19:0] data_in ,//移位比较数据输入
output [19:0] data_out //移位比较数据输出
);
//信号定义
wire [3:0] reg1 ;//移位结果输出
wire [3:0] reg2 ;
wire [3:0] reg3 ;
//左移大4加3比较 [19:16]
bcd_cmp c1
(.cmp_in(data_in[19:16]),.cmp_out(reg1));
//左移大4加3比较 [15:12]
bcd_cmp c2
(.cmp_in(data_in[15:12]),.cmp_out(reg2));
//左移大4加3比较 [11:8]
bcd_cmp c3
(.cmp_in(data_in[11:8]),.cmp_out(reg3));
//比较完成 左移一位
assign data_out = {
reg1[2:0],reg2,reg3,data_in[7:0],1'b0};
endmodule
大四加三处理模块
代码实现
//大四加三处理
module bcd_cmp(
input [3:0] cmp_in ,//比较器数据输入
output reg[3:0] cmp_out //比较器数据输出
);
always @(*) begin
if(cmp_in > 4)
cmp_out = cmp_in + 3;//大于四加三
else
cmp_out = cmp_in;//小或等于四 不作处理
end
endmodule
3、仿真调试
仿真文件【testbench】
`timescale 1ns/1ps
module bin2bcd_tb();
reg [7:0] bin ;
wire [11:0] bcd ;
bin2bcd u_bin2bcd
(
.bin ( bin ),
.bcd ( bcd )
);
parameter CYCLE = 20;
initial begin
#(CYCLE*200);
bin = 8'b0;//bin信号初始化
#(CYCLE*100);
bin = 8'b1010_1101;//173
#(CYCLE*100);
bin = 8'b0000_1101;//13
#(CYCLE*100);
bin = 8'b1010_0100;//164
#(CYCLE*100);
bin = 8'b1000_0000;//128
#(CYCLE*100);
bin = 8'b1111_1111;//255
$stop;
end
endmodule
【do】文件
vlib work
vmap work work
#编译testbench文件
vlog bin2bcd_tb.v
#编译设计文件
vlog ../rtl/bcd_cmp.v
vlog ../rtl/bin2bcd.v
vlog ../rtl/bcd_modify.v
#指定仿真顶层
vsim -novopt work.bin2bcd_tb
#添加信号到波形窗
add wave -position insertpoint sim:/bin2bcd_tb//*
do调试流程
打开仿真调试工具modelsim
点击【file】->【change Directory…】
找到tb文件【或者仿真测试文件还有do文件所在目录】
左下角查看打开的目录
使用指令,开始仿真【do do.do】;然后回车
do filename.do
编译无报错【注意错误和警告信息】
4、仿真验证
将bin用十进制表示,加上【unsigned】
#(CYCLE*200);
bin = 8'b0;//bin信号初始化
#(CYCLE*100);
bin = 8'b1010_1101;//173
#(CYCLE*100);
bin = 8'b0000_1101;//13
#(CYCLE*100);
bin = 8'b1010_0100;//164
#(CYCLE*100);
bin = 8'b1000_0000;//128
#(CYCLE*100);
bin = 8'b1111_1111;//255
对比可以发现,输入值符合预期。
下面任意取一个值进行验证。
十进制数:128
BCD码表示:0001 0010 1000
取BCD输出的高12位为输出结果【输入为8,低位为移位补0值】
经过对比,输出与实际值吻合。
三、BCD码转二进制
1、原理实现
移位算法原理
二进制码左移一位等于未左移的二进制码*2,例如有二进制码101001,转成十进制等于41,左移一位得到1010010,转成十进制等于82。也就是说二进制码左移1位加上左移3位可以等效于二进制码乘以10。
2、模块划分
移位相加
代码实现
/************************工程说明*********************
BCD码转二进制码
*****************************************************/
module bcd2bin(
input clk ,//系统时钟 50Mhz
input rst_n ,//复位 低电平有效
input [3:0] bw ,//BCD码百位
input [3:0] sw ,//BCD码十位
input [3:0] gw ,//BCD码个位
output [9:0] bin //二进制输出
);
//信号定义
reg [9:0] bw_v1 ;//BCD码百位寄存器1
reg [9:0] bw_v2 ;//BCD码百位寄存器2
reg [9:0] bw_v3 ;//BCD码百位寄存器3
reg [9:0] sw_v1 ;//BCD码十位寄存器1
reg [9:0] sw_v2 ;//BCD码十位寄存器2
reg [9:0] gw_v1 ;//BCD码个位寄存器
/**********************换算规则:**********************
BCD百位:a*100=a*(64+32+4)=a*64+a*32+a*4 =a000000+a00000+a00,即a左移6位加上左移5位加上a左移2位
BCD十位:a*10=a*(8+2)=a*8+a*2 =a000+a0,即a左移3位加上左移1位
BCD个位:个位数据不变
将所有的各个位的转换结果相加就是转换后的二进制数
左移运算:2^n 右移运算:1/(2^n)
******************************************************/
//寄存器赋初值
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
bw_v1 <= 0;
bw_v2 <= 0;
bw_v3 <= 0;
sw_v1 <= 0;
sw_v2 <= 0;
gw_v1 <= 0;
end
else begin
bw_v1 <= bw << 6;//左移六位
bw_v2 <= bw << 5;//左移五位
bw_v3 <= bw << 2;//左移两位
sw_v1 <= sw << 3;//左移三位
sw_v2 <= sw << 1;//左移一位
gw_v1 <= gw;//个位保持不变
end
end
assign bin = bw_v1 + bw_v2 + bw_v3 + sw_v1 + sw_v2 + gw_v1;
endmodule
3、仿真验证
仿真测试文件【testbench】
//时间单位/精度
`timescale 1ns/1ps
module bcd2bin_tb();
//激励信号
reg clk ;
reg rst_n ;
reg [3:0] bw ;
reg [3:0] sw ;
reg [3:0] gw ;
//输出信号
wire [9:0] bin ;
//模块例化
bcd2bin u_bcd2bin
(
.clk ( clk ),
.rst_n ( rst_n ),
.bw ( bw ),
.gw ( gw ),
.sw ( sw ),
.bin ( bin )
);
//产生时钟
parameter CYCLE = 20;
always #(CYCLE/2) clk = ~clk;
//产生激励
initial begin
clk = 1'b0;
rst_n = 1'b0;
bw = 4'd0; sw = 4'd0; gw = 4'd0;//000
#(CYCLE*500);
rst_n = 1'b1;
#(CYCLE*1000);
bw = 4'd1; sw = 4'd2; gw = 4'd0;//120
#(CYCLE*1000);
bw = 4'd3; sw = 4'd2; gw = 4'd9;//329
#(CYCLE*1000);
bw = 4'd7; sw = 4'd0; gw = 4'd3;//703
#(CYCLE*1000);
bw = 4'd0; sw = 4'd2; gw = 4'd7;//027
#(CYCLE*1000);
bw = 4'd2; sw = 4'd9; gw = 4'd0;//290
#(CYCLE*5000);
$stop;
end
endmodule
do文件
vlib work
vmap work work
#编译testbench文件
vlog bcd2bin_tb.v
#编译设计文件
vlog ../rtl/bcd2bin.v
#指定仿真顶层
vsim -novopt work.bcd2bin_tb
#添加信号到波形窗
add wave -position insertpoint sim:/bcd2bin_tb//*
验证结果
输入值查看
仿真输入值
bw = 4'd0; sw = 4'd0; gw = 4'd0;//000
#(CYCLE*500);
rst_n = 1'b1;
#(CYCLE*1000);
bw = 4'd1; sw = 4'd2; gw = 4'd0;//120
#(CYCLE*1000);
bw = 4'd3; sw = 4'd2; gw = 4'd9;//329
#(CYCLE*1000);
bw = 4'd7; sw = 4'd0; gw = 4'd3;//703
#(CYCLE*1000);
bw = 4'd0; sw = 4'd2; gw = 4'd7;//027
#(CYCLE*1000);
bw = 4'd2; sw = 4'd9; gw = 4'd0;//290
#(CYCLE*5000);
输出结果
百位:左移六位+左移五位+左移两位
十位:左移三位+左移一位
个位:不变
经对比,结果符合预期。