• 1184查看
  • 0回复

[网络开发] S32K144启动流程分析

[复制链接]


该用户从未签到

发表于 29-8-2023 08:37:26 | 显示全部楼层 |阅读模式

汽车零部件采购、销售通信录       填写你的培训需求,我们帮你找      招募汽车专业培训老师


1. S32K144为NXP公司采用ARM内核(cortex-M4)IP和ARMv7-M架构集成的SOC。

S32K144启动流程分析w1.jpg

2. 内存映射图

2.1 S32K144芯片内存映射图(memory map)

S32K144启动流程分析w2.jpg

2.2 Cortex-M4内存映射图

S32K144启动流程分析w3.jpg

S32K144启动流程分析w4.jpg

Code,SRAM,RAM区域都能保存程序。ARM系统推荐使用Code段来保存执行程序。

3. 编程模型

3.1) Cortex-M4两种工作模式:

线程模式(Thread Model),应用程序正常执行的时候所在的模式,处理器每次reset重启后进入这个模式;异常处理模式(Handler Model),CPU异常处理的时候进入这个模式,当CPU执行完异常处理程序后会退回到Thread Model。

3.2) Cortex-M4两种特权级:

非特权级状态(Unprivileged),软件限制使用MSR和MRS指令,不能使用CPS指令。不能访问系统定时器,NVIC嵌套向量中断控制器和系统控制块。特权级状态(Privileged),能访问所有资源,使用所有指令。

3.3) Thread模式下,CONTROL寄存器控制软件执行在privileged或者unprivileged状态。unpriveleged 软件执行时可以通过 SVC 指令进行supervisor call进入privilieged software。

3.4) 处理器使用降栈。Thread mode下,CONTROL寄存器控制处理器使用main stack还是进程栈process stack。在Handler模式下,处理器只使用main stack。

3.5) 核寄存器(Core registers)

S32K144启动流程分析w5.jpg

R0-R12为通用寄存器

特殊寄存器:

R13是堆栈寄存器(stack Pointer),在Thread mode下,COTROL寄存器的bit[1]控制stack pointer作为Main Stack Pointer(MSP 这个是reset 值)还是Process Stack Pointer(PSP)

note:reset后,处理器装载0x00000000地址处的四字节值到MSP寄存器。也就是说系统启动的时候,0地址处存放的是MSP寄存器的值。

R14是连接寄存器(Link Register),他存储子程序、函数调用、异常处理程序时的返回信息。reset重启时,处理器设置LR寄存器的默认值为0xFFFFFFFF。

R15是程序计数器(Program Counter PC)。存储当前程序地址。reset重启的时候,处理器装载0x00000004地址处的值到PC指针。

程序状态寄存器(Program Status Register),包括应用程序状态寄存器(Application Program Status Register APSR)、中断程序状态寄存器(IPSR)、异常程序状态寄存器(EPSR)。

4. startup_S32K144.S源代码分析:

4.1 上电启动

根据上面的分析,reset后处理器从0x00000000地址处取四字节值到MSP寄存器,也就是取 __StackTop标号的值到MSP寄存器。然后处理器装载0x00000004地址处的值(Reset_Handler标号代表的值)到PC指针,也就是程序跳转到Reset_Handler标号处开始运行。

__isr_vector:    .long   __StackTop                                      /* Top of Stack */    .long   Reset_Handler                                   /* Reset Handler */

4.2 关闭CPU全局中断

通过汇编指令“cpsid i”,关闭 CPU 全局中断的目的是避免启动过程中中断的影响;因为此时中断向量表还未建立好,无法响应外设中断
Reset_Handler:    cpsid   i               /* Mask interrupts */
S32K144启动流程分析w6.jpg

4.3 清零R1-R12通用寄存器

每次复位后, CPU 内核寄存器的值是随机不确定的,所以需要将其清零。

note 1: 为什么清零r1-r7寄存器用ldr伪指令,而清除r8-r12寄存器是要用mov指令 ?

r1-r7是low registers,r8-r12是hight registers,参考ARMv7-M Architecture手册:

大多数16位指令指令只能访问R0-R7这8个通用寄存器(low registers);只有小部分的指令能访问R8-R15寄存器(high registers)

note2: LDR R,label 和 LDR R,=label的区别

