Load/store register results in different value than memory viewer

0

I'm working on a program that runs under Xilinx's QEMU builds. This question is tagged , 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:

  1. Verify disassembly should load/store from register location.
  2. Step through instructions to verify CPU registers have the correct value to access peripheral register.
  3. Accessed memory and variable directly via debugger.
  4. Verify with Xilinx code that QEMU emulates peripheral.
  5. Checked MPU settings (the region is set to non-shared and no restrictions on access)

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.

arm
qemu
xilinx
bare-metal
zynq
asked on Stack Overflow Sep 28, 2016 by rjp • edited Sep 28, 2016 by rjp

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0