fpga-systems-magazine

Сага о светодиодах

Главная » Статьи » Разное » Общее
sergebalakshiy
12.05.2022 16:36
3300
0
5.0

Оглавление

*О найденных опечатках и замечаниях просим сообщить admin@fpga-systems.ru

Введение

Просто я описываю свой путь, свои исследования и свои заметки по дороге FPGA. И, конечно в начале будет светодиод. Потом я опишу свою работу с кнопками. А далее... Посмотрим, что будет далее.
Практически все эксперименты будут проводиться на плате Lichee TANG Premier RISC-V. На этой плате установлен чип EG4S20BG256. Это и есть FPGA. Помогать мне будут Verilog, GTKWAVE и IDE TD (Tang Dinasty). Эти инструменты легко можно найти в интернете. Кроме того, иногда я буду использовать Quartus.

1. Зажигаем светодиод

Это магическое действие производит практически каждый, начинающий свой путь в FPGA. Я не являюсь исключениием из этого правила и для меня следующая мантра “да будет светодиод” является такой же магической и священной как и мантра “Hello, World!”. И не отходя от традиции я привожу схему подключения светодиода, рис.1 и модуль зажигания, листинг 1. Схему я начертил сам, когда разбирался с распиновкой этой платы. Подключение платы к компьютеру было выполнено штатным кабелем через USB интерфейс.

Модуль зажигания я взял из примера Tang_FPGA_Examples/0.LED. Этот пример, а также некоторые другие примеры для работы с этой платой доступны на github. Данный пример был открыт как проект в TD. Для этого предварительно надо скачать эту IDE и файл лицензии к ней. В примере, в файле README указана ссылка для скачивания. Если у вас linux, то скачивать надо соответствующий файл. Но надо заметить что у меня заработало только то, что находилось в файле TD_RELEASE_MAY2019_r4.5.12562_RHEL.rar. Надо распаковать этот архив, в папке bin будет находится IDE. Команда ./td -gui запустит IDE в графическом режиме. Чтобы открыть проект необходимо в папке prj указать на файл с расширением al. Впрочем, если вы предпочитаете консоль, то запустите просто ./td и в открывшейся консоли команда help выдаст вам список практически всего меню, что есть в этой IDE. Наслаждайтесь.

В проекте есть файл io.adc, отвечающий за назначение контактов. Этот файл находится в папке constraint и в нем располагаются следующие данные.

Рис. 1. Электрическая схема

Листинг 1. Назначение контактов.

set_pin_assignment {CLK_IN} { LOCATION = K14; } ##24MHZ
set_pin_assignment {RST_N} { LOCATION = K16; } ##USER_KEY
## RGB LEDs, 3 pins
set_pin_assignment {RGB_LED[0]} { LOCATION = R3; } ##LED_R, R3
set_pin_assignment {RGB_LED[1]} { LOCATION = J14; } ##LED_G, J14
set_pin_assignment {RGB_LED[2]} { LOCATION = P13; } ##LED_B, P13

Поле LOCATION для светодиодов RGB_LED[2:0] содержит номера пинов к которым подключены светодиоды согласно схемы на рис.1. Надо сказать, что на самой плате уже есть три светодиода подключенные к этим пинам. Вернее это одна сборка из трех светодиодов.

Компиляция прошла без ошибок. В результате был получен файл led.bit и этот файл был загружен в плату. После чего светодиод заморгал.

Листинг 2. Управление светодиодом

module led
(
    input wire CLK_IN,
    input wire RST_N,
    output wire [2:0]RGB_LED
);
parameter time1 = 25'd24_000_000; //Генератор 24Mhz
reg [2:0] rledout;
reg [24:0] count;
reg [1:0] shift_cnt;
initial begin
    count=25'b0;
    rledout=3'b1;
    shift_cnt=2'b0;
