Поиск…


замечания

VHDL является составной аббревиатурой для VHSIC (высокоскоростная интегральная схема) HDL (язык описания оборудования). В качестве языка описания аппаратных средств он в основном используется для описания или моделирования схем. VHDL - идеальный язык для описания схем, поскольку он предлагает языковые конструкции, которые легко описывают как параллельное, так и последовательное поведение, а также модель исполнения, которая устраняет неоднозначность, возникающую при моделировании параллельного поведения.

VHDL обычно интерпретируется в двух разных контекстах: для моделирования и синтеза. При интерпретации для синтеза код преобразуется (синтезируется) в эквивалентные аппаратные элементы, которые моделируются. Только подмножество VHDL обычно доступно для использования во время синтеза, а поддерживаемые языковые конструкции не стандартизированы; это функция используемого механизма синтеза и целевого аппаратного устройства. Когда VHDL интерпретируется для моделирования, все языковые конструкции доступны для моделирования поведения аппаратного обеспечения.

Версии

Версия Дата выхода
IEEE 1076-1987 1988-03-31
IEEE 1076-1993 1994-06-06
IEEE 1076-2000 2000-01-30
IEEE 1076-2002 2002-05-17
IEEE 1076c-2007 2007-09-05
IEEE 1076-2008 2009-01-26

Установка или настройка

Программа VHDL может быть смоделирована или синтезирована. Моделирование - это то, что больше всего похоже на исполнение на других языках программирования. Synthesis переводит программу VHDL в сеть логических ворот. Многие инструменты моделирования и синтеза VHDL являются частями коммерческих комплектов электронной автоматизации проектирования (EDA). Они часто также обрабатывают другие языки описания оборудования (HDL), такие как Verilog, SystemVerilog или SystemC. Существуют некоторые бесплатные приложения с открытым исходным кодом.

Моделирование VHDL

GHDL , вероятно, самый зрелый бесплатный VHDL-симулятор с открытым исходным кодом. Он поставляется в трех разных вариантах в зависимости от используемого бэкэнда: gcc , llvm или mcode . В следующих примерах показано, как использовать GHDL (версия mcode ) и Modelsim, коммерческий симулятор HDL от Mentor Graphics, в операционной системе GNU / Linux. Все будет очень похоже на другие инструменты и другие операционные системы.

Привет, мир

Создайте файл hello_world.vhd содержащий:

-- File hello_world.vhd
entity hello_world is
end entity hello_world;

architecture arc of hello_world is
begin
  assert false report "Hello world!" severity note;
end architecture arc;

Модуль компиляции VHDL представляет собой полную программу VHDL, которая может быть скомпилирована отдельно. Объекты - это единицы компиляции VHDL, которые используются для описания внешнего интерфейса цифровой схемы, то есть входных и выходных портов. В нашем примере entity называется hello_world и пуст. Модель, которую мы моделируем, представляет собой черный ящик, у него нет входов и нет выходов. Архитектура - это еще один тип компиляции. Они всегда связаны с entity и они используются для описания поведения цифровой схемы. Один объект может иметь одну или несколько архитектур для описания поведения объекта. В нашем примере объект связан только с одной архитектурой с именем arc которая содержит только один оператор VHDL:

  assert false report "Hello world!" severity note;

Заявление будет выполнено в начале моделирования и распечатайте Hello world! сообщение на стандартном выходе. Затем симуляция закончится, потому что больше нечего делать. Исходный файл VHDL, который мы написали, содержит два блока компиляции. Мы могли бы разделить их на два разных файла, но мы не могли бы разбить их в разных файлах: блок компиляции должен быть полностью содержаться в одном исходном файле. Обратите внимание: эта архитектура не может быть синтезирована, потому что она не описывает функцию, которая может быть непосредственно переведена на логические ворота.

Анализ и запуск программы с помощью GHDL:

$ mkdir gh_work
$ ghdl -a --workdir=gh_work hello_world.vhd
$ ghdl -r --workdir=gh_work hello_world
hello_world.vhd:6:8:@0ms:(assertion note): Hello world!

gh_work - это место, где GHDL хранит файлы, которые он создает. Вот что говорит опция --workdir=gh_work . Фаза анализа проверяет правильность синтаксиса и создает текстовый файл, описывающий единицы компиляции, найденные в исходном файле. Фаза запуска фактически компилирует, связывает и выполняет программу. Следует отметить, что в mcode версии GHDL, никакие двоичные файлы не генерируются. Программа перекомпилируется каждый раз, когда мы имитируем ее. Варианты gcc или llvm ведут себя по-разному. Также обратите внимание, что ghdl -r не принимает имя исходного файла VHDL, например ghdl -a , а имя единицы компиляции. В нашем случае мы передаем ему имя entity . Поскольку у него есть только одна связанная architecture , нет необходимости указывать, какой из них имитировать.

С Modelsim:

$ vlib ms_work
$ vmap work ms_work
$ vcom hello_world.vhd
$ vsim -c hello_world -do 'run -all; quit'
...
# ** Note: Hello world!
#    Time: 0 ns  Iteration: 0  Instance: /hello_world
...

