Вы находитесь на странице: 1из 55

1.​ ​ESL уровень проектирования. TLM-модели.

(Electronic System Level), как уровень над RTL, для hardware и


software проектирования. ESL - уровень представляет собой
поведенческий (до деления на HW/SW) и архитектурный уровни, а
также он позволяет увеличивать производительность на 200 000
вентилей в человеко-год.
Преимущества использования ESL заключаются: в повышении
уровня абстракции представления системы; в возможности
использования более высокого уровня для reuse проектов; в
поддержке непрерывной цепи проектирования, начиная с верхнего
абстрактного уровня. Проектирование представляет собой
последовательность повторяющихся шагов от
абстрактного уровня до создания готового продукта.

Основной концепцией TLM (Transaction-Level Modeling) является


создание модели уровня детализации, достаточного для решения
отдельной задачи
проектирования компонентов системы. Анализ только отдельных
деталей позволяет значительно повысить скорость верификации
моделей. Концепция TLM является независимой от каких-либо
языков, однако основывается на языках высокого уровня
программирования, например, таких как SystemC или SystemVerilog.
Transaction Level Modeling (TLM) предлагает bit-true, address-map
accurate, cycle-less модели. TLM определяет транзакцию (SoC
событие), как мгновенную передачу данных (коммуникацию) или
синхронизацию между двумя модулями, определенными
hardware/software системной спецификацией. Термин TLM определен
для соответствующего ESL (electronic system level) уровня
проектирования. Основной концепцией TLM является то, что
коммуникации между системами и подсистемами и их
функциональность могут быть разработаны и детализированы
независимо друг от друга.

2.​ ​Обзор языков проектирования SoC


Различные языки разрабатывались для различных аспектов
проектирования систем. C/C++ преимущественно используются для
разработки программного обеспечения встроенных систем.

Языки описания аппаратуры (HDLs), VHDL и Verilog, применяются


для проектирования и синтеза цифровых схем. Языки Vera и e
предназначены для функциональной верификации

сложных специализированных интегральных схем (ASIC). Новый


язык SystemVerilog является дополненной версией языка Verilog,
предназначенной для решения нескольких задач
аппаратно-ориентированного системного проектирования. Matlab и
другие подобные инструменты и языки, такие как SPW и System
Studio, широко используются для сбора системных требований и
разработки алгоритмов цифровой обработки сигналов. На рис. 4
представлены области использования языков системного
проектирования . SystemC был создан для проектирования SoC –
устройств на ESL - уровне (electronic system-level) для разработчиков,
использующих C/C++. Стандарт: IEEE Std. 1666-2005 SystemC (Open
SystemC Initiative (OSCI)). SystemC – это единый язык для
проектирования и верификации, который позволяет представлять
архитектурные и другие атрибуты моделей системного уровня в
форме классов языка C++ с открытым исходным кодом. Это дает
возможность вы полнять проектирование и верификацию на
системном уровне, независимо от каких-либо деталей реализации
аппаратуры и программного обеспечения. Также имеется
возможность выполнения совместной

верификации с RTL–проектами. Высокий уровень описания


позволяет значительно быстрее и продуктивнее выполнять анализ
выбора компромиссного решения для архитектуры, чем это можно
было сделать на RT-уровне. Более того, вериикация системной
архитектуры и других системных атрибутов происходит быстрее
точной по временным параметрам или расположению внешних
контактов RTL-моделиl.
3.​ ​История создания языка Verilog, классификация моделей в Verilog.

Verilog – язык описания аппаратуры, разработан в 1985 г. Филиппом


Мурби (Philip Moorby) [40], нуждавшимся в простом, наглядном и
эффективном способе для описания цифровых схем, моделирования и
анализа их функционирования. Язык становится собственностью

Gateway Design Automatiion, а затем Cadence Design Systems. Первый


стандарт Verilog появляется IEEE в 1995 г . Последнее обновление
языка выполнено в 2005 г. [42]. Verilog имеет такую же, а может и
большую, популярность, как и VHDL, широко используется в

промышленности при проектировании ASIC-устройств.

Классификация моделей:

- Поведенческая модель

- Функциональная модель

- Структурные модели

- Модель производительности

- Модель интерфейса

4. Классификация типов данных языка Verilog, их значения, область


применения, массивы, векторы.

1) С двумя состояниями (ключевое слово bit): лучшая производительность,


минимизация использования памяти;
2) Очереди (​ ), динамические и ассоциативные массивы (dynamic и
Queues​
associative arrays), автоматическая память (automatic storage): минимизация
использования памяти, встроенная поддержка поиска и сортировки;
3) Объединения (​ ) и пакеты (​
Unions​ packed​): предлагают множественное
представление одних и тех же данных;
4) Классы и структуры (​
Classes и ​
structures​): поддерживают структуры
абстрактных данных;
5) Строки (​ ): встроенная поддержка строк;
Strings​

6) Типы перечисления (​
Enumerated types​): упрощение записи и анализа кода;

Динамические массивы декларируются без указания их длинны с пустыми скобками []. Для
выделения памяти под массив используется оператор new[]. Если указать дополнительное имя в
операторе new[], то значение будет копироваться в новый элемент (листинг 16.13.).

Цепи и регистры могут группироваться в шины, называемые в языке Verilog


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

5. Системные задачи и функции для управления процессом


моделирования и вывода сообщений.

6.​ ​Вентильные Verilog-модели.


Вентильный уровень – это наиболее низкий из возможных в языке Verilog
уровней абстракции. При разработке моделей на этом уровне проектировщик
оперирует такими понятиями, как МОП-транзистор, КМОП-транзистор и т.п.
[7; 19; 22]. Средства синтеза, разработанные для интегральных микросхем
FPGA-типа, не позволяют синтезировать логические цепи на базе моделей
вентильного уровня. В то же время такие модели полезны для понимания и
исследования физических процессов, протекающих в цифровых цепях.

Описание модели не нашел(

7.​ ​Dataflow-модели в Verilog

DFD — общепринятое сокращение от ​англ. ​data flow diagrams — диаграммы потоков


данных. Так называется ​ методология графического структурного ​анализа​ ,
описывающая внешние по отношению к системе источники и адресаты данных,
логические функции, потоки данных и хранилища данных, к которым
осуществляется доступ.
Диаграмма потоков данных (data flow diagram, DFD) — один из основных
инструментов структурного анализа и проектирования информационных систем,
существовавших до широкого распространения ​UML​. Несмотря на имеющее место в
современных условиях смещение акцентов от структурного к
объектно-ориентированному подходу к анализу и проектированию систем,
«старинные» структурные нотации по-прежнему широко и эффективно
используются как в бизнес-анализе, так и в анализе информационных систем.


8. Поведенческие модели в Verilog

Поведенческие модели – это модели высокого уровня, которые описывают


алгоритм фунционирования устройства без детализации на логические вентили
или пути передач данных. Строятся на базе операторов : initial, always.

Процедурные операторы назначения сигнала используются в конструкциях initial


и alway, и применяются применяются для для присвоения присвоения значений
значений переменным переменным класса register: reg, integer, real, realtime и
time.

Управление задержкой (delay control), использует использует символ символ #;


Управление событием (event control), использует символ @ ;
Управление значением (level-sensitive timing
control), использует оператор wait.
Событие – это изменение значения
регистра или цепи. Существует четыре
типа
событийного управления порядком
выполнения операторов:

обычное событийное управление (regular event control),


➢​

именное событийное управление (named event control),


➢​

событийное управление по ИЛИ (event OR OR control)


➢​

с использованием оператора цикла repeat.


➢​
9. Управление процессом выполнения операторов в Verilog
(условный оператор, операторы выбора, циклы).
С лк ///////////////////////
Операторы:
➢​ IF
➢​ Case (case, casez, cazex)
➢​ Циклы
• ​for
•​while
•​repeat
•​forever
/////////////////
С инета//////////////////
Условный оператор предназначен для организации ветвящихся структур в
поведенческих блоках (always и initial). Его синтаксис представлен ниже:
if Логическое_Выражение
begin
// Блок_операторов_1
End
Else
Begin
// Блок_операторов_2
End
Если Логическое_Выражение равно «ИСТИНА», то выполняется
Блок_операторов_1, в противном случае – выполняется Блок_операторов_2.
Следует помнить, что в языке Verilog, подобно языку Си, логическое значение
«ИСТИНА» соответствует 1, а логическое значение «ЛОЖЬ» соответствует 0.
Таким образом, существует возможность в записи условного оператора вместо
логических выражений использовать арифметические выражения. В этом случае
Блок_операторов_2 выполняется, если результатом вычисления
арифметического выражения будет 0, при любом другом результате вычисления
арифметического выражения (не обязательно 1) будет выполняться
Блок_операторов_1.
Синтаксис оператора выбора case в языке Verilog представлен ниже
При выполнении оператора выбора case производится сравнение
Выражения с Альтернативами в порядке их следования. Для первой
встретившейся Альтернативы, включающей значение, совпадающее со
значением Выражения, выполняется соответствующий оператор или блок
операторов, а последующие альтернативы при этом игнорируются. Если
Выражение не входит ни в одну из Альтернатив, то выполняется
Оператор_по_умолчанию.
Цикл «пока» в языке Verilog описывается ключевым словом while.
Синтаксис цикла «пока» представлен ниже:
Пример использования цикла while:
В данном примере тело цикла является сложным оператором, состоящим
из двух простых операторов ($display(”i = %d”, i) и i = i + 1), заключенных в
операторные скобки begin … end. Условие выхода помещается в круглых скобках
сразу после ключевого слова while. По окончании рассматриваемого фрагмента
программы сигнал i получит значение 10. Т.е. тело цикла будет выполняться,
пока выполняется условие. Если условие становится ложным, то выполнение
цикла, прекращается.
10. Блокирующие и неблокирующие операторы, внутриоператорный
контроль.
(​
http://sensi.org/~svo/verilog/assignments/​очень полезный сайт)

Неблокирующее присваивание

Неблокирующее присваивание обозначает, что ко входу регистра в левой части


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

Блокирующее присваивание

Блокирующее присваивание, заклеймленное некорыми как «медленное», в


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

always @(posedge clk) begin

x = x + 1;

y = y + 1;

end

always @(posedge clk) begin

x <= x + 1;

y <= y + 1;

end

дадут один и тот же результат. Оба выражения выполнятся одновременно, в


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

В то же время, следующая запись являет собой что-то новое:


always @(posedge clk) begin

x = x + 1;

y = x;

end

Здесь ​
x увеличится на 1, а ​y примет значение ​x + 1​. Чтобы записать это выражение
неблокирующими присваиваниями, потребовалась бы такая запись:

always @(posedge clk) begin

x <= x + 1;

y <= x + 1;

end

Цепочку блокирующих присваиваний можно рассматривать как одно большое


выражение. Еще пример:

y <= 3*((input_value >> 4) + center_offset);

или:

y = input_value >> 4;

y = y + center_offset;

y = 3 * y;
Эти две записи эквивалентны. Но вторую запись нельзя понимать как
последовательную цепочку вычислений. Это верно лишь в том смысле, что всё
выражение действительно выстраивается в схему, в которой сначала отрезаются
4 младших разряда, результат и второй операнд идут на вход сумматора, а выход
сумматора отдается умножителю на три. Так это представляется в электрической
схеме и человек для удобства нарисует эту схему слева направо и читать он ее
будет последовательно. Но в получившейся схеме все это выражение выполняется
непрерывно,​ так же как и в предыдущей записи с неблокирующим
присваиванием. Запись результата в регистр, как и следовало ожидать,
происходит по фронту тактового импульса.
11. Операторы assign/deassign и force/release
12. Реализация подпрограмм в Verilog. Задачи и функции.
(​
https://docs.google.com/document/d/14w8UdSupoIr_m8x6Ax5t1gwHzcOoDjW22T3tvh
B_GYE/edit​) лк, там примеры, синтаксис
Как и в любом другом языке программирования подпрограммы позволяют
разбивать большой код на отдельные фрагменты. Verilog располагает двумя
типами подпрограмм: задачи (task) и функции (function). Основная цель функции
– сформировать значение, которое может быть использовано в выражении. Она
всегда возвращает одно значение и должна иметь хотя бы один входной аргумент.
В задаче допускается генерирование значений нескольких выходных переменных
или вообще ни одного, она может не иметь и входных аргументов. Задачи
допускают применение задержек, временного и событийного управления,
функции – нет, поэтому в функциях не должно быть операторов, начинающихся с
символов #, @ или wait. Для декларации аргументов в подпрограммах
используются такие же ключевые слова, как и для описания портов модулей.
Параметр identifier описывает имя функции или
задачи. Для функции – это также имя
возвращаемого значения, для которого
параметром range_of_type задается тип и размер.
Если последний пропущен, то по умолчанию
функция возвращает однобитовое значение
типа reg. Можно также присвоить возвращаемому
значению тип: integer, real, realtime или time. Аргументы
задач, объявленные как inout и out, могут
использоваться только в процедурных
операторах и должны быть: переменной
регистрового класса (reg, integer, real, realtime или time),
памятью, конкатенацией регистров
переменных памяти, элементом или
диапазоном регистрового вектора.
Входые аргументы функции могут быть
описаны одним из двух способов. В первом
случае после имени функции ставится точка с
запятой. После этого следует описание
одного или нескольких входных портов
(старый стиль). Во втором случае описание
входов записывается в круглых скобках,
через запятую после имени функции (новый
стиль). Пример определяет функцию getbyte, с
использованием первого (листинг 6.1 а)) или
второго (листинг 6.1 б)) способа описания
входных параметров. Аналогичным образом
определяются входные параметры и для зада,
что иллюстрируется листингом на примере
задачи my_task, которая имеет пять аргументов:
два входных, два выходных и один
двунаправленный.

