Первое впечатление от ПЛИС осталось неоднозначным, с одной стороны это интересные проекты и возможности в недалеком будущем, но это также и другой подход к решению проблем, следовательно нужно учиться абсолютно с нуля. Чтобы совсем не потеряться в поисках информации, будем последовательно решать задачи.

При изучении всякой новой технологии общепринято начинать с программ hello world. Некоторый уникумы хватаются сразу за серьезные проекты и бросают навсегда, из-за того что не осилить. Мы же этого делать не будем, медленно медленно спустимся и … 🙂

Я всегда начинаю с того, чтобы как то начать взаимодействовать с железякой — визуализировать информацию, в прошлый раз это было с помощью кнопки. Но согласитесь это не интересно, подобную схему можно реализовать имея резистор и кнопку, без всяких микроконтроллеров и ПЛИС. Кроме того особый интерес представляют всякого рода временные задержки, поэтому куда интереснее сделать светодиодную мигалку.

Задача — помигать светодиодом. Оказалось сделать это не так и просто, сформулировать решение просто — включил ножку, подождал, выключил (инвертировал), подождал. С включением и выключением проблем нет, можно просто инвертировать состояние например так a = !a;

Основной проблемой стали задержки. Как сделать так, чтобы логические элементы срабатывали не моментально, а с некой выдержкой. На борту у ПЛИС ничего вроде таймера нет, да и вообще ничего колеблющегося нет — только логика, поэтому очевидное решение — нужно как то использовать кварц на плате. Если у вас нет кварца, то ничего не выйдет, хотя есть мнение, что можно использовать внутренний генератор, но так глубоко я пока не копал.

Теперь представим ситуацию, как имея только логические элементы и 100МГц на борту, нужно сделать всего 0,1-1Гц чтобы помигать светодиодом. Решение в лоб нагуглилось легко — нужно поделить частоту до требуемых значений, возможно вам это известно, но все же попробуем разобраться.

Вначале нужно разобраться что такое D-триггер, взглянем на рисунок — имеется несколько пинов, основные которые нас интересуют D и Q, clk. Работает он так: чтобы мы не подавали на вход D, на выходе Q ничего не будет происходить пока на ножку clk не подаются импульсы.
trigger1

Но как только на ножку clk пойдет импульс, то состояние ножки D будет «скопировано» на ножку Q, т.е. если на входе была логическая единица, то и на выходе установится логическая единица, если ноль, то соответственно и на выходе 0. Обновляться состояние выхода будет с частотой подаваемой на ножку clk. Если ничего не подается, то на выходе будет сохраняться состояние, полученное с последним импульсом. Такой триггер называют еще Flip-flop.

Чем это удобно? Допустим, нужно управлять 24 светодиодами, для этого вам понадобится 24 ножки микроконтроллера. Вместо этого, юзаем три регистра у каждого 8 таких триггеров, т.е. 8 входов и 8 выходов. Так как на выходе данные не появятся пока не будет подан импульс, то все ножки данных можно объединить и управлять по очереди. Итого понадобится 8 ножек данных и 3 ножки для подачи импульсов на каждый из триггеров, т.е. 11 ножек вместо 24. Подобный подход заюзан в схеме светодиодного куба.

Как же это поможет в мигании светодиодом? Данные в D-триггерах обновляются один раз за импульс, либо по нарастанию сигнала, либо по спаду. Теперь небольшая извращенская хитрость у триггера имеется инверсный выход, который соединяется со входом.
trigger2

В первый момент включения, на входе D — 0, на выходе Q тоже ноль, следовательно на инверсном выходе появится логическая единица, ибо это «не» Q. Эта единица пойдет на вход D и при подаче тактового импульса, единица пойдет на выход Q, соответственно на инверсном выходе Q окажется ноль. В следующий такт, все снова поменяется местами. Таким образом, для одного периода на инверсном Q, потребуется два такта сигнала подаваемого на Clk. Вот таким образом и получается делитель частоты. Если не понятно — рекомендую собрать и проверить в том же протеусе или мультисиме.

