VHDL补充计数器问题:将std_logic转换为整数

时间:2011-07-14 23:15:23

标签: integer logic counter vhdl twos-complement

基本上,我的问题是:“这样做不容易吗?”;这个是什么,如下(代码也):

我希望有一种'补码'计数器功能,用VHDL实现,它基本上会反转/补充/不是每一步中的计数器值,为测试提供稍微丰富的位模式。当然,我希望这是可合成的(因此计数器值可能分配给引脚)和可移植代码(即仅实现IEEE库,没有STD_LOGIC_ARITH)。我也不希望默认情况下将所有内容视为无符号(因此我想避免使用STD_LOGIC_UNSIGNED)。

简而言之,这个计数器可以描述为:给定初始值C [0],那么每个时钟滴答的值将是:

C[i+1] = not(C[i]) + ( ( C[i]<(Cmax/2) ) ? 0 : 1 )

...或者给定C是16位宽(这将导致无符号Cmax = 65535和Cmax / 2 = 32768),它也可以写成:

C[i+1] = 65535 - C[i] + ( ( C[i]<32768 ) ? 0 : 1 )

这里的技巧是计数器只应增加一次 - 如果它对互补和“正常”范围都增加,那么就不会发生变化(方程式将在两个值之间“振荡”)。

因此,考虑到检查C [i]&lt;(Cmax / 2)与检查C的最重要(第15位)基本相同,我认为我可以使用某些东西轻松地在VHDL中实现这样的东西像:

Y <= not(Y) + Y(15);

男孩,我错了“容易”:)

第一个问题是上述等式有可能最终达到65535 + 1,在这种情况下结果将需要17位(即溢出);就我而言,我只想截断/忽略任何'进位'。

这导致了使用内容的问题:

  • std_logic_vector已定义补充not();但它没有+(添加)定义的
  • natural / integer可能内部占用32位,因此它们的位宽不是指定的;它们支持算术+,但不支持not()
  • 我也试过了unsigned,也遇到了一些问题(不记得是哪个)

当Y为std_logic_vector时,只能提取第15个(MSB)位,在这种情况下,Y(15)是单个std_logic - 但是,它需要转换为{{ 1}}类型,因为否则未定义加法integer:|

所以,我目前的解决方案(下面)首先有两个计数器寄存器副本;一个是+;另一个是SIGNAL wCntReg : STD_LOGIC_VECTOR(15 DOWNTO 0)。然后:

  • 有两个时钟:一个'主'@ 50 MHz,另一个是'计数器'时钟:主16倍频分(3.125 MHz)。
  • '计数器'时钟应激活下降沿计数器值的计算
  • 计算是通过SIGNAL tmp_na : natural变量执行的(从natural一个副本复制)
  • 显然,STD_LOGIC_VECTOR只有先转换为std_logic才能转换为integer(我很幸运能在网上找到std_logic_vector功能)。

这里最糟糕的部分是如何将vectorize变量值反馈回natural变量值;我能构建的唯一工作命令是:

STD_LOGIC_VECTOR

...;但请注意,此命令基本上“设置”该值,该值将在此命令运行时“实现” next 时间。因此,它无法在“计数器”时钟进程中运行 - 在下面的代码中,我在更快的'主'时钟进程中使用它。

最后,下面的代码确实有效(通过ISE WebPack中的行为模拟) - 但是,我仍然想知道是否有更简单的方法来解决这个问题。

提前感谢您的回答, 干杯!

代码:

wCntReg <= std_logic_vector(to_unsigned(natural'pos(tmp_na), wCntReg'length));

1 个答案:

答案 0 :(得分:6)

首先,avoiding not use std_logic_arith的满分!

如果您将向量定义为unsigned,那么(在流程之外)所需的只是:

  cn_plus_1 <= not cn when cn < halfc else (not cn) + 1;

您可以将cn_plus_1分配给std_logic_vector,因此:

wCntReg <= std_logic_vector(cn_plus_1);

以下是VHDL惯用方法的几个完整示例:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity funny_counter1 is
    port (
        cn: IN unsigned(15 downto 0);
        cn_plus_1 : out unsigned(15 downto 0));
end entity funny_counter1;

architecture a1 of funny_counter1 is
    constant halfc : unsigned(cn'range) := (cn'high => '1', others => '0');
begin  -- architecture a1
    cn_plus_1 <= not cn when cn < halfc else (not cn) + 1;
end architecture a1;

或在同步过程中:

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

entity funny_counter is
    port (
        clk   : in  std_logic;
        reset : in  std_logic;
        cout  : out unsigned(15 downto 0));
end entity funny_counter;

architecture a1 of funny_counter is
    constant halfc : unsigned(cout'range) := (cout'high => '1', others => '0');
begin
    process (clk) is
        variable c   : unsigned(15 downto 0);
        variable add : integer range 0 to 1;
    begin  -- process
        if rising_edge(clk) then  -- rising clock edge
            if reset = '1' then
                c := (others => '0');
            else
                add := 0;
                if c < halfc then
                    add := 1;
                end if;
                c := (not c) + add;
            end if;
            cout <= c;
        end if;
    end process;
end architecture a1;

每当您发现自己在std_logic_vectorinteger(或类似)之间进行大量转换时,通常会使用实际数字。使用unsigned / signed向量或整数。如果你需要一个向量,那么最后将它转换成一个对象。看起来一定不愉快。但首先要问的是,您发送应该的值是否在其接口上使用某种数字类型。只有当它真的是一个“袋子”时才是std_logic_vector最好的类型。名称wCntReg对我来说听起来像是一个计数值,所以它应该有一个数字类型。

您拥有以下代码:

wCntReg <= std_logic_vector(to_unsigned(natural'pos(tmp_na), wCntReg'length));

比它需要的稍差:

wCntReg <= std_logic_vector(to_unsigned(tmp_na, wCntReg'length));

应该工作正常。

最后,你对这个唯一的进入到下一个时钟滴答声效果的评论是VHDL如何工作 - 当经过一段时间信号只更新(通常这只是在过程结束)。如果您想在此之前使用新值,请使用变量 - 它们会立即更新。

因此,如果您的tempna是变量,那么可以直接执行wCntReg分配。

为了完整性:在信号分配之后,另一种方法(通常是kludge IME)是wait for 0 ns;。这将导致一段时间才能从视图更新的角度来讲,通过使所有其他增量周期将当前时间执行(包括要propgate信号分配)的时间将继续前进(通过为0ns!)和一个新的增量周期可开始。