二进制转BCD8421码

发布时间 2023-07-02 16:37:44作者: 浅晓寒

8421码是BCD码中最常用的编码,使用4位二进制表示十进制数0~9,即0000~1001。例如十进制数12转二进制为1100,转化为8421码为0001 0010(十进制为18),两个编码相减得6。二进制转8421的规则是≥10就加6,否则不加6进行校正。
$$
\begin{array}{r}
10010B\
-1100B\
\hline
0110B
\end{array}
$$
double dabble算法是先让要转换的二进制数左移一位(相当于乘2),原来的规则就变为了≥5就加3。

左移前的判断式:$x≥10?+6:+0$,左移后变为$2x≥10?+6:+0$,解出新的判断式$x≥5?+3:+0$

举个例子:将13转为8421码0001 0011

移位次数等于要转换的二进制位数。

使用Verilog实现20位二进制数转8421码

module bcd8421(
	input 	wire				clk,
	input 	wire				reset_n,
	
	input 	wire	[19:0] 		data,	//要转换的二进制数据,0~999_999需要20bit
	
	//分别输出6位
	output	reg		[3:0] 		unit,	//个位
	output	reg 	[3:0] 		ten,	//十位
	output	reg 	[3:0] 		hun,	//百位
	output	reg 	[3:0] 		tho,	//千位
	output	reg 	[3:0] 		t_tho,	//万位
	output	reg 	[3:0] 		h_tho	//十万位
);
	reg [43:0]	data_all;	//43~20是BCD码,19~0是要转换的二进制数据
							//低20位数据每次左移一位至高24位
	
	reg shift_flag;		//左移标志位
	reg [4:0] shift_cnt;	//移位计数,原来有20位二进制数要左移20次 
	
	//完成一次判断和移位操作后,计数加一
	always@(posedge clk or negedge reset_n)
		if(!reset_n)
			shift_cnt <= 5'b0;
		else if((shift_cnt == 5'd21) && (shift_flag == 1'b1))
			shift_cnt <= 5'b0;
		else if(shift_flag == 1'b1)
			shift_cnt <= shift_cnt + 1;
		else
			shift_cnt <= shift_cnt;
		
	always@(posedge clk or negedge reset_n)
		if(!reset_n)
			data_all <= 44'b0;
		else if(shift_cnt <= 5'd0)
			data_all <= {24'b0,data};
		else if((shift_cnt <= 5'd20) && (shift_flag == 1'b0))	//判断是否大于4,大于4则加3
			begin
				data_all[23:20]   <=  (data_all[23:20] > 4) ? (data_all[23:20] + 2'd3) : (data_all[23:20]);
				data_all[27:24]   <=  (data_all[27:24] > 4) ? (data_all[27:24] + 2'd3) : (data_all[27:24]);
				data_all[31:28]   <=  (data_all[31:28] > 4) ? (data_all[31:28] + 2'd3) : (data_all[31:28]);
				data_all[35:32]   <=  (data_all[35:32] > 4) ? (data_all[35:32] + 2'd3) : (data_all[35:32]);
				data_all[39:36]   <=  (data_all[39:36] > 4) ? (data_all[39:36] + 2'd3) : (data_all[39:36]);
				data_all[43:40]   <=  (data_all[43:40] > 4) ? (data_all[43:40] + 2'd3) : (data_all[43:40]);
			end
		else if((shift_cnt <= 5'd20) && (shift_flag == 1'b1))
			data_all <= data_all << 1;
		else
			data_all <= data_all;
	
	always@(posedge clk or negedge reset_n)
		if(!reset_n)
			shift_flag <= 1'b0;
		else 
			shift_flag <= ~shift_flag;	//每次上升沿状态取反,0:大于4加三,1:执行左移操作
			
	//当计数器等于20时,移位判断操作完成,对各个位数的BCD码进行赋值		
	always@(posedge clk or negedge reset_n)
		if(!reset_n) begin
			unit	<= 4'b0;	
			ten		<= 4'b0;	
			hun		<= 4'b0;	
			tho		<= 4'b0;	
			t_tho	<= 4'b0;
			h_tho	<= 4'b0;
		end
		else if(shift_cnt == 5'd21) begin	//shift_cnt等于21时表示所有操作已经完成
			unit	<= data_all[23:20];	
			ten		<= data_all[27:24];	
			hun		<= data_all[31:28];	
			tho		<= data_all[35:32];	
			t_tho	<= data_all[39:36];
			h_tho	<= data_all[43:40];
		end

endmodule

编写testbench仿真文件

`timescale 1ns/1ns

module bcd8421_tb();
	reg				clk;
	reg				reset_n;
	reg     [19:0]		data;
	wire	[3:0] 		unit;	//个位
	wire 	[3:0] 		ten;	//十位
	wire 	[3:0] 		hun;	//百位
	wire 	[3:0] 		tho;	//千位
	wire 	[3:0] 		t_tho;	//万位
	wire 	[3:0] 		h_tho;	//十万位
	
	initial
    begin
        clk     =   1'b1;
		data    =   20'd123456;
        reset_n   <=  1'b0;
        #100
        reset_n   <=  1'b1;
    end

	//clk:产生时钟
	always	#10 clk <=  ~clk;
	
	bcd8421 bcd8421_inst(
		.clk			(clk),
		.reset_n		(reset_n),
		.data			(data),	//要转换的二进制数据,0~999_999需要20bit
		.unit			(unit),	//个位
		.ten			(ten),	//十位
		.hun			(hun),	//百位
		.tho			(tho),	//千位
		.t_tho			(t_tho),	//万位
		.h_tho			(h_tho)	//十万位
	);
	
endmodule

将十进制数123456转为BCD码,仿真结果如图所示。

参考资料:野火FPGA