8. Примитивы, объявляемые пользователем (UDP) Verilog HDL

В этом пункте описывается техника моделирования, позволяющая расширить набор предопределенных примитивов вентилей путем разработки и спецификации новых примитивных элементов, называемых UDP.
Содержание
Превью(обложка) видео для поста 8. Примитивы, объявляемые пользователем (UDP) Verilog HDL

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

Следующие два типа поведения могут быть представлены в UDP:

1) Комбинационный — моделируется комбинационным UDP.

2) Последовательный — моделируется последовательным UDP.

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

Последовательный UDP может моделировать поведение, чувствительное как к уровню, так и к фронту.

Каждый UDP имеет ровно один выход, который может находиться в одном из трех состояний: 0, 1 или x. Трехсоставное значение z не поддерживается. В последовательных UDP выход всегда имеет то же значение, что и внутреннее состояние.

Значения z, передаваемые на входы UDP, должны рассматриваться так же, как и значения x.

8.1 Объявление UDP

Определения UDP не зависят от модулей. Они находятся на том же уровне, что и определения модулей в иерархии синтаксиса. UDP могут появляться в любом месте исходного текста, как до, так и после их экземпляра внутри модуля. Они не должны появляться между ключевыми словами module и endmodule.

Реализации могут ограничивать максимальное количество определений UDP в модели, но они должны допускать не менее 256.

Формальный синтаксис определения UDP приведен в Синтаксисе 8-1.

udp_declaration ::= { attribute_instance } primitive udp_identifier ( udp_port_list ) ; udp_port_declaration { udp_port_declaration } udp_body endprimitive | { attribute_instance } primitive udp_identifier ( udp_declaration_port_list ) ; udp_body endprimitive udp_port_list ::= output_port_identifier , input_port_identifier { , input_port_identifier } udp_declaration_port_list ::= udp_output_declaration , udp_input_declaration { , udp_input_declaration } udp_port_declaration ::= udp_output_declaration ; | udp_input_declaration ; | udp_reg_declaration ; udp_output_declaration ::= { attribute_instance } output port_identifier | { attribute_instance } output reg port_identifier [ = constant_expression ] udp_input_declaration ::= { attribute_instance } input list_of_port_identifiers udp_reg_declaration ::= { attribute_instance } reg variable_identifier udp_body ::= combinational_body | sequential_body combinational_body ::= table combinational_entry { combinational_entry } endtable combinational_entry ::= level_input_list : output_symbol ; sequential_body ::= [ udp_initial_statement ] table sequential_entry { sequential_entry } endtable udp_initial_statement ::= initial output_port_identifier = init_val ; init_val ::= 1'b0 | 1'b1 | 1'bx | 1'bX | 1'B0 | 1'B1 | 1'Bx | 1'BX | 1 | 0 sequential_entry ::= seq_input_list : current_state : next_state ; seq_input_list ::= level_input_list | edge_input_list level_input_list ::= level_symbol { level_symbol } edge_input_list ::= { level_symbol } edge_indicator { level_symbol } edge_indicator ::= ( level_symbol level_symbol ) | edge_symbol current_state ::= level_symbol next_state ::= output_symbol | - output_symbol ::= 0 | 1 | x | X level_symbol ::= 0 | 1 | x | X | ? | b | B edge_symbol ::= r | R | f | F | p | P | n | N | *
Синтаксис 8-1-Синтаксис для UDPs

8.1.1 Заголовок UDP

Определение UDP должно иметь одну из двух альтернативных форм. Первая форма должна начинаться с ключевого слова primitive, за которым следует идентификатор, являющийся именем UDP. За ним следует разделенный запятыми список имен портов, заключенных в круглые скобки, после которых ставится точка с запятой. За заголовком определения UDP должны следовать объявления портов и таблица состояний. Определение UDP должно завершаться ключевым словом endprimitive.

