When debugging, many debuggers disable data and/or instruction caches on the target. The reason for this is that the caches cause the hardware to not truly reflect what the debug information provides. For example, a program may change a variable by updating a memory location. If this location is in the data cache and has not yet been written out to actual memory, the debugger will get the wrong data when it reads the actual memory location for the variable. Similarly, the way that is most frequently used to set a software breakpoint is to modify the memory at the code location where the breakpoint is to be set. If the instruction is already in the instruction cache and is not read from actual memory, the breakpoint will not | fire | even though the instruction is executed. There are a variety of similar problems that can occur when data and/or instruction caches are enabled on the target.
When debugging with a debug monitor on the target hardware, the debug monitor may solve the cache problems by containing code that will force a consistency between the caches and the actual memory by flushing the caches (copying the contents of the caches out to actual memory), and by invalidating the instruction cache (emptying the instruction cache of valid instructions so that it will be refilled from actual memory). The code that forces consistency between the caches and actual memory is executed whenever the target stops at a breakpoint or because of step completion.
When JTAG debugging, there is no debug monitor code running on the target hardware. So it is the responsibility of the host-side debugger to ensure the consistency between the caches and actual memory. Unfortunately, the code that needs to be executed to do this will vary somewhat from target board to target board, so it is not practical to try to explicitly build this into the host side of the debugger.
In SCORE for the PowerPC, there is the concept of a board-dependent set of routines called the | User Configurable Code | (UCC for short) that is written to initialize the board and provide interfaces to board hardware such as UARTs. Typically the UCC also contains board dependent code to initialize and manipulate the caches for the processor/board combination. It is here that code can also be added for the JTAG debugger to use.
Specifically, in SCORE for the PowerPC, JTAG debugging will run with the data and instruction caches enabled if a routine that the debugger (and only the debugger) can use to provide cache consistency is provided. Such a routine should flush the data cache and invalidate the data and instruction caches. The JTAG debugger will look for code that begins with the label "ddci_jtag_debug_fixup_cache" and ends with
"ddci_jtag_debug_fixup_cache_end". The UCC contains a file ucc_asm_macros.h that provides a macro for placing a global label, such as the ones above, in assembly files. To allow PowerPC JTAG debugging with the caches enabled, provide the required routine surrounded by the specified labels, and make sure that it is included in the link.
Here is an example of an assembly file for the Sandpoint 755 that provides the code needed by the debugger.
#include "ucc_asm_macros.h"
/* Define the following to be
appropriate for your machine */
#define HID0_SPR_NUMBER 1008
#define DCACHE_BLOCKSIZE 32
#define PHYMEMHI 0x400 /* high order part
of limit of memory */
#define DCFI 0x400 /* bit 21, Data Cache
Flash Invalidate. */
#define ICFI 0x00000800 /* bit 20, Instruction
Caceh Flash Invalidate */
/* BEGIN FUNCTION */
/* void ddci_init_debug_support(void);
* Input: none
* Output: none
* Side effects:
* None
*/
global_routine(ddci_init_debug_support)
blr
/* The following is used only by the
debugger when JTAG debugging. */
/* It allows the data and instruction
caches to be enabled by the code. */
/* It is executed whenever a breakpoint
is hit or a machine step is */
/* completed. */
global_label(ddci_jtag_debug_fixup_cache)
/* save registers to be used */
stw r3, -4(r1)
stw r4, -8(r1)
stw r5, -12(r1)
mfcr r5
stw r5, -16(r1)
/* Flush the data cache */
li r3, 0
lis r4, PHYMEMHI
add r4, r3, r4
L.ddci_fix_loop:
dcbf 0, r3
addi r3, r3, DCACHE_BLOCKSIZE
cmplw cr0, r3, r4
blt L.ddci_fix_loop
isync
sync
/* Invalidate the data cache */
mfspr r3, HID0_SPR_NUMBER
ori r4, r3, DCFI /* Set DCFI bit
in "HID0" (GPR4). */
xori r5, r4, DCFI /* Clear DCFI bit
in "HID0" (GPR5). */
mtspr HID0_SPR_NUMBER, r4
mtspr HID0_SPR_NUMBER, r5
isync
sync
/* Invalidate the instruction cache */
mfspr r3, HID0_SPR_NUMBER
ori r4, r3, ICFI /* Set bit 20 in
"HID0" (GPR4). */
xori r5, r4, ICFI /* Clear bit 20
in "HID0" (GPR5). */
mtspr HID0_SPR_NUMBER, r4
mtspr HID0_SPR_NUMBER, r5
isync
sync
/* Restore registers used */
lwz r5, -16(r1)
mtcr r5
lwz r5, -12(r1)
lwz r4, -8(r1)
lwz r3, -4(r1)
global_label(ddci_jtag_debug_fixup_cache_end)
blr
.end
Notice that the code actually begins with the global_routine "ddci_init_debug_support" that merely does a return. This is so that we can ensure that the needed coded is linked in by calling ddci_init_debug_support somewhere in the program code. Typically such a call would be added into the UCC code in the file initialize.c. So that the call can be made, the entry also needs to be defined in a header that is included.
In SCORE 2.4 and later, the UCC code for the Sandpoint 750/755 and the UCC code for the Excimer boards contain complete examples of the code needed to allow the debugger to properly handle code that runs with the caches enabled.
Customer Quote:
"DDC-I has a good solution for us and your willingness to get down to the issues and help us get what we need has made it a clear choice."