Menu Close

Verilog实现16进制到10进制(BCD)转换(1)加减计数法

由于在数字系统与计算机系统中,数值的表达方式和与计算都是以2进制的方式实现的,由于2进制与16进制一一对应,对应的权值不变,因此2进制运算有时也称为16进制运算。但由于人们已经习惯了10进制的数制方式,特别当表示的数值较大时,以16进制展现方式在普通用户面前几乎没有直观概念,因此本节及后续的几节内容将介绍16进制到10进制转换的算法和程序实现。

16进制到BCD码的转换有多种方法,其中以加减计数法与权值变换法算法结构完善,被广泛使用。本节内容对加减计数法进行介绍,并将算法用Verilog代码实现。

  1. 加减计数法实现16进制到10进制的转换。

一般情况下如果要转换的数值不大,可以直接利用计数的方式实现。通常情况下10进制数据是用来显示的,因此对转换速率要求不高,毕竟显示更换的速度太快,人的眼睛也不能及时反应。

例如对于小于100M的数据,只要在1秒内完成转换就可以满足要求。

加减计数法的实现算法如下,对于一个给定的2进制数,首先将该数减1,同时将设定的BCD的寄存器加1。在加减的过程中,减法以2进制的方式进行,加法以十进制的方式进行,即在加计数的过程中每个位的权都是10(逢9进1)。

%title插图%num

图1

在图1中,分6个步骤完成

(1)变量定义:

临时变量tmpa: 将输入值捕获用于转换

计算变量 tmp0_BCD,tmp1_BCD… 用于计算

输出变量out0_BCD, out1_BCD… 将计算结果输出

输出指示 cal_done,握手信号,通知上层,计算完成,结果可以使用了

(2)捕获,将输入值捕获下来,存入tmpa中

(3)判断tmpa是否为0,如果为0,不用计算,直接更新输出寄存器

(4)计算,tmpa减1,tmp0_BCD,tmp1_BCD…十进制加1

(5)tmpa==0 ?, 为0,更新输出寄存器,否则继续运算

(6)更新输出寄存器,程序返回继续捕获新数并重复上述步骤。

按照上面6个步骤,抽象出状态机的模型,定义状态,分析状态转移条件,并由状态机实现应用程序。经过分析,主程序应该分成4个状态可以完成上面的算法,4个状态分别如下:

  • 状态0: 计算请求捕获
  • 状态1: 响应请求,并锁存计算的数据
  • 状态2: 计算BCD码
  • 状态3: 更新BCD码寄存器

对应的状态转移图如图1所示。

%title插图%num

图1 状态转移图

 

2. 加减计数法程序设计

例1:利用加减计数法实现16进制到10进制的转换,数据转换范围是0-999999。并将转换后的数据缓存。

程序如下:

module hex2bcd
(
    input rst,
    input clk,

    input cal_req,
    input [ 19: 0 ] indata,
    
    output reg[ 3: 0 ] out0_BCD,
    output reg[ 3: 0 ] out1_BCD,
    output reg[ 3: 0 ] out2_BCD,
    output reg[ 3: 0 ] out3_BCD,
    output reg[ 3: 0 ] out4_BCD,
    output reg[ 3: 0 ] out5_BCD,

    output reg cal_done

);

reg [ 19: 0 ] tmpa;
reg [ 19: 0 ] tmpa_r;

reg cal_req_r;
reg cal_got;
reg cal_update;

reg [ 3: 0 ] tmp0_BCD;
reg [ 3: 0 ] tmp1_BCD;
reg [ 3: 0 ] tmp2_BCD;
reg [ 3: 0 ] tmp3_BCD;
reg [ 3: 0 ] tmp4_BCD;
reg [ 3: 0 ] tmp5_BCD;

reg [ 1: 0 ] cal_st;

// data capture, assume indata and cal_req are synchronized with clk,
// otherwise synchronization should be done first

always@( posedge clk or posedge rst )
if ( rst )
begin
    tmpa <= 0;
    cal_req_r <= 1'b0;
end
else
begin
    if ( cal_req )
    begin
        tmpa <= indata;
        cal_req_r <= 1'b1;
    end
    else if ( cal_got )
        cal_req_r <= 0;
end

//====================

always@( posedge clk or posedge rst )
if ( rst )
begin
    tmp0_BCD <= 0;
    tmp1_BCD <= 0;
    tmp2_BCD <= 0;
    tmp3_BCD <= 0;
    tmp4_BCD <= 0;
    tmp5_BCD <= 0;
    tmpa_r <= 0;
    cal_got <= 0;
    cal_update <= 0;
    cal_st <= 0;