Вторая форма должна начинаться с ключевого слова primitive, за которым следует идентификатор, являющийся именем UDP. За ним следует разделенный запятыми список объявлений портов, заключенных в круглые скобки, за которыми следует точка с запятой. За заголовком определения UDP должна следовать таблица состояний. Определение UDP должно завершаться ключевым словом endprimitive.

UDP имеют несколько портов ввода и ровно один порт вывода; двунаправленные порты ввода- вывода в UDP не допускаются. Все порты UDP должны быть скалярными; векторные порты не допускаются.

Выходной порт должен быть первым в списке портов.

8.1.2 Объявление портов UDP

UDP должны содержать объявления входного и выходного портов. Объявление выходного порта начинается с ключевого слова output, за которым следует одно имя выходного порта. Объявление входного порта начинается с ключевого слова input, за которым следует одно или несколько имен входных портов.

Последовательные UDP должны содержать объявление reg для выходного порта, либо в дополнение к объявлению output, когда UDP объявляется с использованием первой формы заголовка UDP, либо как часть объявления output_declaration. Комбинационные UDP не могут содержать объявление reg. Начальное значение порта вывода может быть указано в initial объявлении в последовательном UDP (см. 8.1.3).

Реализации могут ограничивать максимальное количество входов в UDP, но они должны допускать не менее 9 входов для последовательных UDP и 10 входов для комбинационных UDP.

8.1.3 Объявление initial последовательном UDP

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

8.1.4 Таблица состояния UDP

Таблица состояний определяет поведение UDP. Она начинается с ключевого слова table и завершается ключевым словом endtable. Каждая строка таблицы завершается точкой с запятой.

Каждая строка таблицы создается с помощью различных символов (см. таблицу 8-1), которые обозначают входные значения и состояние выхода. Поддерживаются три состояния — 0, 1 и x.

Состояние z явно исключено из рассмотрения в UDP. Для обозначения определенных комбинаций состояний определен ряд специальных символов. Они описаны в таблице 8-1.

Порядок полей входного состояния каждой строки таблицы состояний берется непосредственно из списка портов в заголовке определения UDP. Он не связан с порядком объявлений входных портов.

Комбинационные UDP имеют одно поле на вход и одно поле на выход. Поля ввода отделяются от поля вывода двоеточием (:). Каждая строка определяет выход для определенной комбинации входных значений (см. 8.2).

Последовательные UDP имеют дополнительное поле, вставленное между входными полями и выходным полем. Это дополнительное поле представляет текущее состояние UDP и считается эквивалентным текущему выходному значению. Оно разделено двоеточиями. Каждая строка определяет выход на основе текущего состояния, определенных комбинаций входных значений и не более одного входного перехода (см. 8.4). Строка, подобная той, что показана ниже, является ошибкой:
(01) (10) 0 : 0 : 1 ;

Если все входные значения заданы как x, то состояние выхода должно быть задано как x.

Нет необходимости явно указывать все возможные комбинации входных значений. Все комбинации входных значений, которые не указаны явно, приводят к состоянию выхода по умолчанию x.

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

8.1.5 Значения Z в UDP

Значение z в записи таблицы не поддерживается и считается недопустимым. Значения z, передаваемые на входы UDP, должны рассматриваться так же, как и значения x.

8.1.6 Краткое описание символов

Чтобы улучшить читаемость и облегчить написание таблицы состояний, предусмотрено несколько специальных символов. В таблице 8-1 приведено значение всех символов значений, которые действительны в табличной части определения UDP.

Таблица 8-1- Символы таблицы UDP
СимволИнтерпретацияКомментарии
0Логический 0
1Логический 1
xНеопределенноеРазрешено в полях ввода и выводадля всех UDPs и в поле текущего состояния последовательных UDP.
?Обозначение для 0, 1, и xНе допускается в поле вывода.
bОбозначение для 0 и 1Разрешено в полях ввода во всех UDPs и в поле текущего состояния последовательных UDP. Не допускается в поле вывода.
Без измененийРазрешено только в поле вывода последовательного UDP.
(vw)Переход значения с v на wv и w могут быть любыми из 0, 1, x, ? или b и допустимы только в поле ввода.
*Такой же, как и (??)Любое изменение значения на входе.
rТакой же, как и (01)Восходящий фронт на входе.
fТакой же, как и (10)Падающий фронт на входе.
pОбозначение для (01), (0 x) и (x1)Потенциальный положительный фронт на входе.
nОбозначение для (10), (1x) и (x0)Потенциальный отрицательный фронт на входе.

