29 35
29 35
29 35
Динамические массивы
Динамические массивы декларируются без указания их длинны с пустыми скобками []. Для выделения
памяти под массив используется оператор 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
Очереди
Новый тип данных. Упрощает процесс поиска и сортировки в структурах. Такой же быстрый, как и
массивы с фиксированной длиной; многообразен как связанный список. Подобно динамическим
массивам очереди могут увеличиваться и уменьшаться в размере во время моделирования, также можно
легко добавлять и удалять любые элементы, как это показано в следующем примере. Декларируется как
массив, но с использованием символа доллара $ (листинг 2.16). Размер массива может быть указан, но
это необязательно.
Очередь может сохранять данные типа данных, которые он получил в момент декларации очереди
int q1 [$]; //пустая очередь, без указания размера
int q2 [$] = {1,2,3,5,8}; // безразмерная очередь,
// инициализируется пятью элементами
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
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)
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
все имена указывают на одну линию. Не важно, в каком порядке выполняется связь имен, поскольку
оператор 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, ...);
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
33. Интерфейсы.Modport.
Группирование сигналов с помощью modport
В предыдущих примерах применялось соединения без указания направления сигнала в интерфейсе.
Однако один и тот же сигнал в различных модулях может играть различную роль. Например, в одном
случае он будет входом, а в другом - выходом. Тогда такой порт внутри интерфейса объявляется через
modport.