Хабрахабр

[Из песочницы] Использование Quartus и ModelSim

Меня всегда интересовала цифровая схемотехника, а в частности языки описания аппаратуры — HDL. У меня давно лежала в списке будущего чтения книга Дэвида М. Хэррис и Сары Л. Хэррис «Цифровая схемотехника и архитектура компьютера», воспользовавшись свободным временем на самоизоляции, я добрался до этой замечательной книги. В процессе чтения я столкнулся с некоторыми трудностями, а в частности, как именно писать и отлаживать код в Quartus Prime. В процессе поисков мне очень помог сайт marsohod.org, но вот процесс симуляции схемы на этом сайте описан с использованием встроенных средств Quartus и в современных версиях программы, этих встроенных средств нет и необходимо использовать ModelSim. Чтобы как-то систематизировать те знания, которые я получил, используя Quartus и ModelSim, я решил написать эту статью. В процессе этой статьи я в качестве примера разберу задачу из книги Дэвида М. Хэррис и Сары Л. Хэррис «Цифровая схемотехника и архитектура компьютера», а конкретно задачу 3.26 про автомат газированной воды. На протяжении статьи я покажу, как установить Quartus, создать проект, написать код и произвести его симуляцию. Всем кому это будет интересно, добро пожаловать под кат.

image

Вас уговорили спроектировать автомат с прохладительными напитками для офиса. Расходы на напитки частично покрывает профсоюз, поэтому они стоят всего по 5 рублей. Автомат принимает монеты в 1, 2 и 5 рублей. Как только покупатель внесет необходимую сумму, автомат выдаст напиток и сдаст сдачу. Спроектируйте конечный автомат для автомата с прохладительными напитками. Входами автомата являются 1, 2 и 5 рублей, а именно, какая из этих монет вставлена.

Предположим, что по каждому тактовому сигналу вставляется только одна монета. Автомат имеет выходы: налить газировку, вернуть 1 рубль, вернуть 2 рубля, вернуть 2 по 2 рубля. Как только в автомате набирается 5 рублей (или больше), он выставляет сигнал «НАЛИТЬ ГАЗИРОВКУ», а также сигналы, возврата соответствующей сдачи. Затем автомат должен быть готов опять принимать монеты.

Конечные автоматы или finite state machine (FSM) относятся к классу синхронных последовательных схем, которые представляют подавляющее большинство схем цифровой схемотехники. Именно таким образом следует реализовывать свои проекты (по крайней мере на первых порах). Такой способ обеспечивает повторяемость и верифицированость схемы и не зависит от отношений задержек различных элементов схемы. Правила построения синхронных последовательных схем гласят, что схема является синхронной последовательной схемой, если ее элементы удовлетворяют следующим условиям:

  • Каждый элемент схемы является либо регистром, либо комбинационной схемой.
  • Как минимум один элемент схемы является регистром.
  • Все регистры тактируются единственным тактовым сигналом.
  • В каждом циклическом пути присутствует как минимум один регистр.

Конечный автомат имеет несколько состояний, которое он хранит в регистрах. При поступлении тактового сигнала, конечный автомат может изменять свое состояние, причем как именно изменится состояние зависит от входных сигналов и текущего состояния. В простейшем случае входных сигналов может не быть вовсе, таким образом работает делитель частоты. Существуют два основных класса конечных автоматов: автомат Мура, в котором выходные сигналы зависят только от текущего состояния автомата и автомат Мили, в котором выходные сигналы зависят от текущего состояния и входных сигналов. В принципе любой конечный автомат можно реализовать как по схеме Мура, так и по схеме Мили, отличие между ними будет в том, что у автомата Мура будет больше состояний и он будет отставать на один такт от автомата Мили. Для схемы автомата газированной воды я буду использовать схему Мили. Распишем состояния конечного автомата:
В качестве входного сигнала, будет выступать двухбитная шина, со следующим кодированием номинала монеты:
Нарисуем диаграмму состояний нашего автомата (на диаграммах состояния автомата Мили, необходимо указывать выходные сигналы на стрелках перехода состояния, я этого делать не буду, чтобы не загромождать диаграмму, все выходные сигнала будут расписаны в таблице ниже):

image

Распишем таблицу изменения состояний и выходных сигналов:

Установка Quartus Prime

Quartus имеет бесплатную Lite Edition, которая имеет некоторые ограничения по сравнению с профессиональной редакцией, основное ограничение это не более 10000 строк исходного кода для симуляции проекта. Скачать её, после регистрации, можно по ссылке, на момент написания статьи наиболее свежая версия была 19.1, на основе работы с этой версией я писал статью. Выбираем Lite Edition, версия 19.1, операционная система Windows (надо отметить, что существует версия Quartus для Linux и она отлично работает, проблемы возникают с ModelSim, которая 32 битная и использует старую версию библиотеки отображения шрифтов, поэтому на первых порах я рекомендую использовать Windows версию), выбираем вкладку Combined Files. Размер архива для скачки весьма большой — 5.6 Gb, учитывайте это. Разворачиваем скаченный архив и запускаем setup.bat. Установка проходит стандартным образом, используем выбор компонентов по умолчанию.