trigger3

Пробуем реализовать делитель на Verilog. Приноровиться к новому языку оказалось довольно не просто. Имеется модуль sim1 — название программы, есть некий output led0 — выход, которым мы в последствии будем дрыгать, чтобы мигать светодиодом. Вход для тактовой частоты clk. Регистр counter, на манер языка си это переменная, куда будем писать данные.

module sim1
(
output led0,
input clk,
output reg counter = 1'b0
);
 
assign led0 = counter;
 
always @(posedge clk)
begin
   counter = !counter;
end
endmodule

Как работает эта программа? Регистр counter является однобитным, изначально запишем в него ноль output reg counter = 1’b0. Строкой assign led0 = counter; мы присваеваем значение регистра counter нашему светодиоду. Независимо от остальной программы, это условие будет всегда выполняться само по себе.

Теперь перейдем к процедурному блоку, он начинается строкой always @(posedge clk) читать эту строку можно так: всегда выполнять код от begin до end, при нарастающем фронте ножки clk. Внутри этого блока мы каждый период тактовой частоты инвертируем состояние регистра(переменной) counter. Конечно велик соблазн написать так:

begin
   led0 = !led0;
end

т.е. просто инвертировать ножку со светодиодом, но Verilog не дает так сделать, присвоение внутри процедурного блока можно делать только для регистров, проводам нельзя ничего присваивать.

Если скомпилировать данную программу и посмотреть Tools-Netlist Viewers — RTL Viewer, то угадайте что мы увидим? Да это же наш D триггер.
trigger4

Но теперь прикинем, это мы поделили 100МГц пополам, т.е. 50МГц — светодиод будет светиться постоянно на такой частоте. А если серьезно, то это же сколько делителей нужно, чтобы поделить 100 000 000 до 0,1Гц. Тут я задумался о каком нибудь симуляторе, ибо язык непривычный, чтобы проверить все догадки, придется не один раз прошить и есть шанс ничего так и не понять.

Первое что нагуглилось Modelsim Altera Starter Edition. Не знаю на сколько хорош этот симулятор, но мне он помог освоиться. По сути всю программу можно писать и отлаживать в нем, а уже готовое решение компилить в квартусе и прошивать. Можно их как то скрестить, но как то с ходу не получилось, потом буду разбираться. Сейчас просто забили на квартус — отлаживаемся в моделсиме.

Создаем новый проект: File-New Project. Указываем имя проекта, директорию и имя библиотеки (лучше оставить work).
trigger5

Дальше создаем новый файл, указываем его имя и задаем язык Verilog
trigger6

Пишем исходник, компилим и нажимаем Simulate — Start Simulation. Выбираем Library-Work, если в начале указывали work.
trigger7

Переключаемся на вкладку Wave внизу, должно быть как то так.
modelsim1

Перетаскиваем clk и counter, на Wave. Для clk указываем clock. Запускаем симуляцию Run или F9.
modelsim2

Все выполняется с указанным шагом. Работает как задумано, на каждые два такта тактовой частоты, приходится один период counter.
modelsim3

Теперь в наших руках мощное оружие отладки 🙂 пора бы уже допилить эту мигалку, первое о чем подумалось — сделать некую переменную, которая бы отсчитывала временные периоды. Но даже так, за одну секунду у нас 100 000 000 тиков, сложно представить себе размер переменной, которая бы хранила такие числа. Для справки: регистрам можно задавать их размерность, т.е. количество хранимых бит, например 8 битный регистр:
reg[7:0] counter = 0’b00000000

