PLL 的 verilog 实现

  锁相环(PLL)是一种常用的频率、相位追踪算法,在信号解调、交流并网等领域有着广泛的应用。本文对全数字锁相环的原理进行介绍,随后给出 verilog 实现及仿真。

PLL 锁相原理

  锁相环结构如下图所示,主要由鉴相器、环路滤波器、压控振荡器等构成

在这里插入图片描述

其中鉴相器是一个乘法器,设参考信号 u i u_i ui 、本地信号 u o u_o uo 均为正弦信号
u i ( t ) = c o s ( ω 1 t + φ 1 ) u_i(t)=cos(\omega_1 t+\varphi_1) ui(t)=cos(ω1t+φ1)

u o ( t ) = c o s ( ω 2 t + φ 2 ) u_o(t)=cos(\omega_2 t+\varphi_2) uo(t)=cos(ω2t+φ2)

  根据积化和差公式, u i u_i ui u o u_o uo 的乘积将包含 ω 1 + ω 2 \omega_1+\omega_2 ω1+ω2 ω 1 − ω 2 \omega_1-\omega_2 ω1ω2 两个频率分量,经过 LF 低通滤波后,将仅剩两者的差频信号
u c = c o s [ ( ω 1 − ω 2 ) t + ( φ 1 − φ 2 ) ] = c o s [ 2 π ( f 1 − f 2 ) t + ( φ 1 − φ 2 ) ] \begin{aligned} u_c&=cos[(\omega_1-\omega_2)t+(\varphi_1-\varphi_2)]\\ &=cos[2\pi(f_1-f_2)t+(\varphi_1-\varphi_2)] \end{aligned} uc=cos[(ω1ω2)t+(φ1φ2)]=cos[2π(f1f2)t+(φ1φ2)]
使用 f 2 = f 0 + K 0 u c f_2=f_0+K_0 u_c f2=f0+K0uc 控制压控振荡器(数字式的一般用 DDS 技术生成)的频率,即可完成锁相。

  假设输入信号相对于基准频率 f 0 f_0 f0 存在 Δ f \Delta f Δf 的频率偏差,则完成锁相后两信号将具有固定的相位偏差 Δ φ \Delta \varphi Δφ,关系如下
Δ f = K 0 c o s ( Δ φ ) \Delta f=K_0cos(\Delta \varphi) Δf=K0cos(Δφ)
当然也应当注意到这里的 Δ φ \Delta \varphi Δφ 符号无法被确定。

verilog 实现

  PLL 模块主程序如下

