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<…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
