Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use "function" in VHDL to return multiple variables from the same calculation?

Tags:

vhdl

fpga

I am trying to implement a decoding code in a function to return multiple variables because, i have to decode two fixed point variables and i get an error for insufficient logic in fpga. so i need to perform decoding code as we do in c language--> call the function twice with two variables and get return in individual digits. is this possible to perform? if it is possible how to do it? or are there any other way to do this?

i am performing the code in below twice for two fixed points numbers and do the decoding for fractional part as well.decoding works fine for integer part and fractional part. after decoding i convert them to std_logic_vector and send to my lcd. that's why i decode the value. code is big around 1000 lines. so i cannot put it here.

in the current code i perform twice same decoding so it makes big logic which fpga does not support. they work individually. so i need to put this once into a function and call the function twice.

VDHL sfixed decoding code does not work properly

as the link code shows i get 6 variables for integer part and 6 variables for fractional part. i need to put this code in a function where it returns 12 variables.

like image 413
komto909 Avatar asked Sep 12 '25 08:09

komto909


1 Answers

In the referenced question VDHL sfixed decoding code does not work properly Renaud Pacalet commented in his answer Your divisions should be integer divisions; they are not (fixed point divisions). there is no particular reason to be using signed fix point numbers in the actual conversion process, nor integers for that matter.

There's an algorithm (Double dabble) that uses shifts and 4 bit conditional adds that can be implemented in a loop statement. The one here is an expansion of the algorithm for 20 bit integer parts.

There's also a method of implementing an equivalent for the fractional part:

[Multi-Digit Binary to Decimal Converter (decimal fractions too!)](http://www.minecraftforum.net/forums/minecraft-discussion/redstone-discussion-and/redstone-creations/352668-multi-digit-binary-to-decimal-converter-decimal"Multi-Digit Binary to Decimal Converter (decimal fractions too!)")

An explanation of how the Algorithm works can be found on this website: http://www.johnloomis.org/ece314/notes/devices/binary_to_BCD/bin_to_bcd.html

I went one step further though, and figured out how to apply the same principle to convert Binary Fractions to Decimal Fractions. For example, 0.11(bin) can be converted to 0.75(dec). by using conversion tables with that perform the inverse function (-3 if 8 or more) and shifting in the opposite direction.

Last edited by Hans_Lemurson: Jun 9, 2012

While not implemented in a Minecraft game the fraction dabble involves a logic cell per comparison and subtraction just like double dabble does for addition.

The bits are shifted serially, double dabble is a multiplier, the fraction dabble is a divider. The double dabble has a maximum number of add3 logic cells of 20 bits x 7 BCD digits (28 bits) or 140 logic cells. It's reduced by the number of cells that can't change the result, either based on not meeting the threshold (> 4) or guaranteed to have all '0' inputs. That ends up as 57 logic cells (each four 4x1 LUTs).

The fraction dabble uses more cells because shifting from the left immediately throws out thresholds for sub3 operations. The reduction following synthesis mapping from the 18 bits x 6 BCD digits or 108 sub3 cells only loses 6, giving a 102 logic cells. The difference between an add3 and a sub3 is modulo four addition versus subtraction and a threshold of (> 4) versus (>7).

The number of logic cells could be reduced further (a lot further for the fraction dabble) by making the BCD value a clocked register and iterating 20 or 18 operations clock at a time. There would be a requirement for a state machine to handshake with inputs and outputs as well as iterate for the number of clocks representing the length of the binary input values.

The absolute value conversion is less than 40 logic cells.

The idea here is that using sequential operations bit at a time represents and performance vs. size trade off (noting you haven't specified the size of your present implementation using sfixed division and modulo reduction).

The following code implements the two dabbles as distinct functions which could be scaled for larger or smaller sfixed values and number of BCD digits and potentially be preceded with multiplexers as noted by Brian Drummond although it seems unlikely based on their sizes it's warranted. (How big are the multiplexers?).

The implication of 'slower' performance is that you don't update the input values (shown as cp here) faster than the fall through time of the absolute value conversion concatenated with the longest time of the the two dabbles (the bigger one).

This code:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.fixed_pkg.all;                    -- included in -2008

entity iopertyki is
end entity;

architecture fum of iopertyki is
    function doubledabble_20bit (inp:  std_logic_vector(19 downto 0)) 
            return unsigned is
        variable bin:   std_logic_vector(inp'range);
        variable bcd:   unsigned(27 downto 0);
    begin
        bin := inp;
        bcd := (others => '0');
        for i in 0 to 19 loop
            if bcd(3 downto 0) > 4 then
                bcd(3 downto 0) := bcd(3 downto 0) + 3;
            end if;
            if bcd(7 downto 4) > 4 then
                bcd(7 downto 4) := bcd(7 downto 4) + 3;
                end if;
            if bcd(11 downto 8) > 4 then
                bcd(11 downto 8) := bcd(11 downto 8) + 3;
            end if;
            if bcd(15 downto 12) > 4 then
                bcd(15 downto 12) := bcd(15 downto 12) + 3;
            end if;
            if bcd(19 downto 16) > 4 then
                bcd(19 downto 16) := bcd(19 downto 16) + 3;
            end if;
            if bcd(23 downto 20) > 4 then
                bcd(23 downto 20) := bcd(23 downto 20) + 3;
            end if;
            bcd := bcd(26 downto 0) & bin(19);
            bin := bin(18 downto 0) & '0';
        end loop;
        return bcd;
    end function;

    function fracdabble_19bit (inp:  std_logic_vector(18 downto 0)) 
            return unsigned is
        variable bin:   std_logic_vector(inp'range);
        variable bcd:   unsigned(23 downto 0);
        -- variable tbcd:  unsigned(23 downto 0); -- DEBUG
    begin
        bin := inp;
        bcd := (others => '0');                -- DEBUG
        for i in 0 to 18 loop

            bcd := bin(0) & bcd(23 downto 1);
            bin := '0' & bin(18 downto 1);
            -- tbcd := bcd;                       -- DEBUG

            if bcd(3 downto 0) > 7 then
                bcd(3 downto 0) := bcd(3 downto 0) - 3;
            end if;
            if bcd(7 downto 4) > 7 then
                bcd(7 downto 4) := bcd(7 downto 4) - 3;
                end if;
            if bcd(11 downto 8) > 7 then
                bcd(11 downto 8) := bcd(11 downto 8) - 3;
            end if;
            if bcd(15 downto 12) > 7 then
                bcd(15 downto 12) := bcd(15 downto 12) - 3;
            end if;
            if bcd(19 downto 16) > 7 then
                bcd(19 downto 16) := bcd(19 downto 16) - 3;
            end if;
            if bcd(23 downto 20) > 7 then
                bcd(23 downto 20) := bcd(23 downto 20) - 3;
            end if;
            -- report "i = " & integer'image(i) & LF & HT &  -- DEBUG
            --     "prior sub3 bcd = " & to_string (tbcd) & LF & HT & -- DEBUG
            --     "after sub3 bcd = " & to_string (bcd); -- DEBUG
        end loop;
        bcd(0) := inp(0);  -- An odd binary produces an odd decimal value
        return bcd;        -- the algorithm loses the LSB
    end function;

    signal cp: sfixed(20 downto -19) := to_sfixed(174334.738295,20,-19);
begin
BCD_CONVERT:
    process (cp)
        variable int_digits:    unsigned(27 downto 0);
        variable frac_digits:   unsigned(23 downto 0);

        alias L6:   unsigned(3 downto 0) is int_digits(27 downto 24);
        alias L5:   unsigned(3 downto 0) is int_digits(23 downto 20);
        alias L4:   unsigned(3 downto 0) is int_digits(19 downto 16);
        alias L3:   unsigned(3 downto 0) is int_digits(15 downto 12);
        alias L2:   unsigned(3 downto 0) is int_digits(11 downto 8);
        alias L1:   unsigned(3 downto 0) is int_digits(7 downto 4);
        alias L0:   unsigned(3 downto 0) is int_digits(3 downto 0);
        alias R5:   unsigned(3 downto 0) is frac_digits(23 downto 20);
        alias R4:   unsigned(3 downto 0) is frac_digits(19 downto 16);
        alias R3:   unsigned(3 downto 0) is frac_digits(15 downto 12);
        alias R2:   unsigned(3 downto 0) is frac_digits(11 downto 8);
        alias R1:   unsigned(3 downto 0) is frac_digits(7 downto 4);
        alias R0:   unsigned(3 downto 0) is frac_digits(3 downto 0);
        variable scp:           sfixed(20 downto -19);
        variable sign:          character;
    begin
        if cp < 0.0 then
            scp := "-"(0.0, cp)(20 downto -19);  -- change sign, slice length
            sign := '-';
        else
            scp := cp;
            sign := ' ';  -- no sign instead of '+'
        end if;
        report LF & HT & "             cp = " & to_string(cp) &
               LF & HT & "absolute val cp = " & to_string(scp);
        report LF & HT & "slv int = " & to_string(to_slv(scp)(38 downto 19))
               & " slv frac = " & to_string(to_slv(scp)(18 downto 0));
        -- leave off sign bit:
        int_digits := doubledabble_20bit(to_slv(scp)(38 downto 19));
        report "int_digits = " & to_string (int_digits);
        -- 55 logic cells following mspping and reduction:
        frac_digits := fracdabble_19bit(to_slv(scp)(18 downto 0)); 
        report "frac_digits = " & to_string (frac_digits);
        -- R6 = "0000"

        report "result   = " & sign &
                integer'image(to_integer(L6)) &
                integer'image(to_integer(L5)) &
                integer'image(to_integer(L4)) &
                integer'image(to_integer(L3)) &
                integer'image(to_integer(L2)) &
                integer'image(to_integer(L1)) &
                integer'image(to_integer(L0)) &
                '.' &
                integer'image(to_integer(R5)) &
                integer'image(to_integer(R4)) &
                integer'image(to_integer(R3)) &
                integer'image(to_integer(R2)) &
                integer'image(to_integer(R1)) &
                integer'image(to_integer(R0));
    end process;

MORE:
    process
    begin
        wait for 20 ns;
        cp <= "-"(cp)(20 downto -19);  -- change sign, slice length
        wait for 20 ns;
        cp <= to_sfixed(-307.83929,20,-19);
        wait;
    end process;
end architecture;

when analyzed, elaborated and simulated produces:

ghdl -a --std=08 iopertyki.vhdl
ghdl -e --std=08 iopertyki
ghdl -r --std=08 iopertyki
iopertyki.vhdl:112:9:@0ms:(report note):
               cp = 000101010100011111110.1011110100000000111
  absolute val cp = 000101010100011111110.1011110100000000111
iopertyki.vhdl:114:9:@0ms:(report note):
  slv int = 00101010100011111110 slv frac = 1011110100000000111
iopertyki.vhdl:118:9:@0ms:(report note): int_digits = 0000000101110100001100110100
iopertyki.vhdl:121:9:@0ms:(report note): frac_digits = 011100111000001010010101
iopertyki.vhdl:124:9:@0ms:(report note): result   =  0174334.738295
iopertyki.vhdl:112:9:@20ns:(report note):
               cp = 111010101011100000001.0100001011111111001
  absolute val cp = 000101010100011111110.1011110100000000111
iopertyki.vhdl:114:9:@20ns:(report note):
  slv int = 00101010100011111110 slv frac = 1011110100000000111
iopertyki.vhdl:118:9:@20ns:(report note): int_digits = 0000000101110100001100110100
iopertyki.vhdl:121:9:@20ns:(report note): frac_digits = 011100111000001010010101
iopertyki.vhdl:124:9:@20ns:(report note): result   = -0174334.738295
iopertyki.vhdl:112:9:@40ns:(report note):
               cp = 111111111111011001100.0010100100100100010
  absolute val cp = 000000000000100110011.1101011011011011110
iopertyki.vhdl:114:9:@40ns:(report note):
  slv int = 00000000000100110011 slv frac = 1101011011011011110
iopertyki.vhdl:118:9:@40ns:(report note): int_digits = 0000000000000000001100000111
iopertyki.vhdl:121:9:@40ns:(report note): frac_digits = 100000111001001010010000
iopertyki.vhdl:124:9:@40ns:(report note): result   = -0000307.839290

Demonstrating proper conversion.

Without knowing anything about your LCD display it didn't seem practical to attempt to map the BCD digits to it.

These transformations from sfixed values to BCD digits represent thinking in hardware versus thinking in terms of machine instructions. If the need to reduce space isn't satisfied by the dabbles, using clocked shift registers can decrease the size further, reducing the requirement for add3 or sub3 cells trading off with a small state machine. That can work because of optical persistence both in how eyes and LCD displays work. You simply can't see things that don't persist for some small number of milliseconds.


Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!