Xilinx Embedded Software 学习笔记
sw_app 目录中的程序由 Vitis/XSCT
工具进行构建和管理,包含适用于 Xilinx SoC 平台的裸核和 RTOS 示例。
通用目录结构如下
1 | <app_name>/ |
Memory Tests
这一部分程序的作用是测试所有可读写内存区域,包括 OCM,BRAM 等。
1 | . |
memory_config.h程序中定义了结构体,与lscripts.ld中的Available Memory Regions表头对应。
1 | struct memory_range_s { |
memory_config_g.c文件根据模板文件memory_config_g.c.in构建,具体作用是替换占位符@MEMNODES@为具体可读写的内存区域,作为数组memory_ranges的逐个元素;替换占位符@NUM_MEM_RANGES@为内存区域的数量。platform.c程序中包含平台初始化与清理函数,初始化时仅启用 I-Cache,针对 ARM 处理器会禁用 D-Cache 数据缓存,因为内存测试必须让数据写到外部存储器。memorytest.c程序中包含main()函数和测试逻辑:遍历所有n_memory_ranges逐个调用测试函数test_memory_range。打印本次测试的内存区域名称、控制器 IP 名、基地址和区域大小
将内存按照 \(4KB\) 分块,
Xil_TestMem32逐块做 32 位读写测试(可选:16 位或 8 位压力测试)
注:测试程序中使用 print 打印,而不是 printf,因为 printf 会在堆上动态分配内存,而此时 heap = 0.
memory_tests.tcl脚本
| 函数 | 作用 |
|---|---|
swapp_get_name |
返回应用名称
Memory Tests |
swapp_get_description |
返回描述信息 |
swapp_is_supported_hw |
检查硬件兼容性:必须有 UART 外设,必须有足够大的 BRAM/OCM |
swapp_is_supported_sw |
检查软件兼容性:必须是 standalone OS,必须设置 stdout |
swapp_generate |
生成 platform_config.h(UART
配置)和 memory_config_g.c(内存范围表) |
swapp_get_linker_constraints |
返回链接约束:代码/数据存放位置,堆大小为 0 |
swapp_get_supported_processors |
返回支持的处理器列表 |
swapp_get_supported_os |
返回 standalone |
另外,generate_memory_config 函数的作用包括
- 遍历所有数据内存范围,排除以下区域:
- 只读内存
- 与安全状态不匹配的内存
- 程序代码/数据所在的内存(避免测试时覆盖自身)
- Flash 存储器(通过 EMC IP 类型检测)
- DDR 控制器本身(ps7_ddrc)、QSPI、NAND、NOR 等非易失性存储
- TCM(紧耦合内存)、OCM、PMU RAM 等片上特殊内存
- AArch32 模式下地址超过 32 位的区域
- MicroBlaze 特殊处理:基地址为 0 的内存跳过前 0x50 字节(向量表区域)。
最终生成合法的内存范围数组。
函数根据
Subtest参数最多可以执行 5 种测试,分别为
- 向内存连续写入累加数然后从头读取,检查读出的值与预期递增的值是否一致,用于检测基本的读写功能;
- 写入的数据只有 1 个 bit 是 1, 其余是 0,例如
0x0000_0001, 0x0000_0002,...,一直左移循环 32 次,用于检测数据线之间是否存在邻位干扰、短路或断路;- 写入的数据只有 1 个 bit 是 0, 其余是 1,同样用于检测数据线,确保高低电平转换没有故障;
- 将当前内存的地址取反后的值写入该地址,读取时,再次计算地址的取反值进行对比,用于检测地址线与地址译码逻辑;
- 将整个内存块填满
0xDEADBEEF,然后回读校验,快速完成大面积的完整性校验,用于验证内存控制器配置;
Zynq DRAM tests
该测试程序在 OCM 上运行,通过 UART 交互操作,用于测试 PS DDR,用户可以选择以下三种测试模式
Memory test
Read eye measurement
Write eye measurement
1 | . |
translation_table.s:ARM MMU 页表(Section 描述符)
| 地址范围 | 大小 | 属性 | 用途 |
|---|---|---|---|
| 0x00000000–0x3FFFFFFF | 1GB | Inner/Outer Write-Back, Cacheable | DDR(可缓存) |
| 0x40000000–0x7FFFFFFF | 1GB | Shareable Device | FPGA Slave 0 |
| 0x80000000–0xBFFFFFFF | 1GB | Shareable Device | FPGA Slave 1 |
| 0xC0000000–0xDFFFFFFF | 512MB | Translation Fault | 保留 |
| 0xE0000000–0xE1FFFFFF | 32MB | Shareable Device | IO 外设(UART/USB/SPI 等) |
| 0xE2000000–0xE3FFFFFF | 32MB | Shareable Device | NOR |
| 0xE4000000–0xE5FFFFFF | 32MB | Cacheable | SRAM |
| 0xE6000000–0xF7FFFFFF | 288MB | Translation Fault | 保留 |
| 0xF8000000–0xF8FFFFFF | 16MB | Shareable Device | AMBA APB 外设 |
| 0xF9000000–0xFBFFFFFF | 48MB | Translation Fault | 保留 |
| 0xFC000000–0xFFEFFFFF | ~63MB | Cacheable | QSPI XIP |
| 0xFFF00000–0xFFFFFFFF | 1MB | Shareable, Cacheable | OCM(高地址映射) |
testDefines.h:定义了 Zynq-7000 SoC 的所有主要外设基地址和偏移量:L2 Cache Controller:0xF8F02000 基地址的各控制/状态寄存器偏移
SCU (Snoop Control Unit):0xFEF00000 基地址
全局定时器:0xF8F00200 区域
系统级寄存器:OCM、DMAC、DDR、TTC、SWDT、EFUSE 等基地址
IO 外设:UART、USB、I2C、SPI、CAN、GPIO、ETH、QSPI、SDIO 等基地址
Fabric 接口:0x40000000 和 0x80000000 基地址
CoreSight 调试: 0xF8800000 区域的 DAPROM、ETB、TPIU、PTM 等
SLCR 寄存器偏移:PLL 控制、时钟控制、复位控制、TrustZone、DDR 控制、MIO 引脚配置(0–53 号引脚)
lscript.ld:定义了两个内存区域:ps7_ram_0(0x00000000,192KB)放置.text、.rodata、.data、.bss、.mmu_tbl、堆(默认 8KB )ps7_ram_1(0xFFFF0000,65024B)放置所有栈:主栈(24KB)、IRQ 栈(24KB)、Supervisor(2KB)、Abort(1KB)、FIQ(1KB)等
该程序用于测试 DDR,为了不干扰程序运行,上述内容全部放置在 OCM 中。
test01.c 作为测试主程序,具体具有以下功能
- 寄存器访问宏:不经过任何驱动抽象,直接访问内存地址
1 | // Macros |
- DDR 控制器与 IO Buffer 初始化
1 | ddrc_reg_values[160] 存储 DDR Controller 的寄存器配置,地址范围 0xF8006000–0xF80062B4 |
- Cache 管理例程
1 | L1DCacheInvalidate() 按 set/way 逐行失效 L1 数据 cache |
这些函数在写完 DRAM 后、读回验证前被调用,避免 Cache 中的脏数据掩盖真实的 DRAM 错误。
内存测试核心流程
通用流程为 写入 DDR → Cache 刷写/失效 → 读回比较 → 统计错误
1 | ┌──────────────────────────────────────────────────────────┐ |
- Mode
0:
Data = Address(地址线测试,查短路/断路) - Mode 1-4:全
0、全F、0xAAAA...、0x5555...(测试固定电平保持能力) - Mode 5-8:棋盘格 pattern(测试相邻位干扰、串扰)
- Mode 9-10:128-word 复杂翻转 pattern(模拟最恶劣的 SSN 同步开关噪声)
- Mode 11-14:
LFSR伪随机序列(模拟真实业务的数据随机性)
眼图测试
写眼图扫描 (
measure_write_eye): 修改R046(wr_data_offset) 和R05F(wr_data_slave_ratio)。 从中心点向两边步进,每修改一次延迟,就运行一次memtest。当错误数errcnt从 \(0\) 变成 \(> 0\) 时,就找到了眼图的 左边界 (mineye) 和 右边界 (maxeye)。读眼图扫描 (
measure_read_eye): 关闭 DDRC 的自动读训练功能,手动修改R050(rd_dqs_slave_ratio) 和R05A(fifo_we_slave_ratio),同样通过报错边界来描绘读眼图。眼图质量评估 (
find_best_eye): 计算 4 个 Byte Lane 的眼图宽度总和与方差,挑选出最宽、最对称的最佳延迟中心点。