Debugging using PDI
Getting started
Debug has its own key value that must be sent to enable it. You can check if this needs doing by issuing ldcs status
to the PDI processor.
The debug key uses the constant 0x3a212dd49f7c8121 to unlock the debug part of the controller.
Initiating a debug session
Put the processor into reset
stcs reset 0x59
ldcs status
The LDCS should return 0x00 if these are the first instructions run in a session.
Turn the debug interface on
key debug
ldcs status
The LDCS should return 0x04 if key nvm
has not been run yet, and 0x06 if it has.
Take the reset processor and put it in debug pause state
stcs r4 1
ldcs r3
stcs reset 0x00
ldcs r3
ldcs r3
Writing bit 1 of r4 puts the processor into debug-based pause once reset is released the first LDCS to r3 is used to verify that r3 is presently 0x10 (processor held in reset, debugging not active). Once reset is released, r3 will initially be 0x14, which should be observed with the second LDCS. Once debug setup is completed, r4 will then read as 0x04, which should be observed with the final LDCS.
Running the processor to an address
The following uses the hardware breakpoint unit, which contains 2 breakpoint address registers and associated control machinery.
Set up run-to-address
sts.u32 0x00000020 0x29 0x01 0x00 0x00
sts.u32 0x00000024 0x00 0x00 0x00 0x00
sts.u8 0x00000040 0x00
sts.u8 0x00000040 0x00
sts.u16 0x00000028 0x00 0x01
sts.u8 0x00000048 0x00
This sequence performs the following operations (some of them need more figuring out how things work to understand):
Stores 0x00000129 (as the target program address to run the processor to) into the hardware breakpoint unit’s first break address register at 0x00000020. Program addresses are in words which is why they are half what they should be going by
objdump
output.Stores 0x00000000 to the hardware breakpoint unit’s second break address register at 0x00000024
Stores 0x00 to a byte register at 0x00000040 (twice) - the purpose for this is not well understood yet and is essential to the proper functioning of the breakpoint unit. Without this, the unit will not engage correctly.
Stores 0x0100 to the hardware breakpoint unit’s breakpoint counter and configuration (16-bit) register at 0x00000028
Stores 0x00 to a byte register at 0x00000048 - the purpose for this is not well understood yet and is essential to the proper functioning of the breakpoint unit. Without this, the unit will not engage correctly.
Set the program counter to a specific address
sts.u32 0x00000004 0x00 0x00 0x00 0x00
This writes the program counter, exposed at 0x00000004, to its POR value
Run the program to breakpoint
stcs reset 0x00
sts.u8 0x0000000a 0x00
stcs r4 0x01
ldcs r3
ldcs r3
This sequence does the following:
With r4 poked, releases reset so that at this stage enters the processor into a debugger-supervised state
Stores 0x00 to the debug register 0x0000000a which appears to arm the processor resuming execution
Ensures r4 holds the value 0x01 which ensures we stay in debugger-supervised state is set
Loads the status of the processor from r3 (should read as 0x14 the first time)
Loads the status of the processor again (should read as 0x04 the second time indicating the breakpoint is hit)
Clean up
sts.u16 0x00000028 0x00 0x00
sts.u8 0x00000048 0x00
Cleans up after the debug run by clearing all set breakpoints by setting the control and counter value to 0
Reading processor state
Read program counter (PC+1)
lds.u32 0x00000004
Reads the program counter from its fixed register address of 0x00000004
Clean up verification
lds.u8 0x00000050
lds.u8 0x0000000b
Reads an unknown special register in the breakpoint unit and the upper half of the control register
Read back Stack Pointer + SREG
st.u32 ptr 0x0100003d
repeat 0x02
ld *(ptr++)
Directly reads from the PDI bus location for the SPL, SPH and SREG registers in peripheral space
Verify state
ldcs r3
lds.u8 0x010001ca
lds.u8 0x010001c4
Verify that the NVM controller in peripheral space is in an idle state
Reading back the AVR registers
sts.u32 0x00000004 0x00 0x00 0x00 0x00
sts.u8 0x0000000a 0x11
sts.u32 0x00000000 0x20 0x00 0x00 0x00
stcs r4 1
st ptr 0x0000000c
repeat 31
ld *ptr
This sequence does the following:
Sets the program counter to 0x0 (is this actually necessary?)
Loads the debug control register with special value 0x11 which turns the multifunction register into a read counter
Loads the multifunction register (now a read counter) with the number of registers to read (32)
Tells the PDI part of the debug controller to exec the action (STCS to r4)
Loads the PDI pointer register with the address of the I/O FIFO (0x0000000c)
Tells the PDI controller to repeat for the number of AVR registers minus 1 (due to how the repeat register works, this makes the load run 32 times)
Reads the registers back from the read FIFO (r0..r31)
Single Stepping
The debug control system has a tempory (single-stepping) breakpoint available too which can be used to implement one-use breakpoints without touching the main two.
Setting up the breakpoint
sts.u8 0x0000000a 0x04
sts.u32 0x00000000 0x55 0x03 0x00 0x00
sts.u32 0x00000004 0x54 0x03 0x00 0x00
This sequence does the following:
Loads the debug control register with special value 0x04 which turns the multifunction register into a temporary breakpoint
Loads the now breakpoint register with the address to break on (0x0355 in this example)
Loads the program counter with the resumption address (0x0354 in this address, making this a single-step run)