vlib , vmap , vcom и vsim четыре команды , которые Modelsim предоставляет. vlib создает каталог ( ms_work ), в котором будут сохранены сгенерированные файлы. vmap связывает каталог, созданный vlib с логическим именем ( work ). vcom компилирует исходный файл VHDL и по умолчанию сохраняет результат в каталоге, связанном с work логическим именем. Наконец, vsim имитирует программу и производит тот же вид вывода, что и GHDL. Обратите внимание, что то, что vsim запрашивает, не является исходным файлом, а именем уже скомпилированной единицы компиляции. Параметр -c указывает, что симулятор запускается в режиме командной строки вместо режима графического пользовательского интерфейса по умолчанию (GUI). Параметр -do используется для передачи сценария TCL для выполнения после загрузки дизайна. TCL - это язык сценариев, очень часто используемый в инструментах EDA. Значение параметра -do может быть именем файла или, как в нашем примере, строкой команд TCL. run -all; quit инструктирует симулятор запускать симуляцию до тех пор, пока она не закончится естественным образом - или навсегда, если она будет продолжаться вечно, - а затем выйти.

Синхронный счетчик

-- File counter.vhd
-- The entity is the interface part. It has a name and a set of input / output
-- ports. Ports have a name, a direction and a type. The bit type has only two
-- values: '0' and '1'. It is one of the standard types.
entity counter is
  port(
    clock: in  bit;    -- We are using the rising edge of CLOCK
    reset: in  bit;    -- Synchronous and active HIGH
    data:  out natural -- The current value of the counter
  );
end entity counter;

-- The architecture describes the internals. It is always associated
-- to an entity.
architecture sync of counter is
  -- The internal signals we use to count. Natural is another standard
  -- type. VHDL is not case sensitive.
  signal current_value: natural;
  signal NEXT_VALUE:    natural;
begin
  -- A process is a concurrent statement. It is an infinite loop.
  process
  begin
    -- The wait statement is a synchronization instruction. We wait
    -- until clock changes and its new value is '1' (a rising edge).
    wait until clock = '1';
    -- Our reset is synchronous: we consider it only at rising edges
    -- of our clock.
    if reset = '1' then
      -- <= is the signal assignment operator.
      current_value <= 0;
    else
      current_value <= next_value;
    end if;
  end process;

  -- Another process. The sensitivity list is another way to express
  -- synchronization constraints. It (approximately) means: wait until
  -- one of the signals in the list changes and then execute the process
  -- body. Sensitivity list and wait statements cannot be used together 
  -- in the same process.
  process(current_value)
  begin
    next_value <= current_value + 1;
  end process;

  -- A concurrent signal assignment, which is just a shorthand for the
  -- (trivial) equivalent process.
  data <= current_value;
end architecture sync;

Привет, мир

Есть много способов распечатать классический «Hello world!». сообщение в VHDL. Самый простой из всего, вероятно, что-то вроде:

-- File hello_world.vhd
entity hello_world is
end entity hello_world;

architecture arc of hello_world is
begin
  assert false report "Hello world!" severity note;
end architecture arc;

Симуляция среды для синхронного счетчика

Симуляционные среды

Симуляционная среда для конструкции VHDL (Проектный тест или DUT) - это еще один проект VHDL, который, как минимум:

  • Объявляет сигналы, соответствующие входным и выходным портам тестируемого устройства.
  • Выполняет запуск тестируемого устройства и подключает его порты к объявленным сигналам.
  • Выполняет процессы, которые управляют сигналами, подключенными к входным портам тестируемого устройства.

Необязательно, среда моделирования может создавать экземпляры других проектов, кроме DUT, например, генераторы трафика на интерфейсах, мониторы для проверки протоколов связи, автоматические верификаторы выходов DUT ...

Симуляционная среда анализируется, разрабатывается и выполняется. Большинство симуляторов предлагают возможность выбора набора сигналов для наблюдения, построения графических осциллограмм, размещения точек останова в исходном коде, шаг в исходном коде ...

В идеале, среда моделирования должна использоваться как надежный тест без регрессии, то есть он должен автоматически обнаруживать нарушения спецификаций DUT, сообщать полезные сообщения об ошибках и гарантировать разумное покрытие функциональных возможностей DUT. Когда такие симуляционные среды доступны, они могут быть повторно запущены при каждом изменении тестируемого устройства, чтобы проверить, что он по-прежнему функционально корректен, без необходимости утомительных и подверженных ошибкам визуальных проверок трассировки моделирования.

На практике создание идеальных или даже просто хороших симуляционных сред является сложной задачей. Это часто или даже сложнее, чем проектирование самого DUT.

В этом примере мы представляем среду моделирования для примера синхронного счетчика . Мы показываем, как запустить его с помощью GHDL и Modelsim и как наблюдать графические формы сигналов с помощью GTKWave с GHDL и встроенным средством просмотра осциллограмм с помощью Modelsim. Затем мы обсудим интересный аспект моделирования: как их остановить?

