Friday, July 11, 2014

Inexpensive JTAG on the imx6 solo: U-Boot Launch and Debug

Summary

Some JTAG products are prohibitively expensive.  This discussion reiterates work that has been done by others, and I'll do my best to reference that material here.  I will hopefully be able to go into a little more depth than those discussions, and answer the basic U-Boot debug questions (loading code, executing code, symbol remap for U-Boot).

 By the end of this tutorial, you should be able to

1) Create a custom .cfg file for your board
2) Launch and connect openocd to your board
3) Launch and open gdb for ARM
4) Connect GDB to the BusBlaster JTAG and the imx6 solo chip
5) Reset the imx6 solo through a GDB command
6) Load and execute a u-boot binary
7) Set and use a breakpoint to halt the processor just prior to relocation
7) Determine the relocation offset of the u-boot binary.
8) Load symbols at the relocated u-boot location, and get a back trace.

Assumptions And Purchase Locations

* Ubuntu 12.04
* imx6solo SOC
* Wandboard Solo PCB (link)  Cost ~$90
* BusBlaster v3 (link) Cost ~$34 !!!!!

I am planning on purchasing an imx6 quad-core board, in which case I would buy

* Wandboard Quad PCB Cost ~$150

I got the recommendation to use the BusBlaster V3 from this page (link). In general the wiki.wandboard.org (link) site was very helpful.  But there were some missing details in that setup description that I want to discuss.

BusBlaster

Plug in the BusBlaster to your machine.  lsusb should show the device as:

[~]$ lsusb | grep FT2232C
Bus 002 Device 054: ID 0403:6010 Future Technology Devices International, Ltd FT2232C Dual USB-UART/FIFO IC


Connecting Bus Blaster To Your Board

If you have a Wandboard, the Wandboard Wiki above describes the connections.  They have a graphic here (link).  Double check the below for my comments on Ground! On your own board, you need to make sure the signals are connected properly. 

VCC   (not connected between BusBlaster and Board)
TRST --> Connected
TMS --> Connected
TDI --> Connected
TDO --> Connected
SRST --> Connected
TCK --> Connected
GND --> Connect to BusBlaster Ground!

In general, when you have a communications interface, you need to ensure that your devices share a common ground.  I do not know why the graphic from the wandboard wiki doesn't connect the two grounds, but I think this is a bad practice, and could lead to a poor quality debug experience.  I have connected the two grounds on my setup.

OpenOCD
OpenOCD is the software package that talks directly to the BusBlaster.   However, the version distributed with Ubuntu is wrong, even with a very recent 13.04 distribution, Ubuntu is only distributing version 0.6.0.  To work properly with the BusBlaster, download the latest openocd code, configure, and install it.

Download latest version here: http://openocd.sourceforge.net

For example, you must at a minimum execute (on Ubuntu):

[...penocd-latest/openocd-0.8.0]$ ./configure; make; sudo make install

[~]$ openocd --version
Open On-Chip Debugger 0.8.0 (2014-06-13-20:01)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.sourceforge.net/doc/doxygen/bugs.html


OpenOCD Setup for Wandboard (and Your imx6solo Based PCB)

Once installed and the version is 0.8.0 or later, we need to do some wandboard specific configuration.  For reset, load, and execution to work, you must be very aware of the proper DDR3 and DCD table initialization for your PCB.  Since we're assuming this is a wandboard, I'll give the values, but also tell you where it came from, so you can derive your own.  This is the first part of the user-generated file: ~/bin/wandboard.cfg.

(updated 07/14/2014)

# Setup information for our busblaster and imx6 solo
set SJC_TAPID 0x1891b01d


# shipped busblaster cfg
source /usr/share/openocd/scripts/interface/ftdi/dp_busblaster.cfg

# imx config
source /usr/share/openocd/scripts/target/imx6.cfg


This performs the initialization of the busblaster, and the basic imx6 configuration.  These files come from openocd, and won't be correct unless OpenOCD 0.8.0 has been installed on your machine.  The next portion of the file is a little more unique. 

# No RTCK
# adapter_khz 27272
adapter_khz 1000

# see https://community.freescale.com/thread/308778

$_TARGETNAME configure -event reset-assert "ddr_init"

# this is needed if we're going to be running code we've
#   loaded manually into ram.
$_TARGETNAME configure -event reset-end    "clear_regs"

