Генератор тактового сигнала в Verilog: проектирование и реализация
Тактовые сигналы фундаментальны для цифровых схем — они синхронизируют разные блоки.
Свойства тактового сигнала
Ключевые свойства: частота (определяет период), скважность (duty cycle) и фаза относительно других тактовых.
Период тактового сигнала
Частота показывает, сколько циклов в единице времени. Период — время для завершения 1 цикла.
Скважность (Duty Cycle)
Доля времени, когда такт высокий, к периоду.
Фаза тактового сигнала
Один цикл — полный круг 360°. Другой такт того же периода, сдвинутый на 1/4 периода, имеет 90° разницу фаз.
Verilog генератор тактового сигнала
Симуляция работает на заданном timescale с ограниченной точностью. Важно, чтобы точность timescale позволяла представить период такта. Например, частота 640000 кГц → период 1.5625 нс. Точность 1пс не хватит (лишняя цифра), симулятор округлит до 1.563 нс → 639795 кГц.
Модуль генератора с тремя параметрами для настройки свойств. Вход enable включает/выключает такт. При общем enable для нескольких тактовых их можно легко синхронизировать по фазе.
`timescale 1ns/1ps
module clock_gen (
input enable,
output reg clk
);
parameter FREQ = 100000; // кГц
parameter PHASE = 0; // градусы
parameter DUTY = 50; // проценты
real clk_pd = 1.0/(FREQ * 1e3) * 1e9; // период в нс
real clk_on = DUTY/100.0 * clk_pd; // время высокого уровня
real clk_off = (100.0 - DUTY)/100.0 * clk_pd; // время низкого
real quarter = clk_pd/4;
real start_dly = quarter * PHASE/90;
reg start_clk;
initial begin
$display("FREQ = %0d kHz", FREQ);
$display("PHASE = %0d deg", PHASE);
$display("DUTY = %0d %%", DUTY);
$display("PERIOD = %0.3f ns", clk_pd);
$display("CLK_ON = %0.3f ns", clk_on);
$display("CLK_OFF = %0.3f ns", clk_off);
$display("QUARTER = %0.3f ns", quarter);
$display("START_DLY = %0.3f ns", start_dly);
end
initial begin
clk <= 0;
start_clk <= 0;
end
// При включении такта задерживаем запуск для фазы
always @ (posedge enable or negedge enable) begin
if (enable) begin
#(start_dly) start_clk = 1;
end else begin
#(start_dly) start_clk = 0;
end
end
// Генерация такта с заданной скважностью
always @(posedge start_clk) begin
if (start_clk) begin
clk = 1;
while (start_clk) begin
#(clk_on) clk = 0;
#(clk_off) clk = 1;
end
clk = 0;
end
end
endmodule
Тестбенч с разными частотами
module tb;
wire clk1, clk2, clk3, clk4;
reg enable;
reg [7:0] dly;
clock_gen u0(enable, clk1);
clock_gen #(.FREQ(200000)) u1(enable, clk2);
clock_gen #(.FREQ(400000)) u2(enable, clk3);
clock_gen #(.FREQ(800000)) u3(enable, clk4);
initial begin
enable <= 0;
for (int i = 0; i < 10; i= i+1) begin
dly = $random;
#(dly) enable <= ~enable;
$display("i=%0d dly=%0d", i, dly);
#50;
end
#50 $finish;
end
endmodule
Simulation Log
xcelium> run FREQ = 100000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 0.000 ns FREQ = 200000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 5.000 ns CLK_ON = 2.500 ns CLK_OFF = 2.500 ns QUARTER = 1.250 ns START_DLY = 0.000 ns FREQ = 400000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 2.500 ns CLK_ON = 1.250 ns CLK_OFF = 1.250 ns QUARTER = 0.625 ns START_DLY = 0.000 ns FREQ = 800000 kHz PHASE = 0 deg DUTY = 50 % PERIOD = 1.250 ns CLK_ON = 0.625 ns CLK_OFF = 0.625 ns QUARTER = 0.312 ns START_DLY = 0.000 ns i=0 dly=36 ... i=9 dly=13 Simulation complete via $finish(1) at time 1110 NS + 0
Тестбенч с разными фазами
module tb;
wire clk1, clk2;
reg enable;
reg [7:0] dly;
clock_gen u0(enable, clk1);
clock_gen #(.FREQ(50000), .PHASE(90)) u1(enable, clk2);
initial begin
enable <= 0;
for (int i = 0; i < 10; i=i+1) begin
dly = $random; #(dly) enable <= ~enable;
$display("i=%0d dly=%0d", i, dly);
end
#50 $finish;
end
endmodule
Simulation Log
xcelium> run FREQ = 100000 kHz PHASE = 0 deg DUTY = 50 % ... FREQ = 100000 kHz PHASE = 90 deg DUTY = 50 % ... FREQ = 100000 kHz PHASE = 180 deg DUTY = 50 % ... FREQ = 100000 kHz PHASE = 270 deg DUTY = 50 % ... i=0 dly=36 ... i=9 dly=13 Simulation complete via $finish(1) at time 1110 NS + 0
Тестбенч с разными скважностями
module tb;
wire clk1, clk2, clk3, clk4;
reg enable;
reg [7:0] dly;
clock_gen u0(enable, clk1);
clock_gen #(.DUTY(25)) u1(enable, clk2);
clock_gen #(.DUTY(75)) u2(enable, clk3);
clock_gen #(.DUTY(90)) u3(enable, clk4);
initial begin
enable <= 0;
for (int i = 0; i < 10; i= i+1) begin
dly = $random; #(dly) enable <= ~enable;
$display("i=%0d dly=%0d", i, dly); #50;
end
#50 $finish;
end
endmodule
Simulation Log
xcelium> run FREQ = 100000 kHz PHASE = 0 deg DUTY = 50 % ... FREQ = 100000 kHz PHASE = 0 deg DUTY = 25 % ... FREQ = 100000 kHz PHASE = 0 deg DUTY = 75 % ... FREQ = 100000 kHz PHASE = 0 deg DUTY = 90 % ... i=0 dly=36 ... i=9 dly=13 Simulation complete via $finish(1) at time 1110 NS + 0
Переключение enable для запуска/остановки тактов
Волна ниже показывает: такты остановлены при enable=0, запущены при enable=1.
Verilog
- Освоение Verilog: полное руководство по проектированию цифровых схем
- Освоение конкатенации в Verilog: практическое руководство
- Понимание назначений в Verilog: типы, синтаксис и лучшие практики
- Блокирующие и неблокирующие назначения в Verilog: ключевые различия
- Мастерство функций в Verilog для эффективного RTL дизайна
- Понимание задач (Tasks) в Verilog: отличия от функций, синтаксис и применение
- Мастерство математических функций Verilog для эффективного аппаратного дизайна
- Мастерство $timeformat в Verilog: точность и контроль отображения времени
- Понимание и печать timescale в Verilog в любой области видимости
- Работа с файлами в Verilog: открытие, чтение, запись и закрытие