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π(f1−f2)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
仿真结果如下

相关文章:
PLL 的 verilog 实现
锁相环(PLL)是一种常用的频率、相位追踪算法,在信号解调、交流并网等领域有着广泛的应用。本文对全数字锁相环的原理进行介绍,随后给出 verilog 实现及仿真。 PLL 锁相原理 锁相环结构如下图所示,主要由鉴相器、环路滤…...
【Hystrix技术指南】(1)基本使用和配置说明
这世间许多事物皆因相信而存在,所以人们亲手捏出了泥菩萨,却选择坚定的去信仰它。 分布式系统的规模和复杂度不断增加,随着而来的是对分布式系统可用性的要求越来越高。在各种高可用设计模式中,【熔断、隔离、降级、限流】是经常被…...
Oracle EBS OM客制化调用API创建销售订单非常慢(FND_FLEX_HASH死锁)
业务场景 由于Oracle EBS标准功的公司间关联交易操作涉及业务节点环节多,需要多个业务部门参考操作完成,浪费人力和花费时间。随着国内集团公司通过业务整合优化,大幅度减少间中很多环节的人为操作,如国内公司间贸易通过类似于客制化出货单申请方式,跨国公司间贸易通过类似…...
【leetcode】394. 字符串解码
题目链接:力扣 给定一个经过编码的字符串,返回它解码后的字符串。 编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。 你可以认为输入字符串总是有效的;输入字符串中没…...
系统架构设计高级技能 · 系统质量属性与架构评估(二)【系统架构设计师】
系列文章目录 系统架构设计高级技能 软件架构概念、架构风格、ABSD、架构复用、DSSA(一)【系统架构设计师】 系统架构设计高级技能 系统质量属性与架构评估(二)【系统架构设计师】 系统架构设计高级技能 软件可靠性分析与设计…...
魅族Pandaer手机壳
Pandaer的设计真是非常好看啊!像是手机壳的花样就特别多,还分出来很多系列,我比较喜欢它的亮面设计,入手了一款iPhone的,花色叫做“失控街头”,壳内部也是亮的,看起来特别浮夸,潮里潮…...
F5洞察2023年网络威胁,助力网络安全防护
2023已经过半,关于网络安全防护的相关讨论话题热度始终居高不下。对于网络安全领域的从业者来说,应当对相关的前瞻分析有所了解。前段时间,我阅读了F5 安全运营中心工程师对威胁网络安全的预测,深受启发,故此选取了几则…...
从零构建深度学习推理框架-4 框架中的算子注册机制
今天要讲的这一注册机制用到了设计模式中的工厂模式和单例模式,所以这节课也是对两大设计模式的一个合理应用和实践。KuiperInfer的注册表是一个map数据结构,维护了一组键值对,key是对应的OpType,用来查找对应的value,…...
使用vscode+ssh免密远程Linux
使用vscodessh免密远程Linux 使用 SSH 密钥对:使用 SSH Agent:ssh-agent的使用场景 使用 SSH 密钥对: 确保你的本地机器上已经生成了 SSH 密钥对。如果没有,请使用以下命令生成密钥对: ssh-keygen -t rsa这将在 ~/.ssh…...
rust-异步学习
rust获取future中的结果 两种主要的方法使用 async: async fn 和 async 块 async 体以及其他 future 类型是惰性的:除非它们运行起来,否则它们什么都不做。 运行 Future 最常见的方法是 .await 它。 当 .await 在 Future 上调用时,它会尝试把…...
【Azure】office365邮箱测试的邮箱账号因频繁连接邮箱服务器而被限制连接 引起邮箱显示异常
azure微软office365邮箱会对频繁连接自身邮箱服务器的IP地址进行,连接邮箱服务器IP限制,也就是黑名单,释放时间不确定,但至少一天及以上。 解决办法,换一个IP,或者新注册一个office365邮箱再重试。 以下是…...
重新登录成功和登录失败处理器
<template><div class="login"><el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form"><h3 class="title">Java1234 Vue3 后台管理系统</h3><el…...
【Spring】(三)Spring 使用注解存储和读取 Bean对象
文章目录 前言一、使用注解储存 Bean 对象1.1 配置扫描路径1.2 类注解储存 Bean 对象1.2.1 Controller(控制器存储)1.2.2 Service(服务储存)1.2.3 Repository(仓库存储)1.2.4 Component(组件储存…...
ParallelCollectionRDD [0] isEmpty at KyuubiSparkUtil.scala:48问题解决
ParallelCollectionRDD [0] isEmpty at KyuubiSparkUtil.scala:48问题解决 这个问题出现在使用Kyubi Spark Util处理ParallelCollectionRDD的过程中,具体是在KyubiSparkUtil.scala文件的第48行调用isEmpty方法时出现的。该问题可能是由以下几个原因引起的࿱…...
---------------- 部署 Zookeeper 集群 ----------------
部署 Zookeeper 集群 1.安装前准备2.安装 Zookeeper修改配置文件在每个节点上创建数据目录和日志目录在每个节点的dataDir指定的目录下创建一个 myid 的文件配置 Zookeeper 启动脚本 //准备 3 台服务器做 Zookeeper 集群 192.168.109.1 192.168.109.2 192.168.109.3 1.安装前准…...
SpringBoot 依赖管理和自动配置---带你了解什么是版本仲裁
😀前言 本篇博文是关于SpringBoot 依赖管理和自动配置,希望能够帮助到您😊 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,您…...
c语言每日一练(2)
前言: 每日一练系列,每一期都包含5道选择题,2道编程题,博主会尽可能详细地进行讲解,令初学者也能听的清晰。每日一练系列会持续更新,暑假时三天之内必有一更,到了开学之后,将看学业情…...
代码随想录第三十七天
代码随想录第三十七天 Leetcode 738. 单调递增的数字 Leetcode 738. 单调递增的数字 题目链接: 单调递增的数字 自己的思路:完全想不到!! 正确思路:大致思路是从后向前遍历,不可以从前向后,如果从前向后没有保证单调递增的顺序&…...
Linux进程间通信--ftok
在C语言中,ftok函数用于生成一个唯一的键值,该键值通常用于创建共享内存,消息队列和信号量等系统资源的标识符。 ftok函数原型入下: key_t ftok(const char *pathname, int proj_id); 参数说明: pathname:…...
Spring Boot集成Mybatis-Plus
Spring Boot集成Mybatis-Plus 1. pom.xml导包 <!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--mysql驱动--><dependency><groupId>mysql<…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...
