等精度频率计的原理分析和实例实现
这一视频主要来分析实现等精度频率计。通过分析硬件实现原理,编写
PL的逻辑实现部分,以及对应 PS的 C语言操作,操作挂在 PS寻址空间的寄存器,
达到控制等精度频率计逻辑,读取计数结果,并在 PS里通过运算,计算出实际
所要测试的频率值。这是一个很好的软件硬件分工协作的例子。硬件逻辑中有精
确到时钟边缘的失序,而软件运算为了提高精度使用了双精度浮点运算。学习这
一视频希望大家在掌握等精度原理和进一步熟悉软件操作的基础上体会如何下
手分析一个设计,进行软件硬件的分工,并最终实现所有细节。
等精度说的就是精度相等,也就是测试出来的精度取决于参考时钟的精度。
他是使用两个计数器同时计数,两个计数器的时钟分别是参考时钟和被测试信号
的时钟。同时【注】开启计数器和同时关闭计数器,运行同样长的时间。
Cref\Fref = Tref
Cin\Fin = Tin
Tref = Tin
所以
Cref\Fref =Cin\Fin
得到
Fin = ( Fref * Cin ) / Cref ;

分析框图,看看具体实现有什么说法:我们看到两个计数器和一个使能控制
寄存器。这两个计数器分别接参考时钟和被测试的信号,在这里被测试信号被当
做一个时钟,在每一个被测信号上升边缘
做一个时钟,在每一个被测信号上升边缘+1。他们是受同一个使能信号控制的,。他们是受同一个使能信号控制的,只有使能信号为只有使能信号为1时候才允许累加。这是基本原理。时候才允许累加。这是基本原理。
控制的时候主控部分输出使能信号(预制门),在被采集信号的上升边缘到
控制的时候主控部分输出使能信号(预制门),在被采集信号的上升边缘到来以后两来以后两个计数器再以自己时钟频率计数。当我们要读取两个计数器的时候,就个计数器再以自己时钟频率计数。当我们要读取两个计数器的时候,就先关闭掉使能信号。在被采集信号的上升边缘到来后两个计数器停止累加采集。先关闭掉使能信号。在被采集信号的上升边缘到来后两个计数器停止累加采集。这样我们读出两个计数器的数值,结合参考频率运算出被测试信号的频率数值。这样我们读出两个计数器的数值,结合参考频率运算出被测试信号的频率数值。而下次控制采集之前,要先清除一下当前的计数器数值,通过清零信号置位。而下次控制采集之前,要先清除一下当前的计数器数值,通过清零信号置位。
我们继续看看一下使能信号的产生,一个外部控制信号出来接在一个同步寄
我们继续看看一下使能信号的产生,一个外部控制信号出来接在一个同步寄存器上,这个寄存器的时钟是被采集信号,这点很重要,也是设计的精髓:我们存器上,这个寄存器的时钟是被采集信号,这点很重要,也是设计的精髓:我们知道是在时钟的上升边缘寄存器知道是在时钟的上升边缘寄存器D的输入更新到的输入更新到Q的输出,看输出波形。我们的输出,看输出波形。我们看下面这个波形图。经过了被采集信号同看下面这个波形图。经过了被采集信号同步的使能信号,去控制被采集时钟的计步的使能信号,去控制被采集时钟的计数器是完全同步,去控制参考时钟的计数器时,会存在最多一个被采集周期的偏数器是完全同步,去控制参考时钟的计数器时,会存在最多一个被采集周期的偏差,可能计数多累加了一个,也可能计数少累加了一个,但是最多就是一个。而差,可能计数多累加了一个,也可能计数少累加了一个,但是最多就是一个。而这一个周期我们可以提高参考时钟频率或提高采集时间的长度来减小这点误差这一个周期我们可以提高参考时钟频率或提高采集时间的长度来减小这点误差的影响,达到可以忽略的要求。的影响,达到可以忽略的要求。
这里还要注意
这里还要注意clr(清零)这个信号,主要看看同步清零和异步清零。写法(清零)这个信号,主要看看同步清零和异步清零。写法以及这里需要用到异步清零。以及这里需要用到异步清零。
用
用verilog语言描述这部分。(分析对应)。语言描述这部分。(分析对应)。
module frc_cal(
input ref_clk,
input sig_clk,
output reg [31:0] ref_c ,
output reg [31:0] sig_c ,
input clr,
input cal_en
);
reg cal_enr ;
always @ (posedge sig_clk or posedge clr) if (clr) cal_enr <= 0;else cal_enr <= cal_en ;
always @ (posedge ref_clk or posedge clr)if (clr)ref_c<=0;else if (cal_enr)ref_c <= ref_c + 1 ;
always @ (posedge sig_clk or posedge clr)if (clr)sig_c<=0;else if (cal_enr)sig_c <= sig_c + 1 ;
endmodule

