Linkerscript kedryte K210 , using: framework-kendryte-standalone-sdk

I’m working on a Kendryte K210 board (env:sipeed-maix-bit-mic).
I’m trying to put variables at a fixed address in the general-purpose SRAM and tried using a linker script to achieve this. But even if I put the full Path to my linker file in the board.json i get
the following error:
riscv64-unknown-elf-objcopy: error: the input file ‘.pio\build\sipeed-maix-bit-mic\firmware.elf’ has no sections

current linker script:

MEMORY
{
    nonCachedGPSRAM (wa!rx) : ORIGIN = 0x40000000, LENGTH = 0x600000
}

my borad.json:

{
    "build": {
        "extra_flags": "",
        "f_cpu": "400000000L",
		"ldscript": "C:/Users/ErikS/Documents/PlatformIO/Projects/Minibot_SD-card/src/LScript.ld",
        "mcu": "K210",
        "hwids": [
            "0x0403",
            "0x6010"
        ],
        "variant": "sipeed_maix_bit",
        "board_def": "BOARD_SIPEED_MAIX_BIT"
    },
    "frameworks": [
        "kendryte-standalone-sdk",
        "kendryte-freertos-sdk",
        "arduino"
    ],
    "name": "Sipeed MAIX BiT with Mic",
    "upload": {
        "maximum_ram_size": 6291456,
        "maximum_size": 16777216,
        "burn_tool": "bit_mic",
        "require_upload_port": true,
        "speed": 1500000
    },
    "url": "https://www.sipeed.com/",
    "vendor": "Sipeed"
}

my platform.ini

[env]
[env:sipeed-maix-bit-mic]
platform = kendryte210
board = sipeed-maix-bit-mic
framework = kendryte-standalone-sdk
upload_port = COM1
monitor_port = COM1
monitor_speed = 115200

If anybody has a smarter solution to put variables at a fixed address, with for example “#pragma”, i would be happy to use it instead of linker scripts.

This is missing all section definitions and various other stuff.

If you’ve only modified the linker script path, that is something that should be done in the platformio.ini. The builder script respects the option

board_build.ldscript = some_path

in the platformio.ini to change it (source).

When I use the standard Kendryte standalone SDK blink example and look at the verbose build output, I see it’s linking against C:\Users\<user>\.platformio\packages\framework-kendryte-standalone-sdk\lds\kendryte.ld. The file already contains a definition for the uncached RAM.

Which has the same origin and length as your definition.

But there’s no section that uses this RAM.

However, as I read it from the datasheet it is the same physical SRAM available through different memory addresses that are either cached or uncached.

So we can’t treat it like additional memory. If we were to modify the PT headers and the sections like

PHDRS
{
  ram_ro   PT_LOAD;
  ram_init PT_LOAD;
  ram      PT_NULL;
  ram_nocache PT_LOAD;
}
//..

  .ram_nocache_section :
  {
    PROVIDE( _ram_nocache_start = ABSOLUTE(.) );
    *(.ram_nocache_section)
    KEEP (*(.ram_nocache_section))
    . = ALIGN(8);
    PROVIDE( _ram_nocache_end = ABSOLUTE(.) );
  } >ram_nocache AT>ram_nocache :ram_nocache

and then

int var_allocated_in_nocache_ram1 __attribute__ ((section (".ram_nocache_section")));
int var_allocated_in_nocache_ram2 __attribute__ ((section (".ram_nocache_section")));

per nm tool the variables are allocated in the non-cached RAM

>C:\Users\Max.platformio\packages\toolchain-kendryte210\bin\riscv64-unknown-elf-nm.exe .\.pio\build\sipeed-maix-bit-mic\firmware.elf | grep “var_”
0000000040000000 D var_allocated_in_nocache_ram1
0000000040000004 D var_allocated_in_nocache_ram2

(notice addresses in 0x40000000), but they of course overshadow whatever the linker is placing at 0x80000000.

Now of course we can use a trick that we do not use the same memory cells twice by e.g. decreasing the length of the ram memory by some bytes, and then move the origin of ram_nocache upwards and set the length to what we initially cut off in ram – but this is very hacky.

Thought differently: If you allocate a normal variable in RAM in C-code, and take its address, it will be in the 0x8000000 region. If you subtract 0x4000000 from it (or mask one bit and or another bit…), then that’s the address you need to read from for the uncached access (or write to). Such addresses can be one-time computed at the start of the program and then used. Does that solve your high-level problem?

Otherwise you can also ask in the SDK’s github on how to handle read/writes to the uncached section – maybe there are some convenience functions for it, but I didn’t find any.

Thank you for the detailed help!
I tried the changes to the original linker script and everything compiles and seems to work,
but when I try to flash my board, “the Programming BIN:” is super slow and would need hours to complete, compared to just seconds before. Do you have an idea why this is the case?

Which changes have you done exactly? You followed the advice regarding computing the non-cached read/write address for a given RAM variable?

Maybe there’s an error in the created bin with some underflow and suddenly the file is a few gigabytes.

Than you for the fast response!
I followed the advice regarding computing the non-cached read/write address for a given RAM variable. I made the additions in the linker file and used the attribute
when defining the variable:

int testval attribute ((section (".ram_nocache_section")));

When building with the changed linkerscript but wihtout using attribute(section) nothing changes and the resulting build folder is 11MB, like before.
With the usage of attribute(section) the resulting build folder is 1.1GB.

Then it’s probably the error you mentioned. Can you tell me how to figure out what’s wrong?

But the attribute section way is as I showed the wrong way (if you don’t take care to decrease the section of the (cache) RAM section).

The way that I think should be used is as said recompute the address and no linker script modifications.

Given e.g. some code

int some_variable = 42; 

int main() { 
   printf("some_variable: %d Address %p\n", some_variable, &some_variable);
}

you should be able to access the variable via a non-cached way by

#include <stdint.h>
int some_variable = 42; 

void* convert_cached_to_nonchached_addr(void* ptr) {
   uint32_t ptr_val = (uint32_t) ptr; 
   ptr_val &= ~(1u << 31u);
   ptr_val |= 1u << 30u;
   return (void*) ptr_val;
}

int main() { 
   printf("some_variable (cached): %d Address %p\n", some_variable, &some_variable);
   int* noncached_some_var = (int*) convert_cached_to_nonchached_addr(&some_variable);
   printf("some_variable (uncached): %d Address %p\n", *noncached_some_var, noncached_some_var);
}