github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/examples/ram-func/main.go (about)

     1  package main
     2  
     3  // This example demonstrates how to use go:section to place code into RAM for
     4  // execution.  The code is present in flash in the `.data` region and copied
     5  // into the correct place in RAM early in startup sequence (at the same time
     6  // as non-zero global variables are initialized).
     7  //
     8  // This example should work on any ARM Cortex MCU.
     9  //
    10  // For Go code use the pragma "//go:section", for cgo use the "section" and
    11  // "noinline" attributes.  The `.ramfuncs` section is explicitly placed into
    12  // the `.data` region by the linker script.
    13  //
    14  // Running the example should print out the program counter from the functions
    15  // below.  The program counters should be in different memory regions.
    16  //
    17  // On RP2040, for example, the output is something like this:
    18  //
    19  //  Go in RAM:    0x20000DB4
    20  //  Go in flash:  0x10007610
    21  //  cgo in RAM:   0x20000DB8
    22  //  cgo in flash: 0x10002C26
    23  //
    24  // This can be confirmed using `objdump -t xxx.elf | grep main | sort`:
    25  //
    26  //  00000000 l    df *ABS*  00000000 main
    27  //  1000760d l     F .text  00000004 main.in_flash
    28  //  10007611 l     F .text  0000000c __Thumbv6MABSLongThunk_main.in_ram
    29  //  1000761d l     F .text  0000000c __Thumbv6MABSLongThunk__Cgo_static_eea7585d7291176ad3bb_main_c_in_ram
    30  //  1000bdb5 l     O .text  00000013 main$string
    31  //  1000bdc8 l     O .text  00000013 main$string.1
    32  //  1000bddb l     O .text  00000013 main$string.2
    33  //  1000bdee l     O .text  00000013 main$string.3
    34  //  20000db1 l     F .data  00000004 main.in_ram
    35  //  20000db5 l     F .data  00000004 _Cgo_static_eea7585d7291176ad3bb_main_c_in_ram
    36  //
    37  
    38  import (
    39  	"device"
    40  	"fmt"
    41  	"time"
    42  	_ "unsafe" // unsafe is required for "//go:section"
    43  )
    44  
    45  /*
    46  	#define ram_func __attribute__((section(".ramfuncs"),noinline))
    47  
    48  	static ram_func void* main_c_in_ram() {
    49  		void* p = 0;
    50  
    51  		asm(
    52  			"MOV %0, PC"
    53  			: "=r"(p)
    54  		);
    55  
    56  		return p;
    57  	}
    58  
    59  	static void* main_c_in_flash() {
    60  		void* p = 0;
    61  
    62  		asm(
    63  			"MOV %0, PC"
    64  			: "=r"(p)
    65  		);
    66  
    67  		return p;
    68  	}
    69  */
    70  import "C"
    71  
    72  func main() {
    73  	time.Sleep(2 * time.Second)
    74  
    75  	fmt.Printf("Go in RAM:    0x%X\n", in_ram())
    76  	fmt.Printf("Go in flash:  0x%X\n", in_flash())
    77  	fmt.Printf("cgo in RAM:   0x%X\n", C.main_c_in_ram())
    78  	fmt.Printf("cgo in flash: 0x%X\n", C.main_c_in_flash())
    79  }
    80  
    81  //go:section .ramfuncs
    82  func in_ram() uintptr {
    83  	return device.AsmFull("MOV {}, PC", nil)
    84  }
    85  
    86  // 'go:noinline' used here to prevent function being 'inlined' into main()
    87  // so it appears in objdump output.  In normal use, go:inline is not
    88  // required for functions running from flash (flash is the default).
    89  //
    90  //go:noinline
    91  func in_flash() uintptr {
    92  	return device.AsmFull("MOV {}, PC", nil)
    93  }