PYNQ 框架 - 时钟系统 + pl_clk 时钟输出不准确问题

ops/2024/11/28 0:20:29/

目录

1. 简介

2. PS 时钟计算

2.1 计算框架

2.2 KV260 的参考时钟

2.3 PL_CLK 设置

3. 测试

3.1 Block design

3.2 引脚绑定

3.3 使用 AD2 测量

3.4 调整分频

PYNQ%20%E6%97%B6%E9%92%9F%E9%A9%B1%E5%8A%A8-toc" style="margin-left:0px;">4. PYNQ 时钟驱动

4.1 源码解析

4.2 查看 PL_CLK

4.3 配置 PL_CLK

5. 总结


1. 简介

ZYNQ MPSoC 具有比较复杂的时钟系统。

PS 的时钟系统为处理器、外设、互连以及其他系统元素生成时钟。有五个系统 PLL 用于生成高频信号,这些信号被用作 LPD 和 FPD 中几十个时钟发生器的时钟源

LPD 中有两个系统 PLL 时钟单元,FPD 电源域中有三个。每个 PLL 单元在其输出上有两个时钟分频器;LPD 中一个,FPD 中一个。这些时钟分频器可以从一个 PLL 提供两种不同的时钟频率(在两个时钟域中)。对于跨电源域的时钟,最大时钟输出频率稍低。

每个系统 PLL 单元都有推荐用途,但各个时钟发生器可以从路由到它的三个 PLL 时钟中选择一个。

系统PLL单元位于LPD和FPD电源域:

  • LPD PLLs:
    • I/O PLL(IOPLL):为所有低速外设和部分互连提供时钟。
    • RPU PLL(RPLL):为 RPU MP Core 和部分互连提供时钟。
  • FPD PLLs:
    • APU PLL(APLL):为APU MPCore时钟和部分互连提供时钟。
    • Video PLL(VPLL):为视频I/O提供时钟。
    • DDR PLL(DPLL):为DDR控制器和部分互连提供时钟。

PS:最近使用 KV260 生成一些时钟,发现使用 PS 输出的 pl_clk 时钟和预设的有较大差异,因此通过实现验证猜想。

2. PS 时钟计算

2.1 计算框架

非常重要,必须理解这五个 PLL 的计算过程,方可手动配置分频参数。

1)PS 输入参考时钟

在以下窗口输入,一般的值为 33.333 MHz。

2)PLL 乘数

PS 输入参考时钟 * PLL 乘数 = VCO 输出时钟。

3)DIV2 分频

该分频为默认的 2 分频,可以选择取消。(需要勾选 Enable Manual Mode)

4)Divisors 分频

一个 6-bit 的可编程分频器。

5)PLL 输出

2.2 KV260 的参考时钟

在《DS987 - Kria K26 SOM Data Sheet》中,可以查看 SOM 系统结构图,包含该系统使用的33.33 MHz 晶振。

2.3 PL_CLK 设置

有四个 PS 至 PL 的时钟输出可以设定,我们先设定两个,分别为 100MHz 和 50MHz。

系统默认使用 IOPLL 作为时钟来源,I/O PLL 常用于为所有低速外设和部分互连提供时钟。

系统将自动推断分频值,如上图所述。

3. 测试

3.1 Block design

其中,counter_flip 模块用于按照指定值对时钟进行分频。目的方便使用 IO 口对时钟信号进行测量。而 clk_wiz_0 对 pl_clk1 进行二倍频。

counter_flip 的分频倍数为 100,即 PL_CLK 频率 = 实测频率 × 100。

Verilog 代码如下:

module counter_flip(input clk,                    // 时钟信号input rst_n,                  // 复位信号output reg sig_out = 0        // 输出信号,初始为0
);parameter MAX_COUNT = 49;        // 目标计数值reg [7:0] count = 8'd0;           // 8位计数器,初始值为0always @(posedge clk or negedge rst_n) beginif (!rst_n) begincount   <= 8'd0;          // 复位时计数器清零sig_out <= 0;             // 复位时输出也清零end else beginif (count == MAX_COUNT) begincount   <= 8'd0;      // 当计数达到最大值时,计数器清零sig_out <= ~sig_out;  // 并翻转输出end else begincount <= count + 1;   // 否则计数器递增endend
endendmodule

3.2 引脚绑定

sig_out_0 -> H12 -> J2.1 -> AD2.CH1
sig_out_1 -> E10 -> J2.3 -> AD2.CH3

3.3 使用 AD2 测量

使用 PYNQ 框架加载 bit 文件,然后使用 AD2 测量。 

1)Channel 1,vivado IDE 显示输出的频率应为 50 MHz,实际测量如下:

测量的频率是:333.33 kHz。

分频倍频数是:100。

那么 pl_clk0 的实际输出是:333.33 kHz * 100 = 33.333 MHz。

2)Channel 2,vivado IDE 显示输出的频率应为 200 MHz,实际测量如下:

测量的频率是:1.3333 MHz。

分频倍频数是:100。

那么 pl_clk1*2 的实际输出是:1.3333 MHz * 100 = 133.33 MHz。 

3.4 调整分频

参考 2.1 计算框架 中的参数,IOPLL 输出时钟频率是 499.995 MHz,理论上:

  • PL0 = 499.995 MHz / 30 = 16.6665 MHz
  • PL1 = 499.995 MHz / 15 = 33.333 MHz

明显不对。说明 IOPLL 的输出频率并不是 499.995 MHz。

直接放弃在 Vivado IDE 中配置 PL_CLK 时钟,通过 PYNQ 框架中的时钟类来调整。

如下:

from pynq.ps import ClocksClocks.set_pl_clk(0, 20, 1)
Clocks.set_pl_clk(1, 10, 1)

再次通过 AD2 进行测量:

1)Channel 1,vivado IDE 显示输出的频率应为 50 MHz,实际测量如下:

测量的频率是:499.99 kHz。

分频倍频数是:100。

那么 pl_clk0 的实际输出是:499.99 kHz * 100 = 49.999 MHz。

2)Channel 2,vivado IDE 显示输出的频率应为 200 MHz,实际测量如下:

测量的频率是:1.3333 MHz。

分频倍频数是:100。

那么 pl_clk1*2 的实际输出是:2.0000 MHz * 100 = 200.00 MHz。 

Channel 1 与 Channel 2 的时钟均符合预期。

PYNQ%20%E6%97%B6%E9%92%9F%E9%A9%B1%E5%8A%A8">4. PYNQ 时钟驱动

4.1 源码解析

源码地址:

/usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/ps.pyorhttps://pynq.readthedocs.io/en/v3.0.0/_modules/pynq/ps.html#Clocks

1) 全局变量

  • ZYNQ_ARCH、ZU_ARCH,这些常量用于区分不同的硬件架构。
  • CPU_ARCH_IS_SUPPORTED 和 CPU_ARCH_IS_x86 用于检测当前运行环境的 CPU 架构是否受支持。

2)时钟寄存器定义

  • 定义了一系列字典,如 ZYNQ_PLL_FIELDS、ZU_CLK_FIELDS 等,这些字典包含了不同寄存器的配置信息(如位偏移、位宽、描述等)。

3)寄存器地址映射

  • 定义了不同硬件架构下的寄存器地址映射,如 ZYNQ_SLCR_REGISTERS 和 ZU_CRL_REGISTERS。

4)_ClocksMeta 元类

  • 这是一个元类,用于定义时钟类的一些属性和方法。它定义了一系列属性(如 cpu_mhz、fclk0_mhz 等),这些属性通过 _instance 属性动态获取其实例的相应方法。

5)_ClocksBase 基类

  • 这是一个抽象基类,定义了 get_pl_clk 和 set_pl_clk 等方法,这些方法用于获取和设置 PL 时钟的频率。
  • _get_src_clk_mhz 和 _get_2_divisors 是辅助方法,用于计算时钟频率和分频值。

6)特定架构的时钟类

  • _ClocksUltrascale 和 _ClocksZynq 是两个具体实现类,分别适用于 Zynq Ultrascale 和 Zynq 7-Series 硬件架构。这些类实现了基类中定义的抽象方法,以及一些特定于硬件的功能,如处理PLL的配置。

7)Clocks 用户类

  • Clocks 类是用户接口,允许用户获取和设置 CPU 和 PL 时钟。它使用 _ClocksMeta 作为其元类,从而继承了一系列动态属性和方法。

4.2 查看 PL_CLK

1)查看 clock_dict

ol.clock_dict
---
{0: {'enable': 1, 'divisor0': 15, 'divisor1': 1},1: {'enable': 1, 'divisor0': 30, 'divisor1': 1},2: {'enable': 0, 'divisor0':  4, 'divisor1': 1},3: {'enable': 0, 'divisor0':  4, 'divisor1': 1}}

2)查看 CPU 时钟频率

from pynq.ps import Clocksprint(f"CPU CLK: {Clocks.cpu_mhz}")---
CPU CLK: 1333.32

3)查看 PL_CLK 时钟

from pynq.ps import Clocksprint(f"FCLK0: {Clocks.fclk0_mhz}")
print(f"FCLK1: {Clocks.fclk1_mhz}")
print(f"FCLK2: {Clocks.fclk2_mhz}")
print(f"FCLK3: {Clocks.fclk3_mhz}")--
FCLK0: 66.666
FCLK1: 33.333
FCLK2: 99.999
FCLK3: 99.999