# This delay affects how soon after SRST we try to halt, so make it as
# small as possible. However, if it is too small we will fail the JTAG scan.
# Delay determined by experimentation
jtag_ntrst_delay 1000


The above does a few things.  but for the purposes of our discussion, it causes the reset-assert event to execute the ddr_init function we will create in ~/bin/wandboard.cfg.  It also causes the reset-end event to call  the clear_regs function we will also create.  

ddr_init comes from u-boot.  Specifically, it comes from the assembly of all the .cfg files that are used in u-boot, which together create the DCD file, which is in turn loaded by the imx6 solo's ROM PRIOR TO EXECUTION of the u-boot.  It is extremely important if we want to load our own code through JTAG and execute it.  For the wandboard these files are:

u-boot/board/boundary/nitrogen6x/nitrogen6s.cfg (which includes):
  #include "ddr-setup.cfg"
  #include "800mhz_2x128mx16.cfg"
  #include "clocks.cfg"


All of the above files are in u-boot/board/boundary/nitrogen6x/, and provide initialization for various registers for the wandboard.

clear_regs was determined by myself to be necessary, when it became clear that after reset the values of the ARM regs were improper (especially the cpsr).   I provide all of this below.

If you are doing this for your own PCB, YOU MUST DETERMINE the contents of all the .cfg files listed above.  Freescale supports this effort, but the most critical is ddr-setup.cfg, which is determined PER-BOARD.  If you have ANY line-length changes versus a standard board, you will have problems if this isn't tuned properly.

For the wandboard, we are fortunate that this is already determined by someone (to whom we are thankful).

Again, please look to the end of this file for the full ~/bin/wandboard.cfg file created.

Once you have all of this ready, you can make sure your version of openocd and configuration is working

(updated 07/14/2014)

[...penocd-latest/openocd-0.8.0]$ openocd -f ~/bin/wandboard.cfg
Open On-Chip Debugger 0.8.0 (2014-07-11-12:57)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.sourceforge.net/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'jtag'
Warn : imx6.sdma: nonstandard IR value
adapter speed: 1000 kHz
trst_and_srst srst_pulls_trst srst_gates_jtag trst_push_pull srst_open_drain connect_deassert_srst
adapter speed: 27272 kHz
jtag_ntrst_delay: 1000
ddr_init
Info : clock speed 27272 kHz
Info : JTAG tap: imx6.dap tap/device found: 0x4ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x4)
Info : TAP imx6.sdma does not have IDCODE
Info : JTAG tap: imx6.sjc tap/device found: 0x1891b01d (mfg: 0x00e, part: 0x891b, ver: 0x1)
Info : imx6.cpu.0: hardware has 6 breakpoints, 4 watchpoints
Info : number of cache level 1
Info : imx6.cpu.0 cluster 0 core 0 multi core


GDB for ARM

GDB needs to be recompiled for arm and available for the user to execute.  You may not want to not install this on the host machine, as you may not want to impact the local x86 based GDB code; that's left to the user.  I just compiled it and ran the install.  If you don't run the install you need to specify the data directory on the command line (not shown here).  Regardless you'll need to reference the gdb executable in your compile folder (I didn't figure out if it is copied to the system anywhere else).

Get GDB here, version 7.7 or later: http://www.gnu.org/software/gdb

configure; make; make install

Since you may already have gdb installed on your machine I recommend copying the gdb executable to /usr/bin/arm-elf-gdb.  This follows some other blog posts out there, and becomes nice when we eventually want to launch ddd (a gdb graphical front-end).

To launch

[...jects/wandboard/gdb-7.7/gdb]$ ./gdb

And to connect to the board  (after disconnecting any SD/MMC cards, and resetting the device)

(gdb) target remote localhost:3333
Remote debugging using localhost:3333
0x00001080 in ?? ()


If you see the above you are connected!  Here are some basic commands

# reset the board (also sets up registers, per our wandboard.cfg!)
(gdb) mon reset

# write 16 words and read them back
(gdb) mon imx6.cpu.0 mww 0x17800000 0x01 0x10
(gdb) mon imx6.cpu.0 mdw 0x17800000 0x10
0x17800000 00000001 00000001 00000001 00000001 ................
0x17800010 00000001 00000001 00000001 00000001 ................
0x17800020 00000001 00000001 00000001 00000001 ................
0x17800030 00000001 00000001 00000001 00000001 ................


# dump all the regs
(gdb) monitor reg
===== ARM registers
(0) r0 (/32): 0x020C4060 (dirty)
(1) r1 (/32): 0x00000000 (dirty)
...