13. Библиотека PLI. Структура и область применения.


(​
https://docs.google.com/document/d/1uQnzDZz2frk6FhxLi1r-q42yXntcKZJ6wVe-aIW
GnRk/edit​ ) лк
Программный интерфейс PLI (рис. 8.1) используется для:
– создания дополнительных системных задач и функций: задач мониторинга,
генерации тестов, отладки фрагментов программы;
– построения различных программных приложений таких как трансляторы, анализ
временных параметров;
– получения информации о проекте: иерархия, связи;
– специальных и пользовательских форм управления выводом информации;
– создания подпрограмм, формирующих тестовые последовательности;
– создания любого основанного на Verilog программного приложения.
PLI позволяет пользователю читать и модифицировать структуры внутренних
данных, получать доступ к среде моделирования.
Проектировщик пишет собственную пользовательскую системную задачу, применяя
PLI-подпрограммы. Она подключается к программе моделирования, которая
обрабатывает ссылку на задачу, зная место расположения соответствующей функции на
языке Си.
Для иллюстрации механизма использования PLI представлен пример создания
элементарной пользовательской системной задачи $welcome_task и функции
$my_function. При вызове данная задача выводит сообщение “You are welcome!".
14. Синтез цифровых схем на основе языков описания аппаратуры.

Синтез – это процесс преобразования модели проекта RTL-уровня в оптимизированную модель


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

Типичные этапы выполнения процесса синтеза изображена на рис

общие принципы выполнения синтеза:


1. HDL -модель должна быть представлена на RTL-уровне. Поведение устройства описывается с точностью до
такта синхросигнала. В общем случае, любая конструкция, которая моделирует работу устройства на каждом такте
функционирования RTL-схемы, может быть синтезирована.
2. Не допускается использование конструкций, включающих временные параметры.
3. Не допускается применения механизмов инициализации средствами HDL-языка, только с помощью внешних
входов, например, сброса и установки.
4. Рекомендуется явно указывать размер (разрядность) данных.
Самое главное, при написании кода очень важно, чтобы проектировщик представлял, из каких аппаратных
элементов будет состоять его проект, как он будет реализован в аппаратуре. Под аппаратными элементами
подразумевается стандартный набор компонентов: триггеры, регистры, сумматоры, вентили, мультиплексоры,
дешифраторы.

Синтезируемые конструкции в Verilog

15. Особенности синтеза типов данных Verilog

Verilog позволяет нам описывать поведение простых логических


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

Для этих целей используются блоки функционального или


поведенческого описания схемы в виде блоков always. В таких
always-блоках имеется возможность описывать поведение схемы, т.е.
задавать определенные последовательности сменяемых состояний,
что подразумевает наличие в схеме тактируемой памяти, которая
обновляется только по приходу определенного тактового сигнала. В
остальное время (при неактивном тактовом сигнале) память должна
сохранять свое состояние в течение долгого времени
Чувствительность к фронтам сигнала появляется у триггеров и
регистров, когда в список чувствительности блока always включено
ключевое слово либо posedge, либо negedge, приводящее к
чувствительности либо к переднему, либо к заднему фронту
соответственно. На самом деле это не совсем верное высказывание,
так как особенности схемосинтеза требуют появления в списке
чувствительности двух (и более) этих ключевых слов, если
потребуется наличие у регистра дополнительного входа для его
асинхронного (нетактируемого) сброса
16. Синтез последовательностных устройств.

17. Особенности синтеза блокирующих и неблокирующих


операторов. Влияние блокирующих операторов на появление
триггеров.

Значительно сложнее ситуация в блоках поведенческого


присвоения/назначения (always). В этих блоках имеются два вида
присвоений: (так называемые) блокирующие и неблокирующие
(blocking и non-blocking). Они привлекаются к работе с помощью
знаков соответственно “=” и “<=”. Практически все разъяснения
работы этих операторов опираются на сложные описания того, как
«симулятор обрабатывает и выполняет неблокирующие процедурные
назначения». Безусловно, понимать то, как средства моделирования и
оценки временных задержек, базирующиеся на анализе Verilog-кода,
очень важно, но это никак не помогает логику работу
схемосинтезатора.

В грубом приближении блокирующие и неблокирующие присвоения


олицетворяют создание параллельного и последовательного
включений регистров соответственно. Сначала рассмотрим простой
Verilog-код, в котором смешаны все виды присвоений для четырех
регистров. Чтобы схемосинтезатор не слишком «увлекался»
оптимизацией схемы, все интересующие нас регистры (a, b, c, d) через
непрерывные присвоения подключены к выходным портам (Outa,
Outb, Outc, Outd). Выходные порты для схемосинтезатора всегда
являются «ценными и незаменимыми» и никогда им не сокращаются.

Пример 31. Verilog-код для иллюстрации блокирующих и


неблокирующих присвоений

module Blocking_ass

( input clk,

input [2:0] a_in,

output [2:0] Outa,

output [2:0] Outb,

output [2:0] Outc,

output [2:0] Outd);

reg [2:0] a;

reg [2:0] b;

reg [2:0] c;

reg [2:0] d;

assign Outa= a; assign Outb= b;

assign Outc= c; assign Outd= d;

always@(posedge clk)

begin

a=a_in; b=a; c=b; d=c; //1 случай

//a<=a_in; b=a; c=b; d=c; //2 случай


//a<=a_in; b<=a; c=b; d=c; //3 случай

//a<=a_in; b<=a; c<=b; d=c; //4 случай

//a<=a_in; b<=a; c<=b; d<=c; // аналогично 4 случаю

end

endmodule

Если подытожить все приведенные сведения о блокирующих и


неблокирующих присвоениях, то можно сказать следующее.
Блокирующие присвоения – это действительно полностью
параллельные действия на параллельной шине, как это и ждал наш
радиолюбитель от цифровой схемотехники. Не очень понятным
является название такого присвоения, но для удобства запоминания
его можно запомнить как «блокировка последовательного
соединения». Другой вариант для запоминания блокирующего
присвоения: оно блокирует задержку на такт между правой и левой
сторонами присвоения, такую привычную для всех языков
программирования процессоров. Неблокирующие присвоения – это
тоже параллельные действия, но на некой последовательной шине.

Иными словами, при последовательном соединении регистров все


действия между всеми отдельно взятыми парами регистров (выходом
56 предшествующего и входом последующего) происходят
одновременно. Разумеется, не нужно полагать, что эти пары регистров
должны быть простыми проводами. Если вместо проводов установить
комбинационные схемы, выполняющие математические функции, то
можно получить некий конвейер вычислений, по которому движется
цифровой поток. В такой трактовке легко понять, что в любом наборе
присвоений b<=a, c<=b и т.д. все выражения справа от присвоения
относятся к одному своему времени t+1, а слева от присвоения – к
одному, но уже другому своему времени t.
18. Этапы процесса имплементации при реализации моделей на
CPLD и FPGA.

СPLD (Complex Programmable Logic Device)

FPGA (Field Programmable Gate Array)

19. Временные параметры комбинационных и


последовательностных элементов. Время установки и хранения.

20. Вычисление максимальной рабочей частоты устройства на


основе временных характеристик его компонентов.
21. Модели задержек в Verilog.

1)Распределенная задержка

2)Сосредоточенная задержка

3)Задержка пути

Rise – задержка задержка при переключении переключении выхода


0->1

Fall – задержка при переключении выхода 1->0

Turn-off – задержка при переключении выхода 0->z

29. Динамические массивы, очереди.

Динамические массивы
Динамические массивы декларируются без указания их длинны с пустыми скобками []. Для выделения
памяти под массив используется оператор new[]. Если указать дополнительное имя в операторе new[], то
значение будет копироваться в новый элемент (листинг 16.13.).
Листинг 16.13. Использование динамических массивов
int dyn[], d2[]; // Пустой динамический массив