Создание проекта

Для создания нового проекта выберем File -> New Project Wizard.... Первое окно Wizard'а информационное, жмем Next, на втором окне выбираем где будет располагаться проект, его название «soda_machine» и элемент дизайна верхнего уровня «soda_machine», как на рисунке:

image

В следующем окне выбираем «Empty project». Окно добавление файлов «Add files», ничего не добавляем. Окно выбора устройства «Family, Devices & Board Settings», для реального проекта очень важное, но так как, наш проект учебный и до реального ему далеко, здесь оставляем настройки по умолчанию, как на рисунке:

image

Окно выбора настроек других инструментов «EDA Tool Settings», выбираем для симуляции проекта использовать «ModelSim-Altera» и формат «System Verilog HDL» как на рисунке:

image

Последнее окно информационное «Summary», жмем Finish.

Написание исходного кода

У нас будет два основных файла с исходным кодом, это собственно модуль soda_machine и его test bench, оба эти файла будут использовать тип данных insert_type, который описывает как мы кодируем номиналы монет и его логично выделить в отдельный файл. Но есть некоторые трудности, связанные с особенностями компиляции Quartus и ModelSim. Quartus компилирует все файлы с исходным кодом за один проход, а ModelSim каждый файл компилирует отдельно, для того, что бы при компиляции Quartus'ом не возникало переопределения типа insert_type, я использовал технику из C/C++ include guard, основанную на директивах макропроцессора. Кроме того, что бы ModelSim был уверен что тип insert_type, используемый в модуле soda_machine и в test bench'е, один и тот же, поместил его описание внутри пакета soda_machine_types. С учетом этих требований файл soda_machine_types.sv выглядит следующим образом:

soda_machine_types.sv