LDR 是ARM中的指令,也是伪指令。当用 LDR r, =imd ;r 为寄存器, imd为立即数LDR 是一条伪指令。编译器会根据 立即数的大小,决定用 ldr 指令或者是mov或mvn指令。当imd能用mov或者mvn操作时,就将它翻译成一条mov或mvn指令。当imd大于mov或mvn能够操作的数时,编译器会将imd存在一个内存单元中,然后再用一条ldr指令加载这个内存单元的的值到寄存器中。

LDR r, label 和 LDR r, =label的区别:
LDR r, =label 会把label表示的值加载到寄存器中,而LDR r, label会把label当做地址,把label指向的地址中的值加载到寄存器中。譬如 label的值是 0x8000, LDR r, =label会将 0x8000加载到寄存器中,而LDR r, label则会将内存0x8000处的值加载到寄存器中。
    /* Init the rest of the registers */    ldr     r1,=0    ldr     r2,=0    ldr     r3,=0    ldr     r4,=0    ldr     r5,=0    ldr     r6,=0    ldr     r7,=0    mov     r8,r7    mov     r9,r7    mov     r10,r7    mov     r11,r7    mov     r12,r7
4.4 初始化堆栈

ARM Cortex M 系列 CPU 内核有 MSP 和 PSP 两个 32-bit 的堆栈,由于中断和异常处理时使用 MSP 所以必须在发生中断/异常之前将其初始化,其初始化值来自默认向量表的 0 地址偏移,即 0x0000 地址存放的 4 个字节。

note: __StackTop是S32K144_64_flash.ld链接器脚本中定义标号,也就是0x20007000地址处,在SRAM中
    /* Initialize the stack pointer */    ldr     r0,=__StackTop    mov     r13,r0

/* S32K144_64_flash.ld */
/* Specify the memory areas */MEMORY{  /* Flash */  m_interrupts          (RX)  : ORIGIN = 0x00005000, LENGTH = 0x00000400  m_flash_config        (RX)  : ORIGIN = 0x00005400, LENGTH = 0x00000010  m_text                (RX)  : ORIGIN = 0x00005410, LENGTH = 0x0007FBF0
  /* SRAM_L */  m_data                (RW)  : ORIGIN = 0x1FFF8000, LENGTH = 0x00008000
  /* SRAM_U */  m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00007000}
//...
/* Initializes stack on the end of block */  __StackTop   = ORIGIN(m_data_2) + LENGTH(m_data_2);  __StackLimit = __StackTop - STACK_SIZE;  PROVIDE(__stack = __StackTop);

4.5 系统初始化

在完成了以上堆栈初始化之后, CPU 就可以运行 C 代码了,所以此时通过调用定义在工程 SDK->platform->device->S32K144->startup 目录下的 system_S32K144.c 中的系统初始化函数--SystemInit():根据工程配置完成:1)CPU 内核 FPU 配置和使能(如果创建应用工程时选择浮点数运算使用硬件 FPU)

2)关闭看门狗(默认配置)

3)使能 CPU 内核指令缓冲(I-Cache)等 MCU 硬件平台配置。

#ifndef __NO_SYSTEM_INIT    /* Call the system init routine */    ldr     r0,=SystemInit    blx     r0#endif
我们么有选择使用FPU和CPU内核指令缓冲,所以主要介绍关闭看门狗操作:

4.5.1 配置CNT(Watchdog Counter Register)寄存器

往看门狗模块的CNT(Watchdog Counter Register)寄存器写入0xD928C520(FEATURE_WDOG_UNLOCK_VALUE)。为什么要写入这个值?

查看S32芯片手册:

Unlock sequence of writing 0xC520 and then 0xD928 for allowing updates to write-once configuration bits .

意思就是看门狗的默认配置是lock的,如果要改变看门狗的配置首先需要解锁:往CNT寄存器中写入

S32K144启动流程分析w7.jpg

0xD928C520,然后再通过CS(Watchdog Control and Status Register)来配置看门狗模块。

4.5.2 配置CS(Watchdog Control and Status Register)

主要配置CS寄存器的四个功能

S32K144启动流程分析w8.jpg

S32K144启动流程分析w9.jpg

S32K144启动流程分析w10.jpg

S32K144启动流程分析w11.jpg

4.5.3 配置看门狗TOVAL寄存器

TOVAL是一个16位的超时寄存器(65535),也就是说是timeout为65535个时钟周期,到点没有喂狗则产生看门狗复位。