8.2 Комбинационные UDPs

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

Например:

Следующий пример определяет мультиплексор с двумя входами данных и управляющим входом:
primitive multiplexer (mux, control, dataA, dataB); output mux; input control, dataA, dataB; table // control dataA dataB mux 0 1 0 : 1 ; 0 1 1 : 1 ; 0 1 x : 1 ; 0 0 0 : 0 ; 0 0 1 : 0 ; 0 0 x : 0 ; 1 0 1 : 1 ; 1 1 1 : 1 ; 1 x 1 : 1 ; 1 0 0 : 0 ; 1 1 0 : 0 ; 1 x 0 : 0 ; x 0 0 : 0 ; x 1 1 : 1 ; endtable endprimitive

Первая запись в этом примере может быть объяснена следующим образом: когда control равно 0, dataА равны 1, и dataB равен 0, то выходной mux равен 1.

Входная комбинация 0xx (управление=0, данныеA=x, данныеB=x) не задана. Если эта комбинация возникает во время моделирования, значение выходного порта mux станет x.

Используя ?, описание мультиплексора можно сократить следующим образом:
primitive multiplexer (mux, control, dataA, dataB); output mux; input control, dataA, dataB; table // control dataA dataB mux 0 1 ? : 1 ; // ? = 0 1 x 0 0 ? : 0 ; 1 ? 1 : 1 ; 1 ? 0 : 0 ; x 0 0 : 0 ; x 1 1 : 1 ; endtable endprimitive

8.3 Последовательные UDPs, чувствительные к уровню

Чувствительное к уровню последовательное поведение представлено так же, как и комбинационное, за исключением того, что выход объявлен как тип reg и в каждой записи таблицы есть дополнительное поле. Это новое поле представляет текущее состояние UDP. Поле выхода в последовательном UDP представляет следующее состояние.

Например:

Рассмотрим пример с защелкой:
primitive latch (q, clock, data); output q; reg q; input clock, data; table // clock data q q+ 0 1 : ? : 1 ; 0 0 : ? : 0 ; 1 ? : ? : - ; // - = no change endtable endprimitive

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

8.4 Чувствительные к фронтам последовательные UDP

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

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

Все переходы, которые не влияют на выход, должны быть явно указаны. В противном случае такие переходы приводят к изменению значения выхода на x. Все неуказанные переходы по умолчанию принимают значение выхода x.

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

Например:

Следующий пример описывает D-флип с нарастающим фронтом импульса:
primitive d_edge_ff (q, clock, data); output q; reg q; input clock, data; table // clock data q q+ // получают выход по нарастающему фронту тактового сигнала (01) 0 : ? : 0 ; (01) 1 : ? : 1 ; (0?) 1 : 1 : 1 ; (0?) 0 : 0 : 0 ; // игнорирование срезов (?0) ? : ? : - ; // игнорировать изменения данных на постоянном тактовом сигнале ? (??) : ? : - ; endtable endprimitive

Термины, такие как (01), представляют собой переходы входных значений. В частности, (01) представляет собой переход от 0 к 1. Первая строка в таблице предыдущего определения UDP интерпретируется следующим образом: когда такстовый сигнал переходит значение с 0 на 1, а данные равны 0, выход переходит в 0 независимо от текущего состояния.

Переход тактового сигнала из 0 в x при данных равных 0, и текущем состоянии, равном 1, приведет к выходу q в Х.

8.5 Инициализация последовательного UDP

