如何在 VHDL 中使用“函数”从同一计算中返回多个变量?

Posted

技术标签:

【中文标题】如何在 VHDL 中使用“函数”从同一计算中返回多个变量?【英文标题】:How to use "function" in VHDL to return multiple variables from the same calculation? 【发布时间】:2017-09-07 09:20:43 【问题描述】:

我正在尝试在函数中实现解码代码以返回多个变量,因为我必须解码两个定点变量,并且由于 fpga 中的逻辑不足而出现错误。所以我需要像我们在 c 语言中那样执行解码代码-> 用两个变量调用函数两次并以单个数字返回。这可以执行吗?如果有可能怎么办?还是有其他方法可以做到这一点?

我对两个定点数执行下面的代码两次 并对小数部分进行解码。解码对于整数部分和小数部分效果很好。 解码后,我将它们转换为 std_logic_vector 并发送到我的液晶显示器。这就是我解码价值的原因。代码大约 1000 行。 所以我不能把它放在这里。

在当前代码中,我执行了两次相同的解码,因此它产生了 fpga 不支持的大逻辑。他们单独工作。所以我需要把它一次放入一个函数并调用该函数两次。

VDHL sfixed decoding code does not work properly

如链接代码所示,我得到 6 个整数部分的变量和 6 个小数部分的变量。我需要将此代码放在一个返回 12 个变量的函数中。

【问题讨论】:

为什么不包含一些代码来演示您的问题,以便我们可以清楚地了解您要做什么。 @scary_jeff 我已经添加了包含我正在关注的部分代码的链接 将有符号的值转换为有符号的幅度,保存符号。整数和小数部分可以看作是两个独立的二进制值。使用 double dabble 创建二进制编码十进制 (BCD) 数字。对显示器中的每个数字使用 BCD 数字,并在正确的位置显示小数点。据我所知,您从未演示过展示方法,也没有描述过转化率或目的。 “所以我需要像我们在 c 语言中那样执行解码代码......”你为什么认为这是解决方案?这不是逻辑综合的工作方式。您不是在对处理器进行编程,而是在描述可配置的逻辑。如果您的代码不合适,您要么需要更大的 FPGA,要么需要简化算法。 函数调用对此无济于事:如果您两次调用相同的函数(例如,在时钟进程中的循环中),您将获得硬件的两个实例化,就像您一样放入两个组件。 (本质上,在综合中,循环是完全展开的)。这意味着您仍然会使用过多的资源。您需要对单个组件或功能进行顺序访问,这两种方法都可以使用,这通常需要状态机来确定组件在任何时候正在执行的任务。这并不是那么困难,但@JHBonarius 说这是一种不同的心态。 【参考方案1】:

在引用的问题VDHL sfixed decoding code does not work properly Renaud Pacalet 在他的回答Your divisions should be integer divisions; they are not (fixed point divisions). 中评论说,没有特别的理由在实际转换过程中使用有符号的定点数,也没有整数。

有一种算法 (Double dabble) 使用移位和 4 位条件加法,可以在循环语句中实现。这里的一个是对 20 位整数部分的算法的扩展。

还有一种实现小数部分等价的方法:

[多位二进制到十进制转换器(也可以是小数!)](http://www.minecraftforum.net/forums/minecraft-discussion/redstone-discussion-and/redstone-creations/352668-multi-digit-binary-to-decimal-converter-decimal"多位二进制到十进制转换器(也可以是小数!)")

算法工作原理的解释可以在这个网站上找到:http://www.johnloomis.org/ece314/notes/devices/binary_to_BCD/bin_to_bcd.html

不过,我更进一步,想出了如何应用相同的原理将二进制分数转换为十进制分数。例如,0.11(bin) 可以转换为 0.75(dec)。通过使用执行反函数的转换表(如果为 8 或更多,则为 -3)并沿相反方向移动。

Hans_Lemurson 最后编辑:2012 年 6 月 9 日

虽然没有在 Minecraft 游戏中实现,但分数 dabble 涉及每个比较和减法的逻辑单元,就像 double dabble 用于加法一样。

位是串行移位的,double dabble 是一个乘法器,fraction dabble 是一个除法器。 double dabble 的 add3 逻辑单元的最大数量为 20 位 x 7 BCD 位(28 位)或 140 个逻辑单元。由于不满足阈值 (> 4) 或保证输入全部为“0”,无法更改结果的单元格数量会减少它。最终有 57 个逻辑单元(每个 4 个 4x1 LUT)。

fraction dabble 使用更多单元格,因为从左侧移动会立即抛出 sub3 操作的阈值。 18 位 x 6 BCD 数字或 108 个 sub3 单元的合成映射后的缩减 只输 6 个,得到 102 个逻辑单元。 add3 和 sub3 之间的差异是模 4 加法与减法以及 (> 4) 与 (>7) 的阈值。

通过将 BCD 值设置为时钟寄存器并一次迭代 20 或 18 个操作时钟,可以进一步减少逻辑单元的数量(对于小数部分来说可以进一步减少)。状态机需要与输入和输出握手,并迭代表示二进制输入值长度的时钟数。

绝对值转换少于 40 个逻辑单元。

这里的想法是,一次使用一个位的顺序操作表示性能与大小之间的权衡(注意您没有使用 sfixed 除法和模减少指定当前实现的大小)。

以下代码将这两个 dabbles 实现为不同的函数,可以针对更大或更小的 sfixed 值和 BCD 位数进行缩放,并可能在多路复用器之前使用,正如 Brian Drummond 所指出的那样,尽管根据它们的大小似乎不太可能是有保证的。 (多路复用器有多大?)。

“较慢”性能的含义是您更新输入值(此处显示为cp)的速度不快于绝对值转换的下降时间与两个涉猎的最长时间(较大的一个)。

这段代码:

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;

当分析、阐述和模拟产生时:

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

演示正确的转换。

在对您的 LCD 显示器一无所知的情况下,尝试将 BCD 数字映射到它似乎是不切实际的。

这些从固定值到 BCD 数字的转换代表了硬件思维与机器指令思维。如果涉猎者不能满足减少空间的需要,使用时钟移位寄存器可以进一步减小大小,减少对 add3 或 sub3 单元的要求,并与小型状态机进行权衡。这可以工作,因为眼睛和 LCD 显示器的工作方式都有光学持久性。你根本看不到那些不会持续几毫秒的东西。

【讨论】:

非常努力。希望这能解决提问者的问题。

以上是关于如何在 VHDL 中使用“函数”从同一计算中返回多个变量?的主要内容,如果未能解决你的问题,请参考以下文章

Excel如何进行多条件多维度计算?

同一个函数通过设计模式在javascript中返回多个对象、数组或函数的值? [复制]

VHDL textio,从文件中读取图像

Excel中,如何多条件判断,输出结果

VHDL组件多路复用器不在modelsim中返回值

excel函数:计算是不是满足计算值,满足直接计算,如不满足则返回一个固定值?