[米联客-XILINX-H3_CZ08_7100] FPGA程序设计基础实验连载-08 FPGA多路分频器实验

软件版本:VIVADO2021.1

操作系统:WIN10 64bit

硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA

实验平台:米联客-MLK-H3-CZ08-7100开发板

板卡获取平台:https://milianke.tmall.com/

登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!

目录

1概述

2 硬件电路分析

3程序设计思路

4 RTL仿真

4.1添加仿真测试源码

4.2仿真结果

5测试结果

5.1 在线逻辑分析仪

5.2开发板现象


1概述

在FPGA中,时钟分频是经常用到的。本节课讲解2分频、3分频、4分频和8分频的Verilog实现并且学习generate语法功能的应用,本文的重点是培养读者对于verilog基础编程的基本功掌握。

2 硬件电路分析

硬件接口和子卡模块请阅读“附录 1”

配套工程的 FPGA PIN 脚定义路径为 fpga_prj/uisrc/04_pin/ fpga_pin.xdc。

3程序设计思路

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'd100_000_000
)
(
input I_sysclk_p,
input I_sysclk_n,
input  I_rst_n,
output O_div2,
output O_div3,
output O_div4,
output O_div8,
output O_div2hz
    );

wire I_clk;
IBUFGDS CLK_U(
.I(I_sysclk_p),
.IB(I_sysclk_n),
.O(I_clk)
);


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

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

reg O_div4_r;
reg O_div8_r;
always@(posedge I_clk)begin
	if(!I_rst_n)
		O_div4_r <= 1'b0;
	else if(div_cnt1==2'b00 || div_cnt1==2'b10)
		O_div4_r <= ~O_div4_r;
	else
		O_div4_r <= O_div4_r;
end

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

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

reg O_div3_r0;
reg O_div3_r1;
always@(posedge I_clk)begin
	if(!I_rst_n)
		O_div3_r0 <= 1'b0;
	else if(pos_cnt < 2'd1)
		O_div3_r0 <= 1'b1;
	else
		O_div3_r0 <= 1'b0;
end

always@(negedge I_clk)begin
	if(!I_rst_n)
		O_div3_r1 <= 1'b0;
	else if(neg_cnt < 2'd1)	
		O_div3_r1 <= 1'b1;
	else
		O_div3_r1 <= 1'b0;
end

reg O_div2hz_r;
reg [25:0] div2hz_cnt;

wire ms250_en = (div2hz_cnt == REF_CLK/4 - 1'b1);
always@(posedge I_clk)
begin
	if(!I_rst_n)
		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 I_clk)
begin
	if(!I_rst_n)
		O_div2hz_r <= 0;
	else if(ms250_en)
		O_div2hz_r <= ~O_div2hz_r;
	else
		O_div2hz_r <= O_div2hz_r;
end

assign O_div2 = O_div2_r;
assign O_div3 = O_div3_r0 | O_div3_r1;
assign O_div4 = O_div4_r;
assign O_div8 = O_div8_r;
assign O_div2hz = O_div2hz_r;

generate  if(DEBUG_ENABLE == 1'b1) begin : debugcore
//添加ila IP ,Chipscope观察信号
ila_0 ila_0 (
	.clk(I_clk), // input wire clk
	.probe0(O_div2hz), // input wire [0:0]  probe0  
	.probe1({O_div2,O_div3,O_div4,O_div8}) // input wire [3:0]  probe1
);
end
endgenerate

endmodule	

代码解释:

2分频代码:

只要基于源时钟每个时钟的上升沿对div2_o_r寄存器取反

4分频和8分频代码

共同使用了div_cnt1计数器,4分频就是对计数器在div_cnt1==2'b00或者div_cnt1==2'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倍源时钟周期就是输出低电平了。

2HZ分频代码:

和前面文章中的流水灯延迟控制的分频方法一样。

4 RTL仿真

4.1添加仿真测试源码

仿真测试文件存放在工程目录uisrc\02_sim中,源码如下:

`timescale 1ns / 1ps

module Clk_Divider_Tb();
// Inputs
reg I_sysclk_p;
reg I_sysclk_n;
reg I_rst_n;
// Outputs
wire O_div2;
//wire O_div3;
wire O_div4;
wire O_div8;
wire O_div2hz;

// Instantiate the Unit Under Test (UUT)
Clk_Divider#(
.DEBUG_ENABLE(1'b0),
.REF_CLK(100000000)
) 
Clk_Divider_inst
(
.I_sysclk_p(I_sysclk_p),
.I_sysclk_n(I_sysclk_n),
.I_rst_n(I_rst_n), 
.O_div2(O_div2),
.O_div4(O_div4),
.O_div8(O_div8),
.O_div2hz(O_div2hz)
);

initial begin
// Initialize Inputs4
    I_sysclk_p= 0;
    I_sysclk_n= 1;
    I_rst_n = 0;
// Wait 100 ns for global reset to finish
    #100;
    I_rst_n=1;
end
 
always #5 I_sysclk_p =~I_sysclk_p;
always #5 I_sysclk_n =~I_sysclk_n;

endmodule

4.2仿真结果

3分频、2分频、4分频、8分频

2HZ分频

5测试结果

5.1 在线逻辑分析仪

通过ila在线逻辑分析仪我们也能看出我们的2分频、4分频、8分频信号都分频成功

5.2开发板现象

(该教程为通用型教程,教程中仅展示一款示例开发板的连接方式,具体连接方式以所购买的开发板型号以及结合配套代码管脚约束为准。)

请确保下载器和开发板已经正确连接,另外需要把核心板上的2P模式开关设置到JTAG模式,即ON ON,并且开发板已经上电。(注意JTAG端子不支持热插拔,而USB接口支持,所以在不通电的情况下接通好JTAG后,再插入USB到电脑,之后再上电,以免造成JTAG IO损坏)

如下图所示LED闪烁,闪烁频率2Hz

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值