One of the more painful parts of the FCS development has been getting the TI C6657 DSP to boot from a NOR flash device connected via SPI. Although it’s conceptually quite simple, there’s a lot of confusing and frequently just incorrect documentation out there on how to do it.
The EVMs for the C6657 and C6678 devices use a two-stage boot process, with the FPGA configuring the DSP’s bootmode pins to start an intermediate bootloader (IBL) from an I2C flash device at address 0x51. The IBL does some initialization and then determines which second-stage bootloader to run based on the DIP switches on the board.
In our design, we have both I2C and SPI flash devices, but the 256Kbit I2C device wouldn’t be able to store our whole application. Since we don’t need the flexibility of the EVM, which also supports Ethernet boot, PCI-E boot, and all kinds of other things, we decided to boot directly from the 256Mbit NOR flash connected via SPI.
There are three main tasks involved in this process: configuring the DSP bootmode pins to run the SPI code in the device’s internal ROM bootloader (RBL), creating a firmware image that can be read by the RBL, and writing that image to the flash.
The first task, configuring the bootmode pins, is a couple of documentation revisions away from being really straightforward:
2:0
determine the boot device, which is 6
(binary 110
) for SPI.12:3
determine the SPI configuration options,
with bits 6:3
being an index into a parameter table (basically an
offset to the start of the boot image, with the value here being multiplied by
128 to get the byte address of said boot image). If you’re using a single
firmware image, this can be set to 0
; the purpose of this field
is to allow multiple applications to be loaded into a single flash
device.8:7
determine the SPI chip select used. As of
the August 2012 revision of the DSP manual (SPRS814A), the description for
this field is “The chip select field value”. Knowing that the C6657 has only
two SPI chip selects, you might guess that a value of 01
would be
SPI CS0, and 10
would be SPI CS1. Alternatively, you might guess
that provision is being made for up to four CS lines, so 00
would
be CS0 and 01
would be CS1. Unfortunately, for no apparent
reason, these pins need to be 10
for CS0, and 01
for
CS1. Values 00
and 11
are both reserved. (According
to the TI forums, these values are being listed in a future version of the DSP
manual.)9
determines the address width; 0
means 16-bit addresses, and 1
means 24-bit addresses. Most large
NOR flash devices (we use the Micron N25Q256A) use 24-bit addresses, so set
this to 1
.10
determines whether 4 or 5-pin SPI is used.
This functionality is not really explained anywhere and from looking through
the C6657 RBL code it’s not immediately clear what it does. We set it to
0
, for 4-pin SPI.12:11
determine the SPI clock polarity and
phase. Again, you might think that these concepts are directly related to the
standard (CPOL, CPHA)
parameterization of SPI bus timing, and
therefore that this field selects a standard SPI mode number. That’s not the
case, as reading the field description in the DSP manual makes clear: mode
0
corresponds to a (CPOL, CPHA)
value of
(0, 1)
; modes 1
and 3
correspond to
unusual timing arrangements for the first SPICLK
cycle; mode
2
corresponds to (CPOL, CPHA) = (0, 0)
. For the
Micron N25Q256A, we selected mode 2
.The second task is what caused most of the pain. For some reason there
isn’t actually a tool that will take you directly from a CCS .out
file (ELF or COFF) to something that can be written to a flash device.
Instead, the process goes something like this. (You may see some variations on
the TI forums; I haven’t tested those but know that this one works.)
hex6x
tool supplied with the C66x
compiler/assembler/linker to convert the ELF/COFF application to a boot table
image in ASCII format. The hex6x
tool takes a .rmd
file with a structure like the following:
fcs.out
--ascii
--boot
-e=_c_int00
ROMS
{
SPI: org = 0x00000400, length = 0x00100000, memwidth = 32, romwidth = 32
}
Here, fcs.out
is the output file name; --ascii
means
ASCII hex output; --boot
means generate a boot table;
-e=_c_int00
means the entry point is _c_int00
(which
is automatically generated by the C66x compiler). The ROMS
block
identifies the output file structure, with one file generated per entry. The
org
, length
, memwidth
and
romwidth
parameters generally don’t need changing, although if
your binary is > 1MiB you’ll want to increase the value of
length
. The output file is specified on the command line, and in
our case it’s fcs.btbl
.bconvert64x -le INFILE OUTFILE
. This tool
is available in the MCSDK distribution, but we have our own version
here;
it compiles on any platform with a standard C compiler.b2i2c
which converts the
boot table to a series of 128-byte chunks containing a 16-bit length, a 16-bit
checksum, and 124 bytes of boot table data. Again, this is available in the
MCSDK, but their version is limited to 128KiB binaries. This can be changed by
updating the value of SIZE
on line 161;
ours
is set to 2MiB. Usage is just b2i2c INFILE OUTFILE
.\n
-separated list of 32-bit words in hexadecimal format (e.g.
0x12345678
), with a header indicating the length of the file:
1651 1 10000 1 LENGTH
, where LENGTH
is the number of
words. This is done using b2ccs
, which also has a
SIZE
definition which might need to be changed for large files.
romparse
, but unfortunately
it’s extremely buggy—it just crashed for me, but even when it works it writes
the wrong output address, so you need to hand-edit the resulting files, and it
doesn’t do PLL configuration correctly so all your SPI timing will be wrong.
Instead of that, I used a tool called bootbuild
, developed by one
of the members of the TI forums. Our version is available
here;
it’s essentially the same approach but generates output files more consistent
with what romparse
theoretically should. The tool takes a
configuration file; ours is below.
section {
boot_mode = 50
param_index = 0
options = 1
core_freq_mhz = 1000
exe_file = "fcs.ccs"
next_dev_addr_ext = 0x0
sw_pll_prediv = 0
sw_pll_mult = 19
sw_pll_postdiv = 1
sw_pll_flags = 1
addr_width = 24
n_pins = 4
csel = 2
mode = 2
c2t_delay = 0
bus_freq_mhz = 10
bus_freq_khz = 0
}
These options are the same as those we’re setting via the bootmode pins; the
only change to SPI device configuration is the clock frequency
(bus_freq_mhz
), which we increase to 10MHz after the PLLs are
configured. Output from this tool is written to i2crom.ccs
.
romparse
and our version of bootbuild
also set
the boot table itself to start at address 0x400
in flash, but
that’s not strictly required), we now just need to convert the “CCS” format
file to binary. Run ccs2bin -swap INFILE OUTFILE
, then write the
resulting binary to the flash device however you see fit.We have a makefile
which automates most of the process above (just build the .btbl
file in CCS and run make fcs.debug.bin
), but depending on how
frequently we run it, we may write a script to do everything in one go. The
actual manipulation of data formats is trivial, but spreading it out over half
a dozen different undocumented tools makes things seem much more complex.
The third task, writing a firmware image to the flash, can be done however
you like. We run these lines through the CPLD, so we could program it directly
from the CPU (running Linux), but for development we used the
norwriter
application in the DSP MCSDK:
mcsdk_2_01_01_04/tools/writer/nor/evmc6657
for those following
along at home. The process here is to compile and write the application to DSP
SRAM via a JTAG adapter, then open the CCS debugger’s memory view and copy the
file from your filesystem into the DDR3 address space, then hit “run” and wait
until it outputs a success message on the built-in console. The instructions
in that tool’s README.txt
are both correct and easy to
follow.