Начальное значение на выходном порту последовательного UDP может быть задано с помощью оператора initial, который обеспечивает процедурное назначение. Начальное утверждение является необязательным.

Как и начальные декларации в модулях, начальное декларация в UDP начинается с ключевого слова initial. Допустимое содержимое начальных деклараций в UDP и допустимые левая и правая стороны их процедурных деклараций присваивания отличаются от начальных деклараций в модулях. Неполный список различий между этими двумя типами начальных утверждений описан в таблице 8-2.

Таблица 8-2 initial декларации в UDP и модулях
Начальные декларации в UDPНачальные декларации в модулях
Содержание ограничено одной процедурной декларацией о назначенииСодержимым может быть одна процедурная декларации любого типа или блочное декларация, содержащее более одной процедурной декларации
Процедурный оператор присваивания должен присвоить значение регистру, идентификатор которого совпадает с идентификатором выходного клеммаПроцедурные операторы присваивания в начальных состояниях могут присваивать значения регистру, идентификатор которого не совпадает с идентификатором выходного клемма
Процедурный оператор присваивания должен присвоить одно из следующих значений: 1’b1, 1’b0, 1’bx, 1, 0Процедурные операторы присваивания могут присваивать значения любого размера, системы счисления и значения

Например:

Пример 1 В следующем примере показан последовательный UDP, содержащий начальное утверждение.
primitive srff (q, s, r); output q; reg q; input s, r; initial q = 1'b1; table // s r q q+ 1 0 : ? : 1 ; f 0 : 1 : - ; 0 r : ? : 0 ; 0 f : 0 : - ; 1 1 : ? : 0 ; endtable endprimitive

Выход q имеет начальное значение 1 в начале моделирования. Спецификация задержки на экземпляре UDP не задерживает время моделирования присвоения этого начального значения выходу. Когда начинается моделирование, это значение является текущим состоянием в таблице состояний. Задержки не допускаются в начальной декларации UDP.

Пример 2 Следующий пример и рисунок 8-1 показывают, как значения применяются в модуле, который создается последовательном UDP с начальной декларацией:
primitive dff1 (q, clk, d); input clk, d; output q; reg q; initial q = 1'b1; table // clk d q q+ r 0 : ? : 0 ; r 1 : ? : 1 ; f ? : ? : - ; ? * : ? : - ; endtable endprimitive module dff (q, qb, clk, d); input clk, d; output q, qb; dff1 g1 (qi, clk, d); buf #3 g2 (q, qi); not #5 g3 (qb, qi); endmodule

UDP dff1 содержит оператор initial, который устанавливает начальное значение его выхода в 1. Модуль dff содержит экземпляр UDP dff1.

На рисунке 8-1 показана схема предыдущего модуля и время распространения моделирования начального значения выхода UDP.

На рисунке 8-1 выходной сигнал с выхода UDP qi включает сети q и qb. В момент времени моделирования 0 значение qi изменяется на 1. Это начальное значение qi не распространяется на сеть q до момента времени моделирования 3, и не распространяется на сеть qb до момента времени моделирования 5.

Глава 8 Примитивы, объявляемые пользователем (UDP) Verilog HDL. Рисунок 8-1 Схема модуля и симуляция времени с переходом начального значения.
Рисунок 8-1 Схема модуля и симуляция времени с переходом начального значения.

8.6 Экземпляры UDP

Синтаксис для создания экземпляра UDP показан в Синтаксисе 8-2.

udp_instantiation ::= udp_identifier [ drive_strength ] [ delay2 ] udp_instance { , udp_instance } ; udp_instance ::= [ name_of_udp_instance ] ( output_terminal , input_terminal { , input_terminal } ) name_of_udp_instance ::= udp_instance_identifier [ range ]
Синтаксис 8-2 — Синтаксис для экземпляров UDP