(39) sp_mon (/32)
(40) lr_mon (/32)
(41) spsr_mon (/32)
(gdb)


# change and display one register
(gdb) monitor reg pc 0x17800002
pc (/32): 0x17800002
(gdb) mon reg pc
pc (/32): 0x17800002


If all of those work, then you are pretty much ready to go!  Now, let's talk about debugging u-boot.

U-Boot Debug With JTAG

One of the challenges with U-Boot is that it relocates itself very soon after excecution, so that it can live in the upper regions of RAM.  This allows the loading of the linux kernel to happen in the lower-regions of ram, without conflicting with the current execution of U-Boot.  From a debug perspective though, this can be a small challenge.

Let's first load the u-boot.  I am currently running the yocto baseline, compiled for the wandboard solo.  So my assumptions are based upon that.  If you haven't built the full freescale distribution yet, now would be a good time to do so.  You need some u-boot executable that's been built to work on the board.

(gdb) load ../../../wandboard/fsl-community-bsp/build/tmp/work/wandboard_solo-poky-linux-gnueabi/u-boot-fslc/v2014.01-r0/git/u-boot
Loading section .text, size 0x2ae88 lma 0x17800000
Loading section .rodata, size 0xa6e0 lma 0x1782ae88
Loading section .hash, size 0x2c lma 0x17835568
Loading section .data, size 0x821a lma 0x17835598
Loading section .got.plt, size 0xc lma 0x1783d7b4
Loading section .u_boot_list, size 0x7fc lma 0x1783d7c0
Loading section .rel.dyn, size 0x6bf0 lma 0x1783dfbc
Loading section .dynsym, size 0x60 lma 0x17844bac
Loading section .dynstr, size 0x1d lma 0x17844c0c
Loading section .dynamic, size 0x80 lma 0x17844c2c
Loading section .interp, size 0x11 lma 0x17844cac
Start address 0x17800000, load size 281780
Transfer rate: 18 KB/sec, 3659 bytes/write.


This loads the code, and it also updates the PC. 

(gdb) mon reg pc
pc (/32): 0x17800000


Now, you can run the executable.  After entering "cont" below, hit ^C to break into the SOC, to see what it is doing

(gdb) cont
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x2ff7d700 in ?? ()


At this point, the bt (back-trace) command fails to provide useful information.  How can we debug this?

(gdb) bt
#0  0x2ff7d700 in ?? ()
#1  0x2ff7d7c8 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)


To fix this issue, you must load the symbols for the u-boot, AT THE RELOCATED ADDRESS.  I will show this here, and then discuss how to find that address for your product and build.

(gdb) add-symbol-file ../../../wandboard/fsl-community-bsp/build/tmp/work/wandboard_solo-poky-linux-gnueabi/u-boot-fslc/v2014.01-r0/git/u-boot 0x2ff7c000
add symbol table from file "../../../wandboard/fsl-community-bsp/build/tmp/work/wandboard_solo-poky-linux-gnueabi/u-boot-fslc/v2014.01-r0/git/u-boot" at
    .text_addr = 0x2ff7c000
(y or n) y
Reading symbols from ../../../wandboard/fsl-community-bsp/build/tmp/work/wandboard_solo-poky-linux-gnueabi/u-boot-fslc/v2014.01-r0/git/u-boot...done.
(gdb) bt
#0  0x2ff7d700 in get_ticks () at timer.c:83
#1  0x2ff7d7c8 in __udelay (usec=<optimized out>) at timer.c:110
#2  0x2ff7c4ec in invalidate_dcache_all () at cache_v7.c:237
#3  0x00001000 in ?? ()


And there it is!  A back trace showing where we have halted in the u-boot, at the relocated location!  Perfect.  Now, let's see how we found that information (specifically, how we found the value 0x2ff7c000 for the add-symbol-file command above.

Determining U-BOOT Relocation Address

Unfortunately, it is non-trivial to determine the relocation address that u-boot is going to deposit your code for execution.

You can read, arch/arm/lib/relocate.S, which does the copy, and arch/arm/lib/crt0.S which calls relocate_code.  This shows that we get passed r0 in relocate_code, and crt0.S is calculating the value.

ENTRY(relocate_code)
    ldr    r1, =__image_copy_start    /* r1 <- SRC &__image_copy_start */
    subs    r4, r0, r1        /* r4 <- relocation offset */
    beq    relocate_done        /* skip relocation */
    ldr    r2, =__image_copy_end    /* r2 <- SRC &__image_copy_end */