/* system_S32K144.c */
/*FUNCTION********************************************************************** * * Function Name : SystemInit * Description   : This function disables the watchdog, enables FPU * and the power mode protection if the corresponding feature macro * is enabled. SystemInit is called from startup_device file. * * Implements    : SystemInit_Activity *END**************************************************************************/void SystemInit(void){/**************************************************************************/                      /* FPU ENABLE*//**************************************************************************/#ifdef ENABLE_FPU  /* Enable CP10 and CP11 coprocessors */  S32_SCB->CPACR |= (S32_SCB_CPACR_CP10_MASK | S32_SCB_CPACR_CP11_MASK);#ifdef  ERRATA_E6940  /* Disable lazy context save of floating point state by clearing LSPEN bit   * Workaround for errata e6940 */  S32_SCB->FPCCR &= ~(S32_SCB_FPCCR_LSPEN_MASK);#endif#endif /* ENABLE_FPU */
/**************************************************************************/                      /* WDOG DISABLE*//**************************************************************************/
#if (DISABLE_WDOG)  /* Write of the WDOG unlock key to CNT register, must be done in order to allow any modifications*/  WDOG->CNT = (uint32_t ) FEATURE_WDOG_UNLOCK_VALUE;  /* The dummy read is used in order to make sure that the WDOG registers will be configured only   * after the write of the unlock value was completed. */  (void)WDOG->CNT;
  /* Initial write of WDOG configuration register:   * enables support for 32-bit refresh/unlock command write words,   * clock select from LPO, update enable, watchdog disabled */  WDOG->CS  = (uint32_t ) ( (1UL << WDOG_CS_CMD32EN_SHIFT)                       |                            (FEATURE_WDOG_CLK_FROM_LPO << WDOG_CS_CLK_SHIFT) |                            (0U << WDOG_CS_EN_SHIFT)                             |                            (1U << WDOG_CS_UPDATE_SHIFT)                         );
  /* Configure timeout */  WDOG->TOVAL = (uint32_t )0xFFFF;#endif /* (DISABLE_WDOG) */
/**************************************************************************/                      /* Power mode protection *//**************************************************************************/#ifdef SYSTEM_SMC_PMPROT_VALUE  /* Power mode protection initialization */  SMC->PMPROT = SYSTEM_SMC_PMPROT_VALUE;#endif}

4.6 RAM初始化

接下来,启动文件会调用定义在 SDK->platform->devices 目录下 startup.c 中的init_data_bss()函数完成应用工程运行所需的 RAM 初始化。
    /* Init .data and .bss sections */    ldr     r0,=init_data_bss    blx     r0

在 startup.c 中通过申明外部变量(extern)的方式,可以引用定义在工程链接文件中的__DATA_ROM、__DATA_RAM、__DATA_END、__CODE_RAM、__CODE_ROM、__CODE_END、__BSS_START 和__BSS_END 符号,获得工程链接结果中.data 段(有初始化值)、 .bss 段(未初始化和初始化值为 0)的全局变量以及重定向到 RAM 中运行的.code 段代码/函数在 Flash 和RAM 中的起始地址和长度(结束地址-开始地址)。

然后再通过数据指针的方式实现全局变量初始化值和重映射代码从 Flash 到 RAM 中的拷贝以及.bss 段的清零:具体包括:

1)初始化.data 段

2)初始化.code 段

3)初始化.bss 段

4)将中断向量表从 Flash 拷贝到 RAM 中并

5)初始化 CPU 系统中断向量偏移地址,使其指向 RAM 中新的中断向量表(如果编译目标为 debug,编译结果存储在 Flash 中)。

note 1:  .code段不是代码段,.code段属于m_data域(RAM)用来存放重映射到RAM中的代码。

note 2: .text段才是代码段,.text段属于m_text域(ROM),存放的就是代码。

note 3:  为什么要将.data .bss 中断向量表拷贝到m_data域(RAM),因为.data中保存的是初始化过后的全局变量/静态局部变量.bss段中保存的是未初始化(初始化为0)的全局变量/静态局部变量,在系统运行的时候是需要改变的,而ROM是只读的,中断向量表同理。

note 4:  代码是不需要改变的,为什么也有部分的代码需要重映射到RAM中执行?-- 因为效率,因为相对于ROM来说,RAM的数据宽度较大,速度较快。

note 5: 怎么将代码重映射到RAM中?

通过上面的分析可知,在 S32K1xx 系列 MCU 的启动过程,会自动将定义在.code 段中的代码/函数从其 Flash 储存地址拷贝到 RAM 中的运行时地址。

