FAQ: How do I re-use projects built in VDSP in a GCC Bare metal environment?

Document created by PrasanthR Employee on Oct 28, 2011Last modified by AndyR on Feb 14, 2012
Version 5Show Document
  • View in full screen mode

Q.

How do I re-use projects built in VDSP in a GCC Bare metal environment?

 

--------------------------------------------------------------------------------------

A.

The ELF output format that Visual DSP++ toolchain produces is not compatible with the GNU toolchain output format. Therefore one cannot reuse executables or libraries built using one toolchain, with the other, unless the software is re built from scratch.

 

There are more examples and libraries built using the VDSP toolchain, compared to the GCC bare metal toolchain. This includes several peripheral device drivers and the various system services. Often one has to re-build these libraries or write from scratch, in order to use with GCC.

 

Quick answer:

 

Generate opcodes of the application, and use it directly from SDRAM.

 

Detailed answer:

 

Loader files or LDR files are specific formats supported by the on-chip BOOTROM in Blackfin, for booting standalone applications, often from a non-volatile boot source such as the parallel flash. The advantage of this format is that both VDSP & GCC toolchain produces the end LDR file in exactly the same format required by BOOTROM. If a user has an LDR file built via VDSP, it can be feasibly used in a GCC environment, by making use of direct code execution method & the BOOTROM callable APIs.

 

Boot source can either be a parallel flash or SDRAM. In case of parallel flash, user must create an LDR file using the splitter tool and burn it before actually using it in application. However, code execution from flash is considerably slower.  When the boot source is an SDRAM, execution can become much faster. If the boot stream is residing in SDRAM, there is no real LDR programming required, other than including the file in the application. For creating the LDR image for direct execution, one must use the Splitter tool in Visual DSP++. This tool though it generates an LDR file, the file comprises of the opcodes of the particular program.

 

Now, since we can generate the opcodes, we actually don’t need the BOOTROM or SDRAM boot either. We could just perform a jump to the specific location.

 

BF527 is used as an example for the below for illustration. User needs to decide on the memory management carefully between the calling application, bootrom (in case used) and the called application. Additionally, the registers must be properly stored and re-loaded by the calling application. The explanations here are only intended for illustrating the concept and need not be the best method in all cases; users are expected to read the Visual DSP++ Loader Manual, as well as the System Reset and Booting chapter in Hardware Reference Manual for in depth information. Throught the discussion we are assuming that the calling application is the one built with GCC toolchain, and the called application is the one pre-built using VDSP.

 

Preparing the application:

 

1. If running from SDRAM directly without involving BOOTROM:

 

Ensure that all code is mapped to SDRAM, Memory segment should be ROM type.

 

MEM_SDRAM0 {  TYPE(ROM) WIDTH(8) START(0x00000010) END(0x0010FFFF) }

 

 

2. If running from SDRAM via BOOTOM, Include a first boot block like this:

 

.section bootblock;

.global _firstblock;

.byte_firstblock[16]=  0x06,0xD0,0x5B,0xAD,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00;

 

Changes in ldf:

 

RESOLVE(_firstblock,0x00000010)

RESOLVE(_main,0x00000020)

 

Ensure that all code is mapped to SDRAM& Memory segment should be in ROM type.

 

MEM_SDRAM0 { TYPE(ROM) WIDTH(8) START(0x00000010) END(0x0010FFFF) }

 

 

.

3. If running from Flash, Include a first boot block like this:

 

.section bootblock;

.global _firstblock;

.var _firstblock[4] = 0xAD7BD006,0x20000020, 0x00000010, 0x00000010;

 

Changes in ldf:

 

RESOLVE(_firstblock,0x20000000)

RESOLVE(_main,0x20000020)

 

Ensure that all code is mapped to Flash-ROM type (Async space). Burn this LDR using Flash programming tools.

 

 

Called Application - exit:

 

Since we are actually intending a return from the called application, it could just hardcode an address and return like this:

 

 

                asm("P0.L = LO(0xFFA04000);");           

                asm("P0.H = HI(0xFFA04000);");                           

                asm("JUMP (P0);");

 

Note that it is the responsibility of the called application to terminate Interrupts, DMA or anything with consequence to the calling application.

 

Loader properties:

 

 

The Loader exe (elf-loader) can have the following options to generate the splitter output:

 

‘elfloader.exe" .\Debug\LED_BLink_BF527_EZ-KIT.dxe -romsplitter -f ASCII -Width 16 -maskaddr 29 -si-revision 0.2 -proc ADSP-BF527

 

Use Project Options – Splitter to define the correct properties. When using Flash boot, the output can be in intel hex and when using the SDRAM, the output can be in ASCII format.

 

 

When using ASCII format, the generated file contains a sequence such as this:

00000010

000000DE

00010101

00000000

06

D0

5B

AD

  ...

 

We only need to take the byte data stream, as the initial data is meant for post processing tools. So one can delete the following:

 

 

00000010 /* 32-bit logical address field */

000000DE /* 32-bit logical length field */

00010101 /* 32-bit control word: 2x address multiply 02 bytes logical width, 01 byte physical width */

00000000 /* reserved */

 

 

Use -meminit with basiccrt.s file for initializing data memory sections specified in the LDF.

 

 

Using the pre-built LDRs with GCC:

 

 

Use additional linker settings to map using a new use Loader Script: -T ldscript.ld -Wl,-Map=Linker.map

 

When the opcode are used directly, define the locations such that the application starts exactly at the location intended when preparing the LDR:

MEM_SDRAM         : ORIGIN = 0x00000010, LENGTH = 1* 1024 * 1024

 

Since the loaded application is returning to a hardcoded instruction address, make sure that this location has the instructions for returning back to the calling application.

 

 

  MEM_L1_CODE_CACHE : ORIGIN = 0xFFA04000, LENGTH = 0xC000

  .textspecial           :

  {

    *(.textspecial .textspecial.*)

  } >MEM_L1_CODE_CACHE

 

 

 

When the LDR is already burnt to Flash, a direct jump to bootrom callable location 0xef000008 will do. When the LDR or application opcodes are residing in SDRAM, one would need to do:

 

 

__attribute__((__section__(".sdram.data")))

unsigned char ldr_dat [] =

{

                #include "led_blink.ldr"

 

};

 

 

SDRAM Controller must be programmed before using the SDRAM. Pre-boot inside bootrom can do this when using SDRAM-boot or user can also program like below:

 

                *pEBIU_SDRRC  = 0x0407;

                *pEBIU_SDBCTL = 0x25;

                *pEBIU_SDGCTL = 0x0091998D;

 

 

Below are some implementations for calling the application and returning from called application gracefully:

 

ret_from_boot() – function for returning from boot-rom to the . It should be hardcoded at 0xFFA04000.

perform_boot_bfrom() - function for jumping in to boot-rom for SDRAM boot.

perform_boot_direct() - function for jumping in to SDRAM location to execute code directly.

 

 

 

 

Sample house-keeping routines in GCC

unsigned int  sp_val;

 

 

__attribute__((__section__(".textspecial")))

void ret_from_boot()

{

asm(" P0.L = _sp_val;  \

            P0.H = _sp_val;  \

            R0 = [P0];      \

            SP = R0;        \

            FP = [SP++];    \

            RETS = [SP++];  \

            (R7:0, P5:0) = [ SP ++ ] \

            RETS = R0;");

}

 

 

__attribute__((__section__(".textspecial")))

void perform_boot_bfrom()

{

asm("       UNLINK;\

            LINK 64;\

            [ -- SP] = (R7:0, P5:0) \

            [ -- SP] = RETS; \

            [ -- SP] = FP;    \

            P0.L = (_sp_val);\

            P0.H = (_sp_val);\

            R0 = SP;\

            [P0] = R0;\

            R0.L = (0x6000);\

            R0.H = (0xFF90);\

            SP = R0;\

            FP = R0;\

            R0.H = 0x0000;\

            R0.L = 0x0010;\

            R1 = 0;\

            R2 = 0;\

            P0.H = 0xEF00;\

            P0.L = 0x0008;\

            JUMP (P0);");

}

 

 

__attribute__((__section__(".textspecial")))

void perform_boot_direct()

{

asm("       UNLINK;\

            LINK 64;\

            [ -- SP] = (R7:0, P5:0) \

            [ -- SP] = RETS; \

            [ -- SP] = FP;    \

            P0.L = (_sp_val);\

            P0.H = (_sp_val);\

            R0 = SP;\

            [P0] = R0;\

            R0.L = (0x6000);\

            R0.H = (0xFF90);\

            SP = R0;\

            FP = R0;\

            P0.H = 0x0000;\

            P0.L = 0x0020;\

            JUMP (P0);");

}

 

 

Using symbols from VDSP generated dxe:

 

To run code without symbols is tough, so the following might help in that case:

 

1. Generate symbol table via elfdump -n .symtab test.dxe

 

2. Reuse the VDSP generated symbols by directly defining values of these symbols in GCC LDSCRIPT.

 

3. Reuse the symbols (global data or functions) correctly as defined in the VDSP project.

 

4. Return from VDSP app via RTS.

 

One could extract just few required user-level symbols from the appropriate file mentioned in symbol table. Even then registers must be properly preserved.

 

 

Example code calling function label

perform_store();

 

load_vdsp_app(); // defined in ldscript - PROVIDE (_test_led_func = 0x20);

 

 

 

 

operations required before calling application
__attribute__((__section__(".textspecial")))
void perform_store()
{
asm("   UNLINK;\
       LINK 64;\
       [ -- SP] = (R7:0, P5:0) \
       [ -- SP] = RETS; \
       [ -- SP] = FP; \
       P0.L = (_sp_val);\
       P0.H = (_sp_val);\
       R0 = SP;\
       [P0] = R0;\
       R0.L = (0x6000);\
       R0.H = (0xFF90);\
       SP = R0;\
       FP = R0;\
       RTS;");\
}

 

 

Attachments

    Outcomes