我正在开发一个在 Xilinx 的 QEMU 构建下运行的程序。这个问题被标记为zynq,但我实际上是针对 QEMU 中的 Zynq MP,特别是 Cortex-R5。
我的背景是微控制器和 Cortex-M,所以这是一个很大的飞跃,并且有很多我不习惯的特性(MPU、AXI、额外缓存)。我正在编写一个驱动程序,该驱动程序正在访问其中一个硬外设(在低功耗域中)中的外设寄存器。我编写了一个函数,reginsert
以简化访问这些寄存器中的位。
void reginsert(uint32_t base, uint32_t offset,
uint32_t mask, uint32_t data)
{
uint32_t volatile * reg = (uint32_t volatile *)(base + offset);
uint32_t regdata;
/* This section actually happens in a critical section for atomicity,
* but I've trimmed that out for brevity */
regdata = *reg;
*reg = (regdata & ~mask) | data;
/* End of critical section */
}
根本不是一个非常令人兴奋的功能。对我来说,这也很简单。
我这样调用函数:
/* Magic number provided by Xilinx, I don't actually plan to keep it as such a
* magical number, but for now while trying things to debug. */
reginsert(XPAR_PSU_UART_0_BASEADDR, XUARTPS_CR_OFFSET, 0x3C, 0);
附加信息:
#define XPART_PSU_UART_0_BASEADDR 0xFF000000
#define XUARTPS_CR_OFFSET 0x00000000
在逐步执行代码时,我发现该函数正确地计算了寄存器的地址,0xFF000000
当查看reg
. 我还可以看到,单步执行寄存器r3
加载了正确地址的程序集。
当执行达到:
regdata = *reg;
拆解有:
ldr r3, [r3]
我进去0x00000000
了r3
。如果我使用内存视图,我可以看到它0xFF000000
的值为0x00000114
. 如果我查看变量视图,我可以看到它*reg
具有相同的值 ( 0x00000114
)。如果我进入屏蔽本地副本的行,然后写回,我最终会得到以下指令:
str r2, [r3] ; r3 has been reloaded with the register address,
; and r2 contains the read-modify-write data.
跳过这一点,我看到0x00000000
应该写入的值(尽管不是正确的值),但该值实际上并没有改变(通过内存或变量视图)。在这些视图中的任何一个中,我都可以手动更改值并正确写入。
我不知道在这个 QEMU 版本中对 Zynq MP 的总线架构进行了多么彻底的仿真,但是芯片具有用于调试访问和处理器访问的单独内存端口,并且不通过 CPU 访问内存,所以有一个以不同方式映射事物的可能性。但是,Xilinx 演示代码确实可以适当地执行相同的操作。
所以,总结一下我的尝试:
- 验证反汇编应该从寄存器位置加载/存储。
- 逐步执行指令以验证 CPU 寄存器是否具有访问外设寄存器的正确值。
- 通过调试器直接访问内存和变量。
- 使用 Xilinx 代码验证 QEMU 模拟外设。
- 检查 MPU 设置(区域设置为非共享且无访问限制)
我不知道我还应该尝试什么,我基本上被难住了。看起来 QEMU,至少通过 gdb 接口,正确地模拟了外围寄存器,所以我认为 CPU 读/写相当简单。我感谢任何帮助、指示、提示等。有很多事情对我来说是新的,包括使用 QEMU 作为开发平台。