ЛР5 > Язык Verilog: Исследование арифметических устройств
Тема: Краткие теоретические сведения по языку Verilog (продолжение) – иерархия проекта, подключение модулей, блокирующее и неблокирующее присваивание, системные функции, построение арифметических устройств.
Структура курса лабораторных работ: Основы Verilog 1. Знакомство со средой моделирования ModelSim |
Скачать Материалы к лабораторной работе по Verilog №5.
1. Теоретические сведения.
Подключение модулей в файле верхнего уровня иерархии
Основной проектной единицей на языке Verilog являются модули. При создании сложных устройств удобно использовать иерархический подход к их построению, т.е. в состав одного устройства может входить несколько модулей. Пришло время научиться подключать готовые модули к проектируемой схеме и связывать между собой. Обычно выход одного модуля соединяется со входом другого проводником, поэтому изменения выходного сигнала источника будут немедленно передаваться на вход приемника. Можно соединять порты ввода/вывода модуля с входами устройства. Связи между модулями могут быть типа wire и reg.
Синтаксис подключения модуля имеет следующий вид:
module_name instance_name( …io ports… );
Где module_name – имя вызываемого модуля, который необходимо подключить. Можно
подключить в одном проекте одновременно несколько одинаковых модулей, просто указав для них разные имена – instance_name. Здесь уместно вспомнить механизм объявления переменных в языках программирования. Сначала записывается тип переменной, а затем – имя переменной. Так и при подключении модуля – вначале записывается имя модуля, который необходимо подключить, а затем имя конкретного экземпляра этого модуля в вызывающем его файле. Далее, в круглых скобках, указывается подключение сигналов (типа reg или wire) к портам ввода-вывода модуля.
Рассмотрим следующий пример:
Рисунок 1. Иерархическое подключение модулей
Допустим, проектируемое устройство включает в себя три модуля (рис. 1): debounce, onepulse, clk_div, каждый из которых описан в соответствующем файле на языке Verilog (имя файла должно совпадать с именем модуля). Соответствующие входные и выходные порты этих модулей изображены на рисунке. Вход модуля изображается с левой стороны блока, выход – с правой. В устройстве необходимо объединить между собой эти модули в файле верхнего уровня иерархии. Соединение модулей осуществляются с помощью сигналов (тип reg) – Clock_100Hz, Clock_1MHz, PB1_Debounced. Файл описания устройства будет выглядеть следующим образом:
module hierarch(Clock_48MHz, PB1, PB1_Single_Pulse);
input Clock_48MHz, PB1; output PB1_Single_Pulse;
reg Clock_100Hz, Clock_1MHz, PB1_Debounced;
debounce debounce1(PB1, Clock_100Hz, PB1_Debounced);
clk_div clk_div1(Clock_48MHz, Clock_1MHz, Clock_100Hz);
onepulse onepulse1(PB1_Debounced, Clock_1MHz, PB1_Single_Pulse);
endmodule
Операторы блокирующего и неблокирующего присваивания
В языке Verilog существуют два типа операторов присвоения: блокирующее blocking (=) и неблокирующее nonblocking (<=).
Для того чтобы понять разницу между данными типами операторов присвоения, необходимо рассмотреть принцип работы Verilog симулятора. В реальном устройстве
(например – цифровой схеме), которая моделируется с помощью языка Verilog, события могут происходить одновременно – при изменении входного сигнала во всех элементах, связанных с ним, начинаются процессы. Они протекают одновременно и приводят к соответствующим изменениям выходных сигналов. Моделирующая программа не может выполнять события одновременно, она создает списки событий, которые будут выполняться
последовательно. Когда все события из списка выполнены, симулятор переходит к обработке
следующего временного шага – увеличивает текущее время моделирования на временной интервал (второй параметр в директиве `timescale) и выполняет обработку списка событий, которые должны произойти на данном шаге. Рассмотрим события происходящие «одновременно» – т.е. на одном временном шаге моделирования.
Допустим, имеется следующий набор команд:
always
@ (posedge CLK) a=b;
always
@ (posedge CLK) b=a;
В данном примере переменные а и b – одноразрядные регистры, к моменту появления положительного фронта тактового сигнала CLK хранящие значения а==0 и b==1. Какое же значение будут иметь эти переменные после выполнения операции присвоения? Это зависит от того, в какой последовательности операции присваивания попадут в список. То есть, поведение такой конструкции зависит от порядка следования операторов в программе. Это означает, что либо обе эти переменные будут равны 0, либо обе равны 1 (в нашем примере – 1). Операция блокирующего присвоения (=) блокирует исполнение других последовательных операций до тех пор, пока она не будет выполнена. Использование операции блокирующего присвоения в параллельно исполняемых блоках нежелательно. Но если в блоке необходимо обеспечить последовательное выполнение операторов, следует использовать данный тип присвоения.
Следующий фрагмент программы гарантирует обнуление переменных a и b по переднему фронту сигнала CLK:
always @ (posedge CLK)
begin
a=0;
b=a;
end
Если в предыдущих примерах использовать оператор неблокирующего присвоения (<=), то поведение устройства изменится:
always @ (posedge CLK) a<=b;
always @ (posedge CLK) b<=a;
В данном случае в список событий, исполняемом на текущем временном шаге моделирования после изменения сигнала CLK, обе операции будут помещены как параллельно исполняемые, т.е. переменные a и b обменяются своими значениями. После прохождения переднего фронта сигнала CLK значения переменных будут следующими: а==1 b==0. Последовательность записи a<=b; b<=a; или b<=a; a<=b; в данном случае не имеет значения, т.к. события моделируются одновременно.
Системные функции языка Verilog
Язык Verilog предоставляет программисту специальные возможности для управления и анализа результатов моделирования. Эти возможности реализованы в виде системных функций симулятора. Следует помнить, что при синтезе системные функции игнорируются.
Благодаря наличию механизма PLI, обеспечивающего подключение исполняемой программы (написанной либо пользователем, либо третьей стороной) к тестовым файлам, число системных функций и задач, которые могут выполняться с их помощью, очень велико. Основное назначение системных функций – сбор и анализ информации, взаимодействие с операционной системой. Признаком системной функции является знак $. Перечислим наиболее популярные системные функции:
$finish – завершение процесса моделирования;
$stop – переход в интерактивный режим;
$display, $write – вывод данных в stdout (данные дублируются в файл протокола), поведение такой функции соответствует функции printf языка C (вывод форматированной строки с поддержкой дополнительных форматов, например, %b -бинарный), или процедуре write языка Паскаль с разделенными «,» аргументами. Функция $display завершает вывод строки командой «перевод строки»;
$monitor – отслеживает изменения аргументов и в конце каждого временного шага моделирования отображает текущие результаты (если были обнаружены изменения значений сигналов). Формат данной функции – как у $display;
$readmemb, $readmemh – обеспечивают считывание данных (в двоичном или шестнадцатеричном формате) из файла в память. Формат файла очень простой – в каждой строке указывается слово заданной разрядности, или указатель адреса (конструкция @<адрес загрузки>). Данную функцию удобно применять для моделирования ПЗУ;
$system – выполняет команду операционной системы (вызов функции языка С system()).
Для выполнения файловых операций используются функции $fopen, $fclose, $fwrite, $fmonitor. Они позволяют сохранять передаваемые данные в файлах. Функции $dumpfile, $dumpvars позволяют записывать изменения сигналов тестируемого модуля, всего проекта или его составных частей, в файле специального формата для дальнейшего анализа. Очень полезные и эффективные функции для обработки данных.
Функция $time – возвращает значение текущего времени моделирования.
Это небольшой перечень стандартных функций. Их полный список указан в документации к моделирующей программе.
Арифметические устройства
К арифметическим устройствам относятся преобразователи, выполняющие арифметические действия (сложение, вычитание, умножение) над входными данными.
На рисунке 1 представлена схема полного одноразрядного сумматора и его графическое изображение.
Рисунок 2. Схема полного одноразрядного сумматора и его графическое изображение.
Назначение сигналов сумматора следующее: a и b – входные сигналы (слагаемые), Сi – вход переноса, s – выходной сигнал (сумма), Со – выход переноса. Таблица истинности одноразрядного сумматора выглядит следующим образом:
Таблица истинности
a |
b |
Сi |
Со |
s |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
0 |
1 |
0 |
1 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
1 |
1 |
0 |
1 |
1 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
1 |
1 |
1 |
1 |
Для создания сумматоров большей разрядности используется каскадное соединение одноразрядных сумматоров. Например, на рисунке 2 показан полный четырехразрядный сумматор с последовательным переносом, состоящий из четырех одноразрядных сумматоров.
Рисунок 3. Полный четырехразрядный сумматор с последовательным переносом.
Пример выполнения операции умножения двух 4-разрядных чисел в двоичном виде показан на рисунке 3. На рисунке 4 показана структурная схема реализации такого перемножителя. Он состоит из логических элементов «И» и полных одноразрядных сумматоров (FA).
Рисунок 4. Операция умножения двух 4-разрядных чисел.
Рисунок 5. Структурная схема перемножителя двух 4-разрядных чисел.
Данные два компонента используются как базовые для создания различных арифметических или арифметико-логических устройств (АЛУ).
2. Порядок выполнения работы.
В лабораторной работе мы создадим проект, описывающий работу полного четырехразрядного сумматора, изображенного на рисунке 3. Для исходного модуля используется структурное описание. Для проверки сумматора создадим эталонную модель, использующую поведенческое описание, и тестовый файл (test-bench). Для этого:
1. Создайте новый проект в среде ModelSim.
2. Создайте исходный файл сумматора на структурном уровне:
module my_sum (Ain, Bin, Ci, Sout, Co);
input Ain, Bin, Ci;
output Sout, Co;
wire [3:0] Ain, Bin, Sout, C;
wire Ci, Co;
bitsum sum1(Ain[0], Bin[0], Sout[0], Ci, C[0]);
bitsum sum2(Ain[1], Bin[1], Sout[1], C[0], C[1]);
bitsum sum3(Ain[2], Bin[2], Sout[2], C[1], C[2]);
bitsum sum4(Ain[3], Bin[3], Sout[3], C[2], C[3]);
assign Co = C[3];
endmodule
module bitsum (A, B, S, Cin, Cout);
input A, B, Cin;
output S, Cout;
wire A, B, S, Res;
wire c1, c2, Cin, Cout;
xor(Res, A, B);
and(c1, A, B);
xor(S, Cin, Res);
and(c2, Cin, Res);
or(Cout, c1, c2);
endmodule
Обратите внимание на подключение готовых модулей в данном файле.
-
Создайте исходный файл эталонного сумматора на поведенческом уровне:
module ref_sum (Ain, Bin, Ci, Sout, Co);
input Ain, Bin, Ci;
output Sout, Co;
wire [3:0] Sout, Ain, Bin;
reg [4:0] S;
always @(Ain, Bin, Ci)
S = Ain + Bin + Ci;
assign Sout = S[3:0];
assign Co = S[4];
endmodule
-
Создайте тестовый файл для подачи входных сигналов и сравнения работы двух модулей:
module test_sum; // Top Level Testbench
wire Ci,cm,cr;
wire [3:0] Ain, Bin;
reg [3:0] Ain_r, Bin_r;
reg Ci_r;
wire [3:0] res_my, res_ref;
my_sum my_block (Ain, Bin, Ci,res_my, cm);
ref_sum ref_block (Ain, Bin, Ci, res_ref, cr);
initial
begin
$display(“\t\t Time Ain Bin Ci res_my cm res_ref cr”);
$monitor($time,,,,,Ain,,,,,Bin,,,,,Ci,,,,,res_my,,,,,,,,cm,,,,,,,res_ref,,,,,,,cr);
#400 $finish;
end
initial
begin
Ain_r = 1;
#50 Ain_r = 5;
#50 Ain_r = 1;
#50 Ain_r = 5;
#50 Ain_r = 1;
#50 Ain_r = 5;
#50 Ain_r = 1;
#50 Ain_r = 5;
end
initial
begin
Bin_r = 2;
#100 Bin_r = 10;
#100 Bin_r = 2;
#100 Bin_r = 10;
end
initial
begin
Ci_r = 1′b0;
#200 Ci_r = 1′b1;
end
assign Ain = Ain_r;
assign Bin = Bin_r;
assign Ci = Ci_r;
endmodule
Обратите внимание на подключение модулей в данном файле и использование системных функций.
5. Скомпилируйте все программы. После успешной компиляции перейдите в режим моделирования. В качестве основного файла для моделирования укажите тестовый файл.
6. Отройте графическое окно и добавьте в него проверяемые сигналы. Запустите проект на моделирование. Ответьте НЕТ на вопрос, хотите ли Вы закончить работу с симулятором. Проверьте полученные результаты:
7. Объясните полученный результат. Выйдите из режима моделирования.
2. Самостоятельная работа.
Спроектировать следующие арифметико-логические устройства (входные данные – восьмиразрядные):
Вариант 1. АЛУ с функциями: А+В, АxorВ, А-1, А-В.
Вариант 2. АЛУ с функциями: А+В, А*В, А-1, В.
Вариант 3. АЛУ с функциями: А+(А+В), А, А+1, А-В-1.
Вариант 4. АЛУ с функциями: not(А+В), not(А*В), А+В+1, (А + notВ)+1.
Вариант 5. АЛУ с функциями: АxorВ, А*В+(А+notВ), А, В.
Вариант 6: Спроектировать умножитель с накоплением (операция МАС).
Для проверки работы устройства создать тестовый файл.