initial begin
dyn = new[5]; // Выделение памяти для 5 элементов
foreach (dyn[j])
dyn[j] = j; // Инициализация элементов
d2 = dyn; // Копирование динамического массива
d2[0] = 5; // Изменение копии
$display(dyn[0],d2[0]); // Вывод обоих значений (0 и 5)
dyn = new[20](dyn); // Увеличение размера и копирование
dyn = new[100]; // Выделение памяти для 100 новых
// элементов. Старые значения при этом будут потеряны
dyn.delete; // Удаление всех элементов
end

Функция ​$size возвращает размер массивов с фиксированной и динамической длинами. Динамические


массивы имеют несколько специальных методов, таких как ​delete и ​size (Листинг 2.4.)​. Последний
возвращает размер только для динамических массивов. Прототипы методов ​size​и ​delete:
function int size();
function void delete();

Листинг 2.14. Использование методов ​size​и ​delete.


int j = addr.size;
addr = new[addr.size()*4](addr); // увеличенный в 4 раза массив addr

int ab[]= new[N]; // Создание временного массива в N элементов


... // Использование ab
ab.delete; // Удаление содержимого массива
$display ("%d", ab.size); // Вывод 0

Можно задавать массивы фиксированной длины, не указывая их размер непосредственно. В этом


элементы массива получают значения в момент его декларации, а компилятор вычисляет размер
массива, исходя из количества указанных элементов.
В листинге 2.15 массив ​mask​получает значение девяти 8-ми битовых масок.
Листинг 2.15. Использование динамического массива для создания списка
bit [7:0] mask[] = ’{8’b0000_0000, ’b0000_0001,8’b0000_0011,
8’b0000_0111,8’b0000_1111, 8’b0001_1111,
8’b0011_1111, 8’b0111_1111,8’b1111_1111};

Можно присваивать между собой значения массивов с динамической и фиксированной длиной, если они
имеют одинаковый тип данных int и одинаковую длину. Если присвоить массив с фиксированной
длиной динамическому массиву, то компилятор вызовет конструктор new[], чтобы выделить память под
массив, а затем скопирует в нее новые значения.

Очереди
Новый тип данных. Упрощает процесс поиска и сортировки в структурах. Такой же быстрый, как и
массивы с фиксированной длиной; многообразен как связанный список. Подобно динамическим
массивам очереди могут увеличиваться и уменьшаться в размере во время моделирования, также можно
легко добавлять и удалять любые элементы, как это показано в следующем примере. Декларируется как
массив, но с использованием символа доллара $ (листинг 2.16). Размер массива может быть указан, но
это необязательно.
Очередь может сохранять данные типа данных, которые он получил в момент декларации очереди
int q1 [$]; //пустая очередь, без указания размера
int q2 [$] = {1,2,3,5,8}; // безразмерная очередь,
// инициализируется пятью элементами

typedef struct {int a, b; bit flag} packet_t;


packet_t q3 [$:16]; //очередь размером в 16 элементов

Листинг 2.16. Операции над очередями


int j = 1, b[$] = {3,4}, q[$] = {0,2,5}; // {0,2,5} Инициализация очереди

initial begin
q.insert(1, j); // {0,1,2,5}, помещает 1 перед 2
q.insert(3, b); // {0,1,2,3,4,5}, помещает значение b[] после 2
q.delete(1); // {0,2,3,4,5} , удаляет элемент с индексом 1
// Следующие операторы самые быстрые
q.push_front(6); // {6,0,2,3,4,5}, добавляет элемент в начало списка
j = q.pop_back; // {6,0,2,3,4} j = 5
q.push_back(8); // {6,0,2,3,4,8}, добавляет элемент в конец списка
j = q.pop_front; // {0,2,3,4,8} j = 6
foreach (q[i])
$display(q[i]);
end

Когда создается очередь, SystemVerilog выделяет дополнительное пространство, которое позволяет


быстро добавлять элементы. Для очередей нет необходимости использовать оператор new[]. Если в
очередь добавляется больше элементов, превышая, таким образом, размер свободного пространства,
SystemVerilog автоматически выделяет дополнительное место. Быстро выполняется операция
чтения(pop) и записи(push) с начала или конца массива. Для ее выполнения используется фиксированное
время. Больше времени потребуется на выполнение этих операций из середины массива, особенно, если
он имеет большую длину, поскольку симулятору будет необходимо сместить половину массива.
В очередь можно копировать значения массивов фиксированной и динамической длины.
Очереди могут быть использованы для создания моделей FIFO, LIFO или других типов памяти с
последовательным доступом.
2.5.1. Методы очередей
В SystemVerilog для очередей определены встроенные методы:
1) ​ — Метод добавляет данный элемент в указанную индексную позицию.
insert(value) ​

2) ​
delete(value)​— Метод удаляет элемент из описанной индексной позиции.

3) ​
push_front(<value>)​— Добавляет значение в новую позицию в начале очереди.

4) ​
push_back(<value>)​— Добавляет значение в новую позицию в конце очереди.

5) ​
variable = pop_front()​— Удаляет первый элемент из очереди и возвращает в его значение.

6) ​
variable = pop_back()​— Удаляет последний элемент из очереди и возвращает в его значение.

7) ​
insert(<index>,<value>) — Изменяет значение элемента в указанной позиции без изменения размера
очереди.
8) ​
variable = <queue_name>[<index>] — Возвращает значение элемента из указанной позиции без изменения
размера очереди.
9) ​
variable = size()​— Возвращает текущее количество элементов в очереди.

