ARM Cortex-M底层技术(十三)手把手教你写分散加载
ARM Cortex-M底层技术(十三)手把手教你写分散加载
还记得之前教大家写的启动代码吗?木看过滴,出门左转,第四篇【编写自己的启动代码】,当然仅仅能编写自己的启动代码怎么够,说了辣么多分散加载的东东,是时候检验一下我们的水平了,合上书,来出题考试了~【自己编写分散加载】。
来司机们,将装B进行到底~
首先,看看我们之前第四篇文章里面的简易版分散加载:
如下,之前按着没讲,前面罗里吧嗦的扯了辣么多分散加载,大家现在再回头看下这个分散加载,估计都看的明白了吧~
load_rom 0x00000000 0x00080000
{
vector_rom 0x00000000 0x400
{
*( vector_table, +first)
}
execute_rom <span class="hljs-number">0x400</span> FIXED <span class="hljs-number">0x0007FC00</span>
{
*( InRoot$$Sections )
.any( +ro )
}
execute_data <span class="hljs-number">0x20000000</span> <span class="hljs-number">0x00010000</span>
{
.any ( +rw +zi )
}
ARM_LIB_HEAP +<span class="hljs-number">0</span> <span class="hljs-keyword">empty</span> <span class="hljs-number">0x400</span> {}
ARM_LIB_STACK <span class="hljs-number">0x20020000</span> <span class="hljs-keyword">empty</span> <span class="hljs-number">-400</span> {}
}
这里还是简单扯几点:
- 没有使用预处理器,比较Low逼;
- 就一个加载域、两个RO运行域(一个项链表运行域,一个根域)、一个RW+ZI运行域以及堆+栈的分配;
- 第三,参考以上两点~~~~
深入理解“FIXED”关键字
但是其实这里还是有一些技能点的~比如:根域的FIXED属性,你若把这个FIXED属性去掉,试试会发生什么???? 没错,链接器罢工了,16个错,看一下,主要是加载域和运行域的地址不匹配导致的错误;
原因是这样的:
- 任何MCU的分散加载必须有一个根区,这点我们在之前讲过;
- 根区是指加载域和运行域相同的地址的区(因为根区里面要放置C Library&分散加载相关代码,而分散加载本身不能被分散加载,所以根区的加载域与运行域必须相同);
- 程序的入口地址必须在根区中,因为程序的入口显然不能被分散加载,所以必须在根区中;
- 如果运行域基址与加载域基址相同则默认可以看做根区(这点很好理解,参考第二点,根区就是运行域与加载域相同的区);
- 加载域后续的执行域指定“+0”偏移,则这些执行域默认都为根区(因为地址上是连续的,基址又与加载域基址相同);
- 如果基址不相同,则需要使用FIXED关键字指定根区;
- FIXED关键字只能用于指定执行域,作用是确保执行域与加载域地址相同。
可能说了这么多还是比较费解,下面我们做一些实验来验证以上的罗里吧嗦看不懂的废话,做一下实验就都懂了:
实验一:
把所有不能被分散加载的代码内容放到与加载域地址相同的执行域里面去,如下:
load_rom 0x00000000 0x00080000 { vector_rom 0x00000000 0x2000 { *( vector_table, +first) *( InRoot KaTeX parse error: Expected 'EOF', got '}' at position 185: …hljs-ln-line"> }̲</div></div></l…Sections标号放人与加载域地址相同的执行域中,把不能被分散加载的部分与可以被分散加载的部分分开;编译&链接,OK,有兴趣的可以自己试一下。
实验二:
看如下分散加载,先说结果,也可以完全正常的编译&链接运行的,至于为什么,前面写过的:
load_rom 0x00000000 0x00080000 { vector_rom 0x00000000 0x400 { *( vector_table, +first) } execute_rom +0 { *( InRoot KaTeX parse error: Expected 'EOF', got '}' at position 369: …hljs-ln-line"> }̲</div></div></l…Sections) .ANY (+RO) } #if (defined( ram_vector_table)) VECTOR_RAM m_interrupts_ram_start EMPTY m_interrupts_ram_size { } #else VECTOR_RAM m_interrupts_start EMPTY 0 { } #endif RW_m_data m_data_start m_data_size-Stack_Size-Heap_Size { ; RW data .ANY (+RW +ZI) } ARM_LIB_HEAP +0 EMPTY Heap_Size { ; Heap region growing up } ARM_LIB_STACK m_data_start+m_data_size EMPTY -Stack_Size { ; Stack region growing down } } LR_m_usb_bdt m_usb_sram_start usb_bdt_size { ER_m_usb_bdt m_usb_sram_start UNINIT usb_bdt_size { * (m_usb_bdt) } } LR_m_usb_ram (m_usb_sram_start + usb_bdt_size) (m_usb_sram_size - usb_bdt_size) { ER_m_usb_ram (m_usb_sram_start + usb_bdt_size) UNINIT (m_usb_sram_size - usb_bdt_size) { * (m_usb_global) } }如上,比较专业的分散加载写法(不是我写的,小编我人懒,于是Copy了原厂的,不过跟我们之前写的简易分散加载结构是一样的,要点我们基本都讲到了,这里主要区别只是使用了预处理器的功能)
分散加载的部分暂时写到这里,一共写了6-7篇文章,当然还有很多内容没有覆盖到,以后我们碰到了再详细写几篇提高篇的内容,大部分基本原理讲清楚了,把这些内容消化掉,足够应付大多数应用了。
下面的文章我们会进入下一个大的专题,就是调试技术,也会有多篇文章,详细介绍深入的调试技巧。