Picosoc linker issue


Hi all,

I’ve just been experimenting with the picosoc example in the BX examples github.
I had previously played with picorv32 on the hx8k demo board from Lattice and wrote a WS2812 LED driver and some firmware to make pretty patterns. That all worked fine.

I’ve added the same stuff to the picosoc example for the BX, and am having some issues I believe are to do with the linker. For example, global variables are uninitialised.

Here’s my fork and branch

And issue on github:

If anyone has any ideas on how to fix this I’m really interested in how it works.


Ohh, yeah…the global variables. You need to use your bootup code to initialize the global variables. The linker script just sets up the location of their initiializatiom data and their location in RAM. Your boot up code then needs to copy the init data to the RAM.


Luke’s right. The startup code (start.S) should copy the initialised data over to the data RAM.

It might help to have a look at the linker script (sections.lds) and the firmware.map file that gets produced during the build to get an idea of where things are going. const values should end up in flash (.text / .rodata), uninitialised data in bss, and initialised in the .data / .sdata sections.

If you look at the sections.lds file, you’ll notice that at the end of the .data section there’s a marker called “_sidata”. This is used by the startup code as a pointer to where the initialised data sits in the flash memory image.

.text :
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    *(.srodata)         /* .rodata sections (constants, strings, etc.) */
    *(.srodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
    _etext = .;        /* define a global symbol at end of code */
    _sidata = _etext;  /* This is used by the startup in order to initialize the .data secion */

The data is then copied from there to RAM (ie. to the .data section) by the startup code.

The actual .data section is bookended by _sdata and _edata markers which are used to signify the start and end of data RAM.

The part of the start.S file that copies data across looks like this:

    # copy data section
    la a0, _sidata
    la a1, _sdata
    la a2, _edata
    bge a1, a2, end_init_data
    lw a3, 0(a0)
    sw a3, 0(a1)
    addi a0, a0, 4
    addi a1, a1, 4
    blt a1, a2, loop_init_data

The other thing that needs to happen on startup is that the bss segment needs to be initialised to all zeros (and the registers probably ought to be zeroed too)…



I’ve had it brought to my attention that the PicoSoC example in the TinyFPGA-BX repo doesn’t actually have the code for copying data at the time of writing.

The start.s file in Clifford Wolf’s project does do the initialisation, but it’s also got some other stuff in there that may or may not be useful to you - eg. driving LED’s during the boot sequence, and banging data out to the SPI flash manually. Clifford’s code also doesn’t contain an IRQ handler:


Clifford also has a different firmware available here:


This one has an interrupt handler, but doesn’t do any of the data copying… :man_shrugging:

For the game SoC example I’ve created my own Frankenstein’s monster of all of the bits that I needed:


… but again this may not be useful to you unless you’re using the same memory layout as I am - I’ve relocated the stack to the start of memory, removed .bss and built the IRQ handler around the existence of QREGS (which required modification of Luke’s picosoc.v file).

Sorry if the “it’s complicated” answer is a little unsatisfying - if you’ve got any other questions I’ll try my best to answer them if I can.



thanks for the details! I want to learn about this part of the compilation process. Will post back here if I get anywhere.


OK, thanks to Luke, Gundy, Clifford’s start.s and Dave Shah I think I understand how this is working.
I added the copy data section from Clifford’s start.s and also the part that initialises bss.

Then I removed the bss initialisation from firmware.c. Seems to work fine! Here’s my start.s