Рисунок 2.2 — Интерфейс модуля верхнего уровня
Рисунок 2.3 — RTL-модель верхнего уровня иерархии
Помимо этого, требовалось построить граф переходов автомата для прямой и обратной генерации. С его помощью можно нагляднее рассмотреть и оценить ход работы автомата. Граф переходов представлен на Рисунке 2.4.
Рисунок 2.4 — Граф переходов автомата
2.3 Содержание модулей
Во-первых, нужно было разработать сам модуль конечного автомата. Переход состояний был реализован через оператор “case”, который меняет состояния автомата в зависимости от значения переменной “cnt”. Эта переменная меняется в зависимости от сигнала “UP”, если он равен 1, то “cnt” инкрементируется, в противном случае — декрементируется. Помимо этого, был добавлен сброс “cnt” при положительном “RST”. Также, реализована загрузка состояния из “DAT_I” в “cnt”. Код модуля конечного автомата представлен на Листинге 2.1.
Листинг 2.1 — Модуль конечного автомата (MAIN_MODULE.v)
module MAIN_MODULE(
input CLK,
input RST,
input CE,
input LOAD,
input [3:0] DAT_I,
input UP,
output reg [3:0] SEQ
); reg [3:0] cnt; always@(cnt)
case(cnt)
4'h0: SEQ <= 4'hD;
4'h1: SEQ <= 4'hC;
4'h2: SEQ <= 4'h8;
4'h3: SEQ <= 4'hD; 4'h4: SEQ <= 4'h4;
4'h5: SEQ <= 4'h9;
4'h6: SEQ <= 4'hA;
4'h7: SEQ <= 4'hE;
4'h8: SEQ <= 4'hF;
4'h9: SEQ <= 4'hD;
4'hA: SEQ <= 4'h2;
4'hB: SEQ <= 4'hD;
4'hC: SEQ <= 4'hA;
4'hD: SEQ <= 4'hE;
4'hE: SEQ <= 4'hE;
4'hF: SEQ <= 4'hA; default: SEQ <= 4'h0;
endcase always@(posedge CLK or posedge RST)
if (RST)
cnt <= 4'h0;
else if (LOAD)
cnt <= DAT_I;
else if (CE)
if (UP)
cnt<=(cnt + 1);
else
cnt <= (cnt - 1);
else SEQ <= SEQ;
endmodule
Помимо модуля конечного автомата, требовалось разработать модули фильтра дребезга контактов и делителя частоты. Дребезг контактов — явление, происходящее в электромеханических коммутационных устройствах и аппаратах (кнопках, реле, герконах, переключателях, контакторах, магнитных пускателях и др.), длящееся некоторое время после замыкания электрических контактов. После замыкания происходят многократные неконтролируемые замыкания и размыкания контактов за счёт упругости материалов и деталей контактной системы — некоторое время контакты отскакивают друг от друга при соударениях, размыкая и замыкая электрическую цепь. То есть, во время нажатия на кнопку, система будет фиксировать огромное количество нажатий.
Фильтр дребезга контактов используется для устранения такого неприятного эффекта. Суть заключается в том, что есть внутренний счётчик, который отсчитывает число. Это число должно быть больше, чем время случайного соприкосновения контактов, но меньше времени, которое контакты соприкасаются при нажатии человеком. Тогда, если счётчик досчитал до этого числа, то кнопка действительно нажата и следует подать соответствующий сигнал.
Помимо этого, для корректной работы светодиодов нужен делитель частоты. Тактовый генератор на отладочной плате имеет частоту 100 МГц, что слишком много. Подобрать частоту мерцания светодиодов нужно так, чтобы глаз различал только постоянное горение светодиода. Оптимальным значением будет 1кГц.
Данные модули были разработаны и реализованы. Код модулей представлен в листингах 2.2 и 2.3 соответственно.
Листинг 2.2 — Модуль фильтра дребезга контактов (BTN_FILTER_CE.v)
module BTN_FILTER_CE(
input CLK,
input CE,
input BTN_IN,
input RST,
output reg BTN_CEO
); parameter [3:0] CNTR_WIDTH = 4; // Internal Counter Width
// Internal signals declaration:
reg [CNTR_WIDTH - 1:0] FLTR_CNT;
reg BTN_D, BTN_S1, BTN_S2;
//-----------------------------------------
// Main Counter:
always @ (posedge CLK, posedge RST)
if(RST) FLTR_CNT <= {CNTR_WIDTH{1'b0}};
else
if(!(BTN_S1 ^ BTN_S2)) // if BTN_S1 = BTN_S2
FLTR_CNT <= {CNTR_WIDTH{1'b0}}; // Return to Zero
else if(CE) // else if Clock Enable
FLTR_CNT <= FLTR_CNT + 1; // Increment
//-----------------------------------------
// Input Synchronizer:
always @ (posedge CLK, posedge RST)
if(RST) Продолжение Листинга 2.2
begin
BTN_D <= 1'b0;
BTN_S1 <= 1'b0;
end
else
begin
BTN_D <= BTN_IN;
BTN_S1 <= BTN_D;
end
//-----------------------------------------
// Output Register:
always @ (posedge CLK, posedge RST)
if(RST) BTN_S2 <= 1'b0;
else if(&(FLTR_CNT) & CE) BTN_S2 <= BTN_S1;
//-----------------------------------------
// Output Front Detector Clock Enable:
always @ (posedge CLK, posedge RST)
if(RST) BTN_CEO <= 1'b0;
else BTN_CEO <= &(FLTR_CNT) & CE & BTN_S1;
//------------------------------------------
endmodule
Листинг 2.3 — Модуль делителя частоты (DIV_CLK.v)
module DIV_CLK(
input CLK,
input RST,
output reg CLK_OUT
); reg [13:0] cnt; always@(posedge CLK or posedge RST) begin
if (RST) begin
cnt <= 0;
CLK_OUT <= 1'b0;
end
else if (cnt == 100000) begin cnt <= 0; CLK_OUT <= 1'b1; end
else begin cnt <= cnt + 1; CLK_OUT <= 1'b0; end
end //assign CLK_OUT = (cnt == 0) ? 1 : 0; endmodule
Также был реализован модуль для обработки нажатия кнопки сброса (Листинг 2.4).
Листинг 2.4 — Модуль обработки нажатия кнопки reset (RST_MODULE.v)
module RST_MODULE(
input CLK,
input R,
output reg Q_RST
);
always @(posedge CLK or negedge R) begin
if (!R) Q_RST <= 1'b1;
else Q_RST <= 1'b0;
end
endmodule Затем, нужно было объединить все вышеперечисленные модули в одном модуле верхнего уровня, представленного в Листинге 2.5.
Листинг 2.5 — Модуль верхнего уровня (TOP.v)
module TOP(
input CPU_RESET,
input CLK,
input BTN_C,
input BTN_U,
input [4:0] SW,
output [3:0] LED
);
wire line_RST;
wire line_CLK;
wire line_CE;
wire line_LOAD; RST_MODULE FB1(
.CLK(CLK),
.R(CPU_RESET),
.Q_RST(line_RST)
); DIV_CLK FB2(
.RST(line_RST),
.CLK(CLK),
.CLK_OUT(line_CLK)
); BTN_FILTER_CE FB3(
.CLK(CLK),
.CE(line_CLK),
.BTN_IN(BTN_C),
.RST(line_RST),
.BTN_CEO(line_CE)
); BTN_FILTER_LOAD FB4(
.CLK(CLK),
.CE(line_CLK),
.BTN_IN(BTN_U),
.RST(line_RST),
.BTN_CEO(line_LOAD)
); MAIN_MODULE FB5(
.CLK(CLK),
.RST(line_RST),
.CE(line_CE),
.LOAD(line_LOAD),
.DAT_I(SW[3:0]),
.UP(SW[4]),
.SEQ(LED[3:0])
);
endmodule
2.4 Тестовое окружение и результат верификации
В Листинге 2.6 описан модуль тестового окружения для тестирования работы моделей проекта и верификации проделанной работы.
Листинг 2.6 — Тестовый модуль (Test_MAIN.v)
module Test_MAIN; // Inputs
reg CLK;
reg RST;
reg CE;
reg LOAD;
reg [3:0] DAT_I;
reg UP; // Outputs
wire [3:0] SEQ; // Instantiate the Unit Under Test (UUT)
MAIN_MODULE uut (
.CLK(CLK),
.RST(RST),
.CE(CE),
.LOAD(LOAD),
.DAT_I(DAT_I),
.UP(UP),
.SEQ(SEQ)
); initial begin
// Initialize Inputs
CLK = 0;
RST = 1;
CE = 0;
LOAD = 0;
DAT_I = 0;
UP = 0; // Wait 100 ns for global reset to finish
#150;
RST = 0;
CE = 1;
LOAD = 0;
DAT_I = 0;
UP = 1;
#150;
UP = 0;
#150;
CE = 0;
LOAD = 1;
DAT_I = 4'b1000;
#150;
CE = 0;
LOAD = 0;
Продолжение Листинга 2.6
DAT_I = 0;
UP = 0;
// Add stimulus here end always
begin
#5;
CLK <= CLK;
end
endmodule
На временной диаграмме (Рис. 2.6) виден результат верификации в виде симуляции. Выходные значения совпадают и соответствуют таблице переходов. Следовательно, автомат спроектирован верно.
Рисунок 2.6 — Результат верификации
2.5 Вывод
В результате был спроектирован конечный автомат, представляющий собой генератор фиксированной последовательности логических сигналов, в виде синтезируемой модели на языке Verilog HDL. Также было реализовано на этом языке тестовое окружение и успешно проведена верификация спроектированной модели при помощи симулятора iSim из состава САПР Xilinx ISE Design Suite.
3 Разработка цифрового узла анализатора последовательности
3.1 Постановка задачи
Требовалось разработать цифровой узел на основе отладочной платы Digilent Nexys 4, представляющий собой анализатор фиксированной последовательности логических сигналов. Узел должен обеспечивать индикацию ожидаемых и вводимых элементов последовательности посредством входящих в состав отладочной платы семисегментных индикаторов согласно данному заданию.
Узел должен быть реализован в виде синтезируемой модели на языке Verilog HDL.
Интерфейс верхнего уровня иерархии модели должен состоять из набора сигналов, представленного на Рисунке 3.1.
Рисунок 3.1 — Интерфейс модели цифрового узла
Очередной элемент последовательности для анализа подается на входы узла SW[3:0] с соответствующих движковых переключателей отладочной платы. Загрузка элемента подтверждается однократным нажатием кнопки BTN_C, сигнал от которой подается на вход узла. В связи с этим следует обеспечивать защиту от дребезга контактов кнопки.
Алгоритм работы цифрового узла представлен на Рисунке 3.2. Значение Х представляет собой номер элемента цифровой последовательности, ввод значения которого (Y) ожидается. Значения Y каждого элемента цифровой последовательности определяются вариантом задания, представленным в Таблице 1.1.
Рисунок 3.2 — Алгоритм распознавания последовательности
Распознавание элементов последовательности осуществляется четверками, т.е. необходимо обеспечить последовательную загрузку в узел элементов Y c номерами 0-3, 4- 7, 8-B, C-F для успешного распознавания последовательности. При осуществлении ввода значения, не соответствующего текущему ожидаемому элементу последовательности, необходимо повторить ввод всей четверки элементов заново.
3.2 Описание автомата
Сначала потребовалась таблица истинности. Она уже была построена в первом разделе и представлена в Таблице 1.2. Далее следовало представить структурную схему готового цифрового узла. С её помощью можно детально рассмотреть входы и выходы каждого отдельного модуля. Данная схема (Verilog RTL-модель, описывающая верхний уровень иерархии) представлена на Рисунке 3.3.
Рисунок 3.3 — Структурная схема автомата
Для полноценной работы цифрового узла нужно было разработать несколько модулей, а именно: модуль “RST”, модуль делителя частоты, модуль фильтра дребезга контактов, модуль основного автомата, модули обработчиков сигналов анодов и катодов, модуль верхнего уровня и тестовое окружение.
Модели делителя частоты и фильтра дребезга контактов уже были описаны в разделе 2.3, а их код представлен в Листингах 2.2 и 2.3.
Семисегментный индикатор, как говорит его название, состоит из семи элементов индикации (сегментов), включающихся и выключающихся по отдельности. Включая их в разных комбинациях, из них можно составить упрощённые изображения арабских цифр. В обычном светодиодном индикаторе используется девять выводов: один идёт к катодам (минусам) всех сегментов, и остальные восемь — к аноду каждого из сегментов, включая точку. Эта схема называется «схема с общим катодом», существуют также схемы с общим анодом, где имеется общий анод (плюс) и индивидуально подключенные катоды. Часто делают не один, а два общих вывода на разных концах корпуса — это упрощает разводку, не увеличивая габаритов.
Для модуля обработчика сигнала катодов требовалось правильно соотнести отображаемую цифру с набором включённых и выключенных катодов. Каждый катод отвечает за свой светодиод, следовательно, нужно было правильно задать последовательность разрядов. Общая схема выводов семисегментного индикатора представлена на Рисунке 3.4.
Рисунок 3.4 — Выводы семисегментного индикатора
3.3 Содержание модулей
Нужно было реализовать модуль, отвечающий за сброс. Данный модуль переводит весь цифровой узел в начальное состояние. Реализация представлена в Листинге 3.1.
Листинг 3.1 — Модуль RST (RST.v)
module RST(
input clk,
input cpu_reset,
output rst
);
reg rst;
always @(posedge clk, negedge cpu_reset)
begin
if (!cpu_reset) begin
rst <= 1'b1;
end
else begin
rst <= 1'b0;
end
end endmodule
Главная часть цифрового узла — модуль конечного автомата. Его код представлен в Листинге 3.2.
Листинг 3.2 — Основной модуль автомата (MAIN.v)
module MAIN(
input RST,
input CLK,
input BTN_C,
input [3:0] NewV,
output reg [31:0] CODE
);
reg [1:0] V_counter = 2'b00;
reg [3:0] V;
reg [3:0] V_index = 4'h0;
reg [3:0] V_point = 4'h1;
always @ (posedge CLK, posedge RST)
begin
if (RST) begin
V_counter <= 2'b00;
CODE <= 32'hxxxx1xxx;
V_index <= 4'h1;
V_point = 4'h1;
end
else if (BTN_C) begin
CODE[19:16] <= CODE[23:20];
CODE[23:20] <= CODE[27:24];
CODE[27:24] <= CODE[31:28];
CODE[31:28] <= NewV;
if (NewV == CODE[15:12]) begin
if (V_counter == 2'b11) begin
CODE[15:0] <= 16'hxxxx;
V_point <= V;
end
else begin
CODE[3:0] <= CODE[7:4];
CODE[7:4] <= CODE[11:8];
CODE[11:8] <= CODE[15:12];
end
CODE[15:12] <= V;
V_index <= V_index + 1'b1;
V_counter <= V_counter + 1'b1;
end
else begin
CODE[15:0] <= 16'hxxxx;
V_index <= V_index - V_counter;
CODE[15:12] <= V_point;
V_counter <= 2'b00;
end
end
end always @ (V_index)
begin
case(V_index)
Продолжение Листинга 3.2
4'h0: V <= 4'hD;
4'h1: V <= 4'hC;
4'h2: V <= 4'h8;
4'h3: V <= 4'hD;
4'h4: V <= 4'h4;
4'h5: V <= 4'h9;
4'h6: V <= 4'hA;
4'h7: V <= 4'hE;
4'h8: V <= 4'hF;
4'h9: V <= 4'hD;
4'hA: V <= 4'h2;
4'hB: V <= 4'hD;
4'hC: V <= 4'hA;
4'hD: V <= 4'hE;
4'hE: V <= 4'hE;
4'hF: V <= 4'hA;
default: V <= 4'hx;
endcase
end
endmodule
Также был реализован модуль, как раз отвечающий за обработку сигналов анодов, он представлен в Листинге 3.3.
Листинг 3.3 — Модуль обработчика сигналов анодов (AN_DECODER.v)
module AN_DECODER(
input [2:0] CNT,
output reg [7:0] AN,
output reg [3:0] HEX_AN
);
always @ (CNT)
case(CNT)
3'h0: AN<=8'h7F;
3'h1: AN<=8'hBF;
3'h2: AN<=8'hDF;
3'h3: AN<=8'hEF;
3'h4: AN<=8'hF7;
3'h5: AN<=8'hFB;
3'h6: AN<=8'hFD;
3'h7: AN<=8'hFE;
endcase
//------------------------------
always @ (AN)
case(AN)
8'b01111111: HEX_AN<=4'h7;
8'b10111111: HEX_AN<=4'h6;
8'b11011111: HEX_AN<=4'h5;
8'b11101111: HEX_AN<=4'h4;
8'b11110111: HEX_AN<=4'h3;
8'b11111011: HEX_AN<=4'h2;
8'b11111101: HEX_AN<=4'h1;
8'b11111110: HEX_AN<=4'h0;
endcase
//------------------------------- endmodule
Разработанный модуль для управления катодами индикатора приведён в Листинге 3.4.
Листинг 3.4 — Модуль обработчика сигналов катодов (CAT_DECODER.v)
module CAT_DECODER(
input [2:0] CNT,
input [31:0] CODE,
output reg [7:0] CAT,
output reg [3:0] HEX_CAT
); reg [3:0] I_CODE; always @ (CNT)
case(CNT)
3'h0: I_CODE <= CODE[3:0];
3'h1: I_CODE <= CODE[7:4];
3'h2: I_CODE <= CODE[11:8];
3'h3: I_CODE <= CODE[15:12];
3'h4: I_CODE <= CODE[19:16];
3'h5: I_CODE <= CODE[23:20];
3'h6: I_CODE <= CODE[27:24];
3'h7: I_CODE <= CODE[31:28];
default: I_CODE <= 4'bx;
endcase always @ (I_CODE)
case(I_CODE)
4'h0: CAT <= 8'b11000000;
4'h1: CAT <= 8'b11111001;
4'h2: CAT <= 8'b10100100;
4'h3: CAT <= 8'b10110000;
4'h4: CAT <= 8'b10011001;
4'h5: CAT <= 8'b10010010;
4'h6: CAT <= 8'b10000010;
4'h7: CAT <= 8'b11111000;
4'h8: CAT <= 8'b10000000;
4'h9: CAT <= 8'b10010000;
4'hA: CAT <= 8'b10001000;
4'hB: CAT <= 8'b10000011;
4'hC: CAT <= 8'b11000110;
4'hD: CAT <= 8'b10100001;
4'hE: CAT <= 8'b10000110;
4'hF: CAT <= 8'b10001110;
default: CAT <= 8'b11111111;
endcase //---------------------------------
always @ (CAT)
case(CAT)
8'b11000000: HEX_CAT<=4'h0;
8'b11111001: HEX_CAT<=4'h1;
8'b10100100: HEX_CAT<=4'h2;
8'b10110000: HEX_CAT<=4'h3;
8'b10011001: HEX_CAT<=4'h4;
8'b10010010: HEX_CAT<=4'h5;
8'b10000010: HEX_CAT<=4'h6;
8'b11111000: HEX_CAT<=4'h7;
8'b10000000: HEX_CAT<=4'h8;
8'b10010000: HEX_CAT<=4'h9;
8'b10001000: HEX_CAT<=4'hA;
Продолжение Листинга 3.4
8'b10000011: HEX_CAT<=4'hB;
8'b11000110: HEX_CAT<=4'hC;
8'b10100001: HEX_CAT<=4'hD;
8'b10000110: HEX_CAT<=4'hE;
8'b10001110: HEX_CAT<=4'hF;
8'b11111111: HEX_CAT<=4'hx;
endcase
//------------------------------------ endmodule
Остался модуль верхнего уровня, который отвечает за правильное соединение всех остальных модулей. Код представлен в Листинге 3.5.
Листинг 3.5 — Модуль верхнего уровня (TOP.v)
module TOP(
input CLK,
input CPU_RST,
input BTN_C,
input [3:0] SW,
output [7:0] CAT,
output [7:0] AN
);
wire line_RST;
wire line_CE;
wire BTN_CE;
wire [2:0] line_CNT;
wire [31:0] line_CODE; RST FB1(
.clk(CLK),
.cpu_reset(CPU_RST),
.rst(line_RST)
); CLOCK_DIVIDER FB2(
.rst(line_RST),
.clk(CLK),
.CEO(line_CE)
); FILTER FB3(
.clk(CLK),
.CE(line_CE),
.BTN_IN(BTN_C),
.rst(line_RST),
.BTN_CEO(BTN_CE)
); COUNTER FB4(
.DISD_CE(line_CE),
.RST(line_RST),
.CLK(CLK),
.CNT(line_CNT)
); AN_DECODER FB5(
.CNT(line_CNT),
Продолжение Листинга 3.5
.AN(AN)
); MAIN FB6(
.CLK(CLK),
.RST(line_RST),
.BTN_C(BTN_CE),
.NewV(SW),
.CODE(line_CODE)
); CAT_DECODER FB7(
.CNT(line_CNT),
.CODE(line_CODE),
.CAT(CAT)
);
endmodule
|