Представленный Вашему вниманию курс "Проектирование процессоров RISC-V в среде Logisim и на языке Verilog в среде Quartus" представляет собой труд, направленный на последовательное освоение основ цифровой схемотехники результатом которого станет создание простого ядра процессора RISC-V.
При создании курса автор был нацелен обеспечить слушателей простым и доступным входом в освоение базовых основ цифрой электроники и открытой архитектуры RISC-V.
Теоретический материал дополняется обширной практической частью, в рамках которой изучаются ключевые блоки цифровых устройств: комбинационные и последовательностные схемы, регистры, счетчики, арифметико-логические устройства, модули памяти и дешифраторы.
Для освоения материала предлагается использование известной в академической среде программы моделирования устройств Logisim Evolution. Материал курса по данной среде является единственным открытым русскоязычным текстовым учебным пособием.
Помимо Logisim Evolution в курсе рассматриваются методы работы в среде Intel Quartus Prime, включая использование языка описания аппаратуры Verilog HDL для разработки цифровых модулей.
Подход в изучении основ схемотехнического проектирования устройств, совмещённый с их описанием на языке описания аппаратуры, позволит лучше понять принципы организации и функционирования как отдельных модулей, так и процессора в целом.
В результате прохождения курса слушатель должен получить базовое понимание фундаментальных принципов цифровой электроники и работы современных микропроцессоров.
Получить основные навыки работы с графическими инструментами проектирования Logisim Evolution, а также навыки создания модулей на языке Verilog HDL в среде Quartus Prime.
Иметь представление об архитектуре современных процессоров и понимать принцип построения и функционирования процессоров RISC-V.
Презентация по установке инструментальных сред
Видеолекция. Основы Logisim (часть 1)
Видеолекция. Основы Logisim (часть 2)
Видеолекция. Описание работы в среде Quartus Prime Lite.
Для синтеза цифровых устройств глобально можно определить несколько методов: схемотехническое построение устройств и текстовое на языках описания аппаратуры.
Язык Verilog является одним из самых популярных языков описания аппаратуры (HDL), используемым для проектирования и моделирования работы цифровых и микропроцессорных систем. Удобство, выразительность, а также широкая поддержка языка со стороны инструментов проектирования, сделали Verilog неотъемлемой частью индустрии разработки цифровых устройств.
Verilog был разработан в 1984 году как язык описания аппаратуры, который позволяет проектировщикам описывать логику цифровых схем на высоком уровне абстракции. Он обеспечивает средства для описания как структурной, так и поведенческой модели аппаратуры. Структурное описание позволяет проектировать систему как комбинацию компонентов, тогда как поведенческое описание фокусируется на логике работы системы.
Язык поддерживает различные уровни абстракции, что позволяет инженерам сосредоточиться на нужной детали в зависимости от этапа проектирования.
Язык Verilog обладает следующими преимуществами:
Необходимо выделить ряд недостатков, которые по мере освоения языка начинающим разработчиком могут быть сняты:
В целом необходимо выделить ряд существенных преимуществ языков описания аппаратуры над схемотехническим вводом:
В литературе можно встретить описание языков описания аппаратуры как составной системы из двух компонент: общеалгоритмической и проблемно-ориентированной.
Алгоритмическая компонента языка Verilog заключается в схожести его семантики и синтаксиса с Си подобными языками. Язык содержит операторы, типы данных, процедурные блоки и функции.
Проблемно-ориентированная компонента заключается в наличии интерфейсов, в том числе данных физического типа, сигналов, конкретизации применяемых компонентов.
Освоение языка можно начать с формирования представления о зарезервированных словах.
В языке Verilog определены следующие зарезервированные слова, применяемые при реализации описания схем - модулей. Соответственно для применения в качестве имен переменных, констант их использовать нельзя. Зарезервированные слова Verilog приведены в таблице 1.1.
always and assign attribute automatic begin buf bufif0 bufif1 case casex casez cell cmos config deassign default defparam disable edge else end endattribute endcase endconfig endfunction realtime |
endgenerate endmodule endprimitive endspecify endtable endtask event for force forever fork function generate genvar highz0 highz1 if ifnone incdir include instance initial inout input integer join large |
liblist library localparam micromodule medium module nand negedge nmos nor not notif0 notif1 nowshowcancelled or output parameter pmos posedge primitive pull0 pull1 pulldown pullup pulsestyle_ondetect pulsestyle_onevent remos |
real reg release repeat rnmos rpmos rtran rtranif0 rtranif1 scalared signed small specify showcancelled specparam strength strong0 strong1 supply0 s upply1 table task time tran tranif0 tranif1 tri |
tri0 tri1 triand trior trireg unsigned use vectored wait wand weak0 weak1 while wire wor xnor xor |
Необходимо закрепить базовые понятия, которыми необходимо руководствоваться при описании аппаратуры:
При написании программного кода разработчик должен оставлять комментарии. В Verilog также существуют средства обозначения комментариев. Комментарии делятся на:
Однострочными комментариями можно закомментировать часть одной строки или всю строку полностью.
Описание каждого объекта аппаратуры осуществляется в модулях и размещается между зарезервированными словами: module и endmodule.
Создаваемые объекты должны иметь интерфейсы - порты.
Порты в Verilog бывают трёх типов:
Порт объявляется следующим образом: тип [разрядность] имя порта;.
Если не задана спецификация диапазона, например input [2:0] a, то считается, что порт имеет ширину одного бита, а передаваемые сигналы считаются скалярными. Если порт представляет собой шину, то есть имеет диапазон, то передаваемые сигналы считаются векторными.
Порты могут иметь в объявлении константные выражения, через зарезервированное слова parameter. Также могут объявляться внутренние цепи через зарезервированное слово wire.
Существует еще один тип источника сигнала reg. Данный тип при описании комбинационной логики ведет себя как переменная - провод wire. При проектировании синхронных схем сигнал reg способен сохранять значение, т.е. является триггером или регистром.
Сигналы или переменные могут быть представлены следующими логическими уровнями:
Пример описания модуля приведен в листинге 1.1, где красным выделены зарезервированные слова, зеленым - пользовательские имена и коментарии, синим функционал.
module name_module #(parameter M=4) // константа (input [3:0] A,B, // четырехразрядные входы А и B input cin,// однопроводная линия output [M-1:0] S, //выход использующий для определения разрядности //константу output co); //выход линия wire [4:0] sum; // внутренние сигналы assign sum = A + B + cin; // Суммируем A, B и cin assign S = sum[M-1:0]; // Выход S - последние M разрядов суммы assign co = sum[4]; // Выход переноса endmodule
Для начала решения проблемно-ориентированных задач, следует остановиться на важнейшем разделе любого проекта - определение этапов выполнения проекта:
Сформируем следующую задачу: необходимо разработать и реализовать схему с тремя входами для мажоритарного контроля, используя язык описания аппаратуры Verilog. Схема должна сравнивать три входа и выдавать сигнал, равный лог. "1" если два или более входов равны лог. "1".
Уточним требования к интерфейсу проектируемой схемы и уровням сигналов реакции:
Входные сигналы:
Выполним математическое моделирование. Для этого сформируем по условиям таблицу истинности (таблица 1.2).
| п/п | Входы | Выход | ||
|---|---|---|---|---|
| A | b | c | y | |
| 1 | 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 1 | 0 |
| 3 | 0 | 1 | 0 | 0 |
| 4 | 0 | 1 | 1 | 1 |
| 5 | 1 | 0 | 0 | 0 |
| 6 | 1 | 0 | 1 | 1 |
| 7 | 1 | 1 | 0 | 1 |
| 8 | 1 | 1 | 1 | 1 |
Проведем анализ таблицы 1.2 истинности и определим строки, в которых комбинация входных сигналов обеспечит лог. "1" на выходе. В данном случае это 4, 6,7,8 строки. Выпишем их:
Сформируем СКНФ из данных:
$$y=a*b*\bar c+a*\bar b*c+]bar a*b*c+a*b*c$$После преобразований формула примет вид:
$$y=(a*b)+(b*c)+(a*c)$$Введем проект на заявленном языке Verilog. Определим имя модуля как major, тип сигнала интерфейса - wire. Имена сигналов были заданы в условии.
Используя процедурный оператор assign, введем функцию, описанную выше на языке Verilog как показано в листинге 1.2.
module major ( // начало модуля
input wire a, // первый вход
input wire b, // второй вход
input wire c, // третий вход
output wire y // выход мажоритарного значения
);
assign y = (a b) | (b c) | (c a); // реализуемая функция
endmodule // окончание модуля
При создании цифровых устройств одним из сложных и самых трудоемких этапов является процесс верификации. Данный этап может отвлекать от 50 до 80% всего времени выполнения проекта.
Верификация - это процесс установления соответствия созданного устройства, техническим условиям спецификации из технического задания.
В рамках верификации выполняются различные тесты, причем могут вноситься изменения в конечную спецификацию устройства.
В рамках выполнения верификации одним из этапов является процедура функциональной верификации - проверка на наличие ошибок в проекте. Для выполнения функциональной верификации применим инструменты симуляции работы схемы.
Так как разработанный выше модуль является достаточно простым, то его функциональная верификация будет несложной - сверка таблицы истинности и временной диаграммы работы. Но наша задача получить первичное представление о верификации и методах ее проведения.
Для проверки разработанного кода перенесем его в среду проектирования Intel Quartus.
В открывшейся форме (рисунок 1.1), добавим входы и выход модуля через меню "Edit" - "Insert" - "Insert Node or Bus" (рисунок 1.2). Нажав на кнопку "Node Finder", перейдем на форму выбора сигналов. Так как мы выбираем все сигналы, нажмем на кнопку "List". Исследуемые сигналы (все) переместим в поле "Selected Nodes", нажав на кнопку >> (рисунок 1.3).
(рис 1.1)
(рис 1.2)
(рис 1.3)
Необходимо исключить один из параметров работы симулятора - noopt. Для этого перейти в меню "Simulation" - "Simulation Settings".
В открывшемся окне, найдем данный параметр и удалим (рисунок 1.4).
(рис 1.4)

