基于KV260的基础视频链路通路(MIPI+Demosaic+VDMA)
基于 KV260 + PCAM_5C 构建视频通路,通过 PYNQ 可视化图像,用以进一步处理图像数据,从 MIPI 至图像数据,包括如下要点:在 PYNQ 下通过 IIC 配置 OV5640配置 MIPI CSI-2 Rx subsystem 经验汇总AXI4-Stream 基础设施配置 Sensor Demosaic经验汇总配置 AXI VDMA经验汇总Sensor OV5640 时钟树图像数
目录
5.6.5 cfg_1080p_30fps_336M_mipi
5.6.6 cfg_1080p_30fps_336M_1lane_mipi
1. 简介
1.1 要点
基于 KV260 + PCAM_5C 构建视频通路,通过 PYNQ 可视化图像,用以进一步处理图像数据,从 MIPI 至图像数据,包括如下要点:
- 在 PYNQ 下通过 IIC 配置 OV5640
- 配置 MIPI CSI-2 Rx subsystem 经验汇总
- AXI4-Stream 基础设施
- 配置 Sensor Demosaic 经验汇总
- 配置 AXI VDMA 经验汇总
- Sensor OV5640 时钟树
- 图像数据流波形抓取
1.2 背景
直接使用 KV260 Base Overlay 构建视频通路,初始化 OV5640 时,会陷入了循环或者无法初始化。如下是官方论坛的问题汇总:
1.2.1 Got stuck
I am trying to connect the Diligent PCam5C with my Kria KV260 board and use the supplied notebook mipi_to_displayport 4, but it seemed to get stuck in a loop at this point
“frame = mipi.readframe()”
1.2.2 Can't be Initialized
2. Overlay
2.1 参考 Overlay
2.1.1 KV260 Base
https://github.com/Xilinx/Kria-PYNQ/tree/main/kv260/basehttps://github.com/Xilinx/Kria-PYNQ/tree/main/kv260/base该 Overlay 支持 KV260 的 Raspberry Pi 摄像头和 PMOD 接口。可以将 Digilent Pcam 5C 摄像头连接到 KV260,并通过 Jupyter 笔记本进行控制。此外,PMOD 接口上支持多种 Grove 和 PMOD设备,所有这些设备都可以通过可编程逻辑中的 Xilinx Microblaze 处理器进行控制。
2.1.2 Pynq-CV-OV5640
在这个 Overlay 中,图像是从连接到 PL 侧的 OV5640 摄像头捕获的,并且该覆盖层包含了几种加速的图像处理算法,可以选择启用哪种算法,而无需下载新的比特流。在示例应用程序 Notebook 中,可以配置 OV5640 摄像头,获取捕获的图像并在笔记本上显示。处理后的视频流将显示在连接到 PL 侧的 HDMI 显示器上。

这个 Overlay 包含以下加速的图像处理算法:
- rgb2hsv(RGB 转 HSV)
- subsample(子采样)
- equalizehist(直方图均衡化)
- gaussianBlur(高斯模糊)
- sobel(Sobel 边缘检测)
- canny(Canny 边缘检测)
- dilation(膨胀)
- erosion(腐蚀)
2.2 自建 Overlay
以上提到的两个 Overlay:
- KV260 Base Overlay 无法使用 Pcam 5C(无法完成初始化)。
- Pynq-CV-OV5640 仅支持 Pynq-Z2 板卡。
因此自建一个极简的 MIPI 调试平台如下:

2.2.1 IIC IP
在 Base 中,axi_iic 模块使用了 100kHz 的总线频率,需要修改至 400kHz,否则会导致初始化失败。

2.2.2 MIPI CSI-2 Rx
MIPI CSI-2 Rx Subsystem v5.1
MIPI CSI-2 RX Subsystem 通过 PPI(Pixel Parallel Interface)从 MIPI D-PHY 核心接收每个通道 8 位数据,支持最多 4 个通道。通过 PPI 接收到的字节数据随后由低级协议模块处理,以提取真实的图像信息。最终提取的图像通过使用 AXI4-Stream 协议,被提供给用户/处理器接口。无论通道数量如何,通道管理块始终处理从 PPI 接收的 32 位数据。
IP 的主要参数如下:

参数说明
- Pixel Format,由 sensor 决定,参考 4.4 Pixel 格式。
- 启用 CRC:当设置此选项时,将对数据包的有效载荷进行 CRC 校验。
-
启用活动通道:当设置此选项时,内核支持动态配置活动通道的数量,该数量可从内核生成时选择的最大通道数中调整,使用参数 Serial Data Lanes。例如,当 Serial Data Lanes 设置为 3 时,可以通过协议配置寄存器将活动通道的数量设置为 1、2 或 3。当活动通道设置大于串行通道设置时,内核会通过中断状态寄存器的第 21 位报告错误。
2.2.3 AXI4-S Subset
AXI4-Stream Subset Converter v1.1
属于 AXI4-Stream Infrastructure IP Suite (PG085) 的一个子 IP。

1)AXI4-Stream Subset Converter 参数配置

一般情况下,选择自动就可以,工具会自动推断前后级是否需要该信号。例如 TSTRB,工具发现前级没有此信号,后级也不需要,因此属性都是“No”。
Extra Settings(重映射字符串)
重映射用户参数的格式遵循类似于 Verilog 向量连接的语法。
- 直接传递:对于 TUSER,字符串 tuser[95:0] 表示将 SI 所有信号不加修改地传递给 MI。
- 位切片元素:对于 TDATA,字符串 tdata[9:2] 表示将 SI 的数据(16位)截取其中 [9:2] 的8位传递给 MI。
- 常量补充:如果 MI 的 TDATA 信号为24位而 SI 的 TDATA 信号为16位,重映射字符串8'b00000000,tdata[15:0] 将常量0分配给 MI 信号的高8位,并在低16位传递 SI 的 TDATA。
- 重复补充:字符串 tdata[7:0], tdata[7:0] 将 SI 的 TDATA 最低有效字节在 MI 信号上重复两次。
- 重映射字符串可以引用相应 SI 信号的位或其他可重映射SI信号的位。例如,TDATA 的重映射字符串可以引用 TUSER SI 信号的位。
- 仅支持二进制格式的常量元素。不支持 h 格式。
2)AXI4-Stream 基础设施套件
AXI4-Stream Infrastructure IP Suite (PG085) 提供了如下 IP:
- AXI4-Stream Broadcaster
- AXI4-Stream Clock Converter
- AXI4-Stream Combiner
- AXI4-Stream Data FIFO
- AXI4-Stream Data Width Converter
- AXI4-Stream Register Slice
- AXI4-Stream Subset Converter
- AXI4-Stream Switch
- AXI4-Stream Interconnect
这些集合能够执行数据切换/路由、数据宽度转换、流水线、时钟转换和数据缓冲。
2.2.4 Demosaic
Sensor Demosaic v1.1

- 最大列/行数:指定 IP 核心在运行时可以处理的最大视频列/行数。任何小于该数值的视频宽度都可以通过 AXI4-Lite 控制接口进行配置,而无需重新生成 IP 核。
- 插值方法:
- Fringe Tolerant Interpolation(边缘容忍插值):生成的图像较为柔和,并抑制边缘伪影。对于可能引入颜色边缘伪影的低成本光学器件,建议使用此选项。
- High Resolution Interpolation(高分辨率插值):适用于高质量光学器件以及高分辨率的应用。此方法会使用更多的 BRAM 和 slice,并大约使 DSP48 的数量增加一倍。
- Horizontal Zipper Artifact Removal(水平拉链伪影去除):此选项添加一个后处理平滑滤波器,以去除水平拉链伪影。后处理滤波器会使输出图像变得柔和,并消耗一些额外的 Slice 资源。
- Use UltraRAM for Line Buffers(使用 UltraRAM 作为行缓冲):在 UltraScale+ 器件中,用于像素插值的行缓冲可以存储在 UltraRAM 中,而不是块 RAM。
1)基础地址空间
该 IP 核由 HLS 生成,所以其基础寄存器空间与 HLS IP 一致。
// ==============================================================
// control
// 0x0 : Control signals
// bit 0 - ap_start (Read/Write/COH)
// bit 1 - ap_done (Read/COR)
// bit 2 - ap_idle (Read)
// bit 3 - ap_ready (Read/COR)
// bit 7 - auto_restart (Read/Write)
// bit 9 - interrupt (Read)
// others - reserved
// 0x4 : Global Interrupt Enable Register
// bit 0 - Global Interrupt Enable (Read/Write)
// others - reserved
// 0x8 : IP Interrupt Enable Register (Read/Write)
// bit 0 - enable ap_done interrupt (Read/Write)
// bit 1 - enable ap_ready interrupt (Read/Write)
// others - reserved
// 0xc : IP Interrupt Status Register (Read/COR)
// bit 0 - ap_done (Read/COR)
// bit 1 - ap_ready (Read/COR)
// others - reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)
2)额外的寄存器
| Address | Register Name | Access Type | Register Description |
|----------|---------------|-------------|-------------------------------------------------|
| 0x0010 | Active Width | R/W | Number of Active Pixels per Scanline |
| 0x0018 | Active Height | R/W | Number of Active Lines per Frame |
| 0x0028 | Bayer Phase | R/W | Bits 1-0: Bayer sampling grid starting position |
3)Bayer Phase
Bayer Phase Register Combination Definitions
该 IP 核支持所有四种可能的拜耳相位(Bayer Phase)组合。

可以参照章节 4.4 Pixel 格式,对应关系如下:
IP OV5640(0x4300[3:0])
--------------------
3 -> 0x0: BGBG...
GRGR...
2 -> 0x1: GBGB...
RGRG...
1 -> 0x2: GRGR...
BGBG...
0 -> 0x3: RGRG...
GBGB...
4)吞吐量
传感器去马赛克核心的像素吞吐量由 ap_clk 与 Sample per clock 设置的乘积定义。
例如,在 ap_clk 时钟频率为 300 MHz 且 Sample per clock = 2 的情况下,传感器去马赛克能够实现600万像素的吞吐率,可以以处理 60Hz 的 4K 分辨率。
2.2.5 Pixel Pack

输入 Demosaic 为 8bit RGB RAW,输出为 RGB888 24bit 数据,这正是 pixcel_pack 模块输入端接收的数据。
pixel_pack kernel,从一个输入流 stream_in_24 读取24位像素数据,并根据给定的模式 mode 将这些数据转换和打包成32位的数据,然后将转换后的数据写入到输出流 stream_out_32 中。
Kernel 中定义了五种模式:V_24, V_32, V_8, V_16, V_16C,每种模式都有其特定的处理逻辑。
2.2.6 AXI VDMA
AXI Video Direct Memory Access v6.3

