Меня всегда интересовала цифровая схемотехника, а в частности языки описания аппаратуры — HDL. У меня давно лежала в списке будущего чтения книга Дэвида М. Хэррис и Сары Л. Хэррис «Цифровая схемотехника и архитектура компьютера», воспользовавшись свободным временем на самоизоляции, я добрался до этой замечательной книги. В процессе чтения я столкнулся с некоторыми трудностями, а в частности, как именно писать и отлаживать код в Quartus Prime. В процессе поисков мне очень помог сайт marsohod.org, но вот процесс симуляции схемы на этом сайте описан с использованием встроенных средств Quartus и в современных версиях программы, этих встроенных средств нет и необходимо использовать ModelSim. Чтобы как-то систематизировать те знания, которые я получил, используя Quartus и ModelSim, я решил написать эту статью. В процессе этой статьи я в качестве примера разберу задачу из книги Дэвида М. Хэррис и Сары Л. Хэррис «Цифровая схемотехника и архитектура компьютера», а конкретно задачу 3.26 про автомат газированной воды. На протяжении статьи я покажу, как установить Quartus, создать проект, написать код и произвести его симуляцию. Всем кому это будет интересно, добро пожаловать под кат.
Вас уговорили спроектировать автомат с прохладительными напитками для офиса. Расходы на напитки частично покрывает профсоюз, поэтому они стоят всего по 5 рублей. Автомат принимает монеты в 1, 2 и 5 рублей. Как только покупатель внесет необходимую сумму, автомат выдаст напиток и сдаст сдачу. Спроектируйте конечный автомат для автомата с прохладительными напитками. Входами автомата являются 1, 2 и 5 рублей, а именно, какая из этих монет вставлена.
Предположим, что по каждому тактовому сигналу вставляется только одна монета. Автомат имеет выходы: налить газировку, вернуть 1 рубль, вернуть 2 рубля, вернуть 2 по 2 рубля. Как только в автомате набирается 5 рублей (или больше), он выставляет сигнал «НАЛИТЬ ГАЗИРОВКУ», а также сигналы, возврата соответствующей сдачи. Затем автомат должен быть готов опять принимать монеты.
Конечные автоматы или finite state machine (FSM) относятся к классу синхронных последовательных схем, которые представляют подавляющее большинство схем цифровой схемотехники. Именно таким образом следует реализовывать свои проекты (по крайней мере на первых порах). Такой способ обеспечивает повторяемость и верифицированость схемы и не зависит от отношений задержек различных элементов схемы. Правила построения синхронных последовательных схем гласят, что схема является синхронной последовательной схемой, если ее элементы удовлетворяют следующим условиям:
Конечный автомат имеет несколько состояний, которое он хранит в регистрах. При поступлении тактового сигнала, конечный автомат может изменять свое состояние, причем как именно изменится состояние зависит от входных сигналов и текущего состояния. В простейшем случае входных сигналов может не быть вовсе, таким образом работает делитель частоты. Существуют два основных класса конечных автоматов: автомат Мура, в котором выходные сигналы зависят только от текущего состояния автомата и автомат Мили, в котором выходные сигналы зависят от текущего состояния и входных сигналов. В принципе любой конечный автомат можно реализовать как по схеме Мура, так и по схеме Мили, отличие между ними будет в том, что у автомата Мура будет больше состояний и он будет отставать на один такт от автомата Мили. Для схемы автомата газированной воды я буду использовать схему Мили. Распишем состояния конечного автомата:
В качестве входного сигнала, будет выступать двухбитная шина, со следующим кодированием номинала монеты:
Нарисуем диаграмму состояний нашего автомата (на диаграммах состояния автомата Мили, необходимо указывать выходные сигналы на стрелках перехода состояния, я этого делать не буду, чтобы не загромождать диаграмму, все выходные сигнала будут расписаны в таблице ниже):
Распишем таблицу изменения состояний и выходных сигналов:
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», как на рисунке:
В следующем окне выбираем «Empty project». Окно добавление файлов «Add files», ничего не добавляем. Окно выбора устройства «Family, Devices & Board Settings», для реального проекта очень важное, но так как, наш проект учебный и до реального ему далеко, здесь оставляем настройки по умолчанию, как на рисунке:
Окно выбора настроек других инструментов «EDA Tool Settings», выбираем для симуляции проекта использовать «ModelSim-Altera» и формат «System Verilog HDL» как на рисунке:
Последнее окно информационное «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, находится в файле soda_machine.sv:
Каким образом будут производится кодирование состояний конечного автомата, я оставил на усмотрение Quartus. Для того что бы указать, как именно следует производить кодирование, используется атрибут (* syn_encoding = «default» *), другие варианты кодирования можно увидеть здесь.
Необходимо заметить, что в реальном проекте выходные сигналы комбинационной логики автомата Мили, необходимо сохранять в регистрах и уже с выхода регистров подавать на выход ПЛИС. Входные сигналы необходимо синхронизировать с тактовой частотой с помощью синхронизаторов, во избежании попадания в метастабильное состояние.
Для добавления файлов в проект используется File -> New «SystemVerilog HDL File» и при сохранении дать соответствующее имя. После добавления этих двух файлов, проект можно скомпилировать Processing -> Start Compilation. После успешной компиляции можно посмотреть полученную схему Tools -> Netlist Viewers -> RTL Viewer:
Для просмотра диаграммы состояний конечного автомата Tools -> Netlist Viewers -> State Machine Viewer
На вкладке 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.tv:
Первые два бита это входной сигнал insert, следующие 4 бита, это наши ожидания выходных сигналов: pour_water, change1, change2, change22. Например в начале файла 5 раз подряд вставляется рублевая монета, на пятой монете, мы ожидаем появления сигнала pour_water, при этом сигналы выдачи сдачи неактивны. Файл soda_machine.tv добавляется в проект File -> New «Text File»
Для удобства работы с ModelSim добавим файл soda_machine_run_simulation.do следующего содержания:
Он запустит нашу симуляцию и выведет графики сигналов в 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. Должно получиться как на рисунке:
В окне New Test Bench Settings нажимаем OK. Окно Test Benches должно получить следующий вид:
В окне Test Benches нажимаем OK. В NativeLink settings устанавливаем галочку Use script to set up simulation и выбираем файл soda_machine_run_simulation.do. Окно Settings
должно иметь вид:
В окне Settings нажимаем OK, производим компиляцию проекта Processing -> Start Compilation, производим запуск симуляции Tools -> Run Simulation Tool -> RTL Simulation. Должен запустится ModelSim и произойти симуляция проекта. Внешний вид вкладки Transcript:
Красной рамкой выделен вывод нашего test bench'а о количестве выполненных тестов и обнаруженных ошибках. Внешний вид вкладки Wave:
Исходный код проекта находится в github.com/igoral5/soda_machine Клонировать проект, затем открыть проект при помощи Quartus File -> Open Project...
выбрать файл soda_machine.qpf. Затем скомпилировать проект Processing -> Start Compilation и запустить симуляцию Tools -> Run Simulation Tool -> RTL Simulation.
Постановка задачи
Вас уговорили спроектировать автомат с прохладительными напитками для офиса. Расходы на напитки частично покрывает профсоюз, поэтому они стоят всего по 5 рублей. Автомат принимает монеты в 1, 2 и 5 рублей. Как только покупатель внесет необходимую сумму, автомат выдаст напиток и сдаст сдачу. Спроектируйте конечный автомат для автомата с прохладительными напитками. Входами автомата являются 1, 2 и 5 рублей, а именно, какая из этих монет вставлена.
Предположим, что по каждому тактовому сигналу вставляется только одна монета. Автомат имеет выходы: налить газировку, вернуть 1 рубль, вернуть 2 рубля, вернуть 2 по 2 рубля. Как только в автомате набирается 5 рублей (или больше), он выставляет сигнал «НАЛИТЬ ГАЗИРОВКУ», а также сигналы, возврата соответствующей сдачи. Затем автомат должен быть готов опять принимать монеты.
Теория
Конечные автоматы или finite state machine (FSM) относятся к классу синхронных последовательных схем, которые представляют подавляющее большинство схем цифровой схемотехники. Именно таким образом следует реализовывать свои проекты (по крайней мере на первых порах). Такой способ обеспечивает повторяемость и верифицированость схемы и не зависит от отношений задержек различных элементов схемы. Правила построения синхронных последовательных схем гласят, что схема является синхронной последовательной схемой, если ее элементы удовлетворяют следующим условиям:
- Каждый элемент схемы является либо регистром, либо комбинационной схемой.
- Как минимум один элемент схемы является регистром.
- Все регистры тактируются единственным тактовым сигналом.
- В каждом циклическом пути присутствует как минимум один регистр.
Конечный автомат имеет несколько состояний, которое он хранит в регистрах. При поступлении тактового сигнала, конечный автомат может изменять свое состояние, причем как именно изменится состояние зависит от входных сигналов и текущего состояния. В простейшем случае входных сигналов может не быть вовсе, таким образом работает делитель частоты. Существуют два основных класса конечных автоматов: автомат Мура, в котором выходные сигналы зависят только от текущего состояния автомата и автомат Мили, в котором выходные сигналы зависят от текущего состояния и входных сигналов. В принципе любой конечный автомат можно реализовать как по схеме Мура, так и по схеме Мили, отличие между ними будет в том, что у автомата Мура будет больше состояний и он будет отставать на один такт от автомата Мили. Для схемы автомата газированной воды я буду использовать схему Мили. Распишем состояния конечного автомата:
Условное обозначение | Описание |
---|---|
S0 | Начальное состояние, накопленная сумма 0 руб. |
S1 | Накопленная сумма 1 руб. |
S2 | Накоплено 2 руб. |
S3 | Накоплено 3 руб. |
S4 | Накоплено 4 руб. |
В качестве входного сигнала, будет выступать двухбитная шина, со следующим кодированием номинала монеты:
Условное обозначение | Значение | Описание |
---|---|---|
I1 | 01 | 1 руб. |
I2 | 10 | 2 руб. |
I5 | 11 | 5 руб. |
Нарисуем диаграмму состояний нашего автомата (на диаграммах состояния автомата Мили, необходимо указывать выходные сигналы на стрелках перехода состояния, я этого делать не буду, чтобы не загромождать диаграмму, все выходные сигнала будут расписаны в таблице ниже):
Распишем таблицу изменения состояний и выходных сигналов:
Состояния | Входные сигналы | Выходные сигналы | ||||
---|---|---|---|---|---|---|
Текущее S | Следующее S' | insert | Налить воды pour_water | Cдача 1 руб. change1 | Сдача 2 руб. change2 | Сдача 2 по 2 руб. change22 |
S0 | S1 | I1 | 0 | 0 | 0 | 0 |
S0 | S2 | I2 | 0 | 0 | 0 | 0 |
S0 | S0 | I5 | 1 | 0 | 0 | 0 |
S1 | S2 | I1 | 0 | 0 | 0 | 0 |
S1 | S3 | I2 | 0 | 0 | 0 | 0 |
S1 | S0 | I5 | 1 | 1 | 0 | 0 |
S2 | S3 | I1 | 0 | 0 | 0 | 0 |
S2 | S4 | I2 | 0 | 0 | 0 | 0 |
S2 | S0 | I5 | 1 | 0 | 1 | 0 |
S3 | S4 | I1 | 0 | 0 | 0 | 0 |
S3 | S0 | I2 | 1 | 0 | 0 | 0 |
S3 | S0 | I5 | 1 | 1 | 1 | 0 |
S4 | S0 | I1 | 1 | 0 | 0 | 0 |
S4 | S0 | I2 | 1 | 1 | 0 | 0 |
S4 | S0 | I5 | 1 | 0 | 0 | 1 |
Практика
Установка 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», как на рисунке:
В следующем окне выбираем «Empty project». Окно добавление файлов «Add files», ничего не добавляем. Окно выбора устройства «Family, Devices & Board Settings», для реального проекта очень важное, но так как, наш проект учебный и до реального ему далеко, здесь оставляем настройки по умолчанию, как на рисунке:
Окно выбора настроек других инструментов «EDA Tool Settings», выбираем для симуляции проекта использовать «ModelSim-Altera» и формат «System Verilog HDL» как на рисунке:
Последнее окно информационное «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
Для просмотра диаграммы состояний конечного автомата Tools -> Netlist Viewers -> State Machine Viewer
State Machine Viewer
На вкладке 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_0
01_0_0_0_0
01_0_0_0_0
01_0_0_0_0
01_1_0_0_0
10_0_0_0_0
10_0_0_0_0
10_1_1_0_0
11_1_0_0_0
10_0_0_0_0
10_0_0_0_0
11_1_0_0_1
10_0_0_0_0
11_1_0_1_0
01_0_0_0_0
01_0_0_0_0
01_0_0_0_0
11_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/clk
add wave /soda_machine_tb/dut/reset
add wave /soda_machine_tb/dut/insert
add wave /soda_machine_tb/dut/state
add wave /soda_machine_tb/dut/nextstate
add wave /soda_machine_tb/dut/pour_water
add wave /soda_machine_tb/dut/change1
add wave /soda_machine_tb/dut/change2
add wave /soda_machine_tb/dut/change22
view structure
view signals
run -all
wave 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. Должно получиться как на рисунке:
В окне New Test Bench Settings нажимаем OK. Окно Test Benches должно получить следующий вид:
В окне Test Benches нажимаем OK. В NativeLink settings устанавливаем галочку Use script to set up simulation и выбираем файл soda_machine_run_simulation.do. Окно Settings
должно иметь вид:
В окне Settings нажимаем OK, производим компиляцию проекта Processing -> Start Compilation, производим запуск симуляции Tools -> Run Simulation Tool -> RTL Simulation. Должен запустится ModelSim и произойти симуляция проекта. Внешний вид вкладки Transcript:
ModelSim вкладка Transcript
Красной рамкой выделен вывод нашего test bench'а о количестве выполненных тестов и обнаруженных ошибках. Внешний вид вкладки Wave:
ModelSim вкладка Wave
Исходный код проекта
Исходный код проекта находится в github.com/igoral5/soda_machine Клонировать проект, затем открыть проект при помощи Quartus File -> Open Project...
выбрать файл soda_machine.qpf. Затем скомпилировать проект Processing -> Start Compilation и запустить симуляцию Tools -> Run Simulation Tool -> RTL Simulation.
neerps
Хорошее руководство для начинающих со скриншотами. Думаю, что многим будет полезно. Спасибо!
Но у меня возникло два вопроса.
1) Можно ли в ПЛИС использовать автоматы Милли с комбинационными выходами?
2) Можно ли подавать на логику переключения состояний сигнал insert, если он не синхронизирован?
anonymous Автор
1) Вы правы, выходы комбинационной логики в реальном проекте необходимо сохранять в регистрах и уже выходы этих регистров подавать на выход ПЛИС, в противном случае возможный ложные срабатывания (кстати, на графике сигналов они видны)
2) Входные сигналы, необходимо пропустить через синхронизатор
Единственно, что меня оправдывает, что это учебный проект и не хотелось его усложнять дополнительной логикой, не относящихся к теме конечных автоматов.
neerps
Мне кажется, что тема синхронизации в ПЛИС крайне важна. Действительно, не обязательно писать подробности здесь. Но можно хотя бы упомянуть слово-два.
Я помню, как однажды на отладочной плате завёл сигнал с кнопки на вход автомата. Благодаря этому я узнал, какова метастабильность на практике, и что бывает, когда автомат попадает в неизвестное состояние. Тем более, что в случае с one-hot таких состояний много. Это потенциальный сбой, и стоит заранее знать, как таких сбоев можно избежать. По крайней мере, снизить вероятность.
anonymous Автор
Согласен с вами. Добавил абзац о выходных регистрах и входных синхронизаторах.
Barma2012
Напишите пожалуйста, как это можно сделать в данном примере (и что такое синхронизаторы?)
anonymous Автор
В простейшем случае, синхронизатор это два последовательно включенных D-триггера с одним сигналом тактовой частоты. Расчет строится на том, что даже если первый триггер попадает в метастабильное состояние, то за время периода тактовой частоты он придет в стабильное состояние. Есть формула, которая рассчитывает время наработки на отказ в зависимости от частоты и внутренних параметров триггера.
anonymous Автор
Добавил синхронизаторы для входных сигналов и регистры для выходных сигналов. Новый исходный код на ветке sync, изменения коснулись файлов soda_machine.sv и soda_machine_types.sv (добавлено новое значение I0=2'b00, отсутствие монеты). Теперь принципиальная схема выглядит так
Barma2012
Да, по схеме понял, большое благодарю!
Правильно ли я понимаю, что из-за двух триггеров, сигнал Insert будет задерживаться на один такт?
anonymous Автор
Сигнал insert_sync1 относительно сигнала insert задержится на два такта (у нас же два триггера, включенных последовательно). Выходные сигналы по сравнению с предыдущей схемой задержатся на три такта, с учётом задержки выходных регистров.
anonymous Автор
Вот как это будет выглядеть в ModelSim