[米联客-安路飞龙DR1-FPSOC] FPGA基础篇连载-08 FPGA多路分频器实验

软件版本:Anlogic -TD5.9.1-DR1_ES1.1

操作系统:WIN10 64bit

硬件平台:适用安路(Anlogic)FPGA

实验平台:米联客-MLK-L1-CZ06-DR1M90G开发板

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

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

 

1 概述

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

在完成本实验前,请确保已经完成前面的实验,包括已经掌握以下能力:

1:完成了TD软件安装

2:完成了modelsim安装以及TD库的编译

3:掌握了TD仿真环境的设置

4:掌握了modesim通过do文件启动仿真

1.1分频器简介

在数字系统的设计中经常会碰到需要使用多个时钟的情况,这就需要对输入时钟进行分频从而得到多种时钟。时钟信号的产生通常具有两种方法,一种是在 FPGA设计中直接使用PLLPhase Locked Loop锁相环),可生成倍频、分频信号。但是这种方式有时候受限于 PLL 本身特性的限制,比如输入100Mhz 时钟,很多PLL都实现不了1Mhz的时钟分频,这个就是PLL本身特性的。另一种则是使用硬件描述语言构建一个分频电路,简单来说就是根据输入时钟,使用计数器计数输入时钟的上升沿个数或者时钟下降沿个数,根据设置分频数的位置进行输出信号的高低电平翻转,以此实现将输入信号的高频率信号转变为低频率输出信号。本节就是带领大家使用Verilog 代码进行分频器电路的设计。

根据分频器的分频比例(晶振频率/分频后频率)是偶数还是奇数,将分频器分为偶数分频器和奇数分频器。

偶数分频:偶数分频器的设计较为简单,用一个简单的计数器就可以实现。假设为N(偶数)分频,只需设计一个计数器从0计数到N/2-1(一共N/2个基准时钟),然后将输出分频时钟翻转、计数器清零,如此循环就可以得到N分频。例如二分频,每检测到基准时钟一个上升沿/下降沿就翻转时钟。

奇数分频;奇数分频器的设计比偶数分频器复杂一些,一个计数器显然不能记录1/2个基准时钟周期。我们可以通过两个计数器分别对基准时钟的上升沿和下降沿计数,分别得到两个相差一个基准时钟周期的N-1分频时钟,再将上升沿N-1分频时钟和下降沿N-1分频时钟相或(上升沿时钟信号|下降沿时钟信号)即N分频电路。

1.2 硬件电路分析

参照"流水灯"硬件电路分析部分

2 程序设计思路

本次实验通过计数器实现不同时钟的分频。本实验顶层模块命名为Clk_Divider,需要两个输入的端口,分别为系统时钟、系统复位,四个时钟分频的输出。

2分频:

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

4分频和8分频

共同使用了div_cnt1计数器,4分频就是对计数器在两个基准时钟之后,即在div_cnt1==2'b00或者div_cnt1==2'b10的时候,对div4_o_r寄存器取反;而8分频是对计数器在四个基准时钟之后,即div_cnt1==2'b00的时候对o_div8_r取反

3分频:

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

2HZ分频:

和流水灯实验的分频方法一样,通过延时,对寄存器取反操作得到想要的分频时钟。2HZ分频时钟周期为500ms,设置一个ms250_en的信号,计时到250ms,信号拉高,对O_div2hz_r寄存器取反,就可以得到2HZ分频时钟。