只有将用户代码分配到 Flash 中的编译目标,即使用 S32K1xx_xx_flash.ld 链接文件的编译目标才存在代码重映射。若是将应用工程编译结果代码分配到 RAM 的编译目标(使用 S32K1xx_xx_ram.ld 链接文件),其编译的函数/代码本身就是储存在 RAM 中的,所以无需重映射 。

将 想 要 重 映 射 的 代 码 / 函 数 通 过 __attribute__((section(".code_ram")))指定到.code_ram 段 由于在应用工程链接文件中已经将用户段.code_ram 放置在了.code 段中,所以,我们只需要在 C 代码中,将想要重映射的代码/函数通过__attribute__ ((section(".code_ram")))指定到.code_ram 段即可。比如下面就是将 main()函数指定到.code_ram 段的具体实现:int __attribute__ ((section(".code_ram"))) main(void) 。

在 S32DS IDE 应用工程中,一个函数若没有特别指定,其将分配到.text 代码段。

需要注意是关键词-- __attribute__ ((section(".code_ram"))) 添加的位置,每个需要指定的函数都要添加这个关键词,因此,可以将其定义为一个宏比如 CODE_RAM 使用:#define CODE_RAM __attribute__ ((section(".code_ram")))然后,再将 CODE_RAM 放在定义的函数名前即可。
/*startup.c*/void init_data_bss(void){    uint32_t n;    /* Declare pointers for various data sections. These pointers     * are initialized using values pulled in from the linker file */    uint8_t * data_ram;    uint8_t * code_ram;    uint8_t * bss_start;    const uint8_t * data_rom, * data_rom_end;    const uint8_t * code_rom, * code_rom_end;    const uint8_t * bss_end;
    /* Addresses for VECTOR_TABLE and VECTOR_RAM come from the linker file */    extern uint32_t __RAM_VECTOR_TABLE_SIZE[];    extern uint32_t __VECTOR_TABLE[];    extern uint32_t __VECTOR_RAM[];
    /* Get section information from linker files */#if defined(__ICCARM__)    /* Data */    data_ram        = __section_begin(".data");    data_rom        = __section_begin(".data_init");    data_rom_end    = __section_end(".data_init");
    /* CODE RAM */    #pragma section = "__CODE_ROM"    #pragma section = "__CODE_RAM"    code_ram        = __section_begin("__CODE_RAM");    code_rom        = __section_begin("__CODE_ROM");    code_rom_end    = __section_end("__CODE_ROM");
    /* BSS */    bss_start       = __section_begin(".bss");    bss_end         = __section_end(".bss");#else    extern uint32_t __DATA_ROM[];    extern uint32_t __DATA_RAM[];    extern uint32_t __DATA_END[];
    extern uint32_t __CODE_RAM[];    extern uint32_t __CODE_ROM[];    extern uint32_t __CODE_END[];
    extern uint32_t __BSS_START[];    extern uint32_t __BSS_END[];
    /* Data */    data_ram        = (uint8_t *)__DATA_RAM;    data_rom        = (uint8_t *)__DATA_ROM;    data_rom_end    = (uint8_t *)__DATA_END;    /* CODE RAM */    code_ram        = (uint8_t *)__CODE_RAM;    code_rom        = (uint8_t *)__CODE_ROM;    code_rom_end    = (uint8_t *)__CODE_END;    /* BSS */    bss_start       = (uint8_t *)__BSS_START;    bss_end         = (uint8_t *)__BSS_END;#endif
    /* Check if VECTOR_TABLE copy is needed */    if (__VECTOR_RAM != __VECTOR_TABLE)    {        /* Copy the vector table from ROM to RAM */        for (n = 0; n < (((uint32_t)__RAM_VECTOR_TABLE_SIZE)/sizeof(uint32_t)); n++)        {            __VECTOR_RAM[n] = __VECTOR_TABLE[n];        }        /* Point the VTOR to the position of vector table */        S32_SCB->VTOR = (uint32_t)__VECTOR_RAM;    }    else    {        /* Point the VTOR to the position of vector table */        S32_SCB->VTOR = (uint32_t)__VECTOR_TABLE;    }
    /* Copy initialized data from ROM to RAM */    while (data_rom_end != data_rom)    {        *data_ram = *data_rom;        data_ram++;        data_rom++;    }
    /* Copy functions from ROM to RAM */    while (code_rom_end != code_rom)    {        *code_ram = *code_rom;        code_ram++;        code_rom++;    }
    /* Clear the zero-initialized data section */    while(bss_end != bss_start)    {        *bss_start = 0;        bss_start++;    }}
     

4.7 打开CPU全局中断

