2
\$\begingroup\$

I am debugging an issue I have on a legacy codebase that is running on an atxmega32c4u chip. I am writing single bytes to registers in PORTC, but the memory view is showing multiple bytes change. The videos below show this happening when stepping through with the debugger, both in the C-code as well as the disassembly.

The std instruction is executing and writing to the expected base memory address, however the memory view shows multiple bytes changing after the instruction is executed, whereas the instruction set manual says that only a single byte will be mutated.

I expected that only addresses 0x640 (PORTC.DIR) and 0x644 (PORTC.OUT) would be written to, however the memory view is showing that addresses 0x640 - 0x648 are being written to with these two lines.

ANSWER

Thanks to user justme - the answer is in the register description (RTFM!). The addresses that are being "mutated" are not actually changing - they are the SET, CLR, and TGL registers for DIR and OUT. For the DIRSET, DIRCLR, and DIRTGL registers, the manual says:

Reading this register will return the value of the DIR register.

Snippet of the manual with section that answers the question higlighted in green

Original question

Setup

  • MCU: atxmega32c4u
  • Debugger: Atmel-ICE
  • IDE: Microchip studio v 7.0.2542

Code

C-code

{
    PORTC.OUT = 0xfe;
    PORTC.DIR = 0x88;
    
    while(1);
    // rest of the codebase omitted

}

Disassembly

    PORTC.OUT = 0xfe;
00001873  LDI R24,0x40      Load immediate 
00001874  LDI R25,0x06      Load immediate 
00001875  LDI R18,0xFE      Load immediate 
00001876  MOVW R30,R24      Copy register pair 
00001877  STD Z+4,R18       Store indirect with displacement 
    PORTC.DIR = 0x88;
00001878  LDI R24,0x40      Load immediate 
00001879  LDI R25,0x06      Load immediate 
0000187A  LDI R18,0x88      Load immediate 
0000187B  MOVW R30,R24      Copy register pair 
0000187C  STD Z+0,R18       Store indirect with displacement 
    while(1);
0000187D  RJMP PC-0x0000        Relative jump 

Videos

This screen capture shows me stepping through the C code and watching the bytes change in the memory view.

Stepping through C code whilst memory view is visible

This screen capture shows me stepping through the disassembly and watching the bytes change in the memory view. The bytes change after the std instruction, which, according to the user manual, should only write a single byte to the destination address.

Stepping through disassembly whilst memory view is visible

enter image description here

\$\endgroup\$
1
  • \$\begingroup\$ Been a long time since I've used the AVR, but I imagine this is just the PORT.IN register changing since you've modified the value on the port. \$\endgroup\$
    – Jon
    Commented Jul 3 at 8:33

1 Answer 1

4
\$\begingroup\$

The code does write to only two addresses.

What happens next is that your debugger reads those memory addresses and that is what it reads from them - you don't even know if some of those addresses are readable or not.

The GPIO port structure is not complex but you just need to know how it works.

First address is the DIR register itself, but the following 3 addresses are for setting, clearing, and toggling the bits in DIR register.

The next four addresses are similar but for the PORT register.

I did not check the register map if these set/clear/toggle registers are readable as I could not find it.

The next register is the IN register and surely it will also show a change happening on those pins when you change them yourself.

This explains why you see changes on 9 addresses, 6 of them may be bogus, just ignore them, that's how it appears to work.

The XMEGA manual also explains that reading the SET/CLR/TGL registers for DIR/PORT read back the current actual DIR/PORT register value.

\$\endgroup\$
11
  • 1
    \$\begingroup\$ The SET/CLR/TGL registers read same as the base (OUT/DIR) port indeed. \$\endgroup\$ Commented Jul 3 at 9:56
  • 1
    \$\begingroup\$ @TimWilliams Thanks for verifying and I just found the manual that says the same thing. \$\endgroup\$
    – Justme
    Commented Jul 3 at 10:00
  • \$\begingroup\$ Might be useful to stop calling these addresses "memory" (a mistake also made by OP). They are not, they are peripheral special function "registers", and what OP is observing are the internal connections inside the GPIO circuitry. Actually I would say this mistake traces back to incorrect "I/O memory" terminology in the documentation. While such a thing as "I/O memory" does exist (DMA packet buffers, for example), that's not what we have here. \$\endgroup\$
    – Ben Voigt
    Commented Jul 3 at 16:42
  • \$\begingroup\$ @BenVoigt They are SFRs for peripherals, yes. But they exist in data memory address space and must be accessed based on data memory address. Only the first 64 addresses can be accessed directly as IO space. When you tell a debugger memory view to dump and from some memory address, it shows what there is, no matter what it is and if it's memory or not. \$\endgroup\$
    – Justme
    Commented Jul 3 at 17:09
  • \$\begingroup\$ @Justme: Yes, a widely-used accurate term is "memory-mapped I/O". \$\endgroup\$
    – Ben Voigt
    Commented Jul 3 at 17:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.