end 
always @(posedge CLK_IN)begin
    if(RST_N==0)begin
        count <= 25'b0;
        rledout <= 3'b1;
        shift_cnt <=2'b0;
    end 
    if(count == time1) begin
        count<= 25'd0; 
        if(shift_cnt==2'b10)begin
            rledout <= 3'b1; 
            shift_cnt <=2'b0;
        end
        else begin
            rledout <= {rledout[1:0],1'b0};
            shift_cnt <= shift_cnt + 1'b1;
        end
    end
    else
        count <= count + 1'b1;
    end 
assign RGB_LED = rledout;
endmodule

Немного пройдемся по строкам этого священного писания. В строке 1 задано имя модуля. В строках 3...5 определены имена входных и выходных портов.

Далее определены три регистра.

  • rledout - три триггера для хранения состояний светодиодов, строка 10;
  • count - 24-х разрядный счетчик, строка 11;
  • shift_cnt сдвиговый регист на 2 разряда, строка 12.

Блок initial (строки 14...19) выполняется один раз и задает начальные значения указанных регистров.

Далее выполняется блок always. Он выполняется каждый раз при положительном фронте тактового сигнала CLK_IN все оставшееся время и в данном случае его основное предназначение это включать и выключать светодиоды в соответствии с заданной в нем логикой.

Всю схему я здесь приводить не буду. Рассмотрим некоторые её части. На рисунке 2 представлен регистр состояний светодиодов. Это просто 3 D триггера.

Рис. 2. Регистр состояний светодиодов

И эта часть схемы соответствует строке 10 в описании модуля. Счетчик count представлен на рисунке 3 Видно, что он состоит из 25-ти D триггеров, у него есть сумматор и мультиплексор для установки начального значения.

Рис. 3. Счетчик

Также и счетчик shift_cnt, рис. 4, реализован на 2-х D триггерах. Кроме того, у него есть схема сравнения соответствующая строке 32 в тексте модуля.

Рис. 4. Счетчик shift_cnt

Можно заметить, что этот модуль зажигания состоит из трех частей. Обозначим эти части следующим образом.

  • делитель тактовой частоты, он же счетчик count, который вырабатывает сигнал для сдвигового регистра при совпадении счетчика с установленным заранее значением time1;
  • сдвиговый регистр shift_cnt;
  • драйвер светодиодов led_drv.

Это можно изобразить так, как на рисунке.

Рис. 5. Функциональная схема модуля верхнего уровня.

Теперь разделим первоначальный текст из листинга 2 на три модуля и потом соберём из этих частей ту же схему.

2. Модуль count

Этот модуль будет выполнять функцию делителя частоты. Коэффициент деления задаётся при помощи параметра TIME1 строка 1.

Листинг 3. Делитель частоты

module count #(parameter TIME1 = 25'd24_000_000) //
(
    input wire CLK,
    input wire RST_N,
    output wire STB
);
reg [24:0] count;
reg strb;
always @(posedge CLK)begin
    if(RST_N==1)begin
        count=25'b0;
        strb=1'b0;
    end
    if(count == TIME1)begin
        count <= 25'd0;
        strb <= ~strb;
    end
    else begin
        count <= count + 1'b1;
    end
end
assign STB = strb;
endmodule // count

3. Модуль shift_cnt

Листинг 4. Сдвиговый регистр

module shift_cnt (
    input wire CLK,
    input wire RST_N,
    output wire [1:0] CNT_N
);
reg [1:0] shift_cnt;
always @(posedge CLK) begin
    if(RST_N==1)begin
        shift_cnt <= 2'b0;
    end
    else begin
        if(shift_cnt<2'b10)begin
            shift_cnt <= shift_cnt + 1'b1;
        end
        else begin
            shift_cnt <= 2'b0;
        end
    end
end // always @ (posedge CLK_IN)
assign CNT_N = shift_cnt;
endmodule // shift_cnt

Этот модуль принимает на вход тактовый сигнал и фактически он отсчитывает количество светодиодов, выдавая на выход номер светодиода, который будет зажжен.

4. Модуль led_drv

Листинг 5. Драйвер светодиодов

module led_drv
(
    input wire CLK_IN,
    input wire [1:0] CNT_N,
    input wire RST_N,
    output wire [2:0] RGB_LED
);
reg [2:0] rledout;
always @(posedge CLK_IN)begin
    if(RST_N==1)begin
        rledout <= 3'b1;
    end
    if(CNT_N==2'b10)begin
        rledout <= 3'b001;
    end
    else begin
        rledout <= {rledout[1:0], 1'b0};
    end
end
assign RGB_LED = rledout;
endmodule

Этот модуль принимает номер светодиода от предыдущего модуля и последовательно включает один из светодиодов.

5. Общая схема

В следующем листинге описана сборка схемы из разработанных выше модулей.

Листинг 6. Общая схема

module top
(
    input wire CLK_IN,
    input wire RST_N,
    output wire [2:0] RGB_LED
);
wire strb;
wire [1:0] shift;
count #(.TIME1(25'd32)) i_count(
    .CLK(CLK_IN),
    .RST_N(RST_N),
    .STB(strb)
);
//Присоединим модуль shift_cnt
shift_cnt i_shift_cnt (
    .CLK(strb),
    .RST_N(RST_N),
    .CNT_N(shift)
);
//Присоединим модуль led_drv
led_drv i_led_drv(
    .CLK_IN(strb),
    .RST_N(RST_N),
    .CNT_N(shift),
    .RGB_LED(RGB_LED)
);
endmodule

В железе эта схема теперь выглядит так. Эта схема вполне соответствует функциональной схеме приведенной на рисунке 5. Соответствие прямое. Понять и проанализировать работу этой схемы гораздо проще, чем эквивалентную по функционалу схему приведённую в листинге 2. Посмотрим как реализованы каждый из этих модулей.

Рис. 6. Электрическая схема

Модуль count, делитель опорной частоты.

Этот модуль можно теперь легко выделить из общей схемы. Здесь видно соответствие элементов схемы с элементами описания в исходном файле. В этом можно убедиться просто просматривая схему и читая строки в исходном файле.

Рис. 7. Делитель частоты.

Модуль shift_cnt, переключатель светодиодов.

Такое же соответствие можно увидеть и в переключателе светодиодов. Схема этого переключателя довольно простая. Так как светодиодов всего три, основу этой схемы составляет 2-х разрядный счетчик, который переходит в различные состояния в соответствии с логикой, представленной в листинге 18. По сигналу RST регистр может быть установлен в 0. В рабочем режиме это обычный счетчик, который отсчитывает количество светодиодов и по достижении полного количества светодиодов сбрасывается в 0 и начинает отсчет сначала. Так задается номер того светодиода, который будет зажжен.

Рис. 8. Переключатель светодиодов.

Модуль led_drv, подключения светодиодов.

Эта схема принимает последовательность номеров светодиодов и, обеспечивает последовательное переключение светодиодов так, что загорается всегда последовательно только один светодиод.

Рис. 9. Подключение светодиодов.

6. Стенд для испытаний полученной схемы

Теперь разработаем стенд, на котором можно будет прогнать схему и посмотреть как она будет работать. Понадобятся пара сигналов, которые будут подаваться на вход модуля.

Листинг 7. Входные сигналы

module top_led_tb
    reg CLK;
    reg RST;
endmodule // top_led_tb

Далее добавим провода для подключения светодиодов.

Листинг 8. Провода для подключения светодиодов

module top_led_tb
    ...
    wire [2:0] RGB_LED;

endmodule // top_led_tb

Теперь подключим модуль верхнего уровня. Другими словами создадим экземпляр схемы.

Листинг 9. Создаем экземпляр модуля

module top_led_tb
    ...
    top i_top(
        .CLK_IN(CLK),
        .RST_N(RST),
        .RGB_LED(RGB_LED)
    );

endmodule // top_led_tb   

Для оживления схемы зададим работу генератора тактовых сигналов.

Листинг 10. Генератор тактового сигнала

module top_led_tb
    ...
    always #10 CLK = ~CLK;

endmodule // top_led_tb    

Тоже самое можно сделать более академически.

Листинг 11. Еще один генератор тактового сигнала

module top_led_tb
    ...
    initial begin
        CLK = 0;
        forever #10 CLK = ~CLK;
    end

endmodule // top_led_tb  

Установим сигналы в 0.

Листинг 12. Начальная установка

module top_led_tb
    ...
    initial begin
        CLK = 0;
        RST = 0;

endmodule // top_led_tb   

Сделаем сброс, подняв сигнал RST в 1 и через #10 единиц времени снова опустим его в 0.

Листинг 13. Выполняем сигнал сброс

module top_led_tb
    ...
    initial begin
    ...
        #50 RST = 1;
        #10 RST = 0
        #50

endmodule // top_led_tb    

Запустим схему в работу.

Листинг 14. Запуск схемы в работу

module top_led_tb
    ...
    initial begin
    ...
        @(posedge CLK)
        #0;
        begin
            $monitor(RGB_LED);
        end
    end

endmodule // top_led_tb    

Некоторое время дадим поработать и выключим.

Листинг 15. Поработаем и выключаем

module top_led_tb
    ...
    initial begin
        #600000 $finish;
    end

endmodule // top_led_tb    

Создадим файл VCD для последующего анализа.

Листинг 16. Создание VCD файла

module top_led_tb
    ...
    initial begin
        $dumpfile(out.vcd);
        $dumpvars(0, top_led_tb);
    end

endmodule // top_led_tb    

Наблюдаем за некоторыми сигналами.

Листинг 17. Монитор сигналов

module top_led_tb
    ...
    initial
        $monitor(RST, RGB_LED);

endmodule // top_led_tb   

И в целом весь стенд выглядит так.

Листинг 18. Схема стенда

`timescale 1ns / 1ps
module top_led_tb();
    reg CLK;
    reg RST;
    wire [2:0] RGB_LED;

    top i_top(
        .CLK_IN(CLK),
        .RST_N(RST),
        .RGB_LED(RGB_LED)
    );

    always
        #10 CLK = ~CLK;

    //Stimulus process
    initial begin
        CLK = 0;
        RST = 0;
        #50 RST = 1;
        #6 RST = 0;
        #50;
        @(posedge CLK)
        #0;
        begin
            $monitor(RGB_LED);
        end
    end
    
    initial begin
        #600000 $finish;
    end

    initial begin
        $dumpfile("out.vcd");
        $dumpvars(0,top_led_tb);
    end

    //наблюдаем на некоторыми сигналами системы
    initial
        $monitor(RST, CLK, RGB_LED);

endmodule

Запускаем стенд. После чего наблюдаем следующие эпюры напряжений на входах и выходах сгенерированной схемы.

Листинг 19. Заклинания для запуска стенда

iverilog shift_cnt.v count.v led_drv.v top.v top_led_tb.v
vvp a.out
gtkwave out.vcd

Рис. 10. Эпюры напряжений на входах и выходах схемы.

7. Параметры

Ссылка; IEEE Std 1364-2005 IEEE STANDARD FOR VERILOG. Описание стандарта Verilog. При анализе листингов можно заметить, что некоторые элементы схемы могут меняться в зависимости от решаемых задач. Например может измениться количество светодиодов в модуле драйвера светодиодов и тогда надо будет изменить количество регистров для подключаемых светодиодов, а также номер последнего светодиода. Также, если изменить количество светодиодов, то придется изменить и разрядность счетчика светодиодов в модуле переключателя светодиодов. Для того чтобы сделать это правильно и без ошибок, можно воспользоваться механизмом определения параметров модуля, который реализован в Verilog и позволяет создавать параметризированные проекты, переопределяя значения параметров во время, когда создается экземпляр схемы.

Немного о параметрах

В языке Verilog cуществует два различных способа обьявления параметров. Первый — это в виде списка параметров модуля, второй — как пункты модуля (с-подобный синтаксис). Объявление модуля может содержать определения параметров одного или обоих типов или может не содержать определений параметров. При обьявлении параметров им сразу присваиваются значения и обьявленные параметры используются где-то внутри модуля.

Первый способ выглядит так. И это упрощенный пример синтаксиса. Полное описание синтаксиса надо смотреть в стандарте.

Листинг 20. Упрощенный синтаксис определения параметров в виде списка параметров модуля

module module_name
#(parameter name1=value, name2=value,...)
(terminal_list);
    ...
endmodule

В следующем примере показано определение трех параметров модуля и их использование в самом модуле. Этот пример и следующие примеры взяты из стандарта IEEE Std 1364-2005.

Листинг 21. Пример определения списка параметров и их использования в модуле

module generic_fifo #(parameter MSB=3, LSB=0, DEPTH=4)
//Эти параметры могут быть переопределены
(
    input [MSB:LSB] in,
    input clk, read, write, reset,
    output [MSB:LSB] out,
    output full, empty 
);

localparam FIFO_MSB = DEPTH*MSB;
localparam FIFO_LSB = LSB;
//Эти параметры являются локальными
//и не могут быть переопределены.
//На них можно повлиять, изменив
//общедоступные параметры выше,
//и модуль будет работать правильно.

reg [FIFO_MSB:FIFO_LSB] fifo;
reg [LOG2(DEPTH):0] depth;
always @(posedge clk or reset) begin
    casex ({read,write,reset})
        // реализация
    endcase
end
endmodule

Пример определения параметров вторым способом приведен в следующем листинге. Полное описание синтаксиса надо смотреть в стандарте.

Листинг 22. Параметры как пункты модуля

module vdff (out, in, clk);
    parameter size=5, delay=1;
    output [size-1:0] out;
    input [size-1:0] in;
    input clk;
    reg [size-1:0] out;
    always @(posedge clk)
        #delay out = in;
endmodule

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

Листинг 23. Пример определения экземпляра модуля с использованием синтаксиса с именоваными параметрами.

module tb2;
    wire [9:0] out_a, out_d;
    wire [4:0] out_b, out_c;
    reg [9:0] in_a, in_d;
    reg [4:0] in_b, in_c;
    reg clk;
    // testbench clock & stimulus generation code ...
    // Four instances of vdff with parameter value assignment by name
    // mod_a has new parameter values size=10 and delay=15
    // mod_b has default parameters (size=5, delay=1)
    // mod_c has one default size=5 and one new delay=12
    // mod_d has a new parameter value size=10.
    // delay retains its default value
    vdff #(.size(10),.delay(15)) mod_a (.out(out_a),.in(in_a),.clk(clk));
    vdff mod_b (.out(out_b),.in(in_b),.clk(clk));
    vdff #(.delay(12)) mod_c (.out(out_c),.in(in_c),.clk(clk));
    vdff #(.delay( ),.size(10) ) mod_d (.out(out_d),.in(in_d),.clk(clk));
endmodule

Здесь показано создание экземпляров модуля vdff и определение новых значений для параметров delay и size для каждого нового экземпляра. Причем, не важно в каком порядке мы указываем имена этих параметров. 

В следующем примере показан позиционный способ определения значений параметров при создании эекземпляров того же модуля vdff.

Листинг 24. Пример определения экземпляра модуля с использованием синтаксиса с именоваными параметрами.

module tb1;
    wire [9:0] out_a, out_d;
    wire [4:0] out_b, out_c;
    reg [9:0] in_a, in_d;
    reg [4:0] in_b, in_c;
    reg clk;
    // testbench clock & stimulus generation code ...
    // Four instances of vdff with parameter value assignment
    // by ordered list
    // mod_a has new parameter values size=10 and delay=15
    // mod_b has default parameters (size=5, delay=1)
    // mod_c has one default size=5 and one new delay=12
    // In order to change the value of delay,
    // it is necessary to specify the (default) value of size as well.
    // mod_d has a new parameter value size=10.
    // delay retains its default value
    vdff #(10,15) mod_a (.out(out_a), .in(in_a), .clk(clk));
    vdff mod_b (.out(out_b), .in(in_b), .clk(clk));
    vdff #( 5,12) mod_c (.out(out_c), .in(in_c), .clk(clk));
    vdff #(10) mod_d (.out(out_d), .in(in_d), .clk(clk));
endmodule

8. Параметрический сахар

Подсластим наш проект параметрическим сахаром.

Модуль count

В модуле count изначально использовался параметр TIME1, смотри листинг 3. Этот параметр введен для того, чтобы иметь возможность задавать управляемые отсчеты для модуля shift_cnt. Сигнал STB на выходе модуля count формируется путем деления опорной частоты на значение параметра TIME1 и имеет длительность такта равную количеству тактов опорной частоты заданной параметром TIME1.

Модуль shift_cnt

В этом модуле необходимо произвести некоторые изменения. Здесь есть счетчик и шина данных, которая подключается к этому счетчику. Разрядность этой шины и счетчика должны быть согласованы между собой. Для правильной настройки разрядности введем параметр SIZE - количество необходимых разрядов. Значение по умолчанию для этого параметра равно 2, этого достаточно для управления 3-мя светодиодами. Локальный параметр NULL введен просто для нагладности.

Для управления работой счетчика необходимо знать сколько делать отсчетов, чтобы правильно переключать светодиоды. Для этого введем параметр LED_CNT - значение этого параметра равно количеству светодиодов в схеме минус 1, так как начало отсчета с 0. Значение этого параметра не должно превышать емкость счетчика. В следующем листинге представлен код модуля shift_cnt с параметрами.

Листинг 25. Сдвиговый регистр с параметрами

module shift_cnt #(parameter SIZE=2, LED_CNT=2)
(
    input wire CLK,
    input wire RST_N,
    output wire [SIZE-1:0] CNT_N
);
    localparam NULL = 'b0;
    reg [SIZE-1:0] shift_cnt;

    always @(posedge CLK) begin
        if(RST_N==1)begin
            shift_cnt <= NULL;
        end
        else begin
            if(shift_cnt<LED_CNT)begin
                shift_cnt <= shift_cnt + 1'b1;
            end
            else begin
                shift_cnt <= NULL;
            end
        end
    end // always @ (posedge CLK_IN)
    assign CNT_N = shift_cnt;
endmodule // shift_cnt

9. Модуль led_drv

В этом модуле также есть счетчик, регистр и шина данных, разрядность которых зависит от количества обслуживаемых светодиодов. Для настройки этих элементов введем следующие параметры.

  • Параметр SIZE устанавливает разрядность шины данных счетчика CNT_N.
  • Параметр END_LED задает условие для переключения счетчика в начальное состояние, а также количество разрядов в регистре rledout и в шине RGB_LED.
module led_drv #(
    parameter SIZE = 2, END_LED = 2'b10
)
(
    input wire CLK_IN,
    input wire [SIZE-1:0] CNT_N,
    input wire RST_N,
    output wire [END_LED:0] RGB_LED
);

    reg [END_LED:0] rledout;

    always @(posedge CLK_IN)begin
        if(RST_N==1)begin
            rledout <= 0;
        end
        if(CNT_N==END_LED)begin
            rledout <= 1;
        end
        else begin
            rledout <= {rledout[END_LED-1:0], 1'b0};
        end
    end
    assign RGB_LED = rledout;
endmodule

10. Модуль top

Модуль top служит для сборки всего устройства. В нем должны быть параметры, которые управляют настройками подключаемых модулей так, чтобы их работа была согласована в соответствии с количеством подключенных светодиодов к схеме.

  • Параметру SIZE назначается количество разрядов для счетчиков.
  • Параметру CLK_DIV назначается значение коэффициента деления тактовой частоты, чтобы управлять скоростью мигания светодиодов.
  • Параметру LED_NUM назначается количество светодиодов в схеме.
  • Локальный параметр LED_CNT задает количество отсчетов, которые должен сделать счетчик в модуле shift_cnt, см. описание модуля shift_cnt в листинге 25.
  • Локальный параметр END_LED передает в модуль led_drv число соответствующее номеру последнего светодиода при счете светодиодов от 0, см. описание модуля led_drv в листинге 26.

Локальные параметры LED_CNT и END_LED введены для удобства и для наглядности.

Листинг 27. Модуль top

module top #(
    parameter SIZE=2, CLK_DIV=25'd32, LED_NUM=3
)
(
    input wire CLK_IN,
    input wire RST_N,
    output wire [LED_NUM-1:0] RGB_LED
);
    //--------------------------------
    // shift_cnt
    localparam LED_CNT=LED_NUM-1;
    //--------------------------------
    // led_drv
    localparam END_LED = LED_NUM-1;
    //--------------------------------
    wire [SIZE-1:0] shift;
    wire strb;
    //--------------------------------
    //Настраиваем модуль count
    count #(.TIME1(CLK_DIV))
    i_count(
        .CLK(CLK_IN),
        .RST_N(RST_N),
        .STB(strb)
    );
    //Присоединим модуль shift_cnt
    shift_cnt #(.LED_CNT(LED_CNT),
        .SIZE(SIZE)
    )
    i_shift_cnt (
        .CLK(strb),
        .RST_N(RST_N),
        .CNT_N(shift)
    );
    //Присоединим модуль led_drv
    led_drv #(
        .SIZE(SIZE),
        .END_LED(END_LED)
        )
    i_led_drv(
        .CLK_IN(strb),
        .RST_N(RST_N),
        .CNT_N(shift),
        .RGB_LED(RGB_LED)
    );
endmodule

11. Стенд с параметрами

Листинг стенда с параметрами представлен ниже. Параметры, расположенные в начале модуля стенда предназначены для конфигурирования устройства. Рассмотрим эти параметры:

  • Параметр CLK_DIV. Это уже знакомый параметр, который задает коэффициент деления опорной частоты и формирования сигнала STB. Меняя значение этого параметра можно изменять скорость переключения светодиодов.
  • Параметр DEPTH задает ёмкость или мощность счетчика. Используя это значение можно вычислить количество разрядов для шин, регистров и счетчиков, используемых в этом устройстве. Это вычисленное значение присваивается параметру SIZE.
  • Параметр LED_NUM содержит количество светодиодов, которые будет поддерживать сгенерированная схема
  • Параметр SIZE определяет разрядность счетчиков в модулях shift_cnt и led_drv. Значение этого параметра определяется как двоичный логарифм от мощности счетчика DEPTH.

Значения этих параметров передеются в модуль top при создании экземпляра устройства.

Листинг 28. Стенд с параметрами

`timescale 1ns / 1ps
module top_led_tb();
    parameter CLK_DIV=25'd64;
    parameter DEPTH=16;
    parameter LED_NUM=10;
    parameter SIZE=$clog2(DEPTH);

    reg CLK;
    reg RST;
    wire [LED_NUM-1:0] RGB_LED;

    top #(
        .CLK_DIV(CLK_DIV),
        .SIZE(SIZE),
        .LED_NUM(LED_NUM)
    )
    i_top(
        .CLK_IN(CLK),
        .RST_N(RST),
        .RGB_LED(RGB_LED)
    );

    always
        #10 CLK = ~CLK;

    //Stimulus process
    initial begin
        CLK = 0;
        RST = 0;
        #50 RST = 1;
        #6 RST = 0;
        #50;
        @(posedge CLK)
        #0;
        begin
            $monitor(RGB_LED);
        end
    end

    initial begin
        #600000 $finish;
    end

    initial begin
        $dumpfile("out.vcd");
        $dumpvars(0,top_led_tb);
    end
                    
    //наблюдаем на некоторыми сигналами системы
    initial 
        $monitor(RST, CLK, RGB_LED);

endmodule

12. Запускаем стенд

При установленных параметрах как на листинге стенда схема будет работать следующим образом.

Рис. 11. Работа схемы с 10 светодиодами

Что дальше?

Далее отделим драйвер светодиодов от схемы управления и свяжем эти две части посредством интерфейса I2C и в другом варианте будем использовать интерфейс SPI.

Потом предположим, что эта схема будет управлять гирляндами светодиодов. В таком случае заменим устройство управления на конечный автомат, который будет создавать различные эффекты на этих гирляндах.

Поигравшись с конечным автоматом, придем к выводу, что неплохо было бы создать устройство управления, которое будет работать используя простые скрипты.

 

      Мотивировать автора     

     Поддержать FPGA комьюнити     

Оставить комментарий/отзыв

Статья будет доступна в формате PDF чуть позже

3300
0
5.0

Всего комментариев : 0
avatar

FPGA-Systems – это живое, постоянно обновляемое и растущее сообщество.
Хочешь быть в курсе всех новостей и актуальных событий в области?
Подпишись на рассылку

ePN