FPGA 多路分频器实验

1 概述

在 FPGA 中,时钟分频是经常用到的。本节课讲解 2 分频、3 分频、4 分频和 8 分频的 Verilog 实现并且学习 generate 语法功能的应。

2 程序设计思路

1)整数倍分频,为 2、4、8,这种 2^n 次方倍数倍数关系的分频最容易实现,所以我们可以把这 3 种分频方式归为一类。

2)3 分频是奇数倍分频,这种分频比较麻烦,对于初学者肯定得思考一番。

3)2HZ 和前文中流水灯的延迟控制方法有一样,只要实现每过 500ms 对寄存器取反操作。

对于这类基础简单的方案,笔者认为,大家学习主要缺少的是思路,所以我们直接拿程序来分析。

Clk_Divider.v

复制代码
`timescale 1ns / 1ps
module Clk_Divider# 
(
parameter DEBUG_ENABLE = 1'b1,
parameter REF_CLK      = 32'd100000000
)
(
input clk_i,
input  rst_n_i,
output div2_o,
//output div3_o,
output div4_o,
output div8_o,
output div2hz_o
    );


//2分频代码:只要基于源时钟每个时钟的上升沿对div2_o_r寄存器取反    
reg div2_o_r;
always@(posedge clk_i)begin
	if(!rst_n_i)
		div2_o_r <= 1'b0;
	else 
		div2_o_r <= ~div2_o_r;
end

//4分频和8分频代码:共同使用了div_cnt1计数器
//4分频就是对计数器在div_cnt1==2'b00或者div_cnt1==2'b10的时候对div4_o_r寄存器取反;
//而8分频是对div_cnt1==2'b00的时候对div8_o_r取反
reg [1:0] div_cnt1;
always@(posedge clk_i)begin
	if(!rst_n_i)
		div_cnt1 <= 2'b00;
	else
		div_cnt1 <= div_cnt1+1'b1;
end

reg div4_o_r;
reg div8_o_r;
always@(posedge clk_i)begin
	if(!rst_n_i)
		div4_o_r <= 1'b0;
	else if(div_cnt1==2'b00 || div_cnt1==2'b10)
		div4_o_r <= ~div4_o_r;
	else
		div4_o_r <= div4_o_r;
end

always@(posedge clk_i)begin
	if(!rst_n_i)
		div8_o_r <= 1'b0;
	else if(div_cnt1==2'b00)
		div8_o_r <= ~div8_o_r;
	else
		div8_o_r <= div8_o_r;
end
/*
3分频的本质是我们需要在每次1.5倍的时钟周期的时候实现3分频寄存器的翻转,但是我们无法直接实现1.5倍的分频。
因此采取分别采取2个计数器pos_cnt和neg_cnt,分别对上升沿和下降沿计数。计数周期是0-1-2,共计3个时钟周期。
我们取pos_cnt == 2'd1的时候div3_o_r0输出高电平,neg_cnt == 2'd1的时候div3_o_r1输出高电平。
由于div3_o_r0和div3_o_r1输出1个时钟的高电平,但是相位相差180°,因此只要执行div3_o = div3_o_r0 | div3_o_r1运算,
就能实现1.5倍周期的输出高电平,那么剩余的1.5倍源时钟周期就是输出低电平了。
*/
reg [1:0] pos_cnt;
reg [1:0] neg_cnt;
always@(posedge clk_i)begin
	if(!rst_n_i)
		pos_cnt <= 2'b00;
	else if(pos_cnt == 2'd2)
		pos_cnt <= 2'b00;
	else
		pos_cnt <= pos_cnt + 1'b1;
end

always@(negedge clk_i)begin
	if(!rst_n_i)	
		neg_cnt <= 2'b00;
	else if(neg_cnt == 2'd2)
		neg_cnt <= 2'b00;
	else
		neg_cnt <= neg_cnt + 1'b1;
end

reg div3_o_r0;
reg div3_o_r1;
always@(posedge clk_i)begin
	if(!rst_n_i)
		div3_o_r0 <= 1'b0;
	else if(pos_cnt < 2'd1)
		div3_o_r0 <= 1'b1;
	else
		div3_o_r0 <= 1'b0;
end

always@(negedge clk_i)begin
	if(!rst_n_i)
		div3_o_r1 <= 1'b0;
	else if(neg_cnt < 2'd1)	
		div3_o_r1 <= 1'b1;
	else
		div3_o_r1 <= 1'b0;
end

reg div2hz_o_r;
reg [25:0] div2hz_cnt;

wire ms250_en = (div2hz_cnt == REF_CLK/4 - 1'b1);
always@(posedge clk_i)
begin
	if(!rst_n_i)
		div2hz_cnt <= 0;
	else if(div2hz_cnt < REF_CLK/4 - 1'b1)
		div2hz_cnt <= div2hz_cnt + 1'b1;
	else
		div2hz_cnt <= 0;
end

always@(posedge clk_i)
begin
	if(!rst_n_i)
		div2hz_o_r <= 0;
	else if(ms250_en)
		div2hz_o_r <= ~div2hz_o_r;
	else
		div2hz_o_r <= div2hz_o_r;
end

assign div2_o = div2_o_r;
assign div3_o = div3_o_r0 | div3_o_r1;
assign div4_o = div4_o_r;
assign div8_o = div8_o_r;
assign div2hz_o = div2hz_o_r;

generate  if(DEBUG_ENABLE == 1'b1) begin : debugcore
//添加ila IP ,Chipscope观察信号
ila_0 ila_0_0 (
	.clk(clk_i), // input wire clk
	.probe0(div2hz_o), // input wire [0:0]  probe0  
	.probe1({div2_o,div4_o,div8_o}) // input wire [3:0]  probe1
);
end
endgenerate

endmodule	

代码解释:

2 分频代码: 只要基于源时钟每个时钟的上升沿对 div2_o_r 寄存器取反

4 分频和 8 分频代码: 共同使用了 div_cnt1 计数器,4 分频就是对计数器在 div_cnt12'b00 或者 div_cnt12'b10 的时候对 div4_o_r 寄存器取反;而 8 分频是对 div_cnt1==2'b00 的时候对 div8_o_r 取反

**3 分频代码:**3 分频的本质是我们需要在每次 1.5 倍的时钟周期的时候实现 3 分频寄存器的翻转,但是我们无法直接实现 1.5倍的分频。因此采取分别采取 2 个计数器 pos_cnt 和 neg_cnt,分别对上升沿和下降沿计数。计数周期是 0-1-2,共计 3 个时钟周期。我们取 pos_cnt == 2'd1 的时候 div3_o_r0 输出高电平,neg_cnt == 2'd1 的时候 div3_o_r1 输出高电平。由于 div3_o_r0 和 div3_o_r1 输出 1 个时钟的高电平,但是相位相差 180°,因此只要执行 div3_o = div3_o_r0 | div3_o_r1 运算,就能实现 1.5 倍周期的输出高电平,那么剩余的 1.5 倍源时钟周期就是输出低电平了。

3 RTL 仿真
Clk_Divider_Tb.v

复制代码
module Clk_Divider_Tb();
// Inputs
reg clk_i;
reg rst_n_i;
// Outputs
wire div2_o;
//wire div3_o;
wire div4_o;
wire div8_o;
wire div2hz_o;

// Instantiate the Unit Under Test (UUT)
Clk_Divider#(
.DEBUG_ENABLE(1'b0),
.REF_CLK(100000000)
) 
Clk_Divider_inst
(
.clk_i(clk_i),
.rst_n_i(rst_n_i), 
.div2_o(div2_o),
.div4_o(div4_o),
.div8_o(div8_o),
.div2hz_o(div2hz_o)
);

initial begin
// Initialize Inputs4
    clk_i= 0;
    rst_n_i = 0;
// Wait 100 ns for global reset to finish
    #100;
    rst_n_i=1;
end
always #5 clk_i =~clk_i;
endmodule

2 分频、3 分频、8 分频

2HZ 分频

相关推荐
nuoxin1141 天前
WILX1200HC-5TG144I替代 LCMXO2-1200HC-5TG144I(富利威)
人工智能·嵌入式硬件·fpga开发·电脑·硬件工程·dsp开发
Bahair_1 天前
【FPGA】使用fdatool设计滤波器系数,使用FIR Compiler导入系数联合滤波
fpga开发
qq_411262421 天前
硬件是ESP32-P4连接LAN8720A,正常初始化之后,设备DHCP失败
stm32·单片机·fpga开发
第二层皮-合肥2 天前
【数据采集专栏】时钟同步(有时钟卡方案)
fpga开发
XINVRY-FPGA2 天前
XCKU035-2FBVA676I AMD Xilinx Kintex UltraScale FPGA
arm开发·嵌入式硬件·网络安全·fpga开发·硬件工程·信号处理·fpga
米琪脆脆屋2 天前
0-1学习FPGA之底层资源——LUT
fpga开发·fpga
TTGGGFF2 天前
ModelSim SE 10.1c 超详细安装与激活保姆级教程(图文详解 2026 仅供学习)
学习·fpga开发
Aaron15882 天前
无人机反制中AOA+TDOA联合定位技术与雷达探测定位技术的应用对比分析
arm开发·嵌入式硬件·fpga开发·硬件工程·无人机·信息与通信·信号处理
暴风雨中的白杨3 天前
fpga复位电平与资源消耗对比测试
fpga开发
ALINX技术博客3 天前
【黑金云课堂】FPGA技术教程Linux开发:NVMe/Qt/OpenCV人脸检测
linux·qt·fpga开发