Первая среда моделирования для синхронного счетчика

Синхронный счетчик имеет два входных порта и один выходной порт. Очень простая среда моделирования может быть:

-- File counter_sim.vhd
-- Entities of simulation environments are frequently black boxes without
-- ports.
entity counter_sim is
end entity counter_sim;

architecture sim of counter_sim is

  -- One signal per port of the DUT. Signals can have the same name as
  -- the corresponding port but they do not need to.
  signal clk:  bit;
  signal rst:  bit;
  signal data: natural;

begin

  -- Instantiation of the DUT
  u0: entity work.counter(sync)
  port map(
    clock => clk,
    reset => rst,
    data  => data
  );

  -- A clock generating process with a 2ns clock period. The process
  -- being an infinite loop, the clock will never stop toggling.
  process
  begin
    clk <= '0';
    wait for 1 ns;
    clk <= '1';
    wait for 1 ns;
  end process;

  -- The process that handles the reset: active from beginning of
  -- simulation until the 5th rising edge of the clock.
  process
  begin
    rst  <= '1';
    for i in 1 to 5 loop
      wait until rising_edge(clk);
    end loop;
    rst  <= '0';
    wait; -- Eternal wait. Stops the process forever.
  end process;

end architecture sim;

Моделирование с помощью GHDL

Давайте скомпилируем и смоделируем это с помощью GHDL:

$ mkdir gh_work
$ ghdl -a --workdir=gh_work counter_sim.vhd
counter_sim.vhd:27:24: unit "counter" not found in 'library "work"'
counter_sim.vhd:50:35: no declaration for "rising_edge"

Тогда сообщения об ошибках сообщают нам две важные вещи:

  • Анализатор GHDL обнаружил, что наш проект создает экземпляр с именем counter но этот объект не был найден в библиотечной work . Это связано с тем, что мы не скомпилировали counter перед counter_sim . При компиляции конструкций VHDL, которые создают экземпляры объектов, нижние уровни всегда должны быть скомпилированы перед верхними уровнями (иерархические проекты также могут быть скомпилированы сверху вниз, но только если они создают экземпляр component , а не сущности).
  • Функция rising_edge используемая нашим дизайном, не определена. Это связано с тем, что эта функция была введена в VHDL 2008, и мы не сказали GHDL использовать эту версию языка (по умолчанию он использует VHDL 1993 с допуском синтаксиса VHDL 1987).

Исправим две ошибки и запустим имитацию:

$ ghdl -a --workdir=gh_work --std=08 counter.vhd counter_sim.vhd
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim
^C

Обратите внимание, что для анализа и моделирования требуется опция --std=08 . Заметим также, что мы запустили симуляцию на entity counter_sim , архитектуре sim , а не в исходном файле.

Поскольку наша среда моделирования имеет бесконечный процесс (процесс, генерирующий часы), симуляция не останавливается, и мы должны прерывать ее вручную. Вместо этого мы можем указать время остановки с --stop-time :

$ ghdl -r --workdir=gh_work --std=08 counter_sim sim --stop-time=60ns
ghdl:info: simulation stopped by --stop-time

То есть, симуляция не говорит нам о поведении нашего DUT. Давайте дамп изменения значений сигналов в файле:

$ ghdl -r --workdir=gh_work --std=08 counter_sim sim --stop-time=60ns --vcd=counter_sim.vcd
Vcd.Avhpi_Error!
ghdl:info: simulation stopped by --stop-time

(игнорируйте сообщение об ошибке, это то, что нужно зафиксировать в GHDL, и это не имеет значения). counter_sim.vcd файл counter_sim.vcd . Он содержит в формате VCD (ASCII) все изменения сигнала во время моделирования. GTKWave может показать нам соответствующие графические формы:

$ gtkwave counter_sim.vcd

где мы видим, что счетчик работает так, как ожидалось.

Форма сигнала GTKWave

Моделирование с помощью Modelsim

Принцип точно такой же, как и у Modelsim:

$ vlib ms_work
...
$ vmap work ms_work
...
$ vcom -nologo -quiet -2008 counter.vhd counter_sim.vhd
$ vsim -voptargs="+acc" 'counter_sim(sim)' -do 'add wave /*; run 60ns'

введите описание изображения здесь

Обратите внимание на параметр -voptargs="+acc" переданный vsim : он не позволяет симулятору оптимизировать сигнал data и позволяет нам видеть его на волновых формах.

Изящно закончившиеся симуляции

С обоими симуляторами нам пришлось прерывать бесконечное симуляцию или указать время остановки с выделенной опцией. Это не очень удобно. Во многих случаях трудно предсказать время окончания моделирования. Было бы намного лучше прекратить симуляцию из кода VHDL среды моделирования, когда будет достигнуто определенное условие, например, когда текущее значение счетчика достигает 20. Этого можно достичь с помощью утверждения в процесс, который обрабатывает сброс:

  process
  begin
    rst <= '1';
    for i in 1 to 5 loop
      wait until rising_edge(clk);
    end loop;
    rst <= '0';
    loop
      wait until rising_edge(clk);
      assert data /= 20 report "End of simulation" severity failure;
    end loop;
  end process;

