I'm working on a program that runs under Xilinx's QEMU builds. This question is tagged zynq, but I'm actually targeting a Zynq MP in QEMU, specifically the Cortex-R5.
My background is in microcontrollers and Cortex-M, so this is a big jump and there are a lot of features (MPU, AXI, extra caches) that I'm not accustomed to. I'm writing a driver that is accessing peripheral registers in one of the hard peripherals (in the low power domain). I've written a function, reginsert
to simplify accessing bits in these registers.
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 */
}
Not a very exciting function at all. To me, it's also straightforward.
I call the function like so:
/* 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);
Additional information:
#define XPART_PSU_UART_0_BASEADDR 0xFF000000
#define XUARTPS_CR_OFFSET 0x00000000
In stepping through the code, I find that this function correctly calculates the address for the register as 0xFF000000
, and I can see this within the Xilinx SDK debugger (gdb based) when viewing the value for reg
. I can also see, stepping through the assembly that the register r3
is loaded with the proper address.
When execution reaches:
regdata = *reg;
The disassembly has:
ldr r3, [r3]
And I get 0x00000000
in r3
. If I use the memory view, I can see that 0xFF000000
has a value of 0x00000114
. If I look in the variable view, I can see that *reg
has the same value (0x00000114
). If I step to the line where the local copy is masked and then written back, I end up at the instruction:
str r2, [r3] ; r3 has been reloaded with the register address,
; and r2 contains the read-modify-write data.
Stepping past this, I see that the value of 0x00000000
(though not the correct value) should have been written, but the value does not actually change (via memory or variable view). In either of these views, I can manually change the value and it writes correctly.
I don't know how thoroughly the bus architecture of the Zynq MP is being emulated in this build of QEMU, but the silicon has separate memory ports for debug access and processor access, and does not access memory through the CPU, so there is a possibility for things to be mapped differently. However, the Xilinx demo code does appropriately perform the same actions.
So, a summary of what I've tried:
I don't know what else I should try, and I'm basically stumped. It appears that QEMU, at least through the gdb interface, properly simulates the peripheral register, so I would think it would be fairly straightforward for the CPU to read/write. I appreciate any help, pointers, tips, etc. There's a lot going on that is new to me, including using QEMU as a development platform.
User contributions licensed under CC BY-SA 3.0