- 10.1 Различия между task и function
- 10.2 Задачи(task) и создание условий для выполнения задач
- 10.2.1 Декларации задач(task)
- 10.2.2 Вызов задач(task) и передача аргументов
- 10.2.3 Использование памяти задачи и одновременная активация
- 10.3 Завершение именованных блоков и задач
- 10.4 Функции и вызов функций
- 10.4.1 Декларации функций
- 10.4.2 Возвращение значения из функции
- 10.4.3 Вызов функции
- 10.4.4 Правила функционирования
- 10.4.5 Использование константных функций(function)

Задачи(task) и функции(function) обеспечивают возможность выполнения общих процедур из нескольких разных мест описания. Они также позволяют разбить большие процедуры на более мелкие, чтобы облегчить чтение и отладку исходных описаний. В этом пункте обсуждаются различия между задачами(task) и функциями(function), описывается, как определять и вызывать задачи и функции, а также приводятся примеры каждой из них.
10.1 Различия между task и function
Следующие правила отличают задачи(task) от функций(function):
- function должна выполняться за одну единицу времени моделирования. task может содержать операторы, управляющие временем.
- function не может включать задачу. task может включать другие задачи и функции.
- function должна иметь по крайней мере один аргумент типа input и не должна иметь аргументов типа output или inout. task может иметь 0 или более аргументов любого типа.
- function должна возвращать одно значение. task не должна возвращать значение.
Цель функции — реагировать на входное значение, возвращая одно значение. Задача может поддерживать несколько целей и может вычислять несколько значений результата. Однако, только аргументы типа output или inout передают значения результатов обратно после вызова задачи.
function используется в качестве операнда в выражении. Значение этого операнда является значением, возвращаемым функцией.
Например:
switch_bytes(old_word, new_word);
Задача switch_bytes возьмет байты в old_word, изменит их порядок и поместит измененные байты в new_word.
new_word = switch_bytes (old_word);
10.2 Задачи(task) и создание условий для выполнения задач
Задача(task) должна быть включена из оператора, который определяет значения аргументов, передаваемых задаче, и переменные, которые получают результаты. Управление должно быть передано обратно процессу включения после завершения задачи. Таким образом, если задача(task) имеет внутри себя временные элементы управления, то время включения задачи может отличаться от времени возврата управления. Задача может разрешать другие задачи, которые, в свою очередь, могут разрешать еще другие задачи, причем количество разрешенных задач не ограничено.
Независимо от того, сколько задач было включено, управление не возвращается до тех пор, пока все включенные задачи не завершатся.
10.2.1 Декларации задач(task)
Синтаксис для определения задач приведен в Синтаксис 10-1.
task_declaration ::=
task [ automatic ] task_identifier ;
{ task_item_declaration }
statement_or_null
endtask
| task [ automatic ] task_identifier ( [ task_port_list ] ) ;
{ block_item_declaration }
statement_or_null
endtask
task_item_declaration ::=
block_item_declaration
| { attribute_instance } tf_ input_declaration ;
| { attribute_instance } tf_output_declaration ;
| { attribute_instance } tf_inout_declaration ;
task_port_list ::=
task_port_item { , task_port_item }
task_port_item ::=
{ attribute_instance } tf_input_declaration
| { attribute_instance } tf_output_declaration
| { attribute_instance } tf_inout_declaration
tf_input_declaration ::=
input [ reg ] [ signed ] [ range ] list_of_port_identifiers
| input task_port_type list_of_port_identifiers
tf_output_declaration ::=
output [ reg ] [ signed ] [ range ] list_of_port_identifiers
| output task_port_type list_of_port_identifiers
tf_inout_declaration ::=
inout [ reg ] [ signed ] [ range ] list_of_port_identifiers
| inout task_port_type list_of_port_identifiers
task_port_type ::=
integer | real | realtime | time
block_item_declaration ::=
{ attribute_instance } reg [ signed ] [ range ] list_of_block_variable_identifiers ;
| { attribute_instance } integer list_of_block_variable_identifiers ;
| { attribute_instance } time list_of_block_variable_identifiers ;
| { attribute_instance } real list_of_block_real_identifiers ;
| { attribute_instance } realtime list_of_block_real_identifiers ;
| { attribute_instance } event_declaration
| { attribute_instance } local_parameter_declaration ;
| { attribute_instance } parameter_declaration ;
list_of_block_variable_identifiers ::=
block_variable_type { , block_variable_type }
list_of_block_real_identifiers ::=
block_real_type { , block_real_type }
block_variable_type ::=
variable_identifier { dimension }
block_real_type ::=
real_identifier { dimension }
Существует два альтернативных синтаксиса объявления задач(task).
Первый синтаксис должен начинаться с ключевого слова task, за которым следует необязательное ключевое слово automatic, затем имя задачи и точка с запятой, и заканчиваться ключевым словом endtask. Ключевое слово automatic объявляет автоматическую задачу, которая является реентерабельной, при этом все объявления задач распределяются динамически для каждой параллельной записи задачи. В объявлениях элементов задачи(task) может быть указано следующее:
- Входные(input) аргументы
- Выходные(output) аргументы
- Двунаправленные(inout) аргументы
- Все типы данных, которые могут быть объявлены в процедурном блоке
Второй синтаксис должен начинаться с ключевого слова task, за которым следует имя задачи и заключенный в круглые скобки список task_port_list(список объявляемых портов). task_port_list должен состоять из нуля или более task_port_item(объявляемый порт), разделенных запятыми. После закрывающей круглой скобки должна быть точка с запятой. Далее следует тело задачи, а затем ключевое слово endtask.
В обоих синтаксисах объявления портов должны иметь тот же синтаксис, что и объявления tf_input_declaration, tf_output_declaration и tf_inout_declaration(т.е. объявления входных, выходных и двунаправленных портов), как описано в Синтаксисе 10-1 выше.
Задачи без необязательного ключевого слова automatic являются статическими задачами, в которых все объявленные элементы выделяются статически. Эти элементы должны быть общими для всех параллельно выполняющихся задач. Задачи с необязательным ключевым словом automatic — это автоматические задачи. Все элементы, объявленные внутри автоматических задач, выделяются динамически для каждого вызова. К элементам автоматических задач нельзя обращаться с помощью иерархических ссылок. Автоматические задачи могут быть вызваны с помощью их иерархического имени.
10.2.2 Вызов задач(task) и передача аргументов
Оператор вызова задач(task) должен передавать аргументы в виде списка выражений, разделенных запятыми и заключенных в круглые скобки. Формальный синтаксис оператора вызова задач(task) приведен в Синтаксисе 10-2.
task_enable ::=
hierarchical_task_identifier [ ( expression { , expression } ) ] ;
Если объявление задачи(task) не имеет аргументов, список аргументов не должен быть представлен в вызове объявления задачи(task). В противном случае должен быть упорядоченный список выражений, длина и порядок которого соответствуют длине и порядку списка аргументов в объявлении задачи. Нулевое выражение не должно использоваться в качестве аргумента в утверждении вызова задачи(task).
Если аргумент в задаче объявлен как input, то соответствующее выражение может быть любым выражением. Порядок оценки выражений в списке аргументов не определен. Если аргумент объявлен как output или inout, то выражение должно быть ограничено выражением, которое допустимо в левой части процедурного присваивания (см. 9.2). Этому требованию удовлетворяют следующие элементы:
- переменные reg, integer, real, realtime и time
- Ссылки на память
- Конкатенации переменных reg, integer и time
- Конкатенации ссылок на память
- Битовая выборка и частичная выборка reg, integer и time переменных
При выполнении оператора вызова задачи(task) входные значения из выражений, перечисленных в операторе вызова, передаются в аргументы, объявленной в задаче. Выполнение возврата из задачи(task) должно передать значения из аргументов типа output и inout задачи в соответствующие переменные в операторе вызова задачи. Все аргументы задачи(task) должны передаваться по значению, а не по ссылке (то есть указателю на значение).
Например:
task my_task;
input a, b;
inout c;
output d, e;
begin
. . . // объявления, которые выполняют работу задачи
. . .
c = foo1; // присваивания, инициализирующие результирующие регистры
d = foo2;
e = foo3;
end
endtask
task my_task (input a, b, inout c, output d, e);
begin
. . . // объявления, которые выполняют работу задачи
. . .
c = foo1; // присваивания, инициализирующие результирующие регистры
d = foo2;
e = foo3;
end
endtask
my_task (v, w, x, y, z);
a = v;
b = w;
c = x;
x = c;
y = d;
z = e;
module traffic_lights;
reg clock, red, amber, green;
parameter on = 1, off = 0, red_tics = 350,
amber_tics = 30, green_tics = 200;
// инициализация цветов
initial red = off;
initial amber = off;
initial green = off;
always begin // последовательность для управления цветами
red = on; // включение красного света
light(red, red_tics); // и ожидание
green = on; // включение зеленого света
light(green, green_tics); // и ожидание
amber = on; // включение янтарный света
light(amber, amber_tics); // и ожидание
end
// задание на ожидание положительных фронтов часов 'tics'
// перед выключением "цветного" света.
task light;
output color;
input [31:0] tics;
begin
repeat (tics) @ (posedge clock);
color = off; // выключить свет
end
endtask
always begin // генерация тактового сигнала
#100 clock = 0;
#100 clock = 1;
end
endmodule // traffic_lights.
10.2.3 Использование памяти задачи и одновременная активация
Задача может быть включена более одного раза одновременно. Все переменные автоматической задачи должны реплицироваться при каждом параллельном вызове задачи для хранения состояния, специфичного для этого вызова. Все переменные статической задачи должны быть статическими в том смысле, что должна существовать одна переменная, соответствующая каждой объявленной локальной переменной в экземпляре модуля, независимо от количества одновременных вызовов задачи. Однако статические задачи в разных экземплярах модуля должны иметь отдельные хранилища друг от друга.
Переменные, объявленные в статических задачах(task), включая аргументы типа input, output и inout, должны сохранять свои значения между вызовами. Они должны быть инициализированы значением инициализации по умолчанию, как описано в п. 4.2.2.
Переменные, объявленные в автоматических задачах, включая аргументы типа output, должны инициализироваться значением инициализации по умолчанию всякий раз, когда выполнение входит в их область действия. Аргументы типа input и inout должны инициализироваться значениями, переданными из выражений, соответствующих этим аргументам, перечисленных в операторах вызова задачи(task).
Поскольку переменные, объявленные в автоматических задачах, деаллоцируются в конце вызова задачи, они не должны использоваться в определенных конструкциях, которые могут ссылаться на них после этого момента:
- Им не должны присваиваться значения с помощью неблокирующих присвоений или процедурных непрерывных присвоений.
- На них нельзя ссылаться в процедурных непрерывных заданиях или процедурных объявлениях о силе.
- На них нельзя ссылаться в элементах управления событиями внутри назначения в неблокирующих назначениях.
- Они не должны отслеживаться с помощью системных задач, таких как $monitor и $dumpvars.
10.3 Завершение именованных блоков и задач
Оператор disable предоставляет возможность завершить действия, связанные с параллельно активными процедурами, сохраняя при этом структурированный характер процедурных описаний Verilog HDL. Оператор disable предоставляет механизм для завершения задачи(task) до выполнения всех ее операторов, прерывания циклического оператора или пропуска операторов для продолжения очередной итерации циклического оператора. Он полезен для обработки условий исключения, таких как аппаратные прерывания и глобальные сбросы.
Оператор disable имеет синтаксическую форму, показанную в Синтаксис 10-3.
disable_statement ::=
disable hierarchical_task_identifier ;
| disable hierarchical_block_identifier ;
Любая из форм оператора disable завершает выполнение задачи или именованного блока. Выполнение возобновляется в операторе, следующем за блоком или за оператором, вызывающее выполнение задачи. Все действия, вызванные в именованном блоке или задаче(task), также должны быть завершены. Если объявления вызова задач вложенные (то есть одна задача вызывает другую, а та вызывает еще одну), то отключение задачи(task) в цепочке приведет к отключению всех задач ниже по цепочке. Если задача вызвана более одного раза, то отключение такой задачи приведет к отключению всех активаций этой задачи.
Результаты следующих действий, которые могут быть инициированы заданием, не указываются, если задание отключено:
- Результаты аргументов вывода и ввода
- Запланированные, но не выполненные неблокируемые задания
- Процедурные непрерывные назначения (объявления о assign и force)
Оператор disable может использоваться внутри блоков и задач(task) для отключения конкретного блока или задачи, содержащей оператор disable. Оператор disable можно использовать для отключения именованных блоков внутри функции(function), но нельзя использовать для отключения функций. В случаях, когда оператор disable внутри функции отключает блок или задачу, вызвавшую функцию, поведение не определено. Отключение автоматической задачи или блока внутри автоматической задачи происходит, как и для обычных задач, для всех параллельных выполнений задачи.
Например:
begin : block_name
rega = regb;
disable block_name;
regc = rega; // это присвоение никогда не будет выполнено
end
begin : block_name
...
if (a == 0)
disable block_name;
...
end // end of named block
// continue with code following named block
task proc_a;
begin
...
...
if (a == 0)
disable proc_a; // return if true
...
...
end
endtask
begin : break
for (i = 0; i < n; i = i+1) begin : continue
@clk
if (a == 0) // "продолжить" цикл
disable continue;
statements
statements
@clk
if (a == b) // "выход" из цикл
disable break;
statements
statements
end
end
fork
begin : event_expr
@ev1;
repeat (3) @trig;
#d action (areg, breg);
end
@reset disable event_expr;
join
always begin : monostable
#250 q = 0;
end
always @retrig begin
disable monostable;
q = 1;
end
10.4 Функции и вызов функций
Цель функции — вернуть значение, которое будет использовано в выражении. Остальная часть этого параграфа объясняет, как объявляется и использовать функции.
10.4.1 Декларации функций
Синтаксис для определения функции приведен в Синтаксис 10-4.
function_declaration ::=
function [ automatic ] [ function_range_or_type ]
function_identifier ;
function_item_declaration { function_item_declaration }
function_statement
endfunction
| function [ automatic ] [ function_range_or_type ]
function_identifier ( function_port_list ) ;
{ block_item_declaration }
function_statement
endfunction
function_item_declaration ::=
block_item_declaration
| { attribute_instance } tf_input_declaration ;
function_port_list ::=
{ attribute_instance } tf_input_declaration
{ , { attribute_instance }tf_input_declaration }
tf_input_declaration ::=
input [ reg ] [ signed ] [ range ] list_of_port_identifiers
| input task_port_type list_of_port_identifiers
function_range_or_type ::=
[ signed ] [ range ]
| integer
| real
| realtime
| time
block_item_declaration ::=
{ attribute_instance } reg [ signed ] [ range ] list_of_block_variable_identifiers ;
| { attribute_instance } integer list_of_block_variable_identifiers ;
| { attribute_instance } time list_of_block_variable_identifiers ;
| { attribute_instance } real list_of_block_real_identifiers ;
| { attribute_instance } realtime list_of_block_real_identifiers ;
| { attribute_instance } event_declaration
| { attribute_instance } local_parameter_declaration ;
| { attribute_instance } parameter_declaration ;
list_of_block_variable_identifiers ::=
block_variable_type { , block_variable_type }
list_of_block_real_identifiers ::=
block_real_type { , block_real_type }
block_variable_type ::=
variable_identifier { dimension }
block_real_type ::=
real_identifier { dimension }
Определение функции начинается с ключевого слова function, за которым следует необязательное ключевое слово automatic, за которым следует необязательное function_range_or_type возвращаемого значения функции, за которым следует имя функции, за которым следует либо точка с запятой, либо список портов функции, заключенный в круглые скобки, а затем точка с запятой, и заканчивается ключевым словом endfunction.
Использование function_range_or_type должно быть необязательным. Функция, указанная без function_range_or_type, по умолчанию использует скаляр для возвращаемого значения. Если используется, function_range_or_type должен указывать, что возвращаемое значение функции является real, integer, time, realtime или вектором (необязательно знаковым) с диапазоном [n:m] бит.
Функция должна иметь по крайней мере один объявленный вход.
Ключевое слово automatic объявляет автоматическую функцию, которая является реентерабельной. При этом все объявления функций выделяются динамически для каждого параллельного вызова функции. К элементам автоматических функций нельзя обращаться с помощью иерархических ссылок. Автоматические функции могут быть вызваны с помощью их иерархического имени.
Входы функций должны быть объявлены одним из двух способов. Первый способ должен содержать имя функции, за которым следует точка с запятой. После точки с запятой следует одно или несколько объявлений входов. По желанию смешанных с объявлениями элементов блока. После объявления элементов функции должно следовать заявление о поведении, а затем ключевое слово endfunction.
Второй метод должен содержать имя функции, за которым следует открытая скобка и одно или несколько объявлений ввода, разделенных запятыми. После всех входных деклараций должна быть закрытая скобка и точка с запятой. После точки с запятой должно быть ноль или более объявлений элементов блока, за которыми следует поведенченский оператор, а затем ключевое слово endfunction.
Например:
function [7:0] getbyte;
input [15:0] address;
begin
// код для извлечения младшего байта из адресуемого слова
. . .
getbyte = result_expression;
end
endfunction
function [7:0] getbyte (input [15:0] address);
begin
// code to extract low-order byte from addressed word
. . .
getbyte = result_expression;
end
endfunction
10.4.2 Возвращение значения из функции
Определение функции должно неявно объявлять внутреннюю для функции переменную с тем же именем, что функция. Эта переменная либо имеет значение по умолчанию 1-битный reg, либо имеет тот же тип, что и тип, указанный в объявлении функции. Определение функции инициализирует возвращаемое значение функции путем присвоения результата функции внутренней переменной с тем же именем, что и функция.
Объявление другого объекта с тем же именем, что и у функции, в области видимости, где объявлена функция(function), является незаконным. Внутри функции подразумевается переменная с именем функции, которая может использоваться в выражениях внутри функции(function). Поэтому также незаконно объявлять другой объект с тем же именем, что и функция, внутри области видимости функции(function).
getbyte = result_expression;
10.4.3 Вызов функции
Вызов функции — это операнд внутри выражения. Вызов функции(function) имеет синтаксис, приведенный в разделе Синтаксис 10-5.
function_call ::=
hierarchical_function_identifier{ attribute_instance } ( expression { , expression } )
Порядок оценки аргументов вызова функции не определен. Например:
word = control ? {getbyte(msbyte), getbyte(lsbyte)}:0;
10.4.4 Правила функционирования
Функции(function) более ограничены, чем задачи(task). Их использование регулируется следующими правилами:
- Определение функции не должно содержать никаких объявлений с контролем времени. То есть любых объявлений, содержащих #, @ или wait.
- Функции не должны включать задачи.
- Определение функции должно содержать по крайней мере один входной аргумент.
- Определение функции не должно иметь ни одного аргумента, объявленного как output или inout.
- Функция не должна иметь никаких неблокирующих назначений или процедурных непрерывных назначений.
- Функция не должна иметь триггеров событий.
Например:
module tryfact;
// определить функцию
function automatic integer factorial;
input [31:0] operand;
integer i;
if (operand >= 2)
factorial = factorial (operand - 1) * operand;
else
factorial = 1;
endfunction
// тестирование функции
integer result;
integer n;
initial begin
for (n = 0; n <= 7; n = n+1) begin
result = factorial(n);
$display("%0d factorial=%0d", n, result);
end
end
endmodule // tryfact
Результаты моделирования следующие:
Перевод Официального Стандарта Verilog HDL
- factorial=1
- factorial=2
- factorial=6
- factorial=24
- factorial=120
- factorial=720
- factorial=5040
10.4.5 Использование константных функций(function)
Вызовы константных функций используются для поддержки построения сложных вычислений значений во время разработки (см. 12.8). Вызов константной функции должен быть вызовом функции константной функции, локальной для вызывающего модуля, где аргументы функции являются константными выражениями. Константные функции являются подмножеством обычных функций Verilog, которые должны удовлетворять следующим ограничениям:
- Они не должны содержать иерархических ссылок.
- Любая функция, вызываемая внутри константной функции, должна быть константной функцией, локальной для текущего модуля.
- Должен быть законным вызов любой системной функции(function), которая разрешена в выражении constant_expression (см. пункт 5). Вызовы других системных функций должны быть запрещены.
- Все системные задачи в рамках постоянной функции игнорируются.
- Все значения параметров, используемые внутри функции, должны быть определены до использования вызывающей константной функции (т.е. любое использование параметра в оценке вызова константной функции представляет собой использование этого параметра в месте первоначального вызова константной функции).
- Все идентификаторы, которые не являются параметрами или функциями, должны быть объявлены локально для текущей функции.
- Если функции используют любое значение параметра, на которое прямо или косвенно влияет оператор defparam (см. 12.2.1), результат будет неопределенным. Это может привести к ошибке или константная функция может вернуть неопределенное значение.
- Функции не должны быть объявлены внутри блока генерации (см. 12.4).
- Функции не должны сами использовать константные функции в любом контексте, требующем константного выражения.
Вызовы константных функций оцениваются во время разработки. Их выполнение не влияет на начальные значения переменных, используемых как во время моделирования, так и при многократных вызовах функции во время разработки. В каждом из этих случаев переменные инициализируются так, как были бы инициализированы при обычном моделировании.
Например:
module ram_model (address, write, chip_select, data);
parameter data_width = 8;
parameter ram_depth = 256;
localparam addr_width = clogb2(ram_depth);
input [addr_width - 1:0] address;
input write, chip_select;
inout [data_width - 1:0] data;
// определить функцию clogb2
function integer clogb2;
input [31:0] value;
begin
value = value - 1;
for (clogb2 = 0; value > 0; clogb2 = clogb2 + 1)
value = value >> 1;
end
endfunction
reg [data_width - 1:0] data_store[0:ram_depth - 1];
ram_model #(32,421) ram_a0(a_addr,a_wr,a_cs,a_data);