Пока data отличаются от 20, моделирование продолжается. Когда data достигают 20, симуляция падает с сообщением об ошибке:

$ ghdl -a --workdir=gh_work --std=08 counter_sim.vhd
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim
counter_sim.vhd:90:24:@51ns:(assertion failure): End of simulation
ghdl:error: assertion failed
  from: process work.counter_sim(sim2).P1 at counter_sim.vhd:90
ghdl:error: simulation failed

Обратите внимание, что мы повторно скомпилировали только среду моделирования: это единственный проект, который изменился, и это верхний уровень. Если бы мы модифицировали только counter.vhd , нам пришлось бы перекомпилировать оба: counter.vhd потому что он изменился и counter_sim.vhd потому что это зависит от counter.vhd .

Сбой моделирования с сообщением об ошибке не очень элегантный. Это может быть даже проблемой при автоматическом анализе сообщений моделирования, чтобы решить, прошел ли автоматический тест без регрессии или нет. Лучшим и более элегантным решением является остановка всех процессов при достижении условия. Это можно сделать, например, путем добавления boolean сигнала End of Simulation ( eof ). По умолчанию он инициализируется значением false в начале моделирования. Один из наших процессов установит его в true когда придет время завершить симуляцию. Все остальные процессы будут контролировать этот сигнал и останавливаться с вечным wait когда это станет true :

  signal eos:  boolean;
...
  process
  begin
    clk <= '0';
    wait for 1 ns;
    clk <= '1';
    wait for 1 ns;
    if eos then
      report "End of simulation";
      wait;
    end if;
  end process;

  process
  begin
    rst <= '1';
    for i in 1 to 5 loop
      wait until rising_edge(clk);
    end loop;
    rst <= '0';
    for i in 1 to 20 loop
      wait until rising_edge(clk);
    end loop;
    eos <= true;
    wait;
  end process;
$ ghdl -a --workdir=gh_work --std=08 counter_sim.vhd
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim
counter_sim.vhd:120:24:@50ns:(report note): End of simulation

И последнее, но не менее важное: в VHDL 2008 есть еще лучшее решение с стандартным пакетом env и процедурами stop и finish , которые он заявляет:

use std.env.all;
...
  process
  begin
    rst    <= '1';
    for i in 1 to 5 loop
      wait until rising_edge(clk);
    end loop;
    rst    <= '0';
    for i in 1 to 20 loop
      wait until rising_edge(clk);
    end loop;
    finish;
  end process;
$ ghdl -a --workdir=gh_work --std=08 counter_sim.vhd
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim
simulation finished @49ns

Сигналы против переменных, краткий обзор семантики моделирования VHDL

В этом примере рассматривается один из самых фундаментальных аспектов языка VHDL: семантика моделирования. Он предназначен для начинающих VHDL и представляет упрощенное представление, где многие детали были опущены (отложенные процессы, процедурный интерфейс VHDL, общие переменные ...) Читатели, заинтересованные в реальной полной семантике, должны ссылаться на Справочное руководство по языку (LRM).

Сигналы и переменные

В большинстве классических императивных языков программирования используются переменные. Они представляют собой контейнеры с ценностями. Оператор присваивания используется для хранения значения в переменной:

a = 15;

и значение, которое в настоящее время хранится в переменной, может быть прочитано и использовано в других операторах:

if(a == 15) { print "Fifteen" }

VHDL также использует переменные, и они имеют ту же роль, что и на большинстве императивных языков. Но VHDL также предлагает другой вид контейнера значений: сигнал. Сигналы также сохраняют значения, также могут быть назначены и прочитаны. Тип значений, которые могут быть сохранены в сигналах, (почти) те же, что и в переменных.

Итак, почему есть два вида контейнеров с ценностями? Ответ на этот вопрос необходим и лежит в основе языка. Понимание разницы между переменными и сигналами - это самое первое, что нужно сделать, прежде чем пытаться запрограммировать что-либо в VHDL.

Проиллюстрируем это различие на конкретном примере: обмен.

Примечание. Все последующие фрагменты кода являются частью процессов. Позже мы увидим, какие процессы.

    tmp := a;
    a   := b;
    b   := tmp;

заменяет переменные a и b . После выполнения этих 3 инструкций новое содержание a является старым контентом b и наоборот. Как и в большинстве языков программирования, требуется третья временная переменная ( tmp ). Если вместо переменных мы хотели поменять сигналы, мы бы писали:

    r <= s;
    s <= r;

или же:

    s <= r;
    r <= s;

с тем же результатом и без необходимости третьего временного сигнала!

Примечание: оператор присваивания сигнала VHDL <= отличается от оператора присваивания переменных := .

Посмотрим на второй пример, в котором мы предполагаем, что подпрограмма print печатает десятичное представление его параметра. Если a - целочисленная переменная, а ее текущее значение равно 15, выполняется:

    a := 2 * a;
    a := a - 5;
    a := a / 5;
    print(a);

будет печатать:

5