Clk_Divider.v文件

 

  1 `timescale 1ns / 1ps
  2 module Clk_Divider# 
  3 (
  4 parameter DEBUG_ENABLE = 1'b1,
  5 parameter REF_CLK      = 32'd25_000_000
  6 )
  7 (
  8 input I_clk,
  9 input  I_rst_n,
 10 output O_div2,
 11 output O_div3,
 12 output O_div4,
 13 output O_div8,
 14 output O_div2hz
 15     );
 16 
 17 //2分频代码:只要基于源时钟每个时钟的上升沿对O_div2_r寄存器取反    
 18 reg O_div2_r;
 19 always@(posedge I_clk)begin
 20     if(!I_rst_n)
 21         O_div2_r <= 1'b0;
 22     else 
 23         O_div2_r <= ~O_div2_r;
 24 end
 25 
 26 //4分频和8分频代码:共同使用了div_cnt1计数器
 27 //4分频就是对计数器在div_cnt1==2'b00或者div_cnt1==2'b10的时候对O_div4_r寄存器取反;
 28 //而8分频是对div_cnt1==2'b00的时候对O_div8_r取反
 29 reg [1:0] div_cnt1;
 30 always@(posedge I_clk)begin
 31     if(!I_rst_n)
 32         div_cnt1 <= 2'b00;
 33     else
 34         div_cnt1 <= div_cnt1+1'b1;
 35 end
 36 
 37 reg O_div4_r;
 38 reg O_div8_r;
 39 always@(posedge I_clk)begin
 40     if(!I_rst_n)
 41         O_div4_r <= 1'b0;
 42     else if(div_cnt1==2'b00 || div_cnt1==2'b10)
 43         O_div4_r <= ~O_div4_r;
 44     else
 45         O_div4_r <= O_div4_r;
 46 end
 47 
 48 always@(posedge I_clk)begin
 49     if(!I_rst_n)
 50         O_div8_r <= 1'b0;
 51     else if(div_cnt1==2'b00)
 52         O_div8_r <= ~O_div8_r;
 53     else
 54         O_div8_r <= O_div8_r;
 55 end
 56 /*
 57 3分频的本质是我们需要在每次1.5倍的时钟周期的时候实现3分频寄存器的翻转,但是我们无法直接实现1.5倍的分频。
 58 因此采取分别采取2个计数器pos_cnt和neg_cnt,分别对上升沿和下降沿计数。计数周期是0-1-2,共计3个时钟周期。
 59 我们取pos_cnt == 2'd1的时候O_div3_r0输出高电平,neg_cnt == 2'd1的时候O_div3_r1输出高电平。
 60 由于O_div3_r0和O_div3_r1输出1个时钟的高电平,但是相位相差180°,因此只要执行O_div3 = O_div3_r0 | O_div3_r1运算,
 61 就能实现1.5倍周期的输出高电平,那么剩余的1.5倍源时钟周期就是输出低电平了。
 62 */
 63 reg [1:0] pos_cnt;
 64 reg [1:0] neg_cnt;
 65 always@(posedge I_clk)begin
 66     if(!I_rst_n)
 67         pos_cnt <= 2'b00;
 68     else if(pos_cnt == 2'd2)
 69         pos_cnt <= 2'b00;
 70     else
 71         pos_cnt <= pos_cnt + 1'b1;
 72 end
 73 
 74 always@(negedge I_clk)begin
 75     if(!I_rst_n)    
 76         neg_cnt <= 2'b00;
 77     else if(neg_cnt == 2'd2)
 78         neg_cnt <= 2'b00;
 79     else
 80         neg_cnt <= neg_cnt + 1'b1;
 81 end
 82 
 83 reg O_div3_r0;
 84 reg O_div3_r1;
 85 always@(posedge I_clk)begin
 86     if(!I_rst_n)
 87         O_div3_r0 <= 1'b0;
 88     else if(pos_cnt < 2'd1)
 89         O_div3_r0 <= 1'b1;
 90     else
 91         O_div3_r0 <= 1'b0;
 92 end
 93 
 94 always@(negedge I_clk)begin
 95     if(!I_rst_n)
 96         O_div3_r1 <= 1'b0;
 97     else if(neg_cnt < 2'd1) 
 98         O_div3_r1 <= 1'b1;
 99     else
100         O_div3_r1 <= 1'b0;
101 end
102 
103 reg O_div2hz_r;
104 reg [25:0] div2hz_cnt;
105 
106 wire ms250_en = (div2hz_cnt == REF_CLK/4 - 1'b1);
107 always@(posedge I_clk)
108 begin
109     if(!I_rst_n)
110         div2hz_cnt <= 0;
111     else if(div2hz_cnt < REF_CLK/4 - 1'b1)
112         div2hz_cnt <= div2hz_cnt + 1'b1;
113     else
114         div2hz_cnt <= 0;
115 end
116 
117 always@(posedge I_clk)
118 begin
119     if(!I_rst_n)
120         O_div2hz_r <= 0;
121     else if(ms250_en)
122         O_div2hz_r <= ~O_div2hz_r;
123     else
124         O_div2hz_r <= O_div2hz_r;
125 end
126 
127 assign O_div2 = O_div2_r;
128 assign O_div3 = O_div3_r0 | O_div3_r1;
129 assign O_div4 = O_div4_r;
130 assign O_div8 = O_div8_r;
131 assign O_div2hz = O_div2hz_r;
132 
133 generate  if(DEBUG_ENABLE == 1'b1) begin : debugcore
134 //添加ila IP ,Chipscope观察信号
135 ila_0 ila_0 (
136     .clk(I_clk), // input wire clk
137     .probe0(O_div2hz), // input wire [0:0]  probe0  
138     .probe1({O_div2,O_div3,O_div4,O_div8}) // input wire [3:0]  probe1
139 );
140 end
141 Endgenerate
142 
143 endmodule

 

 

3 FPGA工程

fpga工程的创建过程不再重复,如有不清楚的请看前面实验,具体的FPGA型号以对应的开发板上芯片为准

米联客的代码管理规范,在对应的FPGA工程路径下创建uisrc路径,并且创建以下文件夹

01_rtl:放用户编写的rtl代码

02_sim:仿真文件或者工程

03_ip:放使用到的ip文件

04_pin:fpgapin脚约束文件或者时序约束文件

05_boot:放编译好的bit或者bin文件(一般为空)

06_doc:放本一些相关文档(一般为空)

 

4 Modelsim仿真

4.1准备工作

Modelsim仿真的创建过程不再重复,如有不清楚的请看前面实验

仿真测试文件源码如下,对仿真精度没什么要求的,可以设置为1ns,另外仿真阶段可以把间隔时间设置小一些,这样仿真速度就快了:

 

 1 `timescale 1ns / 1ps
 2 module sim_top_tb();
 3 // Inputs
 4 reg I_sysclk;
 5 reg I_rst_n;
 6 // Outputs
 7 wire O_div2;
 8 //wire div3_o;
 9 wire O_div4;