1)Basic Options

- Address Width,MAXI 总线的地址位宽,即 VDMA 的寻址范围。
- Frame Buffers,缓存帧的数量。
- Enable Channel,可以单独启用读/写通道。
2)Advanced Options

- Enable Asynchronous Mode,启用异步模式,
此设置允许 m_axi_mm2s_aclk、m_axi_s2mm_aclk、s_axi_lite_aclk、m_axis_mm2s_aclk 和 s_axis_s2mm_aclk 互相异步。当禁用异步时钟时,所有时钟必须频率相同且来自同一源头。
- 启用垂直翻转
当选择此选项时,在启用 S2MM 的情况下将图像垂直翻转。
- Fsync 与 Genlock,请参见 IP 详解部分。
2.3 构建 XSA
<ov5640>/Pynq-CV-OV5640-master/boards/Pynq-Z2/ov5640/bitstream
---
build_bitstream.tcl
check_cv_ov5640.tcl
cv_ov5640.bit
cv_ov5640.hwh
cv_ov5640.tcl
cv_ov5640.xdc
__init__.py
makefile

3. IP 详解
3.1 MIPI CSI-2 Rx Subsystem
3.1.1 IP 基本信息
IP 版本:MIPI CSI-2 Rx Subsystem v5.1

- dphy_clk_200M:Used for control logic and input to MMCM
- video_aclk:视频处理时钟域
- video_aresetn:视频处理时钟域复位
- rxbyteclkhs:PPI high-speed receive byte clock
3.1.2 Subsystem Options

- Pixel Format
由传感器设置决定的。以 OV5640 为例, 由以下3个寄存器决定:
[0x3034, 0x1A], #[6:4]=001 PLL charge pump, [3:0]=1010 MIPI 10-bit mode
[0x4300, 0x00], #[7:4]=0x0 Formatter RAW, [3:0]=0x0 BGBG/GRGR
[0x501f, 0x03], #[2:0]=0x3 Format select ISP RAW (DPC)
- VFB(Video Format Bridge)
视频格式桥接器,当此选项被禁用时,子系统将不考虑像素格式选择,可以自定义配置流数据宽度 (TDATA)。
- Support CSI Spec V2_0
支持 CSI V2.0 功能(RAW16, RAW20 支持和 VCX 功能支持)。
- Support VCX Feature
MIPI CSI-2 标准的 v2.0 版本中特定的 VCX 支持功能用于将可用虚拟通道的最大数量扩展到 16 个。默认不启用,支持4个 VC。

3.1.3 DPHY Options

线速率(Line Rate)要与摄像头 MIPI 输出速率匹配。简易原则如下:
- 1500 Mbps:IP 可以从 TX 接收 600-1500Mbps 的数据速率。
- 599 Mbps:IP 可以从 TX 接收 300-599Mbps 的数据速率。
- 299 Mbps:IP 可以从 TX 接收 150-299Mbps 的数据速率。
- 149 Mbps:IP 可以从 TX 接收 80-149Mbps 的数据速率。
MIPI D-PHY RX (Slave) Core Architecture for UltraScale+ Families

- HS Timeout Counter:用于高速模式下的超时计数。当数据在高速模式下传输时,如果数据在预定时间内没有正确接收,核心会生成超时并进入停止状态。
- ESC Timeout Counter:用于逃逸模式下的超时计数。在逃逸模式下,如果数据在预定时间内没有正确接收,核心会生成超时并进入停止状态。
展开 MIPI CSI-2 Rx Subsystem 可以看到其中包含的 MIPI D-PHY 子模块,也可以查看 PPI 包含的信号,非常多。

3.1.4 CSI-2 Options

- CSI2 Controller Register Interface:AXI4-lite 寄存器接口。禁用此选项可以减少资源使用。
- Embedded non-image Interface:选择此选项以通过单独的 AXI4-Stream 接口处理和卸载嵌入式非图像 CSI-2 数据包(数据类型代码为 0x12)。如果未选择此选项,则此类数据包将不被处理,并被 CSI-2 RX 控制器忽略。
- Filter User Defined data types:选择此选项以过滤用户定义的数据类型(0x30 到 0x37),并且不在图像接口上输出(即使启用了过滤,未支持的 ErrId ISR[8] 也不会被设置)。如果未选择此选项,则此类数据包会被处理并在图像接口上显示。
- Line Buffer Depth:用于适应输出视频接口节流的内部 RAM 深度。可选值为 128、256、512、1024、2048、4096、8192 或 16384。用户可以将其设置为不会导致行缓冲区满状态的最低值。
Data Type Classes:

3.1.5 VFB Options
1)Video Format Bridge (VFB) 的主要作用:
- 数据过滤:处理并过滤来自 CSI-2 RX 控制器的 AXI4-Stream 数据,仅保留用户选择的数据类型(主要是 RAW8 和用户定义的字节型数据类型)。
- 生成输出:根据数据类型信息进一步处理这些数据,并根据每时钟周期请求的像素数量(PPC)生成输出。输出接口遵循 AXI4-Stream Video IP 和系统设计指南中定义的协议。VFB 支持多种数据类型,允许一种主要数据类型用于像素数据,另一种用户定义的字节型数据类型用于元数据。
- 数据对齐:在处理未对齐传输时,VFB 无法指定输出接口的部分最终输出(TKEEP),因此需要丢弃未对齐传输中最后节拍的不需要字节。
2)VFB 的启用与禁用:
当 VFB 选项被禁用时,子系统将不考虑像素格式的选择,可以配置流数据宽度(TDATA)。

3)VFB 的三个参数
- Allowed VC:虚拟通道过滤。
- Pixel Per Clock:每个时钟周期传递的像素数量。比如,OV5640 输出格式为 RGB RAW 10bit,PPC=1,那么 video_out_tdata 位宽是 16bit,如果输出格式为 RGB888,PPC=1,那么 video_out_tdata 位宽将是 24bit。

- TUSER Width:用户自定义的字段。详见下表。
video_out_tuser[n-1:0],其中 n 是根据在 Vivado IDE 中选择的 TUSER Width 确定的。
| Output Range | Description |
|--------------|------------------------------------|
| 95-80 | CRC |
| 79-72 | ECC |
| 71-70 | Reserved |
| 69-64 | Data Type |
| 63-48 | Word Count |
| 47-32 | Line Number |
| 31-16 | Frame Number |
| 15-2 | Reserved |
| 1 | Packet Error |
| 0 | Start of Frame |
3.1.6 IP 地址空间
MIPI CSI-2 RX Subsystem 地址映射分为以下两个区域:
- MIPI CSI-2 RX Controller core
- MIPI D-PHY core
每个 IP 核被分配了 4K 的地址空间:
| IP Cores | Offset |
|--------------------------|----------|
| MIPI CSI-2 RX Controller | 0x0_0000 |
| MIPI D-PHY | 0x0_1000 |
1)MIPI CSI-2 RX Controller 寄存器
| Address Offset | Register Name | Description |
|----------------|---------------------------------|-----------------------------------|
| 0x00 | Core Configuration Register | Core configuration options |
| 0x04 | Protocol Configuration Register | Protocol configuration options |
| 0x10 | Core Status Register | Internal status of the core |
| 0x20 | Global Interrupt Enable Register| Global interrupt enable registers |
| 0x24 | Interrupt Status Register | Interrupt status register |
| 0x28 | Interrupt Enable Register | Interrupt enable register |
| 0x30 | Generic Short Packet Register | Short packet data |
| 0x34 | VCX Frame Error Register | VCX Frame Error Register |
| 0x3C | Clock Lane Information Register | Clock lane status information |
---------------------------------------------------------------------------------------|
# Lane<n> Information Registers
| 0x40 | Lane0 Information | Lane 0 status information |
| 0x44 | Lane1 Information | Lane 1 status information |
| 0x48 | Lane2 Information | Lane 2 status information |
| 0x4C | Lane3 Information | Lane 3 status information |
(0x00) Core Configuration Register
- bit 0, Core Enable, R/W,1: Enable, 0: Disable
- bit 1, Soft Reset, R/W, 1: Reset, 0: Normal
2)MIPI D-PHY 寄存器
| Offset | Name | Width | Access | Description
|--------|------------------|--------|--------|----------------------------------------------------------------
| 0x0 | CONTROL | 32-bit | R/W | Enable and soft reset control for PHY.
| 0x4 | IDELAY_TAP_VALUE | 32-bit | R/W | To program the tap values.
| 0x8 | INIT | 32-bit | R/W | Initialization timer.
| 0x10 | HS_TIMEOUT | 32-bit | R/W | Watchdog timeout in high-speed mode.
| 0x14 | ESC_TIMEOUT | 32-bit | R/W | Escape Mode Timeout.
| 0x18 | CL_STATUS | 32-bit | RO | Status register for PHY error reporting for clock Lane.
| 0x1C | DL0_STATUS | 32-bit | RO | Status registers for PHY error reporting for data lanes 1 to 4.
| 0x20 | DL1_STATUS | 32-bit | RO |
| 0x24 | DL2_STATUS | 32-bit | RO |
| 0x28 | DL3_STATUS | 32-bit | RO |
| 0x30 | HS_SETTLE | 32-bit | R/W | HS_SETTLE timing control for lane 1.
| 0x48 | HS_SETTLE | 32-bit | R/W | HS_SETTLE timing control for lanes2. (Up to 0x60, lane 8)
| 0x64 | DL4_STATUS | 32-bit | RO | Status registers for PHY error reporting for data lanes 5 to 8.
| 0x68 | DL5_STATUS | 32-bit | RO |
| 0x6C | DL6_STATUS | 32-bit | RO |
| 0x70 | DL7_STATUS | 32-bit | RO |
| 0x74 | IDELAY_TAP_VALUE | 32-bit | R/W | To program the tap values.
(0x00) CONTROL Registers
- bit 0, SRST, R/W(1: Reset, 0: Normal)
- bit 1, DPHY_EN, R/W(1: Enable, 0: Disable)
(0x18) CL_STATUS Register
| Bits | Name | Access | Default Value | Description |
|------|--------------|--------|---------------|--------------------------------------------------|
| 5 | ERR_CONTROL | RO | 0 | Clock lane control error. |
| 4 | STOP_STATE | RO | 0 | Clock lane is in the Stop state. |
| 3 | INIT_DONE | RO | 0 | Set after the lane has completed initialization. |
| 2 | ULPS | RO | 0 | Set to 1 when the core in ULPS (ULP State) mode. |
| 1:0 | MODE | RO | 0 | 2'b00: Low Power Mode (Control Mode) |
| | | | 0 | 2'b01: High Speed Mode |
| | | | 0 | 2'b10: Escape Mode |
(0x1C) DL_STATUS Register, (to 0x70)
| Bits | Name | Access | Default Value | Description |
|-------|------------|--------|---------------|--------------------------------------------------------------|
| 31:16 | PKT_CNT | RO | 0 | Number of packets received or transmitted on the data lane. |
| 6 | STOP_STATE | RO | 0 | Data lane is in the Stop state. |
| 5 | ESC_ABORT | R/W1C | 0 | Escape abort. |
| 4 | HS_ABORT | R/W1C | 0 | HS abort. | |
| 3 | INIT_DONE | RO | 0 | Set after the lane has completed initialization. |
| 2 | ULPS | RO | 0 | Set to 1 when the core is in ULPS mode. |
| 1:0 | MODE | RO | 0 | 2'b00: Low Power mode (control mode). |
| | | | | 2'b01: High Speed mode. |
| | | | | 2'b10: Escape mode. |
ESC_ABORT,当数据通道 Escape Timeout 终止(在接收情况下为 Escape Mode 超时,或在发送情况下为 Escape Mode 静默超时),该位被设置为1。
PKT_CNT,此字段使用 rxbyteclkhs 时钟进行更新,并且在对 D-PHY RX IP 复位时,RX 时钟通道必须处于高速模式。否则,该值不会在 MIPI D-PHY RX IP 配置中复位。
3.1.7 时钟域
1)Video Clock 时钟域
- IP 接口 rxbyteclkhs,PPI high-speed receive byte clock;
- IP 接口 video_aclk,视频时钟,用来处理数据。