Если выполнить этот шаг за шагом в отладчике мы можем увидеть значение a переходе от первоначальных 15 до 30, 25 и , наконец , 5.

Но если s - целочисленный сигнал, а его текущее значение равно 15, выполняется:

    s <= 2 * s;
    s <= s - 5;
    s <= s / 5;
    print(s);
    wait on s;
    print(s);

будет печатать:

15
3

Если мы выполним это шаг за шагом в отладчике, мы не увидим изменения значений s до тех пор, пока команда wait . Более того, конечное значение s не будет 15, 30, 25 или 5, но 3!

Это, по-видимому, странное поведение обусловлено принципиально параллельным характером цифрового оборудования, как мы увидим в следующих разделах.

параллелизм

VHDL является языком описания аппаратного обеспечения (HDL), он является параллельным по своей природе. Программа VHDL представляет собой набор последовательных программ, которые работают параллельно. Эти последовательные программы называются процессами:

P1: process
begin
  instruction1;
  instruction2;
  ...
  instructionN;
end process P1;

P2: process
begin
  ...
end process P2;

Процессы, как и аппаратные средства, которые они моделируют, никогда не заканчиваются: они представляют собой бесконечные циклы. После выполнения последней команды выполнение продолжается с первым.

Как и в любом языке программирования, который поддерживает ту или иную форму параллелизма, планировщик отвечает за решение, какой процесс выполнять (и когда) во время моделирования VHDL. Кроме того, язык предлагает конкретные конструкции для межпроцессного взаимодействия и синхронизации.

планирование

Планировщик поддерживает список всех процессов , и для каждого из них, фиксирует его текущее состояние , которое может быть running , run-able или suspended . В running состоянии running не более одного процесса: тот, который в настоящее время выполняется. Пока текущий запущенный процесс не выполняет команду wait , он продолжает работать и не позволяет выполнять какой-либо другой процесс. Планировщик VHDL не является превентивным: каждый процесс обязан приостановить себя и позволить другим процессам работать. Это одна из проблем, с которой часто сталкиваются начинающие VHDL: свободный процесс.

  P3: process
    variable a: integer;
  begin
    a := s;
    a := 2 * a;
    r <= a;
  end process P3;

Примечание: переменная a объявляется локально, а сигналы s и r объявляются в другом месте на более высоком уровне. Переменные VHDL являются локальными для процесса, который объявляет их и не может быть замечен другими процессами. Другой процесс может также объявить переменную с именем a , она не будет той же переменной, что и для процесса P3 .

Как только планировщик возобновит процесс P3 , симуляция застрянет, текущее время моделирования больше не будет прогрессировать, и единственный способ остановить это - убить или прервать симуляцию. Причина заключается в том, что P3 не wait заявления и таким образом остаться в состоянии running состояние навсегда, цикл над его 3 инструкцией. Никакой другой процесс никогда не получит шанс бежать, даже если он run-able .

Даже процессы, содержащие оператор wait могут вызывать одну и ту же проблему:

  P4: process
    variable a: integer;
  begin
    a := s;
    a := 2 * a;
    if a = 16 then
      wait on s;
    end if;
    r <= a;
  end process P4;

Примечание: оператор равенства VHDL = .

Если процесс P4 возобновляется, а значение сигнала s равно 3, оно будет работать вечно, потому что условие a = 16 никогда не будет истинным.

Предположим, что наша программа VHDL не содержит таких патологических процессов. Когда исполняемый процесс выполняет команду wait , он немедленно приостанавливается, и планировщик помещает его в suspended состояние. Команда wait также несет условие, что процесс снова станет run-able . Пример:

    wait on s;

означает приостановить меня до тех пор, пока значение сигнала s изменится . Это условие записывается планировщиком. Затем планировщик выбирает другой процесс среди run-able , помещает его в running состояние и выполняет его. И то же самое повторяется, пока все run-able процессы не будут выполнены и приостановлены.

Важное замечание: когда несколько процессов могут быть run-able , стандарт VHDL не указывает, как планировщик должен выбрать, какой из них запускать. Следствием этого является то, что в зависимости от симулятора, версии симулятора, операционной системы или чего-либо еще, два моделирования одной и той же модели VHDL могли в какой-то момент сделать разные варианты и выбрать другой процесс для выполнения. Если бы этот выбор повлиял на результаты моделирования, мы могли бы сказать, что VHDL не является детерминированным. Поскольку отказ от детерминированности обычно нежелателен, разработчики должны не избегать детерминированных ситуаций. К счастью, VHDL позаботится об этом, и именно здесь сигналы вводят изображение.

Сигналы и межпроцессная связь

VHDL позволяет избежать детерминизма с использованием двух специфических характеристик:

  1. Процессы могут обмениваться информацией только через сигналы
  signal r, s: integer;  -- Common to all processes
...
  P5: process
    variable a: integer; -- Different from variable a of process P6
  begin
    a := s + 1;
    r <= a;
    a := r + 1;
    wait on s;
  end process P5;

  P6: process
    variable a: integer; -- Different from variable a of process P5
  begin
    a := r + 1;
    s <= a;
    wait on r;
  end process P6;

