bfemu - Blackfin emulation library Documentation
- Martin Strubel
The in circuit emulation library libbfemu for the Blackfin series from Analog Devices is a library helping to access the built in debugging and testing features of the Blackfin via its JTAG port. It allows to completely 'remote control' the Blackfin via JTAG and to monitor the system as non-intrusively as possible, thus no existing kernel or monitor/bootloader is required.
For hardware developer, this provides the following functionality:
bfemu currently only supports one blackfin CPU in the JTAG chain. If your chain is longer, please contact section5 (http://www.section5.ch).
- First time programming of the Blackfin hardware platform
- System testing of hardware components under normal running conditions
- Software debugging of kernels or kernel drivers, standalone programs, etc. via Gnu Debugger (GDB) based environments.
The libbfemu is partial OpenSource, meaning, that some driver specific bottom layers may not be disclosed. However, full OpenSourcing of this library is planned for the near future.
bfemu is optimized for stability and speed to run on the ICEBEAR USB JTAG adapter which can be ordered via http://www.section5.ch/icebear. Support for parallel port JTAG adapters is available in the OpenSource version.
bfemu has been heavily tested on the following platforms:
When designing your own Blackfin hardware, make sure to read Analog Devices' JTAG application note EE-68. section5 can also support you in getting your embedded system tested and set up using JTAG.
When debugging under the GDB environment, the debugger needs to know what target to connect to. This target is a tiny server daemon, typically running on the local computer. It is equivalent to a gdbserver, although it does not run on the target hardware. This server is called gdbproxy and is derived from rproxy-0.7. This is the procedure you normally start with:
The Blackfin will now halt and you can:
- Start up gdbproxy:
gdbproxy bfin <special options>
- Start bfin-jtag-gdb
- Connect to the gdbproxy via:
(gdb) target remote :2000
To load an ELF binary into memory and execute:
- Set breakpoints with '
- Single step with '
ni' or '
- Disassemble using '
disassemble $pc $pc+20' or '
- Print core register values '
print $r0' or '
info registers' etc.
- Continue program operation with '
- Stop program with Ctrl-C
(gdb) load blink.dxe
Loading section .text, size 0x1b8 lma 0xffa00000
Start address 0xffa00000, load size 440
Transfer rate: 3520 bits in <1 sec, 440 bytes/write.
More commands: see GDB help
Note that the above examples assume, that the peripheral hardware has been properly initialized. GDB does not initialize anything on start, since it has no knowledge about the current state of the system. Before you load a program into the Blackfin, make sure you have done the correct initialization - read more hints below.
For different system configurations, GDB scripts are used. GDB scripting is a very powerful feature and helps to automatize different system configurations.
When loading the very first program onto a Blackfin, you will very likely have to initialize your SDRAM before you can actually execute a 'load'. Not doing so can cause a hardware error which may require a 'monitor reset' command to recover -- see JTAG internals.
The following example initializes the SDRAM controller for the memory configuration found on the BF533 STAMP:
source mmr.gdb # load the memory register definitions
set *$EBIU_SDGCTL = 0x80111149
set *$EBIU_SDBCTL = 0x00000037
set *$EBIU_SDRRC = 0x00000817
For tested hardware, please see init-* scripts in the scripts/ subdirectory of the Blackfin-JTAG GDB distribution.
It is recommended, to add the initialization commands into the .gdbinit file residing in your hardware development directory -- see also GDB manual.
By default, GDB does not catch any of the Blackfin core exceptions to not interfere with kernel or user code whatsoever. When the core stalls ("CORE FAULT") while debugging, it is very likely that an exception got fired but not handled. Reasons for this can be uninitialized SDRAM or illegal memory accesses. To facilitate debugging of programs, make sure your program contains exception handling code.
Then, you can use the GDB script 'catch_exc.gdb' to make GDB react to hardware exceptions. This basically sets a breakpoint inside the initialized exception handlers.
Example output on illegal memory access:
- 'catch_exc.gdb' takes only effect after your program has initialized the exception handlers!
-- Exception !!
-- EXCAUSE: 24
-- Reason: "Address error / misalignment"
-- Instruction: 0x12e0 <savecall+32>: [p0++] = r0;
For example, check out the
.gdbinit script from the shell demo (which is found in the Blackfin Software download section at the section5 website). The
catch_exc.gdb script is run after the initialization of the exception handlers. We will cause the above Address misalignment exception by dumping memory from an odd address (which is not caught in the shell demo). These are the steps to reproduce such an error:
In some cases, when it is very hard to catch an error, you can also use a loop, for example, to find out, when a hardware error is latched to the IRQ controller:
- Connect a serial cable to your board and configure your terminal program to 8 bits, no parity, 19200 bps.
- Load shell.dxe onto your board using GDB
- When you run the program via 'c', it should output a welcome message:
* UART setup successfull - as you can see :-) *
- Type 'd 1 1' to dump 8 short words from address 1 - this is an illegal access. GDB will catch this exception.
while (!(*$ILAT & 0x20))
This will single step until the hardware error occurs.
Sometimes it is not obvious, why an exception was caused. Especially, when certain interrupts are disabled but latched, errors or program interrupts can happen at an unexpected location.
To display the interrupt status, there are several userdefined commands in the
dump.gdb script. You must load the file
- dump_irq: Dump IRQ status. This shows the pending interrupts (what IRQ handler we are currently in), the latched IRQs (these will be handled next, if not masked), and the IRQ mask register.
- dump_siciar: Dump the system interrupt assigment registers This shows, what peripherals interrupt is assigned to which IRQ channel.
- dump_sic: Shows Peripheral IRQs in service and their IRQ mask
- dump_pll: Shows PLL status and parameters
The bfemu API is documented under the modules section. Please see the examples on usage.
- dump_ebiu: Shows EBIU status and parameters
Note that currently only one Blackfin device is supported in the JTAG chain.
All emulation functions require a valid CPU handle to be passed as first argument. This handle is acquired via calling jtag_init().
If not specified, an API function always returns 0 on success and a negative error code on failure. See "Error Handling" in the modules section for meaning of the error codes.
See also the Troubleshooting section of the ICEBEAR short manual.
When accessing the on-chip L1 program memory via the function set_memory(), or get_memory(), there is one important thing to note: Since access can not happen directly, the L1 data memory is used as an auxiliary buffer to transfer the data blocks via DMA. The DMA context and the auxiliary buffer are NOT preserved!
The user must either:
- Make sure that the buffer starting at
0xff800000 with the size of the current transfer size (maximum L1_DATA_AUXBUF_SIZE) passed to set_memory() or get_memory() is saved first. After all memory transactions, the buffer must be restored. If the transfer size is bigger than L1_DATA_AUXBUF_SIZE, data is automatically transfered in chunks of the latter size.
When accessing the SDRAM, make sure that the PLL and the SDRAM controller are correctly configured. Otherwise, the core may stall.
The command resets the Blackfin core and system. The program counter is set and executed at the address which is depending on the boot mode (see Boot Mode switches of your platform hardware manual) or the SYSCR register.
- Write L1 program memory first on program load, then write L1 data memory.
Doing a single step ('si' command) after a 'monitor reset' shows the location after a reset, which is normally:
0x20000000: external Flash/Async memory
0xef000000: Blackfin Boot ROM
Depending on the silicon revision, the program counter may already be a few instructions ahead of the above listed location. This can not be avoided.
There are two kind of supported breakpoints, software and hardware breakpoints. Software breakpoints are realized by writing the EMUEXCPT instruction into the desired location. For hardware breakpoints and watchpoints, the watchpoint unit of the blackfin is used. The behaviour of the watchpoint unit seems to differ from the documentation and does not produce reliable results on silicon revisions < 0.3. See also wpu_init().
This is a short summary of the components needed to get a full test and debugging system running:
- libbfemu : Blackfin JTAG emulation routines
- gdbproxy : Remote debugging server with Blackfin emulation driver
- bfin-jtag-gdb: Blackfin JTAG target version of GDB
- No PC support yet for hung systems.. (PC is = RETE)
- LSETUP handling needed. Singlestepping through hardware loops can result in weird behaviour.
- Also, if you set a breakpoint in the last command of an LSETUP loop, a SIGTRAP might be received with the PC somewhere inside the LSETUP command (i.e. no longer instruction-aligned). This must be a blackfin core beauty flaw, but I am sure this is a sacrifice to an efficient pipeline.
- bfemu can hang, core fault, or throw an exception when an illegal access to memory is done, for instance, a 32 bit access when it should be a 16 bit MMR. Make sure that the memory you are accessing exists. Future versions of bfemu are planned with a complete memory range validation.
- bfemu can NOT compensate for a bad startup sequence of your code. It is recommended to put all startup and exception handling code into the L1 program RAM to avoid core faults on bad PLL or SDRAM initialization sequences.
- Single stepping over an IDLE function may fail, if there is no system event waking the CPU up. It will then hook in the IDLE state. Under scrutiny.
- Sometimes, the CPU can not be resurrected from an IDLE instruction while the DBGSTAT register is polled (and showing "CPU is idle"). Reset the board and try again. Under examination.
Generated on Thu Sep 1 15:02:53 2005 for bfemu - Blackfin emulation library by