12.11.6. Accessing registers and memory in debug state

This section describes the following:

Reading and writing registers through the DCC

To read a single register, the debugger can use the sequence that Example 12.13 shows. This sequence depends on two other sequences, Example 12.1 and Example 12.4.

Example 12.13. Reading an ARM register

uint32 ReadARMRegister(int Rd)
{
    // Step 1. Execute instruction MCR p14, 0, Rd, c0, c5, 0 through the DBGITR.
    ExecuteARMInstruction(0xEE000E15 + (Rd<<12));
    // Step 2. Read the register value through DBGDTRTX.
    reg_val := ReadDCC();
    return reg_val;
}

Example 12.14 shows a similar sequence for writing an ARM register.

Example 12.14. Writing an ARM register

WriteRegister(int Rd, uint32 reg_val)
{
    // Step 1. Write the register value to DBGDTRRX.
    WriteDCC(reg_val);
    // Step 2. Execute instruction MRC p14, 0, Rd, c0, c5, 0 to the DBGITR.
    ExecuteARMInstruction(0xEE100E15 + (Rd<<12));
}

Reading the PC in debug state

Example 12.15 shows the code to read the PC.

Example 12.15. Reading the PC

ReadPC()
{
    // Step 1. Save R0
    saved_r0 := ReadRegister(0);
    // Step 2. Execute the instruction MOV r0, pc through the DBGITR.
    ExecuteARMInstruction(0xE1A0000F);
    // Step 3. Read the value of R0 that now contains the PC.
    pc := ReadRegister(0);
    // Step 4. Restore the value of R0.
    WriteRegister(0, saved_r0);
    return pc;
}

Note

You can use a similar sequence to write to the PC to set the return address when leaving debug state or to read the CPSR or coprocessor registers.

Writing the CPSR in debug state

Example 12.16 shows the code for writing the CPSR.

Example 12.16. Writing the CPSR

WriteCPSR(uint32 cpsr_val)
{
    // Step 1. Save R0.
    saved_r0 := ReadRegister(0);
    // Step 2. Write the new CPSR value to R0.
    WriteRegister(0, cpsr_val);
    // Step 3. Execute instruction MSR R0, CPSR through the DBGITR.
    ExecuteARMInstruction(0xE12FF000);
    // Step 4. Execute a PrefetchFlush instruction through the DBGITR.
    ExecuteARMInstruction(9xEE070F95);
    // Step 5. Restore the value of R0.
    WriteRegister(0, saved_r0);
}

Reading memory

Example 12.17 shows the code for reading a byte of memory.

Example 12.17. Reading a byte of memory

uint8 ReadByte(uint32 address, bool &aborted)
{
    // Step 1. Save the values of R0 and R1.
    saved_r0 := ReadRegister(0);
    saved_r1 := ReadRegister(1);
    // Step 2. Write the address to R0.
    WriteRegister(0, address);
    // Step 3. Execute the instruction LDRB R1,[R0] through the DBGITR.
    ExecuteARMInstruction(0xE5D01000);
    // Step 4. Read the value of R1 that contains the data at the address.
    datum := ReadRegister(1);
    // Step 5. Restore the corrupted registers R0 and R1.
    WriteRegister(0, saved_r0);
    WriteRegister(1, saved_r1);
    // Step 6. Check the DBGDSCR for a sticky abort.
    aborted := CheckForAborts();
    return datum;
}

Example 12.18 shows the code for checking for aborts after a memory access.

Example 12.18. Checking for an abort after memory access