2)rxbyteclkhs 时钟产生
rxbyteclkhs 接口由 CLK Lane 生成,范围是 10.000–187.500 MHz,从 Line Rate 除以 8 得出。
Table 31: MIPI D-PHY Clocking Details

3.1.8 video_aclk 计算

1)四分之一原则
CSI-2 RX Controller 中 lane Management 的读取路径在 32bit 据路径上运行(无论通道数量多少),并使用视频时钟来处理数据。因此,对于 line rate ≤ 1500 Mb/s 情况,所需的最低视频时钟应大于或等于有效 PPI 时钟的四分之一。
四分之一来自于 PPI 的宽度(32bit)除以 PCS 中每个 Lane 的宽度(8bit),32/8 = 4。

以下示例说明了这一点(四分之一原则):

- 对于 Line Rate=1000 Mb/s,1 Lane,通道管理块的写入有效速率为125 MHz。由于通道管理块的读取路径在32位数据路径上运行,所需的最小视频时钟为1000 MHz/8/4=31.25MHz。
- 对于 Line Rate=800 Mb/s,2 Lane,通道管理块的写入有效速率为100 MHz * 2。同理,所需的最小视频时钟为(800 MHz/8 * 2)/4=50MHz。
- 对于 Line rate=700 Mb/s,3 Lanes,通道管理块的写入有效速率为87.5 MHz * 3。同理,所需的最小视频时钟为(700 MHz/8 * 3)/4=65.625MHz。
- 对于Line rate=1200 Mb/s,4 Lanes,通道管理块的写入有效速率为150 MHz * 4。同理,所需的最小视频时钟为(1200 MHz/8 * 4)/4=150MHz。
除了上述的最小视频时钟要求外,确保在任何给定时间子系统的输出带宽大于或等于输入带宽同样重要。
2)包含像素数据类型计算过程(Line Rate < 1500MHz)
- 对于 Line Rate=1000 Mb/s,1 Lane,PPC=2,数据类型 RAW10,所需的最低视频时钟是(1000/10 *1) /2=50MHz,其中10是一个 RAW10 像素的位数。
- 对于 Line Rate=600 Mb/s,4 Lanes,PPC=1,数据类型 YUV420,所需的最低视频时钟是(600/8 *4)/1=300MHz,其中8是一个8位 YUV420 像素的位数。
- 对于 Line Rate=1200 Mb/s,4 Lanes,PPC=2,数据类型 YUV420,所需的最低视频时钟是(1200/8 *4)/2=300MHz,其中8是一个8位 YUV420 像素的位数。
3)有效的最低所需视频时钟
计算最低所需视频时钟的公式如下:
- video_aclk1(MHz) = Line Rate(Mb/s)*Data Lanes/(8*4)
- video_aclk2(MHz) = (Line Rate(Mb/s)*Data Lanes) /(PPC*Number of Bits Per Pixel)
有效的最低所需视频时钟为:
- video_aclk (MHz) = Max {video_aclk1, video_aclk2}
计算示例:
对于 Line Rate=600 Mb/s,4 Lanes,PPC=1,数据类型 YUV420,所需的最低视频时钟是(600*4)/(1*8)或更高,其中8是一个8位YUV420像素的位数。
- video_aclk1(MHz) = 600*4/(4*8) = 75MHz
- video_aclk2(MHz) = 600*4/(1*8) = 300MHz
最终的视频时钟为:
- video_aclk(MHz) = max{75MHz, 300MHz} = 300MHz
3.2 AXI4-Stream 基础设施
3.2.1 通用准则
第一步建立系统的拓扑结构。
这需要了解每个需要进行通信的 AXI4-Stream 主设备和从设备的主要接口特性。然后,需要根据所需的连接性将 AXI4-Stream 主设备和从设备分组到一个或多个 AXI4-Stream 基础设施块中,以便它们能够交换数据。
一般来说,应该尝试建立系统分区/拓扑,以使用多个较小/较简单的互连,而不是单个大型互连,特别是在设备数量较多的系统中。例如,N主x1从和1主xN从互连的组合通常在面积、延迟和吞吐量方面优于 MxN 互连。当性能或连接要求需要 MxN 互连时,应尽量限制端点数量或指定稀疏连接以减少资源使用。
确定 AXI4-Stream 基础设施系统的数量和拓扑结构后,下一步是根据需要调整每个 AXI4-Stream 基础设施系统以拥有正确的可选接口信号集和设置信号宽度。这将为AXI4-Stream基础设施设置接口信号集,以确保数据可以根据需要在整个系统中进行交换和路由。
最后,应优化和微调 AXI4-Stream 基础设施系统以适应其应用。这包括根据需要调整 FIFO、宽度转换器、时钟转换器、仲裁器和寄存器切片(流水线阶段),以平衡面积、时序、性能和易用性。
3.2.2 时钟
支持多时钟域的模块(S_AXIS_ACLK 和 M_AXIS_ACLK):
- AXI4-Stream Clock Converter
- AXI4-Stream Data FIFO.
支持单时钟域的模块:剩余其他模块。
当终端 IP 与核心交换的时钟具有相位对齐的整数倍时钟比率时,可以使用 AXI4-Stream 时钟转换器的同步模式。这通常在同一个 MMCM 或 PLL 驱动同步整数比率时钟时发生,因为 MMCM/PLL 也可以确保它们的时钟输出之间的相位对齐。AXI4-Stream 时钟转换器和 AXI4-Stream 数据 FIFO 的异步时钟模式允许连接的终端 IP 在与核心交换时钟完全无关的时钟频率或相位下运行。
时钟使能(ACLKEN 端口)的可选功能提供了一个额外的控制层级,用于门控时钟。时钟使能信号可用于控制哪些时钟边沿被视为实际的传输周期。时钟使能可用于诸如保护全局时钟缓冲器、通过步进时钟使能来调试系统中的操作状态,以及动态节能等目的。时钟使能可以在每个接口端口和核心交换机上独立控制。
可选的 AXI4-Lite 控制寄存器接口与 AXI4-Stream 时钟异步运行。
3.3 Pixel Pack
3.3.1 HLS 代码
#include <ap_fixed.h>
#include <ap_int.h>
#include "hls_stream.h"
#include <ap_axi_sdata.h>
typedef ap_axiu<24,1,0,0> narrow_pixel;
typedef ap_axiu<32,1,0,0> wide_pixel;
typedef hls::stream<narrow_pixel> narrow_stream;
typedef hls::stream<wide_pixel> wide_stream;
#define V_24 0
#define V_32 1
#define V_8 2
#define V_16 3
#define V_16C 4
void pixel_pack(narrow_stream& stream_in_24 ,
wide_stream& stream_out_32,
int mode,
ap_uint<8> alpha) {
#pragma HLS INTERFACE mode=axis register_mode=both depth=24 port=stream_in_24 register
#pragma HLS INTERFACE mode=axis register_mode=both depth=24 port=stream_out_32 register
#pragma HLS INTERFACE mode=s_axilite port=mode register
#pragma HLS INTERFACE mode=s_axilite port=alpha register
#pragma HLS INTERFACE mode=ap_ctrl_none port=return
bool last = false;
bool delayed_last = false;
narrow_pixel in_pixel;
wide_pixel out_pixel;
switch (mode) {
case V_24:
while (!delayed_last) {
#pragma HLS pipeline II=4
....
stream_out_32.write(out_pixel);
}
}
}
break;
case V_32:
while (!last) {
#pragma HLS pipeline II=1
....
stream_out_32.write(out_pixel);
}
break;
case V_8:
while (!delayed_last) {
#pragma HLS pipeline II=4
...
stream_out_32.write(out_pixel);
}
}
break;
case V_16:
while (!last) {
#pragma HLS pipeline II=2
....
stream_out_32.write(out_pixel);
}
break;
case V_16C:
while (!last) {
#pragma HLS pipeline II=2
...
stream_out_32.write(out_pixel);
}
break;
}
}
Kernel 中定义了五种模式:V_24, V_32, V_8, V_16, V_16C,每种模式都有其特定的处理逻辑。
1). V_24: 这个模式将读取4个24位的像素,将它们合并成一个96位的缓冲区,然后从这个缓冲区中提取出3个32位的像素并写入到输出流中。
2). V_32: 在这个模式中,每个24位的输入像素被扩展到32位,通过在最高的8位加上一个 alpha 值(透明度信息)。然后将这32位的数据写入到输出流中。
3). V_8: 此模式将4个24位的输入像素的最低8位提取出来,并将这些8位数据打包成一个32位的像素。
4). V_16: 在这个模式下,每两个24位的输入像素被组合成一个32位的输出像素,只包含输入像素的最低16位。
5). V_16C: 此模式处理两个24位的输入像素,并对其中的某些通道进行加法运算,最终生成一个32位的输出像素。它将第一个和第二个输入像素的第二个通道和第三个通道的值分别相加,然后将结果的高8位作为输出像素的相应通道值。
user 和 last 信号
- user 信号用于标识特殊的数据包或帧的开始。
- last 信号表明当前处理的数据是否是流中的最后一个数据。
3.4 AXI VDMA
AXI Video Direct Memory Access v6.3
3.4.1 IP 基本信息