Примечание: комментарии VHDL распространяются от -- до конца строки.

  1. Значение сигнала VHDL не изменяется при выполнении процессов

Каждый раз, когда назначается сигнал, назначенное значение записывается планировщиком, но текущее значение сигнала остается неизменным. Это еще одно существенное отличие от переменных, которые принимают новое значение сразу после назначения.

Посмотрим на выполнение процесса P5 выше и предположим, что a=5 , s=1 и r=0 когда он возобновляется планировщиком. После выполнения команды a := s + 1; , значение переменной a изменяется и становится 2 (1 + 1). При выполнении следующей команды r <= a; это новое значение a (2), которое присваивается r . Но r является сигналом, текущее значение r равно 0. Итак, при выполнении a := r + 1; , переменная a принимает (сразу) значение 1 (0 + 1), а не 3 (2 + 1), как сказала бы интуиция.

Когда сигнал r действительно примет новое значение? Когда планировщик выполнит все запущенные процессы, и все они будут приостановлены. Это также называется: после одного дельта- цикла . Только тогда планировщик будет смотреть на все значения, которые были назначены на сигналы, и фактически обновлять значения сигналов. Моделирование VHDL представляет собой чередование фаз выполнения и фаз обновления сигнала. Во время фаз выполнения значения сигналов замораживаются. Символично, мы говорим, что между фазой выполнения и следующей фазой обновления сигнала произошла дельта времени. Это не в реальном времени. Дельта- цикл не имеет физической продолжительности.

Благодаря этому механизму обновления задержки, VHDL детерминирован. Процессы могут обмениваться данными только с сигналами, и сигналы не меняются во время выполнения процессов. Таким образом, порядок выполнения процессов не имеет значения: их внешняя среда (сигналы) не изменяется во время выполнения. Покажем это на предыдущем примере с процессами P5 и P6 , где начальное состояние равно P5.a=5 , P6.a=10 , s=17 , r=0 и где планировщик решает сначала запустить P5 и P6 , В следующей таблице показано значение двух переменных: текущего и следующего значений сигналов после выполнения каждой команды каждого процесса:

процесс / инструкция P5.a P6.a s.current s.next r.current r.next
Начальное состояние 5 10 17 0
P5 / a := s + 1 18 10 17 0
P5 / r <= a 18 10 17 0 18
P5 / a := r + 1 1 10 17 0 18
P5 / wait on s 1 10 17 0 18
P6 / a := r + 1 1 1 17 0 18
P6 / s <= a 1 1 17 1 0 18
P6 / wait on r 1 1 17 1 0 18
После обновления сигнала 1 1 1 18

При тех же начальных условиях, если планировщик решает сначала запустить P6 и P5 :

процесс / инструкция P5.a P6.a s.current s.next r.current r.next
Начальное состояние 5 10 17 0
P6 / a := r + 1 5 1 17 0
P6 / s <= a 5 1 17 1 0
P6 / wait on r 5 1 17 1 0
P5 / a := s + 1 18 1 17 1 0
P5 / r <= a 18 1 17 1 0 18
P5 / a := r + 1 1 1 17 1 0 18
P5 / wait on s 1 1 17 1 0 18
После обновления сигнала 1 1 1 18

Как мы видим, после выполнения наших двух процессов результат будет таким же, как и порядок исполнения.

Эта антиинтуитивная семантика назначения сигнала является причиной второго типа проблем, с которыми часто сталкиваются начинающие VHDL: назначение, которое, по-видимому, не работает, поскольку оно задерживается на один дельта-цикл. При пошаговом запуске процесса P5 в отладчике после того, как r было назначено 18, а a было назначено r + 1 , можно было ожидать, что значение a равно 19, но отладчик упрямо говорит, что r=0 и a=1 ...

Примечание: один и тот же сигнал может быть назначен несколько раз в течение одной и той же фазы выполнения. В этом случае это последнее назначение, которое решает следующее значение сигнала. Другие задания не имеют никакого эффекта, как если бы они никогда не были выполнены.

Настало время проверить наше понимание: вернитесь к нашему первому примеру подкачки и постарайтесь понять, почему:

  process
  begin
    ---
    s <= r;
    r <= s;
    ---
  end process;

фактически свопит сигналы r и s без необходимости третьего временного сигнала и почему:

  process
  begin
    ---
    r <= s;
    s <= r;
    ---
  end process;

будет строго эквивалентным. Попытайтесь понять также, почему, если s - целочисленный сигнал, а его текущее значение - 15, и мы выполняем:

  process
  begin
    ---
    s <= 2 * s;
    s <= s - 5;
    s <= s / 5;
    print(s);
    wait on s;
    print(s);
    ---
  end process;

два первых присвоения сигнала s не влияют, почему s наконец назначено 3 и почему два печатных значения составляют 15 и 3.

Физическое время

Для моделирования аппаратного обеспечения очень полезно уметь моделировать физическое время, затрачиваемое на некоторую операцию. Вот пример того, как это можно сделать в VHDL. Пример моделирует синхронный счетчик и представляет собой полный, автономный код VHDL, который можно скомпилировать и смоделировать:

-- File counter.vhd
entity counter is
end entity counter;

architecture arc of counter is
  signal clk: bit; -- Type bit has two values: '0' and '1'
  signal c, nc: natural; -- Natural (non-negative) integers
begin
  P1: process
  begin
    clk <= '0';
    wait for 10 ns; -- Ten nano-seconds delay
    clk <= '1';
    wait for 10 ns; -- Ten nano-seconds delay
  end process P1;

  P2: process
  begin
    if clk = '1' and clk'event then
      c <= nc;
    end if;
    wait on clk;
  end process P2;

  P3: process
  begin
    nc <= c + 1 after 5 ns; -- Five nano-seconds delay
    wait on c;
  end process P3;
end architecture arc;

В процессе P1 команда wait не используется, чтобы ждать, пока значение сигнала не изменится, как мы видели до сих пор, но дождаться определенной продолжительности. Этот процесс моделирует генератор тактовых импульсов. Сигнал clk - это часы нашей системы, он периодический с периодом 20 нс (50 МГц) и имеет рабочий цикл.

Процесс P2 моделирует регистр, который, если только возникает передний фронт clk , присваивает значение своего входа nc его выходу c а затем ждет следующего изменения значения clk .

Процесс P3 моделирует инкремент, который присваивает значение своего входа c , увеличиваемое на единицу, его выход nc ... с физической задержкой 5 нс. Затем он ждет, пока не изменится значение его ввода c . Это тоже ново. До сих пор мы всегда назначали сигналы с помощью:

  s <= value;

который по причинам, описанным в предыдущих разделах, мы можем косвенно перевести на:

  s <= value; -- after delta

Эта небольшая цифровая аппаратная система может быть представлена ​​следующим рисунком:

Синхронный счетчик

С введением физического времени и зная, что мы также имеем символическое время, измеренное в дельте , теперь мы имеем двумерное время, которое мы будем обозначать T+D где T - физическое время, измеренное в наносекундах, а D - число дельт (без физической продолжительности).

Полная картина

Существует один важный аспект моделирования VHDL, о котором мы еще не говорили: после этапа выполнения все процессы находятся в состоянии suspended . Мы неофициально заявили, что планировщик затем обновляет значения сигналов, которые были назначены. Но в нашем примере синхронного счетчика он должен одновременно обновлять сигналы clk , c и nc ? Как насчет физических задержек? И что будет дальше со всеми процессами в suspended состоянии и ни один в run-able работоспособности?

Полный (но упрощенный) алгоритм моделирования следующий:

  1. инициализация
    • Установите текущее время Tc в 0 + 0 (0 ns, 0 дельта-цикл)
    • Инициализируйте все сигналы.
    • Выполняйте каждый процесс до тех пор, пока он не приостановит выполнение команды wait .
      • Запишите значения и задержки назначений сигналов.
      • Запишите условия для возобновления процесса (задержка или изменение сигнала).
    • Вычислите следующий раз Tn как самый ранний из:
      • Время возобновления процессов, приостановленное wait for <delay> .
      • В следующий раз, когда значение сигнала изменится.
  1. Цикл моделирования
    • Tc=Tn .
    • Обновите сигналы, которые должны быть.
    • Поместите в run-able состояние все процессы, ожидающие изменения значения одного из сигналов, которые были обновлены.
    • Поместите в run-able работоспособности все процессы, которые были приостановлены wait for <delay> оператора wait for <delay> и для которого время возобновления - Tc .
    • Выполняйте все запущенные процессы до их приостановки.
      • Запишите значения и задержки назначений сигналов.
      • Запишите условия для возобновления процесса (задержка или изменение сигнала).
    • Вычислите следующий раз Tn как самый ранний из:
      • Время возобновления процессов, приостановленное wait for <delay> .
      • В следующий раз, когда значение сигнала изменится.
    • Если Tn бесконечно, остановите симуляцию. Иначе, начните новый цикл моделирования.

Ручное моделирование

В заключение давайте теперь вручную применим упрощенный алгоритм симуляции на синхронном счетчике, представленном выше. Мы произвольно решаем, что при выполнении нескольких процессов порядок будет P3 > P2 > P1 . Следующие таблицы представляют собой эволюцию состояния системы во время инициализации и первых циклов моделирования. Каждый сигнал имеет свой столбец, в котором указано текущее значение. Когда выполняется назначение сигнала, запланированное значение добавляется к текущему значению, например, a/b@T+D если текущее значение равно a а следующее значение будет равным b в момент времени T+D (физическое время плюс дельта-циклы) , Три последних столбца указывают условие возобновления приостановленных процессов (имя сигналов, которые должны измениться, или время возобновления процесса).

Фаза инициализации:

операции Tc Tn clk c nc P1 P2 P3
Установить текущее время 0 + 0
Инициализировать все сигналы 0 + 0 '0' 0 0
P3/nc<=c+1 after 5 ns 0 + 0 '0' 0 0/1 @ 5 + 0
P3/wait on c 0 + 0 '0' 0 0/1 @ 5 + 0 c
P2/if clk='1'... 0 + 0 '0' 0 0/1 @ 5 + 0 c
P2/end if 0 + 0 '0' 0 0/1 @ 5 + 0 c
P2/wait on clk 0 + 0 '0' 0 0/1 @ 5 + 0 clk c
P1/clk<='0' 0 + 0 '0' / '0' @ 0 + 1 0 0/1 @ 5 + 0 clk c
P1/wait for 10 ns 0 + 0 '0' / '0' @ 0 + 1 0 0/1 @ 5 + 0 10 + 0 clk c
Вычислить следующий раз 0 + 0 0 + 1 '0' / '0' @ 0 + 1 0 0/1 @ 5 + 0 10 + 0 clk c

Цикл моделирования №1

операции Tc Tn clk c nc P1 P2 P3
Установить текущее время 0 + 1 '0' / '0' @ 0 + 1 0 0/1 @ 5 + 0 10 + 0 clk c
Обновить сигналы 0 + 1 '0' 0 0/1 @ 5 + 0 10 + 0 clk c
Вычислить следующий раз 0 + 1 5 + 0 '0' 0 0/1 @ 5 + 0 10 + 0 clk c

Примечание: во время первого цикла моделирования нет фазы выполнения, потому что ни один из наших 3 процессов не удовлетворяет условию возобновления. P2 ожидает изменения значения clk и была транзакция на clk , но поскольку старые и новые значения одинаковы, это не изменение стоимости.

Цикл моделирования №2

операции Tc Tn clk c nc P1 P2 P3
Установить текущее время 5 + 0 '0' 0 0/1 @ 5 + 0 10 + 0 clk c
Обновить сигналы 5 + 0 '0' 0 1 10 + 0 clk c
Вычислить следующий раз 5 + 0 10 + 0 '0' 0 1 10 + 0 clk c

Примечание: опять же, этап выполнения отсутствует. nc изменился , но ни один процесс не ждет на nc .

Цикл моделирования №3

операции Tc Tn clk c nc P1 P2 P3
Установить текущее время 10 + 0 '0' 0 1 10 + 0 clk c
Обновить сигналы 10 + 0 '0' 0 1 10 + 0 clk c
P1/clk<='1' 10 + 0 '0' / '1' @ 10 + 1 0 1 clk c
P1/wait for 10 ns 10 + 0 '0' / '1' @ 10 + 1 0 1 20 + 0 clk c
Вычислить следующий раз 10 + 0 10 + 1 '0' / '1' @ 10 + 1 0 1 20 + 0 clk c

Цикл моделирования №4

операции Tc Tn clk c nc P1 P2 P3
Установить текущее время 10 + 1 '0' / '1' @ 10 + 1 0 1 20 + 0 clk c
Обновить сигналы 10 + 1 '1' 0 1 20 + 0 clk c
P2/if clk='1'... 10 + 1 '1' 0 1 20 + 0 c
P2/c<=nc 10 + 1 '1' 0/1 @ 10 + 2 1 20 + 0 c
P2/end if 10 + 1 '1' 0/1 @ 10 + 2 1 20 + 0 c
P2/wait on clk 10 + 1 '1' 0/1 @ 10 + 2 1 20 + 0 clk c
Вычислить следующий раз 10 + 1 10 + 2 '1' 0/1 @ 10 + 2 1 20 + 0 clk c

Цикл моделирования №5

операции Tc Tn clk c nc P1 P2 P3
Установить текущее время 10 + 2 '1' 0/1 @ 10 + 2 1 20 + 0 clk c
Обновить сигналы 10 + 2 '1' 1 1 20 + 0 clk c
P3/nc<=c+1 after 5 ns 10 + 2 '1' 1 1/2 @ 15 + 0 20 + 0 clk
P3/wait on c 10 + 2 '1' 1 1/2 @ 15 + 0 20 + 0 clk c
Вычислить следующий раз 10 + 2 15 + 0 '1' 1 1/2 @ 15 + 0 20 + 0 clk c

Примечание: можно подумать, что обновление nc запланировано на 15+2 , а мы запланировали его на 15+0 . При добавлении ненулевой физической задержки (здесь 5 ns ) к текущему времени ( 10+2 ) дельта-циклы исчезают. Действительно, дельта-циклы полезны только для различения различных времен моделирования T+0 , T+1 ... с тем же физическим временем T Как только физическое время изменится, дельта-циклы могут быть сброшены.

Цикл моделирования №6

операции Tc Tn clk c nc P1 P2 P3
Установить текущее время 15 + 0 '1' 1 1/2 @ 15 + 0 20 + 0 clk c
Обновить сигналы 15 + 0 '1' 1 2 20 + 0 clk c
Вычислить следующий раз 15 + 0 20 + 0 '1' 1 2 20 + 0 clk c

Примечание: опять же, этап выполнения отсутствует. nc изменился , но ни один процесс не ждет на nc .

Моделирование ... переход на английский язык для продолжения чтения

Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow