ЛР5 > Язык Verilog: Исследование арифметических устройств
Статьи по теме
- МКР 1 > «Основи проектування електронної апаратури на ПЛІС – 1»: Перелік завдань на модульну контрольну роботу
- ЛР2 > Исследование комбинационных устройств: особенности языка Verilog
- ЛР3 > Реализация типовых комбинационных устройств
- ЛР4 > Исследование последовательностных логических устройств: Операторы Verilog
- ЛР6 > Исследование конечных автоматов
- ЛР6 > Модернизация процессорного устройства
- ЛР7 > Исследование многофункциональных устройств
- ЛР8 > Разработка простого процессорного модуля на Verilog

Тема: Краткие теоретические сведения по языку Verilog (продолжение) – иерархия проекта, подключение модулей, блокирующее и неблокирующее присваивание, системные функции, построение арифметических устройств.
Структура курса лабораторных работ: Основы Verilog 1. Знакомство со средой моделирования ModelSim |
Скачать Материалы к лабораторной работе по Verilog №5.
1. Теоретические сведения.
Подключение модулей в файле верхнего уровня иерархии
Основной проектной единицей на языке Verilog являются модули. При создании сложных устройств удобно использовать иерархический подход к их построению, т.е. в состав одного устройства может входить несколько модулей. Пришло время научиться подключать готовые модули к проектируемой схеме и связывать между собой. Обычно выход одного модуля соединяется со входом другого проводником, поэтому изменения выходного сигнала источника будут немедленно передаваться на вход приемника. Можно соединять порты ввода/вывода модуля с входами устройства. Связи между модулями могут быть типа wire и reg.
Синтаксис подключения модуля имеет следующий вид:
1 |
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. Файл описания устройства будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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) и выполняет обработку списка событий, которые должны произойти на данном шаге. Рассмотрим события происходящие «одновременно» – т.е. на одном временном шаге моделирования.
Допустим, имеется следующий набор команд:
1 2 3 |
always @ (posedge CLK) a=b; always @ (posedge CLK) b=a; |
В данном примере переменные а и b – одноразрядные регистры, к моменту появления положительного фронта тактового сигнала CLK хранящие значения а==0 и b==1. Какое же значение будут иметь эти переменные после выполнения операции присвоения? Это зависит от того, в какой последовательности операции присваивания попадут в список. То есть, поведение такой конструкции зависит от порядка следования операторов в программе. Это означает, что либо обе эти переменные будут равны 0, либо обе равны 1 (в нашем примере – 1). Операция блокирующего присвоения (=) блокирует исполнение других последовательных операций до тех пор, пока она не будет выполнена. Использование операции блокирующего присвоения в параллельно исполняемых блоках нежелательно. Но если в блоке необходимо обеспечить последовательное выполнение операторов, следует использовать данный тип присвоения.
Следующий фрагмент программы гарантирует обнуление переменных a и b по переднему фронту сигнала CLK:
1 2 3 4 5 |
always @ (posedge CLK) begin a=0; b=a; end |
Если в предыдущих примерах использовать оператор неблокирующего присвоения (<=), то поведение устройства изменится:
1 2 3 |
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. Создайте исходный файл сумматора на структурном уровне:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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 |
Обратите внимание на подключение готовых модулей в данном файле.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
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: Спроектировать умножитель с накоплением (операция МАС).
Для проверки работы устройства создать тестовый файл.