在通过 AXI4-Lite 接口完成寄存器编程后,Control and Status(控制/状态模块)会向 DataMover 发送相应的命令,以便在 AXI4 Master 上执行读写操作。
为了在将像素数据写入 AXI4 内存映射接口或 AXI4 流接口之前暂存数据,系统使用了一个可配置的异步行缓冲器。
- 在数据写入过程中,AXI VDMA 通过 AXI4 流从接口接收帧数据,并通过 AXI4 主接口将其写入系统内存。
- 在数据读取过程中,AXI VDMA 通过 AXI4 主接口从系统内存中读取帧数据,并通过 AXI4 流主接口输出。
写入和读取路径可以独立运行。此外,AXI VDMA 还提供了一个选项,可以通过外部同步信号来实现输入和输出帧的同步。这样可以更好地协调数据流的传输。
简记法则:读写都是针对 DDR 而言,写指对 DDR 写入数据,读是从 DDR 读出数据。
3.4.2 IP 地址空间
| Address Space Offset | Name | Description |
|----------------------|------------------------------|----------------------------------------------|
| 00h | MM2S_VDMACR | MM2S VDMA Control Register |
| 04h | MM2S_VDMASR | MM2S VDMA Status Register |
| 14h | MM2S_REG_INDEX | MM2S Register Index |
| 28h | PARK_PTR_REG | MM2S and S2MM Park Pointer Register |
| 2Ch | VDMA_VERSION | Video DMA Version Register |
| 30h | S2MM_VDMACR | S2MM VDMA Control Register |
| 34h | S2MM_VDMASR | S2MM VDMA Status Register |
| 3Ch | S2MM_VDMA_IRQ_MASK | S2MM Error Interrupt Mask Register |
| 44h | S2MM_REG_INDEX | S2MM Register Index |
| 50h | MM2S_VSIZE | MM2S Vertical Size Register |
| 54h | MM2S_HSIZE | MM2S Horizontal Size Register |
| 58h | MM2S_FRMDLY_STRIDE | MM2S Frame Delay and Stride Register |
| 5Ch to 98h | MM2S_START_ADDRESS (1 to 16) | MM2S Start Address (1 to 16) |
| A0h | S2MM_VSIZE | S2MM Vertical Size Register |
| A4h | S2MM_HSIZE | S2MM Horizontal Size Register |
| A8h | S2MM_FRMDLY_STRIDE | S2MM Frame Delay and Stride Register |
| ACh to E8h | S2MM_START_ADDRESS (1 to 16) | S2MM Start Address (1 to 16) |
| ECh | ENABLE VERTICAL FLIP | Vertical Flip Register |
1)0x28 – PARK_PTR_REG(Park Pointer Register)
此寄存器提供了 MM2S 和 S2MM 的 VDMA 传输管理的停放指针寄存器。

2)0x30 – S2MM VDMA Control Register

- Circular_Park
- 0 = Park Mode,VDMA 将停留在由 PARK_PTR_REG.WrFrmPntrRef 帧缓冲区上。
- 1 = 循环模式,VDMA 将持续循环遍历帧缓冲区。
- RS(Run / Stop)
- 0 = Stop
- 1 = Run
3)0x34 – S2MM VDMA Status Register
- Halted
- 0 = VDMA channel running
- 1 = VDMA channel halted
4)0xAC – S2MM Start Address(offset 0xAC to 0xE8)
最多有 32 个起始地址寄存器,这些寄存器分为两个寄存器组,Bank0 和 Bank1,每组包含16个寄存器。两个寄存器组具有相同的初始偏移量(即0xAC),访问时取决于 S2MM_REG_INDEX 值。如果你想访问第17个起始地址,可以通过将 S2MM_REG_INDEX 设置为1并访问偏移量 0xAC来实现。
当 AXI VDMA 配置为大于 32 位的地址空间时,最多可用 16 个寄存器。两个寄存器用于指定超过32 位的地址:第一个寄存器用于指定起始地址的最低有效 32 位,而下一个寄存器用于指定最高有效 32 位。
因此,在大于 32 位的地址空间中,你最多可以指定 8 个帧缓冲区,每个帧缓冲区宽 64 位。
3.4.3 Fsync Options
帧同步选项,用于设置 AXI VDMA 的各种帧同步模式。
1)None
选择此选项会使 AXI VDMA 进入自由运行模式。在自由运行模式下,视频数据会尽可能快地传输,而无需等待任何外部触发。
2)s2mm fsync
选择此选项时,AXI VDMA 会在 s2mm_fsync 输入信号的下降沿开始处理每一帧。
IP 会生成额外的一个 S2MM_FSYNC 输入端口。
3)s2mm tuser
选择此选项时,AXI VDMA 期望在 AXI4-Stream s_axis_s2mm_tuser(0) 信号上接收到帧起始(SOF)信号。SOF 脉冲的有效传输宽度为 1,必须与帧的第一个像素同步。
3.4.4 GenLock Mode
有四种 Genlock 选项:
1)主设备(Master)
选择此选项时,会在 mm2s_frame_ptr_out 端口输出当前帧号。
2)从设备(Slave)
选择此选项时,从设备按照帧延迟寄存器中设置的帧数跟随主设备,可以通过跳过或重复帧来实现同步。它会在 s2mm_frame_ptr_out 端口输出当前帧号。
3)动态主设备(Dynamic Master)
选择此选项时,AXI VDMA 会跳过动态从设备正在处理的帧缓冲区。举例,就三帧缓存而言,动态Genlock Master 会按照 0,1,2,0,1,2 的顺序循环使用缓存,如果 Slave 正在操作2帧,就会直接跳过2帧,继续循环到0,如果 Slave 一直操作2帧,Master 就会在 0,1 之间一直循环操作。
Dynamic Master 会在 mm2s_frame_ptr_out 端口输出已完成的帧号。
4)动态从设备(Dynamic Slave)
选择此选项时,AXI VDMA 会通过跳过或重复帧来跟随动态主设备。它会在 mm2s_frame_ptr_out 端口输出当前帧号。
3.4.5 Fsync vs Genlock
1)fsync options:
fsync 指的是帧同步信号,它用于标示一帧视频数据的开始和结束。用于配置 VDMA 如何响应外部的帧同步信号或者如何生成内部的帧同步信号,也即如何触发 VDMA 的读取或写入操作。
2)genlock mode:
genlock(生成锁定)是一种视频同步技术,用于在多个视频源之间同步视频帧的时间。genlock 模式允许 VDMA 与其他 VDMA 设备或其他支持 genlock 的视频设备同步。
3)二者区别:
fsync 关注于单个 VDMA 通道如何处理帧同步信号,无论是内部生成还是外部接收,它主要解决的是在一个通道内部的帧同步问题。
genlock mode 则涉及多个 VDMA 通道或设备之间的同步,它解决的是如何在多个设备之间保持严格的帧时间对齐。
3.4.6 VMDA 的必要性
在 FPGA 设计中,为什么不直接把图像输出到显示设备或存储系统,而是要经过 VDMA 呢,这样不是增加了延迟吗?
它可能会引入一些延迟,但这些延迟通常是可以接受的,并且带来的好处远远超过了这些延迟:
1)数据缓冲和同步:VDMA 允许将视频数据缓存在 DDR 内存中,这样可以在不同的时钟域之间进行数据传输和同步。这对于处理高分辨率视频数据尤其重要,因为直接传输可能会导致数据丢失或不同步。
2)灵活性和可扩展性:通过使用 VDMA,可以更灵活地处理视频数据。例如,可以在内存中存储多帧视频数据,方便进行后续的图像处理、分析或显示。这种方法也使得系统更容易扩展和升级。
3)高效的数据传输:VDMA 使用 AXI4-Stream 和 AXI4-MM 协议,可以高效地进行大块数据传输,减少了处理器的负担,提高了系统的整体性能。
4)支持复杂的图像处理:在许多应用中,视频数据需要经过复杂的处理(如滤波、缩放、颜色转换等)后再输出到显示设备或存储系统。VDMA 提供了一个高效的数据传输通道,使得这些处理步骤可以在 FPGA 内部高效完成。
虽然 VDMA 可能会引入一些延迟,但这些延迟通常是可以管理的,并且通过适当的设计和优化,可以将其影响降到最低。
4. OV5640
4.1 基本架构

OV5640 图像传感器核心可以以固定的帧率持续输出像素数据,这一帧率通过 HREF 和 VSYNC 信号来标示。上图展示了 OV5640 图像传感器的功能组成。
传感器内的定时生成器负责输出信号,这些信号控制对图像阵列中各行像素的访问,包括对这些行的预充电和采样操作。在对一行像素进行预充电和采样之间的时间间隔内,像素中存储的电荷会因为暴露于光线而逐渐减少,这个过程就是我们所说的“曝光时间”。
曝光时间可以通过调整预充电和采样之间的时间间隔来控制。在行像素数据被采样之后,这些数据会通过模拟电路进行处理,以校正数据偏差并根据需要调整数据增益。在模拟信号处理之后,是一个模数转换器(ADC),它会将处理过的数据转换成为每个像素10位的数字形式输出。这样的设计确保了图像数据的精确和高质量输出。
4.2 缺陷像素消除 (DPC)
由于工艺和其他原因,传感器阵列中会出现像素缺陷。
缺陷像素消除 (Defect Pixel Cancellation) 功能的主要目的是消除这些损坏或受损像素带来的影响。此外,对于位于图像边界的像素,还有一些特殊功能可用。为了正确消除缺陷像素的影响,首先应确定合适的阈值。
| Address | Register Name | Default | R/W | Description |
|---------|----------------|---------|-----|----------------------------------------------------------------|
| 0x5000 | ISP CONTROL 00 | 0x06 | RW | Bit[2]: Black pixel cancellation enable, 0: Disable, 1: Enable |
| | | | | Bit[1]: White pixel cancellation enable, 0: Disable, 1: Enable |
4.3 颜色插值 (CIP)
CIP(Color Interpolation)功能包括:
- 对原始图像的去噪
- RAW 到 RGB 的插值
- 边缘增强
在传感器的 RAW 格式中,每个像素将是 R、G 或 B 中的一种。CIP 将利用相同颜色的相邻像素来计算其他两种颜色的值。这样就可以获得每个像素的完整 RGB 信息。对于边缘增强,OV5640 提供了手动和自动两种模式。
4.4 Pixel 格式
Ov5640 支持输出格式:8-/10-bit RGB RAW。
文档 5.12 ISP format 描述如下:
| Address | Register Name | Default Value | R/W | Description |
|---------|---------------------|---------------|-----|---------------------------------|
| 0x501F | FORMAT MUX CONTROL | 0x00 | RW | Bit[2:0]: 000: ISP YUV422 |
| | | | | 001: ISP RGB |
| | | | | 010: ISP dither |
| | | | | 011: ISP RAW (DPC) |
| | | | | 100: SNR RAW |
| | | | | 101: ISP RAW (CIP) |
文档 6.5 format description 中描述如下:
| Address | Register Name | Default Value | R/W | Description |
|---------|---------------------|---------------|-----|----------------------------------------------|
| 0x4300 | FORMAT CONTROL 00 | 0xF8 | RW | Bit[7:4]: Output format of formatter module |
| | | | | 0x0: RAW |
| | | | | Bit[3:0]: Output sequence |
| | | | | 0x0: BGBG... |
| | | | | GRGR... |
| | | | | 0x1: GBGB... |
| | | | | RGRG... |
| | | | | 0x2: GRGR... |
| | | | | BGBG... |
| | | | | 0x3: RGRG... |
| | | | | GBGB... |
例如对于 0x4300 = 0x00,bit[7:4] = 0x0 表示输出格式为 RAW,而 bit[3:0]=0x0 表示 RAW 输出序列为 BGBG/GRGR。
文档 6.2 system control, table 6-2 system control registers 描述如下:
| Address | Register Name | Default Value | R/W | Description |
|---------|-----------------|---------------|-----|-----------------------------------|
| 0x3034 | SC PLL CONTRL0 | 0x1A | RW | Bit[6:4]: PLL charge pump control |
| | | | | Bit[3:0]: MIPI bit mode |
| | | | | 0x8: 8-bit mode |
| | | | | 0xA: 10-bit mode |
4.5 Cam 复位
控制 KV260 的引脚为 F11。其功能和原理图如下。
F11(som240_1_a15) -> HDA09 -> U19.10 -> U19.13 -> RPI_ENABLE -> J9.11。
J9 通过排线连接至摄像头模组 J1。
J9.11 -> (摄像头)J1.10 -> CAM_PWUP -> 有两个作用:
- 控制电源开启(2.8V、1.8V)
- 控制 OV5640 reset
KV260 原理图 U19:

《UG1089,Kria KV260 Vision AI Starter Kit》:

KV260 原理图 J9:

Pcam_5C 原理图:



4.6 MIPI control
文档 6.7 mobile industry processor interface (MIPI) 描述如下:
- 时钟通道单向(Uni-directional Clock Lane)
- 数据通道 HS 模式(High-Speed,单向),数据以高速进行单向传输
- 数据通道 LP 模式(Low-Power,双向),数据以低功耗进行双向传输。
4.7 frame rate

4.8 时钟树

note 1: PCLK >= 1*SCLK if Raw or JPEG; PCLK > 2*SCLK if YUV
note 2: MIPISCLK = (4 or 5)*PCLK if 2 lane; = (8 or 10) *PCLK if 1 lane
note 3: to run full size and full speed, adclk=200 MHz, sclk=84 MHz
note 4: exposure unit line period = HTS/(sclk), where HTS=0x380C/0x380D
note 5: frame time is line_period * VTS, where VTS=0x380E/0x380F
note 6: MIPI global timing unit (0x4837) should be set based on PCLK
4.9 其他概念
1)曝光值
曝光值(Exposure Value,简称EV)是描述相机在特定场景下捕获光线的量的一种度量方式。
曝光值通常取决于三个主要的摄影参数:
- 光圈大小(f-number)
- 快门速度(shutter speed)
- ISO 感光度
这三个参数共同确定了传感器接收到的光量,从而影响图像的亮度。
2)自动曝光控制
自动曝光控制(AEC)的功能是计算下一帧的积分时间,并将此信息发送到时序控制块。基于先前帧的统计数据,AEC 能够确定积分时间是应该增加、减少、快速增加、快速减少还是保持不变。
为了避免在周期性光源下的图像闪烁,可以通过整数倍周期调整积分时间。这种 AEC 步进系统被称为带通滤波器,表明曝光时间不是连续的,而是分步进行的。
5. iPython 代码
5.1 Cam_en
cam_en = GPIO(GPIO.get_gpio_pin(0), 'out')
cam_en.write(1)
- 写入0,OV5640 复位
- 写入1,OV5640 正常工作
该 cam_en 信号连接至 PS EMIO:

引脚绑定为 F11:
set_property PACKAGE_PIN F11 [get_ports {rpi_cam_en_tri_io[0]}]
该接口有两个作用:
- 控制电源开启(2.8V、1.8V)
- 控制 OV5640 reset
5.2 IIC SWITCH
axi_iic = AxiIIC(ol.ip_dict["axi_iic_0"])
axi_iic.send(0x74, [0x04], 1, 0) # 打开 Channel2
TCA9546A 是德州仪器生产的一款 4 通道 I2C/SMBus 开关。它具有四个独立的双向通道,可以选择任意一个或多个通道进行数据传输。

KV260 原理图 U18:

5.3 扫描 IIC 设备
ctrl_reg = bytes(1)
axi_iic.receive(0x74, ctrl_reg, 1, 0)
for address in range(0x00, 0x8f):
try:
# Attempt to read 1 byte from the current address
axi_iic.receive(address, [0x00], 1, 0)
print(f"Found I2C device at address: 0x{address:02X}")
except Exception:
# No response from the current address
pass
---
Found I2C device at address: 0x3C
Found I2C device at address: 0x74
ctrl_reg = bytes(1) 创建了一个长度为1的字节对象。不能使用 ctrl_reg = 0,因为 python 默认创建一个整数,调用 axi_iic.receive(0x74, ctrl_reg, 1, 0) 将报错。
TypeError: initializer for ctype 'unsigned char *' must be a cdata pointer, not int
IIC 地址

5.4 配置 Demosaic
demo = ol.v_demosaic_0
demo.write(0x10,1280)
demo.write(0x18,720)
demo.write(0x28,0x03) # Bayer Phase = 3
demo.write(0x00,0x81) # ap_start=1; auto_restart=1
5.5 配置 VDMA
5.5.1 最小配置步骤
AXI VDMA 操作从设置视频参数和起始地址寄存器及 VDMA 控制寄存器开始。最小步骤如下:
1)向通道 VDMACR 寄存器写入控制信息(MM2S 的偏移量为 0x00,S2MM 的偏移量为 0x30),如果需要,设置中断使能,并设置 VDMACR.RS=1 以启动 AXI VDMA 通道。
2)向通道 START_ADDRESS 寄存器 1 到 N 写入有效的视频帧缓冲区起始地址,其中 N 等于帧缓冲区的数量(MM2S 的偏移量为 0x5C 到 0x98,S2MM 的偏移量为 0xAC 到 0xE8)。如果需要,设置 REG_INDEX 寄存器。 当 AXI VDMA 被配置为大于 32 位的地址空间时,每个起始地址需要作为两个寄存器的组合进行编程,其中第一个寄存器用于指定地址的低 32 位,而下一个寄存器用于指定地址的高 32 位。
3)向通道 FRMDLY_STRIDE 寄存器写入有效的帧延迟(仅对 Genlock Slave 有效)和步幅(MM2S 的偏移量为 0x58,S2MM 的偏移量为 0xA8)。
4)向通道 HSIZE 寄存器写入有效的水平尺寸(MM2S 的偏移量为 0x54,S2MM 的偏移量为 0xA4)。
5)向通道 VSIZE 寄存器写入有效的垂直尺寸(MM2S 的偏移量为 0x50,S2MM 的偏移量为 0xA0)。这将启动通道传输视频数据。
5.5.2 关键代码
vdma = ol.axi_vdma_0
cam_vdma = ol.axi_vdma_0
cam_vdma.readchannel.mode = VideoMode(WIDTH, HEIGHT, 24)
cam_vdma.readchannel.start()
cam_vdma = ol.axi_vdma_0
ol 是一个包含 axi_vdma_0 属性的对象,并且 axi_vdma_0 是一个 AxiVDMA 类的实例。
Overlay 类将 FPGA 设计中的 IP 核和层次结构(hierarchies)作为属性展现出来,即当你加载一个 Overlay(即一个比特流文件),你可以通过属性访问设计中的各个 IP 核和子层次结构。
如果没有其他专门的驱动程序可用,PYNQ 将使用 DefaultIP 类来表示顶层的 IP 核,使用 DefaultHierarchy 类来表示包含可寻址 IP 的任何层次结构。这些默认类提供基本的功能,以便用户可以与 IP 进行交互。
你可以通过继承 DefaultIP 和 DefaultHierarchy 类来创建自定义驱动程序。这允许你为特定的 IP 核和层次结构定义更丰富和更复杂的行为。
而 AXI VDMA 的驱动程序就是是遵循这个原则,在如下文件中实现的:
/usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/lib/video/dma.py
5.5.3 VideoMode 属性
cam_vdma.readchannel.mode = VideoMode(WIDTH, HEIGHT, 24)
VideoMode 是定义在 common.py 中的一个属性。
/usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/lib/video/common.py
class VideoMode:
"""用于保存视频模式信息的类
属性
----------
height : int
视频帧的高度(以行数表示)
width : int
视频帧的宽度(以像素为单位)
stride : int
视频帧中一行的宽度(以字节为单位)
bits_per_pixel : int
每像素的位数
bytes_per_Pixel : int
表示每个像素所需的字节数
shape : tuple of int
描述视频帧的类似于 Numpy 的元组
"""
def __init__(self, width, height, bits_per_pixel, fps=60, stride=None):
self.width = width
self.height = height
self.bits_per_pixel = bits_per_pixel
self.bytes_per_pixel = ((bits_per_pixel - 1) // 8) + 1
self.fps = fps
if stride:
self.stride = stride
else:
self.stride = width * self.bytes_per_pixel
if self.bytes_per_pixel == 1:
self.shape = (self.height, self.width)
else:
self.shape = (self.height, self.width, self.bytes_per_pixel)
def __repr__(self):
return ("VideoMode: width={} height={} bpp={} fps={}"
.format(self.width, self.height, self.bits_per_pixel,
self.fps))
def __eq__(self, mode):
return self.width == mode.width and self.height == mode.height and \
self.bits_per_pixel == mode.bits_per_pixel and \
self.fps == mode.fps and self.stride == mode.stride
以上属性中,实际在 VDMA 驱动中使用到的并不多,通过查看 VDMA 的驱动文件,可以看到在 S2MMChannel 和 MM2SChannel 两个类各自的 _writemode 函数。
在 S2MM 中:
def _writemode(self):
self._mmio.write(0xA4, self._mode.width *
self._mode.bytes_per_pixel)
self._mmio.write(0xA8, self._mode.stride)
5.5.4 PYNQ 驱动示例
参考: https://pynq.readthedocs.io/en/latest/overlay_design_methodology/overlay_tutorial.html
可以查看更详细创建自定义驱动的信息和详细示例:
from pynq import DefaultIP
class AddDriver(DefaultIP):
def __init__(self, description):
super().__init__(description=description)
bindto = ['xilinx.com:hls:add:1.0']
def add(self, a, b):
self.write(0x10, a)
self.write(0x18, b)
return self.read(0x20)
5.6 OV5640 寄存器
5.6.1 cfg_init
cfg_init = [
#[7]=0 Software reset; [6]=1 Software power down; Default=0x02
[0x3008, 0x42],
#[1]=1 System input clock from PLL; Default read = 0x11
[0x3103, 0x03],
#[3:0]=0000 MD2P,MD2N,MCP,MCN input; Default=0x00
[0x3017, 0x00],
#[7:2]=000000 MD1P,MD1N, D3:0 input; Default=0x00
[0x3018, 0x00],
#[6:4]=001 PLL charge pump, [3:0]=1000 MIPI 8-bit mode
[0x3034, 0x18],
#PLL1 configuration
#[7:4]=0001 System clock divider /1, [3:0]=0001 Scale divider for MIPI /1
[0x3035, 0x11],
#[7:0]=56 PLL multiplier
[0x3036, 0x38],
#[4]=1 PLL root divider /2, [3:0]=1 PLL pre-divider /1
[0x3037, 0x11],
#[5:4]=00 PCLK root divider /1, [3:2]=00 SCLK2x root divider /1, [1:0]=01 SCLK root divider /2
[0x3108, 0x01],
#PLL2 configuration
#[5:4]=01 PRE_DIV_SP /1.5, [2]=1 R_DIV_SP /1, [1:0]=00 DIV12_SP /1
[0x303D, 0x10],
#[4:0]=11001 PLL2 multiplier DIV_CNT5B = 25
[0x303B, 0x19],
[0x3630, 0x2e],
[0x3631, 0x0e],
[0x3632, 0xe2],
[0x3633, 0x23],
[0x3621, 0xe0],
[0x3704, 0xa0],
[0x3703, 0x5a],
[0x3715, 0x78],
[0x3717, 0x01],
[0x370b, 0x60],
[0x3705, 0x1a],
[0x3905, 0x02],
[0x3906, 0x10],
[0x3901, 0x0a],
[0x3731, 0x02],
#VCM debug mode
[0x3600, 0x37],
[0x3601, 0x33],
#System control register changing not recommended
[0x302d, 0x60],
#??
[0x3620, 0x52],
[0x371b, 0x20],
#?? DVP
[0x471c, 0x50],
[0x3a13, 0x43],
[0x3a18, 0x00],
[0x3a19, 0xf8],
[0x3635, 0x13],
[0x3636, 0x06],
[0x3634, 0x44],
[0x3622, 0x01],
[0x3c01, 0x34],
[0x3c04, 0x28],
[0x3c05, 0x98],
[0x3c06, 0x00],
[0x3c07, 0x08],
[0x3c08, 0x00],
[0x3c09, 0x1c],
[0x3c0a, 0x9c],
[0x3c0b, 0x40],
#[7]=1 color bar enable, [3:2]=00 eight color bar
[0x503d, 0x00],
#[2]=1 ISP vflip, [1]=1 sensor vflip
[0x3820, 0x46],
#[7:5]=010 Two lane mode, [4]=0 MIPI HS TX no power down, [3]=0 MIPI LP RX no power down, [2]=1 MIPI enable, [1:0]=10 Debug mode; Default=0x58
[0x300e, 0x45],
#[5]=0 Clock free running, [4]=1 Send line short packet, [3]=0 Use lane1 as default, [2]=1 MIPI bus LP11 when no packet; Default=0x04
[0x4800, 0x14],
[0x302e, 0x08],
#[7:4]=0x3 YUV422, [3:0]=0x0 YUYV
#[0x4300, 0x30],
#[7:4]=0x6 RGB565, [3:0]=0x0 [b[4:0],g[5:3],g[2:0],r[4:0]]
[0x4300, 0x6f],
[0x501f, 0x01],
[0x4713, 0x03],
[0x4407, 0x04],
[0x440e, 0x00],
[0x460b, 0x35],
#[1]=0 DVP PCLK divider manual control by 0x3824[4:0]
[0x460c, 0x20],
#[4:0]=1 SCALE_DIV=INT(3824[4:0]/2)
[0x3824, 0x01],
#[7]=1 LENC correction enabled, [5]=1 RAW gamma enabled, [2]=1 Black pixel cancellation enabled, [1]=1 White pixel cancellation enabled, [0]=1 Color interpolation enabled
[0x5000, 0x07],
#[7]=0 Special digital effects, [5]=0 scaling, [2]=0 UV average disabled, [1]=1 Color matrix enabled, [0]=1 Auto white balance enabled
[0x5001, 0x03]
]
5.6.2 cfg_720p_30fps
cfg_720p_30fps = [
# 1280 x 720 binned, RAW10, MIPISCLK=280M, SCLK=56Mz, PCLK=56M
#PLL1 configuration
#[7:4]=0010 System clock divider /2, [3:0]=0001 Scale divider for MIPI /1
[0x3035, 0x21],
#[7:0]=70 PLL multiplier
[0x3036, 0x46],
#[4]=0 PLL root divider /1, [3:0]=5 PLL pre-divider /1.5
[0x3037, 0x05],
#[5:4]=01 PCLK root divider /2, [3:2]=00 SCLK2x root divider /1, [1:0]=01 SCLK root divider /2
[0x3108, 0x11],
#[6:4]=001 PLL charge pump, [3:0]=1010 MIPI 10-bit mode
[0x3034, 0x1A],
#[3:0]=0 X address start high byte
[0x3800, (0 >> 8) & 0x0F],
#[7:0]=0 X address start low byte
[0x3801, 0 & 0xFF],
#[2:0]=0 Y address start high byte
[0x3802, (8 >> 8) & 0x07],
#[7:0]=0 Y address start low byte
[0x3803, 8 & 0xFF],
#[3:0] X address end high byte
[0x3804, (2619 >> 8) & 0x0F],
#[7:0] X address end low byte
[0x3805, 2619 & 0xFF],
#[2:0] Y address end high byte
[0x3806, (1947 >> 8) & 0x07],
#[7:0] Y address end low byte
[0x3807, 1947 & 0xFF],
#[3:0]=0 timing hoffset high byte
[0x3810, (0 >> 8) & 0x0F],
#[7:0]=0 timing hoffset low byte
[0x3811, 0 & 0xFF],
#[2:0]=0 timing voffset high byte
[0x3812, (0 >> 8) & 0x07],
#[7:0]=0 timing voffset low byte
[0x3813, 0 & 0xFF],
#[3:0] Output horizontal width high byte
[0x3808, (1280 >> 8) & 0x0F],
#[7:0] Output horizontal width low byte
[0x3809, 1280 & 0xFF],
#[2:0] Output vertical height high byte
[0x380a, (720 >> 8) & 0x7F],
#[7:0] Output vertical height low byte
[0x380b, 720 & 0xFF],
#HTS line exposure time in # of pixels
[0x380c, (1896 >> 8) & 0x1F],
[0x380d, 1896 & 0xFF],
#VTS frame exposure time in # lines
[0x380e, (984 >> 8) & 0xFF],
[0x380f, 984 & 0xFF],
#[7:4]=0x3 horizontal odd subsample increment, [3:0]=0x1 horizontal even subsample increment
[0x3814, 0x31],
#[7:4]=0x3 vertical odd subsample increment, [3:0]=0x1 vertical even subsample increment
[0x3815, 0x31],
#[2]=0 ISP mirror, [1]=0 sensor mirror, [0]=1 horizontal binning
[0x3821, 0x01],
#little MIPI shit: global timing unit, period of PCLK in ns * 2(depends on # of lanes)
[0x4837, 36], # 1/56M*2
#Undocumented anti-green settings
[0x3618, 0x00], # Removes vertical lines appearing under bright light
[0x3612, 0x59],
[0x3708, 0x64],
[0x3709, 0x52],
[0x370c, 0x03],
#[7:4]=0x0 Formatter RAW, [3:0]=0x0 BGBG/GRGR
[0x4300, 0x00],
#[2:0]=0x3 Format select ISP RAW (DPC)
[0x501f, 0x03]
]
5.6.3 cfg_1080p_15fps
cfg_1080p_15fps = [
#1920 x 1080 @ 15 fps, RAW10, MIPISCLK=210, SCLK=42MHz, PCLK=42M
# PLL1 configuration
# [7:4]=0100 System clock divider /4, [3:0]=0001 Scale divider for MIPI /1
[0x3035, 0x41],
# [7:0]=105 PLL multiplier
[0x3036, 0x69],
# [4]=0 PLL root divider /1, [3:0]=5 PLL pre-divider /1.5
[0x3037, 0x05],
# [5:4]=01 PCLK root divider /2, [3:2]=00 SCLK2x root divider /1, [1:0]=01 SCLK root divider /2
[0x3108, 0x11],
# [6:4]=001 PLL charge pump, [3:0]=1010 MIPI 10-bit mode
[0x3034, 0x1A],
# [3:0]=0 X address start high byte
[0x3800, (336 >> 8) & 0x0F],
# [7:0]=0 X address start low byte
[0x3801, 336 & 0xFF],
# [2:0]=0 Y address start high byte
[0x3802, (426 >> 8) & 0x07],
# [7:0]=0 Y address start low byte
[0x3803, 426 & 0xFF],
# [3:0] X address end high byte
[0x3804, (2287 >> 8) & 0x0F],
# [7:0] X address end low byte
[0x3805, 2287 & 0xFF],
# [2:0] Y address end high byte
[0x3806, (1529 >> 8) & 0x07],
# [7:0] Y address end low byte
[0x3807, 1529 & 0xFF],
# [3:0]=0 timing hoffset high byte
[0x3810, (16 >> 8) & 0x0F],
# [7:0]=0 timing hoffset low byte
[0x3811, 16 & 0xFF],
# [2:0]=0 timing voffset high byte
[0x3812, (12 >> 8) & 0x07],
# [7:0]=0 timing voffset low byte
[0x3813, 12 & 0xFF],
# [3:0] Output horizontal width high byte
[0x3808, (1920 >> 8) & 0x0F],
# [7:0] Output horizontal width low byte
[0x3809, 1920 & 0xFF],
# [2:0] Output vertical height high byte
[0x380a, (1080 >> 8) & 0x7F],
# [7:0] Output vertical height low byte
[0x380b, 1080 & 0xFF],
# HTS line exposure time in # of pixels Tline=HTS/sclk
[0x380c, (2500 >> 8) & 0x1F],
[0x380d, 2500 & 0xFF],
# VTS frame exposure time in # lines
[0x380e, (1120 >> 8) & 0xFF],
[0x380f, 1120 & 0xFF],
# [7:4]=0x1 horizontal odd subsample increment, [3:0]=0x1 horizontal even subsample increment
[0x3814, 0x11],
# [7:4]=0x1 vertical odd subsample increment, [3:0]=0x1 vertical even subsample increment
[0x3815, 0x11],
# [2]=0 ISP mirror, [1]=0 sensor mirror, [0]=0 no horizontal binning
[0x3821, 0x00],
# little MIPI shit: global timing unit, period of PCLK in ns * 2(depends on # of lanes)
[0x4837, 48], # 1/42M*2
# Undocumented anti-green settings
[0x3618, 0x00], # Removes vertical lines appearing under bright light
[0x3612, 0x59],
[0x3708, 0x64],
[0x3709, 0x52],
[0x370c, 0x03],
# [7:4]=0x0 Formatter RAW, [3:0]=0x0 BGBG/GRGR
[0x4300, 0x00],
# [2:0]=0x3 Format select ISP RAW (DPC)
[0x501f, 0x03]
]
5.6.4 cfg_1080p_30fps
cfg_1080p_30fps = [
#1920 x 1080 @ 30fps, RAW10, MIPISCLK=420, SCLK=84MHz, PCLK=84M
#PLL1 configuration
#[7:4]=0010 System clock divider /2, [3:0]=0001 Scale divider for MIPI /1
[0x3035, 0x21], # 30fps setting
#[7:0]=105 PLL multiplier
[0x3036, 0x69],
#[4]=0 PLL root divider /1, [3:0]=5 PLL pre-divider /1.5
[0x3037, 0x05],
#[5:4]=01 PCLK root divider /2, [3:2]=00 SCLK2x root divider /1, [1:0]=01 SCLK root divider /2
[0x3108, 0x11],
#[6:4]=001 PLL charge pump, [3:0]=1010 MIPI 10-bit mode
[0x3034, 0x1A],
#[3:0]=0 X address start high byte
[0x3800, (336 >> 8) & 0x0F],
#[7:0]=0 X address start low byte
[0x3801, 336 & 0xFF],
#[2:0]=0 Y address start high byte
[0x3802, (426 >> 8) & 0x07],
#[7:0]=0 Y address start low byte
[0x3803, 426 & 0xFF],
#[3:0] X address end high byte
[0x3804, (2287 >> 8) & 0x0F],
#[7:0] X address end low byte
[0x3805, 2287 & 0xFF],
#[2:0] Y address end high byte
[0x3806, (1529 >> 8) & 0x07],
#[7:0] Y address end low byte
[0x3807, 1529 & 0xFF],
#[3:0]=0 timing hoffset high byte
[0x3810, (16 >> 8) & 0x0F],
#[7:0]=0 timing hoffset low byte
[0x3811, 16 & 0xFF],
#[2:0]=0 timing voffset high byte
[0x3812, (12 >> 8) & 0x07],
#[7:0]=0 timing voffset low byte
[0x3813, 12 & 0xFF],
#[3:0] Output horizontal width high byte
[0x3808, (1920 >> 8) & 0x0F],
#[7:0] Output horizontal width low byte
[0x3809, 1920 & 0xFF],
#[2:0] Output vertical height high byte
[0x380a, (1080 >> 8) & 0x7F],
#[7:0] Output vertical height low byte
[0x380b, 1080 & 0xFF],
#HTS line exposure time in # of pixels Tline=HTS/sclk
[0x380c, (2500 >> 8) & 0x1F],
[0x380d, 2500 & 0xFF],
#VTS frame exposure time in # lines
[0x380e, (1120 >> 8) & 0xFF],
[0x380f, 1120 & 0xFF],
#[7:4]=0x1 horizontal odd subsample increment, [3:0]=0x1 horizontal even subsample increment
[0x3814, 0x11],
#[7:4]=0x1 vertical odd subsample increment, [3:0]=0x1 vertical even subsample increment
[0x3815, 0x11],
#[2]=0 ISP mirror, [1]=0 sensor mirror, [0]=0 no horizontal binning
[0x3821, 0x00],
#little MIPI shit: global timing unit, period of PCLK in ns * 2(depends on # of lanes)
[0x4837, 24], # 1/84M*2
#Undocumented anti-green settings
[0x3618, 0x00], # Removes vertical lines appearing under bright light
[0x3612, 0x59],
[0x3708, 0x64],
[0x3709, 0x52],
[0x370c, 0x03],
#[7:4]=0x0 Formatter RAW, [3:0]=0x0 BGBG/GRGR
[0x4300, 0x00],
#[2:0]=0x3 Format select ISP RAW (DPC)
[0x501f, 0x03]
]
5.6.5 cfg_1080p_30fps_336M_mipi
cfg_1080p_30fps_336M_mipi = [
#1920 x 1080 @ 30fps, RAW10, MIPISCLK=672, SCLK=67.2MHz, PCLK=134.4M
#PLL1 configuration
#[7:4]=0001 System clock divider /1, [3:0]=0001 Scale divider for MIPI /1
[0x3035, 0x11], # 30fps setting
#[7:0]=84 PLL multiplier
[0x3036, 0x54],
#[4]=1 PLL root divider /2, [3:0]=5 PLL pre-divider /1.5
[0x3037, 0x15],
#[5:4]=00 PCLK root divider /1, [3:2]=00 SCLK2x root divider /1, [1:0]=01 SCLK root divider /2
[0x3108, 0x01],
#[6:4]=001 PLL charge pump, [3:0]=1010 MIPI 10-bit mode
[0x3034, 0x1A],
#[3:0]=0 X address start high byte
[0x3800, (336 >> 8) & 0x0F],
#[7:0]=0 X address start low byte
[0x3801, 336 & 0xFF],
#[2:0]=0 Y address start high byte
[0x3802, (426 >> 8) & 0x07],
#[7:0]=0 Y address start low byte
[0x3803, 426 & 0xFF],
#[3:0] X address end high byte
[0x3804, (2287 >> 8) & 0x0F],
#[7:0] X address end low byte
[0x3805, 2287 & 0xFF],
#[2:0] Y address end high byte
[0x3806, (1529 >> 8) & 0x07],
#[7:0] Y address end low byte
[0x3807, 1529 & 0xFF],
#[3:0]=0 timing hoffset high byte
[0x3810, (16 >> 8) & 0x0F],
#[7:0]=0 timing hoffset low byte
[0x3811, 16 & 0xFF],
#[2:0]=0 timing voffset high byte
[0x3812, (12 >> 8) & 0x07],
#[7:0]=0 timing voffset low byte
[0x3813, 12 & 0xFF],
#[3:0] Output horizontal width high byte
[0x3808, (1920 >> 8) & 0x0F],
#[7:0] Output horizontal width low byte
[0x3809, 1920 & 0xFF],
#[2:0] Output vertical height high byte
[0x380a, (1080 >> 8) & 0x7F],
#[7:0] Output vertical height low byte
[0x380b, 1080 & 0xFF],
#HTS line exposure time in # of pixels Tline=HTS/sclk
[0x380c, (2500 >> 8) & 0x1F],
[0x380d, 2500 & 0xFF],
#VTS frame exposure time in # lines
[0x380e, (1120 >> 8) & 0xFF],
[0x380f, 1120 & 0xFF],
#[7:4]=0x1 horizontal odd subsample increment, [3:0]=0x1 horizontal even subsample increment
[0x3814, 0x11],
#[7:4]=0x1 vertical odd subsample increment, [3:0]=0x1 vertical even subsample increment
[0x3815, 0x11],
#[2]=0 ISP mirror, [1]=0 sensor mirror, [0]=0 no horizontal binning
[0x3821, 0x00],
#little MIPI shit: global timing unit, period of PCLK in ns * 2(depends on # of lanes)
[0x4837, 14], # 1/84M*2
#Undocumented anti-green settings
[0x3618, 0x00], # Removes vertical lines appearing under bright light
[0x3612, 0x59],
[0x3708, 0x64],
[0x3709, 0x52],
[0x370c, 0x03],
#[7:4]=0x0 Formatter RAW, [3:0]=0x0 BGBG/GRGR
[0x4300, 0x00],
#[2:0]=0x3 Format select ISP RAW (DPC)
[0x501f, 0x03]
]
5.6.6 cfg_1080p_30fps_336M_1lane_mipi
cfg_1080p_30fps_336M_1lane_mipi = [
#1920 x 1080 @ 30fps, RAW10, MIPISCLK=672, SCLK=67.2MHz, PCLK=134.4M
#PLL1 configuration
#[7:4]=0001 System clock divider /1, [3:0]=0001 Scale divider for MIPI /1
[0x3035, 0x11], # 30fps setting
#[7:0]=84 PLL multiplier
[0x3036, 0x54],
#[4]=1 PLL root divider /2, [3:0]=5 PLL pre-divider /1.5
[0x3037, 0x15],
#[5:4]=00 PCLK root divider /1, [3:2]=00 SCLK2x root divider /1, [1:0]=01 SCLK root divider /2
[0x3108, 0x01],
#[6:4]=001 PLL charge pump, [3:0]=1010 MIPI 10-bit mode
[0x3034, 0x1A],
#[7:5]=001 One lane mode, [4]=0 MIPI HS TX no power down, [3]=0 MIPI LP RX no power down, [2]=1 MIPI enable, [1:0]=10 Debug mode; Default=0x58
[0x300e, 0x25],
#[3:0]=0 X address start high byte
[0x3800, (336 >> 8) & 0x0F],
#[7:0]=0 X address start low byte
[0x3801, 336 & 0xFF],
#[2:0]=0 Y address start high byte
[0x3802, (426 >> 8) & 0x07],
#[7:0]=0 Y address start low byte
[0x3803, 426 & 0xFF],
#[3:0] X address end high byte
[0x3804, (2287 >> 8) & 0x0F],
#[7:0] X address end low byte
[0x3805, 2287 & 0xFF],
#[2:0] Y address end high byte
[0x3806, (1529 >> 8) & 0x07],
#[7:0] Y address end low byte
[0x3807, 1529 & 0xFF],
#[3:0]=0 timing hoffset high byte
[0x3810, (16 >> 8) & 0x0F],
#[7:0]=0 timing hoffset low byte
[0x3811, 16 & 0xFF],
#[2:0]=0 timing voffset high byte
[0x3812, (12 >> 8) & 0x07],
#[7:0]=0 timing voffset low byte
[0x3813, 12 & 0xFF],
#[3:0] Output horizontal width high byte
[0x3808, (1920 >> 8) & 0x0F],
#[7:0] Output horizontal width low byte
[0x3809, 1920 & 0xFF],
#[2:0] Output vertical height high byte
[0x380a, (1080 >> 8) & 0x7F],
#[7:0] Output vertical height low byte
[0x380b, 1080 & 0xFF],
#HTS line exposure time in # of pixels Tline=HTS/sclk
[0x380c, (2500 >> 8) & 0x1F],
[0x380d, 2500 & 0xFF],
#VTS frame exposure time in # lines
[0x380e, (1120 >> 8) & 0xFF],
[0x380f, 1120 & 0xFF],
#[7:4]=0x1 horizontal odd subsample increment, [3:0]=0x1 horizontal even subsample increment
[0x3814, 0x11],
#[7:4]=0x1 vertical odd subsample increment, [3:0]=0x1 vertical even subsample increment
[0x3815, 0x11],
#[2]=0 ISP mirror, [1]=0 sensor mirror, [0]=0 no horizontal binning
[0x3821, 0x00],
#little MIPI shit: global timing unit, period of PCLK in ns * 2(depends on # of lanes)
[0x4837, 28], # 1/84M*2
#Undocumented anti-green settings
[0x3618, 0x00], # Removes vertical lines appearing under bright light
[0x3612, 0x59],
[0x3708, 0x64],
[0x3709, 0x52],
[0x370c, 0x03],
#[7:4]=0x0 Formatter RAW, [3:0]=0x0 BGBG/GRGR
[0x4300, 0x00],
#[2:0]=0x3 Format select ISP RAW (DPC)
[0x501f, 0x03]
]
5.6.7 AWB
cfg_advanced_awb = [
# Enable Advanced AWB
[0x3406 ,0x00],
[0x5192 ,0x04],
[0x5191 ,0xf8],
[0x518d ,0x26],
[0x518f ,0x42],
[0x518e ,0x2b],
[0x5190 ,0x42],
[0x518b ,0xd0],
[0x518c ,0xbd],
[0x5187 ,0x18],
[0x5188 ,0x18],
[0x5189 ,0x56],
[0x518a ,0x5c],
[0x5186 ,0x1c],
[0x5181 ,0x50],
[0x5184 ,0x20],
[0x5182 ,0x11],
[0x5183 ,0x00],
[0x5001 ,0x03]
]
cfg_simple_awb = [
# Disable Advanced AWB
[0x518d ,0x00],
[0x518f ,0x20],
[0x518e ,0x00],
[0x5190 ,0x20],
[0x518b ,0x00],
[0x518c ,0x00],
[0x5187 ,0x10],
[0x5188 ,0x10],
[0x5189 ,0x40],
[0x518a ,0x40],
[0x5186 ,0x10],
[0x5181 ,0x58],
[0x5184 ,0x25],
[0x5182 ,0x11],
# Enable simple AWB
[0x3406 ,0x00],
[0x5183 ,0x80],
[0x5191 ,0xff],
[0x5192 ,0x00],
[0x5001 ,0x03]
]
cfg_disable_awb = [
[0x5001 ,0x02]
]
5.7 导入 py 文件
现有 ov5640_config.py 文件,其中内容如下:
ov5640_config.py
---
ov5640_config = (
[0x3103, 0x11],
[0x3008, 0x82],
[0x3008, 0x42],
[0x3103, 0x03],
[0x3017, 0xff],
[0x3018, 0xff],
...
可以在 iPython 中的单元格中导入:
from ov5640_config import ov5640_config
5.8 配置 OV5640
def ov5640_init():
write_cam_dat(0x3103, 0x11) # [1]=0 System input clock from pad; Default read = 0x11
write_cam_dat(0x3008, 0x82) # [7]=1 Software reset; [6]=0 Software power down; Default=0x02
sleep(0.5)
for cmd in cfg_init:
write_cam_dat(cmd[0], cmd[1])
print("cfg_init complete")
def ov5640_set_mode(ov5640_mode_res):
write_cam_dat(0x3008, 0x42) # [7]=0 Software reset; [6]=1 Software power down; Default=0x02
for cmd in ov5640_mode_res:
write_cam_dat(cmd[0], cmd[1])
print("Set mode complete")
write_cam_dat(0x3008, 0x02) # [7]=0 Software reset; [6]=0 Software power down; Default=0x02
def ov5640_set_awb(ov5640_mode_awb):
write_cam_dat(0x3008, 0x42) # [7]=0 Software reset; [6]=1 Software power down; Default=0x02
for cmd in ov5640_mode_awb:
write_cam_dat(cmd[0], cmd[1])
print("Set awb complete")
write_cam_dat(0x3008, 0x02) # [7]=0 Software reset; [6]=0 Software power down; Default=0x02
Clocks.set_pl_clk(0, None, None, 100)
print(f"FCLK0: {Clocks.fclk0_mhz}")
WIDTH = 1280
HEIGHT = 720
cfg_demo_vdma(WIDTH, HEIGHT)
ov5640_init()
ov5640_set_mode(cfg_720p_30fps) # cfg_720p_30fps; cfg_1080p_15fps; cfg_1080p_30fps
ov5640_set_awb(cfg_simple_awb) # cfg_advanced_awb; cfg_simple_awb
5.9 MMIO 调试
from pynq import MMIO
base_address = 0xA002_0000
address_range = 0x1_0000
mmio = MMIO(base_address, address_range)
mmio.read(0xA8)
6. 调试 MIPI
6.1 抓取的信号
直接观察 mipi_csi2_rx_subsyst_0 输出的 video_out 信号,包括如下内容:
- video_out_tdata[15:0]
- video_out_tdest[9:0]
- video_out_tlast
- video_out_tready
- video_out_tuser[95:0]
- video_out_tvalid

测试 rxbyteclkhs 时钟频率,为 33.75MHz,换算得到 Line Rate 为 270MHz。
6.2 触发信号设置
6.2.1 创建用户探针
在 Video Format Bridge (VFB) 中,我们知道 video_out_tuser[n-1:0] 中 bit0 表示帧开始,如果想要从帧开始进行抓取,需要创建用户自定义探针,方法如下:

6.2.2 查看已有探针
打开如下窗口,可以查看/编辑已定义的探针:

6.2.3 使用用户探针
添加自定义的探针。

使用帧开始触发 ILA 。

6.3 重要波形
6.3.1 整体波形

ILA 通过 tuser(0) - FS 触发 SOF 抓取波形。
可以从 tuser 中将 Data Type、Word Count、Line Number、Frame Number 单独绑定。
Data Type = 0x2B,即 RAW10 格式。

Word Count = 0x640 = (1600)d,每行1600。
Line Number 可以看到行数的增加。
6.3.2 观察帧率
1)Block Design 设计
video_out_tsuser 最低位保存的是 Frame Start 信号,此信号在 video_aclk 时钟域,在帧开始时发出一个周期的脉冲信号。
在同样时钟域(video_aclk)添加一个脉冲触发翻转的逻辑,并引出此信号到 IO,可以通过示波器直接观察到帧率。

需要注意:从总线接口单独分离出来的信号(Spliting single wires off of a bus)不再作为 BD 中接口的一部分进行连接,此信号必须手动连接。
参考:
《Vivado Design Suite User Guide: Designing IP Subsystems Using IP Integrator (UG994)》
Creating a Block Design - Designing with IP Integrator - Making Connections -
Connecting Interface Signals。
2) 脉冲触发翻转的逻辑
module toggle_on_pulse(
input clk ,
input rst_n ,
input pulse_in ,
output reg pulse_out
);
// 内部寄存器用于检测脉冲的上升沿
reg pulse_reg;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
pulse_reg <= 1'b0;
pulse_out <= 1'b0;
end else begin
pulse_reg <= pulse_in;
if (!pulse_reg && pulse_in) begin
// 如果检测到上升沿,翻转输出信号
pulse_out <= ~pulse_out;
end
end
end
endmodule
3)使用示波器查看帧率信息

6.3.3 MIPI Link CLK
1)配置 Block Design

2)counter_flip
计数到 49 后翻转,实现 100 分频。
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位计数器,初始值为0
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
count <= 8'd0; // 复位时计数器清零
sig_out <= 0; // 复位时输出也清零
end else begin
if (count == MAX_COUNT) begin
count <= 8'd0; // 当计数达到最大值时,计数器清零
sig_out <= ~sig_out; // 并翻转输出
end else begin
count <= count + 1; // 否则计数器递增
end
end
end
endmodule
3)加载 cfg_1080p_15fps 配置
cfg_1080p_15fps = [
#1920 x 1080 @ 15 fps, RAW10, MIPISCLK=210, SCLK=42MHz, PCLK=42M
4)测量频率

实测频率 262.52 kHz,那么 rxbyteclkhs = 262.52 kHz *100 * 8 = 210.016 MHz,符合预期。
7. 总结与参考
7.1 总结
涉及 IP 较多,持续更新中。
7.2 参考
MIPI CSI-2 Receiver Subsystem应用总结 | FPGA 开发圈
AMD Xilinx MIPI solution | FPGA 开发圈
MIPI CSI-2 RX Subsystem IP和D-PHY基本调试
MIPI CSI-2 RX Subsystem IP和D-PHY基本调试 | FPGA 开发圈https://fpga.eetrend.com/content/2023/100568839.htmlMIPI 集合

更多推荐




所有评论(0)