Menu Close

Verilog实现16进制到10进制(BCD)转换(3)快速算法

在之前的算法中,算法执行的速度还是有些慢, 我们设计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 所需要的位数。

Posted in FPGA, FPGA 教材教案, Verilog, Verilog, 教材与教案, 文章

发表回复

相关链接