4

都2020年了,想必大家对ESP8266都很熟了,没用过至少也听说过。所以抛开丰富有趣的各种应用,下面来说一说很少人讨论的ESP8266的架构。

架构

ESP8266集成了增强版的超低功耗Tensilica’s Xtensa L106 32-bit内核处理器。为什么说增强版呢,因为Tensilica处理器的特点,就是工程师可以对内核IP做定制化,乐鑫购买IP后,继续进行优化,形成了增强版。
该处理器使用RISC指令集,CPU时钟速度最⾼可达160MHz,芯⽚内置了存储控制器,包含ROM和SRAM。但这个ROM呢,固化一些底层代码,用户是修改不了的,用户程序必须由外部Flash存储,理论上最大可支持16MB的存储。所以这就是为什么买ESP8266模块时,都要看用的多大的Flash,使用SDK开发方式的话,从编译到下载都要选择正确的Flash。

代码和内存的存储分配

熟悉计算机原理的都知道,计算机的本质就是计算,通过执行不同的指令,操作不同的数据来得到不同的数据结果。指令在代码中可以认为是不同的语句,数据就是不同的变量。在单片机中,一般指令存在ROM中,数据变量存在RAM中。有这两个概念后,再看看ESP8266这款芯片。
ESP8266片上有固化的ROM,需要用户外置Flash来存储程序。在物理上有 64KB 的 iRAM,96KB 的 dRAM。
其中:

iRAM:instruction RAM,用来存放指令,位于 0x40100000 开始的 64KB 空间。
dRAM:data RAM,用来存放数据,位于 0x3FFE8000 开始的空间。

所以ESP8266一共有这么几个存储的地方:片上ROM、片上iRAM、片上dRAM,片外Flash。

存储区 作用
片上ROM 固化芯片底层代码,用户无法改动
片上iRAM 存放指令,用于执行程序
片上dRAM 存放数据
片外Flash 存放用户代码、用户数据等

知道iRAM后,就知道我们Flash的地位并不怎样。代码并不是直接在Flash上取指令运行的,而是加载到iRAM中来运行的。

代码开始的地方

ESP8266中所有代码都是加载到iRAM后运行的,只是有的提前加载(IRAM段),有的用时加载(IROM段)。IROM段使用部分称为Cache,IRAM段为真iRAM。
ESP8266有个特殊的Cache概念。在计算机系统中,Cache是为了解决CPU读内存速度过慢而设置的。Cache比内存的读取速度更快,但比寄存器读取速度慢。通常Cache只是小空间存储,用来缓存CPU读取过的内存数据。ESP8266中的Cache使用了iRAM,iRAM共有64k,分了一半(32k)给Cache。ESP8266的底层启动后,会将对应Flash的空间映射到Cache空间。
用户代码是存在外置Flash的,那么芯片启动后,IRAM段加载到iRAM中,底层代码(前面说的固化ROM内)也会将Flash空间映射到Cache中。因为Cache的空间远小于Flash,所以使用了特殊的映射技术可以将最大1M+1M的Flash映射到32K的cache中。而哪些代码需要加载到iRAM,哪些还是放在Cache中运行,则是在链接时,通过指定不同的段来区分。
写过代码的朋友就知道了,在NONOS SDK中,需要ICACHE_FLASH_ATTR来定义函数,以使代码不占用iRAM空间。在RTOS SDK中,使用IRAM_ATTR定义函数,将需要频繁运行,讲究效率的函数放到iRAM中。这个便是iRAM和iROM的区别引起的。
好了,到了这里,我们就知道代码编译生成二进制文件,烧录到Flash后,是怎么由ESP8266执行起来的了。别走开,下面有彩蛋。

ROM的底层

ROM的底层是看不见的,但是既然我们能用到,那就有它的痕迹
https://github.com/espressif/ESP8266_NONOS_SDK/blob/master/ld/eagle.rom.addr.v6.ld

PROVIDE ( ets_memcmp = 0x400018d4 );
PROVIDE ( ets_memcpy = 0x400018b4 );
PROVIDE ( ets_memmove = 0x400018c4 );
PROVIDE ( ets_memset = 0x400018a4 );
PROVIDE ( ets_post = 0x40000e24 );
PROVIDE ( ets_printf = 0x400024cc );
PROVIDE ( ets_putc = 0x40002be8 );
PROVIDE ( ets_rtc_int_register = 0x40002a40 );

这份ld文件里,就描述了底层函数在ROM中的地址,也告诉我们,到底提供了多少功能。
但NONOS SDK官方也已经停止更新了,在RTOS SDK中,底层函数作用也逐渐在淡化,像操作外设的都改成了在用户代码里编写的形式。

NONOS SDK默认链接到iROM中

不知道现在还有多少人在使用NONOS SDK做开发,既然这篇文章说到了代码存放,那就说一下吧。
在NONOS SDK中,用户代码编译链接后,会默认放到iRAM中,所以需要用户手动增加ICACHE_FLASH_ATTR来修饰。但问题是iRAM空间只有32k,是非常有限的。
如果都是自己从0开始写的代码,那也没什么。但是如果是要引用别人的库,或者是自己写的代码要封装成库给其他地方使用,这时就很麻烦了,因为其他平台都不用这样定义。那么有没有解决办法呢,有。
在SDK中找到./ld/eagle.app.v6.ld文件。
.irom0.text : ALIGN(4)段下做如下修改即可。

  .irom0.text : ALIGN(4)
  {
    _irom0_text_start = ABSOLUTE(.);

    *libuser.a:(.literal.* .text.*)   /* 增加此行 */
    *libat.a:(.literal.* .text.*)
    *libcrypto.a:(.literal.* .text.*)
    
    ... ...
    
    _irom0_text_end = ABSOLUTE(.);
  } >irom0_0_seg :irom0_0_phdr

用户代码编译后,会生成libuser.a文件,所以这个文件里都是用户代码。新增语句就是将用户代码中类常量和代码指令都放到iROM中。

End

关于ESP8266架构和代码如何开始运行的东西就到这里了。彩蛋之后,当然是下集预告啦。下次可能会根据开源的 Bootloader代码,来跟着解析一遍哦。
阵容如下,有没有点期待呢。

rboot:https://github.com/raburton/rboot
zboot:https://github.com/zorxx/zboot
esp-bootloader:https://github.com/tuanpmt/esp-bootloader

银翼Neal
18 声望3 粉丝