FPGA学习笔记 -- 分频器

本文详细介绍了FPGA中的时钟分频方法,包括偶分频的六分频和奇分频的五分频。针对六分频,提出了使用计数器和时钟标志位的改进方案,以提高在高频时钟下的稳定性。五分频则通过组合上升沿和下降沿的信号,实现了50%占空比的分频输出。文章还展示了相应的Verilog代码实现,演示了如何在FPGA设计中实现这些分频技术。
摘要由CSDN通过智能技术生成

偶分频——六分频

 可以看到输出的频率对应六个系统时钟周期,即完成了六分频的任务,这里使计数器计数为2将输出波形进行反转,所以0,1,2半个周期就有了三个系统时钟周期。

此方法在低频信号中可以使用,但是在高频时钟下会出现失误, 在FPGA中所有的时钟都要连接到全局时钟网络中去,又叫全局时钟数,其目的是为了保证时钟信号到达每一个器件的时间都尽可能相同,而这种方法并没有连接到全局时钟网络中

module divider_six
(
	input wire sys_clk,
	input wire rst_n,
	
	output reg out_clk
);
reg [1:0] cnt; //两位宽的计数器,使用寄存器类型数据

always@(posedge sys_clk or negedge rst_n) //对计数器进行判断赋值
	if(rst_n == 1'b0)
		cnt <= 2'b0;
	else if(cnt == 2'd2)
		cnt <= 2'b0;
	else 
		cnt <= cnt + 2'b1;
		
always@(posedge sys_clk or negedge rst_n) //对输出进行判断赋值,由于输出中使用了always语句所以为reg类型变量
	if(rst_n == 1'b0)
		out_clk <= 1'b0;
	else if(cnt == 2'd2)
		out_clk <= ~out_clk;
	else 
		out_clk <= out_clk;
		
endmodule
`timescale 1ns/1ns

module tb_divider_six();

reg sys_clk;
reg rst_n;

wire out_clk;

initial
	begin
		sys_clk = 1'b1;
		rst_n <= 1'b0;
		#20
		rst_n <= 1'b1;
	end
	
always #10 sys_clk = ~sys_clk;

divider_six divider_six_1
(
	.sys_clk(sys_clk),
	.rst_n(rst_n),
	
	.out_clk(out_clk)
);

endmodule

对于上述方法的改进,可以使用脉冲标志信号

其中的clk_flag即为六分频输出,在两个clk_flag的上升沿即可视为一个周期

改进代码:

/*  通过时钟标志位的方法与直接进行波形翻转的六分频看上去效果差距不大但若是在接下来对频率进行计数时:

翻转产生的六分频:always always@(posedge out_clk or negedge rst_n)
利用标志位的六分频:always always@(posedge sys_clk or negedge rst_n) 加上对标志位的判断if(clk_flag = 1'b1)
可见标志位中使用的仍然是系统的时钟周期,这在高频时钟下可以保持系统的稳定,避免了时钟网络原因`带来的偏差

*/


module divider_six
(
	input wire sys_clk,
	input wire rst_n,
	
	// output reg out_clk
	output reg clk_flag
);

// reg [1:0] cnt; //两位宽的计数器,使用寄存器类型数据

reg [2:0] cnt; //三位宽的计数器,使用寄存器类型数据


/* always@(posedge sys_clk or negedge rst_n) //对计数器进行判断赋值
	if(rst_n == 1'b0)
		cnt <= 2'b0;
	else if(cnt == 2'd2)
		cnt <= 2'b0;
	else 
		cnt <= cnt + 2'b1;
		
always@(posedge sys_clk or negedge rst_n) //对输出进行判断赋值,由于输出中使用了always语句所以为reg类型变量
	if(rst_n == 1'b0)
		out_clk <= 1'b0;
	else if(cnt == 2'd2)
		out_clk <= ~out_clk;
	else 
		out_clk <= out_clk; */

always@(posedge sys_clk or negedge rst_n)
	if(rst_n == 1'b0)
		cnt <= 3'b0;
	else if(cnt == 3'd5)
		cnt <= 3'b0;
	else 
		cnt <= cnt + 3'b1;

always@(posedge sys_clk or negedge rst_n)
	if(rst_n == 1'b0)
		clk_flag <= 1'b0;
	else if(cnt == 3'd4) // 在计数终止的前一个时钟产生高电平,为在后边对标志位进行利用时会额外占用一个时钟周期
		clk_flag <= 1'b1;
	else 
		clk_flag <= 1'b0;
		
endmodule

 奇分频——五分频——比偶分频稍难

分频法:

分别利用上升沿和下降沿作为触发,但是显然其占空比并不为50%,但是如果利用或运算可以发现其刚好可以合成最终的50%占空比的分频

 

module divider_five
(
	input wire sys_clk,
	input wire res_n,
	
	output wire out_clk
);

reg [2:0] cnt;
reg clk_1;
reg clk_2;

always@(posedge sys_clk or negedge res_n)
	if(res_n == 1'b0)
		cnt <= 3'd0;
	else if(cnt == 3'd4)
		cnt <= 3'd0;
	else 
		cnt <= cnt + 1'b1;

always@(posedge sys_clk or negedge res_n) //时钟上升沿时访问函数
	if(res_n == 1'b0)
		clk_1 <= 1'b0;
	else if(cnt == 3'd2)
		clk_1 <= 1'b1;
	else if(cnt == 3'd4)
		clk_1 <= 1'b0;
	else 
		clk_1 <= clk_1;

always@(negedge sys_clk or negedge res_n) //时钟下降沿时访问函数
	if(res_n == 1'b0)
		clk_2 <= 1'b0;
	else if(cnt == 3'd2)
		clk_2 <= 1'b1;
	else if(cnt == 3'd4)
		clk_2 <= 1'b0;
	else 
		clk_2 <= clk_2;		 
		
assign out_clk = (clk_1 | clk_2); //使用连续赋值语句取clk_1和clk_2的或运算
		
endmodule		
`timescale 1ns/1ns

module tb_divider_five();

reg sys_clk;
reg rst_n;

wire out_clk;

initial
	begin
		sys_clk = 1'b1;
		rst_n <= 1'b0;
		#20
		rst_n <= 1'b1;
	end
	
always #10 sys_clk = ~sys_clk;

divider_five divider_five_1
(
	.sys_clk(sys_clk),
	.res_n(rst_n),
	
	.out_clk(out_clk)
);

endmodule

降频法:

module divider_five
(
	input wire sys_clk,
	input wire res_n,
	
	//output wire out_clk
	output reg clk_flag
);

reg [2:0] cnt;

//分频法部分
/* reg clk_1;
reg clk_2;

always@(posedge sys_clk or negedge res_n)
	if(res_n == 1'b0)
		cnt <= 3'd0;
	else if(cnt == 3'd4)
		cnt <= 3'd0;
	else 
		cnt <= cnt + 1'b1;

always@(posedge sys_clk or negedge res_n) //时钟上升沿时访问函数
	if(res_n == 1'b0)
		clk_1 <= 1'b0;
	else if(cnt == 3'd2)
		clk_1 <= 1'b1;
	else if(cnt == 3'd4)
		clk_1 <= 1'b0;
	else 
		clk_1 <= clk_1;

always@(negedge sys_clk or negedge res_n) //时钟下降沿时访问函数
	if(res_n == 1'b0)
		clk_2 <= 1'b0;
	else if(cnt == 3'd2)
		clk_2 <= 1'b1;
	else if(cnt == 3'd4)
		clk_2 <= 1'b0;
	else 
		clk_2 <= clk_2;		 
		
assign out_clk = (clk_1 | clk_2); //使用连续赋值语句取clk_1和clk_2的或运算 */

//降频法
always@(posedge sys_clk or negedge res_n)
	if(res_n == 1'b0)
		cnt <= 3'd0;
	else if(cnt == 3'd4)
		cnt <= 3'd0;
	else 
		cnt <= cnt + 1'b1;

always@(posedge sys_clk or negedge res_n)
	if(res_n == 1'b0)
		clk_flag <= 1'b0;
	else if(cnt == 3'd3)
		clk_flag <= 1'b1;
	else 
		clk_flag <= 1'b0;		

endmodule		

 

`timescale 1ns/1ns

module tb_divider_five();

reg sys_clk;
reg rst_n;

wire clk_flag;

initial
	begin
		sys_clk = 1'b1;
		rst_n <= 1'b0;
		#20
		rst_n <= 1'b1;
	end
	
always #10 sys_clk = ~sys_clk;

divider_five divider_five_1
(
	.sys_clk(sys_clk),
	.res_n(rst_n),
	
	.clk_flag(clk_flag)
);

endmodule

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值