walker2048 发表于 2024-7-21 21:27

全能小网关|CH32V208--5、如何指定函数在RAM里运行

#### 程序从存储器执行的方式
常见的程序执行方式一般有两种:
1、在Flash里直接执行,这需要Flash支持XIP(Execute In Place),也就是允许程序代码直接从非易失性存储器(如ROM或Flash)执行,同时将数据存储在易失性存储器(如RAM)中。这可以减少启动时间和内存占用。大部分情况下,MCU都是在这种模式下运行的。
2、在RAM里执行,也就是(Load and Go),将程序或数据加载到RAM中,然后从RAM执行程序的过程。这种方法可以加快程序的执行速度,因为从RAM读取通常比从Flash或其他非易失性存储器读取要快。在需要快速执行的程序里非常常见。

#### 这和WCH硬件支持的零等待有什么区别
WCH硬件支持的零等待区,是不需要用户干预的,默认情况下,只要用户程序低于零等待区的大小,就都是在RAM缓存里执行的,不需要等待。
而人工控制的RAM执行程序,需要人为指定哪些函数需要放到RAM里,并且在linkScript文件里写清楚存在哪个位置。

#### 实际案例参考
我们可以打开EVT\EXAM\BLE\MESH\adv_ali_light_add_lightness这个目录的项目,可以看到app_main.c文件里就有对应的例子。例子如下:
```c
/*********************************************************************
* @fn      Main_Circulation
*
* @brief Main loop
*
* @return none
*/
__attribute__((section(".highcode")))
__attribute__((noinline))
void Main_Circulation(void)
{
    while(1)
    {
      TMOS_SystemProcess();
    }
}
```
首先,指定把Main_Circulation这个函数放到RAM里的,就是以下的内容。
```c
__attribute__((section(".highcode")))
```
这是一条编译器特定的属性,用于告诉编译器将这个函数的代码放入名为 .highcode 的段中。在某些嵌入式系统中,可能需要将特定的代码段放在内存的特定区域,以便进行优化或特殊处理。
```c
__attribute__((noinline))
```
这又是一个编译器属性,它指示编译器不要对 Main_Circulation 函数进行内联扩展。内联扩展是编译器优化的一种手段,它会将函数调用替换为函数的实现代码,以减少函数调用的开销。使用 noinline 属性可以防止这种情况发生,确保函数作为一个单独的调用存在。

