Упражнение M11 · мини-проект · конечный автомат
Светофор на десятичном счётчике
Четыре секунды красный, две жёлтый, четыре зелёный, и снова. Школьник Тимофей Никифоров собрал этот светофор из десятичного счётчика 4017 и элементов ИЛИ. Это конечный автомат с десятью состояниями, идущими по кругу, а микросхема счётчика служит его регистром состояния.
1Что это и зачем
Что делает. Светофор, который переключается раз в такт: четыре такта красный, два жёлтый, четыре зелёный, потом снова красный. Если генератор настроен на 1 Гц, получается 4 секунды красного, 2 жёлтого и 4 зелёного. Школьник Тимофей Никифоров сам додумался до этой схемы и собрал её вместе с младшим братом Нилом (статья на Хабре, занятие 1.7, и «Teaching children electronics is due for a major redesign», рисунок 15).
Как работает. У 4017 десять выходов, от Q0 до Q9, и в любой момент высокий уровень ровно на одном из них: на Q0 после сброса, на Q1 после первого растущего фронта такта и так далее до Q9, затем снова Q0. Каждый выход — это одно состояние конечного автомата. Три элемента ИЛИ превращают состояния в свет:
красный = Q0 ИЛИ Q1 ИЛИ Q2 ИЛИ Q3
жёлтый = Q4 ИЛИ Q5
зелёный = Q6 ИЛИ Q7 ИЛИ Q8 ИЛИ Q9
Классическая схема, но на логических элементах. Светофор на 4017 с генератором на 555 — известная радиолюбительская схема. В обычном варианте выходы 4017 объединяют диодами, а не элементами ИЛИ, и красный часто берут с выхода CARRY OUT, на котором единица при счёте 0–4 (см. похожие схемы в разделе 15). С элементами ИЛИ та же идея читается как логика: каждый свет — это ИЛИ от состояний, и именно так схема записана на SystemVerilog дальше на этой странице.
Где применяется. Автомат, у которого состояния идут по кругу в одном и том же порядке, по шагу за такт, — самый простой вид конечного автомата. В микросхемах таких автоматов много: контроллер, который проходит фазы пересылки (выставить адрес, подождать, забрать данные), устройство управления простого процессора (выборка, декодирование, исполнение, запись результата), драйвер индикатора, который зажигает разряды по очереди, round-robin планировщик, который даёт каждому запросу его очередь, таймер, который делит период на интервалы. В ПЛИС такие автоматы часто строят ровно так, как устроены выходы 4017: по триггеру на состояние, единица в каждый момент только в одном из них (унитарный код, one-hot), а выходы — это ИЛИ от состояний.
Зачем это изучать.
- Здесь в самом простом виде видно устройство любого автомата Мура: регистр состояния, логика следующего состояния и выходная логика, которая зависит только от состояния.
- Чтобы поменять длительности, достаточно перекинуть провода на входах ИЛИ, новая схема не нужна. Вот что даёт описание выходов как функций состояния.
- Внутри 4017 — счётчик Джонсона, классический приём цифрового проектирования: пять триггеров дают десять состояний, а каждое состояние распознаёт элемент И на два входа, без глитчей.
- На SystemVerilog та же схема занимает десяток строк, и симулятор сверит с ней версию из микросхем ещё до того, как вы вставите первый провод.
2Главный элемент: счётчик с дешифрированными выходами
Каждый растущий фронт такта сдвигает автомат на шаг по часовой стрелке; сброс сразу переводит его в S0. Цвет кружка — это свет, который горит в этом состоянии. Состояние меняется только по фронту такта, а свет зависит только от состояния: это автомат Мура.
Отдельная логика следующего состояния кругу не нужна: следующее состояние вычисляет сам счётчик. Проектировать надо только выходную логику, три элемента ИЛИ.
Внутри 4017 стоит не двоичный счётчик. Двоичному хватило бы четырёх триггеров на десять состояний, но у него за такт меняются сразу несколько битов (при переходе 7 → 8 меняются все четыре), и дешифратор на мгновение может показать не то состояние — это глитч.
Внутри 4017: счётчик Джонсона и дешифратор
В 4017 пять D-триггеров соединены в сдвиговый регистр, а последний бит возвращается в первый инвертированным. Начиная с 00000, регистр справа заполняется единицами, потом нулями, проходит десять комбинаций и за такт меняет ровно один бит. Каждую комбинацию узнают по двум соседним битам, поэтому на состояние хватает одного элемента И на два входа, и дешифратор не даёт глитчей.
| Состояние | Регистр Джонсона r4…r0 | Дешифратор: q = 1, когда | Q9…Q0 | Перенос | Свет |
|---|---|---|---|---|---|
| S0 | 00000 | ~r0 & ~r4 | 0000000001 | 1 | красный |
| S1 | 00001 | r0 & ~r1 | 0000000010 | 1 | красный |
| S2 | 00011 | r1 & ~r2 | 0000000100 | 1 | красный |
| S3 | 00111 | r2 & ~r3 | 0000001000 | 1 | красный |
| S4 | 01111 | r3 & ~r4 | 0000010000 | 1 | жёлтый |
| S5 | 11111 | r0 & r4 | 0000100000 | 0 | жёлтый |
| S6 | 11110 | ~r0 & r1 | 0001000000 | 0 | зелёный |
| S7 | 11100 | ~r1 & r2 | 0010000000 | 0 | зелёный |
| S8 | 11000 | ~r2 & r3 | 0100000000 | 0 | зелёный |
| S9 | 10000 | ~r3 & r4 | 1000000000 | 0 | зелёный |
Условные обозначения
D-триггер со сбросом
| rst | clk | D | Q после |
|---|---|---|---|
| 1 | любой | любой | 0, сразу |
| 0 | ↑ | 0 | 0 |
| 0 | ↑ | 1 | 1 |
| 0 | нет фронта | любой | хранит Q |
Q принимает значение D по растущему фронту такта (↑) и хранит его до следующего фронта. R = 1 сразу обнуляет Q, не дожидаясь такта.
НЕ
| A | Y |
|---|---|
| 0 | 1 |
| 1 | 0 |
Возвращает инвертированный последний бит в первый триггер. Кружок на входе или выходе элемента означает НЕ.
И с инверсным входом
| A | B | Y = A И НЕ B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
По такому элементу на каждое состояние дешифратора, например q1 = r0 И НЕ r1.
ИЛИ на 2 и на 4 входа
| A | B | Y = A ИЛИ B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
Y = 1, когда 1 хотя бы на одном входе. ИЛИ на 4 входа даёт 1, если единица хотя бы на одном из четырёх.
Десятичный счётчик 4017
CLK считает по растущему фронту, INH = 1 останавливает счёт, RST = 1 ставит счёт в 0. Высокий уровень на одном из Q0–Q9; CO в состояниях 0–4 равен 1.
Временные диаграммы
tb.sv в Icarus Verilog. Сброс снимается между двумя фронтами такта;
дальше каждый растущий фронт (пунктир) сдвигает регистр Джонсона r и переводит автомат на шаг. Каждый выход
Q0–Q9 держит единицу ровно один такт; красный горит четыре такта, жёлтый два, зелёный четыре. · SVG · PNG3Детали
| Деталь | В этой схеме | Кол-во | Цена | Поставщик |
|---|---|---|---|---|
| CD4017 | десятичный счётчик: регистр состояния и дешифратор | 1 | $0.40 | Elexp (elexp.com) |
| CD4072 | два элемента ИЛИ на 4 входа: красный и зелёный | 1 | $0.25 | Elexp (elexp.com) |
| CD4071 | четыре элемента ИЛИ на 2 входа: жёлтый (нужен один) | 1 | $0.25 | Elexp (elexp.com) |
| Генератор на 555 | тактовый сигнал, около 1 Гц | 1 | $0.53 | aliexpress.us |
| Модуль кнопки | сброс; один модуль из набора из пяти | 1 | $0.66 / набор | aliexpress.us |
| Модуль светофора на 5 В | три лампы, резисторы уже на плате | 1 | $0.40 | aliexpress.us |
| Макетная плата на 830 точек | 1 | $3.00 | amazon.com | |
| Блок питания для макетной платы | 5 В на шинах | 1 | $1.00 | aliexpress.us |
| Кабель USB-DC на 9 В | питание блока от USB-зарядки | 1 | $1.17 | aliexpress.us |
| Нарезанные перемычки | 24 проводов на плате | 24 | $11.95 / набор | jameco.com |
| Перемычки Dupont папа-мама | 10 проводов к трём модулям | 10 | $1.30 / набор | aliexpress.us |
Цены из таблицы компонентов LSSK; упражнение входит в полный набор (Comprehensive kit). Кнопка сброса не обязательна: без неё соедините ножку 15 микросхемы 4017 с GND. В 4017 есть дополнительные элементы, которые после включения питания из любого состояния выводят счётчик на круг из десяти состояний, так что светофор начнёт со случайного цвета и дальше пойдёт как обычно.
4Микросхемы, ножка за ножкой
Все три микросхемы — КМОП серии 4000B: работают от 3 до 18 В, здесь от 5 В блока питания. Каждый вход должен быть куда-то подключён: висящий вход КМОП ловит наводки и может переключаться сам по себе, поэтому неиспользуемые входы 4071 подключены к +5V или GND. Все выходы здесь с активным высоким уровнем: 1 — это +5 В и горящая лампа. Микросхемы нарисованы сверху, как они стоят на макетной плате: выемка слева, ножка 1 внизу слева, нумерация против часовой стрелки. Синие стрелки — входы, оранжевые — выходы.
4017: десятичный счётчик с 10 дешифрированными выходами
Активные уровни. Счёт идёт по растущему фронту CLOCK, пока CLOCK INHIBIT в нуле. RESET активен единицей и асинхронный: пока он равен 1, счёт равен 0 и на Q0 высокий уровень, что бы ни происходило с тактом. CLOCK INHIBIT активен единицей: 1 останавливает счёт.
4017 считает и по падающему фронту CLOCK INHIBIT, если CLOCK в этот момент в единице: CLOCK INHIBIT стробирует такт. Здесь он подключён к GND, и важен только CLOCK.
| Ножка | Имя | Направление | Функция | В этой схеме |
|---|---|---|---|---|
| 1 | Q5 | выход | 1 в состоянии 5, иначе 0 | Q5 |
| 2 | Q1 | выход | 1 в состоянии 1, иначе 0 | Q1 |
| 3 | Q0 | выход | 1 в состоянии 0, иначе 0 | Q0 |
| 4 | Q2 | выход | 1 в состоянии 2, иначе 0 | Q2 |
| 5 | Q6 | выход | 1 в состоянии 6, иначе 0 | Q6 |
| 6 | Q7 | выход | 1 в состоянии 7, иначе 0 | Q7 |
| 7 | Q3 | выход | 1 в состоянии 3, иначе 0 | Q3 |
| 8 | VSS | питание | земля, 0 В | GND |
| 9 | Q8 | выход | 1 в состоянии 8, иначе 0 | Q8 |
| 10 | Q4 | выход | 1 в состоянии 4, иначе 0 | Q4 |
| 11 | Q9 | выход | 1 в состоянии 9, иначе 0 | Q9 |
| 12 | CARRY OUT | выход | 1 в состояниях 0-4, 0 в состояниях 5-9; растёт при переходе счёта с 9 на 0 и может тактировать следующий 4017 | не используется |
| 13 | CLOCK INHIBIT | вход | 1 останавливает счёт, 0 разрешает. Активный уровень высокий | на GND |
| 14 | CLOCK | вход | счёт идёт по растущему фронту, с 0 на 1 | CLK |
| 15 | RESET | вход | 1 сразу ставит счёт в 0 (Q0 = 1), не дожидаясь такта. Активный уровень высокий | RST |
| 16 | VDD | питание | питание, +5 В (допустимо от 3 до 18 В) | +5V |
4072: два элемента ИЛИ на 4 входа
Два независимых элемента: ножка 1 = ножки 2 ИЛИ 3 ИЛИ 4 ИЛИ 5, ножка 13 = ножки 9 ИЛИ 10 ИЛИ 11 ИЛИ 12. Здесь первый элемент даёт красный, второй — зелёный, так что заняты все восемь входов.
| Ножка | Имя | Направление | Функция | В этой схеме |
|---|---|---|---|---|
| 1 | J | выход | J = A ИЛИ B ИЛИ C ИЛИ D | RED |
| 2 | A | вход | вход элемента ИЛИ с выходом на ножке 1 | Q3 |
| 3 | B | вход | вход элемента ИЛИ с выходом на ножке 1 | Q1 |
| 4 | C | вход | вход элемента ИЛИ с выходом на ножке 1 | Q0 |
| 5 | D | вход | вход элемента ИЛИ с выходом на ножке 1 | Q2 |
| 6 | NC | — | внутри микросхемы ни к чему не подключена | не подключена |
| 7 | VSS | питание | земля, 0 В | GND |
| 8 | NC | — | внутри микросхемы ни к чему не подключена | не подключена |
| 9 | E | вход | вход элемента ИЛИ с выходом на ножке 13 | Q7 |
| 10 | F | вход | вход элемента ИЛИ с выходом на ножке 13 | Q6 |
| 11 | G | вход | вход элемента ИЛИ с выходом на ножке 13 | Q8 |
| 12 | H | вход | вход элемента ИЛИ с выходом на ножке 13 | Q9 |
| 13 | K | выход | K = E ИЛИ F ИЛИ G ИЛИ H | GREEN |
| 14 | VDD | питание | питание, +5 В (допустимо от 3 до 18 В) | +5V |
4071: четыре элемента ИЛИ на 2 входа
Четыре независимых элемента. Ножка 4 = ножка 5 ИЛИ ножка 6 даёт жёлтый из Q5 и Q4. Остальные три элемента не нужны, но их шесть входов всё равно подключены к +5V или GND, а выходы остаются свободными.
| Ножка | Имя | Направление | Функция | В этой схеме |
|---|---|---|---|---|
| 1 | A | вход | вход элемента ИЛИ с выходом на ножке 3 | на GND |
| 2 | B | вход | вход элемента ИЛИ с выходом на ножке 3 | на GND |
| 3 | J | выход | J = A ИЛИ B | не используется |
| 4 | K | выход | K = C ИЛИ D | YELLOW |
| 5 | C | вход | вход элемента ИЛИ с выходом на ножке 4 | Q5 |
| 6 | D | вход | вход элемента ИЛИ с выходом на ножке 4 | Q4 |
| 7 | VSS | питание | земля, 0 В | GND |
| 8 | E | вход | вход элемента ИЛИ с выходом на ножке 10 | на +5V |
| 9 | F | вход | вход элемента ИЛИ с выходом на ножке 10 | на +5V |
| 10 | L | выход | L = E ИЛИ F | не используется |
| 11 | M | выход | M = G ИЛИ H | не используется |
| 12 | G | вход | вход элемента ИЛИ с выходом на ножке 11 | на +5V |
| 13 | H | вход | вход элемента ИЛИ с выходом на ножке 11 | на +5V |
| 14 | VDD | питание | питание, +5 В (допустимо от 3 до 18 В) | +5V |
5Схема 1: триггеры и логические элементы
traffic_light.sv. · SVG · PNGЧитайте её как конечный автомат:
- Регистр состояния. Пять D-триггеров, от r0 до r4, все работают от одного фронта такта и обнуляются сигналом rst.
- Следующее состояние. Каждый триггер берёт значение левого соседа, а первый — инвертированное значение последнего. Вся логика следующего состояния — один элемент НЕ.
- Дешифратор. Десять элементов И, по одному на состояние. Каждый смотрит на два соседних бита, например q2 = r1 И НЕ r2. Точка на линии означает соединение; линии, которые пересекаются без точки, не соединены.
- Выходная логика. Три элемента ИЛИ.
Первые три блока спрятаны внутри 4017; на макетной плате вы собираете только четвёртый.
6Схема 2: соединения между микросхемами
Все соединения схемы, цепь за цепью. По этому же списку написан traffic_light_chips.sv из
раздела 10.
| Цепь | Откуда | Куда | Провод |
|---|---|---|---|
| CLK | генератор 555, OUT | 4017 14 (CLK) | M-F |
| RST | кнопка сброса, OUT | 4017 15 (RST) | M-F |
| Q0 | 4017 3 (Q0) | 4072 4 (C) | M-M |
| Q1 | 4017 2 (Q1) | 4072 3 (B) | M-M |
| Q2 | 4017 4 (Q2) | 4072 5 (D) | M-M |
| Q3 | 4017 7 (Q3) | 4072 2 (A) | M-M |
| Q4 | 4017 10 (Q4) | 4071 6 (D) | M-M |
| Q5 | 4017 1 (Q5) | 4071 5 (C) | M-M |
| Q6 | 4017 5 (Q6) | 4072 10 (F) | M-M |
| Q7 | 4017 6 (Q7) | 4072 9 (E) | M-M |
| Q8 | 4017 9 (Q8) | 4072 11 (G) | M-M |
| Q9 | 4017 11 (Q9) | 4072 12 (H) | M-M |
| RED | 4072 1 (J) | светофор, R | M-F |
| YELLOW | 4071 4 (K) | светофор, Y | M-F |
| GREEN | 4072 13 (K) | светофор, G | M-F |
| +5V | +5V | 4071 14 (VDD), 4017 16 (VDD), 4072 14 (VDD); неиспользуемые входы: 4071 13 (H), 4071 12 (G), 4071 9 (F), 4071 8 (E) | |
| GND | GND | 4071 7 (VSS), 4017 8 (VSS), 4072 7 (VSS); неиспользуемые входы: 4071 1 (A), 4071 2 (B), 4017 13 (INH) | |
| не подключены | 4017 12 (CO), 4071 3 (J), 4071 10 (L), 4071 11 (M), 4072 6 (NC), 4072 8 (NC) |
7Схема 3: макетная плата
Сборка по шагам
- Сначала пройдите первые шаги: как макетная плата соединена внутри, блок питания с обеими перемычками на 5 В, как микросхема встаёт поперёк канавки.
- Вставьте микросхемы поперёк канавки, выемкой влево: 4071 ножкой 1 в отверстие 12f, 4017 ножкой 1 в 22f, 4072 ножкой 1 в 33f. Проверьте надписи на корпусах.
- Питание: красный провод от ножки 14 или 16 на верхнюю шину + и чёрный от ножки 7 или 8 на нижнюю шину −, для каждой микросхемы. Потом провода неиспользуемых входов. Проследите, чтобы ни один провод не касался + и − одновременно.
- Одиннадцать сигнальных проводов, в порядке списка ниже. Отмечайте каждый провод, когда вставите его.
- Модули, при выключенном питании: кнопку сброса и генератор над платой, светофор под ней. У светофора первым подключите GND.
- Включите питание и потенциометром генератора установите частоту около 1 Гц.
Список проводов
| # | Цепь | Откуда | Куда | |
|---|---|---|---|---|
| Питание микросхем (красные к +, чёрные к −) · 6 проводов | ||||
| 1 | +5V | 12a (4071, ножка 14, VDD) | верхняя шина +, столбик 12 | |
| 2 | GND | 18j (4071, ножка 7, VSS) | нижняя шина −, столбик 18 | |
| 3 | +5V | 22a (4017, ножка 16, VDD) | верхняя шина +, столбик 22 | |
| 4 | GND | 29j (4017, ножка 8, VSS) | нижняя шина −, столбик 29 | |
| 5 | +5V | 33a (4072, ножка 14, VDD) | верхняя шина +, столбик 33 | |
| 6 | GND | 39j (4072, ножка 7, VSS) | нижняя шина −, столбик 39 | |
| Неиспользуемые входы: входы КМОП нельзя оставлять висящими · 7 проводов | ||||
| 7 | GND | 12j (4071, ножка 1, A) | нижняя шина −, столбик 12 | |
| 8 | GND | 13j (4071, ножка 2, B) | нижняя шина −, столбик 13 | |
| 9 | +5V | 13a (4071, ножка 13, H) | верхняя шина +, столбик 13 | |
| 10 | +5V | 14a (4071, ножка 12, G) | верхняя шина +, столбик 14 | |
| 11 | +5V | 17a (4071, ножка 9, F) | верхняя шина +, столбик 17 | |
| 12 | +5V | 18a (4071, ножка 8, E) | верхняя шина +, столбик 18 | |
| 13 | GND | 25a (4017, ножка 13, INH) | верхняя шина −, столбик 25 | |
| Сигнальные провода · 11 проводов | ||||
| 14 | Q3 | 28g (4017, ножка 7, Q3) | 34g (4072, ножка 2, A) | |
| 15 | Q1 | 23h (4017, ножка 2, Q1) | 35h (4072, ножка 3, B) | |
| 16 | Q0 | 24i (4017, ножка 3, Q0) | 36i (4072, ножка 4, C) | |
| 17 | Q2 | 25j (4017, ножка 4, Q2) | 37j (4072, ножка 5, D) | |
| 18 | Q7 | 27j (4017, ножка 6, Q7) | 38c (4072, ножка 9, E) | |
| 19 | Q6 | 26g (4017, ножка 5, Q6) | 37d (4072, ножка 10, F) | |
| 20 | Q8 | 29c (4017, ножка 9, Q8) | 36c (4072, ножка 11, G) | |
| 21 | Q9 | 27d (4017, ножка 11, Q9) | 35d (4072, ножка 12, H) | |
| 22 | Q5 | 22i (4017, ножка 1, Q5) | 16i (4071, ножка 5, C) | |
| 23 | Q4 | 28c (4017, ножка 10, Q4) | 17g (4071, ножка 6, D) | |
| 24 | GREEN | 34b (4072, ножка 13, K) | 11g | |
| Кнопка сброса и генератор над платой (перемычки папа-мама) · 6 проводов | ||||
| 25 | +5V | кнопка сброса, + | верхняя шина +, столбик 19 | |
| 26 | RST | кнопка сброса, OUT | 23a (4017, ножка 15, RST) | |
| 27 | GND | кнопка сброса, − | верхняя шина −, столбик 21 | |
| 28 | +5V | генератор, + | верхняя шина +, столбик 28 | |
| 29 | CLK | генератор, OUT | 24a (4017, ножка 14, CLK) | |
| 30 | GND | генератор, − | верхняя шина −, столбик 26 | |
| Модуль светофора под платой (перемычки папа-мама) · 4 провода | ||||
| 31 | GREEN | светофор, G | 11j | |
| 32 | YELLOW | светофор, Y | 15j (4071, ножка 4, K) | |
| 33 | RED | светофор, R | 33j (4072, ножка 1, J) | |
| 34 | GND | светофор, GND | нижняя шина −, столбик 9 | |
Проверка
- Свет идёт красный–красный–красный–красный–жёлтый–жёлтый–зелёный–зелёный–зелёный–зелёный, по шагу на каждое мигание светодиода генератора.
- Нажмите кнопку сброса: сразу загорается красный и горит, пока кнопка нажата. Когда вы её отпустите, цикл начнётся с четырёх красных шагов.
- Покрутите потенциометр генератора: весь цикл идёт быстрее или медленнее, а соотношение 4 : 2 : 4 остаётся.
Если не работает: лампа, которая никогда не загорается, — обычно провод, сдвинутый на одно отверстие; лампа, которая горит всегда, — вход ИЛИ, по ошибке подключённый к +5V. Две лампы сразу — замкнуты два выхода. Если свет остановился, проверьте ножку 13 микросхемы 4017 (должна быть на GND) и ножку 15 (GND, пока кнопка не нажата). Если микросхемы греются, сразу выключите питание и ищите замыкание между + и −.
8SystemVerilog: общая схема
traffic_light.sv описывает схему 1: регистр Джонсона — это блок always_ff,
дешифратор — десять assign с И, выходная логика — три с ИЛИ. Код синтезируемый: маршрут для ПЛИС
или ASIC превратит его в пять триггеров и несколько элементов. Выход q показывает состояние, как
десять выходов 4017, чтобы его видели тестбенч и платы.
// Exercise M11: a traffic light from a decade counter and OR gates, // after Timofei Nikiforov's breadboard design. // // Generic version that matches schematic 1: five D flip-flops and gates, // not tied to any chip. Synthesizable. module traffic_light ( input logic clk, input logic rst, // 1 = go to state 0 at once, like RESET of the 4017 output logic [9:0] q, // q [k] = 1 in state k, like outputs Q0-Q9 of the 4017 output logic red, output logic yellow, output logic green ); //------------------------------------------------------------------------ // State register: a 5-bit Johnson counter (twisted ring counter). // Every clock it shifts left and feeds the inverted top bit into bit 0: // // state 0 1 2 3 4 5 6 7 8 9 // r 00000 00001 00011 00111 01111 11111 11110 11100 11000 10000 // // Five flip-flops give ten states, and one bit changes per clock. logic [4:0] r; always_ff @ (posedge clk or posedge rst) if (rst) r <= '0; else r <= { r [3:0], ~ r [4] }; //------------------------------------------------------------------------ // Decoder: ten 2-input AND gates. Two neighbouring bits identify each state. assign q [0] = ~ r [4] & ~ r [0]; assign q [1] = r [0] & ~ r [1]; assign q [2] = r [1] & ~ r [2]; assign q [3] = r [2] & ~ r [3]; assign q [4] = r [3] & ~ r [4]; assign q [5] = r [4] & r [0]; assign q [6] = ~ r [0] & r [1]; assign q [7] = ~ r [1] & r [2]; assign q [8] = ~ r [2] & r [3]; assign q [9] = ~ r [3] & r [4]; //------------------------------------------------------------------------ // Output logic: OR gates assign red = q [0] | q [1] | q [2] | q [3]; // 4 clock cycles assign yellow = q [4] | q [5]; // 2 clock cycles assign green = q [6] | q [7] | q [8] | q [9]; // 4 clock cycles endmodule
{ r [3:0], ~ r [4] } — это конкатенация: младшие четыре бита сдвигаются на разряд вверх, а снизу
приходит инвертированный старший бит. Эта строка и есть сдвиговый регистр со своим элементом НЕ.
9Модели микросхем на SystemVerilog
По модулю на микросхему, порты названы как в даташите, номер ножки каждого порта — в комментарии. Все три модели синтезируемые. Выводов питания в моделях нет: в SystemVerilog у провода просто есть значение 0 или 1.
// CD4017B: CMOS decade counter with 10 decoded outputs. // // Model of the chip as seen at its pins. The power pins, VDD (16) and VSS (8), // are left out. Synthesizable. // // The chip counts on a rising edge of CLOCK while CLOCK INHIBIT is low, and // also on a falling edge of CLOCK INHIBIT while CLOCK is high: CLOCK INHIBIT // gates the clock. The model does the same with an AND gate in the clock path. // In an FPGA design keep clock_inhibit at 0; then the gate is just a wire. // // Inside, the counter is a 5-stage Johnson counter with a decoder, the same // circuit as traffic_light.sv. The real chip also has extra gates that bring // the counter into the ten-state sequence from any state after power-up; // the model needs a reset instead. module cd4017 ( input logic clock, // Pin 14 input logic clock_inhibit, // Pin 13: 1 = hold the count input logic reset, // Pin 15: 1 = count 0 (Q0 = 1) at once output logic [9:0] q, // Pins 3 2 4 7 10 1 5 6 9 11 = Q0 ... Q9 output logic carry_out // Pin 12: 1 for counts 0-4, 0 for counts 5-9 ); wire gated_clock = clock & ~ clock_inhibit; logic [4:0] r; always_ff @ (posedge gated_clock or posedge reset) if (reset) r <= '0; else r <= { r [3:0], ~ r [4] }; assign q [0] = ~ r [4] & ~ r [0]; assign q [1] = r [0] & ~ r [1]; assign q [2] = r [1] & ~ r [2]; assign q [3] = r [2] & ~ r [3]; assign q [4] = r [3] & ~ r [4]; assign q [5] = r [4] & r [0]; assign q [6] = ~ r [0] & r [1]; assign q [7] = ~ r [1] & r [2]; assign q [8] = ~ r [2] & r [3]; assign q [9] = ~ r [3] & r [4]; assign carry_out = ~ r [4]; endmodule
В модели 4017 есть деталь, которой вы, возможно, не ждёте: gated_clock. Настоящая микросхема
считает по растущему фронту CLOCK, пока CLOCK INHIBIT в нуле, и ещё по падающему фронту CLOCK INHIBIT, пока CLOCK
в единице; элемент И в цепи такта делает ровно это. В ПЛИС такт не должен проходить через логику, поэтому держите
clock_inhibit в нуле: синтез тогда уберёт этот элемент. Настоящая микросхема ещё и сама выходит на
круг из любого состояния после включения питания; модели нужен сброс.
// CD4071B: quad 2-input OR gate. // // Ports in pin order, named as in the datasheet. The power pins, VSS (7) // and VDD (14), are left out. Synthesizable. module cd4071 ( input logic a, // Pin 1 input logic b, // Pin 2 output logic j, // Pin 3: j = a | b output logic k, // Pin 4: k = c | d input logic c, // Pin 5 input logic d, // Pin 6 // Pin 7: VSS input logic e, // Pin 8 input logic f, // Pin 9 output logic l, // Pin 10: l = e | f output logic m, // Pin 11: m = g | h input logic g, // Pin 12 input logic h // Pin 13 // Pin 14: VDD ); assign j = a | b; assign k = c | d; assign l = e | f; assign m = g | h; endmodule
// CD4072B: dual 4-input OR gate. // // Ports in pin order, named as in the datasheet. The power pins, VSS (7) // and VDD (14), are left out, and so are the unconnected pins 6 and 8. // Synthesizable. module cd4072 ( output logic j, // Pin 1: j = a | b | c | d input logic a, // Pin 2 input logic b, // Pin 3 input logic c, // Pin 4 input logic d, // Pin 5 // Pin 6: not connected // Pin 7: VSS // Pin 8: not connected input logic e, // Pin 9 input logic f, // Pin 10 input logic g, // Pin 11 input logic h, // Pin 12 output logic k // Pin 13: k = e | f | g | h // Pin 14: VDD ); assign j = a | b | c | d; assign k = e | f | g | h; endmodule
10SystemVerilog: схема из микросхем
traffic_light_chips.sv соединяет модели микросхем точно так же, как соединены микросхемы на схеме
2 и на макетной плате, и у него те же порты, что у traffic_light.sv. Неиспользуемые входы подключены
к vdd или gnd, как на плате, а неиспользуемые выходы — к проводам с именами
unused_…. Файл генерируется из списка проводов макетной платы после проверки этого списка на уровне
ножек, поэтому код, рисунки и список проводов не могут разойтись.
// Exercise M11, chip-level version: the chips and wires of schematic 2 and // of the breadboard. Same ports as traffic_light.sv. // // Generated by tools/bb_m11.py from the checked breadboard wire list; // change the wire list there, not this file. Synthesizable. module traffic_light_chips ( input logic clk, input logic rst, output logic [9:0] q, output logic red, output logic yellow, output logic green ); // The power rails of the breadboard, for tied-off inputs wire gnd = 1'b0; wire vdd = 1'b1; // Outputs that go nowhere wire unused_4071_j; wire unused_4071_l; wire unused_4071_m; wire unused_4017_carry_out; //------------------------------------------------------------------------ // CD4071 quad 2-input OR, pin 1 in column 12 cd4071 i_4071 ( .a ( gnd ), // Pin 1 <- GND, unused input .b ( gnd ), // Pin 2 <- GND, unused input .j ( unused_4071_j ), // Pin 3, unused output .k ( yellow ), // Pin 4 -> traffic light Y .c ( q [5] ), // Pin 5 <- 4017 pin 1 (Q5) .d ( q [4] ), // Pin 6 <- 4017 pin 10 (Q4) .e ( vdd ), // Pin 8 <- +5V, unused input .f ( vdd ), // Pin 9 <- +5V, unused input .l ( unused_4071_l ), // Pin 10, unused output .m ( unused_4071_m ), // Pin 11, unused output .g ( vdd ), // Pin 12 <- +5V, unused input .h ( vdd ) // Pin 13 <- +5V, unused input ); //------------------------------------------------------------------------ // CD4017 decade counter, pin 1 in column 22 cd4017 i_4017 ( .clock ( clk ), // Pin 14 <- the 555 clock module .clock_inhibit ( gnd ), // Pin 13 <- GND .reset ( rst ), // Pin 15 <- the reset button .q ( q ), // Pins 3 2 4 7 10 1 5 6 9 11 = Q0 ... Q9 .carry_out ( unused_4017_carry_out ) // Pin 12, unused output ); //------------------------------------------------------------------------ // CD4072 dual 4-input OR, pin 1 in column 33 cd4072 i_4072 ( .j ( red ), // Pin 1 -> traffic light R .a ( q [3] ), // Pin 2 <- 4017 pin 7 (Q3) .b ( q [1] ), // Pin 3 <- 4017 pin 2 (Q1) .c ( q [0] ), // Pin 4 <- 4017 pin 3 (Q0) .d ( q [2] ), // Pin 5 <- 4017 pin 4 (Q2) .e ( q [7] ), // Pin 9 <- 4017 pin 6 (Q7) .f ( q [6] ), // Pin 10 <- 4017 pin 5 (Q6) .g ( q [8] ), // Pin 11 <- 4017 pin 9 (Q8) .h ( q [9] ), // Pin 12 <- 4017 pin 11 (Q9) .k ( green ) // Pin 13 -> traffic light G ); endmodule
11Тестбенч и скрипты для симуляции
tb.sv запускает обе схемы рядом, от одного такта и одного сброса, и сравнивает их с эталонной
моделью: номером состояния от 0 до 9 и цветом, который ему соответствует. Проверка идёт на каждом падающем фронте
такта, через полпериода после смены состояния; отдельно проверяется асинхронный сброс посреди состояния. В конце
тестбенч считает, сколько тактов горит каждый цвет после сброса, и печатает PASS или
FAIL.
tb.sv192 lines · показать кодфайл
// Testbench for exercise M11. // // Runs the generic design (traffic_light.sv) and the chip-level design // (traffic_light_chips.sv) side by side and compares both with a reference // model: a state number from 0 to 9 and the colour that belongs to it. // Works with Icarus Verilog, Verilator (--binary) and Questa. module tb; logic clk; logic rst; logic [9:0] q, q_chips; logic red, red_chips; logic yellow, yellow_chips; logic green, green_chips; traffic_light i_generic ( .clk ( clk ), .rst ( rst ), .q ( q ), .red ( red ), .yellow ( yellow ), .green ( green ) ); traffic_light_chips i_chips ( .clk ( clk ), .rst ( rst ), .q ( q_chips ), .red ( red_chips ), .yellow ( yellow_chips ), .green ( green_chips ) ); //------------------------------------------------------------------------ // Clock with a period of 10 time units. // On the breadboard one period is about a second. initial begin clk = 1'b0; forever # 5 clk = ~ clk; end //------------------------------------------------------------------------ // Reference model: the number of the state logic [3:0] state; always @ (posedge clk or posedge rst) if (rst) state <= 4'd0; else if (state == 4'd9) state <= 4'd0; else state <= state + 4'd1; wire [9:0] q_expected = 10'b1 << state; wire red_expected = state <= 4'd3; wire yellow_expected = state == 4'd4 || state == 4'd5; wire green_expected = state >= 4'd6; //------------------------------------------------------------------------ // Checks at every falling edge, half a period after the state changes int cycle, n_checks, n_errors; function automatic string light_name (input logic r, input logic y, input logic g); case ({ r, y, g }) 3'b100: return "red "; 3'b010: return "yellow"; 3'b001: return "green "; 3'b000: return "dark "; default: return "mixed "; endcase endfunction function automatic logic is_correct ( input logic [9:0] q_actual, input logic r, input logic y, input logic g ); return q_actual === q_expected && r === red_expected && y === yellow_expected && g === green_expected; endfunction // Clock cycles of each colour during the first full period after reset int n_red, n_yellow, n_green, n_period; initial begin cycle = 0; n_checks = 0; n_errors = 0; n_red = 0; n_yellow = 0; n_green = 0; n_period = 0; forever begin logic ok_generic, ok_chips; @ (negedge clk); cycle ++; ok_generic = is_correct (q, red, yellow, green); ok_chips = is_correct (q_chips, red_chips, yellow_chips, green_chips); n_checks += 2; if (! ok_generic) n_errors ++; if (! ok_chips) n_errors ++; $display ("%5d %b %b %s %s %s", cycle, rst, q, light_name (red, yellow, green), ok_generic ? "ok " : "WRONG ", ok_chips ? "ok" : "WRONG"); if (! rst && n_period < 10) begin n_period ++; if (red) n_red ++; if (yellow) n_yellow ++; if (green) n_green ++; end end end //------------------------------------------------------------------------ // Stimulus initial begin $dumpfile ("tb.vcd"); `ifdef __ICARUS__ $dumpvars (0, tb); // Without a scope Icarus also tries to dump $unit `else $dumpvars; `endif $display ("cycle rst Q9..Q0 light generic chips"); // Reset at the start, released between two clock edges rst = 1'b1; # 12 rst = 1'b0; repeat (24) @ (posedge clk); // Reset in the middle of a state: both designs go to state 0 at once, // without waiting for the clock, like RESET of the 4017 # 2 rst = 1'b1; repeat (2) @ (posedge clk); # 7 rst = 1'b0; repeat (12) @ (posedge clk); @ (negedge clk); # 1; // Let the last check print first $display ("One period after reset: red %0d, yellow %0d, green %0d clock cycles", n_red, n_yellow, n_green); if (n_errors == 0 && n_red == 4 && n_yellow == 2 && n_green == 4) $display ("PASS: %0d checks, no errors", n_checks); else $display ("FAIL: %0d errors in %0d checks", n_errors, n_checks); $finish; end endmodule
Каждый скрипт компилирует шесть файлов, запускает симуляцию, пишет журнал и временную диаграмму
tb.vcd и завершается с ошибкой, если тест не прошёл. Запускать можно из любого каталога:
./run_icarus.bash, ./run_verilator.bash или ./run_questa.bash;
./clean.bash удаляет то, что они создают. Диаграмму открывают в
GTKWave или Surfer.
run_icarus.bash22 lines · показать кодфайл
#!/usr/bin/env bash # Exercise M11: run the testbench with Icarus Verilog 12 or later. # The output goes to icarus.log and the waveform to tb.vcd, # for GTKWave or Surfer. set -Eeuo pipefail cd "$(dirname "$0")" files=(tb.sv traffic_light.sv traffic_light_chips.sv cd4017.sv cd4071.sv cd4072.sv) if ! command -v iverilog > /dev/null then printf '%s\n' "$0: iverilog not found, install Icarus Verilog 12 or later" >&2 exit 1 fi iverilog -g2012 -o tb.vvp "${files[@]}" vvp -n tb.vvp | tee icarus.log grep -q '^PASS' icarus.log
run_verilator.bash29 lines · показать кодфайл
#!/usr/bin/env bash # Exercise M11: run the testbench with Verilator 5. --binary builds # a simulator with the testbench's delays; it needs a C++20 compiler. # The output goes to verilator.log and the waveform to tb.vcd. set -Eeuo pipefail cd "$(dirname "$0")" files=(tb.sv traffic_light.sv traffic_light_chips.sv cd4017.sv cd4071.sv cd4072.sv) # OSS CAD Suite unpacked in the home directory has Verilator too if ! command -v verilator > /dev/null && [ -x "$HOME/oss-cad-suite/bin/verilator" ] then PATH="$HOME/oss-cad-suite/bin:$PATH" fi if ! command -v verilator > /dev/null then printf '%s\n' "$0: verilator not found, install Verilator 5" >&2 exit 1 fi verilator --binary -Wall --trace --top-module tb -o tb "${files[@]}" obj_dir/tb | tee verilator.log grep -q '^PASS' verilator.log
run_questa.bash64 lines · показать кодфайл
#!/usr/bin/env bash # Exercise M11: run the testbench with Questa, for example the free # Questa Starter Edition that comes with Quartus Prime Lite. # The output goes to questa.log and the waveform to tb.vcd. set -Eeuo pipefail cd "$(dirname "$0")" files=(tb.sv traffic_light.sv traffic_light_chips.sv cd4017.sv cd4071.sv cd4072.sv) # When vsim is not in PATH, look for it where Quartus installs it if ! command -v vsim > /dev/null then for dir in "$HOME"/intelFPGA_lite/*/questa_fse/bin \ "$HOME"/altera_lite/*/questa_fse/bin \ /opt/intelFPGA_lite/*/questa_fse/bin \ /opt/altera_lite/*/questa_fse/bin do if [ -x "$dir/vsim" ] then PATH="$dir:$PATH" break fi done fi if ! command -v vsim > /dev/null then printf '%s\n' "$0: vsim not found, install Questa or add it to PATH" >&2 exit 1 fi # The Starter Edition needs a free license file from the Intel # licensing site; Quartus keeps it in ~/flexlm/license.dat license="$HOME/flexlm/license.dat" # ${SALT_LICENSE_SERVER-} is empty when the variable is not set, # where a plain $SALT_LICENSE_SERVER would stop the script (set -u) salt_license="${SALT_LICENSE_SERVER-}" if [ -z "$salt_license" ] && [ -f "$license" ] then export SALT_LICENSE_SERVER="$license" fi # Older Questa versions read LM_LICENSE_FILE; ${LM_LICENSE_FILE-} # is empty when the variable is not set lm_license="${LM_LICENSE_FILE-}" if [ -z "$lm_license" ] && [ -f "$license" ] then export LM_LICENSE_FILE="$license" fi rm -rf work vlib work > /dev/null vlog -sv -lint "${files[@]}" vsim -c tb -do "run -all; quit -f" | tee questa.log grep -q 'PASS' questa.log
Проверено в Icarus Verilog 12.0 и 14.0 (разрабатываемая версия), в Verilator 5.053 с -Wall без
предупреждений и в Questa Altera Starter FPGA Edition 2025.2 из Quartus Prime Lite 25.1. Код использует только то,
что поддерживает Icarus Verilog 13. Вот вывод Icarus Verilog:
VCD info: dumpfile tb.vcd opened for output.
cycle rst Q9..Q0 light generic chips
1 1 0000000001 red ok ok
2 0 0000000010 red ok ok
3 0 0000000100 red ok ok
4 0 0000001000 red ok ok
5 0 0000010000 yellow ok ok
6 0 0000100000 yellow ok ok
7 0 0001000000 green ok ok
8 0 0010000000 green ok ok
9 0 0100000000 green ok ok
10 0 1000000000 green ok ok
11 0 0000000001 red ok ok
12 0 0000000010 red ok ok
13 0 0000000100 red ok ok
14 0 0000001000 red ok ok
15 0 0000010000 yellow ok ok
16 0 0000100000 yellow ok ok
17 0 0001000000 green ok ok
18 0 0010000000 green ok ok
19 0 0100000000 green ok ok
20 0 1000000000 green ok ok
21 0 0000000001 red ok ok
22 0 0000000010 red ok ok
23 0 0000000100 red ok ok
24 0 0000001000 red ok ok
25 1 0000000001 red ok ok
26 1 0000000001 red ok ok
27 1 0000000001 red ok ok
28 0 0000000010 red ok ok
29 0 0000000100 red ok ok
30 0 0000001000 red ok ok
31 0 0000010000 yellow ok ok
32 0 0000100000 yellow ok ok
33 0 0001000000 green ok ok
34 0 0010000000 green ok ok
35 0 0100000000 green ok ok
36 0 1000000000 green ok ok
37 0 0000000001 red ok ok
38 0 0000000010 red ok ok
39 0 0000000100 red ok ok
One period after reset: red 4, yellow 2, green 4 clock cycles
PASS: 78 checks, no errors
tb.sv:189: $finish called at 391 (1s)
Чтобы убедиться, что тестбенч ловит ошибки, сломайте что-нибудь и запустите снова: подключите ножку 2
микросхемы 4072 к q [4] вместо q [3] в traffic_light_chips.sv или
поменяйте одну строку дешифратора в traffic_light.sv. Скрипт сообщит FAIL, а в
распечатке появятся строки с WRONG.
12На плате ПЛИС: lab_top в BGM
Репозиторий Basics-Graphics-Music (BGM)
запускает одни и те же лабораторные работы на десятках плат ПЛИС. В каждой работе есть модуль
lab_top с одинаковыми на всех платах портами: такт, медленный такт slow_clk на
1 Гц, сброс, кнопки, светодиоды, семисегментный индикатор.
- Скопируйте работу, например
labs/1_basics/1_06_binary_counter, в новый каталог, скажемlabs/1_basics/1_11_traffic_light. - Замените в нём
lab_top.svнаbgm_lab/lab_top.svиз этого упражнения и добавьтеtraffic_light.sv. Скрипты BGM компилируют все файлы.svкаталога, кромеtb.sv. - Запустите
./03_synthesize_for_fpga.bashи./04_configure_fpga.bash; при первом запуске скрипт спросит, какая у вас плата.
Светофор работает от slow_clk, который каждая плата BGM делает на 1 Гц, поэтому он шагает
раз в секунду, как на макетной плате. BGM делает его в peripherals/slow_clk_gen.sv: счётчик на такте
платы переключает триггер, а на ПЛИС AMD (Xilinx) и Intel (Altera) примитив BUFG или
global выводит этот сигнал в тактовую сеть ПЛИС, так что он доходит до всех триггеров как настоящий
такт. Правый разряд индикатора — маленький светофор: сегмент a (верхний) красный,
g (средний) жёлтый, d (нижний) зелёный. Светодиоды показывают состояние бегущим огоньком. Чтобы запустить схему из
микросхем, добавьте traffic_light_chips.sv и три модели микросхем и поменяйте имя модуля в
экземпляре.
bgm_lab/lab_top.sv117 lines · показать кодфайл
// Exercise M11 on an FPGA board with the Basics-Graphics-Music (BGM) labs: // https://github.com/yuri-panchul/basics-graphics-music // // Copy a lab directory, for example labs/1_basics/1_06_binary_counter, // replace its lab_top.sv with this file and add traffic_light.sv. // For the chip-level design add traffic_light_chips.sv, cd4017.sv, cd4071.sv // and cd4072.sv instead, and change the module name below. // // The traffic light runs from slow_clk, the 1 Hz clock that every BGM board // wrapper makes, so it changes state once a second, like the breadboard with // the 555 module. Reset is the reset button of the board. // The 7-segment display shows the light: segment a red, g yellow, d green. // The LEDs show the state as a running light. `include "config.svh" module lab_top # ( parameter clk_mhz = 50, w_key = 4, w_sw = 8, w_led = 8, w_digit = 8, w_gpio = 100, screen_width = 640, screen_height = 480, w_red = 4, w_green = 4, w_blue = 4, w_x = $clog2 ( screen_width ), 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 logic [w_led - 1:0] led, // A dynamic seven-segment display output logic [ 7:0] abcdefgh, output logic [w_digit - 1:0] digit, // Graphics input [w_x - 1:0] x, input [w_y - 1:0] y, output logic [w_red - 1:0] red, output logic [w_green - 1:0] green, output logic [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 ); //------------------------------------------------------------------------ assign red = '0; assign green = '0; assign blue = '0; assign sound = '0; assign uart_tx = '1; //------------------------------------------------------------------------ // lab_top already has ports named red and green (the screen colours), // so the lights get other names here logic [9:0] q; logic light_red, light_yellow, light_green; traffic_light i_traffic_light ( .clk ( slow_clk ), .rst ( rst ), .q ( q ), .red ( light_red ), .yellow ( light_yellow ), .green ( light_green ) ); //------------------------------------------------------------------------ // --a-- // | | // f b // | | // --g-- // | | // e c // | | // --d-- h assign abcdefgh = { light_red, 2'b0, light_green, 2'b0, light_yellow, 1'b0 }; assign digit = w_digit' (1); assign led = w_led' (q); endmodule
13На плате ПЛИС: hackathon_top в BGM
В варианте BGM для хакатонов используется Tang Nano 9K с экраном 480×272 и платой TM1638 с кнопками,
светодиодами и индикатором. У верхнего модуля hackathon_top порты проще, а первая строка файла
называет плату, так что скрипты ничего не спрашивают.
- Скопируйте каталог, например
hackathon/problems/4_binary_counter. - Замените в нём
hackathon_top.svнаbgm_hackathon/hackathon_top.svи добавьтеtraffic_light.sv. - Запустите
./03_synthesize_for_fpga.bashи./04_configure_fpga.bash.
На экране светофор: три круглые лампы в тёмном корпусе, яркие, когда горят, и тусклые, когда нет. Правый
разряд индикатора показывает те же три сегмента, что в lab_top, а восемь светодиодов — состояния
0–7. Порты цвета экрана уже называются red и green, поэтому сигналы светофора здесь
называются light_red, light_yellow и light_green.
bgm_hackathon/hackathon_top.sv112 lines · показать кодфайл
// Board configuration: tang_nano_9k_lcd_480_272_tm1638_hackathon // This module uses few parameterization and relaxed typing rules // Exercise M11 in the hackathon setup of Basics-Graphics-Music (BGM): // https://github.com/yuri-panchul/basics-graphics-music // // Copy a directory such as hackathon/problems/4_binary_counter, replace its // hackathon_top.sv with this file and add traffic_light.sv. // // The traffic light runs from slow_clock, one beat per second, like the // breadboard with the 555 module. The screen shows a traffic light, // the 7-segment display shows it too (segment a red, g yellow, d green), // and the eight LEDs show states 0-7 as a running light. module hackathon_top ( input logic clock, input logic slow_clock, input logic reset, input logic [7:0] key, output logic [7:0] led, // A dynamic seven-segment display output logic [7:0] abcdefgh, output logic [7:0] digit, // LCD screen interface input logic [8:0] x, input logic [8:0] y, output logic [4:0] red, output logic [5:0] green, output logic [4:0] blue, inout logic [3:0] gpio ); //------------------------------------------------------------------------ // The screen colours are called red and green already, // so the lights get other names logic [9:0] q; logic light_red, light_yellow, light_green; traffic_light i_traffic_light ( .clk ( slow_clock ), .rst ( reset ), .q ( q ), .red ( light_red ), .yellow ( light_yellow ), .green ( light_green ) ); assign led = q [7:0]; assign abcdefgh = { light_red, 2'b0, light_green, 2'b0, light_yellow, 1'b0 }; assign digit = 8'b0000_0001; //------------------------------------------------------------------------ // A traffic light on the 480 x 272 screen: three round lamps in a dark box. // dx is the distance from the middle of the screen, dy1-dy3 the distances // from the centres of the lamps, so a lamp is where dx * dx + dy * dy < 34 * 34. wire [8:0] dx = x > 240 ? x - 240 : 240 - x; wire [8:0] dy1 = y > 56 ? y - 56 : 56 - y; wire [8:0] dy2 = y > 136 ? y - 136 : 136 - y; wire [8:0] dy3 = y > 216 ? y - 216 : 216 - y; wire lamp1 = dx * dx + dy1 * dy1 < 34 * 34; wire lamp2 = dx * dx + dy2 * dy2 < 34 * 34; wire lamp3 = dx * dx + dy3 * dy3 < 34 * 34; always_comb begin red = 0; green = 0; blue = 0; if (x >= 196 & x < 284 & y >= 12 & y < 260) begin red = 3; green = 6; blue = 3; end if (lamp1) begin red = light_red ? 31 : 7; green = 0; blue = 0; end if (lamp2) begin red = light_yellow ? 31 : 7; green = light_yellow ? 50 : 11; blue = 0; end if (lamp3) begin red = 0; green = light_green ? 63 : 14; blue = light_green ? 8 : 2; end end endmodule
14На кристалле: Tiny Tapeout
Tiny Tapeout выпускает настоящие микросхемы из проектов учащихся.
Каждый проект — модуль с портами, как ниже; на демонстрационной плате ui_in идёт от DIP-переключателей,
а uo_out управляет семисегментным индикатором.
Демонстрационная плата сама делает такт проекта и может задать его от 1 Гц до 66,5 МГц (см. раздел
Clock в документации Tiny Tapeout, на английском). Задайте
1 Гц, и project.v подаёт clk прямо на светофор, так же как модуль на 555 тактирует
4017 на макетной плате. Индикатор показывает свет на сегментах a, g и d.
tiny_tapeout/project.v54 lines · показать кодфайл
/* * Exercise M11 on Tiny Tapeout: Timofei's traffic light on the 7-segment * display of the demo board. * * Add traffic_light.sv to source_files in info.yaml next to project.v. * Rename the module to tt_um_<your GitHub name>_<project name> and use the * same name as top_module in info.yaml. */ `default_nettype none module tt_um_example ( input wire [7:0] ui_in, // DIP switches, INPUT Pmod, RP2350 GPIO17-24 output wire [7:0] uo_out, // 7-segment display, OUTPUT Pmod, RP2350 GPIO33-40 input wire [7:0] uio_in, // BIDIR Pmod, RP2350 GPIO25-32 output wire [7:0] uio_out, // (same pins) output wire [7:0] uio_oe, // 1 = your design drives the pin input wire ena, // set by the on-chip project mux, not a board pin input wire clk, // RP2350 GPIO16 input wire rst_n // RP2350 GPIO14 ); // The light should change about once a second. The demo board makes // clk itself and can set it anywhere from 1 Hz to 66.5 MHz, so set the // project clock to 1 Hz (clock_hz: 1 in info.yaml) and clk drives the // traffic light directly. Do not make a slow clock from a counter in an // ASIC: keep one clock and use a clock enable when you need a slow tick. wire [9:0] q; wire red, yellow, green; traffic_light i_traffic_light ( .clk ( clk ), .rst ( ~ rst_n ), .q ( q ), .red ( red ), .yellow ( yellow ), .green ( green ) ); // 7-segment display: uo_out [0] is segment a, ..., uo_out [6] is g, // uo_out [7] is the dot. Segment a (top) is red, g (middle) yellow, // d (bottom) green. assign uo_out = { 1'b0, yellow, 2'b0, green, 2'b0, red }; assign uio_out = 8'b0; assign uio_oe = 8'b0; // List all unused inputs to prevent warnings wire _unused = & { ena, ui_in, uio_in, q, 1'b0 }; endmodule
В info.yaml задайте такт 1 Гц, перечислите оба файла и укажите имя модуля как верхний
модуль:
project:
clock_hz: 1 # демонстрационная плата делает такт 1 Гц
top_module: "tt_um_example" # переименуйте в tt_um_<ваш логин на GitHub>_traffic_light
source_files:
- "project.v"
- "traffic_light.sv"
slow_clk счётчиком, но на
ПЛИС AMD и Intel выводит этот сигнал в тактовую сеть примитивом BUFG или global
(раздел 12); так поступают и в некоторых промышленных проектах на ПЛИС. В ASIC бит счётчика в
роли такта — плохая практика: это второй такт со своим тактовым деревом, его фронты отстают от фронтов
clk на задержку, которую никто не контролирует, о нём нужно отдельно сообщить средствам временного
анализа, а каждый сигнал между двумя тактами пересекает границу тактовых доменов. Здесь плата сама даёт
1 Гц, так что делитель не нужен. Если на кристалле нужен медленный шаг от быстрого такта, оставьте один такт и
добавьте разрешение такта (задача в разделе 15).15Что дальше
Задания для учащихся
- Другие длительности. Красный 5, жёлтый 1, зелёный 4: какие провода переставить? (Q4 добавляется к красному элементу, у которого уже четыре входа, так что понадобится ещё один элемент 4071.)
- Короткий круг. Подключите Q8 к RESET вместо кнопки. Теперь счётчик проходит S0–S7 и начинает сначала: какие теперь цвета? Почему 4017 на несколько наносекунд показывает S8?
- Длинный круг. CARRY OUT растёт раз в десять тактов. Подайте его на такт второго 4017 и сделайте светофор, у которого красный горит 40 тактов.
- Красный вместе с жёлтым, как в Германии или Великобритании: красный, красный + жёлтый, зелёный, жёлтый. Какие состояния идут на какой элемент и сколько нужно элементов на 4 входа?
- Пешеходный светофор со вторым модулем светофора: пешеходам зелёный, пока у машин красный.
- Разрешение такта. Добавьте в
traffic_light.svвходen, чтобы состояние менялось только на тех фронтах такта, гдеen= 1. Вproject.vзадайте такт проекта 10 МГц и сделайте счётчикомen— импульс длиной в один такт каждые 10 000 000 тактов. Светофор по-прежнему переключается раз в секунду, но весь кристалл работает от одного такта: так медленный шаг делают в ASIC. Что нужно поменять в тестбенче? - На SystemVerilog: напишите тот же автомат с состоянием типа
enumи операторомcaseили как унитарное кольцо из десяти триггеров и сравните, сколько ячеек даёт синтез для каждого. Тестбенч проверит любую версию с теми же портами.
Сокращение, которое прячет микросхема
Если регистр Джонсона виден, дешифратор и элементы ИЛИ сокращаются до трёх элементов на два входа: красный —
это состояния 0–3, то есть ровно ~ r [3] & ~ r [4]; жёлтый — r [3] & r [0];
зелёный — r [4] & ~ r [0]. Проверьте по таблице состояний из раздела 2. У 4017 так не выйдет:
биты счётчика Джонсона спрятаны внутри микросхемы. А в ПЛИС это бесплатная оптимизация, и средства синтеза
находят такое сами.
Похожие схемы
Светофор на 555 и 4017 собирают уже несколько десятилетий, чаще всего на диодах. Несколько вариантов для сравнения (по-английски):
- Traffic Light Project на сайте Electronics Club Джона Хьюза: 555 тактирует 4017, диоды объединяют выходы для жёлтого и зелёного, а красный идёт с CARRY OUT. Последовательность британская: красный, красный + жёлтый, зелёный, жёлтый.
- Traffic Lights For A Model Intersection, журнал Silicon Chip, май 1997: светофоры для двух улиц от одного 4017 с «диодной логикой».
- Студенческий контроллер светофора (около 2014 года) на CD4017, 4072 и 7432 — на элементах ИЛИ, как в этом упражнении.
Файлы упражнения
- traffic_light.sv: общая схема (раздел 8)
- cd4017.sv, cd4071.sv, cd4072.sv: модели микросхем (раздел 9)
- traffic_light_chips.sv: схема из микросхем (раздел 10)
- tb.sv и run_icarus.bash, run_verilator.bash, run_questa.bash, clean.bash (раздел 11)
- bgm_lab/lab_top.sv, bgm_hackathon/hackathon_top.sv, tiny_tapeout/project.v (разделы 12–14)
- Рисунки в SVG и PNG в каталоге images/: три схемы, цоколёвки, диаграмма
состояний и временные диаграммы; русские версии с окончанием
_ru
Источники
Схема Тимофея Никифорова описана в статье на Хабре о курсе электроники (занятие 1.7) и в «Teaching children electronics is due for a major redesign», рисунок 15. Микросхемы 4017, 4071 и 4072 описаны в даташитах Texas Instruments на CD4017B, CD4071B и CD4072B.