嵌入式系统/内存
在嵌入式系统中,内存非常宝贵。某些芯片,特别是嵌入式 VLSI 芯片和低端微处理器可能只有少量的 RAM “板载”(直接内置到芯片中),因此它们的内存不可扩展。其他嵌入式系统拥有固定数量的内存,并且没有扩展手段。除了 RAM 之外,一些嵌入式系统还具有一些非易失性内存,例如微型磁盘、FLASH 内存扩展,甚至各种第三方内存卡扩展。然而,请记住,嵌入式系统的内存升级成本可能高于整个系统本身。因此,嵌入式系统程序员需要非常清楚地了解可用的内存和完成任务所需的内存。
内存通常被分割成许多不同的区域,这些区域被预留用于特定目的。
通常有 4 个不同的可寻址区域,每个区域都使用不同的技术实现
- 程序内存(用于保存你编写的程序),通常称为 ROM(尽管大多数开发者更喜欢使用实际上用 Flash 实现此功能的芯片)。在你的程序运行时,无法更改程序内存中的任何数据。但至少当电源恢复时,它仍然存在。
- RAM,用于保存变量和堆栈。(变量的初始值从 ROM 复制)。当电源丢失时,它会忘记所有内容。
- EEPROM。类似于个人计算机中的硬盘驱动器,用于存储可能偶尔会更改的设置,并且需要在下次启动时记住。
- I/O。这实际上是微控制器的全部意义所在。
许多流行的微控制器(包括 8051、Atmel AVR、Microchip PIC、Cypress PSoC)具有“哈佛架构”,这意味着程序只能从“ROM”执行。你可以将字节从 ROM(或其他地方)复制到 RAM,但实际上不可能跳转或调用 RAM 中的这种“代码”。这与台式计算机的情况完全相反,在台式计算机中,你编写的代码只有在复制到 RAM 中之后才能执行。
一些流行的微控制器(如 68HC11 和 68HC12 等)具有统一的地址空间(“冯·诺依曼架构”)。你可以跳转或调用任何地方的代码(尽管跳转到 I/O 空间中的地址几乎肯定不是你真正想做的)。
软件应用程序通常会越来越大。古老的处理器(如火星探测器 Sojourner 上使用的 8085)具有 16 位地址寄存器,可以访问的最大地址为 65 536 个位置,但是,使用这些处理器的系统通常拥有比这多得多的物理 RAM 和 ROM。它们使用“分页”硬件将“内存块”进出直接可访问的空间。早期 Microchip PIC 处理器有两组完全独立的“银行寄存器”,一组用于切换程序 ROM 的不同内存块,另一组用于切换 RAM 的不同内存块。
嵌入式系统编写的程序经常会越来越大,直到它们超过可用的程序空间。有一些技术[1] 用于处理内存不足问题
- 使用“-Os”(优化大小)选项重新编译
- 查找并注释掉“死代码”
- 将重复部分“重构”到一个公共子程序中
- 用 RAM 空间换取程序空间。
- 在“内部程序内存”中放置一个小型解释器,用于加载和解释“指令”。
- 使用比直接用汇编语言编码更紧凑的“指令”,例如 p 代码或线程代码。或者
- 这些“指令”可以放置在 EEPROM 或外部串行 Flash 中,这些 Flash 无法用作程序内存。或者
- 两者兼而有之。这种技术通常用于“邮票”式 CPU 模块中。
- 添加更多内存(可能使用分页或银行机制)
大多数用于台式计算机的 CPU 都有一个“内存管理单元”(MMU)。MMU 处理虚拟内存,保护操作系统使用的内存区域免受不受信任的程序的访问,并且...
大多数嵌入式系统没有 MMU。我们在嵌入式系统/Linux中讨论了可以在没有 MMU 的系统上运行的两种 Linux 版本。
保留内存是为某些目的预留的内存,例如额外的软件安装和启动。
旧的 X86 处理器只有 16 位处理器,如果使用平面内存方案,这些处理器将只能支持 65 KB 的内存。旧的 8086 和 80286 处理器背后的系统工程师提出了对内存进行分段的想法,并使用段寄存器和偏移寄存器的组合来访问有效的 20 位地址范围,最多可以访问 1 MB 的内存。
地址 = (段寄存器 * 16) + 指针寄存器
新的 32 位处理器允许 4 GB 的可寻址内存空间,因此分段内存模型在当前的 32 位机器中几乎被放弃(尽管段寄存器仍然用于实现分页方案)。
内存映射 I/O 是一种机制,通过该机制,处理器使用内存访问技术执行 I/O 访问。这通常是通过实现的,因为内存总线通常比 I/O 总线快得多。内存映射 I/O 可能被使用的另一个原因是使用的体系结构没有单独的 I/O 总线。
在内存映射 I/O 中,CPU 地址空间的某些范围被预留给外部外设。可以使用与其他内存访问相同的指令访问这些位置。但是,对这些地址的读/写被解释为对设备的访问,而不是对主内存中的位置的访问。
CPU 可能期望某个特定设备位于固定位置,或者可以动态分配空间供它使用。
这种工作方式是内存接口通常被设计成总线(一种共享的通信资源),其中连接了多个设备。这些设备通常被排列为主设备和从设备,主设备可以向任何从设备发送和接收数据。一个典型的系统会有
- CPU 作为主设备
- 一个或多个 RAM 和/或 ROM 设备用于程序代码和数据存储
- 外围设备用于与外部世界交互。这些设备的例子可能包括 UART(串行通信)、显示设备或输入设备
进一步阅读
[edit | edit source]一些流行的小型系统解释器(其中一些我们在前面简要提及过)包括
- Forth; 一个 Forth wiki 列出了许多 Forth 移植到嵌入式系统的版本。
- (一个子集) Python
- (一个子集) BASIC 编程,通常是标记化的 BASIC,例如 PBASIC 或 PICAXE BASIC
- 维基百科:CHIP-8
- 维基百科:XPL0
- (一个子集) Lua 函数式编程 ([1])
- (一个子集) Objective Caml ([2])
- (一个子集) 嵌入式系统/C 编程,一个 C 解释器,例如 PicoC 或 维基百科:交互式 C。
- "在微型内存中运行的交互式语言有哪些?" 在 Stack Overflow 上