Запись в заполненную очередь и чтение из пустой очереди приведет к ошибке времени выполнения.
2.5.2 Параметризируемая память FIFO
Этот пример (листинг 2.17.) иллюстрирует использование очередей и их методов в моделях,
разработанных с помощью SystemVerilog.
Пример представляет собой простейшую память FIFO, которая создается с применением очередей в
SystemVerilog. Размер и разрядность данных FIFO контролируется параметрами. Помещение в и
удаление данных из памяти FIFO, точно также как и проверка элементов в FIFO реализуется с помощью
методов очередей.
Очередь также моделируется с помощью SystemVerilog типов с двумя состояниями int и bit. Где
содержание шины и разрешение цепи не важны, типы с двумя состояниями улучшат
производительность.
В среде верификации очереди могут использоваться для сбора информационных данных.
Листинг 2.17. Модель FIFO с использованием очередей
`timescale 1ns / 1ns

module FIFO #(parameter int DEPTH = 31, parameter int WIDTH = 8) (


input bit [WIDTH-1:0] DATA,
input bit CLK, RSTb, WENb, RENb,
output bit FULL, EMPTY,
output bit [WIDTH-1:0] Q);

bit [WIDTH-1:0] mem [$:DEPTH];


// Запись данных в FIFO
always @(posedge CLK, negedge RSTb)
if (RSTb == 0)
mem = '{};
else if (WENb == 0 && mem.size() < DEPTH)
mem.push_back(DATA);

// Чтение данных из FIFO


always @(posedge CLK)
if (RENb == 0 && mem.size() > 0)
Q <= mem.pop_front();

// FIFO control flags


assign EMPTY = (mem.size() == 0) ? 1 : 0;
assign FULL = (mem.size() == DEPTH) ? 1 : 0;
endmodule

30. Ассоциативные массивы.


Для хранения в памяти больших объемов данных SystemVerilog предлагает ассоциативные массивы,
сохраняющие входы для разреженных матриц. На рис.16.5 представлен массив, сохраняющий
информацию в позициях 0:3, 42, 1000, 4521, 200 000. Для создания его модели при использовании
ассоциативных массивов потребуется значительно меньше памяти, чем если бы это был для массив с
фиксированной и динамической длинной из 200 001 элементов.

Рис. 2.6. Пример ассоциативного массива


Синтаксис декларации ассоциативного массива:
data_type array_id [index_type];

где ​
data_type – тип данных элементов массива, может быть любой тип, разрешенный для массивов
фиксированной длины; array_id – имя декларируемого массива; index_type – тип данных, используемый
для индекса или ​*​. Символ ​* - означает, что массив может индексироваться любым целым выражением.
При указании типа индекса, используемое выражение для индексации должно быть только указанного
типа.
Листинг 2.18. представляет декларирование, инициализацию и использования ассоциативных массивов.
Их декларация выполняется с помощью группового символа ​[*]​.
Листинг 2.18. Декларирование, инициализация и использование ассоциативных массивов
initial begin
logic [63:0] assoc[*], idx = 1;
// Инициализация разрозненными значениями
repeat (64) begin
assoc[idx] = idx;
idx = idx << 1;
end
// Перебор всех индексов с помощью foreach
foreach (assoc[i])
$display("assoc[%h] = %h", i, assoc[i]);
// Перебор всех индексов с помощью функция
if (assoc.first(idx))
begin // Получение первого индекса
do
$display("assoc[%h]=%h", idx, assoc[idx]);
while (assoc.next(idx)); // Получение следующего индекса
end
// Поиск и удаление первого элемента
assoc.first(idx);
assoc.delete(idx);
end
Ассоциативный массив assoc состоит из рассеянных элементов 1, 2, 4, 8, 16, и т.д. Для перебора
элементов не удобно использовать оператор ​for​, для этой цели лучше подходит операция ​foreach​. Для
более точного контроля можно также использовать функции ​first и ​next в цикле ​do...while​. Эти функции
принимают значение индекса, в качестве аргумента и возвращают 0 или 1, в зависимости от наличия
элементов в массиве. Ассоциативные массивы могут быть также адресованы с помощью строкового
индекса. В примере (листинг 2.19) выполняется чтение пар имен из файла в ассоциативный массив. Если
попытаться прочитать элемент, который еще не существует, то SystemVerilog вернет 0 или X для типов
для типов с 2-мя или 4-мя значениями, соответственно. Функция exists может быть использования для
проверки существования элемента.
Листинг 2.19. Использование ассоциативного массива с индексом string
// Входной файл содержит: 42 min_address 42 max_address 1492
int switch[string], min_address, max_address;

initial begin
int i, r, file;
string s;
file = $fopen("switch.txt", "r");
while (! $feof(file)) begin
r = $fscanf(file, "%d %s", i, s);
switch[s] = i;
end
$fclose(file);
// Получение минимального адреса, по умолчанию 0
mid_address = switch["min_address"];
// Получение максимального адреса, по умолчанию 1000
if (switch.exists("max_address"))
max_address = switch["max_address"];
else
max_address = 1000;
end

Ассоциативный массив может быть сохранен симулятором в виде дерева. Кроме того, допускается
использовать дополнительные служебные сигналы (overhead), когда необходимо сохранить массив с
далеко стоящими значениями индексов, например для пакетов, адресуемых 32-разрядным или
64-разрядным значениями данных.
2.6.1 Методы ассоциативных массивов
Определено несколько методов для анализа и манипулирования элементами ассоциативных массивов.
1) Функция ​ ()
num​
function int num();

Метод ​
num​() возвращает число элементов в ассоциативном массиве. Возвращает 0, если массив пустой.
int item[*];
item[ 2’b3 ] = 1;
item[ 16’hffff ] = 2;
item[ 4b’1000 ] = 3;
$display( "%0d entries\n", imem.num ); // prints "3 entries"

2) Функция ​ ()
delete​
function void delete( [input index] );

где index –необязательный параметр индекса соответсвующего типа для массива.


Если индекс описан, то метод ​delete() удаляет элемент, соответствующий индексу, если удаляемый
элемент не существует, никаких предупреждений сгенерировано не будет. Если индекс не описан, то
метод удаляет все элементы массива.
int map[ string ];
map["systemverilog"] = 1;
map["is"] = 2;
map["not easy"] = 3;
map.delete("sad"); // удаляет элемент с индексом "sad"
map.delete; // удаляет все элементы ассоциативного массива map

3) Функция exists()
function int exists( input index );

где index – значение соответствующего типа индекса массива.


Функция exists() проверяет существование в заданной индексной позиции элемента. Возвращает 1 если
элемент есть, иначе – 0.
if ( map.exists("SystemVerilog"))
map["SystemVerilog"] += 1;
else
map["SystemVerilog"] = 0;

4) Функция ​ ()
first​
function int first( ref index );

где index – значение соответствующего типа индекса массива.


Метод ​
first() передает через ссылочный параметр index значение первого (наименьшего) индекса массива.
Возвращает 0, если массив пуст, и 1, в противном случае.
string s;
if (map.first( s ))
$display("First entry is : map[ %s ] = %0d\n", s, map[s]);

5) Функция ​
last()
function int last( ref index );

где index – значение соответствующего типа индекса массива.


Метод ​last() передает через ссылочный параметр index значение последнего (максимального) индекса
массива. Возвращает 0, если массив пуст, и 1, в противном случае.
string s;
if (map.last( s ))
$display("Last entry is : map[ %s ] = %0d\n", s, map[s]);

6) Функция ​
next()
function int next( ref index );

где index – значение соответствующего типа индекса массива.


Метод ​
next() выполняет поиск элемента, чей индекс больше заданного индекса. Если в массиве есть еще
элементы, то выполняется связь с соседним элементом и функция возвращает 1. Иначе индекс не
меняется, и функция возвращает 0.
string s;
if (map.first( s ))
do
$display("%s : %d\n", s, map[s]);
while (map.next( s ));

7) Функция ​
prev()
function int prev( ref index );

где index – значение соответствующего типа индекса массива.


Метод prev() выполняет поиск элемента, чей индекс меньше заданного индекса. Если в массиве есть еще
элементы, то выполняется связь с соседним элементом и функция возвращает 1. Иначе индекс не
меняется и функция возвращает 0.
string s;
if (map.last( s ))
do
$display("%s : %d\n", s, map[ s ]);
while ( map.prev( s ) );

Если аргумент, передаваемый в любой из четырех методов перемещения для ассоциативных массивов,
меньше чем размер соответствующего индекса, то функция возвращает значение -1. Поэтому
необходимо копировать столько данных, сколько может подойти аргументу.
Например:
string aa[*];
byte ix;
int status;
aa[1000] = "a";
status = aa.first(ix);
// status is –1
// ix is 232 (последние 8 значащих битов от 1000)

31. Новые процедурные блоки в SV.


Чтобы исключить неоднозначность блока always, в SystemVerilog добавлены три его специальных
версии: ​ , ​always_latch​и ​always_ff​.
always_comb​

Эти три блока имеют бесконечную природу моделирования, как и оператор always. Однако они
улучшают стиль описания аппаратуры и предназначены для разработки синтезируемых моделей
RTL-уровня.
Программным средствам нет необходимости определять тип аппаратуры из контекста описания
операторов. Если стиль записи операторов не удовлетворяет характеру блока, то система разработки
генерирует предупреждение.
3.1.1. Комбинационный процедурный блок.
В отличие от обычного блока ​always​, блок ​always_comb не требует указания специального списка
чувствительности. Он создается автоматически и в него включаются все переменные, значение которых
читается в процедурном блоке, за исключением тех, что объявлены в нем. В следующем примере
оператор a​lways_comb​будет выполняться при каждом изменении переменных a или b.
always_comb
if (!mode)
y = a + b;
else
y = a - b;

Листинг 3.1 представляет пример использования ​always_comb для описания комбинационных фрагментов
схемы контроллера.
Листинг 3.1. Модель контроллера с использованием ​always_comb
module controller (output logic read, write,
input instr_t instruction,
input logic clock, resetN);

enum {WAITE, LOAD, STORE} State, NextState;

always @(posedge clock, negedge resetN)


if (!resetN) State <= WAITE;
else State <= NextState;

always_comb begin
case (State)
WAITE: NextState = LOAD;
LOAD: NextState = STORE;
STORE: NextState = WAITE;
endcase
end

always_comb begin
read = 0; write = 0;
if (State == LOAD && instruction == FETCH) read = 1;
else if (State == STORE && instruction == WRITE) write = 1;
end
endmodule

Существует различие в моделировании между ​always_comb и ​always​. Первый выполняется один раз в
нулевой момент моделирования, после активации всех процедурных блоков.
Verilog-2001 предлагает использовать в списке чувствительности блока always групповой символ ​@* или
@(*)​. Однако это только более короткая запись и не дает таких преимуществ как ​always_comb​. (листинг
3.2).
Листинг 3.2. Различие между ​always_comb​и ​always​.
always @* begin // Обозначает @(data)
a1 = data << 1;
b1 = decode ();
...
end

always_comb begin // Обозначает @(data, sel, c, d, e)


a2 = data << 1;
b2 = decode ();
...
end

function decode; // Функция не имеет входов


begin
case (sel)
2’b01 : decode = d | e;
2’b10 : decode = d & e;
default : decode = c;
endcase
end

3.1.2. Последовательностный процедурный блок ​always_latch​.


Второй специализированный оператор always - always_latch. ​Это процедурный блок, моделирующий
триггер-защелку.
always_latch
if (enable) q <= d;

Пример с листинга 3.3 использует ​always_latch​. 5-битовый счетчик, выполняющий счет от 0 до 31. Вход
ready контролирует начало выполнения счета. Вход ​ ready имеет значение 1 короткий период времени.
Поэтому, когда ready переключается в 1, модель сохраняет это значение для внутреннего сигнала enable.
Защелка сохраняет значение 1, пока счетчик не достигнет значения 31, и затем выполняется очищение
сигнала enable, не давая счетчику выполняться снова, до следующего поступления сигнала ready.
Листинг 3.3. Использование процедурного блока ​always_latch
module register_reader (input clk, ready, resetN,
output logic [4:0] read_pointer);
logic enable; // внутренний сигнал, разрешающий счет
logic overflow; // внутренний флаг переполнения счетчика

always_latch begin // latch the ready input


if (!resetN)
enable <= 0;
else if (ready)
enable <= 1;
else if (overflow)
enable <= 0;
end

always @(posedge clk, negedge resetN) begin // 5-bit counter


if (!resetN)
{overflow,read_pointer} <= 0;
else if (enable)
{overflow,read_pointer} <= read_pointer + 1;
end
endmodule

3.1.3. Последовательностный процедурный блок


Блоки, описывающие последовательностную логику могут моделироваться с помощью ​always_ff​.
always_ff @(posedge clock, negedge resetN)
if (!resetN) q <= 0;
else q <= d;

Все сигналы в списке чувствительности должны быть записаны с указанием фронта ​posedge или ​negedge​.
Событийный контроль внутри блока не допускается.
Блоки ​ , ​always_latch​и ​always_ff​являются синтезируемыми.
always_comb​
32. Структурные модели: соединение портов. Псевдонимы цепей.
Соединение портов
В Verilog использовались соединение сигналов по позиции или по имени. SystemVerilog предлагает три
упрощенные формы описания связей портов:
1) ​
.name​(“dot-name”) соединение портов;

2) ​
.* ​(“dot-star”) соединение портов;

3) С помощью интерфейсов.
Выполняет соединение портов с совпадающими по имени сигналами, упрощая выражение Verilog
.data(data) ​(листинг 4.4 а)) до ​
.data в SystemVerilog (листинг 4.4 б)). Соединение ​
.* (листинг 4.4
в))связывает все порты с соответствующими по имени сигналами.
Листинг 4.4 Примеры соединений портов
а) //Verilog стиль
// Копия модуля с именным соединением портов
pc_stack pcs (
.program_counter(program_counter),
.program_address(program_address),
.clk(clk),
.resetN(resetN),
.instruct_reg(instruct_reg),
.data_bus(data_bus),
.status_reg(status_reg));
prom prom (.dout(program_data),
.clk(clk), .address(program_address));

б) // SystemVerilog стиль с использованием .name


// Копия модуля с соединением портов .name
pc_stack pcs (.program_counter, .program_address,
.clk, .resetN, .instruct_reg, .data_bus, .status_reg);
prom prom (
.dout(program_data),
.clk, .address(program_address));

в) // SystemVerilog стиль с использованием .*


// Копия модуля с соединением портов .*
pc_stack pcs (.*);
prom prom (.*, .dout(program_data),
.address(program_address));

4.3. Псевдонимы цепей


Псевдонимы цепей позволяют использовать два различных имени для обращения к одной линии.
wire clock;
wire clk;
alias clk = clock;

В следующем примере множественных псевдонимов


wire reset, rst, resetN, rstN;
alias rst = reset;
alias reset = resetN;
alias resetN = rstN;

все имена указывают на одну линию. Не важно, в каком порядке выполняется связь имен, поскольку
оператор ​
alias не является оператором присваивания. Псевдонимы могут быть использованы только для
цепей и должны быть одного типа.
wire [3:0][7:0] n2;
alias n2 = n1; // n1 и n2 имеют размер 32-бита
wire [39:0] d_in;
wire [7:0] crc;
wire [31:0] data;
alias data = d_in[31:0]; // Цепь размером в 32 бита
alias crc = d_in[39:32]; // Цепь размером в 8 бита

Использование псевдонимов с конструкциями ​.name и ​.* позволяет значительно упростить запись связей
между портами в иерархических проектах.
Листинг 4.5 представляет структурную SystemVerilog-модель устройства с рис. 4.3 использованием .*
связи портов без использования псевдонимов. Этот же пример с применением псевдонимов приведен в
листинге 4.6.

Рис 4.3 Структурная схема устройства


Листинг 4.5. Использование соединения портов SystemVerilog .* без псевдонимов
module chip
(input wire master_clock,
input wire master_reset, ...);

wire [31:0] address, new_address, next_address;

ROM i1 ( .*, // Обозначает .address(address)


.data(new_address),
.clk(master_clock) );

program_count i2 ( .*, // Обозначает .next_address(next_address)


.jump_address(new_address),
.clock(master_clock),
.reset_n(master_reset) );

address_reg i3 ( .*, // Не соответствует ни каким соединениям


.next_addr(next_address),
.current_addr(address),
.clk(master_clock), .rstN(master_reset) );
endmodule

module ROM (output wire [31:0] data,


input wire [31:0] address,
input wire clk);
...
endmodule

module program_count (output logic [31:0] next_address,


input wire [31:0] jump_address,
input wire clock, reset_n);
...
endmodule

module address_reg (output wire [31:0] current_addr,


input wire [31:0] next_addr,
input wire clk, rstN);
...
endmodule

Листинг 4.6. Использование соединения портов SystemVerilog .* с применением псевдонимов


module chip
(input wire master_clock,
input wire master_reset, ...);

wire [31:0] address, data, new_address, jump_address,


next_address, next_addr, current_addr;

alias clk = clock = master_clock;


alias rstN = reset_n = master_reset;
alias data = new_address = jump_address;
alias next_address = next_addr;
alias current_addr = address;

ROM i1 ( .* );
program_count i2 ( .*);
address_reg i3 ( .* );
endmodule

module ROM (output wire [31:0] data,


input wire [31:0] address,
input wire clk);
...
endmodule

module program_count (output logic [31:0] next_address,


input wire [31:0] new_count,
input wire clock, reset_n);
...
endmodule

module address_reg (output wire [31:0] address,


input wire [31:0] next_address,
input wire clk, rstN);
...
endmodule

33. Интерфейсы.Modport.
Группирование сигналов с помощью modport
В предыдущих примерах применялось соединения без указания направления сигнала в интерфейсе.
Однако один и тот же сигнал в различных модулях может играть различную роль. Например, в одном
случае он будет входом, а в другом - выходом. Тогда такой порт внутри интерфейса объявляется через
modport​.

Конструкция ​modport позволяет группировать сигналы и описывать их направление, что дает


возможность выполнять дополнительную проверку при передаче информации и исключить связанные с
этим ошибки (листинг 5.7.).
Modportопределяет направление порта, которое соответствует модулю. Интерфейс может содержать
любое число деклараций ​modport​, каждая из которых описывает как один или несколько модулей
рассматривают сигналы интерфейса.
Листинг 5.7.Декларации ​modport​в интерфейсе
interface chip_bus (input logic clock, resetN);
logic interrupt_request, grant, ready;
logic [31:0] address;
wire [63:0] data;

modport master (input interrupt_request,


input address, output grant, ready,
inout data, input clock, resetN);

modport slave (output interrupt_request,


output address, input grant, ready,
inout data, input clock, resetN);
endinterface

Конструкции ​modport не содержат тип данных или разрядность порта. Эта информация указывается при
декларации сигналов интерфейса. В modport описывается только направление портов: ​input​, ​output​, ​inout
или r​ef​.
SystemVerilog предлагает два способа указать применяемый ​modport​: в операторе реализации копии
компонента и в декларации портов модуля при его определении. Оба стиля спецификации являются
синтезируемыми.
Первый стиль, в операторе реализации копии модуля копия интерфейса соединяется с портом модуля,
при этом следующим образом может быть указан определенный ​modport​:
<имя копии модуля>.<имя modport>

Например:
chip_bus bus; // Копия интерфейса
primary i1 (bus.master); // Использования резима master

Пример (листинг 5.8) иллюстрирует соединение двух модулей вместе с интерфейсом chip_bus. Модуль
primary соединяется с интерфейсом в режиме master, а модуль secondary – в режиме slave:
Листинг 5.8: Выбор режима modport в момент создания копии модуля
chip_bus (​
interface ​ input logic ​clock, resetN);
modport ​master (...);
modport ​slave (...);
endinterface

primary (​
module ​ pins);
interface ​ // Абстрактный порт интерфейса
...
endmodule

secondary (chip_bus pins); // Определенный порт интерфейса


module ​
...
endmodule

module ​chip (​input logic ​ clock, resetN);


chip_bus bus (clock, resetN); // Копия интерфейса
primary i1 (bus.master); // Использование режима modport
secondary i2 (bus.slave); // Использование режима slave
endmodule

Если ​
modport​указывается в декларации интерфейса модуля, то применяется синтаксис:
<имя интерфейса>.<имя modport>

Например для листинга 5.8:


module secondary (chip_bus.slave pins);
...
endmodule

В этом случае должно быть указано явное имя интерфейса. Тогда в операторе реализации копии модуля
приводится только имя копии интерфейса без описания режима ​modport​. Пример (листинг 5.10:)
представляет запись режима ​modport​в декларации портов модуля.
Листинг 5.10. Выбор режима modport в момент описания портов модуля
interface chip_bus (input logic clock, resetN);
modport master (...);
modport slave (...);
endinterface

module primary (chip_bus.master pins); // Использование режима master


...
endmodule

module secondary (chip_bus.slave pins); // Использование режима slave


...
endmodule

module chip (input logic clock, resetN);


chip_bus bus (clock, resetN); // Копия интерфейса
primary i1 (bus); // Использование режима master
secondary i2 (bus); // Использование режима slave
endmodule

Таким образом, выбрать ​modport можно либо для копии модуля, либо при описании его портов, но
никогда одновременно (листинг 5.11). В один момент допускается указывать тип ​modport только в одной
из этих двух конструкций. Если направление портов не описаны, то по умолчанию все порты
интерфейса имеют режим ​inout и тип ​ref​. Кроме этого в интерфейсе можно объявлять внутренние
сигналы, являющиеся локальными для него.
Листинг 5.11. Использование интерфейса с конструкциями ​modport
// ------------------- Определение интерфейса -----------------
interface main_bus (input logic clock, resetN, test_mode);
wire [15:0] data;
wire [15:0] address;
logic [ 7:0] slave_instruction;
logic slave_request, bus_grant, bus_request;
logic slave_ready, data_ready;
logic mem_read, mem_write;
modport master (inout data,
output address,
output slave_instruction, slave_request,
output bus_grant,
output mem_read, mem_write,
input bus_request,
input slave_ready, data_ready,
input clock, resetN,
input test_mode
);
modport slave (inout data,
inout address,
output mem_read, mem_write,
output bus_request, slave_ready,
input slave_instruction, slave_request,
input bus_grant,
input data_ready,
input clock, resetN
);
modport mem (inout data,
output data_ready,
input address,
input mem_read, mem_write
);
endinterface
//---------------------- Модуль верхнего уровня --------------------
module top (input logic clock, resetN, test_mode);
logic [15:0] program_address, jump_address;
logic [ 7:0] instruction, next_instruction, data_b;
main_bus bus ( .* ); // Копия интерфейса
processor proc1 (.bus(bus.master), .* );
slave slave1 (.bus(bus.slave), .* );
instruction_reg ir ( .* );
test_generator test_gen (.bus(bus), .* );
dual_port_ram ram (.bus(bus.mem), .* ,
.data_b(next_instruction) );
endmodule

34. (меняй билет) ​


Методология верификации с применением управляемого
ограничениями псевдослучайного генерирования тестов.
Но полностью случайный выбор воздействий не всегда может быть полезен. Зачастую
необходимо произвести некоторые ограничения на рандомизацию, выполненные с
учетом наложенных правил или ограничений, определенных пользователем. Далее мы
будем называть такой процесс тестирования «рандомизацией с ограничениями». И язык
SystemVerilog позволяет выполнить такое тестирование. Что это дает? Это дает
возможность автоматически генерировать случайные воздействия с учетом наложенных
ограничений, и, как следствие, автоматизировать создание новых тестов. Суть таких
тестов состоит в том, что при обработке тестбенча дополнительные тесты могут быть
автоматически получены из относительно небольшого набора основных тестов:
разработчик просто изменяет испытательные параметры или добавляет и
совершенствует ограничения. Повышение производительности такого теста показано на
рис. 3.

Рис. 3. Автоматизированная проверка намного более эффективна, чем написание


«прямых» тестов вручную

В случае применения «прямого» подхода к тестированию время, требуемое для


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

Встраивая рандомизацию не только в значения передаваемых или принимаемых


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

SystemVerilog имеет все языковые конструкции по проверке, необходимые для


испытаний по случайным воздействиям, выполненных с учетом наложенных
ограничений. Для SystemVerilog уже накоплен достаточно большой набор рекомендаций
о том, как установить такую среду проверки и как использовать
объектно-ориентированные методики программирования, а также написать компоненты
проверки так, чтобы их можно было повторно использовать для полного набора тестов
для проекта и даже для многократного использования в следующих проектах.
35. Многоуровневая среда верификации.
36. Программы. Этапы моделирования систем в SV.

Системная спецификация

➢Реализуется технической маркетинговой группой

▪ Системная архитектура – Проектирование сверху вниз

➢Н бе ольшая высококвалифицированная группа инженеров.

▪ Блоковый уровень – Плоский стиль (Flat Design Style)

➢Несколько инженерных команд, работающих параллельно

▪ Интеграция – Проектирование снизу вверх

▪ Системная верификация – Симуляция, эмуляция и прототипирование

▪ Завершение проекта – Физические & временные параметры

▪ Выпуск продукции – Производство & Маркетинг

Тут ссылка на лекцию, там намного конкретнее по каждому пункту. (оставил это
на выбор отвечающего)

37. Блок синхронизации. Описание событий через блок синхронизации.


// единственное что нашел в лекциях
Триггеры с синхронизацией по фронту.
Модель D-триггера, управляемая передним фронтом представлена листингом 11.13. а), результат синтеза –
рис. 11.15
а). В интерфейс устройства включает следующие порты: С – вход синхронизации, D – вход данных, Q –
выход данных.
Следующая модель (листинг 11.13 б), рис. 11.15 б)) представляет устройство с синхронизацией по заднему
фронту и с использованием сигнала асинхронного сброса CLR.
Последние две модели иллюстрируют применение синхронной установки S (листинг 11.13 в), рис. 11.15 в))
и сигнала
разрешения синхронизации CE (листинг 11.13 б), рис. 11.15 б)).
Листинг 11.13. Verilog-модель D-триггера

а) // D-триггер
б) // С асинхронным сбросом
в) // С синхронной установкой
г) // С сигналом разрешения синхронизации

а) б)

в) г)
Рис. 11.15. Синтез моделей D-триггера с синхронизацией по фронту

38. Создание и использование классов с псевдослучайным


генерированием значений полей.
Тест может быть создан с помощью генератора псевдослучайных значений
(constrained-random tests (CRT)). Прямой тест (directed test) находит
запланированные ошибки, в то время как CRT-тест обнаруживает случайные
(незапланированные) ошибки:
Модификатор ​rand означает, что данные могут формироваться, получая
новое значение или повторяя какое-либо из предыдущих. При
применении модификатора ​randc значение может появиться повторно,
только после того как будут перебраны все возможные комбинации.
Листинг 13.1. Пример класса с псевдослучайными значениями
class Packet;
// Случайные переменные
rand bit [31:0] src, dst, data[8];
randc bit [7:0] kind;
// Ограничение значений src
constraint c {src > 10; src < 15;}
endclass

Packet p;
initial begin
p = new; // Создание пакета
assert(p.randomize());
transmit(p); // Какая-то пользовательская функция, которая передает
значения дальше
end
Управляющие генерацией константы записываются в конструкцию constraint и
группируется с помощью фигурных скобок { }. Такие скобки используются,
потому что в них заключается декларативный код, а не процедурный, где
необходимо применять begin...end. Если в процессе генерации значения
возникнет ошибка, то функция randomize возвращает 0, что отслеживается с
помощью прямой ассерции.
39. Создание и использование ограничений для
псевдослучайного генерирования значений​. Условные

ограничения​, ​выбор значений из массива​ , приоритеты
значений.(​приоритеты не найдены!)
Класс Stim (Листинг 13.2) содержит класс с псевдослучайной генерацией
значений и несколькими блоками констант. ​Первый блок c_stim задает
диапазон возможных значения для len. ​Множество значений может быть
создано также с помощью оператора inside, как для переменной ​dst​. При этом
задается условие, что данное ограничение будет действовать, если бит
congestion_test равен 1.
SystemVerilog создает множество возможных значений и выбирает из
него значения с равной вероятностью, если нет других условий ограничения
переменной.
class Stim;
const bit [31:0] SRC_ADDR = 42;
typedef enum {READ, WRITE, CONTROL} stim_t;
randc stim_t type; // Переменная типа перечисления
rand bit [31:0] len, src, dst;
bit congestion_test;
constraint c_stim {
len < 1000;
len > 0;
src inside {0, [2:10], [100:107]};
if (congestion_test) {
dst inside {[SRC_ADDR - 100 : SRC_ADDR + 100]};
}
}
endclass

______________________________

Пример правильного и неправильного описания констант с помощью


операторов отношения представлен листингом 13.3. Как видно из примера,
переменная может появляться в нескольких условиях, при этом допускается в
выражении использовать только один оператор отношения (<, <=, ==, >= или >).
Листинг 13.3 Переменные ограничения описаны в определенном порядке
class bad;
rand bit [15:0] a, b, c;
constraint good {0 < a; // Правильное описание
a < b;
b < c;}
constraint bad {0 < a < b < c;} // Неправильно, приведет к ошибке
endclass
__________________________________________________________________________
_____

Допускается выполнять выбор значения из набора, представленного массивом.


integer fives[4] = '{ 5, 10, 15, 20 };
rand integer v; constraint c3 { v inside {fives}; }
В примере (листинг 13.5) таким образом осуществляется выбор дня
недели ​DAYS_E ​из массива choices, имеющего тип enum. Список choices
может быть легко изменен перед генерированием значения. В случае
использования оператора randc, система генерирования переберет все
возможные значения перед повтором уже присвоенных.
Листинг 13.5 Выбор из множества возможных значений
class Days;
typedef enum {SUN, MON, TUE, WED,
THU, FRI, SAT} DAYS_E;
DAYS_E choices[$];
rand DAYS_E choice;
constraint cday {choice inside {choices};}
endclass

Days days;
initial begin
days = new;
days.choices = ‘{Days::SUN, Days::SAT};
assert (days.randomize());
$display("Random weekend day %s\n", days.choice.name);
days.choices = ‘{Days::MON, Days::TUE, Days::WED, Days::THU,
Days::FRI};
assert (days.randomize());
$display("Random week day %s", days.choice.name);
end

Если необходимо добавить или удалить данные из динамического


массива, следует осторожно использовать оператор inside, из-за его
особенностей. Например, существует множество значений, заданное
очередью, из которых должно быть выбрано и удалено только одно. Это
потребует создать N ограничений, где N–количество элементов,
остающихся в очереди.
Обычно ограничения активны все время. В ситуациях, когда необходимо
отключить действие ограничений на некоторое время SystemVerilog предлагает
две формы ​условных операторов​: импликации “->” и “if-else”.
// Блок ограничений с оператором импликации
class BusOp;
...
constraint c_io {
(io_space_mode) -> addr[31] == 1’b1;
}
Если присутствуют выражения для обеих ветвей условия true-false, то
предпочтительней использовать оператор if-else.
// Блок ограничений с оператором if-else
class BusOp;
...
constraint c_len_rw {
if (op == READ)
len inside {[BYTE:LWRD]};
else
len == LWRD;
}
В блоках ограничения, поскольку это декларативный код, используются
фигурные скобки {}. В процедурном коде применяется “begin...end”.

40. Псевдослучайное генерирование значений: управление блоками


ограничений.
Класс может содержать несколько блоков ограничений. Например, одна
группа управляет разрядностью данных транзакции, а другая – длиной
транзакции. Тогда для того, чтобы включать или отключать ограничения,
можно применять метод constraint_mode(). В формате <имя
ограничения>.constraint_mode(), этот метод управляет единственным
ограничением.
Листинг 13.11 Использование constraint_mode
class Packet;
rand int length;
constraint c_short {length inside {[1:32]}; }
constraint c_long {length inside {[1000:1023]}; }
endclass

Packet p;
initial begin
p = new;
// Создание длинных данных long, отключаа ограниничение
для коротких short
p.c_short.constraint_mode(0);
assert (p.randomize());
transmit(p);
// Создание коротких данных, через отключение всех
ограничени
// а затем разрешается использования ограничений для
коротких данных
p.constraint_mode(0);
p.c_short.constraint_mode(1);
assert (p.randomize());
transmit(p);
end

41. Методы определения полноты тестирования. Виды coverage.


Существует два типа метрик покрытия:
1) code coverage - автоматически формируется на основе кода проекта
(​оценивает полноту тестирования с точки зрения проверки операторов,
описывающих модель устройства.)
2) functional coverage – (​использует пользовательскую метрику,
определяющую, какая часть спецификации проекта, указанной в плане
тестирования, была проверена.)

Ключевые аспекты функционального покрытия:


· определяются пользователем и не могут быть автоматически получены из кода

проекта.
· основываются
​ на спецификации проекта и поэтому не зависят от реального
кода модели или структуры.
Конструкции функционального покрытия в SystemVerilog осуществляют
следующие действия:
· Сбор
​ информации о покрытии переменных и выражений, а также о
перекрестном покрытии между ними.
·​ Автоматическое
​ или пользовательское создание корзин покрытия
·​ Фильтрацию
​ условий на нескольких уровнях
· Использование
​ событий и последовательностей для автоматического
переключения сбора значений (sampling) покрытия
·​ Покрытие
​ вызовов процедур и очередей.
· Управление
​ и регулирование формирования покрытия с помощью
директивы

42. Создание и использование covergroup.

Конструкция ​covergroup ​наследует спецификацию модели покрытия.


Спецификация ​covergroup​может включать следующие компоненты:
· События
​ синхронизации, которые определяют моменты считывания значений
для точек
·​ Множество
​ точек покрытия (coverage points)
·​ Перекрестное
​ покрытие между точками покрытия
·​ Необязательные
​ формальные аргументы
·​ Опции
​ покрытия
Конструкция ​covergroup ​является определенным пользователем типом. После
декларации она может иметь несколько копий, которые могут быть
реализованы в различных контекстах. Конструкция ​covergroup ​может быть
записана в пакете, модуле, программе, интерфейсе или классе.

Синтаксис декларации covergroup:


covergroup ​covergroup_identifier [ ​( ​[ tf_port_list ] ​) ​] [ coverage_event ] ​;
{ coverage_spec_or_option }
endgroup ​[​
:​covergroup_identifier ]

coverage_spec_or_option ::=
{attribute_instance} coverage_spec | {attribute_instance} coverage_option ​;

coverage_option ::=
member_identifier ​= ​expression | ​type_option.​member_identifier = expression
option.​

coverage_spec ::=
cover_point | cover_cross

coverage_event ::=
clocking_event | ​
@@( ​block_event_expression ​)
block_event_expression ::=
block_event_expression ​or ​block_event_expression | ​begin ​hierarchical_btf_identifier
|​ hierarchical_btf_identifier
end ​
Синтаксис реализации объекта covergroup:
covergroup_variable_identifier ​= new ​[ ​( ​list_of_arguments ​) ​]
(сори за некст ответ, но я хз что тут может быть важно , а что нет)

Если указано событие синхронизации, то оно определяет моменты времени для сбора
значений с точек покрытия. В противном случае, пользователь должен задать
синхронизацию с помощью процедурных операторов. Это можно выполнить через
встроенный метод sample(). Опция strobe может быть использована для изменения
моментов считывания данных. Когда опция strobe не установлена, значения точек
покрытия считываются по событию экземпляра модели покрытия. Если событие
синхронизации встречается несколько раз за время моделирования, то значения точек
покрытия должны быть также прочитаны несколько раз.
В качестве альтернативы для синхронизации может быть использовано выражение
блокового события, считывание контрольных точек покрытия может быть выполнено в
начале или конце работы заданного именного блока, задачи, функции или метода
класса. Если выражение блокового события начинается с ключевого слова begin, за
которым следует иерархический идентификатор, обозначающий именной блок, задачу,
функцию или метод класса, то событие переключаются сразу до выполнения первого
оператора указанных конструкций. Выражение блокового события, которое описано с
помощью ключевого слова end, выполняется сразу после последнего оператора
указанной конструкции.
Группа покрытия ​covergroup ​может содержать одну или несколько точек покрытия,
которые задаются переменной или выражением. С каждой точкой покрытия
связывается множество корзин определяемых значениями, которые записываются в
корзины, или передаваемыми значениями. Корзины могут быть явно определены
пользователем или автоматически созданные с помощью инструментов моделирования.
{ red, green, blue } color;
enum ​
covergroup ​ g1 @(​posedge ​clk);
c: ​
coverpoint ​ color;
endgroup

Предыдущий пример представляет группу покрытия g1 с одной точкой покрытия,


связанной с переменной color. Значения переменной color записываются по указанному
событию синхронизации - положительному фронту clk.
Поскольку точка покрытия не имеет явно определенных корзин, то будут
автоматически создаты три корзины, по одной для каждого значения типа
перечисления. Группа покрытия может также содержать описание перекрестных точек
между двумя или больше точками покрытия или переменными. Допускается
использование любых комбинаций более чем двух переменных или предварительно
декларированных точек покрытия.
Например:
enum ​{ red, green, blue } color;
[3:0] pixel_adr, pixel_offset, pixel_hue;
bit ​
covergroup ​ g2 @(​posedge ​clk);
Hue: ​coverpoint ​pixel_hue;
Offset: ​
coverpoint ​pixel_offset;
AxC: ​ color, pixel_adr;
cross ​ // пересечение 2 переменных
// (неявное задание точек покрытия
coverpoint)
all: ​ color, Hue, Offset;
cross ​ // пересечение 1 переменной и 2 точек
покрытия coverpoints
endgroup

В примере создается группа покрытия g2, которая содержит две точки покрытия и две
декларации пересечения покрытий cross. Явно заданные точки покрытия с метками
Offset и Hue определены для переменных pixel_offset и pixel_hue. Далее для
переменных color и pixel_adr задается анализ совместного покрытия.
В группе покрытия могут присутствовать одна или несколько опций, контролирующих
и регламентирующих структуру и способ сбора информации о покрытии. Опции
покрытия могут быть заданы для группы покрытия в целом или для отдельных ее
пунктов.

17.2 Использование covergroup в классах


Размещение группы покрытия в классе, обеспечивает простейший способ для создания
подмножества покрытия свойств класса. Интеграция покрытия в класс обеспечивает
интуитивный и ясный механизм для определения модели покрытия связанной с
классом. Например,
class ​
xyz;
bit ​
[3:0] m_x;
int ​
m_y;
bit ​
m_z;

covergroup ​
cov1 @m_z; // встроенная covergroup
coverpoint ​m_x;
coverpoint ​m_y;
endgroup

function new​
();
cov1 = ​new​;
endfunction
endclass

В примере значения полей ​m_x ​and ​m_y ​класса ​xyz ​собираются ​при каждом изменении
переменной ​m_z​. Если группа покрытия определяется внутри класса и нет явной
декларации переменной данной группы в пределах класса, то она будет иметь такое же
имя, которое группа покрытия получила при описании. Таким образом, в предыдущем
примере переменная cov1 соответствует группе покрытия.
Встроенная в класс группа покрытия может определять модель покрытия для
защищенных и локальных свойств класса без возможности ее переопределения в классе
наследнике. Класс может иметь несколько групп покрытия. Следующий пример
представляет класс ​MC​, содержащий две группы покрытия.
​ C;
class M
logic ​
[3:0] m_x;
local logic ​
m_z;
bit ​
m_e;

covergroup ​
cv1
posedge ​clk);
@(​
coverpoint ​m_x;
endgroup
covergroup ​
cv2
@m_e ;
coverpoint ​m_z;
endgroup
endclass

В ​covergroup ​cv1 является членом класса, значение переменной m_x собирается по


каждому положительному фронту сигнала clk. Значения локальной переменной m_z
собираются другой группой ​covergroup ​cv2. Каждая группа покрытия собирает
значения по различным событиям синхронизации.

43. Точки покрытия, перекрестное покрытие, корзины значений для покрытия.

Перекрестное покрытие

Конструкция cross в SystemVerilog позволяет комбинировать значения двух или более точек покрытия в
группу. Оператор cross допускает только точки покрытия или простые имена переменных. Если
необходимо использовать выражения, иерархические имена или переменные, следует описать для них
метку с помощью coverpoint, а затем уже использовать эту метку в операторе cross.

В примере 17.28 создаются точки покрытия для tr.kind и tr.point. Затем эти точки используются для
создания условий перекрестного покрытия для всех возможных комбинаций. Для точки покрытия kind
SystemVerilog создаст 16 корзин (auto[0]...auto[15]), для точки покрытия port – 8 корзин(auto[0]...auto[7]).
Тогда для перекрестного покрытия этих двух точек будет создано 128(8х16) корзин (<auto[0], auto [0]>,
<auto[0], auto

[1]>, …).

Листинг 17.28 Базовое перекрестное покрытие

class Transaction;

rand bit [3:0] kind;

rand bit [2:0] port;

endclass

Transaction tr;

covergroup CovPort;

kind: coverpoint tr.kind; // Создания точки

покрытия kind
port: coverpoint tr.port // Создания точки покрытия

port

cross kind, port; // Пересечение kind и port

endgroup

Случайный testbench создает 200 транзакций и формирует отчет о покрытии. Обратите внимание, даже
если были генерируются все возможные значения для kind и port, около 1/8 части всех возможных
комбинаций не было достигнуто.

«Группа покрытия» может содержать одну или несколько точек покрытия.

«Точка покрытия» определяет интегральное выражение, которое требуется покрыть. Оценка выражения
точки покрытия происходит, когда выбирается «группа покрытия».

44. Последовательности (Sequence).

Простейшее последовательное поведение является линейными. Линейная последовательность - это


конечный список булевых выражений SystemVerilog в линейном порядке возрастания времени. Говорят,
что линейная последовательность имела место на конечном интервале следующих друг за другом
синхрособытий, если первое логическое выражение принимает значение «истина» на первом
синхрособытии, второе логическое выражение получает значение «истина» при поступлении второго
синхрособытия, и так далее, вплоть до последнего логического выражения, которое должно принять
значение «истина» на последнем синхрособытии. Примером простой линейной последовательности
является булево выражение. Его присутствие на указанном синхрособытии означает, что оно принимает
в значение «истина» в данный момент времени.

Более сложное последовательное поведение в SystemVerilog описывается с помощью


последовательностей (sequence). Последовательность – это упорядоченная совокупность булевых
выражений, которые включают множество нулей, конечное или бесконечно число линейных
последовательностей. Если хотя бы одна из линейных последовательностей, входящих во множество,
имеет место в конечном интервале последовательных синхрособытий, то считается, что
последовательность произошла на данном интервале времени.

45. Свойства(Property)

Свойство определяет поведение проекта. Свойство может быть использовано для верификации, как
спецификация предположения, проверка или покрытие. Для того чтобы использовать поведение для
верификации, используются операторы assert, assume, или cover. Декларация свойства само по себе не
производит результат.

Результат вычисления свойств либо «истина», либо «ложь». Существует семь видов свойств:
последовательность (sequence), отрицание (negation), дизъюнкция(disjunction), конъюнкция (conjunction),
if...else, импликация (implication) и создание копии (instantiation).

а) свойство-последовательность является последовательностью,

вычисляемой в значение «истина», если и только если существует непустое совпадение


последовательности. Последовательность, которая допускает пустое совпадение не разрешается для
свойств. Поскольку совпадение существует, если и только если существует первое совпадение.
Вычисление такого свойства - полное трансформирования его sequence_expr в first_match
(sequence_expr). Как только первое совпадение sequence_expr обнаруживается, вычисление свойства
рассматривается как «истина» и нет необходимости в поиске других совпадение.

б) Свойство-отрицание, имеет вид

not property_expr

При каждой попытке вычисления свойств вычисляется property_expr.

Ключевое слово not обозначает, что при вычислении свойства возвращается противоположное значение
для property_expr. Таким образом, если property_expr примет значение «истина», то not property_expr
соответствует значению «ложь», и если property_expr оценивается как «ложь», то not property_expr будет
«истина».

c) Дизъюнкция свойств имеет вид

property_expr1 or property_expr2

Свойство соответствует значению «истина» если и только если как минимум одно из свойств
property_expr1 и property_expr2 «истина».

d) Конъюнкция свойств имеет форму

property_expr1 and property_expr2

Свойство оценивается как «истина», если и только если свойства

property_expr1 и property_expr2 имеют значение «истина»

e) Свойство « if...else» имеет форму

if (expression_or_dist) property_expr1

или

if (expression_or_dist) property_expr1 else property_expr2

Свойство в первом случае оценивается как «истина», если и только если либо expression_or_dist
принимает значение «ложь», либо property_expr1 – «истина». Во втором случае свойство возвращает
значение истина, когда либо expression_or_dist и property_expr1 возвращают значение «истина», либо
expression_or_dist вычисляется в «ложь», а property_expr2 – «истина».

f) Свойство-импликанта имеет форму

sequence_expr |-> property_expr

или

sequence_expr |=> property_expr

Копия именного свойства может быть использована в выражении property_expr or property_spec. В


общем случае, использование копии является законным, если тело именного свойства property_spec
может заменить его копию в property_expr or property_spec, подставляя реальные параметры, вместо
формальных аргументов, и игнорируя локальные переменные. Так, например, если копия именного
свойства используется как операнд property_expr в любом формирующем свойство операторе, то это
свойство не должно включать выражение disable iff.

46. Прямые и параллельные ассерции.

Прямые ассерции используются в процедурном коде в любом месте в конструкциях initial и always.

Синтаксис прямых асерций:

assert ( expression ) [[ pass_stmt ] else fail_stmt ];

Выражение вычисляется сразу, при выполнении оператора. Операторы в ветвях pass и fail, если они
присутствуют, выполняются мгновенно после вычисления выражения.

Процедурный код testbench может проверять значения сигналов проекта, переменных и выполнять
необходимые действия, в случае обнаружения ошибки. Например, в проекте выполняется проверка
запроса шины request и ожидается, что ответ будет получен двумя тактами позже. Тестирование этой
ситуации можно выполнить с помощью оператора if.

Процедурные асерции имеют ветви then- и else-, при необходимости, в них могут быть добавлены
операторы.

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

Параллельные ассерции отличаются от прямых двумя важными аспектами.

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

Синтаксис assert property ( sequential_expr_or_property )

[[ pass_stmt ] else fail_stmt ]

Ключевое слово property отличает параллельную ассерцию от прямой ассерции. Последовательное


выражение вычисляется с использованием значений сигналов и позволяет операторам pass/fail
связываться с testbench. Поскольку ассерции являются внутренней частью языка, эти операторы могут
использовать SystemVerilog в полном объеме для переключения событий, записи информации о
покрытии, или других воздействий на порядок кода верификации, включая вызов Си-функций.

Отдельный язык ассерций эффективен только в режиме “read-only”,позволяет наблюдать за поведением


проекта, но не допускает элементов воздействия ни на проект, ни на testbench.

Параллельные ассерции описывают поведение, которое охватывает большой промежуток времени. В


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

47. Виртуальные интерфейсы.

Виртуальный интерфейс позволяет отделить абстрактные модели и тестовые программы от


реальных сигналов.

Использование виртуального интерфейса позволяет объектам testbench обращаться к


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

Свойства виртуального интерфейса:

● Может использоваться для декларации аргументов функций и задач, наряду с другими


типами данных
● Должен быть инициализирован до его использования
● Значение по умолчанию виртуального интерфейса равно null
● Внутри виртуальных интерфейсов могут быть использованы процедурные присвоения,
непрерывные - нет
● Управлять данными класса net можно только через блок синхронизации
● Виртуальный интерфейс может быть свойством класса и инициализироваться в
процедуре или конструкторе.

Синтаксис

virtual ​[​interface​] interface_identifier [parameter_value_assignment]

[.modport_identifier] list_of_virtual_interface_decl ;

list_of_virtual_interface_decl::=

variable_identifier[=interface_instance_identifier]

{ , variable_identifier [ = interface_instance_identifier ] }

48. Управление потоками: семейство блоков fork-join

Формы блока fork-join

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

join_any. Родительский процесс блокируется, пока хотя бы один любой указанный в нем процесс не
завершит свое выполнение.
join_none. Родительский процесс продолжает свое выполнение параллельно с процессами
порожденными в fork. Порожденные процессы не начнут выполнение, пока родительский поток не
выполнит блокирующий оператор.

UVM

49. Классификация компонентов в UVM: квазистатические,

динамические.

Все компоненты UVM можно разделить на квазистатические и динамические. К квазистатическим


относят те, что формируют структуру среды верификации, которая создается один раз в момент
инициализации моделирования, например driver, monitor, scoreboard, sequencer, agent, environment.
Динамические компоненты, такие как транзакции и sequence, - формируются и изменяются в течение
процесса моделирования и представляют собой тестовые последовательности, подаваемые на входы
устройства, результаты моделирования, формируемые устройствами.

50.Интерфейсные UVC, перечислить и привести характеристики.

Для реализации процесса генерирования тестовых данных в UVM определены следующие классы:

1. Sequence item. (Данные, представляющие транзакцию)

2. Sequence (Последовательность транзакций)

3. Sequencer (Управляет последовательностью или последовательностями, запускает


генерирование данных)

4. Driver (Получает данные от секвенсора и передает их в DUT)

51.Системные UVC, перечислить и дать характеристики.

System UVCs - могут использовать интерфейсные, модульные и другие системные UVCs. Могут не
иметь своих собственных агентов и использовать другие UVCs, формируя на их основе среду
верификации большего размера для целых систем, например cpu, мосты (bridge), мобильные телефоны

52.Структура классов UVM

53.Фазы подготовки и выполнения модели.

1. build_phase(). Создает дочерние компоненты и их конфигурация. Должна начинаться с команды


super.build().

2. connect_phase(). Устанавливаются связи между компонентами, создаются TLM соединения.


3. end_of_elaboration_phase(). Завершающая настройка testbench, выполняется проверка связей
между компонентами и инициализация.

4. start_of_simulation_phase(). Подготовка DUT к моделированию

5. run_phase(). Фаза моделирования DUT.

6. extract_phase(). Извлечение данных из различных точек среды верификации (assert и coverage).

7. check_phase(). Проверка всех неожиданных условий в среде верификации, состояний регистров,


Работа scoreboard.

8. report_phase(). Вывод сообщений о результатах моделирования

9. final_phase() - Завершение моделирования

54. Механизмы конфигурации UVM

Механизмы конфигурации

● Factory

● Config db(Configuration)

Базовый класс для UVM классов данных и иерархии, и содержит основные методы автоматизации
работы с классами библиотеки UVM. Его наследники uvm_sequence_item описывает транзакцию, а
uvm_sequence - последовательнось транзакций. Это так называемая динамическая составляющая среды
верификации. Все составляющие среду верификации компоненты построены на основе класса
uvm_component, который умеет управлять фазами моделирования. Это статические элементы среды
верификации. Конфигурация среды верификации осуществляется с помощью классов uvm_resource_db и
uvm_config_db. Для моделирования состояния регистров и памяти в проекте предназначены классы
uvm_reg, uvm_reg_block, uvm_reg_file, uvm_reg_field и uvm_mem.

55.TLM-интерфейсы в UVM

Преимущества использования TLM-интерфейсов

1) Более высокий уровень абстракции

2) Повторно используемое plug&play соединение

3) Надежность в использовании

4) Меньше кода

5) Легкость реализации

6) Более быстрое моделирование


7) Связь с SystemC

8) Могут быть использованы для разработки эталонных моделей

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

UVM предлагает интерфейсы и сокеты для соединения компонентов на уровне транзакций.


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

TLM - моделирования уровня транзакций - это стиль моделирования для построения абстрактных
моделей компонентов и систем. TLM основан на транзакциях - объектах, которые содержат различные
относящиеся к протоколу данных для абстрактного представления процессов нижнего уровня. TLM
представляет семейство абстрактных уровней описания, начиная с циклического уровня моделирования
(cycle-accurate modeling). Наиболее известные уровни TLM: циклический(cycle-accurate), с
аппроксимированным временем(approximately-timed), loosely-timed, без времени и token-level.

TLM-1 и TLM-2.0 - два TLM-моделирования систем, которые были разработаны как промышленные
стандарты для построения моделей уровня транзакций. Оба были разработаны для SystemC и
стандартизованы с TLM Working Group от Open SystemC Initiative (OSCI). TLM-1 доведен до стандарта
в 2005, а TLM-2.0 в 2009. OSCI объединился с Accellera в 2013 и текущим стандартом SystemC является
IEEE 1666-2011.

TLM-1 и TLM-2 имеют общую базу, и часть разработчиков TLM-1, продолжили потом работать и над
TLM-2, однако они совершенно разные. TLM-1 - система для передачи сообщений. Интерфейсы или не
используют время, или основываются на подсказках для выбора времени. Ни один из интерфейсов не
использует точного временного описаний. Хотя TLM-2.0 также может передавать данные и
синхронизацию между независимыми процессами, он главным образом создавался для
высокопроизводительного моделирования memory-mapped bus-based систем. Обя эти подмножества
были реализованы в SV и доступны через UVM.

56. Sequence, sequencer, driver

UVM Sequencer контролирует поток тестовых наборов (UVM Sequence Item),генерируемый одним или
несколькими последовательностями(UVM Sequence). Служит арбитром для контроля за потоком
транзакций от нескольких последовательностей.

Секвенсер - улучшенный генератор, управляет данными, передаваемыми на выполнение драйверу. По


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

• возможность фиксировать порядок между данными в определенной пользователем sequence, что


формирует более структурный и значимый шаблон данных;

• разрешение изменения времени в reusable-сценариях;

• поддержка применения декларативных и процедурных ограничений в одном сценарии;

• системный уровень синхронизации и управления несколькими интерфейсами.

1.2.7 UVM Sequence

Последовательность в UVM - это объект, определяющий процесс генерирования тестовых


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

Для функционирования последовательности требуется sequencer, один sequencer может управлять


несколькими последовательностями.

1.2.9 UVM Driver

Драйвер эмулирует логику, управляющую DUT, он получает от секвенсора отдельные транзакции (UVM
Sequence Item) и передает их на интерфейс DUT. Таким образом драйвер изменяет уровень абстракции,
преобразуя транзакции(transaction-level) во входные сигналы (pin-level). От включает TLM-порт, по
которому тестовые наборы от секвенсора поступают в драйвер, и подключается к интерфейсу DUT, для
управления сигналами на его входах.

Драйвер и секвенсор формируют трафик на входах DUT, но они не применяются для оценки
функционального покрытия и выполнения формальной верификации (чекеров и ассерций), эти задачи
делегируются мониторам.

57. UVM Factory.

Для целей верификации бывает удобно создавать одну общую структуру среды верификации, а потом
“на лету” заменять одни компоненты и данные на другие. Для этого в методологии верификации
адаптировали механизм фабрик(factory), существовавший до этого в программировании. В UVM
фабрики можно использовать только с классами, производными от uvm_object и uvm_component.

Существует три базовых шага для применения factory:

1) Регистрация
2) Создание

3) Переопределение

Регистрация

Тип класса регистрируется для factory в момент своего создания. Для этого используются
предопределенные скрипты (которые уже были рассмотрены ранее).

`uvm_component_utils(class_type_name)

