[[TOC(Debugging/OpenOCD/Xilinx_Zynq , depth=3)]] = OpenOCD Support for XIlinx Zynq = OpenOCD supports the [http://www.xilinx.com/products/silicon-devices/soc/zynq-7000.html Xilinx Zynq-7000] parts. The support is not current in the OpenOCD source but you can create a suitable environment to the configurations here and access the part. Xilinx define the JTAG access to the Zynq part with a 14-pin header while suitable adaptors such as the Flyswatter2 have the standard ARM 20pin header. You can buy from Avnet a [http://www.em.avnet.com/en-us/design/drc/Pages/ZedBoard-Processor-Debug-Adapter.aspx ZedBoard Processor Debug Adapter]. JTAG access to the Zynq only operates if the board is in a suitable JTAG mode and it is not in a secure boot mode. Please refer to the Xilinx Zynq-7000 Technical Reference Manual and any user manul for your hardware for details on how to set the board's mode into JTAG. == Xilinx SDK PS7 Initialisation == The Xilinx design tools and SDK produce initialisation code. The System design tool lets you specific the type of memory, clocks and bus structures and the tools generate C code for use in the First Stage Boot Loader (FSBL) and TCL code for use with the Xilinx XDM JTAG tool. Save the following code as `xilinx-tcl.cfg`: {{{ # # TCL to allow the Xilinx PS7 Init TCL code to run in OpenOCD. # proc mrd { addr } { mem2array x 32 $addr 1 return $x(0) } proc mask_write { addr mask value } { set curval "[mrd $addr]" set maskedval [expr {$curval & ~$mask}] #echo "curval = [format 0x%08x $curval] maskedval = [format 0x%08x $maskedval]" set writeval(0) [expr {$maskedval | $value}] #echo " $addr <= [format 0x%08x $writeval(0)] ([format 0x%08x $curval]: [format 0x%08x $mask]/[format 0x%08x $value])" array2mem writeval 32 $addr 1 } proc xilinx_ps7_init { } { poll off reset init reset halt targets zc706.cpu.0 sleep 100 halt ps7_debug ps7_init ps7_post_config poll on } }}} This file will allow you to run the TCL script from the Xilinx SDK using OpenOCD. There are some instances around some parts of the initialisation that may not work. Uncomment the `echo` lines, location the writes that fail and comment those out from your PS7 init TCL script. The main purpose of this initialisation process is to get working clocks and DDR memory so you can load your code and run it. == First Stage Boot Loader Initialisation == A second approach to initialisation is to use the FSBL. The FSBL contains the C version of the PS7 initialisation produced by the SDK. The FSBL should detect the mode is JTAG and place the ARM code in a state where JTAG access is enabled. To do this you create an OpenOCD TCL script that loads the FSBL as an ELF file into the OCM and run it, pause for a small amount of time then halt the ARM code. At this point in time the Zynq will be initialised and you can download your application into DDR RAM. == Xilinx Zynq-7000 Configuration File == The following is a target configuration for the Xilinx Zynq-7000. Copy this to a file called `zynq-7000.cfg`: {{{ # # Xilinx Zynq 7000 SoC # # Chris Johns # # Setup # ----- # # Create a user configuration following the "Configuration Basics" in the user # documentation. In the file have: # # source [find interface/ftdi/flyswatter2.cfg] # source [find board/zynq-zc706-eval.cfg] # adapter_khz 2000 # init # if { [info exists CHIPNAME] } { global _CHIPNAME set _CHIPNAME $CHIPNAME } else { global _CHIPNAME set _CHIPNAME zc706 } if { [info exists ENDIAN] } { set _ENDIAN $ENDIAN } else { # this defaults to a bigendian set _ENDIAN little } if { [info exists SMP] } { global _SMP set _SMP 1 } else { global _SMP set _SMP 0 } # # PL Tap. # # ZC706 devices: # 0x03731093 - Eval board 1.1 # 0x23731093 - ?? # # Set in your configuration file or board specific file. # if { [info exists PL_TAPID] } { set _PL_TAPID $PL_TAPID } else { set _PL_TAPID 0x03731093 } jtag newtap $_CHIPNAME tap -irlen 6 -ircapture 0x001 -irmask 0x003 \ -expected-id $_PL_TAPID # # CoreSight Debug Access Port # if { [info exists DAPTAPID] } { set _DAP_TAPID $DAP_TAPID } else { set _DAP_TAPID 0x4ba00477 } jtag newtap $_CHIPNAME dap -irlen 4 -ircapture 0x01 -irmask 0x03 \ -expected-id $_DAP_TAPID # # GDB target: Cortex-A9, using DAP, configuring only one core # Base addresses of cores: # core 0 - 0xF8890000 # core 1 - 0xF8892000 # # Read from the ROM table with the patch to read the nested table. # set _TARGETNAME_0 $_CHIPNAME.cpu.0 set _TARGETNAME_1 $_CHIPNAME.cpu.1 target create $_TARGETNAME_0 cortex_a -coreid 0 \ -endian $_ENDIAN \ -chain-position $_CHIPNAME.dap \ -dbgbase 0x80090000 if { $_SMP } { echo "Zynq CPU1." target create $_TARGETNAME_1 cortex_a -coreid 1 \ -endian $_ENDIAN \ -chain-position $_CHIPNAME.dap \ -dbgbase 0x80092000 target smp $_TARGETNAME_0 $_TARGETNAME_1 } # # Hack to get the registers into a stable state when first booting a zynq in # JTAG mode. If r11 is pointing to an invalid address and you use gdb to set a # register the write will fail because gdb attempts to scan or unwind the # current frame and the bad address seems to lock the bus up. This code puts # the registers into the OCM and hopefull safe. # proc zynq_clear_registers { target } { echo "Zynq-7000 Series setup: $target" set _OCM_END 0x0003FFF0 mww phys 0xF8007000 0x4E00E07F reg r0 0 reg r1 0 reg r2 0 reg r3 0 reg r4 0 reg r5 0 reg r6 0 reg r7 0 reg r8 0 reg r9 0 reg r10 0 reg r11 $_OCM_END reg sp_svc $_OCM_END reg lr_svc $_OCM_END reg sp_abt $_OCM_END reg lr_abt $_OCM_END reg sp_und $_OCM_END reg lr_und $_OCM_END } proc zynq_disable_mmu_and_caches { target } { # arm mcr pX op1 CRn CRm op2 value echo "Disable MMU and caches" # Invalidate caches catch { $target arm mcr 15 0 7 5 0 0 $target arm mcr 15 0 7 7 0 0 # Invalidate all TLBs $target arm mcr 15 0 8 5 0 0 $target arm mcr 15 0 8 6 0 0 $target arm mcr 15 0 8 7 0 0 $target arm mcr 15 4 8 3 0 0 $target arm mcr 15 4 8 7 0 0 set cp [$target arm mrc 15 0 1 0 0] echo "SCTRL => [format 0x%x $cp]" set mask [expr 1 << 29 | 1 << 12 | 1 << 11 | 1 << 2 | 1 << 1 | 1 << 0] set cp [expr ($cp & ~$mask)] $target arm mcr 15 0 1 0 0 $cp echo "SCTRL <= [format 0x%x $cp]" } } proc zynq_boot_ocm_setup { } { # # Enable the OCM # echo "Zynq Boot OCM setup" catch { mww phys 0xF8000008 0xDF0D mww phys 0xF8000238 0 mww phys 0xF8000910 0xC } } proc zynq_restart { wait } { global _SMP global _TARGETNAME_0 global _TARGETNAME_1 set target0 $_TARGETNAME_0 set target1 $_TARGETNAME_1 echo "Zynq reset, resetting the board ... " poll off # # Issue the reset via the SLCR # catch { mww phys 0xF8000008 0xDF0D mww phys 0xF8000200 1 } echo "Zynq reset waiting for $wait msecs ... " sleep $wait # # Reconnect the DAP etc due to the reset. # $target0 cortex_a dbginit $target0 arm core_state arm if { $_SMP } { $target1 arm core_state arm $target1 cortex_a dbginit cortex_a smp_off } poll on # # We can now halt the core. # if { $_SMP } { targets $target1 halt } targets $target0 halt } proc zynq_zc706_gdb_attach { target } { catch { halt } } }}} == Custom Board Configuration == Create a directory somewhere on your host and create a `target` directory and place the `zynq-7000.cfg` file in it. Create a board configuration file. In this example we will create `zynq-zc706-eval.cfg` with the contents of: {{{ # # Xilinx Zynq ZC706 Evaluation Board # # Chris Johns # set PL_TAPID 0x23731093 source [find target/zynq-7000.cfg] source [find xilinx-tcl.cfg] # # Configure the reset. # reset_config srst_only adapter_nsrst_assert_width 250 adapter_nsrst_delay 400 }}} Finally I have more than one Flyswatter2 connected so I create a configuration file for each pod. The `zynq-zc706-1.cfg` is: {{{ source [find interface/ftdi/flyswatter2.cfg] source [find zynq-zc706-eval.cfg] ftdi_serial FS01 # Set the speed adapter_khz 10000 init }}} == Running OpenOCD == Start OpenOCD using: {{{ $ openocd -f ps7_init.tcl -f zynq-zc706-1.cfg -c reset }}} The PS7 TCL file is passed on the command line so you can vary the initialisation for specific boards as you need by changing how to start OpenOCD. == GDB Configuration == This configuration allow you to commit into your repository a standard configuration placing the host specific configuration in your home directory. Create a `$HOME/.gdbinit` file and place in it: {{{ def zynq-connect target remote :3334 end def zynq-fsbl-restart mon xilinx_ps7_init end def zynq-restart mon xilinx_ps7_init mon load_image /my/path/fsbl.elf 0x00000000 elf mon resume 0 mon sleep 2000 mon halt end }}} To debug an FSBL create a file called `zynq-gdbinit` and have your build system copy it to a suitable place in your build tree calling it `.gdbinit`. It should contain: {{{ # # Zynq FSBL Support. # zynq-connect zynq-fsbl-restart load b _exit }}} For an RTEMS application create another `zynq-gdbinit` file and also have your build system copy it to your build tree calling it `.gdbinit` and place in it: {{{ # # Zynq debug start up. # zynq-connect zynq-restart load b _exit b bsp_reset tb main c }}} == Issues == 1. The latest OpenOCD may have problems when the L2 cache is enabled. This is being looked into as time permits. 1. SMP support is not well tested.