How to access the memory mapped debug registers?

Now that the funny PABT-behaviour is found to be (probably) caused ny debug state, I'd like to exit debug state before return from PABT exception. The ARM v7-A/R ARM says that I should write RRQ to DBGDRCR, but it seems that in Cortex-A7 it's not accessible via CP14.

It should be accessible via memory-mapped registers, and the manual gives the offset, but offset from what?

I understood that the register offset is the register number multiplied by four, so for DBGDRCR the offset would be 36*4 = 144 (0x90).

In the TRM it says:

Table 10-26 Address mapping for debug trace components

Address range Componenta

0x00000 - 0x00FFF ROM table

0x01000 - 0x0FFFF Reserved

0x10000 - 0x10FFF CPU 0 Debug

0x11000 - 0x11FFF CPU 0 PMU

0x12000 - 0x12FFF CPU 1 Debug

0x13000 - 0x13FFF CPU 1 PMU

0x14000 - 0x14FFF CPU 2 Debug

0x15000 - 0x15FFF CPU 2 PMU

0x16000 - 0x16FFF CPU 3 debug

Are these too offsets from somewhere, or absolute addresses?

Do I write to DBGDRCR by just writing the value into address 0x100090 (core 0, assuming not locked)?

Or do I need to calculate the register address from the contents of DBGDRAR?