之后我们做成
AXI总线外设,注意分配寄存器,两个计数器分别占据两个
寄存器,(清零)和(预置门)分别设置一个寄存器,只用到最低位,端口引出。
在
Vivado挂在 Zynq7的总线上分配地址,生成 Bit流文件。之后在 SDK编写 C语言控制程序。
实现过程
1.使用提供的 axi_lite从属外设模板和自定义 IP源代码来创建自定义 IP。
打开
Vivado软件,单击 Manage IP,然后选择 New IP Location,然后在新建 IP位置窗口中单击 Next。
选择Verilog作为 Target Language Mixed作为 Simulator language,对于IP位置,请键入 D:/IP Core,然后单击 Finish(将其他设置保留为默认值,如果
提示创建目录,请单击确定) 。

2.运行创建和封装 IP向导 选择 Tools > Create and Package New IP…

在下一个窗口中,单击 Next

由于我们需要挂在到总线上,因此创建一个带
AXI 总线的用户 IP,故选择 Create a new AXI4 peripheral。 点击 Next.

设置IP 的名字为 Frq_Cnt,版本号默认,并且记住 IP的位置 ,单击 Next.

设置总线形式为Lite 总线, Lite 总线是简化的 AXI 总线消耗的资源少,当然性能也是比完全版的 AXI 总线差一点, 4 采用 Lite 总线就够了,设置寄存器数量为 4,因为后面我们需要用到 4个寄存器。 单击 Next.

选择edit IP 单击 Finish 完成

完成后的界面如下图所示
3.用户 IP 的修改
IP创建完成后,并不能立马使用,还需要做一些修改。
- 打开Feq_Cnt_v1_0.v 文件在以下位置修改

2.打开PWM_IP_v1_0_S00_AXI.v 添加端口和用户逻辑部分


新建一个frc_cal.v 文件实现等精度频率计然后保存到Frq_Cnt_1.0/hdl 文件夹,并添加进来。
module frc_cal(
input ref_clk,
input sig_clk,
output reg [31:0] ref_c ,
output reg [31:0] sig_c ,
input clr,
input cal_en
);
reg cal_enr ;
always @ (posedge sig_clk or posedge clr)if (clr) cal_enr <= 0;else cal_enr <= cal_en ;
always @ (posedge ref_clk or posedge clr)if (clr)ref_c<=0;else if (cal_enr)ref_c <= ref_c + 1 ;
always @ (posedge sig_clk or posedge clr)if (clr)sig_c<=0;else if (cal_enr)sig_c <= sig_c + 1 ;
endmodule
点击File–>点击 Save all files,最终如下

4.修改完成后还要重新打包
1 选择 tool–>Create and Package New Ip…
2. 选择 package your current project 选择 next

3.保持默认设置,不做任何修改,点击 Next

4.点击 Next 选择 Overwrite

5.点击 Finish,完成。

6.执行以下操作检查 IP是否封装完成,展开 IP XACT(1)>双击
component.xml,展开 Ports and Interface,可以看到封装 IP完成。