我们再来看看项目使用的LD文件,从项目属性这里可以看到,使用的LD文件是EVT\EXAM\BLE\HAL\Link.ld,打开这个文件,内容如下。
```c
ENTRY( _start )

__stack_size = 2048;

PROVIDE( _stack_size = __stack_size );


MEMORY
{
/* CH32V20x_D6 - CH32V203F6-CH32V203G6-CH32V203K6-CH32V203C6 */
/*
        FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 32K
        RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 10K
*/

/* CH32V20x_D6 - CH32V203K8-CH32V203C8-CH32V203G8-CH32V203F8 */
/*
        FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 64K
        RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
*/

/* CH32V20x_D8 - CH32V203RB
   CH32V20x_D8W - CH32V208x
   FLASH + RAM supports the following configuration
   FLASH-128K + RAM-64K
   FLASH-144K + RAM-48K
   FLASH-160K + RAM-32K
*/
        FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 448K
        RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K

}


SECTIONS
{

        .init :
        {
                _sinit = .;
                . = ALIGN(4);
                KEEP(*(SORT_NONE(.init)))
                . = ALIGN(4);
                _einit = .;
        } >FLASH AT>FLASH

.vector :
{
      *(.vector);
          . = ALIGN(64);
                KEEP(*(SORT_NONE(.handle_reset)))
} >FLASH AT>FLASH

   .highcode :
    {
      . = ALIGN(4);
      *(.highcode);
      *(.highcode.*);
                . = ALIGN(4);
    } >FLASH AT>FLASH
   
        .text :
        {
                . = ALIGN(4);

      EXCLUDE_FILE (*wchble.a) *(.text .text*)
      
                *(.text)
                *(.text.*)
                *(.rodata)
                *(.rodata*)
                *(.sdata2.*)
                *(.glue_7)
                *(.glue_7t)
                *(.gnu.linkonce.t.*)
                . = ALIGN(4);
        } >FLASH AT>FLASH

        .fini :
        {
                KEEP(*(SORT_NONE(.fini)))
                . = ALIGN(4);
        } >FLASH AT>FLASH

        PROVIDE( _etext = . );
        PROVIDE( _eitcm = . );       

        .preinit_array:
        {
          PROVIDE_HIDDEN (__preinit_array_start = .);
          KEEP (*(.preinit_array))
          PROVIDE_HIDDEN (__preinit_array_end = .);
        } >FLASH AT>FLASH
       
        .init_array   :
        {
          PROVIDE_HIDDEN (__init_array_start = .);
          KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
          KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
          PROVIDE_HIDDEN (__init_array_end = .);
        } >FLASH AT>FLASH
       
        .fini_array   :
        {
          PROVIDE_HIDDEN (__fini_array_start = .);
          KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
          KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
          PROVIDE_HIDDEN (__fini_array_end = .);
        } >FLASH AT>FLASH
       
        .ctors          :
        {
          /* gcc uses crtbegin.o to find the start of
             the constructors, so we make sure it is
             first.Because this is a wildcard, it
             doesn't matter if the user does not
             actually link against crtbegin.o; the
             linker won't look for a file to match a
             wildcard.The wildcard also means that it
             doesn't matter which directory crtbegin.o
             is in.*/
          KEEP (*crtbegin.o(.ctors))
          KEEP (*crtbegin?.o(.ctors))
          /* We don't want to include the .ctor section from
             the crtend.o file until after the sorted ctors.
             The .ctor section from the crtend file contains the
             end of ctors marker and it must be last */
          KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
          KEEP (*(SORT(.ctors.*)))
          KEEP (*(.ctors))
        } >FLASH AT>FLASH
       
        .dtors          :
        {
          KEEP (*crtbegin.o(.dtors))
          KEEP (*crtbegin?.o(.dtors))
          KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
          KEEP (*(SORT(.dtors.*)))
          KEEP (*(.dtors))
        } >FLASH AT>FLASH

        .dalign :
        {
                . = ALIGN(4);
                PROVIDE(_data_vma = .);
        } >RAM AT>FLASH       

        .dlalign :
        {
                . = ALIGN(4);
                PROVIDE(_data_lma = .);
        } >FLASH AT>FLASH

        .data :
        {
            *(.gnu.linkonce.r.*)
            *(.data .data.*)
            *(.gnu.linkonce.d.*)
                . = ALIGN(8);
            PROVIDE( __global_pointer$ = . + 0x800 );
            *(.sdata .sdata.*)
            *(.gnu.linkonce.s.*)
            . = ALIGN(8);
            *(.srodata.cst16)
            *(.srodata.cst8)
            *(.srodata.cst4)
            *(.srodata.cst2)
            *(.srodata .srodata.*)
            . = ALIGN(4);
                PROVIDE( _edata = .);
        } >RAM AT>FLASH

        .bss :
        {
                . = ALIGN(4);
                PROVIDE( _sbss = .);
          *(.sbss*)
      *(.gnu.linkonce.sb.*)
                *(.bss*)
           *(.gnu.linkonce.b.*)               
                *(COMMON*)
                . = ALIGN(4);
                PROVIDE( _ebss = .);
        } >RAM AT>FLASH

        PROVIDE( _end = _ebss);
        PROVIDE( end = . );

    .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size :
    {
      PROVIDE( _heap_end = . );   
      . = ALIGN(4);
      PROVIDE(_susrstack = . );
      . = . + __stack_size;
      PROVIDE( _eusrstack = .);
    } >RAM

        /*.stack ORIGIN(RAM)+LENGTH(RAM) :
        {
      PROVIDE( _heap_end = . );   
                . = ALIGN(4);
                PROVIDE(_eusrstack = . );
        } >RAM*/
}

```
可以看到在54行就有highcode这个段的定义了,紧跟在中断向量表后面。我们也可以在编译后的map文件里,看到这个段都有什么函数和符号内容。

在图片里,我们也可以明确看到,前面定义的蓝牙主循环代码已经放在RAM里了(由于WCH的零等待区已经是在RAM里,其实这个地址已经可以确保肯定是在RAM里执行的)。
如果我们不使用这种方式指定,会有什么问题么?分两种情况来分析:
1、程序远小于零等待区大小,那不管是否指定放在highcode段里,都是不需要等待的。
2、程序大于零等待区,那就必须指定放在highcode段里,确保这些函数可以告诉运行,不会因为超出零等待区导致执行缓慢。

Jacktang 发表于 2024-7-22 08:44

<p>使用这种方式指定,会有出现的两种情况,分析的到位</p>

lugl4313820 发表于 2024-7-23 07:35

<p>放RAM里,除了速度,还有其他的什么意义吗?比如说稳定性如何?</p>

walker2048 发表于 2024-7-23 22:37

lugl4313820 发表于 2024-7-23 07:35
放RAM里,除了速度,还有其他的什么意义吗?比如说稳定性如何?

<p>纯粹就是为了速度。比方说ch582m,直接在flash里执行gpio翻转,只能有500k还是1.2M左右的翻转频率 ,但是如果把死循环gpio翻转的同一个函数,放在RAM里,就可以达到5M左右的翻转频率。放到RAM里,其实就是在flash执行的代码无法满足性能要求的时候,用来提速的一种手段。</p>
页: [1]
查看完整版本: 全能小网关|CH32V208--5、如何指定函数在RAM里运行