(рис 1.5)
(рис 1.6)
Более совершенные методы верификации подразумевают применение тестирующего оборудования или отладочного модуля, именуемого testbench. Testbench генерирует набор сигналов для проектируемого устройства, а затем принимает их для последующего анализа.
Стратегия работы с тестовым окружением - поочередная подача восьми возможных комбинаций на входы a,b,c
Для создания testbench необходимо рассмотреть основы синтаксиса данных файлов.
Первая строка кода testbench может содержать директиву масштаба модельного времени, определяющая значение залежек модели, которая начинается с обратной кавычки: `timescale. С этой директивой указываются временные параметры: N1 - определяет единицы времени задержки сигналов, следующих за символом #; N2 - точность расчета.
Оба параметра указываются в наносекундах или пикосекундах и могут принимать значения только 1, 10 или 100. По умолчанию назначаются ps.
Пример: `timescale 1ns/1ns
В следующей строке тестового модуля указывается ключевое слово module и имя модуля с обязательной приставкой tb, указывающей на то, что это тестовый модуль проекта.
Так как это тестовый модуль, то он не имеет портов. В него вставляется тестируемый модуль и входы/ выходы тестируемого модуля взаимодействуют с переменными типа reg и wire.
Например, для тестируемого модуля будет применено следующее объявление:
reg a; reg b; reg c; wire y;
Следующей частью кода является инстанцирование тестируемого модуля в тестовый модуль. При этом в списке портов устанавливается соответствие между портами тестового и тестируемого модулей. Модуль testbench в общем случае, может иметь отличные от тестируемого имена входов/выходов.
После имени тестируемого модуля должен быть указан тип подсистемы, которая тестируется:
Пример инстанцирования для рассматриваемого примера:
major uut (
.a(a),
.b(b),
.c(c)
Далее с помощью конструкций, определяемых ключевыми словами initial begin…end выполняется моделирование.
Конструкции содержат описания начальных значений и дальнейшего поведения входных сигналов во времени. Блок initial задает симулятору точку, с которой начинается работа. Следует отметить, что тестовый модуль без блока initial не работает.
Следующим этапом указываются варианты поведения сигналов и указание временных единиц (задержки) для их смены.
Также можно применять команды для вывода информации во время симуляции:
Пример: $monitor("a=%b, b=%b, c=%b, y=%b", $time, a, b, c, y);
Далее указывается одна из конструкций системной директивы $finish, которая завершает процесс моделирования. И наконец, ключевое слово endmodule указывает на окончание описания тестового модуля.
В листинге 1.3 приведен исходный код тестового модуля tb_major
`timescale 1ns/1ns
module tb_major;
reg a;
reg b;
reg c;
wire y;
major uut (
.a(a),
.b(b),
.c(c),
.y(y)
);
initial begin
$monitor("a=%b, b=%b, c=%b, y=%b", $time, a, b, c, y);
a = 0; b = 0; c = 0;//y=0
#10;
a = 0; b = 0; c = 1;//y=0
#10;
a = 0; b = 1; c = 0;//y=0
#10;
a = 0; b = 1; c = 1;//y=1
#10;
a = 1; b = 0; c = 0;//y=0
#10;
a = 1; b = 0; c = 1;//y=1
#10;
a = 1; b = 1; c = 0;//y=1
#10;
a = 1; b = 1; c = 1;//y=1
#10;
$finish;
end
endmodule
Для реализации верификации с использованием созданного тестового модуля необходимо выполнить ряд манипуляций с проектом.
Перейти в меню "Assigments" - "Settings". В открывшемся окне, перейти в раздел "EDA Tool Settings". В строке "Simulation" для столбца "Tool Name" выбрать Modelsim-Altera. В столбце "Format(s) " выбрать Verilog HDL (рисунок 1.8). Нажать кнопки Apply и OK.
(рис 1.7)
(рис 1.8)
Далее в меню пройти в меню "Assigments" - "Settings". В открывшемся окне, перейти в подраздел "Simulation" раздела "EDA Tool Settings". В окне настроек установить метку "Compile test bench" (рисунок 1.9).
(рис 1.9)
Нажать кнопку Test Benches. В новом окне (рисунок 1.10), нажать накнопку New.
(рис 1.10)
В диалоговом окне, приведенном на рисунке 1.11, в поле Test bench name ввести tb_major. Далее добавить файл, нажав на кнопку "… " поля "File name". Выбрать файл и нажать кнопку Add, затем кнопку ОК.
(рис 1.11)
Вернувшись в окно Test Benches, убедиться, что файл добавлен (рисунок 1.12). Далее нажать OK.
(рис 1.12)
(рис 1.13)
Необходимо отметить, что качественный testbench должен содержать код самопроверки, то есть операторы, отслеживающие возникновение ошибки в ходе работы.
Для представления целых чисел применяется следующий формат записи: size'base value, где size - размер, определяющий систему счисления, в которой представлено число, base - система счисления числа, value - непосредственное значение.
Для обозначения базы применяются следующие символы:
Примеры:
4'b1010 - двоичное четырёхразрядное число (в десятичной системе 10);
6'o71 - шестиразрядное восьмеричное число;
8'd127 - восьмиразрядное десятичное число 127;
127 - десятичное число (по умолчанию).
Для представления действительных чисел в Verilog применяется запись либо c десятичной точкой, либо в экспоненциальном виде. Запись с точкой имеет вид: value_i.value_f, где: value_i - целая часть, value_f - дробная часть
Примеры:
1.8
3.8e10 // e или E для равно $$3,8*10^{10}$$
2.1e-9// 2,1*$$10^{-9}$$
Выражения и операции на языке Verilog, применяющие примитивы относятся к следующим видам:
В таблице 1.3 приведены операции языка Verilog для построения выражений.
| Побитные операторы | |
| ~ | инверсия |
| побитное "И" | |
| | | побитное "ИЛИ" |
| ^ | побитное "Исключающее ИЛИ" |
| ~^ или ^~ | побитное "Исключающее ИЛИ НЕ" |
| Логические операторы | |
| ! | логическое "НЕ" |
| AND | логическое "И" |
| || OR | логическое "ИЛИ" |
| Редукция | |
| редуцированное "И" | |
| ~ | редуцированное "НЕ-И" |
| | | редуцированное "ИЛИ" |
| ~| | редуцированное "НЕ-ИЛИ" |
| ^ | редуцированное "Исключающее ИЛИ" |
| ~^ или ^~ | редуцированное "Исключающее ИЛИ НЕ" |
| Арифметические операторы | |
| + | сложение |
| - | вычитание |
| * | умножение |
| / | деление |
| Отношение | |
| > | больше |
| < | меньше |
| >= | больше либо равно |
| <= | меньше либо равно |
| = = | логическое равенство |
| != | логическое неравно |
| = = = | 4 state логическое равенство |
| != = | 4 state логическое неравно |
| Операции сдвига | |
| >> | логический сдвиг вправо |
| << | логический сдвиг влево |
| >>> | арифметический сдвиг вправо |
| <<< | арифметический сдвиг влево |
| Разное | |
| { , } | склеивание |
| {n{m}} | копирует m значение n раз |
| ? : | условие |
В листинге 1.3 приведен пример организации комбинационной схемы сравнения, использующей операторы отношения.
module comparator ( input [7:0] A, B, output O_EQ, O_NE, O_BR, O_MR, O_BOL, O_MEN); assign O_EQ = A == B; assign O_NE = A != B; assign O_BR = A >= B; assign O_MR = A <= B; assign O_BOL = A >B; assign O_MEN = A < B; endmodule
Оператор assign в Verilog предназначен для описания непрерывных присвоений в структуре цифровых схем. Этот оператор используется для связывания значений между сигналами и переменными.
Назначение оператора assign:
В листинге 1.4 дается пример применения оператора assign. На рисунке 1.14 можно наблюдать результат на уровне RTL.
module logic_operators ( input [3:0] a, b, // объявление входных векторных портов output [3:0] y, // объявление выходного векторного порта output z); assign y = a b; // побитная операция " И" над входами assign z = y; // редукция, объединение выходов y через операцию "И" и получение выхода //Z endmodule
(рис 1.14)
Возможно присваивание при объявлении, например, wire [2:0] z = x + y;.
Процедурный блок initial используется в основном для симуляции. Эта конструкция позволяет задавать начальные условия для симулируемой схемы, такие как инициализация регистров или создание масок и тестов.
В симуляциях блоки initial выполняются только один раз в момент старта симуляции. Это делает их идеальными для настройки начальных значений переменных и запуск тестов.
Блок always в Verilog предназначен для описания синхронных и асинхронных схем, а также для сложных комбинационных схем. Он используется для определения логики управления, где состояние выходных сигналов зависит от состояния входных сигналов.
Пример: always @ (posedge clock) . Приведенный код позволяет запускать логику только по фронту тактового сигнала.
В синхронных схемах данный процедурный блок позволяет задавать поведение схемы при определенном тактовом сигнале, что используется для создания регистров и других синхронных элементов.
В асинхронных схемах always применяется для описания поведения схем, которые реагируют на изменения входных сигналов немедленно, без учета тактового сигнала.
Общий вид записи: блока always @(<сигналы>) <действия>, где <сигналы> - это список всех входных сигналов, к которым чувствителен блок. Это список входных сигналов, изменение которых влияет на выходные сигналы этого блока. Выполнение оператора или операторов в блоке <действия> будет приостановлено, до тех пор, пока не произойдет изменение какого-либо из сигналов в списке чувствительности.
В списке чувствительности имена входных сигналов разделяются ключевым словом "or" , например, always @(a or b or d) <statements>.
Иногда гораздо проще и надежней включать в список чувствительности все сигналы. Это делается аналогично примеру: always @* <statements>.
Для присвоения значения сигналам в процедурных блоках initial и always применяются типы данных из класса переменных reg.
Как было отмечено выше reg - переменная, занимающая в памяти определенное число битов (диапазон *… : …+), по умолчанию - 1 бит.
Примеры объявления переменных:
reg a, b, c ; // три скалярные однобитовые переменные
reg signed [7:0] d1, d2; // две 8-битовые переменные со знаком
reg [7:0] Q[0:3][0:15] ; // двумерный массив 8-битовых переменных
reg clock = 0, reset=1; // две переменные типа reg с начальными значениями.
При использовании конструкции always для комбинационных и асинхронных последовательностных схем применяется оператор блокирующего присваивания (=). Операторы выполняются один за другим. Пример:
always @(*) //анализируем все сигналы begin y=ab; z= a||b; end
Для проектирования последовательностной синхронной логики в блоке always применяется неблокирующее присваивание (<=). В данном случае список чувствительности содержит сигналы от значения которых зависит, когда произойдет сохранение значения переменной.
Пример:
wire d, clk; reg q; always @ (posedge clk) q<=d;
Пример неблокирующего присваивания для получения последовательного регистра приведен в листинге 1.5.
module nonb_trigger ( input d,clk, output reg q,q1,q2,q3); always @(posedge clk) begin q<=d; q1<=q; q2<=q1; q3<=q2; end endmodule
Из описания модуля в листинге видно, что значения всех переменных формируются по фронту clk. Первый оператор присваивает переменной q значение с входа d, но при этом другие присваивания не блокируются. Так как значение d еще неизвестно, а оно будет известно только в следующем такте фронта clk, поэтому следует что значение на входе q1 изменится на следующем такте.
RTL представление модуля представлено на рисунке 1.15.
(рис 1.15)
Для описания условий в Verilog применяются условия стандартные для высокоуровневых языков программирования. Пример приведен в листинге 1.4.
Общий вид записи:
if(<условие>) <действие1>; else <действие 2>;
Пример, описывающий применение данной конструкции, показан для D-триггера в листинге 1.6. Если на вход rst поступает лог. "0", триггер сбрасывается. В противном случае происходит неблокирующее присваивание переменной q данных с входа d по фронту сигнала clk.
always @(posedge clk or negedge rst_n) // Анализируем два сигнала begin if (!rst_n) // если вход rst_n переходит в 0 - спад q <= 1'b0; // сбрасываем выход else q <= d; // иначе на выход поступают данные или с переменной d end
Также в Verilog возможно применение конструкции else if, то есть проверка работы предыдущего условия. Если оно не исполнено, то осуществляется обработка условия в конструкции eslse-if, последующие конструкции не обрабатываются.
В листинге 1.7 приведен код для реализации двухразрядного дешифратора с применением конструкции eslse-if. На рисунке 1.16 приведено RTL представление модуля дешифратора.
module shifr (
input [1:0] in, // вход (2 разряда)
output reg [3:0] out // выход (4 разряда)
);
// проверка изменения сигналов на входе
always @(*) begin
// сброс выхода
out = 4'b0000;
// Дешифрация входного кода
if (in == 2'b00)
out = 4'b0001; // активен выход 0
else if (in == 2'b01)
out = 4'b0010; // активен выход 1
else if (in == 2'b10)
out = 4'b0100; // активен выход 2
else if (in == 2'b11)
out = 4'b1000; // активен выход 3
else out=4'bxxxx;
end
endmodule
(рис 1.16)
Использование оператора if вводит большие задержки, потому что подразумевает вычисление выражения для сигналов (при моделировании работы схемы) в том порядке, в котором они появляются в модели (выражение в конечной ветви оператора if вычисляется последним).
Правильным и более выразительным вариантом проверки входных условий может быть метод case, который позволяет сделать выбор из многих вариантов (на языке схемотехники мультиплексор со многими входами).
Конструкция case схожа по синтаксису и формату применения с высокоуровневыми языками программирования.
Базовый синтаксис следующий:
case (<проверяемый операнд>) option1: <действие>; option2: <действие >; default: <если не работают условия выше>; endcase
В разделе "Комбинационные схемы" данного курса данная конструкция применяется при описании дешифратора семисегментного индикатора.
В листинге 1.8 приведен пример работы с case для описанного выше двухразрядного дешифратора.
module shifr1 (
input [1:0] in, //вход (2 разряда)
output reg [3:0] out // выход (4 разряда)
);
// проверка изменения сигналов на входе
always @(*) begin
// сброс выходов
out = 4'b0000;
// Дешифрация входного кода
case (in)
2'b00: out = 4'b0001; // активен выход 0
2'b01: out = 4'b0010; // активен выход 1
2'b10: out = 4'b0100; // активен выход 2
2'b11: out = 4'b1000; // активен выход 3
default: out = 4'b0000; // по умолчанию сброшен
endcase
end
endmodule
На рисунке 1.17 приведена RTL схема данного модуля. Как видно из рисунка схема получила вид дешифратора.
(рис 1.17)
Для сокращения кода описываемого узла или устройства можно сводить повторяющиеся блоки в функции. Синтаксис функций схож с языком С.
Базовый синтаксис следующий:
Function [<возвращаемый тип>] <имя> ([список портов]) <действия> Endfunction
В листинге 1.9 приведен пример функции, реализующей 8-разрядный сумматора.
function [7:0] sum (input [7:0] a,b);// объявление функции портов и выходного тип данных и //аргумента begin sum=a+b;// реализуемая функция end endfunction
На данном этапе обзор языка Verilog и его основных конструкций закончен.
С объемными материалами по разработке на FPGA с использованием Verilog можно ознакомиться:
Также рекомендуется изучение и применение дополнительной литературы и информационных источников:
по среде Logisim
По языку Verilog HDL и среде Intel Quartus Prime Lite Edition
Представленный Вашему вниманию курс "Проектирование процессоров RISC-V в среде Logisim и на языке Verilog в среде Quartus" представляет собой труд, направленный на последовательное освоение основ цифровой схемотехники результатом которого станет создание простого ядра процессора RISC-V.
При создании курса автор был нацелен обеспечить слушателей простым и доступным входом в освоение базовых основ цифрой электроники и открытой архитектуры RISC-V.
Теоретический материал дополняется обширной практической частью, в рамках которой изучаются ключевые блоки цифровых устройств: комбинационные и последовательностные схемы, регистры, счетчики, арифметико-логические устройства, модули памяти и дешифраторы.
Для освоения материала предлагается использование известной в академической среде программы моделирования устройств Logisim Evolution. Материал курса по данной среде является единственным открытым русскоязычным текстовым учебным пособием.
Помимо Logisim Evolution в курсе рассматриваются методы работы в среде Intel Quartus Prime, включая использование языка описания аппаратуры Verilog HDL для разработки цифровых модулей.
Подход в изучении основ схемотехнического проектирования устройств, совмещённый с их описанием на языке описания аппаратуры, позволит лучше понять принципы организации и функционирования как отдельных модулей, так и процессора в целом.
В результате прохождения курса слушатель должен получить базовое понимание фундаментальных принципов цифровой электроники и работы современных микропроцессоров.
Получить основные навыки работы с графическими инструментами проектирования Logisim Evolution, а также навыки создания модулей на языке Verilog HDL в среде Quartus Prime.
Иметь представление об архитектуре современных процессоров и понимать принцип построения и функционирования процессоров RISC-V.
Презентация по установке инструментальных сред
Видеолекция. Основы Logisim (часть 1)
Видеолекция. Основы Logisim (часть 2)
Видеолекция. Описание работы в среде Quartus Prime Lite.
Для синтеза цифровых устройств глобально можно определить несколько методов: схемотехническое построение устройств и текстовое на языках описания аппаратуры.
Язык Verilog является одним из самых популярных языков описания аппаратуры (HDL), используемым для проектирования и моделирования работы цифровых и микропроцессорных систем. Удобство, выразительность, а также широкая поддержка языка со стороны инструментов проектирования, сделали Verilog неотъемлемой частью индустрии разработки цифровых устройств.
Verilog был разработан в 1984 году как язык описания аппаратуры, который позволяет проектировщикам описывать логику цифровых схем на высоком уровне абстракции. Он обеспечивает средства для описания как структурной, так и поведенческой модели аппаратуры. Структурное описание позволяет проектировать систему как комбинацию компонентов, тогда как поведенческое описание фокусируется на логике работы системы.
Язык поддерживает различные уровни абстракции, что позволяет инженерам сосредоточиться на нужной детали в зависимости от этапа проектирования.
Язык Verilog обладает следующими преимуществами:
Необходимо выделить ряд недостатков, которые по мере освоения языка начинающим разработчиком могут быть сняты:
В целом необходимо выделить ряд существенных преимуществ языков описания аппаратуры над схемотехническим вводом:
В литературе можно встретить описание языков описания аппаратуры как составной системы из двух компонент: общеалгоритмической и проблемно-ориентированной.
Алгоритмическая компонента языка Verilog заключается в схожести его семантики и синтаксиса с Си подобными языками. Язык содержит операторы, типы данных, процедурные блоки и функции.
Проблемно-ориентированная компонента заключается в наличии интерфейсов, в том числе данных физического типа, сигналов, конкретизации применяемых компонентов.
Освоение языка можно начать с формирования представления о зарезервированных словах.
В языке Verilog определены следующие зарезервированные слова, применяемые при реализации описания схем - модулей. Соответственно для применения в качестве имен переменных, констант их использовать нельзя. Зарезервированные слова Verilog приведены в таблице 1.1.
always and assign attribute automatic begin buf bufif0 bufif1 case casex casez cell cmos config deassign default defparam disable edge else end endattribute endcase endconfig endfunction realtime |
endgenerate endmodule endprimitive endspecify endtable endtask event for force forever fork function generate genvar highz0 highz1 if ifnone incdir include instance initial inout input integer join large |
liblist library localparam micromodule medium module nand negedge nmos nor not notif0 notif1 nowshowcancelled or output parameter pmos posedge primitive pull0 pull1 pulldown pullup pulsestyle_ondetect pulsestyle_onevent remos |
real reg release repeat rnmos rpmos rtran rtranif0 rtranif1 scalared signed small specify showcancelled specparam strength strong0 strong1 supply0 s upply1 table task time tran tranif0 tranif1 tri |
tri0 tri1 triand trior trireg unsigned use vectored wait wand weak0 weak1 while wire wor xnor xor |
Необходимо закрепить базовые понятия, которыми необходимо руководствоваться при описании аппаратуры:
При написании программного кода разработчик должен оставлять комментарии. В Verilog также существуют средства обозначения комментариев. Комментарии делятся на:
Однострочными комментариями можно закомментировать часть одной строки или всю строку полностью.
Описание каждого объекта аппаратуры осуществляется в модулях и размещается между зарезервированными словами: module и endmodule.
Создаваемые объекты должны иметь интерфейсы - порты.
Порты в Verilog бывают трёх типов:
Порт объявляется следующим образом: тип [разрядность] имя порта;.
Если не задана спецификация диапазона, например input [2:0] a, то считается, что порт имеет ширину одного бита, а передаваемые сигналы считаются скалярными. Если порт представляет собой шину, то есть имеет диапазон, то передаваемые сигналы считаются векторными.
Порты могут иметь в объявлении константные выражения, через зарезервированное слова parameter. Также могут объявляться внутренние цепи через зарезервированное слово wire.
Существует еще один тип источника сигнала reg. Данный тип при описании комбинационной логики ведет себя как переменная - провод wire. При проектировании синхронных схем сигнал reg способен сохранять значение, т.е. является триггером или регистром.
Сигналы или переменные могут быть представлены следующими логическими уровнями:
Пример описания модуля приведен в листинге 1.1, где красным выделены зарезервированные слова, зеленым - пользовательские имена и коментарии, синим функционал.
module name_module #(parameter M=4) // константа (input [3:0] A,B, // четырехразрядные входы А и B input cin,// однопроводная линия output [M-1:0] S, //выход использующий для определения разрядности //константу output co); //выход линия wire [4:0] sum; // внутренние сигналы assign sum = A + B + cin; // Суммируем A, B и cin assign S = sum[M-1:0]; // Выход S - последние M разрядов суммы assign co = sum[4]; // Выход переноса endmodule
Для начала решения проблемно-ориентированных задач, следует остановиться на важнейшем разделе любого проекта - определение этапов выполнения проекта:
Сформируем следующую задачу: необходимо разработать и реализовать схему с тремя входами для мажоритарного контроля, используя язык описания аппаратуры Verilog. Схема должна сравнивать три входа и выдавать сигнал, равный лог. "1" если два или более входов равны лог. "1".
Уточним требования к интерфейсу проектируемой схемы и уровням сигналов реакции:
Входные сигналы:
Выполним математическое моделирование. Для этого сформируем по условиям таблицу истинности (таблица 1.2).
| п/п | Входы | Выход | ||
|---|---|---|---|---|
| A | b | c | y | |
| 1 | 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 1 | 0 |
| 3 | 0 | 1 | 0 | 0 |
| 4 | 0 | 1 | 1 | 1 |
| 5 | 1 | 0 | 0 | 0 |
| 6 | 1 | 0 | 1 | 1 |
| 7 | 1 | 1 | 0 | 1 |
| 8 | 1 | 1 | 1 | 1 |
Проведем анализ таблицы 1.2 истинности и определим строки, в которых комбинация входных сигналов обеспечит лог. "1" на выходе. В данном случае это 4, 6,7,8 строки. Выпишем их:
Сформируем СКНФ из данных:
$$y=a*b*\bar c+a*\bar b*c+]bar a*b*c+a*b*c$$После преобразований формула примет вид:
$$y=(a*b)+(b*c)+(a*c)$$Введем проект на заявленном языке Verilog. Определим имя модуля как major, тип сигнала интерфейса - wire. Имена сигналов были заданы в условии.
Используя процедурный оператор assign, введем функцию, описанную выше на языке Verilog как показано в листинге 1.2.
module major ( // начало модуля
input wire a, // первый вход
input wire b, // второй вход
input wire c, // третий вход
output wire y // выход мажоритарного значения
);
assign y = (a b) | (b c) | (c a); // реализуемая функция
endmodule // окончание модуля
При создании цифровых устройств одним из сложных и самых трудоемких этапов является процесс верификации. Данный этап может отвлекать от 50 до 80% всего времени выполнения проекта.
Верификация - это процесс установления соответствия созданного устройства, техническим условиям спецификации из технического задания.
В рамках верификации выполняются различные тесты, причем могут вноситься изменения в конечную спецификацию устройства.
В рамках выполнения верификации одним из этапов является процедура функциональной верификации - проверка на наличие ошибок в проекте. Для выполнения функциональной верификации применим инструменты симуляции работы схемы.
Так как разработанный выше модуль является достаточно простым, то его функциональная верификация будет несложной - сверка таблицы истинности и временной диаграммы работы. Но наша задача получить первичное представление о верификации и методах ее проведения.
Для проверки разработанного кода перенесем его в среду проектирования Intel Quartus.
В открывшейся форме (рисунок 1.1), добавим входы и выход модуля через меню "Edit" - "Insert" - "Insert Node or Bus" (рисунок 1.2). Нажав на кнопку "Node Finder", перейдем на форму выбора сигналов. Так как мы выбираем все сигналы, нажмем на кнопку "List". Исследуемые сигналы (все) переместим в поле "Selected Nodes", нажав на кнопку >> (рисунок 1.3).
(рис 1.1)
(рис 1.2)
(рис 1.3)
Необходимо исключить один из параметров работы симулятора - noopt. Для этого перейти в меню "Simulation" - "Simulation Settings".
В открывшемся окне, найдем данный параметр и удалим (рисунок 1.4).
(рис 1.4)

(рис 1.5)
(рис 1.6)
Более совершенные методы верификации подразумевают применение тестирующего оборудования или отладочного модуля, именуемого testbench. Testbench генерирует набор сигналов для проектируемого устройства, а затем принимает их для последующего анализа.
Стратегия работы с тестовым окружением - поочередная подача восьми возможных комбинаций на входы a,b,c
Для создания testbench необходимо рассмотреть основы синтаксиса данных файлов.
Первая строка кода testbench может содержать директиву масштаба модельного времени, определяющая значение залежек модели, которая начинается с обратной кавычки: `timescale. С этой директивой указываются временные параметры: N1 - определяет единицы времени задержки сигналов, следующих за символом #; N2 - точность расчета.
Оба параметра указываются в наносекундах или пикосекундах и могут принимать значения только 1, 10 или 100. По умолчанию назначаются ps.
Пример: `timescale 1ns/1ns
В следующей строке тестового модуля указывается ключевое слово module и имя модуля с обязательной приставкой tb, указывающей на то, что это тестовый модуль проекта.
Так как это тестовый модуль, то он не имеет портов. В него вставляется тестируемый модуль и входы/ выходы тестируемого модуля взаимодействуют с переменными типа reg и wire.
Например, для тестируемого модуля будет применено следующее объявление:
reg a; reg b; reg c; wire y;
Следующей частью кода является инстанцирование тестируемого модуля в тестовый модуль. При этом в списке портов устанавливается соответствие между портами тестового и тестируемого модулей. Модуль testbench в общем случае, может иметь отличные от тестируемого имена входов/выходов.
После имени тестируемого модуля должен быть указан тип подсистемы, которая тестируется:
Пример инстанцирования для рассматриваемого примера:
major uut (
.a(a),
.b(b),
.c(c)
Далее с помощью конструкций, определяемых ключевыми словами initial begin…end выполняется моделирование.
Конструкции содержат описания начальных значений и дальнейшего поведения входных сигналов во времени. Блок initial задает симулятору точку, с которой начинается работа. Следует отметить, что тестовый модуль без блока initial не работает.
Следующим этапом указываются варианты поведения сигналов и указание временных единиц (задержки) для их смены.
Также можно применять команды для вывода информации во время симуляции:
Пример: $monitor("a=%b, b=%b, c=%b, y=%b", $time, a, b, c, y);
Далее указывается одна из конструкций системной директивы $finish, которая завершает процесс моделирования. И наконец, ключевое слово endmodule указывает на окончание описания тестового модуля.
В листинге 1.3 приведен исходный код тестового модуля tb_major
`timescale 1ns/1ns
module tb_major;
reg a;
reg b;
reg c;
wire y;
major uut (
.a(a),
.b(b),
.c(c),
.y(y)
);
initial begin
$monitor("a=%b, b=%b, c=%b, y=%b", $time, a, b, c, y);
a = 0; b = 0; c = 0;//y=0
#10;
a = 0; b = 0; c = 1;//y=0
#10;
a = 0; b = 1; c = 0;//y=0
#10;
a = 0; b = 1; c = 1;//y=1
#10;
a = 1; b = 0; c = 0;//y=0
#10;
a = 1; b = 0; c = 1;//y=1
#10;
a = 1; b = 1; c = 0;//y=1
#10;
a = 1; b = 1; c = 1;//y=1
#10;
$finish;
end
endmodule
Для реализации верификации с использованием созданного тестового модуля необходимо выполнить ряд манипуляций с проектом.
Перейти в меню "Assigments" - "Settings". В открывшемся окне, перейти в раздел "EDA Tool Settings". В строке "Simulation" для столбца "Tool Name" выбрать Modelsim-Altera. В столбце "Format(s) " выбрать Verilog HDL (рисунок 1.8). Нажать кнопки Apply и OK.
(рис 1.7)
(рис 1.8)
Далее в меню пройти в меню "Assigments" - "Settings". В открывшемся окне, перейти в подраздел "Simulation" раздела "EDA Tool Settings". В окне настроек установить метку "Compile test bench" (рисунок 1.9).
(рис 1.9)
Нажать кнопку Test Benches. В новом окне (рисунок 1.10), нажать накнопку New.
(рис 1.10)
В диалоговом окне, приведенном на рисунке 1.11, в поле Test bench name ввести tb_major. Далее добавить файл, нажав на кнопку "… " поля "File name". Выбрать файл и нажать кнопку Add, затем кнопку ОК.
(рис 1.11)
Вернувшись в окно Test Benches, убедиться, что файл добавлен (рисунок 1.12). Далее нажать OK.
(рис 1.12)
(рис 1.13)
Необходимо отметить, что качественный testbench должен содержать код самопроверки, то есть операторы, отслеживающие возникновение ошибки в ходе работы.
Для представления целых чисел применяется следующий формат записи: size'base value, где size - размер, определяющий систему счисления, в которой представлено число, base - система счисления числа, value - непосредственное значение.
Для обозначения базы применяются следующие символы:
Примеры:
4'b1010 - двоичное четырёхразрядное число (в десятичной системе 10);
6'o71 - шестиразрядное восьмеричное число;
8'd127 - восьмиразрядное десятичное число 127;
127 - десятичное число (по умолчанию).
Для представления действительных чисел в Verilog применяется запись либо c десятичной точкой, либо в экспоненциальном виде. Запись с точкой имеет вид: value_i.value_f, где: value_i - целая часть, value_f - дробная часть
Примеры:
1.8
3.8e10 // e или E для равно $$3,8*10^{10}$$
2.1e-9// 2,1*$$10^{-9}$$
Выражения и операции на языке Verilog, применяющие примитивы относятся к следующим видам:
В таблице 1.3 приведены операции языка Verilog для построения выражений.
| Побитные операторы | |
| ~ | инверсия |
| побитное "И" | |
| | | побитное "ИЛИ" |
| ^ | побитное "Исключающее ИЛИ" |
| ~^ или ^~ | побитное "Исключающее ИЛИ НЕ" |
| Логические операторы | |
| ! | логическое "НЕ" |
| AND | логическое "И" |
| || OR | логическое "ИЛИ" |
| Редукция | |
| редуцированное "И" | |
| ~ | редуцированное "НЕ-И" |
| | | редуцированное "ИЛИ" |
| ~| | редуцированное "НЕ-ИЛИ" |
| ^ | редуцированное "Исключающее ИЛИ" |
| ~^ или ^~ | редуцированное "Исключающее ИЛИ НЕ" |
| Арифметические операторы | |
| + | сложение |
| - | вычитание |
| * | умножение |
| / | деление |
| Отношение | |
| > | больше |
| < | меньше |
| >= | больше либо равно |
| <= | меньше либо равно |
| = = | логическое равенство |
| != | логическое неравно |
| = = = | 4 state логическое равенство |
| != = | 4 state логическое неравно |
| Операции сдвига | |
| >> | логический сдвиг вправо |
| << | логический сдвиг влево |
| >>> | арифметический сдвиг вправо |
| <<< | арифметический сдвиг влево |
| Разное | |
| { , } | склеивание |
| {n{m}} | копирует m значение n раз |
| ? : | условие |
В листинге 1.3 приведен пример организации комбинационной схемы сравнения, использующей операторы отношения.
module comparator ( input [7:0] A, B, output O_EQ, O_NE, O_BR, O_MR, O_BOL, O_MEN); assign O_EQ = A == B; assign O_NE = A != B; assign O_BR = A >= B; assign O_MR = A <= B; assign O_BOL = A >B; assign O_MEN = A < B; endmodule
Оператор assign в Verilog предназначен для описания непрерывных присвоений в структуре цифровых схем. Этот оператор используется для связывания значений между сигналами и переменными.
Назначение оператора assign:
В листинге 1.4 дается пример применения оператора assign. На рисунке 1.14 можно наблюдать результат на уровне RTL.
module logic_operators ( input [3:0] a, b, // объявление входных векторных портов output [3:0] y, // объявление выходного векторного порта output z); assign y = a b; // побитная операция " И" над входами assign z = y; // редукция, объединение выходов y через операцию "И" и получение выхода //Z endmodule
(рис 1.14)
Возможно присваивание при объявлении, например, wire [2:0] z = x + y;.
Процедурный блок initial используется в основном для симуляции. Эта конструкция позволяет задавать начальные условия для симулируемой схемы, такие как инициализация регистров или создание масок и тестов.
В симуляциях блоки initial выполняются только один раз в момент старта симуляции. Это делает их идеальными для настройки начальных значений переменных и запуск тестов.
Блок always в Verilog предназначен для описания синхронных и асинхронных схем, а также для сложных комбинационных схем. Он используется для определения логики управления, где состояние выходных сигналов зависит от состояния входных сигналов.
Пример: always @ (posedge clock) . Приведенный код позволяет запускать логику только по фронту тактового сигнала.
В синхронных схемах данный процедурный блок позволяет задавать поведение схемы при определенном тактовом сигнале, что используется для создания регистров и других синхронных элементов.
В асинхронных схемах always применяется для описания поведения схем, которые реагируют на изменения входных сигналов немедленно, без учета тактового сигнала.
Общий вид записи: блока always @(<сигналы>) <действия>, где <сигналы> - это список всех входных сигналов, к которым чувствителен блок. Это список входных сигналов, изменение которых влияет на выходные сигналы этого блока. Выполнение оператора или операторов в блоке <действия> будет приостановлено, до тех пор, пока не произойдет изменение какого-либо из сигналов в списке чувствительности.
В списке чувствительности имена входных сигналов разделяются ключевым словом "or" , например, always @(a or b or d) <statements>.
Иногда гораздо проще и надежней включать в список чувствительности все сигналы. Это делается аналогично примеру: always @* <statements>.
Для присвоения значения сигналам в процедурных блоках initial и always применяются типы данных из класса переменных reg.
Как было отмечено выше reg - переменная, занимающая в памяти определенное число битов (диапазон *… : …+), по умолчанию - 1 бит.
Примеры объявления переменных:
reg a, b, c ; // три скалярные однобитовые переменные
reg signed [7:0] d1, d2; // две 8-битовые переменные со знаком
reg [7:0] Q[0:3][0:15] ; // двумерный массив 8-битовых переменных
reg clock = 0, reset=1; // две переменные типа reg с начальными значениями.
При использовании конструкции always для комбинационных и асинхронных последовательностных схем применяется оператор блокирующего присваивания (=). Операторы выполняются один за другим. Пример:
always @(*) //анализируем все сигналы begin y=ab; z= a||b; end
Для проектирования последовательностной синхронной логики в блоке always применяется неблокирующее присваивание (<=). В данном случае список чувствительности содержит сигналы от значения которых зависит, когда произойдет сохранение значения переменной.
Пример:
wire d, clk; reg q; always @ (posedge clk) q<=d;
Пример неблокирующего присваивания для получения последовательного регистра приведен в листинге 1.5.
module nonb_trigger ( input d,clk, output reg q,q1,q2,q3); always @(posedge clk) begin q<=d; q1<=q; q2<=q1; q3<=q2; end endmodule
Из описания модуля в листинге видно, что значения всех переменных формируются по фронту clk. Первый оператор присваивает переменной q значение с входа d, но при этом другие присваивания не блокируются. Так как значение d еще неизвестно, а оно будет известно только в следующем такте фронта clk, поэтому следует что значение на входе q1 изменится на следующем такте.
RTL представление модуля представлено на рисунке 1.15.
(рис 1.15)
Для описания условий в Verilog применяются условия стандартные для высокоуровневых языков программирования. Пример приведен в листинге 1.4.
Общий вид записи:
if(<условие>) <действие1>; else <действие 2>;
Пример, описывающий применение данной конструкции, показан для D-триггера в листинге 1.6. Если на вход rst поступает лог. "0", триггер сбрасывается. В противном случае происходит неблокирующее присваивание переменной q данных с входа d по фронту сигнала clk.
always @(posedge clk or negedge rst_n) // Анализируем два сигнала begin if (!rst_n) // если вход rst_n переходит в 0 - спад q <= 1'b0; // сбрасываем выход else q <= d; // иначе на выход поступают данные или с переменной d end
Также в Verilog возможно применение конструкции else if, то есть проверка работы предыдущего условия. Если оно не исполнено, то осуществляется обработка условия в конструкции eslse-if, последующие конструкции не обрабатываются.
В листинге 1.7 приведен код для реализации двухразрядного дешифратора с применением конструкции eslse-if. На рисунке 1.16 приведено RTL представление модуля дешифратора.
module shifr (
input [1:0] in, // вход (2 разряда)
output reg [3:0] out // выход (4 разряда)
);
// проверка изменения сигналов на входе
always @(*) begin
// сброс выхода
out = 4'b0000;
// Дешифрация входного кода
if (in == 2'b00)
out = 4'b0001; // активен выход 0
else if (in == 2'b01)
out = 4'b0010; // активен выход 1
else if (in == 2'b10)
out = 4'b0100; // активен выход 2
else if (in == 2'b11)
out = 4'b1000; // активен выход 3
else out=4'bxxxx;
end
endmodule
(рис 1.16)
Использование оператора if вводит большие задержки, потому что подразумевает вычисление выражения для сигналов (при моделировании работы схемы) в том порядке, в котором они появляются в модели (выражение в конечной ветви оператора if вычисляется последним).
Правильным и более выразительным вариантом проверки входных условий может быть метод case, который позволяет сделать выбор из многих вариантов (на языке схемотехники мультиплексор со многими входами).
Конструкция case схожа по синтаксису и формату применения с высокоуровневыми языками программирования.
Базовый синтаксис следующий:
case (<проверяемый операнд>) option1: <действие>; option2: <действие >; default: <если не работают условия выше>; endcase
В разделе "Комбинационные схемы" данного курса данная конструкция применяется при описании дешифратора семисегментного индикатора.
В листинге 1.8 приведен пример работы с case для описанного выше двухразрядного дешифратора.
module shifr1 (
input [1:0] in, //вход (2 разряда)
output reg [3:0] out // выход (4 разряда)
);
// проверка изменения сигналов на входе
always @(*) begin
// сброс выходов
out = 4'b0000;
// Дешифрация входного кода
case (in)
2'b00: out = 4'b0001; // активен выход 0
2'b01: out = 4'b0010; // активен выход 1
2'b10: out = 4'b0100; // активен выход 2
2'b11: out = 4'b1000; // активен выход 3
default: out = 4'b0000; // по умолчанию сброшен
endcase
end
endmodule
На рисунке 1.17 приведена RTL схема данного модуля. Как видно из рисунка схема получила вид дешифратора.
(рис 1.17)
Для сокращения кода описываемого узла или устройства можно сводить повторяющиеся блоки в функции. Синтаксис функций схож с языком С.
Базовый синтаксис следующий:
Function [<возвращаемый тип>] <имя> ([список портов]) <действия> Endfunction
В листинге 1.9 приведен пример функции, реализующей 8-разрядный сумматора.
function [7:0] sum (input [7:0] a,b);// объявление функции портов и выходного тип данных и //аргумента begin sum=a+b;// реализуемая функция end endfunction
На данном этапе обзор языка Verilog и его основных конструкций закончен.
С объемными материалами по разработке на FPGA с использованием Verilog можно ознакомиться:
Также рекомендуется изучение и применение дополнительной литературы и информационных источников:
по среде Logisim
По языку Verilog HDL и среде Intel Quartus Prime Lite Edition
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.