bool CheckForAborts()
{
    // Step 1. Check the DBGDSCR for a sticky abort.
    dbgdscr := ReadDebugRegister(34);
    if (dbgdscr & ((1<<6) + (1<<7))
    {
        // Step 2. Clear the sticky flag by writing DBGDRCR[2].
        WriteDebugRegister(36, 1<<2);
        return true;
    }
    else
    {
        return false;
    }
}

Note

You can use a similar sequence to read a halfword of memory and to write to memory.

To read or write blocks of memory, substitute the data instruction with one that uses post-indexed addressing. For example:

LDRB R1, [R0],1

This prevents reloading the address value for each sequential word.

Example 12.19 shows the code for reading a block of bytes of memory.

Example 12.19. Reading a block of bytes of memory

ReadBytes(uint32 address, bool &aborted, uint8 *data, int nbytes)
{
    // Step 1. Save the value of R0 and R1.
    saved_r0 := ReadRegister(0);
    saved_r1 := ReadRegister(1);
    // Step 2. Write the address to R0
    WriteRegister(0, address);
    while (nbytes > 0)
    {
        // Step 3. Execute instruction LDRB R1,[R0],1 through the DBGITR.
        ExecuteARMInstruction(0xE4D01001);
            // Step 4. Read the value of R1 that contains the data at the
            // address.
        *data++ := ReadRegister(1);
        --nbytes;
    }
    // Step 5. Restore the corrupted registers R0 and R1.
    WriteRegister(0, saved_r0);
    WriteRegister(1, saved-r1);
    // Step 6. Check the DBGDSCR for a sticky abort.
    aborted := CheckForAborts();
    return datum;
}

Example 12.20 shows the sequence for reading a word of memory.

Note

A faster method is available for reading and writing words using the direct memory access function of the DCC. See Fast memory read/write.

Example 12.20. Reading a word of memory

uint32 ReadWord(uint32 address, bool &aborted)
{
    // Step 1. Save the value of R0.
    saved_r0 := ReadRegister(0);
    // Step 2. Write the address to R0.
    WriteRegister(0, address);
    // Step 3. Execute instruction LDC p14, c5, [R0] through the DBGITR.
    ExecuteARMInstruction(0xED905E00);
    // Step 4. Read the value from the DTR directly.
    datum := ReadDCC();
    // Step 5. Restore the corrupted register R0.
    WriteRegister(0, saved_r0);
    // Step 6. Check the DBGDSCR for a sticky abort.
    aborted := CheckForAborts();
    return datum;
}

Fast register read/write

When multiple registers must be read in succession, you can optimize the process by placing the DCC into stall mode and by writing the value 1 to the DCC access mode bits. For more information, see CP14 c1, Debug Status and Control Register.

Example 12.21 shows the sequence to change the DTR access mode.

Example 12.21. Changing the DTR access mode

SetDTRAccessMode(int mode)
{
    // Step 1. Write the mode value to DBGDSCR[21:20].
    dbgdscr := ReadDebugRegister(34);
    dbgdscr := (dbgdscr & ~(0x3<<20)) | (mode<<20);
    WriteDebugRegister(34, dbgdscr);
}

Example 12.22 shows the sequence to read registers in stall mode.

Example 12.22. Reading registers in stall mode

ReadRegisterStallMode(int Rd)
{
    // Step 1. Write the opcode for MCR p14, 0, Rd, c5, c0 to the DBGITR.
    // Write stalls until the DBGITR is ready.
    WriteDebugRegister(33, 0xEE000E15 + (Rd<<12));
    // Step 2. Read the register value through the DCC. Read stalls until 
    // DBGDTRTX is ready
    reg_val := ReadDebugRegister(32);
    return reg_val;
}

Example 12.23 shows the sequence to write registers in stall mode.

Example 12.23. Writing registers in stall mode

WriteRegisterInStallMode(int Rd, uint32 value)
{
    // Step 1. Write the value to the DBGDTRRX.
    // Write stalls until the DBGDTRRX is ready.
    WriteDebugRegister(32, value);
    // Step 2. Write the opcode for MRC p14, 0, Rd, c5, c0 to the DBGITR.
    // Write stalls until the DBGITR is ready.
    WriteDebugRegister(33, 0xEE100E15 + (Rd<<12));
}

Note

To transfer a register to the CPU when in stall mode, you are not required to poll the DBGDSCR each time an instruction is written to the DBGITR and a value read from or written to the DTR. The CPU stalls using the signal PREADYDBGm until the previous instruction has completed or the DTR register is ready for the operation.

Fast memory read/write

This section provides example code to enable faster reads from memory by making use of the DTR access mode.

Example 12.24 shows the sequence for reading a block of words of memory.

Example 12.24. Reading a block of words of memory

ReadWords(uint32 address, bool &aborted, uint32 *data, int nwords)
{
    // Step 1. Write the value 0b01 to DBGDSCR[21:20] for stall mode.
    SetDTRAccessMode(1);
    // Step 2. Save the value of R0.
    saved_r0 := ReadRegisterInStallMode(0);
    // Step 3. Write the address to read from to the DBGDTRRX.
    // Write stalls until the DBGDTRRX is ready.
    WriteRegisterInStallMode(0, address);
    // Step 4. Write the opcode for LDC p14, c5, [R0], 4 to the DBGITR.
    // Write stalls until the DBGITR is ready.
    WriteDebugRegister(33, 0xECB05E01);
    // Step 5. Write the value 0b10 to DBGDSCR[21:20] for fast mode.
    SetDCCAccessMode(2);
    // Step 6. Loop reading out the data.
    // Each time a word is read from the DBGDTRTX, the instruction is reissued.
    while (nwords > 1)
    {
        *data++ = ReadDebugRegister(35);
        --nwords;
    }
    // Step 7. Write the value 0b00 to DBGDSCR[21:20] for non-blocking mode.
    SetDTRAccessMode(0);
    // Step 8. Must wait for the final instruction to complete. If there
    // was an abort, this completes immediately.
    do
    {
       dbgdscr := ReadDebugRegister(34);
    }
    until (dbgdscr & (1<<24));
    // Step 9: Check for aborts.
    aborted := CheckForAborts();
    // Step 10: Read the final word from the DCC.
    if (!aborted) *data := ReadDCC();
    // Step 11. Restore the corrupted register r0.
    WriteRegister(0, saved_r0);
}

Example 12.25 shows the sequence for writing a block of words to memory.

Example 12.25. Writing a block of words to memory (fast download)

WriteWords(uint32 address, bool &aborted, uint32 *data, int nwords)
{
    // Step 1. Save the value of R0.
    saved_r0 := ReadRegister(0);
    // Step 2. Write the value 0b10 to DBGDSCR[21:20] for fast mode.
    SetDTRAccessMode(2);
    // Step 3. Write the opcode for MRC p14, 0, R0, c5, c0 to the DBGITR.
    // Write stalls until the DBGITR is ready but the instruction is not issued.
    WriteDebugRegister(33, 0xEE100E15);
    // Step 4. Write the address to read from to the DBGDTRRX
    // Write stalls until the DBGITR is ready, but the instruction is not reissued.
    WriteDebugRegister(32, address);
    // Step 5. Write the opcode for STC p14, c5, [R0], 4 to the DBGITR.
    // Write stalls until the DBGITR is ready but the instruction is not issued.
    WriteDebugRegister(33, 0xECA05E01);
    // Step 6. Loop writing the data.
    // Each time a word is written to the DBGDTRRX, the instruction is reissued.
    while (nwords > 0)
    {
        WriteDebugRegister(35, *data++);
        --nwords;
    }
    // Step 7. Write the value b00 to DBGDSCR[21:20] for normal mode.
    SetDTRAccessMode(0);
    // Step 8. Restore the corrupted register R0.
    WriteRegister(0, saved_r0);
    // Step 9. Check the DBGDSCR for a sticky abort.
    aborted := CheckForAborts();
}

Note

As the amount of data transferred increases, these functions reach an optimum performance of one debug register access per data word transferred.

After writing data to memory, you must execute a data synchronization barrier instruction to ensure that the memory window updates properly

Copyright © 2010-2011 ARM. All rights reserved.ARM DDI 0460C
Non-ConfidentialID021511