/* * file			: ADPLL.v* author		: 今朝无言* lab		    : WHU-EIS-LMSWE* date			: 2023-08-03* version		: v1.0* description	: 锁相环* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
module ADPLL(
input						clk,
input						rst_n,input		signed	[15:0]	A,		//参考信号
input		signed	[15:0]	B,		//本地信号output		signed	[15:0]	df		//频偏
);parameter	CLK_FREQ	= 1_000_000;	//采样频率reg signed	[15:0]	df	= 16'd0;//-----------------------multi---------------------------------
reg	signed	[31:0]	multi	= 32'd0;always @(posedge clk) beginif(~rst_n) beginmulti	<= 32'd0;endelse beginmulti	<= A*B;end
end//------------------------FIR---------------------------------
wire	signed	[15:0]	multi_filt  [1:3];localparam	FIR_N = 20;	//FIR阶数wire	[16*(FIR_N+1)-1:0]	FIR_params;FIR_params_0d1 FIR_params_inst(.params		(FIR_params)
);wire    clk_div10;
wire    clk_div100;clkdiv #(.N(10)) clkdiv10(.clk_in     (clk),.clk_out    (clk_div10)
);clkdiv #(.N(100)) clkdiv100(.clk_in     (clk),.clk_out    (clk_div100)
);//低通滤波						多级低通滤波,中间穿插下采样
FIR_filter #(.N(FIR_N + 1))
FIR_filter_inst1(.clk			(clk),.rst_n			(rst_n),.filter_params	(FIR_params),.data_in		(multi[31:16]),.data_out		(multi_filt[1])
);//低通滤波
FIR_filter #(.N(FIR_N + 1))
FIR_filter_inst2(.clk			(clk_div10),.rst_n			(rst_n),.filter_params	(FIR_params),.data_in		(multi_filt[1]),.data_out		(multi_filt[2])
);//低通滤波
FIR_filter #(.N(FIR_N + 1))
FIR_filter_inst3(.clk			(clk_div100),.rst_n			(rst_n),.filter_params	(FIR_params),.data_in		(multi_filt[2]),.data_out		(multi_filt[3])
);//---------------------control---------------------------------
always @(posedge clk_div100) begindf	<= multi_filt[3];		//  df=K*multi_filt,此处省略鉴相灵敏度K,外部请自行设置合理的K值s
endendmodule

  低通滤波器及其参数代码如下

/* * file         : FIR_filter.v* author       : 今朝无言* lab		    : WHU-EIS-LMSWE* date		    : 2023-07-03* version      : v1.0* description  : FIR 滤波器*/
module FIR_filter(
input							clk,
input							rst_n,input				[16*N-1:0]	filter_params,input		signed	[15:0]		data_in,
output	reg	signed	[15:0]		data_out
);parameter	N		= 32;	//滤波器参数个数
parameter	div_N	= 16;	//sum结果除 2^div_N,作为 filter 的输出//FIR 滤波器参数
reg	signed	[15:0] b[0:N-1];integer	m;
always @(*) beginfor(m=0; m<N; m=m+1) beginb[m]	<= filter_params[(m << 4) +: 16];end
endreg	signed	[15:0]	shift_reg[0:N-1];integer	i;
always @(posedge clk) beginif(~rst_n) beginfor(i=N-1; i>=0; i=i-1) beginshift_reg[i]	<= 16'd0;endendelse beginfor(i=N-1; i>0; i=i-1) beginshift_reg[i]	<= shift_reg[i-1];endshift_reg[0]		<= data_in;end
endreg		signed	[31:0]	multi[0:N-1];integer	j;
always @(*) beginfor(j=0; j<N; j=j+1) beginmulti[j]	<= shift_reg[j] * b[j];//这里可以考虑使用multiplier IP核,使用LUT搭建(而这里直接乘使用的是DSP资源,一般的FPGA芯片只有几百个)end
endreg		signed	[47:0]	sum;integer	k;
always @(*) beginsum		= 0;for(k=0; k<N; k=k+1) beginsum	= sum + multi[k];end
endalways @(posedge clk) begindata_out	<= sum[47-div_N : 32-div_N];
endendmodule
/* * file			: FIR_params.v* author		: 今朝无言* lab			: WHU-EIS-LMSWE* date			: 2023-08-04* version		: v1.0* description	: FIR 滤波器    lowpass   N=20   fc=0.1 fs*/
module FIR_params_0d1(
output	[335:0]	params
);assign	params[15:0]	= 16'h0000;
assign	params[31:16]	= 16'h0057;
assign	params[47:32]	= 16'h0131;
assign	params[63:48]	= 16'h0302;
assign	params[79:64]	= 16'h0616;
assign	params[95:80]	= 16'h0A6D;
assign	params[111:96]	= 16'h0FA8;
assign	params[127:112]	= 16'h1518;
assign	params[143:128]	= 16'h19E1;
assign	params[159:144]	= 16'h1D28;
assign	params[175:160]	= 16'h1E53;
assign	params[191:176]	= 16'h1D28;
assign	params[207:192]	= 16'h19E1;
assign	params[223:208]	= 16'h1518;
assign	params[239:224]	= 16'h0FA8;
assign	params[255:240]	= 16'h0A6D;
assign	params[271:256]	= 16'h0616;
assign	params[287:272]	= 16'h0302;
assign	params[303:288]	= 16'h0131;
assign	params[319:304]	= 16'h0057;
assign	params[335:320]	= 16'h0000;endmodule

关于 FIR 滤波器这部分可以参考我之前的博文。

仿真

  仿真测试代码如下

