Проблемы со счетчиком дополнения VHDL: преобразование std_logic в целое число - PullRequest
4 голосов
/ 15 июля 2011

По сути, мой вопрос таков: " Разве это не легче сделать? "; и что «это», следует ниже (код тоже):

Я хотел иметь своего рода функцию счетчика «дополнения», реализованную в 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] <(Cmax / 2) в основном такая же, как проверка самого старшего (15-го) бита C, я подумал, что мог бы легко реализовать что-то подобное в VHDL, используя что-то вроде : </p>

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

Мальчик, я был неправ насчет "легко":)

Первая проблема заключается в том, что вышеприведенное уравнение может оказаться в 65535 + 1, и в этом случае результат будет нуждаться в 17 битах (т.е. переполнение); в моем случае я просто хотел бы обрезать / игнорировать любые «биты переноса».

Это приводит к проблеме того, что использовать:

  • std_logic_vector имеет дополнение not() определено; но + (сложение) не определено
  • natural / integer может внутренне занимать 32 бита, и поэтому ширина битов для них не обязательно указывается; они поддерживают арифметику +, но без дополнения not()
  • Я тоже пытался unsigned, тоже были некоторые проблемы (не помню какие)

15-й (MSB) бит может быть извлечен, только если Y равен std_logic_vector, и в этом случае Y (15) представляет собой одиночный std_logic - но затем его необходимо преобразовать в тип integer, поскольку в противном случае дополнение + не определено: |

Итак, мое текущее решение (ниже) сначала имеет две копии счетчика; один SIGNAL wCntReg : STD_LOGIC_VECTOR(15 DOWNTO 0); другой - SIGNAL tmp_na : natural. Тогда:

  • Существует два тактовых сигнала: один «ведущий» @ 50 МГц, другой - «счетчик»: 16-кратная частота основного делителя (3.125 МГц).
  • Часы «счетчика» должны активировать расчет значений счетчика по падающему фронту
  • Расчет выполняется с помощью переменной natural (которая копирует из STD_LOGIC_VECTOR one)
  • Очевидно, std_logic можно преобразовать в integer, только если сначала он был преобразован в std_logic_vector (мне повезло найти функцию vectorize в сети).

Самая неприятная часть здесь заключалась в том, как вернуть значение переменной natural обратно в STD_LOGIC_VECTOR; единственная рабочая команда, которую я мог построить, это:

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

...; однако обратите внимание, что эта команда в основном «устанавливает» значение, которое будет «выполнено» в следующий раз, когда эта же команда будет выполнена. Таким образом, он не может работать в тактовом процессе «счетчик» - в приведенном ниже коде он используется в более быстром «тактовом» процессе синхронизации.

Наконец, приведенный ниже код работает (проходит моделирование поведения в ISE WebPack) - но я все же хотел бы знать, есть ли более простой способ решить эту проблему.

Заранее спасибо за любые ответы, Ура!

код:

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

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 -----------------------------

1 Ответ

6 голосов
/ 15 июля 2011

Во-первых, полные оценки для избегая не использовать 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_vector с и integer с (или аналогичными), вы обычно работаете с реальными числами.Работайте с векторами 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сразу после.

Для полноты: другой способ обойти это (который обычно является IME Kludge) к wait for 0 ns; после назначения сигнала.Это приводит к тому, что с точки зрения обновлений проходит некоторое время, поэтому все остальные дельта-циклы будут выполняться в течение текущего времени (включая присвоение сигнала, которое вы хотите распространить), время будет двигаться (на 0 нс!), И новый дельта-цикл можетначать.

...