I received a bricked Saber Lite board as a gift. Before I could do anything useful with it, I had to get it to boot.
I’ve always been attracted to new bright shiny things. And there’s nothing like a fresh technical challenge to get me fired up. In this instance, a colleague at the office handed me a Boundary Devices Saber Lite demo board to play with – the only problem is, it was completely bricked. It would not boot at all. When I plugged it in, the 5V DC LED would light up, but nothing else happened. No output came from the serial port. Even the reset button would not work. It seemed like it was dead! My undertaking was to see if I could get it to boot, and then do some interesting things with it – using SourcePoint to debug source/symbols is one thing to try. We also have some JTAG-based ScanWorks test and programming tools that work with the i.MX6 CPU on this board. The fun part about this is that I could use the same tools to test and debug the board as well as play with it. And at the same time learning more about embedded development, U-boot, Arm firmware, and other technical topics. So, I got started right away!
The Saber Lite board is an older kit. I think that it dates back to 2012 or so. The documentation on it seems to be not as thorough as what I’ve seen, for example, on the MinnowBoard. And, to make matters worse, it goes by several names: Saber Lite, Sabre Lite, BD-SL-i.MX6 (this is the Boundary Devices version of the board), etc.; and it is part of the NXP Nitrogen6 family of boards. I bring this up because Googling information on other peoples’ experiences with the board is quite difficult. Google does a good job of helping me find what I’m looking for, but I could be missing some key material by not putting in the right search terms. That’s what happens when the marketing branding police run amok.
But, be that as it may. It’s a pretty cool design nonetheless. It comes equipped with a four-core NXP i.MX6Quad processor, 1GB DDR3, 2MB SPI NOR flash, and Ethernet, USB, SATA and HDMI. Here’s what it looks like, top view:
And the block diagram is below:
Given that the board is bricked and will not boot or output anything to the console, I had no idea what might be wrong. There could be a board hardware problem; maybe the bootROM in the CPU is corrupted; maybe the SPI flash somehow got blown; maybe the serial port is defective and won’t output anything; it could be anything. So, I decided to go through a methodical board bring up approach, to isolate the fault, and get the board up ASAP so I could play with it.
So, the first thing I did was to connect our SourcePoint JTAG-based debugger, and halt the i.MX6 CPU as it was sitting there powered up. I saw the following:
I can see that the i.MX6 CPU is operational. It is fetching instructions, and in a tight loop near the reset vector, not too far from the x’00000000’ bootROM start address.
I single-stepped the code to see what it was doing. In fact, the CPU is in a tight loop that I’ve reconstructed below:
00000FB8 2000 MOVS R0,#00
00000FBA f8d1 1144 LDR.W R1,[R1,#144]
00000FBE 0649 LSLS R1,R1,#19
00000FC0 d500 BPL 00000fc4
00000FC4 4770 BX LR
00000CFC 2801 CMP R0,#01
00000CFE d100 BNE 00000d02
00000D02 4620 MOV R0,R4
00000D04 bd10 POP {R4,PC}
0000270E 2801 CMP R0,#01
00002710 d1fb BNE 0000270a
0000270A f7fe faf3 BL 00000cf4
00000CF4 b510 PUSH {R4,LR}
00000CF6 2400 MOVS R4,#00
00000CF8 f000 f95d BL 00000fb6
00000FB6 49ae LDR R1,00001270 (02184000)
The last LDR instruction resumes the loop back at address 00000fb8.
Now, I have a workingman’s knowledge of x86b assembler, but Arm assembler is still somewhat new to me. I think that I’m somewhere in the on-chip ROM, based on the reference from https://community.nxp.com/thread/328204:
I think the semi-official method on the i.MX6 series is to force the device into serial bootloader mode (BOOT_MODE0 == 1, BOOT_MODE1 == 0), leaving the USB port unconnected. When halt finally takes effect, the PC will be in the on-chip ROM waiting for a USB connection.
So, I’m guessing here, but I think that’s what it’s doing. In any event, I’m reassured that at least the CPU is operational.
I next decide to try to load u-boot on the platform. By hooking up to the serial port with PuTTY, I’ll be able to see what’s going on; maybe the board is working fine, but the SPI flash contains nothing useful.
Rather than use one of the pre-fabricated u-boot images, I wanted to build it myself. Good instructions are here, but they are incomplete. Here are the steps I used on my Linux (Ubuntu 16.04 LTS) machine:
$ sudo apt-get install gcc-arm-linux-gnueabihf bison flex
$ git clone https://github.com/boundarydevices/u-boot-imx6 \
-b boundary-v2018.07
$ cd u-boot-imx6
~/u-boot-imx6$ sudo apt-get install crossbuild-essential-armhf
~/u-boot-imx6$ export ARCH=arm
~/u-boot-imx6$ export CROSS_COMPILE=arm-linux-gnueabihf-
~/u-boot-imx6$ make nitrogen6q_defconfig
~/u-boot-imx6$ make -j2
That filled the u-boot-imx6 directory with the needed binaries:
I did some homework and realized that the u-boot.imx file is the key one to use.
Here is where it got a little tricky. There are several ways to u-boot the Saber Lite:
- From the SPI flash.
- From the SD card.
- From the USB OTG port.
Obviously, #1 isn’t working, otherwise the board would be booting already. So, I decided to try #2. But, the documentation is a little fuzzy here (at least, I thought it was). The Saber Lite has a slot for a full-size SD card on the bottom of the board, but there’s also a MicroSD slot that has the label “SD4/BOOT” next to it. I decided to use the latter (probably a good choice!), and burned the image onto a MicroSD card using:
$ sudo dd if=/home/alan/u-boot-imx6/u-boot.imx of=dev/sdf1 bs=1k seek=1 conv=fsync
Note: I had to use the Linux “lsblk” command to determine which “sdx” device (in this case sdf1) the USB port to write to – being very careful of course! Also, I placed the MicroSD card within a MicroSD Adapter card, and plugged that in turn to an SD-to-USB adapter so I could write to the card.
Alas, when I tried to boot from the MicroSD card, it didn’t work! Nothing came out of the serial console.
I’m probably doing something wrong. For future reference, the DIP switch settings which determine “boot mode” are a little ambiguous. If you look carefully at the board, you see two tiny switches:
And the settings are:
00 FUSES
01 USB OTG
10 INTERNAL
I’m not clear if ‘00’ or ‘10’ cause the board to boot from the MicroSD card. I tried them both – or, at least I think I did, with no luck. I probably did something wrong – perhaps the image did not burn properly – I’ll be trying this again later.
In the meantime, I decided to try #3 above. I set the switches to 01. This boots from the USB OTG port – a little MicroUSB jack on the side of the board.
On my Linux machine, I entered these commands:
~$ git clone git://github.com/boundarydevices/imx_usb_loader
~$ cd imx_usb_loader/
~/imx_usb_loader$ make
And the make was successful. The imx_usb executable was created, which is used to load the u-boot image over the USB OTG interface into RAM.
I then copied the u-boot.imx image file that I built earlier into the imx_usb_loader directory, and then, with my PC USB cabled up to the MicroUSB OTG port, typed:
~/imx_usb_loader$ sudo ./imx_usb u-boot.imx
And that worked too! Here’s the output:
And, simultaneously, PuTTY displayed the u-boot being loaded into RAM (actually On-Chip Memory (OCM) I think) and executing:
It works! This made me very happy.
Next time, I will update the SPI flash, if possible, so I don't have to load in the image over the USB OTG connection every time.
Want to learn more about u-boot and how to eliminate its requirement for board bring-up? Larry Osborn’s blog Using u-boot as a production test strategy – Really? is a good read.