end
else
begin
    case ( cal_st )
    0:
    begin
        tmp0_BCD <= 0;
        tmp1_BCD <= 0;
        tmp2_BCD <= 0;
        tmp3_BCD <= 0;
        tmp4_BCD <= 0;
        tmp5_BCD <= 0;
        cal_got <= 0;
        cal_update <= 0;
        tmpa_r <= 0;

        if ( cal_req_r )
            cal_st <= 1;
    end
    1:
    begin
        cal_got <= 1'b1;
        tmpa_r <= tmpa;
        cal_st <= 2;
    end
    2:
    begin
        cal_got <= 0;
        if ( tmpa_r == 0 )
            cal_st <= 3;
        else
        begin
            tmpa_r <= tmpa_r - 1'b1;

            if ( tmp0_BCD == 9 )
            begin
                tmp0_BCD <= 0;

                if ( tmp1_BCD == 9 )
                begin
                    tmp1_BCD <= 0;

                    if ( tmp2_BCD == 9 )
                    begin
                        tmp2_BCD <= 0;

                        if ( tmp3_BCD == 9 )
                        begin
                            tmp3_BCD <= 0;

                            if ( tmp4_BCD == 9 )
                            begin
                                tmp4_BCD <= 0;

                                if ( tmp5_BCD == 9 )
                                    tmp5_BCD <= 0;
                                else
                                    tmp5_BCD <= tmp5_BCD + 1'b1;
                            end
                            else
                                tmp4_BCD <= tmp4_BCD + 1'b1;
                        end
                        else
                            tmp3_BCD <= tmp3_BCD + 1'b1;
                    end
                    else
                        tmp2_BCD <= tmp2_BCD + 1'b1;
                end
                else
                    tmp1_BCD <= tmp1_BCD + 1'b1;
            end
            else
                tmp0_BCD <= tmp0_BCD + 1'b1;
        end
    end

    3:
    begin
        cal_update <= 1'b1;
        cal_st <= 0;
    end
    default:
        cal_st <= 3;
    endcase
end

//=====================update

always@( posedge clk or posedge rst )
if ( rst )
begin
    out0_BCD <= 0;
    out1_BCD <= 0;
    out2_BCD <= 0;
    out3_BCD <= 0;
    out4_BCD <= 0;
    out5_BCD <= 0;
    cal_done <= 1'b0;
end
else
begin
    cal_done <= 1'b0;

    if ( cal_update )
    begin
        out0_BCD <= tmp0_BCD;
        out1_BCD <= tmp1_BCD;
        out2_BCD <= tmp2_BCD;
        out3_BCD <= tmp3_BCD;
        out4_BCD <= tmp4_BCD;
        out5_BCD <= tmp5_BCD;
        cal_done <= 1'b1;
    end
end

endmodule

 

仿真文件代码如下所示:

`timescale 1ns / 1ps


module sim(
    );
    
reg clk = 0;

always #5 clk = ~clk;

reg rst = 1;
reg cal_req = 0;
reg [19:0] indata = 0;

wire [3:0] out0_BCD;
wire [3:0] out1_BCD;
wire [3:0] out2_BCD;
wire [3:0] out3_BCD;
wire [3:0] out4_BCD;
wire [3:0] out5_BCD;

wire cal_done;
initial
begin
    rst = 1;
    #200;
    rst = 0;
    #20;
    @(posedge clk);
    
    indata = 20'hf_423f;      //999_999
    cal_req = 1;
    @(posedge clk);
    cal_req = 0;
    while (!cal_done) @(posedge clk);
    
    indata = 0;
    cal_req = 1;
    @(posedge clk);
    cal_req = 0;
    while (!cal_done) @(posedge clk);
    
    indata = 1;
    cal_req = 1;
    @(posedge clk);
    cal_req = 0;
    while (!cal_done) @(posedge clk);
    
    indata = 123456;
    cal_req = 1;
    @(posedge clk);
    cal_req = 0;
    while (!cal_done) @(posedge clk);
    
    #200;
    $stop;
end

add_sub_hex_bcd  add_sub_hex_bcd_inst
(
    .rst        (rst),
    .clk        (clk),

    .cal_req    (cal_req),
    .indata     (indata),

    .out0_BCD   (out0_BCD),
    .out1_BCD   (out1_BCD),
    .out2_BCD   (out2_BCD),
    .out3_BCD   (out3_BCD),
    .out4_BCD   (out4_BCD),
    .out5_BCD   (out5_BCD),
    
    .cal_done   (cal_done)

);    
    
endmodule

 

仿真波形如下所示:

%title插图%num

图2 仿真波形图

 

%title插图%num

图3 仿真波形图

从图2和图3中可以看出数据转换的结果是正确的。总的来说,上述程序有如下特点:

  • 缺点:数据转换的时间不等长,数值越大,计算时间越长
  • 优点:允许的时钟频率很高。对于Artix 系列FPGA, 200MHz时钟下,布局布线可以做到很好的时序收敛

本程序相对比较简单,但要注意程序的各个过程之间的握手,以及与外界接口之间的握手。

思考题:

1.分析下面的代码在整体中的作用。

always@(posedge clk or posedge rst)
if(rst)
begin
    tmpa <= 0;
    cal_req_r <= 1'b0;
end
else
begin
    if(cal_req)
    begin
        tmpa <= indata;
        cal_req_r <= 1'b1;
    end
    else if(cal_got)
        cal_req_r <= 0;
end

 

2.上面的例子采用一段式状态机实现HEX-BCD的转换,尝试用二段,三段式状态机描述上面的方法。并用PRA006与PRX100-D验证算法的正确性。

Posted in FPGA, FPGA, IP开发, Verilog, 开发语言, 教材与教案, 文章

发表评论

相关链接