在之前的算法中,算法执行的速度还是有些慢, 我们设计BCD 快速算法实现,这样就不会因为输入数据的不同, 而产生输出的结果的时钟周期不同。在文章权值变换法实现16进制到BCD码转换中, 我们介绍了6位数码管的的hex 到 BCD 的转换方法, 将这篇文章的内容总结, 整理,就可以实现任意的hex数到BCD的转换, 也可以实现已知BCD 数码管的个数, 推算出需要的hex 的位数了。本文中的使用的都是参数化的设计, 适应性还是比较强的。同时也利用了系统函数$clog2 , $ceil , $log10等,帮助我们定位位宽。
代码1:在已知多少位BCD码,计算可以输入的二进制最大有多少位。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
`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 |
仿真程序:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
`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码位数
代码如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
`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 |
仿真文件如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
`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 所需要的位数。