`timescale 100ns/1nsmodule PLL_tb();reg		clk_1M	= 1'b1;
always #5 beginclk_1M	<= ~clk_1M;
endreg		rst_n	= 1'b1;//---------------------参考信号A-------------------------------
wire			[15:0]	A_out_tmp;
wire	signed	[15:0]	A_out;		//参考信号localparam	f0	= 24'd10_000;
localparam	df	= -24'd9;		//频率偏差DDS #(.Freq(1_000_000)
)
DDS_inst1(.clk		(clk_1M),.rst_n		(rst_n),.fout		(f0+df),.phase0		(16'd0),.sin_out	(A_out_tmp)
);assign	A_out	= A_out_tmp - 16'd32768;//---------------------本地信号B-------------------------------
wire			[15:0]	B_out_tmp;
wire	signed	[15:0]	B_out;wire	signed	[23:0]	df2;		//控制本地信号的频偏DDS #(.Freq		(1_000_000)
)
DDS_inst2(.clk		(clk_1M),.rst_n		(rst_n),.fout		(f0+df2),.phase0		(16'd0),.sin_out	(B_out_tmp)
);assign	B_out	= B_out_tmp - 16'd32768;//-----------------------PLL---------------------------------
wire	signed	[15:0]	df_PLL;ADPLL #(.Freq		(1_000_000)
)
PLL_inst(.clk		(clk_1M),.rst_n		(rst_n),.A			(A_out),		//参考信号.B			(B_out),		//本地信号.df			(df_PLL)		//频偏
);assign	df2	= df_PLL/64;//-----------------------tb---------------------------------
initial beginrst_n	<= 1'b0;#5000;rst_n	<= 1'b1;#100;#1000000;$stop;
endendmodule

  DDS 代码如下

/* * file			: DDS.v* author		: 今朝无言* Lab			: WHU-EIS-LMSWE* date			: 2023-05-17* version		: v1.0* description	: 根据给定频率输出正弦信号* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
module DDS(
input			clk,
input			rst_n,input	[23:0]	fout,		//输出正弦波的频率  1k-10M 要24位
input	[15:0]	phase0,		//初相output	[15:0]	sin_out
);parameter	Freq	= 100_000_000;		//clk频率,Hz//-----------------相位累加器-----------------------
reg		[47:0]	int_f_16	= 48'd0;	//相位累加器,x-16定点数
wire	[55:0]	dphi_16;				//相位步进//dphi*Freq=fout*T, T=65536
assign	dphi_16	= (fout << 32)/Freq;always @(posedge clk or negedge rst_n) beginif(~rst_n) beginint_f_16	<= 48'd0;endelse beginint_f_16	<= int_f_16 + dphi_16;end
end//-----------------正弦查找表-----------------------
wire	[15:0]	phase;sin_gen sin_gen_inst(.clk		(clk),.phase		(phase),		//相位.sin_out	(sin_out)
);assign phase	= phase0 + (int_f_16 >> 16);endmodule

相应的正弦查找表如下(该模块使用线性插值的方法,在仅少量增加资源消耗的情况下,将量化误差缩小了两个数量级;这部分也可详见我之前的博文)

/* * file			: sin_gen.v* author		: 今朝无言* Lab			: WHU-EIS-LMSWE* date			: 2023-05-17* version		: v1.0* description	: 根据给定相位输出正弦信号* Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.*/
module sin_gen(
input			clk,input	[15:0]	phase,		//相位,0~65535对应[0~2pi)
output	[15:0]	sin_out
);//---------------------正弦查找表-------------------------
wire	[7:0]	addr1;
wire	[7:0]	addr2;
wire	[15:0]	sin_dat1;
wire	[15:0]	sin_dat2;//sin rom, 16bit, 256 depth
sin_rom sin_rom_inst1(.clka	(clk),.addra	(addr1),.douta	(sin_dat1)
);sin_rom sin_rom_inst2(.clka	(clk),.addra	(addr2),.douta	(sin_dat2)
);//-----------线性插值获取更精确的相位分辨率-------------------
assign	addr1	= (phase>>8);
assign	addr2	= (phase>>8)+1;wire	[15:0]	phase1;
wire	[15:0]	phase2;assign	phase1	= addr1<<8;
assign	phase2	= addr2<<8;reg		[15:0]	phase_d0;
reg		[15:0]	phase_d1;	//由于rom数据2拍后才给出,因此phase需要与之同步
reg		[15:0]	phase1_d0;
reg		[15:0]	phase1_d1;always @(posedge clk) beginphase_d0	<= phase;phase_d1	<= phase_d0;phase1_d0	<= phase1;phase1_d1	<= phase1_d0;
endwire	[31:0]	multi;
assign	multi	= (sin_dat2 > sin_dat1)? (sin_dat2 - sin_dat1)*(phase_d1 - phase1_d1) : (sin_dat1 - sin_dat2)*(phase_d1 - phase1_d1);assign	sin_out	= (sin_dat2 > sin_dat1)? sin_dat1 + (multi >> 8) : sin_dat1 - (multi >> 8);endmodule

  仿真结果如下

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/62445.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

交换机的堆叠技术

目录 一、堆叠的优势 1、提高可靠性 2、简化组网 3、简化管理 4、强大的网络拓展 二、堆叠的方式 1、堆叠卡堆叠 2、业务口堆叠 3、堆叠卡和业务卡堆叠的优缺点 三、堆叠的原理 1、角色 2、单机堆叠 3、堆叠ID 4、堆叠的优先级 5、堆叠的建立过程 1&#xff09…

【算法】双指针——leetcode盛最多水的容器、剑指Offer57和为s的两个数字

盛水最多的容器 &#xff08;1&#xff09;暴力解法 算法思路&#xff1a;我们枚举出所有的容器大小&#xff0c;取最大值即可。 容器容积的计算方式&#xff1a; 设两指针 i , j &#xff0c;分别指向水槽板的最左端以及最右端&#xff0c;此时容器的宽度为 j - i 。由于容器…

React Native连接Zebra斑马打印机通过发送CPCL指令打印(Android 和 iOS通用)

自 2015 年发布以来&#xff0c;React Native 已成为用于构建数千个移动应用程序的流行跨平台移动开发框架之一。通常&#xff0c;我们有开发人员询问如何将 Link-OS SDK 与 React Native 应用程序集成&#xff0c;以便在 Zebra 打印机上打印标签。在本教程中&#xff0c;我们将…

[Kubernetes]Kubeflow Pipelines - 基本介绍与安装方法

1. 背景 近些年来&#xff0c;人工智能技术在自然语言处理、视觉图像和自动驾驶方面都取得不小的成就&#xff0c;无论是工业界还是学术界大家都在惊叹一个又一个的模型设计。但是对于真正做过算法工程落地的同学&#xff0c;在惊叹这些模型的同时&#xff0c;更多的是在忧虑如…

最大交换(力扣)枚举 JAVA

给定一个非负整数&#xff0c;你至多可以交换一次数字中的任意两位。返回你能得到的最大值。 示例 1 : 输入: 2736 输出: 7236 解释: 交换数字2和数字7。 示例 2 : 输入: 9973 输出: 9973 解释: 不需要交换。 注意: 给定数字的范围是 [0, 10^8] 解题思路&#xff1a; 1、数最…

C++实现俄罗斯方块(源码+详解)

&#x1f442; Take me Hand Acoustic - Ccile Corbel - 单曲 - 网易云音乐 源码Debug工具 &#xff08;1&#xff09;cppreference.com &#xff08;主&#xff09; &#xff08;2&#xff09;必应 (bing.com) &#xff08;3&#xff09;GPT&#xff08;主&#xff09; &#…

静态时序分析与时序约束

一、时序分析的基本概念 1. 时钟 理性的时钟模型是一个占空比为50%且周期固定的方波&#xff1a; 实际电路中输入给FPGA的晶振时钟信号是正弦波&#xff1a; 2. 时钟抖动 Clock Jitter&#xff0c;时钟抖动&#xff0c;相对于理想时钟沿&#xff0c;实际时钟存在不随时钟存在…

PlanetScale vs. Neon - MySQL 和 Postgres 间的第二仗

本文为「数据库全方位对比系列」第三篇&#xff0c;该系列的前两部作品为&#xff1a; 全方位对比 Postgres 和 MySQL全方位对比 Postgres 和 MongoDB 根据 2023 年 Stack Overflow 调研&#xff0c;Postgres 已经取代 MySQL 成为最受欢迎和渴望的数据库了。 看起来 MySQL 和 …

ubuntu 安装 cuda

ubuntu 安装 cuda 初环境与设备在官网找安装方式 本篇文章将介绍ubuntu 安装 CUDA Toolkit CUDA Toolkit 是由 NVIDIA&#xff08;英伟达&#xff09;公司开发的一个软件工具包&#xff0c;用于支持并优化 GPU&#xff08;图形处理器&#xff09;上的并行计算和高性能计算。它…

Mongodb 安装

一、win10安装 服务端下载地址&#xff1a;Download MongoDB Community Server | MongoDB shell 工具下载地址&#xff1a;MongoDB Shell Download | MongoDB 服务端安装时选择custom&#xff0c;否则安装文件没有bin目录。 将安装后的文件中的bin目录加到环境变量。 设置…

视频添加字幕

1、依靠ffmpeg 命令 package zimu;import java.io.IOException;public class TestSrt {public static void main(String[] args) {String videoFile "/test/test1.mp4";String subtitleFile "/test/test1.SRT";String outputFile "/test/testout13…

Go http.Handle和http.HandleFunc的路由问题

Golang的net/http包提供了原生的http服务&#xff0c;其中http.Handle和http.HandleFunc是两个重要的路由函数。 1. 函数介绍 http.HandleFunc和http.Handle的函数原型如下&#xff0c;其中DefaultServeMux是http包提供的一个默认的路由选择器。 func HandleFunc(pattern st…