夜雨聆风学习资料网

ARTICLE · 1108304

【lib_sw_pll】深度分析:软件PLL实现机制与应用指南

【lib_sw_pll】深度分析:软件PLL实现机制与应用指南

基于 XMOS lib_sw_pll (v3.2.0) 源码及文档,深入剖析软件锁相环的LUT和SDM两种实现机制。


一、概述

1.1 SDK基本信息

属性
值
SDK全称
lib_sw_pll
提供方
XMOS
版本号
3.2.0
适用芯片
xcore.ai系列
核心能力
软件实现的锁相环,支持LUT和SDM两种模式
依赖库
lib_xassert
开发工具
XMOS XTC Tools 15.3.0+

1.2 功能范围

  • LUT模式
    :基于查找表的PLL实现,适用于高稳定性要求场景
  • SDM模式
    :基于Sigma-Delta调制的PLL实现,适用于宽范围频率跟踪
  • 频率跟踪
    :支持异步采样率转换场景的时钟同步
  • 自适应控制
    :自动调整环路参数以适应不同输入频率

1.3 不支持的特性

参考 lib_sw_pll/README.rst:

  • 不支持硬件PLL的所有功能
  • 不支持超高频参考时钟(>100MHz)
  • 不支持动态时钟切换期间的无缝过渡

二、整体架构与模块关系

2.1 架构层次图

2.2 模块职责说明

模块
文件
职责
依据
LUT模式PLL
sw_pll_lut.c
基于查找表的PLL实现,高稳定性
lib_sw_pll/lib_sw_pll/src/sw_pll_lut.c
SDM模式PLL
sw_pll_sdm.c
基于Sigma-Delta调制的PLL,宽频率范围
lib_sw_pll/lib_sw_pll/src/sw_pll_sdm.c
公共模块
sw_pll_common.c
PLL公共数据结构和辅助函数
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
API头文件
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控制 → 输出时钟 → 反馈到PFD

2.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
定义PLL接口、数据结构和配置参数
lib_sw_pll/lib_sw_pll/api/sw_pll.h
src/sw_pll_lut.c
LUT模式PLL核心实现,使用预计算查找表
lib_sw_pll/lib_sw_pll/src/sw_pll_lut.c
src/sw_pll_sdm.c
SDM模式PLL核心实现,使用Sigma-Delta调制
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/
Python工具集,用于PLL参数计算和行为仿真
lib_sw_pll/python/sw_pll/
examples/
包含3个示例应用,演示LUT和SDM模式的使用
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
sw_pll_state_t*
PLL状态结构指针
-
Kp
sw_pll_15q16_t
比例系数(15Q16格式)
SW_PLL_15Q16(0.25)
Ki
sw_pll_15q16_t
积分系数(15Q16格式)
SW_PLL_15Q16(0.0156)
Kii
sw_pll_15q16_t
双积分系数(15Q16格式)
SW_PLL_15Q16(0.00098)
loop_rate_count
size_t
每N次调用执行一次控制循环
16
pll_ratio
size_t
MCLK与参考时钟的比值
256
ref_clk_expected_inc
uint32_t
参考时钟期望增量(0表示精确采样)
0
lut_table_base
int16_t*
LUT表基址
预计算数组
num_lut_entries
size_t
LUT条目数
256
app_pll_ctl_reg_val
uint32_t
APP PLL控制寄存器值
0x0B808200
app_pll_div_reg_val
uint32_t
APP PLL分频寄存器值
0x80000001
nominal_lut_idx
unsigned
标称频率LUT索引
128(中点)
ppm_range
unsigned
PPM范围
2000

前置条件:

  • 必须在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
APP PLL初始小数分频值
0x8000000D
ctrl_mid_point
SDM控制中点
0(对称范围)

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发送到SDM
  • false
    :控制循环未执行(未达到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晶振):

