简体   繁体   English

在VHDL中将8位二进制数转换为BCD

[英]Convert 8bit binary number to BCD in VHDL

The algorithm is well known, you do 8 left shifts and check the units, tens or hundreds bits (4 each) after each shift. 该算法是众所周知的,您需要进行8个左移,并在每次移位后检查数十或数百位(每个4位)的单位。 If they are above 4 you add 3 to the group and so on... 如果它们大于4,则将3加到组中,依此类推...

Here is a process based solution that does not work. 这是无法使用的基于过程的解决方案。 It will compile but the output is not what I wanted. 它将编译,但是输出不是我想要的。 Any thoughts what could be the problem? 有什么想法可能是问题吗?

library ieee ;
use ieee.std_logic_1164.all ;
use ieee.std_logic_unsigned.all ;

entity hex2bcd is
    port ( hex_in  : in  std_logic_vector (7 downto 0) ;
           bcd_hun : out std_logic_vector (3 downto 0) ;
           bcd_ten : out std_logic_vector (3 downto 0) ;
           bcd_uni : out std_logic_vector (3 downto 0) ) ;
end hex2bcd ;

architecture arc_hex2bcd of hex2bcd is
begin
    process ( hex_in )
        variable hex_src : std_logic_vector (7 downto 0) ;
        variable bcd     : std_logic_vector (11 downto 0) ;
    begin
        hex_src := hex_in ;
        bcd     := (others => '0') ;

        for i in 0 to 7 loop
            bcd := bcd(11 downto 1) & hex_src(7) ; -- shift bcd + 1 new entry
            hex_src := hex_src(7 downto 1) & '0' ; -- shift src + pad with 0

            if bcd(3 downto 0) > "0100" then
                bcd(3 downto 0) := bcd(3 downto 0) + "0011" ;
            end if ;
            if bcd(7 downto 4) > "0100" then
                bcd(7 downto 4) := bcd(7 downto 4) + "0011" ;
            end if ;
            if bcd(11 downto 8) > "0100" then
                bcd(11 downto 8) := bcd(11 downto 8) + "0011" ;
            end if ;
        end loop ;

        bcd_hun <= bcd(11 downto 8) ;
        bcd_ten <= bcd(7  downto 4) ;
        bcd_uni <= bcd(3  downto 0) ;

    end process ;
end arc_hex2bcd ;

The comments were getting too long. 评论太长了。

Consider the following block diagram: 考虑以下框图:

bin8bcd框图

This represents an unrolled loop ( for i in 0 to 7 loop ) and shows that no add +3 occurs before i = 2 for the LS BCD digit and no add +3 occurs before i = 5 for the middle BCD digit, and no adjustment occurs on the MS BCD digit, which is comprise in part of static '0' values. 这表示展开的循环( for i in 0 to 7 loop ),并且表明对于LS BCD数字,在i = 2之前没有加+3发生,对于中间BCD数字,在i = 5之前没有加+3发生,并且没有调整发生在MS BCD数字上,该数字部分包含静态“ 0”值。

This gives us a total of 7 add3 modules (represented by the enclosing if statement, and conditional add +3). 这总共给我们提供了7个add3模块(由封闭的if语句表示,而有条件的add +3表示)。

This is demonstrated in VHDL: 这在VHDL中得到了证明:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity bin8bcd is
    port (
        bin:    in  std_logic_vector (7 downto 0);
        bcd:    out std_logic_vector (11 downto 0)
    );
end entity;

architecture struct of bin8bcd is
    procedure add3 (signal bin: in  std_logic_vector (3 downto 0); 
                    signal bcd: out std_logic_vector (3 downto 0)) is
    variable is_gt_4:  std_logic;
    begin
        is_gt_4 := bin(3) or (bin(2) and (bin(1) or bin(0)));

        if is_gt_4 = '1' then
        -- if to_integer(unsigned (bin)) > 4 then
            bcd <= std_logic_vector(unsigned(bin) + "0011");
        else
            bcd <= bin;
        end if;
    end procedure;

    signal U0bin,U1bin,U2bin,U3bin,U4bin,U5bin,U6bin:
                std_logic_vector (3 downto 0);

    signal U0bcd,U1bcd,U2bcd,U3bcd,U4bcd,U5bcd,U6bcd:
                std_logic_vector (3 downto 0);       