`ifndef soda_machine_types_sv_quard package soda_machine_types; typedef enum logic [1:0] {I1=2'b01, I2=2'b10, I5=2'b11} insert_type; endpackage `define soda_machine_types_sv_quard`endif

Теперь собственно модуль soda_machine, находится в файле soda_machine.sv:

soda_machine.sv

`include "soda_machine_types.sv"import soda_machine_types::*; module soda_machine( input logic clk, // Clock input logic reset, // Active high level input insert_type insert, output logic pour_water, output logic change1, output logic change2, output logic change22); typedef enum logic [2:0] {S0, S1, S2, S3, S4} state_type; (* syn_encoding = "default" *) state_type state, nextstate; // Логика сброса и хранения состояния конечного автомата always_ff @(posedge clk, posedge reset) if (reset) state <= S0; else state <= nextstate; // Логика вычисления следующего состояния в зависимости от текущего состояния и входных сигналов always_comb case (state) S0: case (insert) I1: nextstate = S1; I2: nextstate = S2; I5: nextstate = S0; endcase S1: case (insert) I1: nextstate = S2; I2: nextstate = S3; I5: nextstate = S0; endcase S2: case (insert) I1: nextstate = S3; I2: nextstate = S4; I5: nextstate = S0; endcase S3: if (insert == I1) nextstate = S4; else nextstate = S0; S4: nextstate = S0; endcase // Логика получения выходных сигналов assign pour_water = (state == S4) | (insert == I5) | (state == S3) & (insert == I2); assign change1 = (state == S1) & (insert == I5) | (state == S3) & (insert == I5) | (state == S4) & (insert == I2); assign change2 = (state == S2) & (insert == I5) | (state == S3) & (insert == I5); assign change22 = (state == S4) & (insert == I5); endmodule

Каким образом будут производится кодирование состояний конечного автомата, я оставил на усмотрение Quartus. Для того что бы указать, как именно следует производить кодирование, используется атрибут (* syn_encoding = «default» *), другие варианты кодирования можно увидеть здесь.

Для добавления файлов в проект используется File -> New «SystemVerilog HDL File» и при сохранении дать соответствующее имя. После добавления этих двух файлов, проект можно скомпилировать Processing -> Start Compilation. После успешной компиляции можно посмотреть полученную схему Tools -> Netlist Viewers -> RTL Viewer:

RTL Viewer

image

Для просмотра диаграммы состояний конечного автомата Tools -> Netlist Viewers -> State Machine Viewer

State Machine Viewer

image

На вкладке Encoding видно, что Quartus применил схему кодирования «one-hot», это когда для каждого состояния используется отдельный D-триггер, причем состояние S0 кодируется 0, а не 1 как для других состояний, делается это для упрощения схемы сброса в начальное состояние. Можно заметить, что RTL Viewer показывает не совсем принципиальную схему, это скорее концепт. Для просмотра принципиальной схемы использовать Tools -> Netlist Viewrs -> Technology Map Viewer (Post-Fitting)

Симуляция

В принципе на текущий момент у нас есть схема автомата по продаже газированной воды, но необходимо убедится, что она работает правильно, для этого напишем test bench и разместим его в файле soda_machine_tb.sv:

soda_machine_tb.sv

`include "soda_machine_types.sv"import soda_machine_types::*; module soda_machine_tb; insert_type insert; logic [5:0] testvectors[10000:0]; int vectornum, errors; logic clk, reset, pour_water, change1, change2, change22; logic pour_water_expected, change1_expected, change2_expected, change22_expected; // Тестируемый модуль soda_machine dut( .clk(clk), .reset(reset), .insert(insert), .pour_water(pour_water), .change1(change1), .change2(change2), .change22(change22) ); // Эмуляция тактовой частоты always #5 clk = ~clk; // Начальная установка сигналов initial begin // Чтение файла с тестовыми векторами $readmemb("../../soda_machine.tv", testvectors); vectornum = 0; errors = 0; clk = 1; // Выполним сброс схемы reset = 1; #13; reset = 0; end // Установка тестового воздействия always @(posedge clk) begin #1; {insert, pour_water_expected, change1_expected, change2_expected, change22_expected} = testvectors[vectornum]; end // Проверка, соответствуют ли выходные сигналы нашим ожиданиям always @(negedge clk) if (~reset) begin if ((pour_water !== pour_water_expected) || (change1 !== change1_expected) || (change2 !== change2_expected) || (change22 !== change22_expected)) begin $error("%3d test insert=%b\noutputs pour_water=%b (%b expected), change1=%b (%b expected), change2=%b (%b expected), change22=%b (%b expected)", vectornum + 1, insert, pour_water, pour_water_expected, change1, change1_expected, change2, change2_expected, change22, change22_expected); errors = errors + 1; end vectornum = vectornum + 1; if (testvectors[vectornum] === 6'bx) begin $display("Result: %3d tests completed with %3d errors", vectornum, errors); $stop; end end endmodule

Для проверки нашего модуля используется файл тестовых векторов soda_machine.tv:

soda_machine.tv

01_0_0_0_001_0_0_0_001_0_0_0_001_0_0_0_001_1_0_0_010_0_0_0_010_0_0_0_010_1_1_0_011_1_0_0_010_0_0_0_010_0_0_0_011_1_0_0_110_0_0_0_011_1_0_1_001_0_0_0_001_0_0_0_001_0_0_0_011_1_1_1_0

Первые два бита это входной сигнал insert, следующие 4 бита, это наши ожидания выходных сигналов: pour_water, change1, change2, change22. Например в начале файла 5 раз подряд вставляется рублевая монета, на пятой монете, мы ожидаем появления сигнала pour_water, при этом сигналы выдачи сдачи неактивны. Файл soda_machine.tv добавляется в проект File -> New «Text File»

Для удобства работы с ModelSim добавим файл soda_machine_run_simulation.do следующего содержания:

soda_machine_run_simulation.do

add wave /soda_machine_tb/dut/clkadd wave /soda_machine_tb/dut/resetadd wave /soda_machine_tb/dut/insertadd wave /soda_machine_tb/dut/stateadd wave /soda_machine_tb/dut/nextstateadd wave /soda_machine_tb/dut/pour_wateradd wave /soda_machine_tb/dut/change1add wave /soda_machine_tb/dut/change2add wave /soda_machine_tb/dut/change22view structureview signalsrun -allwave zoom full

Он запустит нашу симуляцию и выведет графики сигналов в ModelSim. Файл soda_machine_run_simulation.do добавляется в проект File -> New «Tcl script File»

Теперь настроим проект, что бы автоматически запускалась симуляция. Выбираем пункт меню Assignments -> Settings, выбираем категорию EDA Tool Settings -> Simulation. В настройках NativeLink settings выбираем Compile test bench: и нажимаем кнопку Test Benches... в открывшемся окне Test Benches нажимаем кнопку New... В открывшемся окне New Test Bench Settings заполняем поле Test bench name: soda_machine_tb и нажимаем кнопку выбора файла ... в нижней части окна, выбираем наш файл soda_machine_tb.sv и нажимаем кнопку Add. Должно получиться как на рисунке:

image

В окне New Test Bench Settings нажимаем OK. Окно Test Benches должно получить следующий вид:
image

В окне Test Benches нажимаем OK. В NativeLink settings устанавливаем галочку Use script to set up simulation и выбираем файл soda_machine_run_simulation.do. Окно Settings
должно иметь вид:

image

В окне Settings нажимаем OK, производим компиляцию проекта Processing -> Start Compilation, производим запуск симуляции Tools -> Run Simulation Tool -> RTL Simulation. Должен запустится ModelSim и произойти симуляция проекта. Внешний вид вкладки Transcript:

ModelSim вкладка Transcript

image

Красной рамкой выделен вывод нашего test bench'а о количестве выполненных тестов и обнаруженных ошибках. Внешний вид вкладки Wave:

ModelSim вкладка Wave

image

Исходный код проекта находится в github.com/igoral5/soda_machine Клонировать проект, затем открыть проект при помощи Quartus File -> Open Project...
выбрать файл soda_machine.qpf. Затем скомпилировать проект Processing -> Start Compilation и запустить симуляцию Tools -> Run Simulation Tool -> RTL Simulation.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»