频率
公式
用途
11289600Hz
44.1kHz × 256
44.1kHz音频系统
12288000Hz
48kHz × 256
48kHz音频系统
22579200Hz
44.1kHz × 512
高分辨率音频
24576000Hz
48kHz × 512
高分辨率音频
45158400Hz
44.1kHz × 1024
专业音频
49152000Hz
48kHz × 1024
专业音频
0
-
关闭PLL,进入低功耗

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
sw_pll_15q16_t
比例系数,影响响应速度
0x1000 (0.25)
Ki
sw_pll_15q16_t
积分系数,影响稳态精度
0x0100 (0.0156)
Kii
sw_pll_15q16_t
双积分系数,增强稳定性
0x0010 (0.00098)
loop_rate_count
size_t
每N次调用执行一次控制
16
pll_ratio
size_t
MCLK与参考时钟的比值
256
num_lut_entries
size_t
LUT表条目数
256
nominal_lut_idx
unsigned
标称频率LUT索引(中点)
128
ppm_range
unsigned
频率调整范围(ppm)
2000

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. 初始化PFD    sw_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*x    sw_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.5        frac_16bit = int(fractional_part * 65536)        lut_table.append(frac_16bit)    return lut_table

LUT表特性:

参数
说明
典型值
num_entries
LUT条目数
256
freq_range
频率覆盖范围
±2000ppm
resolution
频率分辨率
~8ppm/步
format
数据格式
16位有符号整数
storage
存储大小
512字节(256×2)

4.17 SDM噪声整形原理

SDM模式采用高阶Sigma-Delta调制技术,通过噪声整形将量化噪声推到高频,从而获得更高的频率分辨率。

SDM噪声整形传递函数:

H(z) = (1 - z^-1)^N其中N为SDM阶数,典型值为2或3

SDM架构:

噪声整形效果:

SDM阶数
噪声衰减斜率
适用场景
1阶
-6dB/倍频
低复杂度场景
2阶
-12dB/倍频
标准应用
3阶
-18dB/倍频
高精度要求

4.18 资源需求对比

资源类型
LUT模式
SDM模式
说明
线程数
1
2
SDM需要独立的调制器线程
内存占用
~2KB
~4KB
SDM额外的IIR和累加器状态
时钟块
1
1
两者相同
端口
1 (参考输入)
1 (参考输入)
两者相同
CPU负载
~5 MIPS
~10 MIPS
SDM额外的调制计算
频率分辨率
中等(由LUT步长决定)
高(SDM噪声整形)
SDM更优
抖动性能
低
中等
LUT更优

五、配置参数与编译选项

5.1 编译宏

宏名称
默认值
有效范围
作用
SW_PLL_LUT_ENABLE
1
0/1
启用LUT模式支持
SW_PLL_SDM_ENABLE
1
0/1
启用SDM模式支持
SW_PLL_MAX_LOOP_BW_HZ
10000
100-50000
最大环路带宽

5.2 运行时参数

参数
类型
有效值
作用
ref_clk_khz
unsigned
10-100000
参考时钟频率
target_clk_khz
unsigned
10000-2000000
目标输出频率
loop_bw_hz
unsigned
100-50000
环路带宽

六、错误处理与已知问题

6.1 官方已知问题

编号
描述
影响
修复状态
依据
#1
SDM模式在高频时抖动较大
高采样率音频
未修复
lib_sw_pll/CHANGELOG.rst
#2
LUT模式启动时间较长
快速启动场景
未修复
lib_sw_pll/CHANGELOG.rst

6.2 隐式限制

  • DCO频率范围受限(典型±2000ppm)
  • 参考时钟必须稳定,不支持抖动较大的输入
  • 环路带宽设置过大可能导致不稳定

七、资源需求与性能指标

7.1 内存占用

部分
LUT模式
SDM模式
.text
~4KB
~6KB
.data
~512B
~1KB
.bss
~1.5KB
~3KB

7.2 CPU需求

模式
典型MIPS
峰值MIPS
LUT模式
~5
~8
SDM模式
~10
~15

八、典型应用场景与示例

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)
  • 需要硬件级抖动性能的场景
  • 需要快速频率切换的场景

十、参考文档

  1. lib_sw_pll/README.rst
     - 模块概述和特性说明
  2. lib_sw_pll/doc/rst/lib_sw_pll.rst
     - 详细技术文档
  3. lib_sw_pll/lib_sw_pll/api/sw_pll.h
     - API定义
  4. lib_sw_pll/examples/
     - 示例应用
  5. lib_sw_pll/CHANGELOG.rst
     - 版本变更历史
  6. lib_sw_pll/python/sw_pll/sw_pll_sim.py
     - PLL仿真工具
  7. XMOS xcore.ai Programming Guide - 时钟控制章节

版权声明:本文基于XMOS lib_sw_pll (v3.2.0)进行技术分析,仅供学习交流使用。整理时间:2026年作者:稳态临界

相关学习资料