在完成 RAM 初始化和中断向量表初始化后,就可以打开 CPU 全局中断,响应外设中断了;打开 ARM Cortex M 系列 CPU 内核的全局中断通过汇编语句--“cpsie i”完成。
    cpsie   i               /* Unmask interrupts */

4.8 跳转到应用程序 main()函数在完成以上准备工作之后,启动过程的最后一步是跳转到应用程序 main()函数.

    bl      main

5. 相关应用

从bootloader跳转到application的时候,要设置应用中R13寄存器中的堆栈地址,然后直接跳转到app中的Reset_Handler执行:bootup_application(0x5000, 0x5004)

同时需要修改app程序中链接器脚本中的连接地址
MEMORY{  /* Flash */  m_interrupts          (RX)  : ORIGIN = 0x00005000, LENGTH = 0x00000400  m_flash_config        (RX)  : ORIGIN = 0x00005400, LENGTH = 0x00000010  m_text                (RX)  : ORIGIN = 0x00005410, LENGTH = 0x0007FBF0
  /* SRAM_L */  m_data                (RW)  : ORIGIN = 0x1FFF8000, LENGTH = 0x00008000
  /* SRAM_U */  m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00007000}

void bootup_application(uint32_t appEntry, uint32_t appStack){    static void (*jump_to_application)(void);    static uint32_t stack_pointer;
    //shutdown_drivers();
    jump_to_application = (void (*)(void))appEntry;    stack_pointer = appStack;    S32_SCB->VTOR = APP_IMAGE_START;    //__set_MSP(stack_pointer);    __asm volatile ("MSR msp, %0\n" : : "r" (stack_pointer) : "sp");    //__set_PSP(stack_pointer);    __asm volatile ("MSR psp, %0\n" : : "r" (stack_pointer) : "sp");    jump_to_application();}

S32K144启动流程分析w12.jpg

参考文档:


    1.1 Cortex-M4 Devices Generic User Guide

    1.2 ARM Cortex-M4 Processor Technical reference manal

    1.3 ARMv7-M Architecture

    1.4 S32K1xx Series Reference Manual

    1.5 汽车电子expert成长之路公众号文章




该用户从未签到

发表于 18-3-2025 10:07:08 | 显示全部楼层
针对S32K144启动流程的分析如下:

S32K144是NXP公司采用ARM Cortex-M4内核和ARMv7-M架构集成的SOC,其启动流程复杂而关键。

首先,内存映射图是理解芯片内存布局的基础。S32K144的内存映射包括Code段、SRAM和RAM区域。ARM系统推荐将执行程序保存在Code段,以保证程序执行的稳定性和可靠性。

其次,Cortex-M4有两种工作模式:线程模式和处理器异常模式。在应用程序正常执行时,处于线程模式。在该模式下,启动流程涉及初始的系统设置、内存初始化等。具体的启动流程还会涉及中断控制器、时钟系统等的初始化。

启动流程的分析还需要结合具体的编程模型和开发工具链,以确保系统正确、高效地启动和运行。建议详细查阅S32K144的技术手册和相关文档,进行深入的研究和理解。
回复 支持 反对

使用道具 举报



该用户从未签到

发表于 18-3-2025 10:07:08 | 显示全部楼层
针对您关于S32K144启动流程的分析请求,回复如下:

S32K144作为NXP公司采用ARM Cortex-M4内核和ARMv7-M架构的SOC,其启动流程分析如下:

1. 启动流程初始于芯片复位,进入已知状态,如初始寄存器值设定等。
2. 根据内存映射图,系统初始化RAM、SRAM及Code区域。ARM系统推荐将执行程序保存在Code段。
3. Cortex-M4有两种工作模式:线程模式和处理器异常模式。在应用程序正常执行时,处于线程模式。在该模式下,CPU执行指令、处理数据等。此外,还需配置相关硬件模块,如时钟、中断等。

具体的启动流程涉及到更细节的设置和初始化,包括中断向量表设置、堆栈初始化等。为确保系统正常运行,还需进行低电平硬件的初始化,如GPIO、时钟系统等。这些步骤对于确保系统稳定性和性能至关重要。
回复 支持 反对

使用道具 举报

快速发帖

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|手机版|小黑屋|Archiver|汽车工程师之家 ( 渝ICP备18012993号-1 )

GMT+8, 19-8-2025 02:19 , Processed in 0.514247 second(s), 36 queries .

Powered by Discuz! X3.5

© 2001-2013 Comsenz Inc.