1

I'm trying to make a 16bit to BCD conversion. I have found this link for a 8 bit and I'm trying to convert it to 16 bits. http://vhdlguru.blogspot.nl/2010/04/8-bit-binary-to-bcd-converter-double.html

I don't know what im doing wrong the rpm_1000 keeps changing and the rpm_100 stays at 4. Does anyone have a idea what i did wrong?

process (Hex_Display_Data)
        variable i : integer:=0;
        variable bcd : std_logic_vector(19 downto 0) := (others => '0');
        variable bint : std_logic_vector(15 downto 0) := Hex_Display_Data;

    begin
        for i in 0 to 15 loop  -- repeating 16 times.
        bcd(19 downto 1) := bcd(18 downto 0);  --shifting the bits.
        bcd(0) := bint(15); -- shift bit in
        bint(15 downto 1) := bint(14 downto 0); --removing msb
        bint(0) :='0'; -- adding a '0'


        if(i < 15 and bcd(3 downto 0) > "0100") then --add 3 if BCD digit is greater than 4.
        bcd(3 downto 0) := bcd(3 downto 0) + "0011";
        end if;

        if(i < 15 and bcd(7 downto 4) > "0100") then --add 3 if BCD digit is greater than 4.
        bcd(7 downto 4) := bcd(7 downto 4) + "0011";
        end if;

        if(i < 15 and bcd(11 downto 8) > "0100") then  --add 3 if BCD digit is greater than 4.
        bcd(11 downto 8) := bcd(11 downto 8) + "0011";
        end if;

        if(i < 15 and bcd(15 downto 12) > "0100") then  --add 3 if BCD digit is greater than 4.
        bcd(15 downto 12) := bcd(15 downto 12) + "0011";
        end if;

    end loop;

    rpm_1000    <= bcd(15 downto 12);
    rpm_100     <= bcd(11 downto 8);
    rpm_10      <= bcd(7 downto 4);
    rpm_1       <= bcd(3 downto 0);


end process ;
Dylan
  • 45
  • 1
  • 9
  • You might find it useful to include a [Minimal, Complete and Verifiable Example](http://stackoverflow.com/help/mcve) if you require help debugging. Note that there are two `i` variables in the process, a declared variable and a loop constant in an inner declarative region (the loop statement). The declared variable `i` is not used and can be removed. Your 'vhdlguru' reports "[I consider myself, still as a newbie in VHDL and forgive me if you find any mistakes in my posts...I am NOT a guru in vhdl.](http://vhdlguru.blogspot.co.nz/p/about-me.html)". –  Sep 17 '16 at 17:27

2 Answers2

2

Note four BCD digits can be wholly contained in 14 bits of input (your Hex_Display_Data) and unused bcd 'bits' (19 downto 16) will get eaten during synthesis along with all the add 3's that can't occur because their upper two bits are '0's (not > 4).

If you constrain your bcd value to 4 hex digits, and your loop iteration to 14 bits:

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

entity bin2bcd is
    port ( 
        input:      in   std_logic_vector (15 downto 0);
        ones:       out  std_logic_vector (3 downto 0);
        tens:       out  std_logic_vector (3 downto 0);
        hundreds:   out  std_logic_vector (3 downto 0);
        thousands:  out  std_logic_vector (3 downto 0)
    );
end entity;

architecture fum of bin2bcd is
    alias Hex_Display_Data: std_logic_vector (15 downto 0) is input;
    alias rpm_1:    std_logic_vector (3 downto 0) is ones;
    alias rpm_10:   std_logic_vector (3 downto 0) is tens;
    alias rpm_100:  std_logic_vector (3 downto 0) is hundreds;
    alias rpm_1000: std_logic_vector (3 downto 0) is thousands;
begin
    process (Hex_Display_Data)
        type fourbits is array (3 downto 0) of std_logic_vector(3 downto 0);
        -- variable i : integer := 0;  -- NOT USED
        -- variable bcd : std_logic_vector(15 downto 0) := (others => '0');
        variable bcd:   std_logic_vector (15 downto 0);
        -- variable bint : std_logic_vector(15 downto 0) := Hex_Display_Data;
        variable bint:  std_logic_vector (13 downto 0); -- SEE process body
    begin
        bcd := (others => '0');      -- ADDED for EVERY CONVERSION
        bint := Hex_Display_Data (13 downto 0); -- ADDED for EVERY CONVERSION

        for i in 0 to 13 loop
            bcd(15 downto 1) := bcd(14 downto 0);
            bcd(0) := bint(13);
            bint(13 downto 1) := bint(12 downto 0);
            bint(0) := '0';

            if i < 13 and bcd(3 downto 0) > "0100" then
                bcd(3 downto 0) := 
                    std_logic_vector (unsigned(bcd(3 downto 0)) + 3);
            end if;
            if i < 13 and bcd(7 downto 4) > "0100" then
                bcd(7 downto 4) := 
                    std_logic_vector(unsigned(bcd(7 downto 4)) + 3);
            end if;
            if i < 13 and bcd(11 downto 8) > "0100" then
                bcd(11 downto 8) := 
                    std_logic_vector(unsigned(bcd(11 downto 8)) + 3);
            end if;
            if i < 13 and bcd(15 downto 12) > "0100" then
                bcd(11 downto 8) := 
                    std_logic_vector(unsigned(bcd(15 downto 12)) + 3);
            end if;
        end loop;

        (rpm_1000, rpm_100, rpm_10, rpm_1)  <= 
                  fourbits'( bcd (15 downto 12), bcd (11 downto 8), 
                               bcd ( 7 downto  4), bcd ( 3 downto 0) );
    end process ;
