1. 学习强化篇

本书读到这里,标志着各位读者已经完成了“基础入门篇”相关章节的学习,笔者要祝贺各位读者,FPGA的学习各位已经入门了。

在上一板块“基础入门篇”中,我们对FPGA的基础知识做了系统讲解,其中包括Verilog语法的学习;组合逻辑、时序逻辑、层次化、状态机、Latch、阻塞赋值与非阻塞赋值的相关知识的介绍;计数器、分频器的具体实现方法;按键、LED灯、蜂鸣器、数码管、红外遥控、温湿度传感器等部分简单外设的使用;还详细讲 解了快速开发法宝 — IP核的配置与使用方法。

接下来,在本版块“学习强化篇”,笔者会通过诸多实例,为读者详细讲解常用通讯接口的相关内容,如“RS232”“RS485”“SPI”“I2C”;关于图像处理的入门知识也会有诸多涉猎,如“VGA”“HDMI”“TFT_LCD”“Sobel算法”;除此之外,常用芯片和传感器的使用也会有所涉及,这部分内容之 所以放在这里,是因为他们的使用涉及到IP核的使用及部分通讯接口,如“RTC实时时钟”“环境光传感器”“WM8978音频芯片”。

“学习强化篇”的学习对上是“基础入门篇”内容的延申;对下是“进阶提高篇”知识的铺垫,读者务必认真掌握。在学习过程中,对“基础入门篇”内容若有遗忘,及时回顾学习。

2. 简易频率计的设计与验证

频率测量在电子设计领域和测量领域经常被使用,因此频率测量方法的学习和掌握是非常有必要的。在本章节,我们将为读者讲解等精度测量法的原理和实现方法,使用FPGA 结合所学知识设计并实现一个简易频率计。

2.1. 理论学习

频率测量在诸多领域都有广泛的应用,常用的频率测量方法有两种,分别是频率测量法和周期测量法。

频率测量法:在时间t内对被测时钟信号的时钟周期N进行计数,然后求出单位时间内的时钟周期数,即为被测时钟信号的时钟频率。

周期测量法:先测量出被测时钟信号的时钟周期T,然后根据频率f = 1/T求出被测时钟信号的频率。

但是上述两种方法都会产生±1个被测时钟周期的误差,在实际应用中有一定的局限性;而且根据两种方式的测量原理,很容易发现频率测量法适合于测量高频时钟信号,而周期测量法适合于低频时钟信号的测量,但二者都不能兼顾高低频率同样精度的测量要求。

等精度测量法与前两种方式不同,其最大的特点是,测量的实际门控时间不是一个固定值,它与被测时钟信号相关,是被测时钟信号周期的整数倍。在实际门控信号下,同时对标准时钟和被测时钟信号的时钟周期进行计数,再通过公式计算得到被测信号的时钟频率。

由于实际门控信号是被测时钟周期的整数倍,就消除了被测信号产生的±1时钟周期的误差,但是会产生对标准时钟信号±1时钟周期的误差。等精度测量原理示意图如图 31‑1所示。

freqme002

图 31‑1 等精度测量原理示意图

结合等精度测量原理和原理示意图可得:被测时钟信号的时钟频率fx的相对误差与被测时钟信号无关;增大“软件闸门”的有效范围或者提高“标准时钟信号”的时钟频率fs,可以减小误差,提高测量精度。

了解了等精度测量原理之后,我们来说明一下被测时钟信号的计算方法。

首先我们先分别对实际闸门下被测时钟信号和标准时钟信号的时钟周期进行计数。

实际闸门下被测时钟信号周期数为X,设被测信号时钟周期为Tfx,它的时钟频率fx = 1/Tfx,由此可得等式:X * Tfx = X / fx = Tx(实际闸门)。

实际闸门下标准时钟信号周期数为Y,设被测信号时钟周期为Tfs,它的时钟频率fs = 1/Tfs,由此可得等式:Y * Tfs = Y / fs = Tx(实际闸门)。

其次,将两等式结合得到只包含各自时钟周期计数和时钟频率的等式:X / fx = Y / fs = Tx(实际闸门),等式变换,得到被测时钟信号时钟频率计算公式:fx = X * fs / Y。

最后,将已知量标准时钟信号时钟频率fs和测量量X、Y带入计算公式,得到被测时钟信号时钟频率fx。

2.2. 实战演练

在理论学习小结,我们对等精度测量的相关知识做了讲解,接下来,我们根据等精度测量原理设计一个简易频率计。

2.2.1. 实验目标

利用所学知识,设计一个基于等精度测量原理的简易频率计,对输入的未知时钟信号做频率测量,并将测量结果在数码管上显示。

要求:标准时钟信号频率为100MHz,实际闸门时间大于或等于1s,目的是减小误差,提高测量精度。

2.2.2. 硬件资源

如图 31‑2所示,使用板卡引出I/O口F14作为模拟测试时钟输出端口;使用板卡引出I/O口F15作为待测试时钟输入端口。

freqme003

图 31‑2 硬件资源

2.2.3. 程序设计

了解了实验要求之后,根据等精度测量相关知识,我们开始实验工程的程序设计。接下来我们将会通过整体说明和分步介绍的方式,对整个实验工程做详细说明,带领读者一步步实现简易频率计的设计。

2.2.3.1. 整体说明

根据实验要求,结合等精度测量相关知识,实验工程整体框架如图 31‑3所示;子功能模块的简要描述,具体见表格 31‑1。

freqme004

图 31‑3 实验工程整体框图

表格 31‑1 子功能模块简介

模块名称

功能描述

freq_meter

顶层模块

clk_test_gen

时钟生成模块,生成待检测时钟

freq_meter_calc

频率计算模块,检测并计算时钟频率

seg_595_dynamic

数码管显示模块,显示时钟频率

由图表可知,本实验工程包括4个子模块,其中频率计算模块(freq_meter_calc)是实验工程的核心模块,他将输入的待检测信号利用等精度测量法进行计算,得出被测时钟信号时钟频率并输出;数码管显示模块(seg_595_dynamic)接收频率计算模块输出的计算结果,并显示在数码管上;被测时钟生成模 块(clk_test_gen)负责产生某一频率的待检测时钟信号;最后的顶层模块(freq_meter),内部将上述3个子功能模块实例化其中,连接各自对应信号,外部输入时钟、复位和待检测信号,输出段选、位选和待检测数据。

数码管显示模块(seg_595_dynamic)的相关内容在前文已经有了详细介绍;被测时钟生成模块(clk_test_gen)为调用IP核生成,关于IP核的调用前面章节也有详细介绍,这两模块的内容不再赘述。

接下来,我们会分别对频率计算模块(freq_meter_calc)和顶层模块(freq_meter)的内容做一下介绍。

注:由频率计算模块输出的测量结果的单位为Hz,为提高频率计测量范围,将结果除以1000后,再传入数码管显示模块,同时数码管小数点左移三位,所以数码管显示结果的单位为MHz;被测时钟生成模块(clk_test_gen)负责产生待检测时钟信号,如有条件的读者可用信号发生器代替该模块,直接输入待检测时钟信 号。

2.2.3.2. 频率计算模块

模块框图

频率计算模块的作用是:使用等精度测量法对输入的待检测时钟信号进行频率测量,并将测量结果输出。频率计算模块框图,具体见图 31‑4;模块输入输出端口描述,具体见表格 31‑2。

freqme005

图 31‑4 频率计算模块框图

模块内部实例化一个时钟生成IP核,负责将50MHz系统时钟信号(sys_clk)倍频生成100MHz标准时钟。

为什么要使用100MHz时钟信号作为标志信号呢?在前文的等精度测量原理中,我们知道,被测时钟信号的时钟频率fx的相对误差与被测时钟信号无关;增大“软件闸门”的有效范围或者提高“标准时钟信号”的时钟频率fs,可以减小误差,提高测量精度。

使用100 MHz时钟作为标准时钟信号,实际闸门时间大于或等于1s,就可使测量的最大相对误差小于或等于10-8,即精度达到1/100 MHz,大大提高了测量精度。

表格 31‑2 模块输入输出信号功能描述

信号

位宽

类型

功能描述

sys_clk

1Bit

Input

系统时钟,频率50MHz

sys_rst_n

1Bit

Input

复位信号。低电平有效

clk_test

1Bit

Intput

待检测时钟信号输入

freq

34Bit

Output

频率测量结果输出

模块有3路输入信号:时钟(sys_clk)、复位(sys_rst_n)和待检测时钟信号(clk_test);有输出信号1路,输出频率测量结果(freq)。

波形图绘制

在模块框图小节,我们已经对本模块做了简单介绍,接下来,我们将通过波形图的绘制,对各信号波形进行详细说明,教会读者等精度测量的实现方法。频率计算模块整体波形图如图 31‑5所示。

freqme006

图 31‑5 频率计算模块整体波形图

结合等精度测量法原理,我们对各信号波形的设计与实现进行详细说明。

第一部分:软件闸门gate_s及相关信号的设计与实现

由等精度测量原理可知,实现等精度测量必不可少的是实际闸门,而实际闸门是由软件闸门得来,所以我们先来生成一下软件闸门。我们计划一个完整周期的软件闸门为1.5s,前0.25s保持低电平,中间1s保持高电平,最后0.25s保持低电平。低电平部分是为了将各计数器清0,并计算待测时钟信号时钟频率;高电平部分就 是软件闸门有效部分,高电平保持1s是为了提高测试精度,在前文已有提及。

软件闸门的生成我们需要声明计数器进行时间计数,计数时钟使用系统时钟sys_clk。声明软件闸门计数器cnt_gate_s,计数时钟为50MHz系统时钟,时钟周期为20ns,计数器cnt_gate_s初值为0,在(0 – CNT_GATE_S_MAX)范围内循环计数。

声明软件闸门gate_s,只有计数器cnt_gate_s计数在((CNT_RISE_MAX+1)-(CNT_GATE_S_MAX-CNT_RISE_MAX))范围内保持有效高电平,高电平保持时间为1s,其他时刻均为低电平。两信号波形图如下。

freqme007

图 31‑6 gate_s、cnt_gate_s信号波形图

第二部分:实际闸门gate_a的设计与实现

生成软件闸门后,使用被测时钟对软件闸门进行同步生成实际闸门gate_a,实际闸门波形图如下。

freqme008

图 31‑7 gate_a信号波形图

第三部分:实际闸门下,标准信号和被测信号时钟计数相关信号的波形设计与实现

在实际闸门下,分别对标准信号和被测信号的时钟周期进行计数。声明计数器cnt_clk_stand,在实际闸门下对标准时钟信号clk_stand进行时钟周期计数;声明计数器cnt_clk_test,在实际闸门下对被测时钟信号clk_test进行时钟周期计数,两计数器波形如下。

freqme009

图 31‑8 cnt_clk_stand、cnt_clk_test信号波形图

计数器cnt_clk_stand、cnt_clk_test在实际闸门下计数完成后,需要进行数据清零,方便下次计数。但是被测时钟频率的计算需要计数器的数据,所以在计数器数据清零之前我们需要将计数器数据做一下寄存,对于数据寄存的时刻,我们选择实际闸门的下降沿。

声明寄存器cnt_clk_stand_reg;在标准时钟信号clk_stand同步下对实际闸门打一拍得到gate_a_s;使用实际闸门gate_a和gate_a_s得到标准时钟下的实际闸门下降沿标志信号gate_a_fall_stand。当gate_afall_stand信号为高电平时,将计数器cn t_clk_stand数值赋值给寄存器cnt_clk_stand_reg。

对于计数器cnt_clk_test的数值寄存,我们使用相同的方法,声明寄存器cnt_clk_test_reg;在被检测时钟信号clk_test同步下对实际闸门打一拍得到gate_a_t;使用实际闸门gate_a和gate_a_t得到被检测时钟下的实际闸门下降沿标志信号gate_a_fall_test 。当gate_a_fall_test信号为高电平时,将计数器cnt_clk_test数值赋值给cnt_clk_test_reg。

上述各信号的信号波形如图 31‑9所示。

freqme010

图 31‑9 标准信号和被测信号时钟计数相关信号波形图

第四部分:计算标志信号calc_flag、频率计算结果freq信号波形的设计与实现

实际闸门下的标准时钟和被测时钟的周期个数已经完成计数,且对结果进行了寄存,标准时钟信号的时钟频率为已知量,得到这些参数,结合公式可以进行频率的求解。同时,新的问题出现,在哪一时刻进行数据求解。

我们可以利用最初声明的软件闸门计数器cnt_gate_s,声明计算标志信号calc_flag,在计数器cnt_gate_s计数到最大值,将calc_flag拉高一个时钟周期的高电平作为计算标志,计算被检测时钟信号时钟频率freq。两信号波形图如下。

freqme011

图 31‑10 calc_flag、freq信号波形图

到了这里,频率计算模块涉及的各信号波形均已设计并实现,经过整合后就得到频率计算模块整体波形图。本模块波形图的设计仅供参考,读者也可按照自己思路设计波形图。

代码编写

波形图各信号讲解完毕,参照绘制的波形图编写模块参考代码。频率计数模块参考代码,具体见代码清单 31‑1。

代码清单 31‑1 频率计算模块参考代码(freq_meter_calc.v)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
 module freq_meter_calc
 (
 input wire sys_clk , //系统时钟,频率50MHz
 input wire sys_rst_n , //复位信号,低电平有效
 input wire clk_test , //待检测时钟

 output reg [33:0] freq //待检测时钟频率

 );
 ////
 //\* Parameter And Internal Signal \//
 ////
 //parameter define
 parameter CNT_GATE_S_MAX = 28'd37_499_999 , //软件闸门计数器计数最大值
 CNT_RISE_MAX = 28'd6_250_000 ; //软件闸门拉高计数值
 parameter CLK_STAND_FREQ = 28'd100_000_000 ; //标准时钟时钟频率
 //wire define
 wire clk_stand ; //标准时钟,频率100MHz
 wire gate_a_fall_s ; //实际闸门下降沿(标准时钟下)
 wire gate_a_fall_t ; //实际闸门下降沿(待检测时钟下)

 //reg define
 reg [27:0] cnt_gate_s ; //软件闸门计数器
 reg gate_s ; //软件闸门
 reg gate_a ; //实际闸门
 reg gate_a_stand ; //实际闸门打一拍(标准时钟下)
 reg gate_a_test ; //实际闸门打一拍(待检测时钟下)
 reg [47:0] cnt_clk_stand ; //标准时钟周期计数器
 reg [47:0] cnt_clk_stand_reg ; //实际闸门下标志时钟周期数
 reg [47:0] cnt_clk_test ; //待检测时钟周期计数器
 reg [47:0] cnt_clk_test_reg ; //实际闸门下待检测时钟周期数
 reg calc_flag ; //待检测时钟时钟频率计算标志信号

 ////
 //\* Main Code \//
 ////
 //cnt_gate_s:软件闸门计数器
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_gate_s <= 28'd0;
 else if(cnt_gate_s == CNT_GATE_S_MAX)
 cnt_gate_s <= 28'd0;
 else
 cnt_gate_s <= cnt_gate_s + 1'b1;

 //gate_s:软件闸门
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 gate_s <= 1'b0;
 else if((cnt_gate_s>= CNT_RISE_MAX)
 && (cnt_gate_s <= (CNT_GATE_S_MAX - CNT_RISE_MAX)))
 gate_s <= 1'b1;
 else
 gate_s <= 1'b0;

 //gate_a:实际闸门
 always@(posedge clk_test or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 gate_a <= 1'b0;
 else
 gate_a <= gate_s;

 //cnt_clk_stand:标准时钟周期计数器,计数实际闸门下标准时钟周期数
 always@(posedge clk_stand or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_clk_stand <= 48'd0;
 else if(gate_a == 1'b0)
 cnt_clk_stand <= 48'd0;
 else if(gate_a == 1'b1)
 cnt_clk_stand <= cnt_clk_stand + 1'b1;

 //cnt_clk_test:待检测时钟周期计数器,计数实际闸门下待检测时钟周期数
 always@(posedge clk_test or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_clk_test <= 48'd0;
 else if(gate_a == 1'b0)
 cnt_clk_test <= 48'd0;
 else if(gate_a == 1'b1)
 cnt_clk_test <= cnt_clk_test + 1'b1;

 //gate_a_stand:实际闸门打一拍(标准时钟下)
 always@(posedge clk_stand or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 gate_a_stand <= 1'b0;
 else
 gate_a_stand <= gate_a;

 //gate_a_fall_s:实际闸门下降沿(标准时钟下)
 assign gate_a_fall_s = ((gate_a_stand == 1'b1) && (gate_a == 1'b0))
 ? 1'b1 : 1'b0;

 //cnt_clk_stand_reg:实际闸门下标志时钟周期数
 always@(posedge clk_stand or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_clk_stand_reg <= 32'd0;
 else if(gate_a_fall_s == 1'b1)
 cnt_clk_stand_reg <= cnt_clk_stand;

 //gate_a_test:实际闸门打一拍(待检测时钟下)
 always@(posedge clk_test or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 gate_a_test <= 1'b0;
 else
 gate_a_test <= gate_a;

 //gate_a_fall_t:实际闸门下降沿(待检测时钟下)
 assign gate_a_fall_t = ((gate_a_test == 1'b1) && (gate_a == 1'b0))
 ? 1'b1 : 1'b0;

 //cnt_clk_test_reg:实际闸门下待检测时钟周期数
 always@(posedge clk_test or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_clk_test_reg <= 32'd0;
 else if(gate_a_fall_t == 1'b1)
 cnt_clk_test_reg <= cnt_clk_test;

 //calc_flag:待检测时钟时钟频率计算标志信号
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 calc_flag <= 1'b0;
 else if(cnt_gate_s == (CNT_GATE_S_MAX - 1'b1))
 calc_flag <= 1'b1;
 else
 calc_flag <= 1'b0;

 //freq:待检测时钟信号时钟频率
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 freq <= 34'd0;
 else if(calc_flag == 1'b1)
 freq <= (CLK_STAND_FREQ / cnt_clk_stand_reg \* cnt_clk_test_reg);

 ////
 //\* Instantiation \//
 ////
 //---------- clk_gen_inst ----------
 clk_gen clk_gen_inst
 (
 .areset (~sys_rst_n ),
 .inclk0 (sys_clk ),

 .c0 (clk_stand )
 );

 endmodule

参考代码编写完成,对于代码的仿真验证,等到顶层模块介绍完毕后,对整体工程进行仿真,不再对本模块进行单独仿真。

2.2.3.3. 顶层模块

模块框图

顶层模块较为简单,内部实例化各子功能模块,连接各自对应信号,顶层模块框图,具体见图 31‑11;外部有3路输入、3路输出,共6路信号。输入为时钟、复位和待检测时钟信号;输出为段选、位选和生成的待检测时钟信号,具体见表格 31‑3。

freqme012

图 31‑11 顶层模块框图

表格 31‑3 模块输入输出信号功能描述

信号

位宽

类型

功能描述

sys_clk

1Bit

Input

系统时钟,频率50MHz

sys_rst_n

1Bit

Input

复位信号。低电平有效

clk_test

1Bit

Intput

待检测时钟信号输入

clk_out

1Bit

Output

输出待检测时钟信号

shcp

1Bit

Output

移位寄存器时钟

stcp

1Bit

Output

数据存储器时钟

ds

1Bit

Output

串行数据输入

oe

1Bit

Output

使能信号

代码编写

顶层模块较为简单,无需波形图的绘制,直接编写顶层模块参考代码。顶层模块参考代码,具体见代码清单 31‑2。

代码清单 31‑2 顶层模块参考代码(freq_meter.v)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
module freq_meter
(
input wire sys_clk , //系统时钟,频率50MHz
input wire sys_rst_n , //复位信号,低电平有效
input wire clk_test , //待检测时钟

output wire clk_out , //生成的待检测时钟
output wire stcp , //输出数据存储寄时钟
output wire shcp , //移位寄存器的时钟输入
output wire ds , //串行数据输入
output wire oe

);

//wire define
wire [33:0] freq ; //计算得到的待检测信号时钟频率

////
//\* Instantiation \//
////
//---------- clk_gen_test_inst ----------
clk_test_gen clk_gen_test_inst
(
.areset (~sys_rst_n ), //复位端口,高电平有效
.inclk0 (sys_clk ), //输入系统时钟

.c0 (clk_out ) //输出生成的待检测时钟信号
);

//------------- freq_meter_calc_inst --------------
freq_meter_calc freq_meter_calc_inst
(
.sys_clk (sys_clk ), //系统时钟,频率50MHz
.sys_rst_n (sys_rst_n ), //复位信号,低电平有效
.clk_test (clk_test ), //待检测时钟

.freq (freq ) //待检测时钟频率
);

//------------- seg_595_dynamic_inst --------------
seg_595_dynamic seg_595_dynamic_inst
(
.sys_clk (sys_clk ), //系统时钟,频率50MHz
.sys_rst_n (sys_rst_n ), //复位信号,低有效
.data (freq/1000 ), //数码管要显示的值
.point (6'b001000 ), //小数点显示,高电平有效
.seg_en (1'b1 ), //数码管使能信号,高电平有效
.sign (1'b0 ), //符号位,高电平显示负号

.stcp (stcp ), //输出数据存储寄时钟
.shcp (shcp ), //移位寄存器的时钟输入
.ds (ds ), //串行数据输入
.oe (oe ) //输出使能信号

);

endmodule

2.2.3.4. RTL视图

使用Quartus II软件对工程进行编译,编译通过后,查看RTL视图如下图 31‑12。由图可知,实验工程的RTL视图与实验整体框图相同,各信号线均已正确连接。

freqme013

图 31‑12 RTL视图

2.2.3.5. 仿真验证

仿真代码编写

顶层模块参考代码编写完成,实验工程通过编译,整个实验工程也已介绍完毕,开始对实验工程进行整体仿真。仿真参考代码如代码清单 31‑3所示。

代码清单 31‑3 顶层模块仿真参考代码(tb_freq_meter.v)

module tb_freq_meter();

//wire define wire stcp ; //输出数据存储寄时钟 wire shcp ; //移位寄存器的时钟输入 wire ds ; //串行数据输入 wire oe ;

//reg define reg sys_clk ; reg sys_rst_n ; reg clk_test ;

//时钟、复位、待检测时钟的生成 initial begin sys_clk = 1’b1; sys_rst_n <= 1’b0; #200 sys_rst_n <= 1’b1; #500 clk_test = 1’b1; end

always #10 sys_clk = ~sys_clk ; //50MHz系统时钟 always #100 clk_test= ~clk_test ; //5MHz待检测时钟

//重定义软件闸门计数时间,缩短仿真时间 defparam freq_meter_inst.freq_meter_calc_inst.CNT_GATE_S_MAX = 240 ; defparam freq_meter_inst.freq_meter_calc_inst.CNT_RISE_MAX = 40 ;

//————- freq_meter_inst ————- freq_meter freq_meter_inst ( .sys_clk (sys_clk ), //系统时钟,频率50MHz .sys_rst_n (sys_rst_n ), //复位信号,低电平有效 .clk_test (clk_test ), //待检测时钟

.clk_out (clk_out ), //生成的待检测时钟 .stcp (stcp ), //输出数据存储寄时钟 .shcp (shcp ), //移位寄存器的时钟输入 .ds (ds ), //串行数据输入 .oe (oe ) );

endmodule

仿真波形分析

使用Modelsim对顶层模块仿真参考代码进行仿真,仿真波形中,我们只查看频率计算模块的各信号仿真波形。频率计算模块整体仿真波形,具体见图 31‑13;频率计算模块局部仿真波形,具体见图 31‑14 ~ 图 31‑20。

由整体和局部仿真波形可以看出,模块仿真波形和绘制波形图,各信号波形变化一致,模块通过仿真验证。

freqme014

图 31‑13 频率计算模块整体仿真波形图

freqme015

图 31‑14 频率计算模块局部仿真波形图(一)

freqme016

图 31‑15 频率计算模块局部仿真波形图(二)

freqme017

图 31‑16 频率计算模块局部仿真波形图(三)

freqme018

图 31‑17 频率计算模块局部仿真波形图(四)

freqme019

图 31‑18 频率计算模块局部仿真波形图(五)

freqme020

图 31‑19 频率计算模块局部仿真波形图(六)

freqme021

图 31‑20 频率计算模块局部仿真波形图(七)

2.3. 上板验证

2.3.1. 引脚约束

仿真验证通过后,准备上板验证,上板验证之前先要进行引脚约束。工程中各输入输出信号与开发板引脚对应关系如表格 31‑4所示。

表格 31‑4 引脚分配表

信号名

信号类型

对应引脚

备注

sys_clk

Input

E1

时钟

sys_rst_n

Input

M15

复位

clk_test

Input

F15

待测试时钟输入端口

clk_out

Output

F14

模拟测试时钟输出端口

stcp

Output

K9

存储寄存器时钟

shcp

Output

B1

移位寄存器时钟

ds

Output

R1

串行数据

oe

Output

L11

输出使能,低有效

下面进行管脚分配,管脚的分配方法在前面章节已有所讲解,在此就不再过多叙述,管脚的分配如下图 31‑21所示。

freqme022

图 31‑21 管脚分配

2.3.1.1. 结果验证

如图 31‑22所示,开发板连接12V直流电源和USB-Blaster下载器JTAG端口;使用短路帽或导线连接F14、F15 I/O口。线路正确连接后,打开开关为板卡上电。

freqme023

图 31‑22 程序下载连线图

如图 31‑23所示,使用“Programmer”为开发板下载程序。

freqme024

图 31‑23 程序下载窗口

由图 31‑24可知,实验工程中模拟输出的待测时钟信号的时钟频率为103.571429MHz。

freqme025

图 31‑24 待测时钟信号时钟频率

如图 31‑25所示,使用短路帽或导线连接I/O口F14和F15,数码管显示的为频率计的时钟频率测量结果,测量值与实际值相同,频率计达到预期效果。

freqme026

图 31‑25 频率计测量结果

2.4. 章末总结

本章节带领读者学习了等精度测量法,并使用此方法设计并实现了简易频率计,对于等精度测量法的内容,希望读者要切实掌握。

2.5. 拓展训练

  1. 更改代码,调节基准时钟频率或软件闸门的保持时间对同一信号进行频率测量,观察测量结果,思考基准时钟频率大小和软件闸门保持时间对测量结果的影响;

2.使用其他两种频率测量法,设计简易频率计,与本实验工程比较测量结果。