В общем я сделал так: запускаю первый 12 битный счетчик, он считает до 4096, инвертирует некий промежуточный бит on, таким образом on будет работать на частоте 100 000 000/ (4096*2) ~ 12кГц,
дальше прицепляюсь к нарастанию фронта on и делю его еще одним 12 битным счетчиком 12 207/(4096*2) получилось примерно 1,5Гц. В результате получился такой код:

module sim1
(
input wire key0,
output led0,
input clk
);
reg[12:0] counter = 1'b0;
reg on = 1'b0;
reg [12:0] counter2 = 1'b0;
reg on2 = 1'b0;
 
assign led0 = on2;
 
always @(posedge clk)
begin
   counter = counter + 1'b1;
   if(counter == 4095)
     begin
       on = !on;
    end
end
always @(posedge on)
begin
  counter2 = counter2 + 1'b1;
  if(counter2 == 4095)
    begin
      on2 = !on2;
      end
end
endmodule

Вполне очевидно, что можно это реализовать проще, например повесить нормальный часовой кварц и нормально поделить на степень двойки, но я ориентировался на то что было 🙂 По поводу кварца, в симуляции все работало замечательно, а на железке нет, дело в том что кварц посажен на ножку 75 и в Pin planner нельзя забывать указать это для clk, иначе работать не будет.

Получилась немного сумбурно, но главный восторг — все работает, я гарантирую.

Проект квартуса

