📜  在嵌入式系统中从 RAM 执行

📅  最后修改于: 2022-05-13 01:57:01.133000             🧑  作者: Mango

在嵌入式系统中从 RAM 执行

先决条件:随机存取存储器(RAM),嵌入式系统介绍

介绍 :
与计算机中的应用程序不同,嵌入式系统中的软件不从 RAM 中执行。在绝大多数现代嵌入式系统架构中,程序(指令)存储在微控制器的闪存(代码闪存/程序闪存)中并直接执行。早些时候,ROM 用于程序存储和执行。
由于存储设备中的新兴技术,现在正在使用闪存。 RAM 是易失性存储器,在复位或电源循环时不包含任何内容。它用于存储数据并在运行时保存变量的值。通过在代码闪存中存储和执行程序(使用下载工具和调试器),可以安全地开发嵌入式产品。 MCU 具有用于代码闪存和 RAM 的专用存储空间部分。高级微控制器中的内存映射包含 Data flash、DSPR、PSPR、Boot ROM、EEPROM、调试内存等。为简单起见,我们将考虑使用 code flash 来存储和执行程序,以及 RAM 来存储变量和数据。在某些情况下,必须从 RAM 运行子程序。
本文介绍了将代码段复制到 RAM 的需要、实现它的各种方法、它的优缺点。复制到 RAM 意味着托管两个代码副本——一个在闪存中,另一个在 RAM 中。

图。1。微控制器的内存映射

需要从 RAM 执行:

  1. 在闪存重新编程期间(引导加载程序功能),某些要从 RAM 执行的功能,如擦除程序闪存、将数据写入程序闪存。代码不应从正在擦除/编程的同一闪存中运行。在某些芯片中,闪存在对闪存执行操作时会冻结。因此,需要将代码部分放在 RAM 中并执行。
  2. 在重新编程闪存(引导加载程序功能)期间,要从 RAM 放置和执行中断向量地址和 ISR。
  3. 从 RAM 执行的引导加载程序。引导加载程序功能最安全的方法是从主机设备接收引导加载程序文件并将完整代码放入 RAM。
  4. 从 RAM 运行程序比从闪存运行快得多,例如将频繁执行的代码放在 RAM 中以优化时间。
  5. 嵌入式系统中的一些调度程序在 RAM 中创建任务,指向闪存中的程序。
  6. 在闪存操作正在进行时从 RAM 运行应用程序。

复制代码部分并从 RAM 执行:

1. 一些控制器提供了一种非常直接的方式来将代码复制到 RAM。您所需要的只是浏览工具链和用户手册。下面列出了不同的方法——

  • 一些控制器提供属性或 API 或编译器指令来将代码复制到 RAM。
  • 在某些控制器中,启动例程运行时代码(其本身从闪存执行)根据需要将程序操作码从闪存复制到 RAM,就像它为初始化变量所做的那样。
  • 在某些控制器中,更新链接描述文件就足以将代码段放入 RAM。但是必须有一些源代码将代码部分复制到 RAM。在大多数情况下,内部由启动例程完成。

2. 复制函数到 RAM –

  • 下面的代码段将 func_to_be_copied()函数复制到 RAM。 dummy_endfunc() 位于函数末尾以确定 func_to_be_copied() 的大小。确保编译器未优化 dummy_endfunc()。诸如“#pragma”之类的编译器指令可用于避免优化。可以调用 ExecuteRamFunc() 从 RAM 执行 func_to_be_copied()。
C
// Global buffer stored in RAM and function is copied to this buffer 
unsigned char RAM_area[1024] 
void func_to_be_copied(void)
{
//piece of code
}
// No other function to be placed in between
// Make compiler settings for not to optimize dummy_endfunc
void dummy_endfunc(void)
{
    // No code
}
  
void CopyFlashFuncToRam(void)
{
   unsigned long int func_size = 0, cntr;
   unsigned char *dest_ptr;
   dest_ptr = (unsigned char *) func_to_be_copied ;  
   func_size = (unsigned long int)dummy_endfunc - (unsigned long int)func_to_be_copied;
   for(cntr = 0; cntr < func_size; cntr++)
   {
      RAM_area[cntr] = dest_ptr[cntr]; 
   }   
}
  
void ExecuteRamFunc(void)
{
   void(*func_ptr)(void) = (void(*)(void)) &RAM_area[0];
   func_ptr();  
}


3.函数的大小也可以使用return 语句来计算,该语句可以放在func_to_be_copied() 的末尾。开始复制函数,直到遇到 return 语句的操作码。存在函数内部使用的数据可能与返回语句的操作码相同的风险。为此,您需要通过参考用户手册了解说明的对齐要求。

4. 将代码部分复制到 RAM –
假设您在闪存中放置了一段代码,例如需要从 RAM 执行的引导加载程序代码。引导加载程序的大小应该提前知道。在下载到闪存时,也可以在引导加载程序代码的末尾写入签名字节以了解代码大小。如果引导加载程序的大小未知,则可以考虑分配的部分大小。可以按照第 1 点中解释的相同方式将代码部分复制到 RAM。 (2)。确保在链接描述文件中保留 RAM 以用于指令执行。

从 RAM 执行时,请考虑以下事项 -

  • 用于指令执行的 RAM 应通过链接描述文件或通过声明全局缓冲区来保留。
  • 应知道复制段的起始地址,以便程序计数器可以正确指向或通过将函数指针指向正确地址来执行。
  • RAM 测试可以在启动时进行,以确保其正确性。

好处 :

  1. 表现 -
    如果将频繁执行的关键例程放在 RAM 中,则进行时间优化。 RAM 的执行速度明显快于闪存。
  2. 降低功耗 –
    如果整个应用程序正在从 RAM 中执行,则可以关闭闪存。
  3. 如果代码从 RAM 执行,则通常需要短寻址,即访问控制器的 RAM、数据闪存或 EEPROM 可以通过短指针访问,而如果代码从闪存执行(闪存通常是大存储器,100 KB 到 MB),它需要长寻址。简而言之,汇编程序通常会生成 sjmp、acall 等指令,这些指令通常需要较少的机器周期数。

缺点:

  1. RAM 是一种稀缺资源,价格昂贵,并且与闪存相比通常尺寸要小得多。如果非静态代码放在RAM中,就是浪费RAM空间,从而限制了可变数据的使用。
  2. 在两个地方托管相同的例程 - 一个在闪存中,另一个在 RAM 中。
  3. 在 RAM 中调试代码很困难。可以在反汇编中进行调试。 ELF 文件一般用于调试,其中包含调试信息、符号表等。如果将代码移动到其他地方,则在调试器中看不到符号,但可以查看和调试在反汇编窗口中执行的指令。
  4. 有时从 RAM 执行会导致意外的硬故障。因此,建议先从闪存执行,测试功能,然后从 RAM 重新执行。