Furthermore we can read include/generated/generic-asm-offsets.h to see how crt0.S gets the values and does the calculation, but frankly it is easier to break into the device and see the value.  Starting again (responses from gdb truncated):

(gdb) mon reset
adapter speed: 1000 kHz
...

(gdb) load /home/dixter1/Projects/wandboard/fsl-community-bsp/build/tmp/work/wandboard_solo-poky-linux-gnueabi/u-boot-fslc/v2014.01-r0/git/u-boot
Loading section .text, size 0x2ae88 lma 0x17800000
...

(gdb) file /home/dixter1/Projects/wandboard/fsl-community-bsp/build/tmp/work/wandboard_solo-poky-linux-gnueabi/u-boot-fslc/v2014.01-r0/git/u-boot
...

# answer yes to everything #

# Notice this is at the NON relocated location
(gdb) break relocate_code
Note: breakpoint 1 also set at pc 0x178019fc.

(gdb) cont
Continuing.

Breakpoint 1, relocate_code () at relocate.S:25
25        subs    r4, r0, r1        /* r4 <- relocation offset */
 

# dump the r0 value
(gdb) mon reg r0
r0 (/32): 0x2FF7C000


Done!  See the above example for how to use this value, with the add-symbol-file command.

MY FULL ~/bin/wandboard.cfg (updated 07/14/2014)

# Setup information for our busblaster and imx6 solo
set SJC_TAPID 0x1891b01d


# shipped busblaster cfg
source /usr/share/openocd/scripts/interface/ftdi/dp_busblaster.cfg

# imx config
source /usr/share/openocd/scripts/target/imx6.cfg

# SRST connects to POR_B
reset_config trst_and_srst srst_pulls_trst

# No RTCK
# adapter_khz 27272
adapter_khz 1000

# see https://community.freescale.com/thread/308778

$_TARGETNAME configure -event reset-assert "ddr_init"

# this is needed if we're going to be running code we've
#   loaded manually into ram.
$_TARGETNAME configure -event reset-end    "clear_regs"

# This delay affects how soon after SRST we try to halt, so make it as
# small as possible. However, if it is too small we will fail the JTAG scan.
# Delay determined by experimentation
jtag_ntrst_delay 1000

gdb_port 3333
telnet_port 3000

# unused
proc wdog_reset {} {
     mwh phys 0x20bc000 4
}

# just a test function
proc test {text} {
     puts "test: begin $text"
     puts "test: end $text"
}

# This program resets the regs to the address 0x17800000
#   making the incorrect assumption that the start of the code
#   is located there.
# It sets up the cpsr so we can run the code properly.
proc clear_regs {} {
     puts "clear_regs begin"
     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 0
     reg r12 0

     # assume our executable is at 0x17800000. otherwise the
     #   user must manually set the value after a reset.
     reg pc  0x17800000

     # shamelessly stolen from the samsung configuration
     #   this fixes problems executing loaded code.
     reg cpsr 0x1d3
     arm mcr 15 0 15 2 4 0x70000013

     puts "clear_regs end"
}