13 комментариев: ModelSim, Verilog, Hello world

  • » Вместо этого,
    юзаем три триггера у каждого по 8
    входов и 8 выходов, так как на выходе
    защелки данные не появятся пока не
    будет подан импульс, то все ножки
    данных можно объединить и управлять
    по очереди.»
    Может имелось в виду — 3 регистра по 8 триггеров? 🙂 И защелкой, кажись, называеться D-триггер, работающий по уровню.
    Тип инпут и аутпут — разве это провода? Ими ж как-бы описываються входы и выходы модуля. Для проводов свой тип должен быть, аля node или как-то так. Или не? 🙂
    А вообще — статья классная 🙂 Знакомлюсь с верилогом. Еще один вариант упрощения кода — просто один длиинный-длинный счетчик-делитель частоты 🙂

  • К стати, 12-разрядный счетчик сам сбрасываеться на 4096 импульсе, По-этому вместо on можна попробовать использовать 12 разряд первого счетчика. По крайней мере — в AHDL можна было, может и тут можна 🙂

  • Пока еще сложно врубиться сходу в Verilog — выдержка с сайта марсохода «Модули имеют входы и выходы, которые ведут себя как сигналы wire», я интерпретировал что провода, это и есть входы/выходы. Возможно это не так, пока не понятно. С триггерами поправил 🙂 12 разряд простирается от 2 048 до 4095, так что его заюзать не получится

  • Согласен, нам на то, чтоб врубиться в AHDL на лабах тоже время достаточно ушло 🙂 Правда — нас основательно спасала книга, в которой он описан. Может и вам поискать какую-нибудь даже он-лайн книгу по верилогу? Может поможет 🙂
    А с счетчиком — согласен, затупил. Но тогда триггер on смело можно заменить 13 разрядом в counter 🙂 Счетчик досчитал до 4095 — 13й разряд установился, остальные — сбросились. Еще раз досчитал — 13й и все остальные — сбросились)
    Да, может это и необязательно, но можна указать, что D-триггер, у которого инверсный выход подключен на вход даных — это Т-триггер? 🙂

  • Насколько я понимаю у плисины тоже есть своя память, так вот что лучше заюзать 13 битную переменную или 12 битную + однобитную? Думаю второй вариант менее затратный. Опять же, я не вдавался в подробности, но вроде бы как в квартусе ограничение, он обрезает все переменные до 12бит. По поводу триггеров, я их в своей практике обычно не юзаю, буду потихоньку изучать — добавлю.
    ЗЫ: особого гемора с верилогом нет, просто скорее непривычно.

  • На счет того, что меньше расходует память — не знаю. Как я понимаю, там, дэ-факто, для каждого внутреннего проводника отдельная ячейка памяти, и как бы мы чего не описывали, мы просто будем замыкать те или другие «проводки», т.е. разници не будет никакой. Но точно не скажу, это только догадки 🙂
    Видел проект на верилоге с 27-разрядным регистром 🙂

  • Могу посоветовать более удобный и мощный симулятор — Active HDL, Он куда очевиднее ModelSim и позволяет делать больше интересных и полезных штук — рисовать FSM(конечные автоматы) и генерировать их них код, вычислять покрытие тестов и визуализировать эти данные, исполнять Tcl скрипты и ещё куча-куча всего. Ну и SystemVerilog конечно же, но он нужен для отладки очень больших проектов.

    Про непривычно да, вы верно отметили))) Я наоборот начинал с верилога, а потом пересел на микроконтроллеры и долго не мог осознать, что в Cи нету аналога always, Зато после того как свой таймер написал на верилоге стало как-то проще его прогать в готовой железке.

    А по поводу кода я бы сделал так. Добавил бы асинхронный сброс и привязал бы его к ножке (если это конечно возможно)
    module sim1
    (
    input wire key0,
    output led0,
    input clk
    );
    reg[12:0] counter = 1’b0;
    reg on = 1’b0;
    reg [12:0] counter2 = 1’b0;
    reg on2 = 1’b0;

    assign led0 = on2;

    always @(posedge clk щк negedge reset)
    begin
    if(!reset)

    counter = counter + 1’b1;
    if(counter == 4095)
    begin
    on = !on;
    end
    end
    always @(posedge on)
    begin
    counter2 = counter2 + 1’b1;
    if(counter2 == 4095)
    begin
    on2 = !on2;
    end
    end
    endmodule

  • Случайно отправил коммент раньше времени
    module sim1
    (
    input reset,
    input wire key0,
    output led0,
    input clk
    );
    reg[12:0] counter;
    reg on;
    reg [12:0] counter2;
    reg on2;

    assign led0 = on2;

    always @(posedge clk or negedge reset)
    if(!reset)
    begin
    counter<=12'h0;
    on<=1'b0;
    else
    begin
    if(counter == 4095)
    on <= !on;
    else
    counter <= counter + 1′b1;

    end

    always @(posedge on or negedge reset)
    if(!reset)
    begin
    counter<=12'h0;
    on2<=1'b0;
    end
    else
    if(counter2 == 4095)
    on2 <= !on2;
    else
    counter2 <= counter2 + 1′b1;
    endmodule

    <= — это неблокирующее присваивание. От блокирующего оно отличается тем, что выполныется не сразу по коду, а только после окончания всего always блока. Для синхронных схем правильнее использовать неблокирующее присваивание, чем блокирующее потому что синтезатор правильнее интерпретирует его и использует D-триггеры, а не что-то ещё. Не знаю как в ПЛИС, при разработке ASIC это важно. Рискну предположить что большой проект может просто не поместиться в кристалл, если синтезатор напихает туда не то, что там должно быть. Добавление сброса это скорее для успокоения души — опять же с точки зрения синтезатора это важно. И вам спокойнее — сбросили схему один раз, она инициализировалась как надо и всё хорошо.

  • Константин, спасибо, на выходных гляну.

  • Константин on 21.05.2014.
    Попробовал код в xilinx не проходит …. Точно в квартусе проходит ? :))

  • Дмитрий on 01.07.2014 в 23:57

    Если добавить пару begin-end-ов, которые я по глупости забыл то всё работает=)

  • подскажите а как задавать определённые входные сигналы на входы а не только клок в modelsim

  • сейчас не вспомню, ибо моделсим не стоит, но там было очень просто, в определенный момент времени на диаграмме просто выбираешь ноль или единица.

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

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

Последние комментарии
  • Загрузка...
Счетчик
Яндекс.Метрика