4.3 配置 PL_CLK

# 直接指定频率,PYNQ 自动计算分频系数
Clocks.set_pl_clk(0, None, None, 100) # 指定 PL0 输出 100 MHzprint(f"FCLK0: {Clocks.fclk0_mhz}")
---
FCLK0: 99.999

 Or

# 指定分频系数
Clocks.set_pl_clk(0, 15, 1)# 可以先不分频,看看该 PLL 当前的输出
Clocks.set_pl_clk(0, 1, 1)
print(f"FCLK0: {Clocks.fclk0_mhz}")
---
FCLK0: 999.99# 再设定分频系数
Clocks.set_pl_clk(0, 10, 1)
print(f"FCLK0: {Clocks.fclk0_mhz}")
---
FCLK0: 99.999

5. 总结

  • 使用 PYNQ 加载 bit 后,发现 PL_CLK 的输出值不准确。
  • Vivado IDE 中,ZU 时钟输出 DIV2 似乎并未生效。
  • 通过 PYNQ Clocks 类,可以动态调节 PL_CLK 输出。


http://www.ppmy.cn/ops/137214.html

相关文章

戴尔 AI Factory 上的 Agentic RAG 搭载 NVIDIA 和 Elasticsearch 向量数据库

作者&#xff1a;来自 Elastic Hemant Malik, Dell Team 我们很高兴与戴尔合作撰写白皮书《戴尔 AI Factory with NVIDIA 上的 Agentic RAG》。白皮书是一份供开发人员参考的设计文档&#xff0c;概述了实施 Agentic 检索增强生成 (retrieval augmented generation - RAG) 应用…

一文掌握如何用python开发小程序

1. 准备工作 - 安装必要的工具和库 - 首先要安装Python开发环境&#xff0c;推荐使用Python 3.x版本。可以从官方网站&#xff08;https://www.python.org/downloads/&#xff09;下载安装包进行安装。 - 对于小程序开发&#xff0c;需要安装相应的框架。如果是开发微信小程…

二,[ACTF2020 新生赛]Include1感谢 Y1ng 师傅供题。

进入靶场后&#xff0c;发现tips可以点击 点击后进入此页面 猜测此为文件包含漏洞,构造payload&#xff0c;并成功得到base64编码后的源码 详解payload&#xff1a; php://filter/readconvert.base64-encode/resourceflag.php 1.php://filter是PHP中的一个流封装协议&#xf…

【项目日记】仿mudou的高并发服务器 --- 实现基础高并发服务器基础模块

一个人知道自己为什么而活&#xff0c; 就可以忍受任何一种生活。 --- 尼采 --- ✨✨✨项目地址在这里 ✨✨✨ ✨✨✨https://gitee.com/penggli_2_0/TcpServer✨✨✨ 仿mudou的高并发服务器 1 高并发服务器2 事件管理 Channel模块3 多路转接 Poller模块4 反应堆 EventLoop模…

3、Python中的条件语句:if, else 和 elif

在Python编程中,条件语句是控制程序执行流程的重要工具。 它允许程序根据特定条件的真或假来选择不同的代码路径。这种能力使得程序能够做出决策,从而变得更加灵活和强大。 今天,我们将详细介绍Python中的条件语句,包括 if、else 和 elif 的用法。 1. if 语句 if 语句用…

Spring Boot 动态数据源切换

背景 随着互联网应用的快速发展&#xff0c;多数据源的需求日益增多。Spring Boot 以其简洁的配置和强大的功能&#xff0c;成为实现动态数据源切换的理想选择。本文将通过具体的配置和代码示例&#xff0c;详细介绍如何在 Spring Boot 应用中实现动态数据源切换&#xff0c;帮…

docker私有仓库的介绍以及 Docker registry 安装

文章目录 什么是 Docker Registry镜像仓库工作机制常用的镜像仓库快速部署 Docker Registry上传镜像下载镜像部署域名地址的Docker registry 什么是 Docker Registry 镜像仓库&#xff08;Docker Registry&#xff09;负责存储、管理和分发镜像&#xff0c;并且提供了登录认证…

饿汉模式和懒汉模式(面试)

目录 饿汉模式和懒汉模式&#xff08;面试&#xff09;饿汉模式懒汉模式多线程环境下是否线程安全呢饿汉模式是线程安全的懒汉模式是线程不安全的怎么才能让懒汉模式变成线程安全的呢&#xff1f; 饿汉模式和懒汉模式&#xff08;面试&#xff09; 饿汉模式 在Singleton类内部…