Pixhawk Bootloader Notes

Bootloader的代码在GitHub上。

Bootloader的代码中有很多编译目标,其中包括PX4FMUv2、PX4IO、PX4FLOW等,分别对应Pixhawk系列的各种产品和第三方的产品。从这里也可以看出,Pixhawk的系列产品主要使用两个系列的处理器,也就是STM32F4XXX和STM32F1XXX。针对STM32F4XXX和STM32F1XXX,分别有两个Makefile来负责构建,也就是Makefile.f1和Makefile.f4,具体针对哪个开发板是在环境变量TARGET_HW来指定的,同时还通过LINKER_FILE指定了所用的链接器脚本。Bootloader根目录下的Makefile负责根据具体编译目标设定环境变量,进而将不同的构建命令分发到Makefile.f1或者Makefile.f4去。

  • PX4FMU:第一版PX4FMUv2部分的固件,已经没有产品;
  • PX4FMUv2:第二版PX4FMUv2部分的固件;
  • PX4IO:PX4IO部分的固件;
  • PX4FLOW:Pixhawk配套的光流模块;
  • PX4AEROCORE:Gumstix开发的Pixhawk衍生板,主要是可以通过UART或者SPI连接Gumstix Overo COM这个Linux嵌入板;
  • PX4DISCOVERY:Pixhawk的开发板;
  • PX4MAVSTATION:

下面,分别针对Pixhawk中的两个处理单元PX4FMUv2和PX4IO来分析。

PX4FMUv2 Bootloader

芯片启动配置

PX4FMUv2使用的芯片是STM32F427VIT6,它内置2MB Flash和256KB RAM,根据ST提供的DM00031020中3.4 Embedded Flash memory in STM32F42xxx and STM32F43xxx的说法和Table 6的图表,内置Flash被映射到从0x08000000开始到0x081FFFFF的2MB区域内,并组织为两个Bank。而根据DM00031020 Reference Manual中2.3.1 Embedded SRAM的说法,SRAM的起始地址是0x20000000。STM32F42xxx系列处理器除了256KB的System SRAM外,还有4KB的Backup SRAM。Backup SRAM同Battery Backup Domain有关,会在Standby Mode下保持内容。

STM32F427VIT6属于Cortex M3系列处理器,按照DUI0552A Cortex M3 Devices Generic User Guide中2.3.2 Exception types的说法,在系统启动时或者热复位时,会发出reset异常,进而去执行vector table中的reset handler,所以,系统的初始化代码应该写在reset handler中。DUI0552A 2.3.4 Vector table讲到,vector table包含复位后堆栈指针的地址和针对所有异常的exception vector。在系统复位时,vector table固定在0x00000000,但是特权软件可以通过VTOR寄存器将其位置指定到其他地址。

从上面的信息可知,系统启动后,应该是从0x00000000去找vector table,然后执行里面的reset handler。但是,STM32F427VIT6的Embbeded Flash位于0x08000000,系统又是如何执行的呢?

阅读DM00071990 Figure 19. Memory map可知,地址0x00000000会在启动时根据BOOT0和BOOT1的定义映射为Embbeded Flash、System Memory或者Embedded SRAM的别名。而按照DM00031020 2.4 Boot Configuration里的Table 2的说法,STM32F4XXX里BOOT0和BOOT1引脚的配置含义如下:

  • BOOT1任意,BOOT0=0:从Embbeded Flash启动;
  • BOOT1=0,BOOT0=1:从System Memory启动,这里保存的是STM32出厂时内置的bootloader,这个bootloader可以用来烧写内部Flash,详细信息可以参考AN2606;
  • BOOT1=1,BOOT0=1:从Embedded SRAM启动。

在参照Pixhawk 2.4.6的原理图,BOOT0(第6页右侧BOOT0引脚)通过下拉电阻接地,BOOT1(第1页左侧PB2-BOOT1引脚)也通过下拉电阻接地,所以BOOT0=0且BOOT1=0,PX4FMU总是从Embbeded Flash启动,此时0x00000000和0x08000000都指向Embbeded Flash。

NOTE:在STM32F4XXX系列芯片上,还可以选择从Bank 1启动还是Bank 2启动。Pixhawk没有使用这一功能,总是从Bank 1启动,所以这里不做展开,具体细节可以参考DM00031020 2.4 Boot Configuration。

Bootloader的代码结构和启动流程

搞清了芯片规定的启动入口,我们就来看看bootloader是如何组织和执行的。

整个PX4FMUv2 Bootloader的代码由三个部分组成:

  • libopencm3库:这个库的目标是为各种ARM Cortex-M3微处理器提供开源的固件库,支持的范围包括ST STM32、Atmel SAM3U等。它的代码分为公共文件和针对不同芯片的文件,每个芯片都有自己的Makefile。对于PX4FMU使用的STM32F4系列芯片来说,其使用的是libopencm3\lib\stm32\f4\Makefile。libopencm3库编译后,会在libopencm3\lib目录下生成针对每个芯片的*.a文件和对应的链接器脚本*.ld文件。不过bootloader没有使用这个链接器脚本,也许这个链接器脚本就是参考性质的。libopencm3在其libopencm3\lib\cm3\vector.c文件中定义了至关重要的vector_table表,这张表被指定到.vectors这个section中,最终会被bootloader的链接器脚本放置在Embbeded Flash的起始位置。vector.c中定义了reset_handler,在里面调用了bootloader定义的main
  • STM32F1和STM32F4通用的代码,包括
    • bl.c:负责提供bootloader协议的处理功能,其中最重要的函数是bootloader,该函数由main循环调用处理同上位机的通信。
    • cdcacm.c:负责提供usb通信功能
    • usart.c:负责提供串口通信功能
  • STM32F4专用的代码
    • main_f4.c:提供了main函数的定义,如果不执行bootloader的任务,那么会通过jump_to_app跳转到系统执行,跳转的地址由APP_LOAD_ADDRESS定义,对于PX4FMUV2来说,是0x08004000。

由于采用了STM32F4XXX系列芯片,PX4FMUv2使用stm32f4.ld这个链接器脚本。在这个脚本中,定义内存区域为从0x08000000开始的16KB Flash区和从0x20000000开始的128KB SRAM区。可以看出,它仅使用了2MB Flash的前16KB和256KB SRAM的前128KB(这个说法其实不准确,因为256KB SRAM中包括了64KB的CCM,而CCM其实被映射到另一块地址,同SRAM不连续)。0x08000000加上16KB后,就是0x08004000,也就是PX4FMU的代码紧接着bootloader的代码。

从生成的ELF来看,reset_handler的地址就是位于从0x08000000开始的一块区域内,其中的跳转指令都指向0x08000000后续的区域内;而根据DM00046982中2.1.3 Core registers一节的说法(Page 18),reset后PC会被设置为0x00000004,指向reset handler。

下面节选reset_handler的汇编代码如下:

08001804 <reset_handler>:
 8001804:    2300          movs    r3, #0
 8001806:    4918          ldr    r1, [pc, #96]    ; (8001868 <reset_handler+0x64>)
 8001808:    4818          ldr    r0, [pc, #96]    ; (800186c <reset_handler+0x68>)
 800180a:    185a          adds    r2, r3, r1
 800180c:    4282          cmp    r2, r0
 800180e:    d204          bcs.n    800181a <reset_handler+0x16>
 8001810:    4817          ldr    r0, [pc, #92]    ; (8001870 <reset_handler+0x6c>)
 8001812:    581c          ldr    r4, [r3, r0]
 8001814:    505c          str    r4, [r3, r1]
 8001816:    3304          adds    r3, #4
 8001818:    e7f5          b.n    8001806 <reset_handler+0x2>
 800181a:    4b16          ldr    r3, [pc, #88]    ; (8001874 <reset_handler+0x70>)
 800181c:    429a          cmp    r2, r3
 800181e:    d203          bcs.n    8001828 <reset_handler+0x24>
 ...

从上面可以看到,所有b*跳转指令的目标地址都位于0x08000000后面,也就是只要有一次跳转,就会将PC刷成0x08000000后面的地址,后面就都跑在0x08000000上了。

F-RAM的连接方式

F-RAM使用SPI2接口连接到FMU,但是,这部分在Firmware中初始化,Bootloader不管。

PX4IO Bootloader

手册资料中的名词解释

  • Embedded Flash:对于STM32F427VIT6来说,内嵌的Flash划分为两个Bank,被映射到0x08000000地址;它们通过ART Accelerator™缓存技术连接到处理器,该技术使得处理器可以在180MHz的频率直接执行Flash上的代码而无需等待;
  • Embedded SRAM:对于STM32F427VIT6来说,内嵌的SRAM共256KB,被分为112KB、16KB、64KB、64KB四个部分,其中有64KB是CCM;其中它们112KB、16KB、64KB三块在内存地址空间中连续,被映射到0x20000000,而64KG的CCM被映射到独立的位置(需要查一下)。有时,Embedded SRAM的概念还会包含额外4KB的Backup SRAM,不过通常会通过256+4的写法明确指出。
  • CCM (Core Coupled Memory):直接连接到处理器的高速内存。
  • System Memory:是一块内部的ROM,用于保存ST出厂时预置的内部bootloader,这个bootloader可以通过可用的串行接口(USART、CAN、USB、I2C、SPI等)将固件烧写到Embedded Flash中。
  • Backup SRAM:大小有4KB,只能由CPU访问,在Standby模式下也会保留其中的内容。

stm32f4.ld注解

/************************************************************************
 *
 *   Copyright (c) 2012-2014 PX4 Development Team. All rights reserved.
 *   Copyright (c) 2010 libopencm3 project (Uwe Hermann, Stephen Caudle)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * LICENSE NOTE FOR EXTERNAL LIBOPENCM3 LIBRARY:
 *
 *   The PX4 development team considers libopencm3 to be
 *   still GPL, not LGPL licensed, as it is unclear if
 *   each and every author agreed to the LGPS -> GPL change.
 *
 ***********************************************************************/

/**
 * @file stm32f4.ld
 *
 * Linker script for ST STM32F4 bootloader (use first 16K of flash, all 128K RAM).
 * 这里提到的all 128K RAM不正确,STM32F427VIT6有192KB的普通SRAM(也就是刨除了64KB的CCM)。
 *
 * @author Uwe Hermann <[email protected]>
 * @author Stephen Caudle <[email protected]>
 */

/* Define memory regions. */
/*
 * 链接器默认使用全部的内存地址,但是嵌入式系统通常的内存分布都依赖于芯片,所以可以使用MEMORY
 * 命令来定义内存区域。
 */
MEMORY
{
    rom (rx)  : ORIGIN = 0x08000000, LENGTH = 16K
    ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}

/*
 * VMA影响代码中指令使用的地址,例如跳转到哪里之类;
 * LMA影响代码被加载到哪里,对于生成bin来说,就是在bin中怎么排列。
 */

/* Enforce emmission of the vector table. */
/* 强制声明vector_table为其他模块提供的外部符号,它的定义在libopencm3中。 */
EXTERN (vector_table)

/* Define sections. */
SECTIONS
{
    . = ORIGIN(rom);    /* 将当前VMA定义为rom区域的起点,也就是0x08000000 */
                        /* 由于没有用AT命令单独定义LMA,所以维持LMA同VMA的 */
                        /* 偏移量,现在是0x0,也就是现在LMA也是0x08000000 */
    .text : {
        *(.vectors)     /* Vector table 中断向量表 */
        *(.text*)       /* Program code */
        . = ALIGN(4);
        *(.rodata*)     /* Read-only data */
        . = ALIGN(4);
        _etext = .;     /* _etext定义为当前的VMA */
    } >rom              /* 分配到rom这个region */

    /* C++ Static constructors/destructors, also used for __attribute__
     * ((constructor)) and the likes */
    .preinit_array : {
        . = ALIGN(4);
        __preinit_array_start = .;
        KEEP (*(.preinit_array))
        __preinit_array_end = .;
    } >rom
    .init_array : {
        . = ALIGN(4);
        __init_array_start = .;
        KEEP (*(SORT(.init_array.*)))
        KEEP (*(.init_array))
        __init_array_end = .;
    } >rom
    .fini_array : {
        . = ALIGN(4);
        __fini_array_start = .;
        KEEP (*(.fini_array))
        KEEP (*(SORT(.fini_array.*)))
        __fini_array_end = .;
    } >rom

    . = ORIGIN(ram);    /* 将当前VMA定义为ram区域的起点,也就是0x20000000 */

    .data : AT(_etext) {    /* 将data的LMA定义为_etext,也就是紧接着代码区 */
            _data = .;      /* 定义_data为data的VMA */
            *(.data*)       /* Read-write initialized data */
            . = ALIGN(4);
            _edata = .;
    } >ram
    _data_loadaddr = LOADADDR(.data);   /* 定义_data_loadaddr为data的LMA */

        .bss : {
                *(.bss*)        /* Read-write zero initialized data */
                *(COMMON)
                . = ALIGN(4);
                _ebss = .;
        } >ram AT >rom

        /*
         * The .eh_frame section appears to be used for C++ exception handling.
         * You may need to fix this if you're using C++.
         */
        /DISCARD/ : { *(.eh_frame) }

        . = ALIGN(4);
        end = .;
}

/* 定义stack的位置为0x20020000 */
PROVIDE(_stack = 0x20020000);

reset_handler注解

void WEAK __attribute__ ((naked)) reset_handler(void)
{
    volatile unsigned *src, *dest;
    funcp_t *fp;

    /* 将.data从Embbeded Flash拷贝到Embbeded SRAM */
    for (src = &_data_loadaddr, dest = &_data;
        dest < &_edata;
        src++, dest++) {
        *dest = *src;
    }

    /* 清空.bss */
    while (dest < &_ebss) {
        *dest++ = 0;
    }

    /* might be provided by platform specific vector.c */
    /* 对于stm32f4来说,在vector_chipset.c中开启浮点处理器 */
    pre_main();

    /* Constructors. */
    for (fp = &__preinit_array_start; fp < &__preinit_array_end; fp++) {
        (*fp)();
    }
    for (fp = &__init_array_start; fp < &__init_array_end; fp++) {
        (*fp)();
    }

    /* Call the application's entry point. */
    /* 这个入口由libopencm3的使用者提供,例如main_f4.c */
    main();

    /* Destructors. */
    for (fp = &__fini_array_start; fp < &__fini_array_end; fp++) {
        (*fp)();
    }

}

PX4IO

results matching ""

    No results matching ""