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<…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...

Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...

R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...

在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例
目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码:冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...