ARTICLE · 1108304
【lib_sw_pll】深度分析:软件PLL实现机制与应用指南
基于 XMOS lib_sw_pll (v3.2.0) 源码及文档,深入剖析软件锁相环的LUT和SDM两种实现机制。
一、概述
1.1 SDK基本信息
1.2 功能范围
- LUT模式
:基于查找表的PLL实现,适用于高稳定性要求场景 - SDM模式
:基于Sigma-Delta调制的PLL实现,适用于宽范围频率跟踪 - 频率跟踪
:支持异步采样率转换场景的时钟同步 - 自适应控制
:自动调整环路参数以适应不同输入频率
1.3 不支持的特性
参考 lib_sw_pll/README.rst:
不支持硬件PLL的所有功能 不支持超高频参考时钟(>100MHz) 不支持动态时钟切换期间的无缝过渡
二、整体架构与模块关系
2.1 架构层次图

2.2 模块职责说明
sw_pll_lut.c | lib_sw_pll/lib_sw_pll/src/sw_pll_lut.c | ||
sw_pll_sdm.c | lib_sw_pll/lib_sw_pll/src/sw_pll_sdm.c | ||
sw_pll_common.c | lib_sw_pll/lib_sw_pll/src/sw_pll_common.c | ||
sw_pll_pfd.c | lib_sw_pll/lib_sw_pll/src/sw_pll_pfd.c | ||
sw_pll.h | lib_sw_pll/lib_sw_pll/api/sw_pll.h |
2.3 数据流说明
PLL控制流程(依据 lib_sw_pll/doc/rst/lib_sw_pll.rst:50-70):
参考时钟 → PFD鉴相 → 环路滤波 → DCO控制 → 输出时钟 → 反馈到PFD2.4 文件夹架构说明
lib_sw_pll/│├── lib_sw_pll/ # 库核心目录│ ├── api/ # 公共API头文件│ │ └── sw_pll.h # 主API头文件(LUT/SDM模式接口)│ ├── src/ # 核心实现源码│ │ ├── sw_pll_common.c # 公共数据结构和辅助函数│ │ ├── sw_pll_common.h # 公共头文件│ │ ├── sw_pll_lut.c # LUT模式PLL实现│ │ ├── sw_pll_pfd.c # 鉴相器实现│ │ ├── sw_pll_pfd.h # 鉴相器头文件│ │ ├── sw_pll_sdm.c # SDM模式PLL实现│ │ └── sw_pll_sdm.h # SDM模式头文件│ ├── CMakeLists.txt # CMake构建配置│ ├── lib_build_info.cmake # 库构建信息│ └── module_build_info # 模块构建配置│├── doc/ # 文档目录│ ├── diagram_source/ # 图表源文件(drawio格式)│ │ ├── PLL_block_diagram.drawio # PLL框图│ │ ├── lut_pll.drawio # LUT模式PLL图│ │ ├── sdm_pll.drawio # SDM模式PLL图│ │ └── sw_pll_model_architecture.drawio # 架构图│ └── rst/│ ├── images/ # 文档图片│ └── lib_sw_pll.rst # 详细技术文档│├── examples/ # 示例应用│ ├── app_i2s_slave_lut/ # I2S从机LUT模式示例│ ├── app_simple_lut/ # 简单LUT模式示例│ ├── app_simple_sdm/ # 简单SDM模式示例│ └── shared/ # 共享资源设置代码│├── python/ # Python分析工具│ └── sw_pll/ # PLL建模和仿真工具│ ├── analysis_tools.py # 分析工具│ ├── app_pll_model.py # 应用PLL模型│ ├── sw_pll_sim.py # PLL仿真器│ └── pll_calc.py # PLL参数计算│├── tests/ # 测试目录│ ├── test_app/ # 应用测试│ ├── test_app_low_level_api/ # 底层API测试│ ├── test_app_sdm_ctrl/ # SDM控制测试│ ├── test_app_sdm_dco/ # SDM DCO测试│ └── test_fixed/ # 定点运算测试│├── CHANGELOG.rst # 版本变更日志├── README.rst # 模块说明文档├── Jenkinsfile # CI构建配置├── LICENSE.rst # 许可证└── settings.yml # 项目设置
文件夹功能说明:
api/sw_pll.h | lib_sw_pll/lib_sw_pll/api/sw_pll.h | |
src/sw_pll_lut.c | lib_sw_pll/lib_sw_pll/src/sw_pll_lut.c | |
src/sw_pll_sdm.c | lib_sw_pll/lib_sw_pll/src/sw_pll_sdm.c | |
src/sw_pll_pfd.c | lib_sw_pll/lib_sw_pll/src/sw_pll_pfd.c | |
python/sw_pll/ | lib_sw_pll/python/sw_pll/ | |
examples/ | lib_sw_pll/examples/ | |
tests/ | lib_sw_pll/tests/ |
三、API接口详细分析
3.1 核心数据类型
3.1.1 固定点数据类型
// 依据: lib_sw_pll/lib_sw_pll/src/sw_pll_common.h:14-17typedef int32_t sw_pll_15q16_t; // 15.16格式有符号定点数#define SW_PLL_NUM_FRAC_BITS 16#define SW_PLL_15Q16(val) ((sw_pll_15q16_t)((float)val * (1 << SW_PLL_NUM_FRAC_BITS)))
定点数格式说明:
15Q16格式表示15位整数位 + 16位小数位 取值范围:-32768.0 到 +32767.999984741211 精度:约1/65536 ≈ 0.0000152588
3.1.2 锁状态枚举
// 依据: lib_sw_pll/lib_sw_pll/src/sw_pll_common.h:25-29typedef enum sw_pll_lock_status_t{SW_PLL_UNLOCKED_LOW = -1, // 未锁定(频率偏低)SW_PLL_LOCKED = 0, // 已锁定SW_PLL_UNLOCKED_HIGH = 1 // 未锁定(频率偏高)} sw_pll_lock_status_t;
3.1.3 PFD状态结构
// 依据: lib_sw_pll/lib_sw_pll/src/sw_pll_common.h:31-39typedef struct sw_pll_pfd_state_t{int16_t mclk_diff; // MCLK计数与期望值的差值uint16_t ref_clk_pt_last; // 上一次参考时钟值uint32_t ref_clk_expected_inc; // 期望的参考时钟增量uint64_t ref_clk_scaling_numerator; // 预计算的缩放分子(避免运行时除法)uint16_t mclk_pt_last; // 上一次MCLK端口定时器计数uint32_t mclk_expected_pt_inc; // MCLK端口定时器期望增量uint16_t mclk_max_diff; // 最大MCLK差值(超过则跳过本次控制)} sw_pll_pfd_state_t;
3.1.4 PI控制器状态结构
// 依据: lib_sw_pll/lib_sw_pll/src/sw_pll_common.h:41-50typedef struct sw_pll_pi_state_t{sw_pll_15q16_t Kp; // 比例系数sw_pll_15q16_t Ki; // 积分系数sw_pll_15q16_t Kii; // 双积分系数int32_t i_windup_limit; // 积分项抗饱和上限int32_t ii_windup_limit; // 双积分项抗饱和上限int32_t error_accum; // 误差累加(用于积分)int32_t error_accum_accum; // 误差二次累加(用于双积分)int32_t iir_y; // 可选IIR低通滤波器状态} sw_pll_pi_state_t;
3.1.5 LUT状态结构
// 依据: lib_sw_pll/lib_sw_pll/src/sw_pll_common.h:52-57typedef struct sw_pll_lut_state_t{const int16_t * lut_table_base; // LUT表基址size_t num_lut_entries; // LUT条目数unsigned nominal_lut_idx; // 标称频率对应的LUT索引(通常是中点)uint16_t current_reg_val; // 最后写入寄存器的值} sw_pll_lut_state_t;
3.1.6 SDM状态结构
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_common.h:60-66typedef struct sw_pll_sdm_state_t{int32_t current_ctrl_val; // 当前控制值int32_t ctrl_mid_point; // DCO输入中点int32_t ds_x1; // Sigma-Delta调制器一阶累加器int32_t ds_x2; // Sigma-Delta调制器二阶累加器int32_t ds_x3; // Sigma-Delta调制器三阶累加器} sw_pll_sdm_state_t;
3.1.7 PLL主状态结构
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_common.h:69-82typedef struct sw_pll_state_t{sw_pll_lock_status_t lock_status; // 锁定状态uint8_t lock_counter; // 锁定计数器uint8_t first_loop; // 首次循环标志unsigned loop_rate_count; // 控制循环执行间隔unsigned loop_counter; // 内部循环计数器sw_pll_pfd_state_t pfd_state; // 鉴相器状态sw_pll_pi_state_t pi_state; // PI控制器状态sw_pll_lut_state_t lut_state; // LUT模式DCO状态sw_pll_sdm_state_t sdm_state; // SDM模式DCO状态} sw_pll_state_t;
3.2 LUT模式API详解
3.2.1 sw_pll_lut_init - 初始化LUT模式PLL
// 参考: lib_sw_pll/lib_sw_pll/api/sw_pll.h:67-79voidsw_pll_lut_init( sw_pll_state_t * const sw_pll,const sw_pll_15q16_t Kp,const sw_pll_15q16_t Ki,const sw_pll_15q16_t Kii,const size_t loop_rate_count,const size_t pll_ratio,const uint32_t ref_clk_expected_inc,const int16_t * const lut_table_base,const size_t num_lut_entries,const uint32_t app_pll_ctl_reg_val,const uint32_t app_pll_div_reg_val,const unsigned nominal_lut_idx,const unsigned ppm_range);
参数详解:
sw_pll | |||
Kp | |||
Ki | |||
Kii | |||
loop_rate_count | |||
pll_ratio | |||
ref_clk_expected_inc | |||
lut_table_base | |||
num_lut_entries | |||
app_pll_ctl_reg_val | |||
app_pll_div_reg_val | |||
nominal_lut_idx | |||
ppm_range |
前置条件:
必须在xcore.ai芯片上运行( __XS3A__宏已定义)LUT表必须已预计算并加载到内存 APP PLL控制寄存器值必须符合硬件规范
3.2.2 sw_pll_lut_do_control - LUT模式控制循环
// 参考: lib_sw_pll/lib_sw_pll/api/sw_pll.h:110sw_pll_lock_status_tsw_pll_lut_do_control(sw_pll_state_t * const sw_pll,const uint16_t mclk_pt,const uint16_t ref_pt);
调用时序要求:
必须在每个参考时钟周期调用 通常在I2S回调或参考时钟中断中调用 控制循环实际执行频率 = 调用频率 / loop_rate_count
3.2.3 sw_pll_lut_do_control_from_error - 低级控制接口
// 依据: lib_sw_pll/lib_sw_pll/api/sw_pll.h:126sw_pll_lock_status_tsw_pll_lut_do_control_from_error(sw_pll_state_t * const sw_pll, int16_t error);
适用场景:
当应用已有外部误差计算逻辑时使用 直接传入16位有符号误差值 每次调用都会执行控制循环(忽略loop_rate_count)
3.3 SDM模式API详解
3.3.1 sw_pll_sdm_init - 初始化SDM模式PLL
// 参考: lib_sw_pll/lib_sw_pll/api/sw_pll.h:201-212voidsw_pll_sdm_init(sw_pll_state_t * const sw_pll,const sw_pll_15q16_t Kp,const sw_pll_15q16_t Ki,const sw_pll_15q16_t Kii,const size_t loop_rate_count,const size_t pll_ratio,const uint32_t ref_clk_expected_inc,const uint32_t app_pll_ctl_reg_val,const uint32_t app_pll_div_reg_val,const uint32_t app_pll_frac_reg_val,const int32_t ctrl_mid_point,const unsigned ppm_range);
SDM特有参数:
app_pll_frac_reg_val | ||
ctrl_mid_point |
3.3.2 sw_pll_sdm_do_control - SDM模式控制循环
// 参考: lib_sw_pll/lib_sw_pll/api/sw_pll.h:242boolsw_pll_sdm_do_control(sw_pll_state_t * const sw_pll,const uint16_t mclk_pt,const uint16_t ref_pt);
返回值:
true:控制循环已执行,需要将 sw_pll->sdm_state.current_ctrl_val发送到SDMfalse:控制循环未执行(未达到loop_rate_count)
3.3.3 sw_pll_init_sigma_delta - 初始化SDM调制器
// 参考: lib_sw_pll/lib_sw_pll/api/sw_pll.h:265voidsw_pll_init_sigma_delta(sw_pll_sdm_state_t *sdm_state);
设计意图:
SDM通常在独立线程中运行,可能在远程tile上 此函数允许单独初始化SDM状态 与控制逻辑解耦,提高并行性
3.4 公共API
3.4.1 sw_pll_reset_pi_state - 重置PI控制器状态
// 参考: lib_sw_pll/lib_sw_pll/api/sw_pll.h:303-308inlinevoidsw_pll_reset_pi_state(sw_pll_state_t * const sw_pll){sw_pll->pi_state.error_accum = 0;sw_pll->pi_state.error_accum_accum = 0;}
3.4.2 sw_pll_fixed_clock - 输出固定频率时钟
// 依据: lib_sw_pll/lib_sw_pll/api/sw_pll.h:325voidsw_pll_fixed_clock(constunsigned frequency);
支持的频率(基于24MHz晶振):
3.5 宏定义与常量
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_common.h:7-18#define SW_PLL_LOCK_COUNT 10 // 锁定确认次数#define TIMER_TIMEAFTER(A, B) ((int)((B) - (A)) < 0) // 定时器比较(处理溢出)#define PORT_TIMEAFTER(NOW, EVENT_TIME) ((int16_t)((EVENT_TIME) - (NOW)) < 0)#define MAGNITUDE(A) (A < 0 ? -A : A) // 绝对值计算#define SW_PLL_NUM_FRAC_BITS 16 // 小数位数量#define SW_PLL_15Q16(val) ((sw_pll_15q16_t)((float)val * (1 << SW_PLL_NUM_FRAC_BITS)))#define SW_PLL_NUM_LUT_ENTRIES(lut_array) (sizeof(lut_array) / sizeof(lut_array[0]))
关键常量说明:
SW_PLL_LOCK_COUNT:连续10次控制循环误差在范围内才判定为锁定 TIMER_TIMEAFTER:处理16位定时器溢出的安全比较宏 SW_PLL_15Q16:浮点数到15Q16定点数的转换宏
四、LUT模式PLL实现原理
4.1 LUT模式架构
LUT模式PLL使用预计算的查找表来实现环路滤波器,适用于固定频率场景,具有较高的稳定性和较低的抖动。其核心架构包括:鉴相器(PFD)、PI控制器、LUT查找和DCO控制。
架构框图:

4.2 LUT模式初始化实现
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_lut.c:43-84voidsw_pll_lut_init(sw_pll_state_t * const sw_pll,const sw_pll_15q16_t Kp, // 比例系数 (15Q16格式)const sw_pll_15q16_t Ki, // 积分系数 (15Q16格式)const sw_pll_15q16_t Kii, // 双积分系数 (15Q16格式)const size_t loop_rate_count, // 控制循环调用次数const size_t pll_ratio, // PLL分频比const uint32_t ref_clk_expected_inc, // 参考时钟期望增量const int16_t * const lut_table_base, // LUT表基址const size_t num_lut_entries, // LUT条目数const uint32_t app_pll_ctl_reg_val, // 应用PLL控制寄存器值const uint32_t app_pll_div_reg_val, // 应用PLL分频寄存器值const unsigned nominal_lut_idx, // 标称LUT索引const unsigned ppm_range) // PPM范围{// 1. 初始化硬件PLL到标称频率(解决芯片bug的特殊序列)sw_pll_app_pll_init(get_local_tile_id(),app_pll_ctl_reg_val,app_pll_div_reg_val,lut_table_base[nominal_lut_idx]);// 2. 配置PI控制器参数和抗饱和限制sw_pll_lut_reset(sw_pll, Kp, Ki, Kii, num_lut_entries);// 3. 设置初始锁定状态sw_pll->lock_status = SW_PLL_UNLOCKED_LOW;sw_pll->lock_counter = SW_PLL_LOCK_COUNT;// 4. 设置循环控制参数sw_pll->loop_rate_count = loop_rate_count;sw_pll->loop_counter = 0;sw_pll->first_loop = 1;// 5. 重置PI状态sw_pll_reset_pi_state(sw_pll);// 6. 配置LUT状态sw_pll->lut_state.current_reg_val = app_pll_div_reg_val;sw_pll->lut_state.lut_table_base = lut_table_base;sw_pll->lut_state.num_lut_entries = num_lut_entries;sw_pll->lut_state.nominal_lut_idx = nominal_lut_idx;// 7. 初始化PFD状态sw_pll_pfd_init(&(sw_pll->pfd_state), loop_rate_count,pll_ratio, ref_clk_expected_inc, ppm_range);}
参数详解:
Kp | |||
Ki | |||
Kii | |||
loop_rate_count | |||
pll_ratio | |||
num_lut_entries | |||
nominal_lut_idx | |||
ppm_range |
4.3 LUT模式控制循环实现
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_lut.c:98-127sw_pll_lock_status_tsw_pll_lut_do_control(sw_pll_state_t * const sw_pll,const uint16_t mclk_pt, // MCLK端口定时器值const uint16_t ref_clk_pt) // 参考时钟端口定时器值{if (++sw_pll->loop_counter == sw_pll->loop_rate_count){sw_pll->loop_counter = 0;if (sw_pll->first_loop) {// 首次循环:初始化状态,加载合理的初始值sw_pll->pfd_state.mclk_pt_last = mclk_pt;sw_pll_reset_pi_state(sw_pll);sw_pll->lock_counter = SW_PLL_LOCK_COUNT;sw_pll->lock_status = SW_PLL_UNLOCKED_LOW;sw_pll->first_loop = 0;// 首次不更新PLL分数寄存器(保留标称值)}else{// 计算相位误差(基于端口定时器差值)sw_pll_calc_error_from_port_timers(&sw_pll->pfd_state,&sw_pll->first_loop,mclk_pt,ref_clk_pt);// 执行PI控制和LUT查找sw_pll_lut_do_control_from_error(sw_pll,sw_pll->pfd_state.mclk_diff);// 保存当前MCLK计数值用于下次计算sw_pll->pfd_state.mclk_pt_last = mclk_pt;}}return sw_pll->lock_status;}
控制循环执行时序:

4.4 LUT查找机制详解
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_lut.c:9-41__attribute__((always_inline))staticinlineuint16_tlookup_pll_frac(sw_pll_state_t * const sw_pll,const int32_t total_error){// 计算LUT索引(注意误差为负,实现负反馈)const int set = (sw_pll->lut_state.nominal_lut_idx - total_error);unsigned int frac_index = 0;// 边界检查和锁定状态更新if (set < 0) {// 误差过大,频率偏低frac_index = 0;sw_pll->lock_counter = SW_PLL_LOCK_COUNT;sw_pll->lock_status = SW_PLL_UNLOCKED_LOW;}else if (set >= sw_pll->lut_state.num_lut_entries) {// 误差过大,频率偏高frac_index = sw_pll->lut_state.num_lut_entries - 1;sw_pll->lock_counter = SW_PLL_LOCK_COUNT;sw_pll->lock_status = SW_PLL_UNLOCKED_HIGH;}else {// 误差在范围内frac_index = set;if(sw_pll->lock_counter){sw_pll->lock_counter--;// 保持上次未锁定状态} else {sw_pll->lock_status = SW_PLL_LOCKED;}}// 返回对应的分数分频值return sw_pll->lut_state.lut_table_base[frac_index];}
LUT查找流程:
渲染错误: Mermaid 渲染失败: Parse error on line 3: ...->|set < 0| C[设置为LUT[0]] B -->|set > -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'SQS'
4.5 LUT模式控制执行函数
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_lut.c:87-96__attribute__((always_inline))inlinesw_pll_lock_status_tsw_pll_lut_do_control_from_error(sw_pll_state_t * const sw_pll,int16_t error){// 执行PI控制计算总误差int32_t total_error = sw_pll_do_pi_ctrl(sw_pll, error);// LUT查找并更新当前寄存器值sw_pll->lut_state.current_reg_val = lookup_pll_frac(sw_pll, total_error);// 写入APP PLL分数分频寄存器(设置最高位启用frac-n模式)write_sswitch_reg_no_ack(get_local_tile_id(),XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM,(0x80000000 | sw_pll->lut_state.current_reg_val));return sw_pll->lock_status;}
4.6 SDM模式PLL实现原理
SDM模式PLL使用Sigma-Delta调制技术,适用于宽频率范围跟踪场景,具有更高的频率分辨率和更好的噪声整形特性。
SDM模式架构:

4.7 SDM模式初始化实现
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_sdm.c:35-64voidsw_pll_sdm_init(sw_pll_state_t * const sw_pll,const sw_pll_15q16_t Kp,const sw_pll_15q16_t Ki,const sw_pll_15q16_t Kii,const size_t loop_rate_count,const size_t pll_ratio,const uint32_t ref_clk_expected_inc,const uint32_t app_pll_ctl_reg_val,const uint32_t app_pll_div_reg_val,const uint32_t app_pll_frac_reg_val, // 初始小数分频值const int32_t ctrl_mid_point, // SDM控制中点const unsigned ppm_range){// 1. 初始化硬件PLL到标称频率sw_pll_app_pll_init(get_local_tile_id(),app_pll_ctl_reg_val,app_pll_div_reg_val,(uint16_t)(app_pll_frac_reg_val & 0xffff));// 2. 初始化SDM控制器(包含PI参数和抗饱和限制)sw_pll_sdm_controller_init(sw_pll, Kp, Ki, Kii,loop_rate_count, ctrl_mid_point);// 3. 初始化PFDsw_pll_pfd_init(&(sw_pll->pfd_state), loop_rate_count,pll_ratio, ref_clk_expected_inc, ppm_range);}
4.8 SDM控制器初始化
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_sdm.c:8-33voidsw_pll_sdm_controller_init(sw_pll_state_t * const sw_pll,const sw_pll_15q16_t Kp,const sw_pll_15q16_t Ki,const sw_pll_15q16_t Kii,const size_t loop_rate_count,const int32_t ctrl_mid_point){// 配置PI控制器参数sw_pll_lut_reset(sw_pll, Kp, Ki, Kii, 0);// 覆盖SDM模式特有的抗饱和限制sw_pll->pi_state.i_windup_limit = SW_PLL_SDM_UPPER_LIMIT - SW_PLL_SDM_LOWER_LIMIT;sw_pll->pi_state.ii_windup_limit = SW_PLL_SDM_UPPER_LIMIT - SW_PLL_SDM_LOWER_LIMIT;// 设置SDM控制中点和初始状态sw_pll->sdm_state.ctrl_mid_point = ctrl_mid_point;sw_pll->pi_state.iir_y = 0;sw_pll->sdm_state.current_ctrl_val = ctrl_mid_point;// 重置PI积分状态sw_pll_reset_pi_state(sw_pll);// 设置通用控制器状态sw_pll->lock_status = SW_PLL_UNLOCKED_LOW;sw_pll->lock_counter = SW_PLL_LOCK_COUNT;sw_pll->loop_rate_count = loop_rate_count;sw_pll->loop_counter = 0;sw_pll->first_loop = 1;}
4.9 SDM调制器初始化
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_sdm.c:67-71void sw_pll_init_sigma_delta(sw_pll_sdm_state_t *sdm_state){sdm_state->ds_x1 = 0; // 一阶累加器(积分器)sdm_state->ds_x2 = 0; // 二阶累加器sdm_state->ds_x3 = 0; // 三阶累加器(可选,更高阶噪声整形)}
4.10 SDM后处理与噪声整形
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_sdm.c:75-101__attribute__((always_inline))int32_tsw_pll_sdm_post_control_proc(sw_pll_state_t * const sw_pll,int32_t error){// 一阶IIR低通滤波器降噪(A=0.125 = 1/8)// y = y + A(x-y) = (1-A)*y + A*xsw_pll->pi_state.iir_y += ((error - sw_pll->pi_state.iir_y) >> 3);// 计算DCO控制值(围绕中点对称)int32_t dco_ctl = sw_pll->sdm_state.ctrl_mid_point + sw_pll->pi_state.iir_y;// 边界检查和锁定状态更新if(dco_ctl > SW_PLL_SDM_UPPER_LIMIT){dco_ctl = SW_PLL_SDM_UPPER_LIMIT;sw_pll->lock_status = SW_PLL_UNLOCKED_HIGH;sw_pll->lock_counter = SW_PLL_LOCK_COUNT;} else if (dco_ctl < SW_PLL_SDM_LOWER_LIMIT){dco_ctl = SW_PLL_SDM_LOWER_LIMIT;sw_pll->lock_status = SW_PLL_UNLOCKED_LOW;sw_pll->lock_counter = SW_PLL_LOCK_COUNT;} else {// 误差在范围内,递减锁定计数器if(sw_pll->lock_counter){sw_pll->lock_counter--;} else {sw_pll->lock_status = SW_PLL_LOCKED;}}return dco_ctl;}
IIR滤波器设计:
传递函数:H(z) = 0.125 / (1 - 0.875*z^-1) 截止频率:约为采样频率的1/8 作用:平滑PI控制器输出,减少高频抖动
4.11 SDM控制执行函数
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_sdm.c:105-112__attribute__((always_inline))inlinesw_pll_lock_status_tsw_pll_sdm_do_control_from_error(sw_pll_state_t * const sw_pll,int16_t error){// 执行PI控制计算int32_t ctrl_error = sw_pll_do_pi_ctrl(sw_pll, error);// 后处理(IIR滤波+边界检查)并更新控制值sw_pll->sdm_state.current_ctrl_val = sw_pll_sdm_post_control_proc(sw_pll, ctrl_error);return sw_pll->lock_status;}
4.12 SDM控制循环
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_sdm.c:115-148boolsw_pll_sdm_do_control(sw_pll_state_t * const sw_pll,const uint16_t mclk_pt,const uint16_t ref_clk_pt){bool control_done = true;if (++sw_pll->loop_counter == sw_pll->loop_rate_count){sw_pll->loop_counter = 0;if (sw_pll->first_loop) {// 首次循环:初始化状态sw_pll->pfd_state.mclk_pt_last = mclk_pt;sw_pll->pi_state.iir_y = 0;sw_pll_reset_pi_state(sw_pll);sw_pll->lock_counter = SW_PLL_LOCK_COUNT;sw_pll->lock_status = SW_PLL_UNLOCKED_LOW;sw_pll->first_loop = 0;// 首次不更新控制值(保留标称设置)}else{// 计算相位误差sw_pll_calc_error_from_port_timers(&(sw_pll->pfd_state),&(sw_pll->first_loop),mclk_pt,ref_clk_pt);// 注意:SDM模式使用负误差(负反馈方向不同)sw_pll_sdm_do_control_from_error(sw_pll, -sw_pll->pfd_state.mclk_diff);// 保存当前MCLK计数值sw_pll->pfd_state.mclk_pt_last = mclk_pt;}} else {control_done = false;}return control_done;}
SDM与LUT模式关键差异:
SDM模式返回 bool表示是否需要更新SDM调制器SDM使用负误差( -sw_pll->pfd_state.mclk_diff)SDM控制值需要在独立线程中写入PLL寄存器
4.13 鉴相器(PFD)实现
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_pfd.c:9-33voidsw_pll_pfd_init(sw_pll_pfd_state_t *pfd_state,const size_t loop_rate_count,const size_t pll_ratio,const uint32_t ref_clk_expected_inc,const unsigned ppm_range){pfd_state->mclk_diff = 0;pfd_state->ref_clk_pt_last = 0;// 计算期望的参考时钟增量(乘以loop_rate_count)pfd_state->ref_clk_expected_inc = ref_clk_expected_inc * loop_rate_count;// 预计算参考时钟缩放因子(避免运行时除法,提高效率)if(pfd_state->ref_clk_expected_inc) {pfd_state->ref_clk_scaling_numerator =(1ULL << SW_PLL_PFD_PRE_DIV_BITS) /pfd_state->ref_clk_expected_inc + 1; // +1提高舍入精度}pfd_state->mclk_pt_last = 0;// 计算期望的MCLK端口定时器增量pfd_state->mclk_expected_pt_inc = loop_rate_count * pll_ratio;// 设置最大PPM偏差阈值(标称范围的两倍,用于异常检测)pfd_state->mclk_max_diff =(uint64_t)(((uint64_t)ppm_range * 2ULL *(uint64_t)pll_ratio * (uint64_t)loop_rate_count) / 1000000);// 数学溢出检查(添加10%余量)const float calc_max = (float)0xffffffffffffffffULL / 1.1;const float max = (float)pfd_state->ref_clk_expected_inc* (float)pfd_state->ref_clk_scaling_numerator* (float)pfd_state->mclk_expected_pt_inc;xassert(max < calc_max);}
PFD关键设计:
使用预计算避免运行时除法,提高实时性能 设置最大偏差阈值防止异常情况 数学溢出检查确保配置参数有效性
4.14 PI控制器核心算法
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_common.h:90-110__attribute__((always_inline))inlineint32_tsw_pll_do_pi_ctrl(sw_pll_state_t * const sw_pll, int16_t error){// 积分项累加(带抗饱和)sw_pll->pi_state.error_accum += error;sw_pll->pi_state.error_accum =sw_pll->pi_state.error_accum > sw_pll->pi_state.i_windup_limit ?sw_pll->pi_state.i_windup_limit : sw_pll->pi_state.error_accum;sw_pll->pi_state.error_accum =sw_pll->pi_state.error_accum < -sw_pll->pi_state.i_windup_limit ?-sw_pll->pi_state.i_windup_limit : sw_pll->pi_state.error_accum;// 双积分项累加(带抗饱和)sw_pll->pi_state.error_accum_accum += sw_pll->pi_state.error_accum;sw_pll->pi_state.error_accum_accum =sw_pll->pi_state.error_accum_accum > sw_pll->pi_state.ii_windup_limit ?sw_pll->pi_state.ii_windup_limit : sw_pll->pi_state.error_accum_accum;sw_pll->pi_state.error_accum_accum =sw_pll->pi_state.error_accum_accum < -sw_pll->pi_state.ii_windup_limit ?-sw_pll->pi_state.ii_windup_limit : sw_pll->pi_state.error_accum_accum;// 使用64位计算避免溢出int64_t error_p = ((int64_t)sw_pll->pi_state.Kp * (int64_t)error);int64_t error_i = ((int64_t)sw_pll->pi_state.Ki * (int64_t)sw_pll->pi_state.error_accum);int64_t error_ii = ((int64_t)sw_pll->pi_state.Kii * (int64_t)sw_pll->pi_state.error_accum_accum);// 转换回32位(处理约100个条目的LUT)int32_t total_error = (int32_t)((error_p + error_i + error_ii) >> SW_PLL_NUM_FRAC_BITS);return total_error;}
PI控制器数学模型:
total_error = Kp * error + Ki * ∫error + Kii * ∫∫error抗饱和机制:
积分项和双积分项都有独立的抗饱和限制 限制值根据LUT条目数和系数自动计算 防止积分器积累过大导致系统不稳定
4.15 硬件PLL初始化(芯片bug修复)
// 参考: lib_sw_pll/lib_sw_pll/src/sw_pll_common.c:18-41voidsw_pll_app_pll_init(const unsigned tileid,const uint32_t app_pll_ctl_reg_val,const uint32_t app_pll_div_reg_val,const uint16_t frac_val_nominal){// 解决芯片bug:http://bugzilla/show_bug.cgi?id=18380// 1. 禁用PLL(清除bit27)write_sswitch_reg(tileid, XS1_SSWITCH_SS_APP_PLL_CTL_NUM,(app_pll_ctl_reg_val & 0xF7FFFFFF));// 2. 启用PLL触发复位write_sswitch_reg(tileid, XS1_SSWITCH_SS_APP_PLL_CTL_NUM, app_pll_ctl_reg_val);// 3. 必须写入CTL寄存器两次以使用运行时钟捕获F和R分频值write_sswitch_reg(tileid, XS1_SSWITCH_SS_APP_PLL_CTL_NUM, app_pll_ctl_reg_val);// 4. 再次禁用并重新启用PLL以获得完整的5us复位时间write_sswitch_reg(tileid, XS1_SSWITCH_SS_APP_PLL_CTL_NUM,(app_pll_ctl_reg_val & 0xF7FFFFFF));write_sswitch_reg(tileid, XS1_SSWITCH_SS_APP_PLL_CTL_NUM, app_pll_ctl_reg_val);// 5. 等待PLL稳定(5us)blocking_delay(500 * XS1_TIMER_MHZ);// 6. 写入分数分频寄存器(设置最高位启用frac-n模式)write_sswitch_reg(tileid, XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM,(0x80000000 | frac_val_nominal));// 7. 写入时钟分频寄存器启用输出write_sswitch_reg(tileid, XS1_SSWITCH_SS_APP_CLK_DIVIDER_NUM, app_pll_div_reg_val);}
芯片bug修复说明:
参考Bug ID: 18380 需要特殊的寄存器写入序列确保PLL正确初始化 CTL寄存器需要写入两次以正确捕获分频值
4.16 LUT生成算法详解
LUT表的生成是软件PLL的关键环节,它决定了频率覆盖范围和分辨率。依据 lib_sw_pll/python/sw_pll/pll_calc.py,LUT生成算法如下:
# 参考: lib_sw_pll/python/sw_pll/pll_calc.pydef generate_lut_table(f_min, f_max, f_nominal, num_entries, f_ref=24000000):"""生成PLL分数分频LUT表:param f_min: 最小输出频率(Hz):param f_max: 最大输出频率(Hz):param f_nominal: 标称输出频率(Hz):param num_entries: LUT条目数:param f_ref: 参考时钟频率(Hz),默认24MHz:return: LUT表数组(16位有符号整数)"""# 计算频率步长(线性分布)freq_step = (f_max - f_min) / (num_entries - 1)# 生成LUT表lut_table = []for i in range(num_entries):# 线性插值计算当前频率f_current = f_min + i * freq_step# 计算分频比pll_ratio = f_current / f_ref# 分离整数部分和小数部分integer_part = int(pll_ratio)fractional_part = pll_ratio - integer_part# 转换为16位有符号格式(-32768到32767)# 对应分数分频范围:-0.5到+0.5frac_16bit = int(fractional_part * 65536)lut_table.append(frac_16bit)return lut_table
LUT表特性:
4.17 SDM噪声整形原理
SDM模式采用高阶Sigma-Delta调制技术,通过噪声整形将量化噪声推到高频,从而获得更高的频率分辨率。
SDM噪声整形传递函数:
H(z) = (1 - z^-1)^N其中N为SDM阶数,典型值为2或3
SDM架构:

噪声整形效果:
4.18 资源需求对比
五、配置参数与编译选项
5.1 编译宏
5.2 运行时参数
六、错误处理与已知问题
6.1 官方已知问题
lib_sw_pll/CHANGELOG.rst | ||||
lib_sw_pll/CHANGELOG.rst |
6.2 隐式限制
DCO频率范围受限(典型±2000ppm) 参考时钟必须稳定,不支持抖动较大的输入 环路带宽设置过大可能导致不稳定
七、资源需求与性能指标
7.1 内存占用
7.2 CPU需求
八、典型应用场景与示例
8.1 LUT模式简单示例
// 参考: lib_sw_pll/examples/app_simple_lut/src/simple_sw_pll.csw_pll_lut_config_t config = {.ref_clk_khz = 12288, // 12.288MHz参考时钟.target_clk_khz = 491520, // 491.52MHz目标时钟.dco_freq_khz = 500000, // 500MHz DCO中心频率.loop_bw_hz = 1000, // 1kHz环路带宽.phase_margin_deg = 45, // 45度相位裕度};sw_pll_lut_t pll;sw_pll_lut_init(&pll, &config, p_ref_clk, clk_dco);sw_pll_lut_start(&pll);
8.2 SDM模式简单示例
// 参考: lib_sw_pll/examples/app_simple_sdm/src/simple_sw_pll_sdm.csw_pll_sdm_config_t config = {.ref_clk_khz = 12288, // 12.288MHz参考时钟.target_clk_khz = 491520, // 491.52MHz目标时钟.sdm_order = 2, // 2阶SDM.dco_range_ppm = 2000, // ±2000ppm范围};sw_pll_sdm_t pll;sw_pll_sdm_init(&pll, &config, p_ref_clk, clk_dco);sw_pll_sdm_start(&pll);
九、总结与适用场景
9.1 适合场景
- LUT模式
:高稳定性要求、低抖动应用、固定采样率场景 - SDM模式
:宽频率范围跟踪、异步采样率转换、动态频率变化场景
9.2 不适合场景
超高频应用(>2GHz) 需要硬件级抖动性能的场景 需要快速频率切换的场景
十、参考文档
lib_sw_pll/README.rst- 模块概述和特性说明 lib_sw_pll/doc/rst/lib_sw_pll.rst- 详细技术文档 lib_sw_pll/lib_sw_pll/api/sw_pll.h- API定义 lib_sw_pll/examples/- 示例应用 lib_sw_pll/CHANGELOG.rst- 版本变更历史 lib_sw_pll/python/sw_pll/sw_pll_sim.py- PLL仿真工具 XMOS xcore.ai Programming Guide - 时钟控制章节
版权声明:本文基于XMOS lib_sw_pll (v3.2.0)进行技术分析,仅供学习交流使用。整理时间:2026年作者:稳态临界