1). Ensure that you are using the bfin-elf toolchain. That was built and linked against newlib which is intended for free-standing, embedded applications.
2). C programs should include <asm/mach-bf533/cdefBF532.h>
3). Assembly programs should include <asm/mach-bf533/defBF532.h>, and <asm/macros.h>. The latter is to handle LO( ) and HI( ) macros.
4). Note there are quite a few essential command-line switches for bfin-elf-gcc.
5). For simple applications, on the bfin-elf-ld command line, specify both –Ttext <start address> as well as the entry point –e <entry point>. Note that this will be using the linker’s standard rules for placing objects into the target. For more complex applications, a linker script file is more appropriate. See “Building Complex Free-Standing Applications.”
6). For making a serial bootable image, the resulting .bin file needs to be converted to a .ldr format. A utility program, bin2ldr as found in the u-boot/tools/bin2ldr directory is used to do this. However, before doing that, please read the details of “Making bin2ldr work for a BF532 target”, also “Using bin2ldr to create bootable .LDR images” below regarding alternate start address for the loaded image.
It is assumed you have installed and built u-boot as per uClinux docs. For the P1, it needs to be built for a board type “serial_stamp_config”. This step is necessary because that will allow you to use bin2ldr and you’ll have all set up properly.
bin2ldr takes your application’s .bin file, adds a bit of startup code (init_sdram.bin), and the execution transfer code (jump.bin) to build a proper .ldr image that can be flashed into serial PROM.
Take a look in bin2ldr.c and note that it takes the final load address for the application from APP_ENTRY which is set in bin2ldr.h. Be sure to set your preferred start address in bin2ldr.h. Next have a look where the Block 1, init block is created. Change the target address from 0xFFA00000 to 0xFFA08000 because thats the start address for L1 SRAM for a BF532. The same change is needed for Block 1, jump block. Also, note that #define RESVECT should set to 0x0000000 for the BF532.
Another bit of detail is that init_sdram.S should be replaced by the SOC-supplied startup code P1_startup.asm found on the SOC Machines CD\P1 Blackfin Boot Monitor directory. That will ensure SDRAM for the P1 is initialized per SOC Machines recommendations.
Ensure bin2ldr is fresh by doing a make clean, make in the u-boot/tools/bin2ldr directory. Next do ./runme.sh to build init_sdram.bin and jump.bin for a BF532 and your chosen startup location. Copy these two .bin files over to where you are building your embedded application code. These will be used when building .ldr images.
Copy your application .bin file over to a file named app.bin. Now you can run bin2ldr which will use app.bin, init_sdram.bin, and jump.bin to build the .ldr image. The result will be named app.ldr. Rename that to match your own application’s needs.
If you are using a serial downloader like SOC’s sfprog.exe, you will need an Intel Hex format for the .ldr file. That is created with the bin-elf-objcopy –I binary –O ihex utility. You can down load that with the sfprog utility that communicates with the AVR side.
Example: sfprog –cCOM2 –dhello.hex This assumes that at least P1 rev 1.1 hardware level and AVRmon085 is installed in the AVR processor .
The Blackfin bootloader boots from serial (SPI) Flash from files stored in .LDR format. LDR format is described in EE-240.
Blackfin bootloader boots from multi-EXE blocks. One reason is that the bootloader can load a bit of code, execute it, and then continue loading/executing blocks as it goes. This is how one can start up DRAM, setup wait states for external access, set up SPI speed, and initialize other peripherals before they are involved.
Typically, the first block is made to execute from L1
on-chip RAM. One objective is to ready SDRAM so that the next payload of bytes
can be loaded there. This bit of
startup code is included into the very first block of the .LDR file. The .LDR
has the FLAG field bit 3 set indicating that the ROM bootloader must first
execute the block of startup code before continuing the boot process. This
makes sense as here we initialize the processor, SPI, and SDRAM.
For an example of
such startup code, see Init_Sdram.S.
Note the use
of an RTS at the end.
This is how control
gets back to the Blackfin bootloader.
This bit of startup code is not build and
linked into our application proper. Dont make that part of the application
linking. It is built as part of UBOOT as an independent, .BIN file. You would
need to build a UBOOT for the appropriate target first.
When we build our application, as the final
step, we build the .LDR file for
execution by the Blackfin on-chip bootloader. The .LDR is built by the bin2ldr
utility found in UBOOOT/tools. This utility sticks the startup module into the
first block of the .LDR file and sets
the INIT flag in that boot block. After that sequence, bin2ldr
adds our application into the .LDR to
complete building the .LDR.
Incidentally, bin2ldr does not
know where our application is in memory It finds this out by adding another bit
of startup code into the .LDR which is simply a JUMP main() to where the
address of main is hard-coded See the bin2ldr.h for the
hard-coded address. So, we can build our application at say 0x1000 and make
sure the JUMP header takes us there. See jump.S/bin2ldr.h for a
code example.
Below is a detailed analysis of a .LDR file,
hello.hex.
Analysis
of a .LDR File
|
|
:10
0000 00 400080FF 04000000 1200 D6
00 00 00 00 00 45 target addr:FF800040 byte cnt:00000004
flag=0012 --- ignore block :10
0010 00 0000A0FF CC000000 0A00 66 01 67 01 40 05 C0 04
93 target addr:FFA00000 byte cnt:000000CC
flag=000A --- init, BF533 :100020
00 5001510152015301580159015A015B011C
---- startup code :100030
00 54015501560157015C015D015E015F01EC :100040
00 48E1C0FF08E1100080E10003008A2400BD :100050
00 08E1000048E1C0FF80E1001C32002400FC :100060
00 00972000420048E1C0FF08E10C00009525 :100070
00 2849FA1308E1040048E1C0FF80E10300C9 :100080
00 0097240008E1180A48E1C0FF80E1170842 :100090
00 0097240008E1140A48E1C0FF80E1130042 :1000A0
00 0097240008E1100A48E1C0FF40E19100F8 :1000B0
00 00E18D99009324001F011E011D011C0108 :1000C0
00 17011601150114011B011A01190118016C :1000D0
00 13011201110110018004000527012601FE :1000E0
00 10000000 400080FF 04000000 1200 68 07 BC target addr:FF800040 byte cnt:00000004
flag:0012 --- ignore block :10
00F0 00 00 00 0000A0FF 0C000000 0200 08 E1 00 10 5A JUMP main(). target addr:FFFA00000 byte cnt:000000C
flag:0002 --- non-init, BF533 :10
0100 00 48 E1 00 00 50 00 00 00 00100000 48070000 17 :10
0110 00 0280 target address: 00001000 byte cnt: 00000748 flag: 8002 --- last block, BF533 08E1082048E1E0FF00E1F21040E1
40 --- Application data bytes :100120000000009200E1F41040E1000000920092
13 :1001300000E1F01040E10000009200E1001140E1
18 :100140000000009200E1061140E10000009200E1
91 :100150000C1140E10000009200E1121140E10000AA :10016000009200E1181140E10000009200E11E1130 :10082000036082C608C082C60BC68B528B091A06AB :1008300082C608C0C04310000000436865636B2196 :100840000A0D000000000D0A48656C6C6F20426CB8 :0A08500061636B66696E0D0A00001B :00000001FF |
One of the features of the Blackfin is the
speed of the L1 code and data space. These spaces are typically used as cache
memory for speeding up certain parts of code. For our fee-standing
applications, we want to use these spaces for DSP code like, for example FIR
filters that need to executed as fast as possible.
For these kinds of applications, most of the
application code will be run from SDRAM, and a few routines will reside in
L1code RAM. L1code is located at 0xFFA08000 for the BF532 and SDRAM beginning
at 0x00000000. Problem is that when the HEX image is created from the binary
(elf format) file, this diverse spread in code represent a huge address range.
Using objdump, which is not smart enough to figure out that the
code is actually very sparse, will attempt to emit one huge monolithic output
file. It usually exits in error due to file size. So, some scheme needs to be
devised to get around this issue.
We know that bin2ldr creates
three blocks: pre-initialise
startup, jump, and user application. What we have to do is remove the
L1code-related code from the user application and merge that into the jump
block. It also is expected that L1code would contain addresses that need to be
resolved when building the user application.
The resolution is to use a loader script
file. An example is shown below. The loader script file ensure that the
different code and data segments gets placed in the correct target spaces.
Proceed to include DSP code in the body of
the jump.S routine, making sure all needed entry points are
declared global. The free-standing version of jump.bin is
build as usual. Then when we link the user application code, note that we
include jump.o to resolve
addresses related DSP routines residing jump.S. After the ld
step, objcopy is then instructed to exclude jump.o
from the object. This way, user applications only contains SDRAM code and there
is no problem converting that to a HEX file. During the bin2ldr
step to build the .LDR-format file, jump will be loaded in L1code
space and the DSP code would be resolvable and accessible from the user
application side.
Loader Script File |
|
/* Customized linker
script for P1 Pirana */ OUTPUT_FORMAT("elf32-bfin",
"elf32-bfin",
"elf32-bfin") OUTPUT_ARCH(bfin) ENTRY(start) SEARCH_DIR("/usr/local/bfin-elf/lib"); /* Do we need any of these
for elf? __DYNAMIC = 0; */ /* This is specific for
the P1 BF532 */ /* Note: For .LDR files
where code is loaded to L1, we must leave space for JUMP at the bottom of L1 */ MEMORY { l1code(rwx) : ORIGIN
= 0xffa08000, LENGTH = 0x00008000 l1data(rwx) : ORIGIN
= 0xff804000, LENGTH = 0x00004000 ram(rwx) : ORIGIN
= 0x00001000, LENGTH = 0x02000000 } SECTIONS { .l1code : {
jump.o (.text) } > l1code .text : { *(EXCLUDE_FILE (jump.o) .text)
*.(text) } > ram .rodata : { *.(rodata) } > ram .data : { *.(data) } > ram .bss : { *.(bss) *(COMMON) } > l1data /DISCARD/ : {
*.(comment) } } |
Example Build Script
|
|
# Note we use bfin-elf
toolchain. That was linked against newlib, which # is intended for
free-standing applications. # ----------------------------------------------------------------------- # Options, Includes, and
Library # -g ... debug info. # -Os ... Optimize for size, -O0 ... no
optimization. # -fno_builtin ... explicity define built-in functions
with leading "__". # -ffreestanding ... no stdlib or main() available. implies
-fno_builtin. # -nostdinc ... nosearching for C libraries. # -ffixed-P5 ... leave register P5 alone. Do not use it
for code generation. # -isystem ... search includes here after all -I have
been searched. # -pipe ... uses pipes instead of temp
directories. # -Dxxxxx ... Parameters passed into GCC/GASM/LD # -Wall ... Report all warnings # -Wstrict-prototypes ...
strict on prototypes # -c ... compile only, no linker #
-xassembler-with-cpp ... Assembly with
pre-processing, otherwise it will not honor #defines. # ... thought that .S extension meant that,
but apparently not. # -save-temps ... keep
intermediate files (.i .s) so we can peek at generated assembly. # # NB. Use the -s option on
ld command line to reduce size and extra stuff in .bin # # Want to see disassembled code, use: # bfin-elf-objdump -d delay.o .... example
for delay.o # # Want to see where code is loaded, use: # bfin-elf-objdump --debugging -d -h hello
> hello.lst # clean out rm -f *.o *.i rm hello hello.bin
jump.bin init_sdram.bin rm hello.hex rm hello.ldr rm hello.map # We need to set
application start address in bin2ldr.h gcc -o bin2ldr bin2ldr.c bfin-elf-gcc -O0 -ffixed-P5 -D__KERNEL__ -I/opt/uClinux/bfin-uclinux/bfin-uclinux/include
-fno-builtin -ffreestanding -nostdinc -D__BLACKFIN__ -mno-underscore
-DCONFIG_BLACKFIN -D__blackfin__
-D__ADSPLPBLACKFIN__ -Wall -Wstrict-prototypes -save-temps -c hello.c
Init.c ISR.c utils.c # Building auxilliary
assembly routines (delay etc ..) bfin-elf-gcc
-xassembler-with-cpp -O0 -ffixed-P5
-D__KERNEL__ -I/opt/uClinux/bfin-uclinux/bfin-uclinux/include
-D__KERNEL__ -fno-builtin
-ffreestanding -nostdinc -D__BLACKFIN__ -mno-underscore -DCONFIG_BLACKFIN
-D__blackfin__ -D__ADSPLPBLACKFIN__ -Wall -Wstrict-prototypes -c led.S crt0.S
exc_handler.S jump.S init_sdram.S # Build a free-standing
binary image of jump & init_sdram # for inclusion into the
.LDR file blocks bfin-uclinux-ld -o
init_sdram init_sdram.o -e 0x00000000 bfin-elf-objcopy -O binary
init_sdram init_sdram.bin bfin-uclinux-ld -o jump
jump.o -e 0x00000000 bfin-elf-objcopy -O binary
jump jump.bin #Link step. # Note that although
jump.o was made into a free-standing module # above, we link jump.o in
here too so that linker can resolve any # RAM calls to adresses in
L1CODE. The actual L1CODE in the final # binary will be stripped
out in the objcopy step below. This is # because bin2ldr will
place this l1code (jump.bin) section as a # seperate .DXE in the
.LDR # bfin-uclinux-ld -s -TP1loader.ld
\ -o hello -e start crt0.o hello.o led.o Init.o ISR.o utils.o
jump.o\ -L/opt/uClinux/bfin-uclinux/lib/gcc/bfin-uclinux/3.4.4 -lgcc \ -Map hello.map #Create BIN outputs # Note that L1CODE is
excluded here. BIN2LDR will inlcude L1CODE # as a separate .DXE when
building the .LDR bfin-elf-objcopy
--remove-section=.l1code -O binary hello hello.bin 2>/dev/null # Build the LDR file now cp hello.bin app.bin ./bin2ldr bfin-elf-objcopy -I binary
-O ihex app.ldr hello.hex cp app.ldr hello.ldr rm -f app.bin rm -f app.ldr # For diagnostic
information bfin-elf-objdump
--debugging -d -h hello > hello.lst |