begin
    U0bin <= '0' & bin (7 downto 5);
    U1bin <= U0bcd(2 downto 0) & bin(4);
    U2bin <= U1bcd(2 downto 0) & bin(3);
    U3bin <= U2bcd(2 downto 0) & bin(2);
    U4bin <= U3bcd(2 downto 0) & bin(1);

    U5bin <= '0' & U0bcd(3) & U1bcd(3) & U2bcd(3);
    U6bin <= U5bcd(2 downto 0) & U3bcd(3);

U0: add3(U0bin,U0bcd);

U1: add3(U1bin,U1bcd);

U2: add3(U2bin,U2bcd);

U3: add3(U3bin,U3bcd);

U4: add3(U4bin,U4bcd);

U5: add3(U5bin,U5bcd);

U6: add3(U6bin,U6bcd);

OUTP:
    bcd <= '0' & '0' & U5bcd(3) & U6bcd & U4bcd & bin(0);

end architecture;

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity bin8bcd_tb is
end entity;

architecture foo of bin8bcd_tb is
    signal bin: std_logic_vector (7 downto 0) := (others => '0');
    -- (initialized to prevent those annoying metavalue reports)

    signal bcd: std_logic_vector (11 downto 0);

begin

DUT:
    entity work.bin8bcd
        port map (
            bin => bin,
            bcd => bcd
        );

STIMULUS:
    process

    begin
        for i in 0 to 255 loop
            bin <= std_logic_vector(to_unsigned(i,8));
            wait for 1 ns;
        end loop;
        wait for 1 ns;
        wait;
    end process;
end architecture;

That when the accompanying test bench is run yields: 在运行附带的测试台时,结果如下:

bin8bcd_tb png

And if you were to scroll through the entire waveform you'd find that all bcd outputs from 001 to 255 are present and accounted for (no holes), no 'X's or 'U's anywhere. 而且,如果您要滚动浏览整个波形,则会发现存在从001到255的所有bcd输出,并且都已考虑在内(无孔),任何地方都没有'X'或'U'。

From the representation in the block diagram showing i = 7 we see that no add +3 occurs after the final shift. 从显示i = 7的框图中的表示,我们可以看到在最后一次移位之后没有加+3发生。

Also note that the LSB of bcd is always the LSB of bin, and that bcd(11) and bcd(10) are always '0'. 还要注意,bcd的LSB始终是bin的LSB,bcd(11)和bcd(10)始终为0。

