在之前的算法中,算法执行的速度还是有些慢, 我们设计BCD 快速算法实现,这样就不会因为输入数据的不同, 而产生输出的结果的时钟周期不同。在文章权值变换法实现16进制到BCD码转换中, 我们介绍了6位数码管的的hex 到 BCD 的转换方法, 将这篇文章的内容总结, 整理,就可以实现任意的hex数到BCD的转换, 也可以实现已知BCD 数码管的个数, 推算出需要的hex 的位数了。本文中的使用的都是参数化的设计, 适应性还是比较强的。同时也利用了系统函数$clog2 , $ceil , $log10等,帮助我们定位位宽。
代码1:在已知多少位BCD码,计算可以输入的二进制最大有多少位。
`timescale 1ns / 1ps
module hex2bcd_fast #
(
parameter BCD_NUM = 10,
parameter HEX_NUM = $clog2(10 ** BCD_NUM)
)
(
input sys_clk,
input [ HEX_NUM - 1: 0 ] hex,
input start,
output reg [ BCD_NUM * 4 - 1: 0 ] bcd_out,
output reg rdy,
input rst
);
reg [ HEX_NUM - 1: 0 ] hex_reg;
integer i,j;
always @ ( * )
begin
hex_reg = hex;
bcd_out = 0;
for ( i = HEX_NUM - 1; i >= 0; i = i - 1 )
begin
for(j = 1; j <= BCD_NUM; j = j + 1)
begin
if( bcd_out[j*4 - 1 -:4] >= 5 )
bcd_out[j*4 - 1 -:4] = bcd_out[j*4 - 1 -:4] + 3;
end
{ bcd_out,hex_reg } = { bcd_out,hex_reg } << 1;
end
end
reg [ 3: 0 ] count;
reg [ 1: 0 ] st;
always@( posedge sys_clk or posedge rst )
if ( rst )
begin
count <= 0;
rdy <= 0;
st <= 0;
end
else
begin
rdy <= 0;
case ( st )
0:
begin
if ( start )
st <= 1;
end
1:
begin
if ( count == 2 )
begin
count <= 0;
st <= 2;
end
else
count <= count + 1;
end
2:
begin
st <= 0;
rdy <= 1'b1;
end
default:
st <= 0;
endcase
end
endmodule
仿真程序:
`timescale 1ns / 1ps
module test_sim(
);
reg rst = 1;
reg clk = 0;
always #5 clk = ~clk;
parameter BCD_NUM = 6;
parameter HEX_NUM = $clog2(10 ** BCD_NUM);
reg hex_start = 0;
reg [ HEX_NUM - 1: 0 ] hex_data = 0;
wire bcd_rdy;
initial
begin
rst = 1;
#10;
rst = 0;
#20;
@(posedge clk);
hex_data = 0;
hex_start = 1;
@(posedge clk);
hex_start = 0;
while (!bcd_rdy) @(posedge clk);
hex_data = 23;
hex_start = 1;
@(posedge clk);
hex_start = 0;
while (!bcd_rdy) @(posedge clk);
hex_data = 999;
hex_start = 1;
@(posedge clk);
hex_start = 0;
while (!bcd_rdy) @(posedge clk);
hex_data = 2323;
hex_start = 1;
@(posedge clk);
hex_start = 0;
while (!bcd_rdy) @(posedge clk);
hex_data = 54687;
hex_start = 1;
@(posedge clk);
hex_start = 0;
while (!bcd_rdy) @(posedge clk);
hex_data = 999999;
hex_start = 1;
@(posedge clk);
hex_start = 0;
while (!bcd_rdy) @(posedge clk);
#200;
$stop;
end
wire [ BCD_NUM * 4 - 1: 0 ] bcd_out;
wire [3:0] bcd_seg[ BCD_NUM - 1:0 ];
genvar i;
generate
for (i = 0; i < BCD_NUM; i = i + 1)
assign bcd_seg[ i ] = bcd_out[ i*4 + 3 : i*4 ];
endgenerate
hex2bcd_fast #
( .BCD_NUM (BCD_NUM))
hex2bcd_fast_inst
(
.rst (rst),
.sys_clk (clk),
.start (hex_start),
.hex (hex_data),
.bcd_out (bcd_out),
.rdy (bcd_rdy)
);
endmodule
仿真输出结果

从仿真结果可以看出, 输入的数据不会影响输出所需要的时钟数量,也就是说,这个算法需要的时钟是稳定的, 可以快速的计算出BCD 的结果。注意,这种算法转换的效率比较高, 输入,输出需要的时钟稳定, 但是缺点是综合布线后,时钟不会非常高。(布线逻辑比较大) 当然,掌握算法后, 也可以通过增加pipeline 分级锁存, 使得整个综合时序提高。
代码2:在已知输入的二进制位宽,计算BCD码位数
代码如下:
`timescale 1ns / 1ps
module hex2bcd_hex_width #
(
parameter HEX_NUM = 20,
parameter [31:0] BCD_NUM = $ceil( $log10(2 ** HEX_NUM + 1) )
)
(
input sys_clk,
input [ HEX_NUM - 1: 0 ] hex,
input start,
output reg [ BCD_NUM * 4 - 1: 0 ] bcd_out,
output reg rdy,
input rst
);
reg [ HEX_NUM - 1: 0 ] hex_reg;
integer i,j;
always @ ( * )
begin
hex_reg = hex;
bcd_out = 0;
for ( i = HEX_NUM - 1; i >= 0; i = i - 1 )
begin
for(j = 1; j <= BCD_NUM; j = j + 1)
begin
if( bcd_out[j*4 - 1 -:4] >= 5 )
bcd_out[j*4 - 1 -:4] = bcd_out[j*4 - 1 -:4] + 3;
end
{ bcd_out,hex_reg } = { bcd_out,hex_reg } << 1;
end
end
reg [ 3: 0 ] count;
reg [ 1: 0 ] st;
always@( posedge sys_clk or posedge rst )
if ( rst )
begin
count <= 0;
rdy <= 0;
st <= 0;
end
else
begin
rdy <= 0;
case ( st )
0:
begin
if ( start )
st <= 1;
end
1:
begin
if ( count == 2 )
begin
count <= 0;
st <= 2;
end
else
count <= count + 1;
end
2:
begin
st <= 0;
rdy <= 1'b1;
end
default:
st <= 0;
endcase
end
endmodule
仿真文件如下:
`timescale 1ns / 1ps
module test_sim(
);
parameter HEX_NUM = 19;
parameter [31:0] BCD_NUM = $ceil( $log10(2 ** HEX_NUM) );
reg rst = 1;
reg clk = 0;
always #5 clk = ~clk;
reg hex_start = 0;
reg [ HEX_NUM - 1: 0 ] hex_data = 0;
wire bcd_rdy;
initial
begin
rst = 1;
#10;
rst = 0;
#20;
@(posedge clk);
hex_data = 0;
hex_start = 1;
@(posedge clk);
hex_start = 0;
while (!bcd_rdy) @(posedge clk);
hex_data = 23;
hex_start = 1;
@(posedge clk);
hex_start = 0;
while (!bcd_rdy) @(posedge clk);
hex_data = 999;
hex_start = 1;
@(posedge clk);
hex_start = 0;
while (!bcd_rdy) @(posedge clk);
hex_data = 2323;
hex_start = 1;
@(posedge clk);
hex_start = 0;
while (!bcd_rdy) @(posedge clk);
hex_data = 54687;
hex_start = 1;
@(posedge clk);
hex_start = 0;
while (!bcd_rdy) @(posedge clk);
hex_data = 999999;
hex_start = 1;
@(posedge clk);
hex_start = 0;
while (!bcd_rdy) @(posedge clk);
#200;
$stop;
end
wire [ BCD_NUM * 4 - 1: 0 ] bcd_out;
wire [3:0] bcd_seg[BCD_NUM - 1:0];
genvar i;
generate
for (i = 0; i < BCD_NUM; i = i + 1)
assign bcd_seg[ i ] = bcd_out[ i*4 + 3 : i*4 ];
endgenerate
hex2bcd_hex_width #( .HEX_NUM (HEX_NUM))
hex2bcd_fast_inst
(
.rst (rst),
.sys_clk (clk),
.start (hex_start),
.hex (hex_data),
.bcd_out (bcd_out),
.rdy (bcd_rdy)
);
endmodule
仿真结果如下:

从结果中可以看出, 我们利用$ceil 系统函数 和 $log10 系统函数,实现了输入hex 的位数, 计算出BCD 所需要的位数。