简体   繁体   English

VHDL 补码计数器问题:将 std_logic 转换为 integer

[英]VHDL complement counter issues: converting std_logic to integer

Essentially, my question is: " can this not be done any easier? ";本质上,我的问题是:“这不是更容易吗? ”; and what 'this' is, follows below (code too):什么是'this',如下(代码也是):

I wanted to have a sort of a 'complement' counter function, implemented in VHDL, which would basically invert/complement/not the counter value in each step, giving slightly richer bit patterns for testing.我想要一种用 VHDL 实现的“补码”计数器 function,它基本上会在每一步中反转/补码/不反转计数器值,从而为测试提供稍微丰富的位模式。 Of course, I wanted this as synthesizable (so the counter value could be possibly assigned to pins) and portable code (ie implemented only IEEE libraries, no STD_LOGIC_ARITH ).当然,我希望它是可综合的(因此计数器值可以分配给引脚)和可移植代码(即仅实现 IEEE 库,没有STD_LOGIC_ARITH )。 I also don't want to by default treat all as unsigned (so I'd like to avoid STD_LOGIC_UNSIGNED ).我也不想默认将所有内容都视为无符号(所以我想避免STD_LOGIC_UNSIGNED )。

In brief, this counter could be described as: given initial value C[0], then values at each clock tick would be:简而言之,这个计数器可以描述为:给定初始值 C[0],那么每个时钟滴答的值将是:

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

... or given C is 16-bit wide (which will result with unsigned Cmax = 65535 and Cmax/2 = 32768), it could also be written as: ...或给定 C 是 16 位宽(这将导致无符号 Cmax = 65535 和 Cmax/2 = 32768),它也可以写成:

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

The trick here is that the counter should only increment once - if it increases for both complementary and 'normal' ranges, then no changes will happen (equation will 'oscillate' between two values).这里的诀窍是计数器应该只增加一次 - 如果它在互补和“正常”范围内都增加,那么不会发生任何变化(方程将在两个值之间“振荡”)。

So, given that the check C[i]<(Cmax/2) is basically the same as checking the most significant (15th) bit of C, I thought that I could easily implement something like this in VHDL using something like:因此,鉴于检查 C[i]<(Cmax/2) 与检查 C 的最高有效位(第 15 位)基本相同,我认为我可以使用以下方式在 VHDL 中轻松实现类似的东西:

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

Boy, was I wrong about "easily":)男孩,我对“容易”有误吗:)

First problem is that it is possible the above equation could end up at 65535+1, in which case the result will need 17 bits (ie overflow);第一个问题是,上面的等式有可能以 65535+1 结尾,在这种情况下,结果需要 17 位(即溢出); in my case, I'd simply want to truncate/ignore any 'carry bits'.就我而言,我只想截断/忽略任何“进位”。

This leads into the problem of what to use:这导致了使用什么的问题:

  • std_logic_vector has complement not() defined; std_logic_vector定义了补码not() but it doesn't have + (addition) defined但它没有定义+ (加法)
  • natural / integer may internally take up 32 bits, and as such bit width for them is not necesarilly specified; natural / integer可能在内部占用 32 位,因此不必指定它们的位宽; they support arithmetic + , but no complement not()他们支持算术+ ,但没有补码not()
  • I tried unsigned too, had some problems also (cannot remember which)我也试过unsigned ,也有一些问题(不记得是哪个)

The 15th (MSB) bit can only be extracted when Y is std_logic_vector , in which case, Y(15) is a single std_logic - but then, it needs to be converted to integer type, because otherwise addition + is not defined:|第 15(MSB)位只能在 Y 为std_logic_vector时提取,在这种情况下,Y(15) 是单个std_logic - 但随后需要将其转换为integer类型,因为否则未定义加法+ :|