end architecture;

Note the use of aliases to enable your names to be used in an existing otherwise compatible Minimal, Complete and Verifiable Example which your question did not provide.

Aggregate signal assignment is also taken from the original, your assignment to the individual digits should work just fine.

There are two changes besides limiting the conversion to 14 bits and the number of BCD digits to match the number of digits output.

The bcd and bint variables are now cleared every time the process is resumed (sensitive to updates to Hex_Display_Data). These were causing causing your otherwise unverifiable errors more than likely.

Extraneous parentheses have been removed.

You didn't supply context clauses. The code shown uses package numeric_std as opposed to the -2008 numeric_std_unsigned offering compatibility with earlier revisions of the standard while using IEEE authored packages.

You'll get something that works, provable with a testbench:

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

entity bin2bcd_tb is
end entity;

architecture foo of bin2bcd_tb is
    signal input:      std_logic_vector (15 downto 0) := (others => '0');
    signal ones:       std_logic_vector (3 downto 0);
    signal tens:       std_logic_vector (3 downto 0);
    signal hundreds:   std_logic_vector (3 downto 0);
    signal thousands:  std_logic_vector (3 downto 0);
begin
DUT:
    entity work.bin2bcd
        port map (
            input => input,
            ones => ones,
            tens => tens,
            hundreds => hundreds,
            thousands => thousands
        );
STIMULUS:
    process
    begin
        for i in 0 to 1001 loop
            wait for 20 ns;
            input <= std_logic_vector(to_unsigned(9999 - i, 16));
        end loop;
        wait for 20 ns;
        wait;
    end process;
end architecture;

Some other stimulus scheme can be used to toggle BCD digit roll over of all four digits.

This testbench provides input values starting at 9999 and decrementing 1001 times to show all four digits transitioning:

bin2bcd_tb.png

I can easily be modified to prove every transition of every BCD digit.

In summary the errors you were encountering appear to have come from the difference in elaboration for variables in a subprogram, where bcd and bint would be dynamically elaborated and initialized every function call, and in the process where they would be only initialized once.

From examining Xilinx's User Guide 901 Vivado Design Suite User Guide, Synthesis (2015.3), Chapter 4: VHDL Support, Combinatorial Processes, case Statements, for-loop Statements, the for loop appears to be supported for synthesis and has been reported to be synthesis eligible in other double dabble questions on stackoverflow. The issue would be support for repetitive assignment to variables in repeated sequences of sequential statements, which should be supported. There are is at least one other double dabble question on stackoverflow where successful synthesis had been reported using such a for loop.

Note that constraining the input value you deal with to 14 bits doesn't detect the effects of larger binary numbers (> 9999) which your process does not otherwise do either providing only 4 BCD output digits. You could deal with that by checking if the input value is greater than 9999 (x"270F").

The + 3 represents 1 LUT depth in an FPGA (4 bit input, 4 bit output), there are some number of them layered in depth based on the size of the converted number (the range of i). Allowing time for conversion propagation through ADD3's is offset by the rate at which the display can be visually interpreted. If you updated Hex_Display_Data in the millisecond range you likely could not tell the difference visually.

Community
  • 1
  • 1
0

Running the loop 16 times will cause the value in the BCD registers to be multiplied by 65536 (mod 100000) and added to the value in the binary registers. Say the value is 4000. Then 4000x65536 yields 44000. 44000x65536 yields 84000. 84000x65536 yields 24000. 24000x65536 yields 64000. And 64000x65536 yields 4000.

To make the algorithm work, you must start out by clearing the BCD registers. It also wouldn't hurt to fix the comment about how many times your loop runs.

Incidentally, a practical implementation of a binary to BCD converter should generally accept a clock input, and perform one step for each active clock edge. If your VHDL is running entirely in simulation the complexity of the resulting logic won't matter, but trying to perform everything at once in real hardware will be rather expensive. By contrast, the hardware to do a simple shift of the binary number and a multiply-by-two of the BCD number will be much simpler. Note that if you do things "all at once", the most significant bit of the output will depend upon the second-least-significant bit of the input, meaning the input signal will have to propagate through all the logic in one step. By contrast, if you shift by one bit per clock cycle, each bit of the output will depend only upon at most four bits of the input (since each digit will be in the range 0-9 before the adjustment phase, adding 3 will never cause a carry out).

Also, the "double dabble" algorithm requires that the adjustment be performed before the BCD shifts, but it looks as though the code is performing the adjustment after. Doing the adjustment after is fine if one looks at bit ranges 16..13, 12..9, 8..5, and 4..1 rather than 15..12, etc. Alternatively, one could specify that the value of bits 19..17 should be the value of bits 18..16, the value of bits 16..13 should be either the value of bits 15..12 (if less than 5) or the value of bits 15..12, plus three (if greater), etc. Such a formulation would set the value of each bit in exactly one place, which would make it easier to see how it should be rendered into hardware.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • isnt it cleared here? variable bcd : std_logic_vector(19 downto 0) := (others => '0'); – Dylan Sep 17 '16 at 16:48
  • The behavior you described was symptomatic of it not being cleared. I don't know why it wouldn't be cleared, but I think one of the other answers identified a possible reason. – supercat Sep 18 '16 at 21:17