1. Упражнение BB-FSM-01 · Светофор Тимофея
Светофор — удобный первый конечный автомат: выходы зависят от сохранённого состояния, а каждый фронт такта переводит автомат в следующее состояние. Здесь повторяется последовательность красный → жёлтый → зелёный → красный. Красному отведены четыре состояния, жёлтому два, зелёному четыре.
Это реализация идеи Тимофея Никифорова из курса Юрия Панчула. Та же структура «регистр состояния + дешифрация выходов» встречается в контроллерах периферии, автоматах протоколов и управляющей логике процессоров. Упражнение связывает схему на макетной плате с синтезируемым RTL.
Что нужно знать: шины питания макетной платы, нумерацию выводов DIP, функцию ИЛИ и фронты такта. Здесь изучается заданная последовательность; согласование перекрёстка и входы пешеходов в упражнение не входят.
2. Автомат состояний и логика ИЛИ
Десять окружностей — десять разных состояний, даже если несколько включают один огонь. Например, S0 и S3 дают красный, но переходят в разные следующие состояния. Сброс выбирает S0 из любого состояния и удерживает его при высоком rst. Условия обычных переходов предполагают rst=0.
S0 → S1 → S2 → S3 → S4 → S5 → S6 → S7 → S8 → S9 → S0
red = S0 | S1 | S2 | S3
yellow = S4 | S5
green = S6 | S7 | S8 | S9S0–S9 — имена состояний и дешифрированных сигналов, а не номера выводов микросхемы. В установившемся состоянии ровно один сигнал S равен 1. В источнике «выходы 1–4, 5–6, 7–10» перечислены с единицы; здесь последовательно используются обозначения Q0–Q9 из даташита.
| Состояние | Выход U1 / вывод | red | yellow | green | Следующее по clk ↑ |
|---|---|---|---|---|---|
| S0 | Q0 / 3 | 1 | 0 | 0 | S1 |
| S1 | Q1 / 2 | 1 | 0 | 0 | S2 |
| S2 | Q2 / 4 | 1 | 0 | 0 | S3 |
| S3 | Q3 / 7 | 1 | 0 | 0 | S4 |
| S4 | Q4 / 10 | 0 | 1 | 0 | S5 |
| S5 | Q5 / 1 | 0 | 1 | 0 | S6 |
| S6 | Q6 / 5 | 0 | 0 | 1 | S7 |
| S7 | Q7 / 6 | 0 | 0 | 1 | S8 |
| S8 | Q8 / 9 | 0 | 0 | 1 | S9 |
| S9 | Q9 / 11 | 0 | 0 | 1 | S0 |
Двухвходовый ИЛИ даёт 1, когда хотя бы один вход равен 1:
| A | B | A | B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
Для четырёхвходового ИЛИ комбинация 0000 даёт 0, а остальные пятнадцать комбинаций дают 1. Сброс асинхронный, активный высокий: он сразу выбирает S0, в том числе между фронтами. Обычный счёт идёт только по положительным фронтам такта.
При частоте f длительности равны 4/f, 2/f и 4/f секунды. На диаграмме сброс задан при t=0, первый счётный фронт — при t=1 с. Если отпустить реальный асинхронный сброс в произвольный момент, первый красный интервал может быть короче; последующие полные циклы сохраняют отношение 4/2/4.
Попробуйте последовательность состояний
Анимация показывает логические состояния; это не HDL-симулятор и не электрическая модель.
3. Необходимые компоненты
| Поз. | Компонент | Кол. | Назначение | Строка LSSK |
|---|---|---|---|---|
| U1 | CD4017BE · DIP-16 | 1 | Десятичный счётчик, десять дешифрированных выходов | 37 |
| U2 | CD4072BE · DIP-14 | 1 | Два элемента 4ИЛИ: красный и зелёный | 21 |
| U3 | CD4071BE · DIP-14 | 1 | Один элемент 2ИЛИ для жёлтого; три не используются | 20 |
| LED-R | Светодиод на 5 В | 1 | Красный, со встроенным ограничением тока | 46 |
| LED-Y | Светодиод на 5 В | 1 | Жёлтый, со встроенным ограничением тока | 48 |
| LED-G | Светодиод на 5 В | 1 | Зелёный, со встроенным ограничением тока | 47 |
| CLK | Тактовый модуль 555 | 1 | Готовый модуль; настроить примерно на 1 Гц | 54 |
| RESET | Кнопочный модуль | 1 | Модуль на 5 В: OUT=0 отпущена, OUT=1 нажата; встроенная подтяжка к земле | 65 |
| BB | Макетная плата на 830 контактов | 1 | Одна плата, центральный разрыв и четыре шины питания | 45 |
| PSU | Модуль стабилизированного питания 5 В | 1 | Выбрать 5 В; использовать поддерживаемый USB-вход или источник 9 В из таблицы | 56 |
| W | Провода M–M | 39 | 37 перемычек платы и два провода питания; взять упаковку 40 шт. | 70 |
| M | Провода M–F | 6 | По три для CLK и RESET с установленными штырьками | 71 |
Номера строк относятся к локальному снимку LSSK от 14 октября 2026 года. Во время обновления Google-таблица не открывалась. Указано число реально используемых компонентов; закупочная упаковка кнопок содержит пять штук. Для модуля питания нужен совместимый входной кабель и источник; они не входят в 39 проводов M–M. Вместо модуля можно взять готовый лабораторный источник 5 В.
4. Микросхемы CMOS: функции, выводы и полярность
U1 CD4017BE: циклически выбирает Q0…Q9. CLOCK, вывод 14, продвигает счёт по положительному фронту при нуле на INHIBIT, выводе 13. RESET, вывод 15, активен единицей и имеет приоритет над счётом. Перенос на выводе 12 равен 1 в S0…S4 и 0 в S5…S9; здесь он не используется.
У второго тактового входа есть особенность: спад INHIBIT при высоком CLOCK тоже продвигает счёт. В нашей схеме INHIBIT постоянно соединён с GND. Модель и тест микросхемы проверяют это поведение; внешнему интерфейсу упражнения нужны только clk и rst. Даташит TI; Таблица тактирования семейства 4017.
U2 CD4072BE: два четырёхвходовых ИЛИ. Входы 2,3,4,5 → выход 1 (красный); входы 9,10,11,12 → выход 13 (зелёный). U3 CD4071BE: четыре двухвходовых ИЛИ. Входы 1,2 → выход 3 (жёлтый). Используемые логические выходы активны единицей. Даташит TI на ИЛИ.
U1 · CD4017BE
| Вывод | Сигнал микросхемы | В этой схеме |
|---|---|---|
| 1 | Q5 | S5 |
| 2 | Q1 | S1 |
| 3 | Q0 | S0 |
| 4 | Q2 | S2 |
| 5 | Q6 | S6 |
| 6 | Q7 | S7 |
| 7 | Q3 | S3 |
| 8 | VSS | Соединить с 0 В |
| 9 | Q8 | S8 |
| 10 | Q4 | S4 |
| 11 | Q9 | S9 |
| 12 | CARRY | Не подключать |
| 13 | INHIBIT | Соединить с 0 В |
| 14 | CLOCK | clk |
| 15 | RESET | rst |
| 16 | VDD | Питание +5 В |
U2 · CD4072BE
| Вывод | Сигнал микросхемы | В этой схеме |
|---|---|---|
| 1 | J | red |
| 2 | A | S0 |
| 3 | B | S1 |
| 4 | C | S2 |
| 5 | D | S3 |
| 6 | NC | Не подключать |
| 7 | VSS | Соединить с 0 В |
| 8 | NC | Не подключать |
| 9 | E | S6 |
| 10 | F | S7 |
| 11 | G | S8 |
| 12 | H | S9 |
| 13 | K | green |
| 14 | VDD | Питание +5 В |
U3 · CD4071BE
| Вывод | Сигнал микросхемы | В этой схеме |
|---|---|---|
| 1 | A | S4 |
| 2 | B | S5 |
| 3 | J | yellow |
| 4 | K | Не подключать |
| 5 | C | Соединить с 0 В |
| 6 | D | Соединить с 0 В |
| 7 | VSS | Соединить с 0 В |
| 8 | E | Соединить с 0 В |
| 9 | F | Соединить с 0 В |
| 10 | L | Не подключать |
| 11 | M | Не подключать |
| 12 | G | Соединить с 0 В |
| 13 | H | Соединить с 0 В |
| 14 | VDD | Питание +5 В |
Неиспользуемые логические входы U3 (5,6,8,9,12,13) соединены с GND. Неиспользуемые выходы (4,10,11) свободны. Выводы 6 и 8 U2 имеют обозначение NC и остаются свободными. Не соединяйте выходы друг с другом вместо элемента ИЛИ.
5. Схема 1 · Логические элементы
Каждый переход задан графом в разделе 2. Эту поведенческую структуру реализует traffic_light.sv. Блок состояний задаёт последовательность из десяти состояний, а не конкретное внутреннее кодирование.
6. Схема 2 · Соединения микросхем
red — U2.1, yellow — U3.3, green — U2.13. Соедините каждый выход с анодом соответствующего индикатора с ограничением тока; катоды — с общей GND. Тактовый модуль, сброс и источник питания имеют общую землю.
7. Схема 3 · Сборка на макетной плате
Установите все DIP-корпуса поперёк центрального разрыва, выемкой вверх: U1 в строках 8–15, U2 в 22–28, U3 в 35–41. Вывод 1 находится в столбце e первой строки; противоположный верхний вывод — в f. В каждой строке соединены a–e и отдельно f–j; эти половины не соединены друг с другом.
LP/LM — левая положительная шина и земля; RP/RM — правые. Отверстия шин нумеруются 01–50 сверху вниз независимо от 63 строк контактных полей. На рисунке принят разрыв между отверстиями шин 25 и 26; все четыре разрыва замкнуты. Перед использованием этой нумерации проверьте соединения своей платы.
| Внешнее соединение | Отверстия платы |
|---|---|
| CLK: OUT / + / − | h47 / LP05 / LM05 |
| RESET: OUT / + / − | h50 / LP06 / LM06 |
| Питание +5 В / GND | LP01 / LM01 |
| Красный LED анод / катод | b48 / b49 |
| Жёлтый LED анод / катод | b52 / b53 |
| Зелёный LED анод / катод | b56 / b57 |
Полная таблица соединений по отверстиям (37 перемычек)
| Провод | Цепь | Откуда | Куда |
|---|---|---|---|
| W01 | +5V | LP25 | LP26 |
| W02 | GND | LM25 | LM26 |
| W03 | +5V | RP25 | RP26 |
| W04 | GND | RM25 | RM26 |
| W05 | +5V | LP02 | RP02 |
| W06 | GND | LM02 | RM02 |
| W07 | S0 | a10 | a23 |
| W08 | S1 | a09 | a24 |
| W09 | S2 | a11 | a25 |
| W10 | S3 | a14 | a26 |
| W11 | S4 | j14 | a35 |
| W12 | S5 | a08 | a36 |
| W13 | S6 | a12 | j27 |
| W14 | S7 | a13 | j26 |
| W15 | S8 | j15 | j25 |
| W16 | S9 | j13 | j24 |
| W17 | GND | a15 | LM08 |
| W18 | GND | j11 | RM08 |
| W19 | GND | a28 | LM09 |
| W20 | GND | a39 | LM10 |
| W21 | GND | a40 | LM11 |
| W22 | GND | a41 | LM12 |
| W23 | GND | j41 | RM09 |
| W24 | GND | j40 | RM10 |
| W25 | GND | j37 | RM11 |
| W26 | GND | j36 | RM12 |
| W27 | GND | a49 | LM13 |
| W28 | GND | a53 | LM14 |
| W29 | GND | a57 | LM15 |
| W30 | clk | j10 | j47 |
| W31 | rst | j09 | j50 |
| W32 | +5V | j08 | RP08 |
| W33 | +5V | j22 | RP09 |
| W34 | +5V | j35 | RP10 |
| W35 | red | a22 | a48 |
| W36 | green | j23 | a56 |
| W37 | yellow | a37 | a52 |
8. Синтезируемая схема на SystemVerilog
Принятый стиль кода
В каждом модуле сразу после заголовка стоят timeunit 1ns; и timeprecision 1ps;. Для портов-цепей используются обычные input/output, для процедурных выходов — output logic. Имена портов выровнены пробелами.
RTL, модели микросхем, оболочки и тестбенчи следуют правилам стиля SystemVerilog, составленным по BGM и systemverilog-homework. Отступ — четыре пробела. Для регистров используются logic и always_ff, для комбинационных процедур — always_comb; порты подключаются явно по именам. Руководство учитывает различия сброса и записи FSM в двух репозиториях.
Оба варианта имеют одинаковый интерфейс: clk, rst → red, yellow, green, state[9:0]. Обобщённый вариант использует унитарный (one-hot) регистр состояния, поэтому три уравнения прямо соответствуют схеме 1. Перед работой нужен сброс.
Обобщённый RTL — полный код
// Synthesizable generic FSM. The state output uses one-hot encoding.
// Reset is asynchronous and active high; it selects S0.
module traffic_light
(
input clk,
input rst,
output red,
output yellow,
output green,
output logic [9:0] state
);
timeunit 1ns;
timeprecision 1ps;
wire [9:0] next_state = { state [8:0], state [9] };
// State register
always_ff @ (posedge clk or posedge rst)
if (rst)
state <= 10'b00_0000_0001;
else
state <= next_state;
// Moore outputs depend only on the current state.
assign red = | state [3:0];
assign yellow = | state [5:4];
assign green = | state [9:6];
endmodule
SystemVerilog описывает параллельную аппаратуру: регистр хранит состояние, а все три выходных уравнения действуют постоянно. Конкатенация циклически сдвигает единичный маркер, а не программные инструкции. Внутри реальной 4017 работает пятиразрядный счётчик Джонсона; функциональная эквивалентность не требует одинакового внутреннего хранения.
9. Модели микросхем CMOS на SystemVerilog
Все три модели — синтезируемые функциональные модели, логические порты которых названы по выводам. Физические VDD/VSS и NC показаны на схеме и не изображаются как HDL-питание. Задержек распространения нет. Модели не проверяют ток LED, качество питания, метастабильность или электрические ограничения импульсов.
cd4017b.sv
// Synthesizable, zero-delay functional model of a powered CD4017B.
// Physical supply pins: 16 = VDD, 8 = VSS; they are not HDL ports.
// Binary encoding abstracts the chip's five-stage Johnson counter.
// Reset is required. Illegal Johnson-state recovery and electrical effects
// (current, setup/hold, pulse width and propagation delay) are not modeled.
module cd4017b
(
input p14_clock,
input p13_inhibit,
input p15_reset,
output p3_q0,
output p2_q1,
output p4_q2,
output p7_q3,
output p10_q4,
output p1_q5,
output p5_q6,
output p6_q7,
output p9_q8,
output p11_q9,
output p12_carry
);
timeunit 1ns;
timeprecision 1ps;
// Falling INHIBIT while CLOCK is high is also a count event.
// INHIBIT is tied low in this exercise, so synthesis removes the gate.
// This models the chip; it is not a recommendation to gate FPGA clocks.
wire effective_clock = p14_clock & ~ p13_inhibit;
logic [3:0] count;
always_ff @ (posedge effective_clock or posedge p15_reset)
if (p15_reset)
count <= '0;
else if (count == 4'd9)
count <= '0;
else
count <= count + 1'b1;
assign p3_q0 = (count == 4'd0);
assign p2_q1 = (count == 4'd1);
assign p4_q2 = (count == 4'd2);
assign p7_q3 = (count == 4'd3);
assign p10_q4 = (count == 4'd4);
assign p1_q5 = (count == 4'd5);
assign p5_q6 = (count == 4'd6);
assign p6_q7 = (count == 4'd7);
assign p9_q8 = (count == 4'd8);
assign p11_q9 = (count == 4'd9);
assign p12_carry = (count < 4'd5);
endmodule
cd4072b.sv
// Synthesizable functional model: two four-input OR gates.
// Physical pins: 14 = VDD, 7 = VSS, 6/8 = NC. No electrical/timing model.
module cd4072b
(
input p2_a,
input p3_b,
input p4_c,
input p5_d,
output p1_j,
input p9_e,
input p10_f,
input p11_g,
input p12_h,
output p13_k
);
timeunit 1ns;
timeprecision 1ps;
assign p1_j = p2_a | p3_b | p4_c | p5_d;
assign p13_k = p9_e | p10_f | p11_g | p12_h;
endmodule
cd4071b.sv
// Synthesizable functional model: four two-input OR gates.
// Physical supply pins: 14 = VDD, 7 = VSS. No electrical/timing model.
module cd4071b
(
input p1_a,
input p2_b,
input p5_c,
input p6_d,
input p8_e,
input p9_f,
input p12_g,
input p13_h,
output p3_j,
output p4_k,
output p10_l,
output p11_m
);
timeunit 1ns;
timeprecision 1ps;
assign p3_j = p1_a | p2_b;
assign p4_k = p5_c | p6_d;
assign p10_l = p8_e | p9_f;
assign p11_m = p12_g | p13_h;
endmodule
Оба кодирования следуют одному графу состояний. Модель 4017 использует двоичный счётчик как абстракцию и воспроизводит последовательность сброса/счёта, перенос и запрет такта. Восстановление физического счётчика Джонсона из недопустимых внутренних кодов не моделируется. Здесь запрет такта соединён с нулём, поэтому синтез удаляет элемент в цепи эффективного такта.
10. SystemVerilog: соединения микросхем
Этот список соединений генерируется из той же базы выводов и цепей, что и схемы 2 и 3. Он создаёт по экземпляру модели каждой физической микросхемы и соединяет неиспользуемые логические входы с нулём. Интерфейс совпадает с разделом 8, поэтому варианты взаимозаменяемы в тестбенче и оболочках.
RTL соединений микросхем — полный код
// Generated by tools/build_diagrams.py from the same pins/nets as the drawings.
// Synthesizable with the ideal chip models; all supply pins are physical-only.
module traffic_light_chips
(
input clk,
input rst,
output red,
output yellow,
output green,
output [9:0] state
);
timeunit 1ns;
timeprecision 1ps;
cd4017b u1
(
.p3_q0 (state [0]),
.p2_q1 (state [1]),
.p4_q2 (state [2]),
.p7_q3 (state [3]),
.p10_q4 (state [4]),
.p1_q5 (state [5]),
.p5_q6 (state [6]),
.p6_q7 (state [7]),
.p9_q8 (state [8]),
.p11_q9 (state [9]),
.p12_carry (),
.p13_inhibit (1'b0),
.p14_clock (clk),
.p15_reset (rst)
);
cd4072b u2
(
.p1_j (red),
.p2_a (state [0]),
.p3_b (state [1]),
.p4_c (state [2]),
.p5_d (state [3]),
.p9_e (state [6]),
.p10_f (state [7]),
.p11_g (state [8]),
.p12_h (state [9]),
.p13_k (green)
);
cd4071b u3
(
.p1_a (state [4]),
.p2_b (state [5]),
.p3_j (yellow),
.p4_k (),
.p5_c (1'b0),
.p6_d (1'b0),
.p8_e (1'b0),
.p9_f (1'b0),
.p10_l (),
.p11_m (),
.p12_g (1'b0),
.p13_h (1'b0)
);
endmodule
11. Тестбенчи и запуск симуляторов
Тестбенч сравнивает оба варианта с независимо вычисленным ожидаемым состоянием и набором огней. Одного совпадения вариантов недостаточно: две ошибочные схемы могут совпасть. Проверяются 12 полных циклов, 285 наблюдений, асинхронный сброс при обоих уровнях такта, удерживаемый сброс, неизменность по спаду и переход S9 → S0.
Все три тестбенча используют отдельный непрерывный генератор такта, стимулы по событиям, тела задач SystemVerilog и тайм-ауты в тактах. Ожидание спада применяется только для проверки поведения при низком CLOCK; каждая задержка установления объяснена в исходнике и в пояснениях к правилам стиля.
Основной тестбенч эквивалентности
// Simulation only. Portable procedural checks; no classes, DPI or UVM.
module tb_traffic_light;
timeunit 1ns;
timeprecision 1ps;
localparam period = 20;
localparam timeout_cycles = 500;
logic clk = 1'b0;
logic rst = 1'b0;
always # (period / 2) clk = ~ clk;
logic red_generic, yellow_generic, green_generic;
logic red_chips, yellow_chips, green_chips;
logic [9:0] state_generic, state_chips;
int expected = 0;
int checks = 0;
int red_slots = 0;
int yellow_slots = 0;
int green_slots = 0;
int visits [0:9];
traffic_light generic_dut
(
.clk (clk),
.rst (rst),
.red (red_generic),
.yellow (yellow_generic),
.green (green_generic),
.state (state_generic)
);
traffic_light_chips chip_dut
(
.clk (clk),
.rst (rst),
.red (red_chips),
.yellow (yellow_chips),
.green (green_chips),
.state (state_chips)
);
task check_state;
logic [9:0] wanted;
logic [2:0] lamps;
wanted = 10'b1 << expected;
lamps = expected < 4 ? 3'b100 : expected < 6 ? 3'b010 : 3'b001;
if ( state_generic !== wanted
|| state_chips !== wanted
|| { red_generic, yellow_generic, green_generic } !== lamps
|| { red_chips, yellow_chips, green_chips } !== lamps)
begin
$fatal (1, "state=%0d generic=%b/%b chips=%b/%b",
expected,
state_generic, { red_generic, yellow_generic, green_generic },
state_chips, { red_chips, yellow_chips, green_chips });
end
checks = checks + 1;
endtask
task tick;
@ (posedge clk);
expected = rst ? 0 : (expected + 1) % 10;
# 1; // Let register updates and lamp decoding settle before sampling.
check_state ();
endtask
// Called between rising edges, after tick or the focused low-phase check.
// Checking before the next clock edge distinguishes asynchronous reset.
task reset_now;
rst = 1'b1;
expected = 0;
# 1; // Asynchronous register update and combinational output settling.
check_state ();
rst = 1'b0;
endtask
initial
begin
$dumpfile ("traffic_light.vcd");
$dumpvars (0, tb_traffic_light);
for (int i = 0; i < 10; i ++)
visits [i] = 0;
@ (posedge clk);
# 1; // Place the initial reset after the clock event, without a race.
reset_now ();
for (int round = 0; round < 12; round ++)
for (int i = 0; i < 10; i ++)
begin
visits [expected] = visits [expected] + 1;
if (red_generic)
red_slots = red_slots + 1;
if (yellow_generic)
yellow_slots = yellow_slots + 1;
if (green_generic)
green_slots = green_slots + 1;
tick ();
end
if (red_slots != 48 || yellow_slots != 24 || green_slots != 48)
$fatal (1, "4/2/4 duration mismatch");
for (int i = 0; i < 10; i ++)
if (visits [i] != 12)
$fatal (1, "Missing state %0d", i);
// Reset from every state while CLOCK is high, then while it is low.
for (int low_phase = 0; low_phase < 2; low_phase ++)
for (int i = 0; i < 10; i ++)
begin
tick ();
reset_now ();
repeat (i)
tick ();
if (low_phase != 0)
begin
// Necessary exception: exercise reset with CLOCK low and
// verify that a falling clock edge does not advance state.
@ (negedge clk);
# 1; // Observe any erroneous falling-edge state update.
check_state ();
end
reset_now ();
end
rst = 1'b1;
expected = 0;
repeat (3)
tick (); // Reset dominates the clock.
rst = 1'b0;
tick ();
$display ("PASS traffic_light: %0d checks; 12 full cycles; red/yellow/green=48/24/48; reset and wrap",
checks);
$finish;
end
initial
begin
repeat (timeout_cycles) @ (posedge clk);
$fatal (1, "Timeout after %0d clock cycles", timeout_cycles);
end
endmodule
Два дополнительных тестбенча проверяют все 256 комбинаций входных выводов моделей ИЛИ, запрет и перенос 4017, а затем оба варианта каждой оболочки в течение 30 шагов и оба режима индикатора; далее проверяются три такта с отключёнными выходами и независимые сбросы кнопкой и выделенным входом.
Тесты моделей микросхем
// Simulation only. Chip pin truth tables and 4017 clock semantics.
module tb_cmos;
timeunit 1ns;
timeprecision 1ps;
localparam period = 20;
localparam timeout_cycles = 500;
logic clk = 1'b0;
logic inhibit = 1'b0;
logic rst = 1'b0;
logic [7:0] a = '0;
always # (period / 2) clk = ~ clk;
logic [3:0] or2;
logic [1:0] or4;
logic [9:0] q;
logic carry;
int expected = 0;
cd4071b g2
(
.p1_a (a [0]),
.p2_b (a [1]),
.p5_c (a [2]),
.p6_d (a [3]),
.p8_e (a [4]),
.p9_f (a [5]),
.p12_g (a [6]),
.p13_h (a [7]),
.p3_j (or2 [0]),
.p4_k (or2 [1]),
.p10_l (or2 [2]),
.p11_m (or2 [3])
);
cd4072b g4
(
.p2_a (a [0]),
.p3_b (a [1]),
.p4_c (a [2]),
.p5_d (a [3]),
.p1_j (or4 [0]),
.p9_e (a [4]),
.p10_f (a [5]),
.p11_g (a [6]),
.p12_h (a [7]),
.p13_k (or4 [1])
);
cd4017b counter
(
.p14_clock (clk),
.p13_inhibit (inhibit),
.p15_reset (rst),
.p3_q0 (q [0]),
.p2_q1 (q [1]),
.p4_q2 (q [2]),
.p7_q3 (q [3]),
.p10_q4 (q [4]),
.p1_q5 (q [5]),
.p5_q6 (q [6]),
.p6_q7 (q [7]),
.p9_q8 (q [8]),
.p11_q9 (q [9]),
.p12_carry (carry)
);
task check_counter;
if (q !== (10'b1 << expected) || carry !== (expected < 5))
$fatal (1, "4017 state/carry mismatch: expected=%0d q=%b carry=%b",
expected, q, carry);
endtask
task tick;
@ (posedge clk);
expected = rst ? 0 : inhibit ? expected : (expected + 1) % 10;
# 1; // Counter register and decoded output settling.
check_counter ();
endtask
initial
begin
@ (posedge clk);
# 1; // Assert reset after the clock event, without a stimulus/DUT race.
rst = 1'b1;
expected = 0;
# 1; // Check asynchronous reset before the next clock edge.
check_counter ();
// Inputs are changed after sampling, safely before the next clock edge.
for (int i = 0; i < 256; i ++)
begin
a = 8' (i);
tick ();
if ( or2 !== { (| a [7:6]), (| a [5:4]), (| a [3:2]), (| a [1:0]) }
|| or4 !== { (| a [7:4]), (| a [3:0]) })
$fatal (1, "OR pin truth-table mismatch input=%h", a);
end
rst = 1'b0;
repeat (30)
tick ();
inhibit = 1'b1;
# 1; // Raising inhibit while CLOCK is high must not count.
check_counter ();
repeat (4)
tick ();
// Necessary exception: falling INHIBIT with CLOCK low must not count.
// Posedge-only stimulus cannot test this half of the chip's truth table.
@ (negedge clk);
inhibit = 1'b0;
# 1; // Settle the effective clock and output decode before checking.
check_counter ();
tick ();
inhibit = 1'b1;
# 1; // Raising inhibit with CLOCK high must not count.
check_counter ();
inhibit = 1'b0;
expected = (expected + 1) % 10;
# 1; // Falling inhibit with CLOCK high IS a count event.
check_counter ();
rst = 1'b1;
expected = 0;
# 1; // Reset must act without waiting for the next clock edge.
check_counter ();
inhibit = 1'b1;
tick ();
inhibit = 1'b0;
# 1; // Reset also dominates the falling-INHIBIT count event.
check_counter ();
repeat (2)
tick ();
$display ("PASS CMOS: 256 OR input combinations; 30 count edges; carry, inhibit and asynchronous reset");
$finish;
end
initial
begin
repeat (timeout_cycles) @ (posedge clk);
$fatal (1, "Timeout after %0d clock cycles", timeout_cycles);
end
endmodule
Тесты BGM и Tiny Tapeout
// Simulation only. Both core versions in all three integration wrappers.
module tb_integrations;
timeunit 1ns;
timeprecision 1ps;
localparam period = 20;
localparam timeout_cycles = 200;
logic clk = 1'b0;
logic rst = 1'b0;
logic ena = 1'b1;
always # (period / 2) clk = ~ clk;
logic [7:0] ui = '0;
logic [7:0] key = '0;
logic [7:0] lab_led [0:1];
logic [7:0] hack_led [0:1];
logic [7:0] tt_out [0:1];
logic [7:0] tt_io [0:1];
logic [7:0] tt_oe [0:1];
int n_bgm = 0;
int n_tt = 0;
logic [7:0] seven [0:9];
generate
genvar k;
for (k = 0; k < 2; k ++)
begin : versions
lab_top # (.CHIP_MODEL (k)) lab
(
.clk (clk),
.slow_clk (clk),
.rst (rst),
.key (key [3:0]),
.sw (8'b0),
.led (lab_led [k]),
.abcdefgh (),
.digit (),
.x (10'b0),
.y (9'b0),
.red (),
.green (),
.blue (),
.mic (24'b0),
.sound (),
.uart_rx (1'b0),
.uart_tx (),
.gpio ()
);
hackathon_top # (.CHIP_MODEL (k)) hack
(
.clock (clk),
.slow_clock (clk),
.reset (rst),
.key (key),
.led (hack_led [k]),
.abcdefgh (),
.digit (),
.x (9'b0),
.y (9'b0),
.red (),
.green (),
.blue (),
.gpio ()
);
tt_um_timofei_traffic_light # (.USE_CHIP_MODEL (k)) tt
(
.ui_in (ui),
.uo_out (tt_out [k]),
.uio_in (8'b0),
.uio_out (tt_io [k]),
.uio_oe (tt_oe [k]),
.ena (ena),
.clk (clk),
.rst_n (~ rst)
);
end
endgenerate
task check_all;
logic [2:0] bgm_lamps;
logic [2:0] tt_lamps;
logic [7:0] segments;
bgm_lamps = n_bgm < 4 ? 3'b001 : n_bgm < 6 ? 3'b010 : 3'b100;
tt_lamps = n_tt < 4 ? 3'b001 : n_tt < 6 ? 3'b010 : 3'b100;
segments = ui [0] ? seven [n_tt] : n_tt < 4 ? 8'h01 : n_tt < 6 ? 8'h40 : 8'h08;
for (int j = 0; j < 2; j ++)
begin
if ( lab_led [j] !== { 5'b0, bgm_lamps }
|| hack_led [j] !== { 5'b0, bgm_lamps })
$fatal (1, "BGM output mismatch state=%0d model=%0d", n_bgm, j);
if ( tt_out [j] !== (ena ? segments : 8'b0)
|| tt_io [j] !== (ena ? { 5'b0, tt_lamps } : 8'b0)
|| tt_oe [j] !== (ena ? 8'h07 : 8'b0))
$fatal (1, "Tiny Tapeout output mismatch state=%0d model=%0d", n_tt, j);
end
endtask
task tick;
@ (posedge clk);
n_bgm = rst || key [0] ? 0 : (n_bgm + 1) % 10;
n_tt = rst ? 0 : (n_tt + 1) % 10;
# 1; // Register updates and wrapper output decoding must settle.
check_all ();
endtask
initial
begin
seven [0] = 8'h3f;
seven [1] = 8'h06;
seven [2] = 8'h5b;
seven [3] = 8'h4f;
seven [4] = 8'h66;
seven [5] = 8'h6d;
seven [6] = 8'h7d;
seven [7] = 8'h07;
seven [8] = 8'h7f;
seven [9] = 8'h6f;
@ (posedge clk);
# 1; // Drive reset after the clock event, without a stimulus/DUT race.
rst = 1'b1;
n_bgm = 0;
n_tt = 0;
# 1; // Asynchronous reset must be observable before the next clock.
check_all ();
rst = 1'b0;
// Each display mode sees all ten states; stimulus changes after sampling.
for (int i = 0; i < 30; i ++)
begin
ui = 8' ((i / 10) % 2);
tick ();
end
// ena blanks TT outputs but must not stop the internal state sequence.
ena = 1'b0;
repeat (3)
tick ();
ena = 1'b1;
# 1; // Combinational output-enable and display settling.
check_all ();
// Check the BGM reset button independently of the dedicated TT reset.
key = 8'd1;
n_bgm = 0;
# 1; // Asynchronous button reset; TT must retain its own state.
check_all ();
repeat (2)
tick ();
key = '0;
tick ();
rst = 1'b1;
n_bgm = 0;
n_tt = 0;
# 1; // Asynchronous dedicated reset in all wrappers.
check_all ();
repeat (2)
tick ();
rst = 1'b0;
tick ();
$display ("PASS integrations: both implementations in BGM lab/hackathon and Tiny Tapeout; 30 steps, both display modes, 3 disabled cycles, independent button and dedicated resets");
$finish;
end
initial
begin
repeat (timeout_cycles) @ (posedge clk);
$fatal (1, "Timeout after %0d clock cycles", timeout_cycles);
end
endmodule
cd breadboard/traffic_light
./scripts/run_icarus.sh
./scripts/run_questa.sh
./scripts/run_verilator.shЗапустите любой установленный симулятор или все три. Скрипты работают в отдельных подкаталогах build/, запускают три тестбенча и требуют строку PASS. Основной тест записывает traffic_light.vcd. Пути к программам можно задать переменными окружения IVERILOG/VVP, VLIB/VMAP/VLOG/VSIM или VERILATOR.
| Средство | Результат проверки |
|---|---|
| Icarus 12.0 / 14.0 | Симуляция проходит. Требуемая цель совместимости — Icarus 13; именно эта версия здесь не установлена. |
| Questa 2025.2 | Все исходники компилируются без ошибок и предупреждений. Запуск недоступен: лицензия симулятора не настроена. |
| Verilator 5.053 devel | Все три тестбенча проходят с --binary --timing --trace. |
| Yosys + Slang | Оба основных варианта синтезируются и проходят check -assert. |
Для синтеза запустите python3 scripts/check_synthesis.py с Yosys, включающим фронтенд Slang; при необходимости задайте путь к программе через YOSYS. Оба варианта проходят проверку на исходном современном SystemVerilog. Встроенный парсер локального Yosys 0.43 не принимает объявления timeunit.
Тестбенчи используют обычные процедурные конструкции SystemVerilog-2012 без UVM, DPI и классов конкретного симулятора. Протокол проверки · Общий скрипт запуска · Проверка синтеза. Физическая схема пока не проверялась.
12. Подключение к BGM lab_top
Используйте slow_clk BGM, а не исходный такт в мегагерцах. В проверенной оболочке Tang Nano 9K + LCD + TM1638 формируется медленный такт 1 Гц. Для длительностей в секундах другие конфигурации должны давать ту же частоту.
traffic_light dut
(
.clk (slow_clk),
.rst (rst | key [0]),
.red (lamp_red),
.yellow (lamp_yellow),
.green (lamp_green),
.state (state)
);
// Or instantiate traffic_light_chips with the same ports.Полный пример сохраняет параметризованный интерфейс BGM, выводит красный/жёлтый/зелёный на LED 0/1/2 и заполняет экран текущим цветом. Неиспользуемые выходы явно заданы. key[0] — сброс; нужны как минимум одна кнопка и три LED. Для варианта соединений микросхем установите CHIP_MODEL=1.
Полный lab_top.sv
integration/bgm_lab/lab_top.sv
// Full BGM lab interface. Requires w_key >= 1 and w_led >= 3.
module lab_top
# (
parameter CHIP_MODEL = 0,
parameter clk_mhz = 50,
parameter w_key = 4,
parameter w_sw = 8,
parameter w_led = 8,
parameter w_digit = 8,
parameter w_gpio = 100,
parameter screen_width = 640,
parameter screen_height = 480,
parameter w_red = 4,
parameter w_green = 4,
parameter w_blue = 4,
parameter w_x = $clog2 (screen_width),
parameter w_y = $clog2 (screen_height)
)
(
input clk,
input slow_clk,
input rst,
// Keys, switches, LEDs
input [w_key - 1:0] key,
input [w_sw - 1:0] sw,
output [w_led - 1:0] led,
// A dynamic seven-segment display
output [ 7:0] abcdefgh,
output [w_digit - 1:0] digit,
// Graphics
input [w_x - 1:0] x,
input [w_y - 1:0] y,
output [w_red - 1:0] red,
output [w_green - 1:0] green,
output [w_blue - 1:0] blue,
// Microphone, sound output and UART
input [ 23:0] mic,
output [ 15:0] sound,
input uart_rx,
output uart_tx,
// General-purpose Input/Output
inout [w_gpio - 1:0] gpio
);
timeunit 1ns;
timeprecision 1ps;
logic lamp_red, lamp_yellow, lamp_green;
logic [9:0] state;
wire exercise_reset = rst | key [0];
generate
if (CHIP_MODEL == 0)
begin : generic_version
traffic_light dut
(
.clk (slow_clk),
.rst (exercise_reset),
.red (lamp_red),
.yellow (lamp_yellow),
.green (lamp_green),
.state (state)
);
end
else
begin : chip_version
traffic_light_chips dut
(
.clk (slow_clk),
.rst (exercise_reset),
.red (lamp_red),
.yellow (lamp_yellow),
.green (lamp_green),
.state (state)
);
end
endgenerate
// Infrastructure LEDs and graphics use active-high logical signals.
assign led = w_led' ({ 5'b0, lamp_green, lamp_yellow, lamp_red });
assign red = { w_red { lamp_red | lamp_yellow } };
assign green = { w_green { lamp_green | lamp_yellow } };
assign blue = '0;
assign abcdefgh = '0;
assign digit = '0;
assign sound = '0;
assign uart_tx = 1'b1;
assign gpio = 'z;
wire unused_inputs = & { 1'b0, clk, sw, key, x, y, mic, uart_rx, state };
endmodule
В новом каталоге упражнения внутри BGM скопируйте вспомогательные файлы скриптом ниже, затем перенесите стандартные нумерованные .bash-скрипты из соседнего упражнения. Выберите плату обычным способом BGM. Скрипт подготовки не перезаписывает существующий каталог; он не запускает синтез и не прошивает плату.
python3 scripts/stage_integration.py lab /path/to/BGM/labs/1_basics/1_11_timofei_traffic_light13. Подключение к BGM hackathon_top
Реальное имя модуля BGM — hackathon_top (в «hackaton_top» из задания пропущена h). Его сигналы — slow_clock и reset. Пример соответствует интерфейсу хакатона Tang Nano 9K 480×272 + TM1638.
traffic_light_chips dut
(
.clk (slow_clock),
.rst (reset | key [0]),
.red (lamp_red),
.yellow (lamp_yellow),
.green (lamp_green),
.state (state)
);Полный hackathon_top.sv
integration/bgm_hackathon/hackathon_top.sv
// The BGM module is spelled hackathon_top, with "h".
module hackathon_top
# (
parameter CHIP_MODEL = 0
)
(
input clock,
input slow_clock,
input reset,
input [7:0] key,
output [7:0] led,
// A dynamic seven-segment display
output [7:0] abcdefgh,
output [7:0] digit,
// LCD screen interface
input [8:0] x,
input [8:0] y,
output [4:0] red,
output [5:0] green,
output [4:0] blue,
inout [3:0] gpio
);
timeunit 1ns;
timeprecision 1ps;
logic lamp_red, lamp_yellow, lamp_green;
logic [9:0] state;
wire exercise_reset = reset | key [0];
generate
if (CHIP_MODEL == 0)
begin : generic_version
traffic_light dut
(
.clk (slow_clock),
.rst (exercise_reset),
.red (lamp_red),
.yellow (lamp_yellow),
.green (lamp_green),
.state (state)
);
end
else
begin : chip_version
traffic_light_chips dut
(
.clk (slow_clock),
.rst (exercise_reset),
.red (lamp_red),
.yellow (lamp_yellow),
.green (lamp_green),
.state (state)
);
end
endgenerate
assign led = { 5'b0, lamp_green, lamp_yellow, lamp_red };
assign red = { 5 { lamp_red | lamp_yellow } };
assign green = { 6 { lamp_green | lamp_yellow } };
assign blue = '0;
assign abcdefgh = '0;
assign digit = '0;
assign gpio = 'z;
wire unused_inputs = & { 1'b0, clock, key, x, y, state };
endmodule
Установите CHIP_MODEL=0 для обобщённого варианта или 1 для модели микросхем. Нужна конфигурация платы с USE_HACKATHON_TOP, например tang_nano_9k_lcd_480_272_tm1638_hackathon.
python3 scripts/stage_integration.py hackathon /path/to/BGM/hackathon/problems/11_timofei_traffic_light14. Подключение к Tiny Tapeout project.v
Файл project.v использует заданный интерфейс Tiny Tapeout и уникальное имя верхнего модуля tt_um_timofei_traffic_light. rst_n активен нулём, поэтому автомат получает ~rst_n. Установите внешний такт проекта 1 Гц; оболочка не делит такт в мегагерцах.
| Порт / RP2350 | Назначение |
|---|---|
| clk / GPIO16; rst_n / GPIO14 | Такт и сброс; выбрать проект, подать сброс, затем запустить такт. |
| ui_in[0] / GPIO17 | DIP 0: 0 = полоски по положению огня, 1 = номер состояния 0…9. Остальные DIP не используются. |
| uo_out[7:0] / GPIO40…33 | {dp, g, f, e, d, c, b, a} · активный уровень 1 |
| uio_out[2:0] / GPIO27…25 | {green, yellow, red} |
| uio_oe[7:0] | 00000111 при ena=1; иначе все выводы — входы. |
| ena | Разрешение мультиплексора проектов, не вывод платы. При 0 гасит индикатор и отключает двунаправленные выходы; счёт состояний не останавливает. |
В режиме полосок сегмент a (верхний) означает красный, g (средний) — жёлтый, d (нижний) — зелёный; сам индикатор не меняет цвет светодиодов. В десятичном режиме показывается номер состояния, а не обратный отсчёт. Этот порядок отличается от BGM abcdefgh[7:0]. Пока проект управляет uio[2:0], соответствующие выводы RP2350 должны быть входами.
Полный project.v
integration/tinytapeout/project.v
// Synthesizable Tiny Tapeout top; compile as SystemVerilog.
// Set the external project clock to 1 Hz.
// Set USE_CHIP_MODEL to 1 to synthesize the pin-connected version.
module tt_um_timofei_traffic_light
# (
parameter USE_CHIP_MODEL = 0
)
(
input [7:0] ui_in,
output [7:0] uo_out,
input [7:0] uio_in,
output [7:0] uio_out,
output [7:0] uio_oe,
input ena,
input clk,
input rst_n
);
timeunit 1ns;
timeprecision 1ps;
logic red, yellow, green;
logic [9:0] state;
generate
if (USE_CHIP_MODEL == 0)
begin : generic_version
traffic_light dut
(
.clk (clk),
.rst (~ rst_n),
.red (red),
.yellow (yellow),
.green (green),
.state (state)
);
end
else
begin : chip_version
traffic_light_chips dut
(
.clk (clk),
.rst (~ rst_n),
.red (red),
.yellow (yellow),
.green (green),
.state (state)
);
end
endgenerate
// TT output order [7:0] = { dp, g, f, e, d, c, b, a }; active high.
// DIP 0 = 0: top/middle/bottom bar represents red/yellow/green.
// DIP 0 = 1: show state number 0..9, not seconds remaining.
logic [7:0] decimal_segments;
always_comb
begin
decimal_segments = '0;
case (state)
10'b00_0000_0001: decimal_segments = 8'h3f;
10'b00_0000_0010: decimal_segments = 8'h06;
10'b00_0000_0100: decimal_segments = 8'h5b;
10'b00_0000_1000: decimal_segments = 8'h4f;
10'b00_0001_0000: decimal_segments = 8'h66;
10'b00_0010_0000: decimal_segments = 8'h6d;
10'b00_0100_0000: decimal_segments = 8'h7d;
10'b00_1000_0000: decimal_segments = 8'h07;
10'b01_0000_0000: decimal_segments = 8'h7f;
10'b10_0000_0000: decimal_segments = 8'h6f;
default: decimal_segments = '0;
endcase
end
wire [7:0] bars = { 1'b0, yellow, 2'b00, green, 2'b00, red };
assign uo_out = ena ? (ui_in [0] ? decimal_segments : bars) : '0;
assign uio_out = ena ? { 5'b0, green, yellow, red } : '0;
assign uio_oe = ena ? 8'b0000_0111 : '0;
wire unused_inputs = & { 1'b0, ui_in [7:1], uio_in };
endmodule
python3 scripts/stage_integration.py tinytapeout /tmp/timofei_tt_sourcesВыберите фронтенд синтеза с поддержкой timeunit и timeprecision, например Slang для Yosys. Если шаблон использует старый встроенный парсер Yosys, перед синтезом этих исходников нужно сменить фронтенд.
Скопируйте подготовленные исходники в src/ выбранного шаблона Tiny Tapeout. Перенесите фрагмент списка исходников в его info.yaml, сохранив обязательные поля автора, названия, выводов и документации. Это не полный файл подачи проекта. Для варианта соединений микросхем перед синтезом установите USE_CHIP_MODEL=1.
Официальная таблица GPIO платы · Шаблон Verilog · Запуск платы
15. Первый запуск, задания и источники
- При выключенном питании установите микросхемы и все 37 перемычек. Проверьте выемки, выводы питания и разрывы шин.
- Подключите модули и индикаторы с ограничением тока. До запуска проверьте 5 В между шинами.
- Настройте готовый генератор примерно на 1 Гц. Удерживайте RESET=1: красный должен гореть, несмотря на тактирование.
- Отпустите сброс. Наблюдайте повторение красного/жёлтого/зелёного в отношении 4/2/4. Измеряйте полный цикл после начального интервала сброса.
- Запустите тестбенчи. Затем подключите любой вариант через готовую оболочку для ПЛИС или ASIC.
| Наблюдение | Что проверить |
|---|---|
| Всегда красный | RESET постоянно равен 1, нет фронтов такта или U1.13 не на GND. |
| Нет одного цвета | Полярность LED, вывод выхода ИЛИ и соответствующие входы Q. |
| Неверная длительность / пропущен цвет | Перепутаны номера состояний и физических выводов; свериться с таблицей. |
| Тусклые огни или нестабильность | Измеренное питание, общая земля, нагрузка модулей и определённость входных уровней. Логическая HDL-симуляция этого не проверяет. |
Следующие задания: измените группировку на 3/3/4; сначала перерисуйте граф состояний и предскажите таблицу, затем меняйте провода. Объясните, почему прямое соединение выходов Q не заменяет ИЛИ. Сравните one-hot RTL с двоичным счётчиком 0…9 и дешифратором. Разберите разницу между коротким переходным эффектом задержек и неверным установившимся состоянием.
Схемы и модели без задержек описывают установившуюся логику. У реальных выходов есть задержки распространения; упражнение не доказывает отсутствие переходных импульсов на огнях. Используется готовый тактовый модуль курса: собирать генератор здесь не нужно.
Источники и воспроизводимость
- Курс и упражнение со светофором
- Таблица компонентов LSSK (использован локальный снимок 14 октября)
- Описание проекта; использованы цитата из задания и сохранённый текст
- Basics-Graphics-Music
- Подход к обучению
- Базовые упражнения на макетной плате
- Страница проекта Maker Faire (загрузка завершилась тайм-аутом; технические выводы на неё не опираются)
Страницы: python3 breadboard/tools/build_site.py; схемы и структурный RTL: python3 breadboard/tools/build_diagrams.py --png. Для PNG нужен Inkscape. Проверки проводов · Контрольные суммы исходников BGM.