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, arm, and linux - embedded development
Writeups and help for various development topics. Author has experience with Linux, Board Bringup, U-Boot, OpenEmbedded, Yocto, OpenWRT, Android, Ubuntu, Git, Perforce, Clearcase, Subversion, imx6 solo, MDM9615, Tegra 2, OMAP4430 (and others), various embedded platforms, and just about anything else related to embedded software development.
Friday, July 11, 2014
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.
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.
Subscribe to:
Posts (Atom)