10 wire O_div8;
11 wire O_div2hz;
12 
13 // Instantiate the Unit Under Test (UUT)
14 Clk_Divider#(
15 .DEBUG_ENABLE(1'b0),
16 .REF_CLK(1000)
17 ) 
18 Clk_Divider_inst
19 (
20 .I_sysclk(I_sysclk),
21 .I_rst_n(I_rst_n), 
22 .O_div2(O_div2),
23 .O_div4(O_div4),
24 .O_div8(O_div8),
25 .O_div2hz(O_div2hz)
26 );
27 
28 initial begin
29 // Initialize Inputs4
30     I_sysclk= 0;
31     I_rst_n = 0;
32 // Wait 100 ns for global reset to finish
33     #100;
34     I_rst_n=1;
35 end
36 always #10 I_sysclk =~I_sysclk;
37 endmodule

 

 

 

 

4.2启动modelsim仿真

启动后,右击需要观察的信号,添加到波形窗口

设置restart

设置运行500ms(如果运行时间太长可以修改小一些)

从仿真结果可以看到PWM占空比的调整

5测试结果

5.1 开发板现象

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

请确保下载器和开发板已经正确连接,并且开发板已经上电。(注意JTAG端子不支持热插拔,而USB接口支持,所以在不通电的情况下接通好JTAG后,再插入USB到电脑,之后再上电,以免造成JTAG IO损坏)

 

接通电源后LED闪烁,闪烁频率2Hz

posted @ 2024-07-29 14:30  米联客(milianke)  阅读(7)  评论(0编辑  收藏  举报