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

Рисунок 1
Архитектура кода VHDL для этой схемы
1 architecture Behavioral of circuit1 is 2 signal sig1: std_logic; 3 begin 4 sig1 <= (a and b); 5 out1 <= (sig1 or c); 6 out2 <= (not d); 7 end Behavioral;
Как вы можете видеть, сигнал имеет четкое отображение в аппаратное обеспечение: он становится (группой) проводов. Имеет ли смысл иметь несколько присвоений сигналу «hljs»> sig1 <= (a и b); sig1 <= (c или d);
Если эти два назначения находятся в параллельной части кода, они выполняются одновременно. Мы можем рассмотреть эквивалентное оборудование вышеуказанного кода, как показано на рисунке 2.

Фигура 2
На рисунке 2 показано, что множественные присвоения сигнала в параллельной части кода не являются хорошей идеей, поскольку между этими назначениями может возникнуть конфликт. Например, если A = C = 0 и B = D = 1, первая строка назначит sig1 = (0 и 1) = 0, а вторая попытается присвоить sig1 = (0 или 1) = 1. Вот почему, в параллельной части кода VHDL не допускает множественных присвоений сигналу. Что делать, если эти два назначения были в последовательной части кода? Компилятор может принимать несколько назначений внутри процесса, но даже в этом случае выживет только последнее назначение, а предыдущие будут проигнорированы. Чтобы объяснить это, обратите внимание, что процесс можно рассматривать как черный ящик, чья внутренняя операция может быть задана каким-то абстрактным описанием поведения. В этом описании используются последовательные операторы. Связь между черным ящиком процесса и внешним миром достигается с помощью сигналов. Процесс может считывать значение этих сигналов или присваивать им значение. Поэтому VHDL использует сигналы для подключения последовательной части кода к параллельному домену. Поскольку сигнал подключен к параллельному домену кода, не имеет смысла назначать несколько значений одному и тому же сигналу. Вот почему, сталкиваясь с множеством назначений на сигнал, VHDL рассматривает только последнее назначение как действительное назначение.
Обновление значения сигнала
Интерпретация процесса черного окна процесса показывает еще одно важное свойство назначения сигнала внутри процесса: когда мы назначаем значение сигналу внутри процесса, новое значение сигнала будет недоступно сразу. Значение сигнала будет обновляться только после завершения текущего процесса. Следующий пример еще раз поясняет этот момент. В этом примере используются инструкции VHDL «if». Обратите внимание, что в будущих статьях мы увидим больше примеров этого утверждения; однако, поскольку он аналогичен условным структурам других языков программирования, следующий код должен быть легко понят. Краткое описание этого утверждения вы можете найти в предыдущей статье.
Пример. Запишите код VHDL для счетчика, который подсчитывается от 0 до 5.
Ниже приводится одно возможное описание VHDL:
1 library IEEE; 2 use IEEE. STD_LOGIC_1164. ALL; 3 entity SigCounter is 4 Port (clk: in STD_LOGIC; 5 out1: out integer range 0 to 5); 6 end SigCounter; 7 architecture Behavioral of SigCounter is 8 signal sig1: integer range 0 to 6; 9 begin 10 process(clk) 11 begin 12 if (clk'event and clk="1") then 13 sig1 <= sig1+1; 14 if (sig1=6) then 15 sig1 <= 0; 16 end if; 17 end if; 18 out1 <= sig1; 19 end process; 20 end Behavioral;
В этом примере sig1 определяется как сигнал целочисленного типа в декларативной части архитектуры. С каждым нарастающим фронтом clk значение сигнала sig1 будет увеличиваться на единицу. Когда sig1 достигнет 6, условие оператора «if» в строке 14 будет оценено как true, а sig1 примет значение 0. Похоже, что sig1, значение которого в конечном итоге передается на выходной порт out1, всегда будет принимать значения в диапазоне от 0 до 5. Другими словами, кажется, что оператор if if строки 14 никогда не позволит sig1 взять значение 6. Рассмотрим работу кода более подробно.
Предположим, что предыдущий запуск процесса устанавливает sig1 в 5. При следующем нарастающем фронте clk выполняются инструкции внутри оператора if if строки 12. Строка 13 добавит одно к текущему значению sig1, которое равно 5, и присвойте результат sig1. Следовательно, новое значение sig1 будет 6; однако следует отметить, что значение сигнала sig1 будет обновляться только после завершения текущего процесса. В результате в этом запуске процесса условие оператора «if» в строке 14 будет оцениваться как ложное, а соответствующая «then» ветвь будет обходить. Достигнув конца тела процесса, значение sig1 будет обновлено до 6. Хотя мы предполагали, что sig1 находится в диапазоне от 0 до 5, он может принимать значение 6!
Аналогично, на следующем нарастающем фронте clk строка 13 назначит 7 sig1. Однако обновление значения сигнала будет отложено до тех пор, пока мы не достигнем конца тела процесса. В этом запуске процесса условие оператора «if» в строке 14 возвращает true, и, следовательно, строка 15 будет устанавливать sig1 в ноль. Как вы видите, в этом запуске процесса есть два назначения одного и того же сигнала. Основываясь на обсуждении предыдущего раздела, вступит в силу только последнее назначение, то есть новое значение sig1 будет равно нулю. Достигнув конца этого процесса, sig1 примет это новое значение. Как вы видите, sig1 будет принимать значения в диапазоне от 0 до 6, а не от 0 до 5! Вы можете проверить это в следующем ISE-моделировании кода.

Рисунок 3
Следовательно, при использовании сигналов внутри процесса следует отметить, что новое значение сигнала будет доступно в конце текущего запуска процесса. Не обращая внимания на это свойство, является распространенным источником ошибок, особенно для тех, кто не знаком с VHDL.
Чтобы обобщить наше обсуждение до сих пор, сигнал моделирует межкомпонентные соединения. Если мы назначим несколько значений сигналу внутри процесса, будет рассмотрено только последнее назначение. Более того, назначенное значение будет доступно в конце прогона процесса, и обновления не будут выполняться немедленно.
Переменная: другой полезный объект VHDL
Как обсуждалось в предыдущей статье, последовательные утверждения позволяют нам иметь алгоритмическое описание схемы. Код таких описаний каким-то образом похож на код, написанный языком программирования. В компьютерном программировании «переменные» используются для хранения информации, которая должна быть указана и использована программами. С помощью переменных мы можем более легко описать алгоритм при написании компьютерной программы. Вот почему, помимо сигналов, VHDL позволяет нам использовать переменные внутри процесса. Хотя оба сигнала и переменные могут использоваться для представления значения, они имеют несколько отличий. Переменная не обязательно отображается в одно соединение. Кроме того, мы можем назначить несколько значений переменной, а обновление нового значения - немедленно. В остальной части статьи мы объясним эти свойства более подробно.
Прежде чем продолжить, обратите внимание, что переменные могут быть объявлены только в последовательном модуле, таком как процесс (единственным исключением является «общая» переменная, которая не рассматривается в этой статье). Чтобы получить более удобные переменные VHDL, рассмотрите следующий сегмент кода, который определяет переменную var1.
1 process(clk) 2 variable var1: integer range 0 to 5; 3 begin 4 var1:= 3; 5 … 6 end process;
Подобно сигналу, переменная может быть любого типа данных (см. Предыдущие статьи этой серии, чтобы узнать больше о разных типах данных). Однако переменные являются локальными для процесса. Они используются для хранения промежуточных значений и не могут быть доступны за пределами процесса. Более того, как показано в строке 4 вышеуказанного кода, присвоение переменной использует нотацию «: =», тогда как назначение сигнала использует «<=».
Множественные назначения переменной
Рассмотрим следующий код. В этом случае определяется переменная var1 типа std_logic. Затем в строках 12, 13 и 14 этой переменной присваиваются три значения.
1 library IEEE; 2 use IEEE. STD_LOGIC_1164. ALL; 3 entity VarTest1 is 4 Port (in1, in2, in3: in STD_LOGIC; 5 out1: out STD_LOGIC); 6 end VarTest1; 7 architecture Behavioral of VarTest1 is 8 begin 9 process(in1, in2, in3) 10 variable var1: std_logic; 11 begin 12 var1:= in1; 13 var1:= (var1 and in2); 14 var1:= (var1 or in3); 15 out1 <= var1; 16 end process; 17 end Behavioral;
На рисунке 4 показана схема RTL приведенного выше кода, который генерируется Xilinx ISE.

Рисунок 4
Легко проверить, что приведенная схема соответствует поведению, описанному в процессе; однако этот пример показывает, что отображение переменных в аппаратное обеспечение является чем-то более сложным, чем преобразование сигналов. Это связано с тем, что последовательные утверждения описывают поведение схемы. Как вы можете видеть, в этом примере каждая операция назначения переменных строк 13 и 14 создала другой провод, хотя оба этих назначения имеют одно и то же имя переменной, то есть var1.
Обновление значения переменной
Переменные обновляются немедленно. Чтобы проверить это, мы модифицируем код вышеуказанного счетчика и будем использовать переменную вместо сигнала. Код приведен ниже:
1 library IEEE; 2 use IEEE. STD_LOGIC_1164. ALL; 3 entity VarCounter is 4 Port (clk: in STD_LOGIC; 5 out1: out integer range 0 to 5); 6 end VarCounter; 7 architecture Behavioral of VarCounter is 8 begin 9 process(clk) 10 variable var1: integer range 0 to 6; 11 begin 12 if (clk'event and clk="1") then 13 var1:= var1+1; 14 if (var1=6) then 15 var1:= 0; 16 end if; 17 end if; 18 out1 <= var1; 19 end process; 20 end Behavioral;
Поскольку новое значение переменной доступно сразу, выход будет находиться в диапазоне от 0 до 5. Это показано в следующем результате моделирования ISE.

Рисунок 5
Резюме
- Сигнал моделирует схемы соединений. Если мы назначим несколько значений сигналу внутри процесса, будет рассмотрено только последнее назначение. Кроме того, назначенное значение будет доступно в конце текущего прогона процесса, и обновления не будут выполняться немедленно.
- Одна переменная может создавать несколько межкомпонентных соединений.
- Мы можем назначить несколько значений одной и той же переменной, и назначенные новые значения вступят в силу немедленно.
- Подобно сигналу, переменная может быть любого типа данных.
- Переменные являются локальными для процесса. Они используются для хранения промежуточных значений и не могут быть доступны за пределами процесса.
- Назначение переменной использует нотацию «: =», тогда как при назначении сигнала используется «<=».
Чтобы просмотреть полный список моих статей, перейдите на эту страницу.