至此,创建用户IP完成。
5添加 Frq_Cnt_v1_0 IP
重新建立一个新的空的工程。
Create Block Design 直接添加 zynq7 processing system。这个前面的教程内容部分已经重复很多次了,这里不重复。
1.在进程导航窗格中,单击 Project Setting选项,选择 IP,然后单击 Add Repository 按钮。 浏览窗口打开,浏览到 IP核的位置
(
D:/IP Core/ip_repo/Frq_Cnt_1.0 然后单击“ Select”,单击 OK。

3. 注意工具如何在目录中检测到新的 IP,点击 Apply,然后 OK。

这样做后,就可以将
Frq_Cnt_1.0添加到当前项目的 IP库中,下一步是将
其添加到块设计中 ,为了此次试验效果,本实验还添加了 OLED IP核,添加方法
同上。
4. 由于前面的过程非常详细,很多步骤省略,搭建完成后的系统如下图所示

5.在源窗格中,选择系统框图 system.bd””,右击并选择 Generate Output Products,默认设置,直接点 generate,运行结束后,点击 OK。


6.在源窗格中,选择系统框图 system.bd””,右击并选择 Create HDL Wrapper
选择第二项 Let Vivado manage Wrapper and auto update,点击 OK


添加约束文件,如下:
set_property PACKAGE_PIN U10 [get_ports DC]
set_property PACKAGE_PIN U9 [get_ports RES]
set_property PACKAGE_PIN AB12 [get_ports SCLK]
set_property PACKAGE_PIN AA12 [get_ports SDIN]
set_property PACKAGE_PIN U11 [get_ports VBAT]
set_property PACKAGE_PIN U12 [get_ports VDD]
set_property IOSTANDARD LVCMOS33 [get_ports DC]
set_property IOSTANDARD LVCMOS33 [get_ports RES]
set_property IOSTANDARD LVCMOS33 [get_ports SCLK]
set_property IOSTANDARD LVCMOS33 [get_ports SDIN]
set_property IOSTANDARD LVCMOS33 [get_ports VBAT]
set_property IOSTANDARD LVCMOS33 [get_ports VDD]
set_property PACKAGE_PIN Y9 [get_ports {clk}]
set_property IOSTANDARD LVCMOS33 [get_ports {clk}]
set_property PACKAGE_PIN Y11 [get_ports {sig_in}]
set_property IOSTANDARD LVCMOS33 [get_ports {sig_in}]
set_property PACKAGE_PIN AA11 [get_ports {CLK1M_OUT}]
set_property IOSTANDARD LVCMOS33 [get_ports {CLK1M_OUT}]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets sig_in_IBUF]
- 在 Flow Navigator窗口中的 Program and Debug部分, 单击 Generate Bitstream 将出现一个对话框,要求保存所做的修改,单击 Save保存; 也可能
会出现一个对话框,显示“ No Implementation Result Available”,单击 Yes运行 综
合 和实现。 生成比特流可能会在 综合 后 执行 整个实现过程,单击是以在出现提
示时运行实现。 实现 可能需要一段时间才能完成,具体取决于 计算机 的性能。
8.点击 File > Export > Export Hardware, 确保勾选 Include bitstream 选项

9.选择 File>Launch SDK,打开 SDK,注意所有与设计相关的文件(包
括 IP)已经在上一步导出到 SDK中,导出的资源中包括 ZedboardOLED_v1_0 IP核的驱动。
SDK 工程部分的 C 工程新建工程也不详细讲解,前面已经重复很多次了,
这里只给出 C 代码略作分析。
#include <stdio.h>
#include "sleep.h"
#include "xil_io.h"
#include "oled.h"
//void print(char *str);
#define REG_Cin *( volatile unsigned int *) (0x43C10000 + 4 )
#define REG_Cref *( volatile unsigned int *) (0x43C10000 + 0 )
#define REG_CLR *( volatile unsigned int * ) (0x43C10000 + 8 )
#define REG_EN *( volatile unsigned int *) (0x43C10000 + 12 )
void START_CNTR(void) {
REG_CLR = 1 ;
REG_CLR = 0 ;
REG_EN = 1 ;
}
void STOP_CNTR (){
REG_EN = 0 ;
}
void INIT_CNTR(){
REG_EN = 0 ;
REG_CLR = 1 ;
REG_CLR = 0 ;
}
int main()
{
char s [30] ;
Xil_Out32(OLED_BASE_ADDR,0xff);
OLED_Init(); //初始化液晶初始化液晶
OLED_ShowString(0,16, "BY SYSCLK.COM");
double Fref,Fin,Cref,Cin
Fref = 100000000.0000 ;
INIT_CNTR();
while(1)
{
START_CNTR() ;
sleep(7) ;
STOP_CNTR();
Cin = (double )REG_Cin * 1.00 ;
Cref =(double ) REG_Cref *1.00 ;
Fin = ( Fref * Cin ) / Cref ;
sprintf(s,"%lf",(double )Fin);
OLED_ShowString(0,0,(const u8* ) s);
OLED_Refresh_Gram();
printf("%s\n",s);
}
return 0;
}
下面下载到板子验证
,本次试验,我没看到应有的现象,重在过程。(希望下面
做的同学给予意见)。
