So, my current solution (below) first has two copies of the counter register;所以,我目前的解决方案(如下)首先有两个计数器寄存器的副本; one is SIGNAL wCntReg: STD_LOGIC_VECTOR(15 DOWNTO 0) ;一个是SIGNAL wCntReg: STD_LOGIC_VECTOR(15 DOWNTO 0) the other is SIGNAL tmp_na: natural .另一个是SIGNAL tmp_na: natural Then:然后:

  • There are two clocks: one 'master' @ 50 MHz, the other is the 'counter' clock: the master 16-times frequency divided (3.125 MHz).有两个时钟:一个“主”@ 50 MHz,另一个是“计数器”时钟:主时钟 16 倍分频(3.125 MHz)。
  • The 'counter' clock should activate calculation of counter values on falling edge “计数器”时钟应在下降沿激活计数器值的计算
  • The calculation is performed via natural variable (which copies from the STD_LOGIC_VECTOR one)计算通过natural变量执行(从STD_LOGIC_VECTOR复制)
  • Apparently, std_logic can only be converted to integer if its converted to std_logic_vector first (I was lucky to find the vectorize function on the net).显然, std_logic只能转换为integer ,如果它先转换为std_logic_vector (我很幸运在网上找到了vectorize的function)。

The nastiest part here was how to feed back the natural variable value, back to the STD_LOGIC_VECTOR one;这里最讨厌的部分是如何将natural变量值反馈给STD_LOGIC_VECTOR的; the only working command I could construct is:我可以构建的唯一有效命令是:

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

...; ...; however, note that this command basically 'sets' the value, which will be 'effectuated' next time this same command runs.但是,请注意,此命令基本上是“设置”该值,下次运行同一命令时,该值将“生效”。 Thus, it cannot run in the 'counter' clock process - in the code below, I have it in the faster 'master' clock process.因此,它不能在“计数器”时钟进程中运行——在下面的代码中,我将它放在更快的“主”时钟进程中。

Finally, the code below does work (goes through behavioral simulation in ISE WebPack) - but, I'd still like to know if there is a more straightforward way to solve this.最后,下面的代码确实有效(通过 ISE WebPack 中的行为模拟) - 但是,我仍然想知道是否有更直接的方法来解决这个问题。

Thanks in advance for any answers, Cheers!提前感谢您的任何答案,干杯!

The code:编码:

----------------------------------------------------------------------------------

library IEEE;
  use IEEE.STD_LOGIC_1164.ALL;
  -- use IEEE.STD_LOGIC_ARITH.ALL;
  -- use IEEE.STD_LOGIC_UNSIGNED.ALL;
  use IEEE.NUMERIC_STD.ALL;


ENTITY complement_count_test_tbw IS
END complement_count_test_tbw;

ARCHITECTURE testbench_arch OF complement_count_test_tbw IS

  -- http://www.ingenieurbuero-eschemann.de/downloads/ipicregs/example/vhdl/test/timer_regs_tb.vhd
  -- convert std_logic to std_logic_vector(0 downto 0)
  function vectorize(s: std_logic) return std_logic_vector is
  variable v: std_logic_vector(0 downto 0);
  begin
      v(0) := s;
      return v;
  end;


  -- DECLARE REGISTERS ==========================

  -- 'wires'
  SIGNAL wtCLK : std_logic := '0';

  -- counter register: 16 bit
  SIGNAL wCntReg : STD_LOGIC_VECTOR(15 DOWNTO 0) := (others => 'Z');

  -- temporary 'natural' copy of counter register
  -- http://www.velocityreviews.com/forums/t21700-std_logic_vector-to-unsigned-type-casting.html
  SIGNAL tmp_na : natural;


  -- clock parameters
  constant PERIODN : natural := 20; -- can be real := 20.0;
  constant PERIOD : time := PERIODN * 1 ns;
  constant DUTY_CYCLE : real := 0.5;
  constant OFFSET : time := 100 ns;

  -- freq divisor; with initial values
  constant fdiv : natural := 16;
  SIGNAL fdiv_cnt : natural := 1;
  SIGNAL wfdiv_CLK : std_logic := '0';

