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

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_comb, always_latch и always_ff.
Эти три блока имеют бесконечную природу моделирования, как и оператор always. Однако они
улучшают стиль описания аппаратуры и предназначены для разработки синтезируемых моделей RTL-
уровня.
Программным средствам нет необходимости определять тип аппаратуры из контекста описания
операторов. Если стиль записи операторов не удовлетворяет характеру блока, то система разработки
генерирует предупреждение.
3.1.1. Комбинационный процедурный блок.
В отличие от обычного блока always, блок always_comb не требует указания специального списка
чувствительности. Он создается автоматически и в него включаются все переменные, значение которых
читается в процедурном блоке, за исключением тех, что объявлены в нем. В следующем примере
оператор always_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_comb, always_latch и always_ff являются синтезируемыми.

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
или ref.
SystemVerilog предлагает два способа указать применяемый modport: в операторе реализации копии
компонента и в декларации портов модуля при его определении. Оба стиля спецификации являются
синтезируемыми.
Первый стиль, в операторе реализации копии модуля копия интерфейса соединяется с портом модуля,
при этом следующим образом может быть указан определенный modport:
<имя копии модуля>.<имя modport>
Например:
chip_bus bus; // Копия интерфейса
primary i1 (bus.master); // Использования резима master
Пример (листинг 5.8) иллюстрирует соединение двух модулей вместе с интерфейсом chip_bus. Модуль
primary соединяется с интерфейсом в режиме master, а модуль secondary – в режиме slave:
Листинг 5.8: Выбор режима modport в момент создания копии модуля
interface chip_bus (input logic clock, resetN);
modport master (...);
modport slave (...);
endinterface

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


...
endmodule

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


...
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. Методология верификации с применением управляемого ограничениями


псевдослучайного генерирования тестов.
35. Многоуровневая среда верификации.

Оценить