`uvm_object_utils(class_type_name)

для параметризируемых классов:

`uvm_component_param_utils(class_type_name #(params))

`uvm_object_param_utils(class_type_name #(params))

Регистрация необходим для переопределения по имени, переопределение по типу может быть


выполнена без регистрации. Листинг 3.1 представляет примеры классов uvm_object и uvm_component с
регистрацией.

Листинг 1. Пример использования макросов

class packet extends uvm_object;

`uvm_object_utils(packet)

endclass

// параметризируемый класс

class packet #(type T=int, int mode=0) extends uvm_object;

`uvm_object_param_utils(packet #(T,mode))

endclass

class driver extends uvm_component;

`uvm_component_utils(driver)

endclass

class monitor #(type T=int, int mode=0) extends uvm_component;

`uvm_component_param_utils(driver#(T,mode))
endclass

Создание

Для создания объекта или компонента в UVM рекомендуется использовать статический метод create()
вместо new().

Синтаксис :

type_name::type_id::create(string name, uvm_component parent)

Функция create() возвращает объект класса type_name. Объект переопределяется фабрикой, основываясь
на контексте определяемым полным именем родителя. Аргумент context, если он определен, заменяет
контекст родителя. Новый объект будет иметь имя листа и родителя. Классы, основанные на uvm_object,
не требуют указывать второй аргумент.

Листинг 3.2 Примеры использования функции create()

// 1

class_type object_name;

...

object_name = clss_type::type_id::create("object_name",this);

// 2

task mycomponent::run_phase(uvm_phase phase);

mytype data; // данные должны быть типа mytype или производного от него.

data = mytype::type_id::create("data");

$display("type of object is: %0s", data.get_type_name());

...

endtask

Переопределение

Переопределять классы и объекты можно основывая на строки имени или типе класса. Существует 4
метода для переопределения. Первые два меняют тип данных для определенного объекта класса или
группы объектов, если строка full_inst_path содержит специальные символы. Различие между ними
заключается в форме задания типа класса, через ссылку (uvm_object_wrapper) или строку. Тип
uvm_object_wrapper - абстрактный интерфейс к созданным объектам. Вторая пара методов заменяет
один типа класса на другой.
1. Переопределение объекта по индексу типа:

function void set_inst_override_by_type (uvm_object_wrapper original_type,

uvm_object_wrapper override_type, string full_inst_path )

Заменяет для объекта full_inst_path типа "original_type" на "override_type", где "override_type" является
наследником от "original_type".

2. Переопределение объекта по имени типа:

function void set_inst_override_by_name (string original_type_name,

string override_type_name, string full_inst_path )

Аргументы original_type_name и override_type_name представляют строковое имя типов,


зарегистрированных в factory. Все объектов уровня full_inst_path с типом "original_type_name" будут
заменены типом "override_type_name".

3. Переопределение типа через индекс типа

function void set_type_override_by_type (uvm_object_wrapper original_type,

uvm_object_wrapper override_type, bit replace = 1 )

Объект original_type может быть переопределен типом override_type.

4. Переопределение типа по имени

function void set_type_override_by_name (string original_type_name,

string override_type_name, bit replace = 1)

Объект с именем типа original_type_name может быть заменнен override_type_name.

Когда выполняется множественное переопределение, можно использовать аргумент replace, чтобы


управлять выполнением предыдущего переопределения. Если replace = 1, то переопределение будет
выполнено, иначе будет сохранено предыдущее переопределение.

Вместо этих функций могут быть использованы статические методы set_type_override и set_inst_override.
Первая функция заменяет тип класса глобально, для всех объектов в среде верификации; второй только
для заданного строкой объекта.

<orig_type>::type_id::set_type_override(<override_type>::get_type(),bit replace = 1);

Пример:

ubus_master_driver::type_id::set_type_override(ubus_new_master_driver::get_type);

<orig_type>::type_id::set_inst_override(<override_type>::get_type(), string inst_path);


Пример:

ubus_slave_monitor::type_id::set_inst_override(ubus_new_slave_monitor:: get_type(), “slaves[0].monitor”);