디지털 클럭의 핵심 속성은 주파수(frequency)(클럭 주기 결정), 듀티 사이클(duty cycle), 다른 클럭과의 관계에서 클럭 위상(phase)입니다.
클럭 주기
주파수는 특정 시간 동안 몇 사이클이 있는지를 나타냅니다. 따라서 클럭 주기는 1 사이클을 완료하는 데 걸리는 시간입니다.
클럭 듀티 사이클
클럭이 하이 상태인 시간이 주기 대비 몇 퍼센트인지 정의합니다.
클럭 위상
한 클럭 주기를 360도 원으로 볼 때, 다른 클럭은 원의 다른 위치에 상대적으로 배치되어 다른 위상을 가질 수 있습니다. 예를 들어, 같은 주기의 다른 클럭이 주기의 1/4만큼 오른쪽으로 이동하면 90도 위상 차이라고 할 수 있습니다.
Verilog 클럭 생성기
시뮬레이션은 timescale 지시자로 지정된 제한된 정밀도를 가진 주어진 타임스케일에서 작동해야 합니다. 따라서 timescale 정밀도가 클럭 주기를 표현하기에 충분히 좋은지 확인하는 것이 중요합니다. 예를 들어, 클럭 주파수가 640000 kHz로 설정되면 클럭 주기는 1.5625 ns가 되는데, 1ps timescale 정밀도로는 추가 소수점을 표현할 수 없어 시뮬레이션이 마지막 자리를 반올림하여 3자리 timescale 정밀도에 맞춥니다. 이로 인해 클럭 주기가 1.563으로 늘어나 실제로는 639795 kHz가 됩니다!
다음 Verilog 클럭 생성기 모듈은 위에서 논의한 세 가지 속성을 조정하는 세 가지 파라미터를 가집니다. 모듈은 클럭을 필요에 따라 비활성화/활성화할 수 있는 입력 enable을 가집니다. 여러 클럭이 공통 enable 신호로 제어되면 상대적 위상을 쉽게 맞출 수 있습니다.
`timescale 1ns/1ps
module clock_gen (
input enable,
output reg clk
);
parameter FREQ = 100000; // kHz 단위
parameter PHASE = 0; // 도(degree) 단위
parameter DUTY = 50; // 퍼센트 단위
real clk_pd = 1.0/(FREQ * 1e3) * 1e9; // ns로 변환
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
// 변수들을 0으로 초기화
initial begin
clk <= 0;
start_clk <= 0;
end
// 클럭이 활성화되면 위상 효과를 위해 클럭 구동을 1로 지연시킴
// start_dly는 설정된 위상에 대한 올바른 지연으로 구성됨
// enable이 0이면 현재 클럭 주기 완료를 위해 충분한 시간 허용
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
// 듀티 사이클을 비대칭 클럭 on/off 시간으로 달성하고
// 클럭이 켜진 동안 계속 실행
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