在FPGA程序设计中使用PLL是分频和倍频最方便的选择可以生成自己想要的频率脉冲,在分频和倍频选择上也可以使用计数器来完成实现
1、分频
1.分频系数方式
module pll #(
parameter SYS_FREQ = 26'd50_000_000,
OUT_FREQ = 20'd500_000
)
(
input clk ,
input rst_n
);
//参数定义
//中间信号定义
reg clk_500k;
wire [25:0] coef ;
reg [25:0] cnt_500k;
assign coef = (SYS_FREQ/OUT_FREQ) >>1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_500k <= 26'b1;
clk_500k <= 1'b0;
end
else if(cnt_500k < coef)begin
cnt_500k <= cnt_500k +26'd1;
end
else if(cnt_500k == coef)begin
clk_500k <= ~clk_500k;
cnt_500k <= 26'b1;
end
else begin
cnt_500k <= cnt_500k;
end
end
这种方式分频需要给出模块时钟频率和分频得到的时钟频率,计算除分频系数使用计数器得到自己想要的时钟频率,这种当时分频得到的时钟缺点就是有些模块时钟的频率和得到的时钟频率计算除法会取整,误差太明显了,这种方式用来计算串口的波特率还是可以的,毕竟串口一次传输一字节的数据不会持续太久。
2.偶分频
2分频
module pll
(
input clk ,
input rst_n
);
//参数定义
parameter num = 8'd2;
//中间信号定义
wire [7:0] num_r ;
reg clk_out ;
reg [7:0] cnt ;
assign num_r = num>>1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
clk_out <= 1'b0;
cnt <= 8'd1;
end
else if(cnt == num_r)begin
clk_out <= ~clk_out;
cnt <= 8'd1;
end
else if(cnt <= num_r)begin
cnt <= cnt+8'd1;
end
else begin
clk_out <= clk_out;
cnt <= cnt;
end
end
endmodule
2.奇分频
7分频
module pll
(
input clk ,
input rst_n
);
//参数定义
parameter num = 8'd7;
//中间信号定义
wire [7:0] num_r ;
reg clk_p ;
reg clk_n ;
wire clk_out ;
reg [7:0] cnt1 ;
reg [7:0] cnt2 ;
assign num_r = num>>1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
clk_p <= 1'b0;
cnt1 <= 8'd1;
end
else if(cnt1 <= num)begin
cnt1 <= cnt1+8'd1;
if(cnt1 == num_r || cnt1 == num)begin
clk_p <= ~clk_p;
if(cnt1 == num)
cnt1 <= 8'd1;
else
;
end
end
else begin
clk_p <= clk_p;
cnt1 <= cnt1;
end
end
always @(negedge clk )begin
if(!rst_n)begin
clk_n <= 1'b0;
cnt2 <= 8'd1;
end
else if(cnt2 <= num)begin
cnt2 <= cnt2+8'd1;
if(cnt2 == num_r || cnt2 == num )begin
clk_n <= ~clk_n;
if(cnt2 == num)
cnt2 <= 8'd1;
else
;
end
end
else begin
cnt2 <= cnt2;
clk_n <= clk_n;
end
end
assign clk_out = clk_p & clk_n;
endmodule
2、倍频
建议还是使用pll,信号的改变是根据时钟的上升沿或下降沿产生的,将一个周期的时钟分为两个周期就相当于变化四次,但是一个周期时钟就上升沿或下降沿我不知到怎么去倍频,网上也有使用组合逻辑异或去倍频,我也试过,这种方式它产生变化后会马上返回之前的状态,保持不到一个时钟的四分之一,反正我没成功,要么就是用timescale 设置精度单位,但这种写法太不实际了也不建议,还是用pll,不然设计pll来干什么的麻,还有就是最近使用国产FPGA,用了高云、易灵思平台,因该还要接触复旦微,发现这些频台的的pll 都有误差,国产的平台都挺麻烦的,可能是自己使用不熟,还有就是易灵思的软件运行太慢,容易未响应,但易灵思FAE、工程师还可以的刚晚上11.了还打电话回我问题。
3、tb程序
`timescale 1ns/1ns
module pll_tb();
//激励信号定义
reg tb_clk ;
reg tb_rst_n ;
//时钟周期参数定义
parameter CLOCK_CYCLE = 20;
pll u_pll(
.clk (tb_clk ),
.rst_n (tb_rst_n )
);
//产生时钟
initial tb_clk = 1'b0;
always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;
//产生激励
initial begin
tb_rst_n = 1'b1;
#(CLOCK_CYCLE*2);
tb_rst_n = 1'b0;
#(CLOCK_CYCLE*20);
tb_rst_n = 1'b1;
end
endmodule