The add3 can be hand optimized to create an increment by 3 using logic operators to get rid of any possibility of reporting meta values derived from bin (and there'd be a lot of them). 可以使用逻辑运算符手动优化add3以创建3的增量,从而消除报告从bin派生的元值的任何可能性(并且有很多可能)。

As far as I can tell this represents the most optimized representation of 8 bit binary to 12 bit BCD conversion. 据我所知,这代表了8位二进制到12位BCD转换的最优化表示。

Sometime previously I wrote a C program to provide input to espresso (a term minimizer): 之前的某个时候,我编写了一个C程序来为espresso(术语“最小化器”)提供输入:

/*
 * binbcd.c   - generates input to espresso for 8 bit binary
 *         to 12 bit bcd.
 *
 */
#include <stdlib.h>
#include <stdio.h>


int main (argc, argv)
int argc;
char **argv;
{
int binary;
int bit;
char bcd_buff[4];
int digit;
int bcd;

    printf(".i 8\n");
    printf(".o 12\n");

    for (binary = 0; binary < 256; binary++)  {
        for ( bit = 7; bit >= 0; bit--) {
            if ((1 << bit) & binary)
                printf("1");
            else
                printf("0");
        }

        digit = snprintf(bcd_buff,4,"%03d",binary); /* leading zeros */

        if (digit != 3) {
            fprintf(stderr,"%s: binary to string conversion failure, digit = %d\n",
                argv[0],digit);
            exit (-1);
        }

        printf (" ");  /* input to output space */

        for ( digit = 0; digit <= 2; digit++) {
            bcd = bcd_buff[digit] - 0x30;
            for (bit = 3; bit >= 0; bit--) {
                if ((1 << bit) & bcd) 
                    printf("1");
                else
                    printf("0"); 
            }
        }
        /* printf(" %03d",binary); */
        printf("\n");
    }

    printf (".e\n");
    exit (0);

Then started poking around with intermediary terms, which leads you directly to what is represented in the block diagram above. 然后开始研究中间术语,直接将您引向上面框图中的内容。

And of course you could use an actual component add3 as well as use nested generate statements to hook everything up. 当然,您可以使用实际的组件add3以及使用嵌套的generate语句来连接所有内容。

You won't get the same minimized hardware from a loop statement representation without constraining the if statements (2 < i < 7 for the LS BCD digit, 5 < i < 7 for the middle BCD digit). 如果不限制if语句(对于LS BCD数字为2 <i <7,对于中间BCD数字为5 <i <7),您将无法从循环语句表示中获得相同的最小化硬件。

You'd want the subsidiary nested generate statement to provide the same constraints for a shortened structural representation. 您希望辅助嵌套的generate语句为缩短的结构表示提供相同的约束。

A logic operator version of add3 is shown on PDF page 5 on the university lecture slides for Binary to BCD Conversion using double dabble, where the forward tick is used for negation notation, "+" signifies OR, and Adjacency signifies AND. 大学讲座幻灯片的 PDF第5页上显示了add3的逻辑运算符版本,用于使用双涉水进行二进制到BCD的转换,其中正向刻度用于表示否定符号,“ +”表示OR,而邻接表示AND。

The add3 then looks like: 然后,add3看起来像:

procedure add3 (signal bin: in  std_logic_vector (3 downto 0); 
                signal bcd: out std_logic_vector (3 downto 0)) is

begin

    bcd(3) <=  bin(3) or 
              (bin(2) and bin(0)) or 
              (bin(2) and bin(1));

    bcd(2) <= (bin(3) and bin(0)) or
              (bin(2) and not bin(1) and not bin(0));

    bcd(1) <= (bin(3) and not bin(0)) or
              (not bin(2) and bin(1)) or
              (bin(1) and bin(0));

    bcd(0) <= (bin(3) and not bin(0)) or
              (not bin(3) and not bin(2) and bin(0)) or
              (bin(2) and bin(1) and not bin(0));

end procedure;

Note this would allow package numeric_std (or equivalent) to be dropped from the context clause. 请注意,这将允许从上下文子句中删除软件包numeric_std(或等效项)。

If you write signals in AND terms in the same order (in this case left to right) the duplicated AND terms show up well, as the also do using espresso. 如果您以相同的顺序(在这种情况下,从左到右)在AND项中写入信号,则重复的AND项会很好地显示,就像使用espresso一样。 There is no value in using intermediary AND terms in an FPGA implementation, these all fit it LUTs just the way they are. 在FPGA实现中使用中间AND术语没有任何价值,所有这些都完全适合LUT。

espresso input for add3: .i 4 .o 4 0000 0000 0001 0001 0010 0010 0011 0011 0100 0100 0101 1000 0110 1001 0111 1010 1000 1011 1001 1100 1010 ---- 1011 ---- 1100 ---- 1101 ---- 1110 ---- 1111 ---- .e 用于add3的espresso输入: .i 4 .o 4 0000 0000 0001 0001 0010 0010 0011 0011 0100 0100 0101 1000 0110 1001 0111 1010 1000 1011 1001 1100 1010 ---- 1011 ---- 1100 ---- 1101 ---- 1110 ---- 1111 ---- .e

And espresso's output (espresso -eonset): .i 4 .o 4 .p 8 -100 0100 00-1 0001 --11 0010 -01- 0010 -110 1001 -1-1 1000 1--1 1100 1--0 1011 .e 和espresso的输出(espresso-eonset):.i .i 4 .o 4 .p 8 -100 0100 00-1 0001 --11 0010 -01- 0010 -110 1001 -1-1 1000 1--1 1100 1--0 1011 .e

When you consider the combinatorial 'depth' of the binary to BCD conversion, for an FPGA it's 6 LUTs (the 6th an input to something following). 考虑到二进制到BCD转换的组合“深度”,对于FPGA,它是6个LUT(第6个是后面输入的输入)。 That likely limits the clock speed to something shy of 100 MHz if the conversion occurs in one clock. 如果转换发生在一个时钟中,则可能会将时钟速度限制在100 MHz以下。

By pipelining or using sequential logic (clocked loop) you'd be able to run an FPGA at it's fastest speed while executing in 6 clocks. 通过流水线或使用顺序逻辑(时钟循环),您将能够以最快的速度运行FPGA,同时执行6个时钟。

At least two issues appear: 至少出现两个问题:

  • Adding is done after shift, and not before as described in the Double dabble algorithm 加法是在移位之后完成的,而不是在Double dabble算法中描述的之前完成的

  • The bcd shift goes bcd(11 downto 1) , but should be bcd(10 downto 0) bcd移位为bcd(11 downto 1) ,但应为bcd(10 downto 0)

So try with the code: 因此,尝试使用代码:

process ( hex_in )
    variable hex_src : std_logic_vector (7 downto 0) ;
    variable bcd     : std_logic_vector (11 downto 0) ;
begin
    hex_src := hex_in ;
    bcd     := (others => '0') ;

    for i in 0 to 7 loop
        if bcd(3 downto 0) > "0100" then
            bcd(3 downto 0) := bcd(3 downto 0) + "0011" ;
        end if ;
        if bcd(7 downto 4) > "0100" then
            bcd(7 downto 4) := bcd(7 downto 4) + "0011" ;
        end if ;
        if bcd(11 downto 8) > "0100" then
            bcd(11 downto 8) := bcd(11 downto 8) + "0011" ;
        end if ;

        bcd := bcd(10 downto 0) & hex_src(7) ; -- shift bcd + 1 new entry
        hex_src := hex_src(6 downto 0) & '0' ; -- shift src + pad with 0
    end loop ;

    bcd_hun <= bcd(11 downto 8) ;
    bcd_ten <= bcd(7  downto 4) ;
    bcd_uni <= bcd(3  downto 0) ;

end process ;

However, the implementation may require a slow clock... 但是,实施可能需要慢一点的时钟...

Based on Davids observations in the comments, the code be optimized to: 根据评论中Davids的观察,将代码优化为:

process ( hex_in )
    variable hex_src : std_logic_vector (4 downto 0) ;
    variable bcd     : std_logic_vector (11 downto 0) ;
begin
    bcd             := (others => '0') ;
    bcd(2 downto 0) := hex_in(7 downto 5) ;
    hex_src         := hex_in(4 downto 0) ;

    for i in hex_src'range loop
        if bcd(3 downto 0) > "0100" then
            bcd(3 downto 0) := bcd(3 downto 0) + "0011" ;
        end if ;
        if bcd(7 downto 4) > "0100" then
            bcd(7 downto 4) := bcd(7 downto 4) + "0011" ;
        end if ;
        -- No roll over for hundred digit, since in 0 .. 2

        bcd := bcd(10 downto 0) & hex_src(hex_src'left) ; -- shift bcd + 1 new entry
        hex_src := hex_src(hex_src'left - 1 downto hex_src'right) & '0' ; -- shift src + pad with 0
    end loop ;

    bcd_hun <= bcd(11 downto 8) ;
    bcd_ten <= bcd(7  downto 4) ;
    bcd_uni <= bcd(3  downto 0) ;
end process ;

1.you need take bit 10 to 0 of BCD and from 6 to 0 of hex_src to shift properly. 1.您需要将BCD的位10设置为0,并将hex_src的位设置为6至0才能正确移位。

2.after the 8th shift, the hex_src value you should not add further, try restricting adding on 7th shifting, you can use if statement to avoid. 2.在第8个移位之后,您不应该进一步添加hex_src值,尝试在第7个移位上限制加法,可以使用if语句来避免。

  1. after each conversion reset BCD value to zero. 每次转换后,将BCD值重置为零。

after above correction code should work 上述更正代码后应该工作

This works in quartus 18.1 lite 这适用于Quartus 18.1 Lite

    LIBRARY ieee;                       
USE ieee.std_logic_1164.ALL; 
use ieee.numeric_std.all;  
--converting a 8bit binary number to a 12bit bcd
entity bin2bcd is
port (bin :in std_logic_vector (7 downto 0);
        bcd1 : out std_logic_vector (3 downto 0);
        bcd2 : out std_logic_vector (3 downto 0);
        bcd3 : out std_logic_vector (3 downto 0));
end entity;

architecture rtl of bin2bcd is 
begin
process ( bin )
    variable binx : std_logic_vector (7 downto 0) ;
    variable bcd     : std_logic_vector (11 downto 0) ;
begin
    bcd             := (others => '0') ;
    binx        := bin(7 downto 0) ;

    for i in binx'range loop
        if bcd(3 downto 0) > "0100" then
          bcd(3 downto 0) := std_logic_vector(unsigned( bcd(3 downto 0)) + "0011"); 

        end if ;
        if bcd(7 downto 4) > "0100" then
           bcd(7 downto 4) := std_logic_vector(unsigned( bcd(7 downto 4)) + "0011");    
        end if ;
        bcd := bcd(10 downto 0) & binx(7) ; 
        binx := binx(6 downto 0) & '0' ; 
    end loop ;

    bcd3 <= bcd(11 downto 8) ;
    bcd2 <= bcd(7  downto 4) ;
    bcd1 <= bcd(3  downto 0) ;
end process ;
end architecture;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM