This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

RTOS/AM3358: Linking PRU object file

Part Number: AM3358


Tool/software: TI-RTOS

Hello all,

I have been working on a computer vision product that uses the BeagleBone Black, and I've been developing PRU code that grabs  frames and writes it to RAM. I have the PRU code working independently from ti-rtos and I am figuring out how to integrate the code into ti-rtos. I have got the HexPRU utility working correctly, as far as I can tell. I found the TI Linker file that shows how to accomplish the linking of the obj  files generated by the hexpru utility, but it uses the TI compiler/linker and the project I am creating uses GCC. I'm hoping it would be easier to refactor the linker file than it would be to rewrite the build system for the libraries that my project depends on. The lds file that I have going is posted below, its mostly the boiler plate linker file. 

MEMORY
{

    SRAM :     o = 0x402F0400,  l = 0x0000FC00  /* 64kB internal SRAM */
    L3OCMC0 :  o = 0x40300000,  l = 0x00010000  /* 64kB L3 OCMC SRAM */
    M3SHUMEM : o = 0x44D00000,  l = 0x00004000  /* 16kB M3 Shared Unified Code Space */
    M3SHDMEM : o = 0x44D80000,  l = 0x00002000  /* 8kB M3 Shared Data Memory */
    DDR0 :     o = 0x80000000,  l = 0x20000000  /* 256MB external DDR Bank 0 */
}

/* Linker script to place sections and symbol values. Should be used together
 * with other linker script that defines memory region DDR0.
 * It references following symbols, which must be defined in code:
 *   Entry : Entry of reset handler
 * 
 * It defines following symbols, which code can use without definition:
 *   __exidx_start
 *   __exidx_end
 *   __etext
 *   __data_start__
 *   __preinit_array_start
 *   __preinit_array_end
 *   __init_array_start
 *   __init_array_end
 *   __fini_array_start
 *   __fini_array_end
 *   __data_end__
 *   __bss_start__
 *   __bss_end__
 *   __end__
 *   end
 *   __HeapLimit
 *   __HeapBase        - To be compatible with Linaro's semihosting support 
 *   __StackLimit
 *   __StackTop
 *   __StackBase    - To be compatible with Linaro's semihosting support 
 *   __stack
 */
ENTRY(Entry)

SECTIONS
{
    .rsthand :
    {
        . = ALIGN(0x10000);
        KEEP(*(.isr_vector))
        *startup_ARMCA8.o (.text)
    } > DDR0
    
    . = ALIGN(4);
    .text :
    {
        *(.text*)

        KEEP(*(.init))
        KEEP(*(.fini))

        /* .ctors */
        *crtbegin.o(.ctors)
        *crtbegin?.o(.ctors)
        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
        *(SORT(.ctors.*))
        *(.ctors)

        /* .dtors */
         *crtbegin.o(.dtors)
         *crtbegin?.o(.dtors)
         *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
         *(SORT(.dtors.*))
         *(.dtors)

        *(.rodata*)

        KEEP(*(.eh_frame*))
    } > DDR0

    .ARM.extab : 
    {
        *(.ARM.extab* .gnu.linkonce.armextab.*)
    } > DDR0

    __exidx_start = .;
    .ARM.exidx :
    {
        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
    } > DDR0
    __exidx_end = .;

        
    .data :
    {
        . = ALIGN(4);
        __data_start__ = .;
        *(vtable)
        *(.data*)

        . = ALIGN(4);
        /* preinit data */
        PROVIDE_HIDDEN (__preinit_array_start = .);
        KEEP(*(.preinit_array))
        PROVIDE_HIDDEN (__preinit_array_end = .);

        . = ALIGN(4);
        /* init data */
        PROVIDE_HIDDEN (__init_array_start = .);
        KEEP(*(SORT(.init_array.*)))
        KEEP(*(.init_array))
        PROVIDE_HIDDEN (__init_array_end = .);


        . = ALIGN(4);
        /* finit data */
        PROVIDE_HIDDEN (__fini_array_start = .);
        KEEP(*(SORT(.fini_array.*)))
        KEEP(*(.fini_array))
        PROVIDE_HIDDEN (__fini_array_end = .);



        . = ALIGN(4);
        /* All data end */
        __data_end__ = .;

    } > DDR0

    .bss :
    {
        . = ALIGN(4);
        __bss_start__ = .;
        *(.bss*)
        *(COMMON)
        __bss_end__ = .;
    } > DDR0
    
    .heap (NOLOAD):
    {
        /* The line below can be used to FILL the memory with a known value and
         * debug any stack overruns. For this to work, the specifier (NOLOAD) above 
         * must be removed at the expense of an increase in the output binary size */
        FILL(0xDEADBEEF)
        . = ALIGN(4);
        __end__ = .;
        end = __end__;
        /* The line below created to be compatible with Linaro's semihosting support */
        __HeapBase = __end__; 
        *(.heap*)
        . = . + HEAPSIZE;
        __HeapLimit = .; 
    } > DDR0

	framegrabber_text :
	{
		PRU1_framegrabber.obj(.text);
	} > DDR0

	framegrabber_data :
	{
		PRU1_framegrabber.obj(.data);
	} > DDR0

	ramwriter_text :
	{
		PRU0_ramwriter.obj(.text);
	} > DDR0

	ramwriter_data :
	{
		PRU0_ramwriter.obj(.data);
	} > DDR0



    /* .stack section doesn't contain any symbols. It is only
     * used for linker to calculate size of stack sections, and assign
     * values to stack symbols later */
    .stack (NOLOAD):
    {
        /* The line below can be used to FILL the memory with a known value and
         * debug any stack overruns. For this to work, the specifier (NOLOAD) above 
         * must be removed at the expense of an increase in the output binary size */
        FILL(0xBAD0BAD0)
        . = ALIGN(4);
        __StackLimit = . ;
        *(.stack*)
        . = . + STACKSIZE;
        __StackTop = . ;
        /* The line below created to be compatible with Linaro's semihosting support */
        __StackBase = . ;
    } > DDR0
    PROVIDE(__stack = __StackTop);

}

I then reference the sections that contain the data and instruction files in a file that (I'm hoping) will contain the data and instruction ram for the PRUs. 

const unsigned char __attribute__ ((section ("framegrabber_text"))) framegrabber_inst [0x1000];
const unsigned char __attribute__ ((section ("framegrabber_data")))framegrabber_ram [0x1000];
const unsigned char __attribute__ ((section ("ramwriter_text")))ramwriter_inst [0x1000];
const unsigned char __attribute__ ((section ("ramwriter_data")))ramwriter_ram [0x1000];

At the end of all of it, I get the following error:

section framegrabber_text loaded at [80000000,80000fff] overlaps section .text loaded at [80000000,8001727b]

I assume that this is an error with the rest of the sections. Does anyone know how to fix this error? I've been able to paste together various fixes from previous E2E posts and other resources but I am newish to the GCC linker script, so I feel I'm a bit out of my league here. Any help is appreciated. Also, this is my first post here so if this is in the wrong place, let me know.

At 

  • The RTOS team have been notified. They will respond here.
  • Marc,

    If you just want to put framegrabber and ramwriter into DDR, the .text and .data entries your SECTIONS is sufficient, and there is no additional attributes for the framegrabber and ramwriter needed.

    Regards,
    Garrett
  • Garret,

    Thanks for your response. The framegrabber and ramwriter need to go into RAM, but they need to be able to be referenced by the C code via arrays, as they represent the DATARAM and IRAM sections for the PRU Code, and need to be loaded via the PRU API in TI-RTOS.

    Maybe I am missing an important step that's happening in the background but the example code for loading PRU code from the PRUDemo project in the pru-support-software package does it this way but with the TI toolchain, which I'd like to avoid using if possible. It uses a directive run_start which it doesn't look like is a thing in ld.

    For your convenience, here is the linker file that I'm referencing:

    -stack  0x1000                             /* SOFTWARE STACK SIZE           */
    -heap   0x2000                             /* HEAP AREA SIZE                */
    -e Entry
    /* Since we used 'Entry' as the entry-point symbol the compiler issues a    */
    
    /* warning (#10063-D: entry-point symbol other than "_c_int00" specified:   */
    /* "Entry"). The CCS Version (5.1.0.08000) stops building from command      */
    /* line when there is a warning. So this warning is suppressed with the     */
    /* below flag. */
    --diag_suppress=10063
    --retain=PRU_LED0_image.obj(*)
    --retain=PRU_LED1_image.obj(*)
    --retain=PRU_Switch_image.obj(*)
    --retain=PRU_Audio_image.obj(*)
    --retain=PRU_Hardware_UART_image.obj(*)
    --retain=PRU_HDQ_TempSensor0_image.obj(*)
    --retain=PRU_HDQ_TempSensor1_image.obj(*)
    
    
    
    /* SPECIFY THE SYSTEM MEMORY MAP */
    
    
    MEMORY
    {
            DDR_MEM        : org = 0x80000000  len = 0x8000000           /* RAM */
    }
    /* SPECIFY THE SECTIONS ALLOCATION INTO MEMORY */
    SECTIONS	
    {
        .text:Entry : load > 0x80000000
        .text    : load > DDR_MEM              /* CODE                          */
        .data    : load > DDR_MEM              /* INITIALIZED GLOBAL AND STATIC VARIABLES */
        .bss     : load > DDR_MEM              /* UNINITIALIZED OR ZERO INITIALIZED */
                                               /* GLOBAL & STATIC VARIABLES */
                        RUN_START(bss_start)
                        RUN_END(bss_end)
        .const   : load > DDR_MEM              /* GLOBAL CONSTANTS              */
        .cinit	 : load > DDR_MEM
        .stack   : load > DDR_MEM HIGH //0x87FFF000           /* SOFTWARE SYSTEM STACK         */
        LED0_text: {PRU_LED0_image.obj(.text)} load > DDR_MEM run_start(LED0_INST)
        LED0_data: {PRU_LED0_image.obj(.data)} load > DDR_MEM run_start(LED0_DATA)
        LED1_text: {PRU_LED1_image.obj(.text)} load > DDR_MEM run_start(LED1_INST)
        LED1_data: {PRU_LED1_image.obj(.data)} load > DDR_MEM run_start(LED1_DATA)
        SW_text: {PRU_Switch_image.obj(.text)} load > DDR_MEM run_start(SW_INST)
        SW_data: {PRU_Switch_image.obj(.data)} load > DDR_MEM run_start(SW_DATA)
        AUDIO_text: {PRU_Audio_image.obj(.text)} load > DDR_MEM run_start(AUDIO_INST)
    
        AUDIO_data: {PRU_Audio_image.obj(.data)} load > DDR_MEM run_start(AUDIO_DATA)
        HW_UART_text: {PRU_Hardware_UART_image.obj(.text)} load > DDR_MEM run_start(UART_INST)	
    	HW_UART_data: {PRU_Hardware_UART_image.obj(.data)} load > DDR_MEM run_start(UART_DATA)
    	
    	TEMPSENSOR0_text: {PRU_HDQ_TempSensor0_image.obj(.text)} load > DDR_MEM run_start(SLAVE_INST)
        TEMPSENSOR0_data: {PRU_HDQ_TempSensor0_image.obj(.data)} load > DDR_MEM run_start(SLAVE_DATA)
       	TEMPSENSOR1_text: {PRU_HDQ_TempSensor1_image.obj(.text)} load > DDR_MEM run_start(MASTER_INST)
        TEMPSENSOR1_data: {PRU_HDQ_TempSensor1_image.obj(.data)} load > DDR_MEM run_start(MASTER_DATA)
    }

    and here's where the arrays are in turn defined

    extern const unsigned char LED0_INST[0x1000];
    extern const unsigned char LED0_DATA[0x1000];
    extern const unsigned char LED1_INST[0x1000];
    extern const unsigned char LED1_DATA[0x1000];
    extern const unsigned char SW_INST[0x1000];
    extern const unsigned char SW_DATA[0x1000];
    extern const unsigned char AUDIO_INST[0x1000];
    extern const unsigned char AUDIO_DATA[0x1000];
    extern const unsigned char UART_INST[0x1000];
    extern const unsigned char UART_DATA[0x1000];
    extern const unsigned char SLAVE_INST[0x1000];
    extern const unsigned char SLAVE_DATA[0x1000];
    extern const unsigned char MASTER_INST[0x1000];
    extern const unsigned char MASTER_DATA[0x1000];

    Let me know if what I'm asking makes sense

    Thanks for your time,

    Marc

  • Marc,

    The link you referred to is for PRU loading with Linux remoteProc. For TI-RTOS integration, please refer to the PRUSS driver -
    processors.wiki.ti.com/.../Processor_SDK_RTOS_PRUSS

    And for the IRAM and Data RAM loading, you can use the APIs:
    PRUICSS_setPRUBuffer( );
    PRUICSS_pruWriteMemory( );
    see details in pdk_am335x_1_0_8/packages/ti/drv/pruss/docs/doxygen/html/pruicss__drv_8c.html

    The framegrabber and ramwriter can remain in DDR initially.

    Regards,
    Garrett
  • Garret,

    Thank you for your help. I have one last question. If my .text and .data sections from my PRU code is being stored in the general .text and .data sections in my linker file, how can I point a buffer to them? This is what I was trying to accomplish with framegrabber and ramwriter sections in my linker file, but the linker complains about section overlap. If there is an existing example that does this with GCC, that would be a great help. 

    My basic strategy is:

    Import obj code from PRU projects that have been parsed into data & text sections

    Create C style arrays that hold the data and text sections for each PRU (done through linker magic - the part in question here)

    write those buffers via the PRUICSS API that you referenced earlier.

    This seems feasible, but when I force the overlap to not happen via an AT() directive in the lds file, the Entry point seems to want to point to the .text section in the PRU file, not from the actual executable code. 

    I appreciate your help with this

    Marc

  • Marc,

    This thread should help you move forward - e2e.ti.com/.../2324171, which details the steps to convert PRU out file to .bin files and .c array and then execute the PRU image.

    Regards,
    Garrett