Экземпляры UDP задаются внутри модулей так же, как и вентиля (см. 7.1). Имя экземпляра необязательно, как и для вентилей. Порядок подключения портов соответствует порядку, указанному в определении UDP. Можно указать только две задержки, поскольку z не поддерживается для UDP. Для массива экземпляров UDP может быть указан необязательный диапазон. Правила соединения портов остаются такими же, как описано в п. 7.1.

Например:

Следующий пример создает экземпляр флип-флопа D-типа d_edge_ff (определен в п. 8.4).
module flip; reg clock, data; parameter p1 = 10; parameter p2 = 33; parameter p3 = 12; d_edge_ff #p3 d_inst (q, clock, data); initial begin data = 1; clock = 1; #(20 * p1) $finish; end always #p1 clock = ~clock; always #p2 data = ~data; endmodule

8.7 Описание совмещения чувствительности к уровню и фронту

Определения UDP позволяют совмещать конструкции чувствительные к уровню и фронту в одной таблице. При изменении входных данных сначала обрабатываются случаи чувствительные к фронту, а затем случаи чувствительные к уровню. Таким образом, когда чувствительные к уровню и чувствительные к фронту случаи задают разные выходные значения, результат задается чувствительным к уровню случаем.

Например:
primitive jk_edge_ff (q, clock, j, k, preset, clear); output q; reg q; input clock, j, k, preset, clear; table // clock jk pc state output/next state ? ?? 01 : ? : 1 ; // предустановка значения ? ?? *1 : 1 : 1 ; ? ?? 10 : ? : 0 ; // очистка значения ? ?? 1* : 0 : 0 ; r 00 00 : 0 : 1 ; r 00 11 : ? : - ; r 01 11 : ? : 0 ; r 10 11 : ? : 1 ; r 11 11 : 0 : 1 ; r 11 11 : 1 : 0 ; f ?? ?? : ? : - ; b *? ?? : ? : - ; // j and k transition cases b ?* ?? : ? : - ; endtable endprimitive

В этом примере логика предустановки и очистки чувствительна к уровню. Когда комбинация предустановки и очистки имеет значение 01, выход имеет значение 1. Аналогично, когда комбинация предустановки и очистки имеет значение 10, на выходе значение 0.

Остальная логика чувствительна к фронтам тактовых импульсов. В обычных случаях тактовой синхронизации флип-флоп чувствителен к нарастающему фронту тактового сигнала, на что указывает символ r в поле тактового сигнала в этих записях. Нечувствительность к спадающему фронту тактового сигнала обозначается дефисом (-) в поле вывода (см. таблицу 8-1) для записи с f в качестве значения тактового сигнала. Помните, что для данного входного перехода должен быть указан желаемый выход, чтобы избежать нежелательных значений x на выходе. Последние две записи показывают, что переходы на входах j и k не изменяют выход при постоянном низком или высоком значении тактового сигнала.

8.8 Доминирование чувствительности к уровню

В таблице 8-3 показаны чувствительные к уровню и фронтам записи в примере из раздела 8.7, их чувствительное к уровню или фронтам поведение, а также случай входных значений, которые включает каждая из них.

Таблица 8-3 Совмещение чувствительных к уровню и чувствительных к фронту записей
ВходВключенный случайПоведение
? ?? 01: ?: 1;0 00 01: 0: 1;Чувствительный к уровню
f ?? ??: ?: -;f 00 01: 0: 0;Чувствительный к фронтам

Включенные случаи определяют противоположные значения следующего состояния для одной и той же комбинации входа и текущего состояния. Включенный случай, чувствительный к уровню, указывает, что когда значения входов clock, jk и pc равны 0, 00 и 01, а текущее состояние равно 0, выход изменяется на 1. Включенный случай, чувствительный к фронту, указывает, что когда clock падает с 1 на 0, другие входы jk и pc равны 00 и 01, а текущее состояние равно 0, выход изменяется на 0.

Если сначала обрабатывается регистр, чувствительный к фронту, а затем регистр, чувствительный к уровню, выход изменяется на 1.