Verilog 时钟发生器
时钟是构建数字电路的基础,因为它允许不同的模块相互同步。
时钟的属性
数字时钟的关键属性是它的频率 决定时钟周期 , 它的占空比 和时钟相位 相对于其他时钟。
时钟周期
频率表示在某个时间段内可以找到多少个周期。因此时钟周期是完成 1 个周期所用的时间。
<无脚本>
时钟占空比
时钟与其时间周期相比处于高电平的时间量定义了占空比。
<无脚本>
时钟相位
如果一个时钟的一个周期可以看成一个360度的完整圆,那么另一个时钟可以相对放置在圆的不同位置,占据不同的相位。例如,同一时间周期的另一个时钟右移了其周期的1/4,可以说是相差90度。
<无脚本>
Verilog 时钟发生器
模拟需要在给定的时间尺度上运行,该时间尺度具有由 timescale 指令指定的有限精度。因此,重要的是时间刻度的精度足以代表一个时钟周期。例如,如果时钟的频率设置为 640000 kHz,则其时钟周期将为 1.5625 ns,此时 1ps 的时间刻度精度将不够,因为要表示一个额外的点。因此,模拟将四舍五入最后一位数字以适应 3 点时间刻度精度。这会将时钟周期提高到 1.563,实际上代表 639795 kHz!
下面的 Verilog 时钟发生器模块具有三个参数来调整上述三个不同的属性。该模块有一个输入使能,允许根据需要禁用和启用时钟。当多个时钟由一个共同的使能信号控制时,它们可以相对容易地进行定相。
`timescale 1ns/1ps
module clock_gen ( input enable,
output reg clk);
parameter FREQ = 100000; // in kHZ
parameter PHASE = 0; // in degrees
parameter DUTY = 50; // in percentage
real clk_pd = 1.0/(FREQ * 1e3) * 1e9; // convert to 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
// Initialize variables to zero
initial begin
clk <= 0;
start_clk <= 0;
end
// When clock is enabled, delay driving the clock to one in order
// to achieve the phase effect. start_dly is configured to the
// correct delay for the configured phase. When enable is 0,
// allow enough time to complete the current clock period
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
// Achieve duty cycle by a skewed clock on/off time and let this
// run as long as the clocks are turned on.
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;
wire clk2;
wire clk3;
wire 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
<无脚本> 
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=1 dly=129 i=2 dly=9 i=3 dly=99 i=4 dly=13 i=5 dly=141 i=6 dly=101 i=7 dly=18 i=8 dly=1 i=9 dly=13 Simulation complete via $finish(1) at time 1110 NS + 0
具有不同时钟相位的测试台
module tb;
wire clk1;
wire 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
<无脚本> 
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 = 100000 kHz PHASE = 90 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 2.500 ns FREQ = 100000 kHz PHASE = 180 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 5.000 ns FREQ = 100000 kHz PHASE = 270 deg DUTY = 50 % PERIOD = 10.000 ns CLK_ON = 5.000 ns CLK_OFF = 5.000 ns QUARTER = 2.500 ns START_DLY = 7.500 ns i=0 dly=36 i=1 dly=129 i=2 dly=9 i=3 dly=99 i=4 dly=13 i=5 dly=141 i=6 dly=101 i=7 dly=18 i=8 dly=1 i=9 dly=13 Simulation complete via $finish(1) at time 1110 NS + 0
具有不同占空比的测试台
module tb;
wire clk1;
wire clk2;
wire clk3;
wire 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
<无脚本> 
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 = 100000 kHz PHASE = 0 deg DUTY = 25 % PERIOD = 10.000 ns CLK_ON = 2.500 ns CLK_OFF = 7.500 ns QUARTER = 2.500 ns START_DLY = 0.000 ns FREQ = 100000 kHz PHASE = 0 deg DUTY = 75 % PERIOD = 10.000 ns CLK_ON = 7.500 ns CLK_OFF = 2.500 ns QUARTER = 2.500 ns START_DLY = 0.000 ns FREQ = 100000 kHz PHASE = 0 deg DUTY = 90 % PERIOD = 10.000 ns CLK_ON = 9.000 ns CLK_OFF = 1.000 ns QUARTER = 2.500 ns START_DLY = 0.000 ns i=0 dly=36 i=1 dly=129 i=2 dly=9 i=3 dly=99 i=4 dly=13 i=5 dly=141 i=6 dly=101 i=7 dly=18 i=8 dly=1 i=9 dly=13 Simulation complete via $finish(1) at time 1110 NS + 0
切换启用以启动/停止时钟
下面的波形显示,当 enable 为低电平时时钟停止,当 enable 设置为高电平时时钟开始。
<无脚本>
Verilog