#
# This function is based upon the wandboard solo configuration from
#   u-boot-fslc/v2014.01-r0 git repository.  this results from the
#   assembly of the various configurations that come together out of
#   the board.cfg file's indication as to what the wandboard config
#   should be.  At the time of this writing the wandboard is built with
#   config file: board/boundary/nitrogen6x/nitrogen6s.cfg
#
# This is interesting of course, because it implies that the ddr3 config
#   was stolen from the nitrogen board, and likely requires a re-tuning.
#
proc ddr_init {} {
   puts "ddr_init: begin"

   halt
   dap apcsw 1

   # asm/arch-mx6/mx6-ddr.h
   set MX6_IOM_DRAM_DQM0   0x020e05ac
   set MX6_IOM_DRAM_DQM1   0x020e05b4
   set MX6_IOM_DRAM_DQM2   0x020e0528
   set MX6_IOM_DRAM_DQM3   0x020e0520
   set MX6_IOM_DRAM_DQM4   0x020e0514
   set MX6_IOM_DRAM_DQM5   0x020e0510
   set MX6_IOM_DRAM_DQM6   0x020e05bc
   set MX6_IOM_DRAM_DQM7   0x020e05c4

   set MX6_IOM_DRAM_CAS    0x020e056c
   set MX6_IOM_DRAM_RAS    0x020e0578
   set MX6_IOM_DRAM_RESET  0x020e057c
   set MX6_IOM_DRAM_SDCLK_0        0x020e0588
   set MX6_IOM_DRAM_SDCLK_1        0x020e0594
   set MX6_IOM_DRAM_SDBA2  0x020e058c
   set MX6_IOM_DRAM_SDCKE0 0x020e0590
   set MX6_IOM_DRAM_SDCKE1 0x020e0598
   set MX6_IOM_DRAM_SDODT0 0x020e059c
   set MX6_IOM_DRAM_SDODT1 0x020e05a0

   set MX6_IOM_DRAM_SDQS0  0x020e05a8
   set MX6_IOM_DRAM_SDQS1  0x020e05b0
   set MX6_IOM_DRAM_SDQS2  0x020e0524
   set MX6_IOM_DRAM_SDQS3  0x020e051c
   set MX6_IOM_DRAM_SDQS4  0x020e0518
   set MX6_IOM_DRAM_SDQS5  0x020e050c
   set MX6_IOM_DRAM_SDQS6  0x020e05b8
   set MX6_IOM_DRAM_SDQS7  0x020e05c0

   set MX6_IOM_GRP_B0DS    0x020e0784
   set MX6_IOM_GRP_B1DS    0x020e0788
   set MX6_IOM_GRP_B2DS    0x020e0794
   set MX6_IOM_GRP_B3DS    0x020e079c
   set MX6_IOM_GRP_B4DS    0x020e07a0
   set MX6_IOM_GRP_B5DS    0x020e07a4
   set MX6_IOM_GRP_B6DS    0x020e07a8
   set MX6_IOM_GRP_B7DS    0x020e0748
   set MX6_IOM_GRP_ADDDS   0x020e074c
   set MX6_IOM_DDRMODE_CTL 0x020e0750
   set MX6_IOM_GRP_DDRPKE  0x020e0758
   set MX6_IOM_GRP_DDRMODE 0x020e0774
   set MX6_IOM_GRP_CTLDS   0x020e078c
   set MX6_IOM_GRP_DDR_TYPE        0x020e0798

   set MX6_MMDC_P0_MDCTL   0x021b0000
   set MX6_MMDC_P0_MDPDC   0x021b0004
   set MX6_MMDC_P0_MDOTC   0x021b0008
   set MX6_MMDC_P0_MDCFG0  0x021b000c
   set MX6_MMDC_P0_MDCFG1  0x021b0010
   set MX6_MMDC_P0_MDCFG2  0x021b0014
   set MX6_MMDC_P0_MDMISC  0x021b0018
   set MX6_MMDC_P0_MDSCR   0x021b001c
   set MX6_MMDC_P0_MDREF   0x021b0020
   set MX6_MMDC_P0_MDRWD   0x021b002c
   set MX6_MMDC_P0_MDOR    0x021b0030
   set MX6_MMDC_P0_MDASP   0x021b0040
   set MX6_MMDC_P0_MAPSR   0x021b0404
   set MX6_MMDC_P0_MPZQHWCTRL      0x021b0800
   set MX6_MMDC_P0_MPWLDECTRL0     0x021b080c
   set MX6_MMDC_P0_MPWLDECTRL1     0x021b0810
   set MX6_MMDC_P0_MPODTCTRL       0x021b0818
   set MX6_MMDC_P0_MPRDDQBY0DL     0x021b081c
   set MX6_MMDC_P0_MPRDDQBY1DL     0x021b0820
   set MX6_MMDC_P0_MPRDDQBY2DL     0x021b0824
   set MX6_MMDC_P0_MPRDDQBY3DL     0x021b0828
   set MX6_MMDC_P0_MPDGCTRL0       0x021b083c
   set MX6_MMDC_P0_MPDGCTRL1       0x021b0840
   set MX6_MMDC_P0_MPRDDLCTL       0x021b0848
   set MX6_MMDC_P0_MPWRDLCTL       0x021b0850
   set MX6_MMDC_P0_MPMUR0  0x021b08b8

   set MX6_MMDC_P1_MDCTL   0x021b4000
   set MX6_MMDC_P1_MDPDC   0x021b4004
   set MX6_MMDC_P1_MDOTC   0x021b4008
   set MX6_MMDC_P1_MDCFG0  0x021b400c
   set MX6_MMDC_P1_MDCFG1  0x021b4010
   set MX6_MMDC_P1_MDCFG2  0x021b4014
   set MX6_MMDC_P1_MDMISC  0x021b4018
   set MX6_MMDC_P1_MDSCR   0x021b401c
   set MX6_MMDC_P1_MDREF   0x021b4020
   set MX6_MMDC_P1_MDRWD   0x021b402c
   set MX6_MMDC_P1_MDOR    0x021b4030
   set MX6_MMDC_P1_MDASP   0x021b4040
   set MX6_MMDC_P1_MAPSR   0x021b4404
   set MX6_MMDC_P1_MPZQHWCTRL      0x021b4800
   set MX6_MMDC_P1_MPWLDECTRL0     0x021b480c
   set MX6_MMDC_P1_MPWLDECTRL1     0x021b4810
   set MX6_MMDC_P1_MPODTCTRL       0x021b4818
   set MX6_MMDC_P1_MPRDDQBY0DL     0x021b481c
   set MX6_MMDC_P1_MPRDDQBY1DL     0x021b4820
   set MX6_MMDC_P1_MPRDDQBY2DL     0x021b4824
   set MX6_MMDC_P1_MPRDDQBY3DL     0x021b4828
   set MX6_MMDC_P1_MPDGCTRL0       0x021b483c
   set MX6_MMDC_P1_MPDGCTRL1       0x021b4840
   set MX6_MMDC_P1_MPRDDLCTL       0x021b4848
   set MX6_MMDC_P1_MPWRDLCTL       0x021b4850
   set MX6_MMDC_P1_MPMUR0  0x021b48b8

   # asm/arch-mx6/crm_regs.h
   set CCM_CCGR0           0x020C4068
   set CCM_CCGR1           0x020C406c
   set CCM_CCGR2           0x020C4070
   set CCM_CCGR3           0x020C4074
   set CCM_CCGR4           0x020C4078
   set CCM_CCGR5           0x020C407c
   set CCM_CCGR6           0x020C4080
   set CCM_CCOSR       0x020c4060

   # asm/arch-mx6/iomux.h
   set MX6_IOMUXC_GPR4        0x020e0010
   set MX6_IOMUXC_GPR6        0x020e0018
   set MX6_IOMUXC_GPR7        0x020e001c

   #ddr-setup.cfg
   mww phys $MX6_IOM_DRAM_SDQS0 0x00000030
   mww phys $MX6_IOM_DRAM_SDQS1 0x00000030
   mww phys $MX6_IOM_DRAM_SDQS2 0x00000030
   mww phys $MX6_IOM_DRAM_SDQS3 0x00000030
   mww phys $MX6_IOM_DRAM_SDQS4 0x00000030
   mww phys $MX6_IOM_DRAM_SDQS5 0x00000030
   mww phys $MX6_IOM_DRAM_SDQS6 0x00000030
   mww phys $MX6_IOM_DRAM_SDQS7 0x00000030
  
   mww phys $MX6_IOM_GRP_B0DS 0x00000030
   mww phys $MX6_IOM_GRP_B1DS 0x00000030
   mww phys $MX6_IOM_GRP_B2DS 0x00000030
   mww phys $MX6_IOM_GRP_B3DS 0x00000030
   mww phys $MX6_IOM_GRP_B4DS 0x00000030
   mww phys $MX6_IOM_GRP_B5DS 0x00000030
   mww phys $MX6_IOM_GRP_B6DS 0x00000030
   mww phys $MX6_IOM_GRP_B7DS 0x00000030
   mww phys $MX6_IOM_GRP_ADDDS 0x00000030
   # 40 Ohm drive strength for cs0/1sdba2cke0/1sdwe
   mww phys $MX6_IOM_GRP_CTLDS 0x00000030
  
   mww phys $MX6_IOM_DRAM_DQM0 0x00020030
   mww phys $MX6_IOM_DRAM_DQM1 0x00020030
   mww phys $MX6_IOM_DRAM_DQM2 0x00020030
   mww phys $MX6_IOM_DRAM_DQM3 0x00020030
   mww phys $MX6_IOM_DRAM_DQM4 0x00020030
   mww phys $MX6_IOM_DRAM_DQM5 0x00020030
   mww phys $MX6_IOM_DRAM_DQM6 0x00020030
   mww phys $MX6_IOM_DRAM_DQM7 0x00020030
  
   mww phys $MX6_IOM_DRAM_CAS 0x00020030
   mww phys $MX6_IOM_DRAM_RAS 0x00020030
   mww phys $MX6_IOM_DRAM_SDCLK_0 0x00020030
   mww phys $MX6_IOM_DRAM_SDCLK_1 0x00020030
  
   mww phys $MX6_IOM_DRAM_RESET 0x00020030
   mww phys $MX6_IOM_DRAM_SDCKE0 0x00003000
   mww phys $MX6_IOM_DRAM_SDCKE1 0x00003000
  
   mww phys $MX6_IOM_DRAM_SDODT0 0x00003030
   mww phys $MX6_IOM_DRAM_SDODT1 0x00003030
  
   # (differential input)
   mww phys $MX6_IOM_DDRMODE_CTL 0x00020000
   # (differential input)
   mww phys $MX6_IOM_GRP_DDRMODE 0x00020000
   # disable ddr pullups
   mww phys $MX6_IOM_GRP_DDRPKE 0x00000000
   mww phys $MX6_IOM_DRAM_SDBA2 0x00000000
   # 40 Ohm drive strength for cs0/1sdba2cke0/1sdwe
   mww phys $MX6_IOM_GRP_DDR_TYPE 0x000C0000
  
   # Read data DQ Byte0-3 delay
   mww phys $MX6_MMDC_P0_MPRDDQBY0DL 0x33333333
   mww phys $MX6_MMDC_P0_MPRDDQBY1DL 0x33333333
   mww phys $MX6_MMDC_P0_MPRDDQBY2DL 0x33333333
   mww phys $MX6_MMDC_P0_MPRDDQBY3DL 0x33333333
   mww phys $MX6_MMDC_P1_MPRDDQBY0DL 0x33333333
   mww phys $MX6_MMDC_P1_MPRDDQBY1DL 0x33333333
   mww phys $MX6_MMDC_P1_MPRDDQBY2DL 0x33333333
   mww phys $MX6_MMDC_P1_MPRDDQBY3DL 0x33333333
  
   #
   # MDMISC        mirroring       interleaved (row/bank/col)
   mww phys $MX6_MMDC_P0_MDMISC 0x00081740
  
   #
   # MDSCR con_req
   mww phys $MX6_MMDC_P0_MDSCR 0x00008000



   # 800mhz_2x128mx16.cfg
   mww phys $MX6_MMDC_P0_MDPDC 0x0002002D
   mww phys $MX6_MMDC_P0_MDCFG0 0x40435323
   mww phys $MX6_MMDC_P0_MDCFG1 0xB66E8D63
   mww phys $MX6_MMDC_P0_MDCFG2 0x01FF00DB
   mww phys $MX6_MMDC_P0_MDRWD 0x000026D2
   mww phys $MX6_MMDC_P0_MDOR 0x00431023
   mww phys $MX6_MMDC_P0_MDOTC 0x00333030
   mww phys $MX6_MMDC_P0_MDPDC 0x0002556D
   mww phys $MX6_MMDC_P0_MDASP 0x00000017
   mww phys $MX6_MMDC_P0_MDCTL 0x83190000
   mww phys $MX6_MMDC_P0_MDSCR 0x04008032
   mww phys $MX6_MMDC_P0_MDSCR 0x00008033
   mww phys $MX6_MMDC_P0_MDSCR 0x00048031
   mww phys $MX6_MMDC_P0_MDSCR 0x13208030
   mww phys $MX6_MMDC_P0_MDSCR 0x04008040
   mww phys $MX6_MMDC_P0_MPZQHWCTRL 0xA1390003
   mww phys $MX6_MMDC_P1_MPZQHWCTRL 0xA1390003
   mww phys $MX6_MMDC_P0_MDREF 0x00005800
   mww phys $MX6_MMDC_P0_MPODTCTRL 0x00022227
   mww phys $MX6_MMDC_P1_MPODTCTRL 0x00022227
   mww phys $MX6_MMDC_P0_MPDGCTRL0 0x42350231
   mww phys $MX6_MMDC_P1_MPDGCTRL0 0x42350231
   mww phys $MX6_MMDC_P0_MPDGCTRL1 0x021A0218
   mww phys $MX6_MMDC_P1_MPDGCTRL1 0x021A0218
   mww phys $MX6_MMDC_P0_MPRDDLCTL 0x4B4B4E49
   mww phys $MX6_MMDC_P1_MPRDDLCTL 0x4B4B4E49
   mww phys $MX6_MMDC_P0_MPWRDLCTL 0x3F3F3035
   mww phys $MX6_MMDC_P1_MPWRDLCTL 0x3F3F3035
   mww phys $MX6_MMDC_P0_MPWLDECTRL0 0x0040003C
   mww phys $MX6_MMDC_P0_MPWLDECTRL1 0x0032003E
   mww phys $MX6_MMDC_P1_MPWLDECTRL0 0x0040003C
   mww phys $MX6_MMDC_P1_MPWLDECTRL1 0x0032003E
   mww phys $MX6_MMDC_P0_MPMUR0 0x00000800
   mww phys $MX6_MMDC_P1_MPMUR0 0x00000800
   mww phys $MX6_MMDC_P0_MDSCR 0x00000000
   mww phys $MX6_MMDC_P0_MAPSR 0x00011006


   #clocks.cfg

   mww phys $CCM_CCGR0 0x00C03F3F
   mww phys $CCM_CCGR1 0x0030FC03
   mww phys $CCM_CCGR2 0x0FFFC000
   mww phys $CCM_CCGR3 0x3FF00000
   mww phys $CCM_CCGR4 0x00FFF300
   mww phys $CCM_CCGR5 0x0F0000C3
   mww phys $CCM_CCGR6 0x000003FF

   # enable AXI cache for VDOA/VPU/IPU */
   mww phys $MX6_IOMUXC_GPR4 0xF00000CF
   # set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7
   mww phys $MX6_IOMUXC_GPR6 0x007F007F
   mww phys $MX6_IOMUXC_GPR7 0x007F007F

   #
   # Setup CCM_CCOSR register as follows:
   #
   # cko1_en  = 1     --> CKO1 enabled
   # cko1_div = 111  --> divide by 8
   # cko1_sel = 1011 --> ahb_clk_root
   #
   # This sets CKO1 at ahb_clk_root/8 = 132/8 = 16.5 MHz
   #
   mww phys $CCM_CCOSR 0x000000fb

   puts "ddr_init: end"
}


imx6 board-bringup: the need for JTAG

Recently I was working for a client and demo'd a Lauterbach for the imx6 solo SOC.  As anyone in the industry knows, the Lauterbach devices are top-notch, best in class, and come with a GUI that works on Ubuntu (though it is somewhat obfuscated) as well as Windows.  These are excellent products and I'm a very big fan.  However, the cost per seat is on the order of $3000 US.

So how do we deal with this cost in a small business environment, while maintaining or even excelling at our effectiveness in development?

Development For a U-Boot Based Platform: The Need for JTAG

In general, we need JTAG for very limited circumstances when developing a Linux based product (Embedded products are different... that's for a later discussion).  The reason it is limited is that there are absolutely excellent tools to help you once the OS has been launched.  From system trace capabilities, to system profiling, to the basics like gdb and kdb, there is a lot available.

But before the OS has launched on the board, there's a hole.  And that debug hole is usually just after the uP has started to execute, but just before the serial port has been setup.  For the IMX6, the boot sequence is as follows.

  Reset --> ROM Execution --> uBoot.imx Load  ---> Exec uBoot.imx

Freescale provides tools to help with much of the above. For example, the uBoot.imx contains the DDR3 tuning (and clock setup, and other values) contained in the "DCD" table. These values are programmed into the board.  But what if they fail to load?  What if we fail to execute the u-Boot?

JTAG can be used to break into the micro processor, and examine registers to see if any of the values have taken effect.  One can then tweak the values, test them, attempt to load DDR3 memory, and more.

see u-boot/board/boundary/nitrogen6x/ddr-setup.cfg

What about after the first execution of the uBoot, but before the serial ports are properly setup?  If we crash in this region of time, there's no serial console for debug.  Certainly we can toggle LEDs and perform other operations, but JTAG can quickly fill in the gap without requiring the modification of source code.

There are additional concerns of course: after u-boot launches the kernel but before the linux kernel can start writing to a serial port.  There is a whole host of operations that take place during this time, and if there is a flaw, a crash prior to the first context switch, there is very limited debug capability available.

For these reasons, if you're working with a new board design, it is important to consider JTAG as a first-step to board development.

Ironically, in my most recent project I didn't have JTAG available during the most critical portions of board-bringup.  Solving the board-bringup issues without JTAG was an absolutely incredible experience.  But once done, I would rather have better tools the next time around.