And what's the ROM table?

  • Hello,

    Are these too offsets from somewhere, or absolute addresses?

    It is the offsets from somewhere.

    The base address is product specific.

    You seem to use Raspberry Pi2.

    As I don't have Raspberry Pi2, I don't know it.

    According to the searching results, it seems to be 0x80000000.

    But I'm not sure.

    Best regards,

    Yasuhiko Koumoto.

  • Thanks. I started hunting for the info, and found out that the thing should start with reading the DBGDRAR (Debug ROM Address Register) then

    Read the ROM Table entry for the component, and extract the Address offset for the component. The Address

    offset is bits [31:12] of the ROM Table entry

    Now the thing is figuring out how to identify the right component. I haven't found any explanations of what kind of things are the 'components'. Is core 0 a component? And core 1 another component? How about peripherals? And Devices?

    I guess I should find '0x9' (CoreSight compliant debug component) in the 'class'-field of component ID register of the right component?

    And 'continuation code' (used as designer ID) 0x4 and identity code of 3B in the JEP-106 field of the 'conceptual 64-bit peripheral ID register'? And DBGDIDR version-field should be 0b0101 (ARMv7, v7.1 Debug architecture)?

    Quite a mess just to exit the debug state.

    Are there descriptions for other possible values in those fields and their meanings?

  • Ah, figured out the debug register accesses. Still can't tell from the ROM table and ID registers, which component is the right one.

    (I know it's the first component, though.)

    And I still don't seem to be able to return from the BKPT (added some debug about the components, unlocked the registers and wrote the RRQ-bit to exit debug state).

    Not much better?

    PIDR is actually a word formed from the lowest bytes of PIDR 3-0.

    Finally! Got into main()

    DBGDRAR: 40020003 DBGDSAR: 40030003 DBGLSR: 00000003 DBGLSR2: 00000001

    comp: 00000000 : 00010003

    CIDR1: 00000090

    PIDR5: 00000004

    PIDR: 005bbc07

    comp: 00000001 : 00011003

    CIDR1: 00000090

    PIDR5: 00000004

    PIDR: 005bb9a7

    comp: 00000002 : 00012003

    CIDR1: 00000090

    PIDR5: 00000004

    PIDR: 005bbc07

    comp: 00000003 : 00013003

    CIDR1: 00000090

    PIDR5: 00000004

    PIDR: 005bb9a7

    comp: 00000004 : 00014003

    CIDR1: 00000090

    PIDR5: 00000004

    PIDR: 005bbc07

    comp: 00000005 : 00015003

    CIDR1: 00000090

    PIDR5: 00000004

    PIDR: 005bb9a7

    comp: 00000006 : 00016003

    CIDR1: 00000090

    PIDR5: 00000004

    PIDR: 005bbc07

    comp: 00000007 : 00017003

    CIDR1: 00000090

    PIDR5: 00000004

    PIDR: 005bb9a7

    comp: 00000008 : 00000000

    trying SVC

    SVC EXCEPTION

    exc_addr: 000003e8

    SPSR: 68000013

    returned from SVC

    trying BKPT1

    PABT EXCEPTION

    exc_addr: 000090d4

    SPSR: 60000013

    my_cpsr: 60000197

    dbgdscr: 0204000e

    IFSR: 00000002

    dbgdscr2: 0204000e

    i: 00000000returned

    UNDEFINED EXCEPTION

    exc_addr: 00900008

    SPSR: 600103d3

    Looks like it returned from PABT, but made an UNDEFINED exception right after, and the address is, to put it mildly, weird.

    The mode was, however, SVC again, before the UNDEFINED EXCEPTION, and it didn't restart.

    I haven't changed anything in the main below the first SVC. Added the component info dump before it, though.

    Before this "debug circus" I found out that adding 3 debug prints in the PABT handler made it return fine, but removing any of them, caused different weird return problems depending on which print I removed.

    I guess I'll try again with all this plus the debug prints.

    My head hurts...

    [EDIT]

    With the debug prints, it booted 3 times and jammed.

    This time the first time the exceptions were entered from SVC-mode (as expected), but the second time from FIQ (I don't have it in use) and the 3rd time from IRQ.

    IFSR was 2 => the cause was BKPT.

    [/EDIT]

    [EDIT2]

    Added some more debug, and seems like it's getting even more weird. The reboot seems to take place a more or less random time after returning from the PABT.

    Also after restart the mode seems to be about random.

    trying SVC

    SVC EXCEPTION

    exc_addr: 000003e8

    SPSR: 68000013

    returned from SVC

    trying BKPT1

    PABT EXCEPTION

    exc_addr: 000090f4

    SPSR: 60000013

    my_cpsr: 60000197

    dbgdscr: 0204000e

    IFSR: 00000002

    PABT done

    my_cpsr: 60000013

    returned from BKPT1

    Finally! Got into main()

    ...

    trying SVC

    SVC EXCEPTION

    exc_addr: 000003e8

    SPSR: 6800001f

    returned from SVC

    trying BKPT1

    PABT EXCEPTION

    exc_addr: 000090f4

    SPSR: 6000001f

    my_cpsr: 60000197

    dbgdscr: 0204000e

    IFSR: 00000002

    PABT done

    my_cpsr: 6000001f

    returned from BKPT1

    entering main

    and then nothing. The echoing doesn't work, and even the rest of the string is not printed (should be "entering main loop").

    [/EDIT2]

  • Yeah. The PABT-problem is solved.

    The debug state was not entered at all.

    I realized that everything worked as long as I used the 'raw write', but as soon as I called the interrupt driven serial I/O, bad things happened.

    And I found the cause: when the string has been written into the serial buffer, the tx interrupts are enabled and characters are read from the buffer and written into the tx FIFO (if it wasn't full) to put the ball rolling. It was done in serial_start_tx().

    void serial_start_tx()
    {
        uint32_t cpsr_store;
    
        // no messing with tx interrupt
        cpsr_store = disable_save_ints();
        // re-enable tx interrupts
        *((volatile uint32_t *)UART0_IMSC) |= (1<<5);
    
        (void) serial_tx();
    
        // Clear transmit interrupt - probably not needed
        //*((volatile uint32_t *)UART0_ICR) = (1<<5);
    
        restore_ints(cpsr_store);
    }
    

    To not mess up with the interrupts (simultaneous reading from the tx buffer by both serial_start_tx and tx interrupt) the IRQs were disabled before starting the transmission and restored after that, but there was a small bug in the restoring:

    static inline uint32_t disable_save_ints()
    {
        uint32_t status;
        asm volatile (
                "mrs %[var_reg], cpsr\n\t"
                "dsb\n\t"
                "cpsid aif\n\t"
                :[var_reg] "=r" (status)::
        );
        return status;
    }
    
    static inline void restore_ints(uint32_t status)
    {
        asm volatile (
                "msr cpsr_fsxc, %[var_reg]\n\t"
                :[var_reg] "=r" (status)::
        );
    }
    

    In the definition I promised that the restote_ints() doesn't return anything...

    This solved the problem:

    static inline void restore_ints(uint32_t status)
    {
        asm volatile (
                "msr cpsr_fsxc, %[var_reg]\n\t"
                ::[var_reg] "r" (status):
        );
    }
    

    The good news is that while debugging the problem, I learned A LOT about the debug HW!