BEGIN

  -- initializations of connections:

  -- instances of components, and their wiring (port maps)...
  -- END instances of components, and their wiring (port maps)...


  -- PROCESSES (STATE MACHINES) CODE =========

  -- clock process for generating CLK
  PROCESS
  BEGIN

    WAIT for OFFSET;

    CLOCK_LOOP : LOOP
      wtCLK <= '0';
      -- MUST refresh counter reg here with value of tmp_na
      wCntReg <= std_logic_vector(to_unsigned(natural'pos(tmp_na), wCntReg'length));
      WAIT FOR (PERIOD - (PERIOD * DUTY_CYCLE));
      wtCLK <= '1';
      WAIT FOR (PERIOD * DUTY_CYCLE);
    END LOOP CLOCK_LOOP;
  END PROCESS;

  -- freq divided clock
  freq_divisor: PROCESS(wtCLK)
  BEGIN
    IF rising_edge(wtCLK) THEN -- posedge
      IF fdiv_cnt = fdiv THEN
        -- reset
        fdiv_cnt <= 1 ;
        wfdiv_CLK <= not(wfdiv_CLK);
      ELSE
        fdiv_cnt <= fdiv_cnt + 1;
      END IF;
    END IF;
  END PROCESS freq_divisor;



  -- sim: count
  PROCESS
  BEGIN

    WAIT for 10 ns;

    tmp_na <= 125;

    WAIT for 10 ns;


    TESTCOUNT_LOOP: LOOP

      -- change counter on negedge of freq. divided clock
      WAIT until falling_edge(wfdiv_CLK);

      tmp_na <= to_integer(unsigned(not(wCntReg))) + to_integer(unsigned(vectorize(wCntReg(15))));

      WAIT for 10 ns;

    END LOOP TESTCOUNT_LOOP;

  END PROCESS;

  -- END PROCESSES (STATE MACHINES) CODE =====

-- END IMPLEMENT ENGINE of 'CORE' ===============
END testbench_arch;
-- END ARCHITECTURE -----------------------------

Firstly, full marks for avoiding not use std_logic_arith !首先,避免不使用 std_logic_arith的满分!

If you define your vectors as unsigned then (outside of a process) all that is required is this:如果您将向量定义为unsigned ,那么(在进程之外)所需要的就是:

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

and you can assign cn_plus_1 to a std_logic_vector thus:您可以将cn_plus_1分配给std_logic_vector因此:

wCntReg <= std_logic_vector(cn_plus_1);

Here's a couple of complete examples of VHDL idiomatic ways of doing it:以下是 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;

or in a synchronous process:或在同步过程中:

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;

Whenever you find yourself converting a lot between std_logic_vector s and integer s (or similar), you are usually working with actual numbers.每当您发现自己在std_logic_vectorinteger (或类似的)之间进行大量转换时,您通常会使用实际数字。 Work with either unsigned / signed vectors or with integers.使用unsigned /有signed向量或整数。 If you need a vector at the end of it all, convert it one and for all at the end.如果您需要一个向量,请在最后将其一劳永逸地转换。 It's not pleasant to look at necessarily.看着不一定是令人愉快的。 But first ask if the thing you are sending the value to ought to be using a numerical type of some sort on its interface.但首先要询问您发送值的对象是否应该在其接口上使用某种数字类型。 Only if it truly is a 'bag-of-bits' is a std_logic_vector the best type.只有当它真的是一个“比特袋”时, std_logic_vector才是最好的类型。 The name wCntReg sound like a count value to me, so it should have a numeric type to my mind. wCntReg这个名字对我来说听起来像是一个计数值,所以在我看来它应该是一个数字类型。

This code you have:你有这个代码:

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

is slightly worse than it needs to be:比它需要的稍差:

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

ought to work fine.应该可以正常工作。

Finally, you comment on this only coming into effect on the next clock tick is how VHDL works - signals are only updated when some time passes (often this is only at the end of a process).最后,您评论这仅在下一个时钟滴答时生效是 VHDL 的工作方式 - 仅在经过一段时间后才更新信号(通常仅在进程结束时)。 If you want to make use of the new value before then, use a variable - they update straight away.如果您想在此之前使用新值,请使用变量 - 它们会立即更新。

So if your tempna were a variable, you could do the wCntReg assignment straight after.因此,如果您的tempna是一个变量,您可以在之后立即进行 wCntReg 赋值。

For completeness: another way around this (which is usually a kludge IME) is to wait for 0 ns;为了完整性:解决此问题的另一种方法(通常是 kludge IME)是wait for 0 ns; after the signal assignment.信号分配后。 This causes some time to pass from the point of view of updates, so all other delta cycles will execute for the current time (including the signal assignment you want to propgate) time will move on (by 0ns.) and a new delta cycle can begin.从更新的角度来看,这会导致一些时间过去,因此所有其他增量周期将在当前时间执行(包括您要传播的信号分配)时间将继续(0ns)并且新的增量周期可以开始。

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

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