一、概述
S32K3 系列芯片有 4 种存储器:Flash、 SRAM、TCM 和 Cache 存储器。 S32K3 系列芯片还内置了一些带有专用存储器的模块,如 EMAC 和 CAN。Flash 专用于编写代码和存储数据。 此外,S32K3系列中的所有芯片都有一个 8KB 的 UTEST 扇区,用于存储重要配置或为应用程序保留信息。 S32K3系列芯片的 Flash program 存储器从 512KB 到 8MB不等。RAM 由 SRAM 和 TCM 组成。 SRAM 存储器的部分区域在 Standby 状态下可用。这意味着,在将MCU 设置为 Standby 模式后,该存储器的内容将被保留。 S32K3 系列芯片利用了 Arm Cortex M7架构的 TCM 特性,其主要目的是让一些重要数据在内核访问上有确定性的时间保证,避免访问中出现任何延迟。实时操作系统可以利用该特性。
引入概念:ARM7与ARM-CortesM7的区别:
ARM7:
架构系列: ARM7 属于 ARMv4 和 ARMv5 架构系列。
应用领域: ARM7 多用于嵌入式系统,例如一些较老的移动设备和嵌入式系统中的微控制器。
特征: ARM7 架构具有较低的功耗和较小的指令集,适用于资源受限的环境。
ARM7没有MMU(内存管理单元),只能叫做MCU(微控制器),不能运行诸如Linux、WinCE等这些现代的多用户多进程操作系统,因为运行这些系统需要MMU,才能给每个用户进程分配进程自己独立的地址空间。ucOS、ucLinux这些精简实时的RTOS不需要MMU,当然可以在ARM7上运行。
Cortex-M7:
架构系列: Cortex-M7 属于 ARMv7-M 架构系列。
应用领域: Cortex-M7 主要用于高性能嵌入式系统,例如实时操作系统(RTOS)和一些需要高性能的嵌入式应用。
特征: Cortex-M7 架构提供了更强大的性能,支持更大的存储器范围,具有更高的时钟频率,适用于对性能要求较高的嵌入式系统。
到了ARMv7架构的时候开始以Cortex来命名,并分成Cortex-A、Cortex-R、Cortex-M三个系列。三大系列分工明确:“A”系列面向尖端的基于虚拟内存的操作系统和用户应用;“R”系列针对实时系统;“M”系列对微控制器。简单的说Cortex-A系列是用于移动领域的CPU,Cortex-R和Cortex-M系列是用于实时控制领域的MCU。
所以看上去ARM7跟Cortex-M很像,因为他们都是MCU,但确是不同代不同架构的MCU(Cortex-M比ARM7高了三代!),所以性能也有很大的差距。此外,Cortex-M系列还细分为M0、M3、M4和超低功耗的M0+,用户依据成本、性能、功耗等因素来选择芯片。
二、通过__attribute__指定数据或函数到固定地址
.c文件里面指定参数
volatile uint8 __attribute__ ((section(".Demstate1")))buffer3x[200] = {0,0};
链接文件
int_sram_Dem : ORIGIN = 0x2040DF00, LENGTH = 0x00001000
{
. = ALIGN(4);
KEEP(*(.Demstate1))
. = ALIGN(4);
} > int_sram_Dem
测试结果
此时注意一下,函数的放置方式也是这样的。
三、S32DS通过#Program section来将数据或者函数放置在特定的地址内
memmap.h文件
#ifdef ADC_START_SEC_VAR_CLEARED_32
/**
* @file Adc_MemMap.h
*/
#undef ADC_START_SEC_VAR_CLEARED_32
#define ENTERED_ADC_START_SEC_VAR_CLEARED_32
#ifndef MEMMAP_MATCH_ERROR
#define MEMMAP_MATCH_ERROR
#else
#ifndef ADC_STOP_SEC_VAR_CLEARED_32
#error "MemMap.h, no valid matching start-stop section defined."
#endif
#endif
/**
* @file Adc_MemMap.h
*/
#undef MEMMAP_ERROR
#pragma GCC section bss ".Demstate1X"
// #pragma GCC section data ".Demstate1X"
#endif
#ifdef ADC_STOP_SEC_VAR_CLEARED_32
/**
* @file Adc_MemMap.h
*/
#ifdef ENTERED_ADC_START_SEC_VAR_CLEARED_32
#undef ENTERED_ADC_START_SEC_VAR_CLEARED_32
#else
#error "MemMap.h, no valid matching start-stop section defined."
#endif
#ifdef MEMMAP_MATCH_ERROR
#undef MEMMAP_MATCH_ERROR
#endif
#undef ADC_STOP_SEC_VAR_CLEARED_32
/**
* @file Adc_MemMap.h
*/
#undef MEMMAP_ERROR
#pragma GCC section bss
#endif
.c文件
#define ADC_START_SEC_VAR_CLEARED_32
#include "Hw_Dem_Memmap.h"
//uint32 FeeNumbersxxx[2] = {0xAA,0xBB};
uint32 FeeNumbersxxx[2];
#define ADC_STOP_SEC_VAR_CLEARED_32
#include "Hw_Dem_Memmap.h"
链接文件
int_sram_Dem1 : ORIGIN = 0x2040EF00, LENGTH = 0x00001000
.DemstateX1 :
{
. = ALIGN(4);
*(.Demstate1X)
. = ALIGN(4);
} > int_sram_Dem1
测试结果
第二种方式
.c里面设置
#pragma GCC section bss ".dtc_frezz_data"
DTC_FRZ_DATA_T DTC_FrzData[DTC_NUM];
#pragma GCC section bss "default"
#pragma GCC section bss ".dtc_state_data"
DTC_STA_DATA_T DTC_StaData[DTC_NUM];
#pragma GCC section bss "default"
链接文件里面设置
CPU0_SRAM0_DEMDTCDATA : ORIGIN = 0x20417000, LENGTH = 0x00000E00 /* 3584Bytes */
CPU0_SRAM0_DEMDTCSTATE : ORIGIN = 0x20417E00, LENGTH = 0x00000200 /* 512Bytes */
/*SRAM0 For Dtc data*/
.CPU0_DTC_FREZZDATA :
{
. = ALIGN(4);
CPU0_DTC_FREZZDATA_START = .;
. = ALIGN(4);
*(.dtc_frezz_data)
. = ALIGN(4);
CPU0_DTC_FREZZDATA_STOP = .;
}>CPU0_SRAM0_DEMDTCDATA
/*SRAM0 For Dtc state*/
.CPU0_DTC_STATEDATA :
{
. = ALIGN(4);
CPU0_DTC_FREZZDATA_START = .;
. = ALIGN(4);
*(.dtc_state_data)
. = ALIGN(4);
CPU0_DTC_FREZZDATA_STOP = .;
}>CPU0_SRAM0_DEMDTCSTATE
结果如下
四、S32K3存储器指南
1、特性
需要注意的一个重要特性是,S32K3 系列芯片中的所有存储器都具有错误检测和纠错码(ECC)功能。
2、Flash存储器
Flash 存储器中有一些区域受到保护,可供应用程序内核使用。这些区域仅适用于 HSE_B 内核。
Flash 存储器有 3 种操作模式。当芯片在用户模式(User mode)下工作时,可以访问 Flash 存储器阵列以执行读取、编程或擦除操作。用户模式是 Flash 存储器的默认工作模式。所有寄存器都具有读写权限。在低功耗模式下,Flash 存储器的电源被关闭,无法访问,在该模式下不允许操作。最后,在 Utest 模式下,可以验证 Flash 存储器的完整性。
Flash 存储器可以通过单次读取、两次读取或四次读取功能在不同块之间执行多次读取,在多核芯片中,如果有多个线程并行运行(在存储器的不同部分/块上),可以同时对这些线程进行两次读取或四次读取,该功能由内部控制,而不是由用户控制。它还具有“ 边读边写” (RWW)功能,可同时执行读取和写入操作(仅适用于在不同的块中执行操作的情况);例如,在S32K324 中,如果 Core 0 应用程序正在块 0 中执行写入操作,与此同时,Core 1 可以读取存储在 Data Flash 块中的数据。
在使用 Flash 存储器时,需要考虑 4 个重要的操作:
• 读取 Flash 存储器
• 锁定和解锁扇区或超级扇区
• 对 Flash 进行编程
• 擦除 Flash 存储器
1、读取
复位后,Flash 存储器处于默认状态,控制器可以读取阵列和寄存器。读取 Flash 会返回 256 位长度的数据,读取寄存器将返回 32 位长度的数据。对于读取操作,不必考虑锁定或解锁扇区。读取操作由 PFlash 控制器执行,该控制器是系统总线和嵌入式 Flash 存储器之间的接口。
2、写入或编程
最小编址单位为 2 个字(64 位)、8字节,数据必须 64 位对齐。最多可同时编程 4 页,1 页为 8 个字(256 位)。这意味着一次编程操作最多可以更改 1024 位。在执行编程操作或写入操作时,存储器会计算并存储 ECC 位。 ECC 按 64 位双字处理。 ECC 需要 8 位。
编程操作将位的逻辑值从 1 改为 0,这意味着不允许从 0 改为 1 的编程操作,并且在执行任何编程操作之前需要擦除 Flash 存储器。当 Data Flash 用于 EEPROM 仿真时,恩智浦批准的驱动程序可以在 64 位 ECC 段中进行重复编程(over-programming),允许对同一位置最多进行3 次重复编程,无需在扇区中执行擦除操作。此功能可用于更改数据记录的记录状态,而无需事先擦除数据。 EEPROM 仿真技术经常用到此功能。需要注意,它仅适用于恩智浦批准的驱动程序,请咨询适用于 S32K3 芯片的 RTD 软件,了解可用的 FEE 驱动程序。
在执行编程操作之前,必须先解锁包含指定地址的扇区。对锁定的扇区或超级扇区执行编程操作,操作会失败,并且 MCRS[PEP]位会报告错误。
编程顺序流程图如下
3、擦除
擦除操作是指将扇区或块中的所有位设置为 1 的流程。最小擦除单位可以是扇区,扇区大小为8KB。要擦除扇区或块,必须在擦除操作之前将其解锁。擦除流程也会清除 ECC 位。
函数示例
4、锁定和解锁扇区或超级扇区
块由大小分别为 8KB 和 64KB 的扇区和超级扇区组成。使用锁定功能可保护这些扇区不受写入或擦除操作的影响。块最后的 256 KB 部分具有扇区保护功能,而其余部分则具有超级扇区保护
功能。 Data Flash 具有扇区保护功能,UTEST 扇区具有独立的扇区编程保护。 S32K3 系列芯片
的某些成员由于内存大小原因而无法提供超级扇区保护。
下图展示了 1MB 块的扇区和超级扇区分布。
超级扇区的锁定和解锁流程由 PFCBLKn_SSPELOCKn 寄存器控制,扇区的锁定和解锁流程由PFCBLKn_SPELOCKn 寄存器控制。 PFCBLKn_SSPELOCKn 寄存器有 12 个可用位,其中每个位对应于一个超级扇区,同样,PFCBLKn_SPELOCKn 寄存器有 32 个可用位,对应于 32 个可用扇区。如果需要执行解锁流程来允许编程或擦除操作,则应将相应的寄存器
PFCBLKn_SSPELOCKn 或 PFCBLKn_SPELOCKn 从 1 改为 0。向 PFCBLKn_SPELOCKn 或PFCBLKn_SSPELOCKn 的任何位写入 1 将锁定扇区或超级扇区,以防编程和擦除操作。
PFCBLKn_SSPELOCKn 和 PFCBLKn_SPELOCKn 寄存器所有位的 out of reset 值为 1,这意味着所有扇区在复位后都受到保护,不受编程和擦除操作的影响。
3、UTEST扇区
S32K3 系列芯片的所有成员都具有 8KB UTEST 扇区。在该扇区中,可以存储有关应用程序的重要信息,例如版本号、永久参数、配置(启动或应用)等。在 UTEST 扇区中,有一些区域是为 SoC保留的,供恩智浦使用。如需了解可用区域的更多详细信息,请参见《 S32K3 参考手册》中随附的S32K3xx_DCF_client.xlsx。
当写入测试模式密封(Test mode seal)时,UTEST 扇区是一个 OTP(一次性可编程)空间。测试模式密封在 UTEST 扇区中分配,为安全起见,它的值被编写为 0x5A4B3C2D,这意味着只有新的数据或配置可以附加在 UTEST 扇区中,不允许擦除。
在 UTEST 扇区中写入数据的流程与在其他块中写入数据的流程相同。解锁流程也是一样的,但不同之处在于,UTEST 扇区有自己的 PFCBLKU_SPELOCK[SLCK] 寄存器,用于在编程操作中锁定或解锁扇区。如前所述,UTEST 扇区是 8KB 扇区,因此 PFCBLKU_SPELOCK 寄存器中只有 1 个位可更改。按照扇区保护逻辑,如果 SLCK 位设置为 0,则可对 UTEST 扇区进行编程操作。
下面的示例是裸机代码的一部分,介绍如何解锁 UTEST 扇区,此示例可用于其他块。
4、TCM
紧耦合存储器(TCM)是采用 Arm(R)Cortex M7 架构的存储器,其主要特点是与内核有专用连接。该存储器分为 Instruction TCM(I-TCM)和 Data TCM(D-TCM)。在 S32K3 系列芯片中,有两个与 Cortex M7 的专用连接,一个用于 I-TCM,另一个用于 D-TCM。每个 CM7 内核都有一个可用的 ITCM 和 DTCM 存储器,因此 TCM 存储器的大小和地址取决于 S32K3 产品上可用的内核数量及其配置(锁步或解耦)
TCM 是内存映射的,可以通过两类总线进行访问。一种是专用的内核连接总线,另一种是 Backdoor访问总线(供 AHBS 总线上其他 Master 访问)。每次访问都有一个定义的 TCM 起始地址,结束地址由存储器大小决定.
对于专用的内核访问,I-TCM(Instruction-TCM)使用 64 位总线接口,D-TCM(Data-TCM)使用 32 位总线接口。任何其他的 Master(如 eDMA、其他内核(在解耦配置下)、 EMAC 和 HSE内核)都可以通过 32 位 AHB 接口访问 TCM 存储器。
对于锁步模式下的芯片,备用内核的 TCM 被添加到主内核,以扩大主内核的可访问大小。在解耦模式下,TCM 专用于每个内核。
TCM 可用作系统存储器,也可以用作内核专用存储器。当 TCM 用作多核芯片上的系统存储器时,所有启用的内核和非内核 master 都可以使用禁用的内核的 TCM。为了将禁用内核的 ITCM 和 DTCM用作系统存储器,需要对寄存器做一些配置。
下面的框图介绍了其他 master 访问 TCM 存储器(S32K32x 和 S32K34x 系列芯片)所用的路径。
由于 TCM 位于存储器映射中,并且有一个物理地址,用户可以决定在编译时要存储在 TCM 中的内容。使用 TCM 的一个好处是,它具有确定的访问时间(一个时钟周期),因此 TCM 可用于存储关键数据和代码,例如,频繁更新的变量、中断处理程序、数据处理等。这些数据由用户决定,而不是由作为 Cache 存储器的控制逻辑决定。与其他存储器一样,TCM 具有 ECC 保护功能,在芯片上电复位后使用前,用户必须对其进行初始化以避免 ECC 错误,在芯片上电复位阶段后,需要进行写入操作来设置初始 ECC 码字。在 S32K3 系列芯片中,内核可以通过直接访问和 Backdoor 访问方式对 ITCM 和 DTCM 进行初始化。
此外,DMA 可通过 Backdoor 对 DTCM 进行初始化,但由于 Backdoor 提供 32 位接入,DMA 无法对 ITCM 进行初始化(ITCM 需要 64 位写入以避免 ECC 错误)。例如,下面的代码描述了如何将 TCM 用作系统存储器,如何通过 M7_0 内核初始化 ITCM,以及如何通过 DMA 初始化 DTCM。重要提示:要初始化 DTCM,DMA 需要使用 Backdoor 地址才能访问 DTCM。
5、SRAM
S32K3 系列芯片可以包含 1 个到 3 个 SRAM 块。需要注意:TCM 存储器被视为 RAM 存储器的一部分。
每个 SRAM 块都有自己的 SRAM 控制器(PRAMC),支持对内核的快速读/写操作。 PRAM 控制器是系统总线和集成的 RAM 阵列之间的接口。系统总线支持 64 位数据,RAM 阵列支持 64 位数据+8 位 ECC。
在 SRAM 存储器中,有一个区域在微控制器处于 Standby 模式(Standby mode)时也可用。 SRAM 存储器将前 32 KB 分配给了 Standby SRAM。该区域由电源域 0 供电,因此在 Standby模式下,存储在 Standby 区域中的信息将被保留。 SRAM 存储器的其余部分由电源域 1 供电,仅在运行模式下可用。
与 TCM 存储器一样,所有 SRAM 存储器都必须初始化以避免 ECC 错误,也可以通过内核或 DMA进行初始化。如果省略了初始化步骤,那么对 SRAM 存储器的任何读取或写入操作都会导致不可纠正的 ECC 错误事件。
5.1、读取
无论数据大小,读取事件都可以配置为以**零等待状态(zero wait state)或一等待状态(one wait state)**的响应作为事件完成的表现。在将数据返回到系统总线之前,PRAM 控制器 Flow Trough Disable field PRCRx[FT-DIS]寄存器在读取事件上插入等待状态。当系统频率大于 120 MHz 时,等待状态的插入非常重要。此等待状态对写入事件没有任何影响。如需了解等待状态的更多信息,请参见《 S32K3 参考手册》的“ 时钟” 章节中的 Gasket 配置。
5.2、写入
写入操作能以 64 位或更少的位进行。当执行对齐 64 位写入时,写入操作以零等待状态在单相周期中执行。当执行小于 64 位的写入或未对齐写入时,将执行读取-修改-写入(RMW)操作,以执行写入并重新计算新的 ECC 代码。 RMW 操作将向写入流程插入一些周期,这意味着对于 SRAM存储器执行对齐 64 位写入比未对齐的小于 64 位的写入的性能更好。
读取-修改-写入可按照以下顺序进行解释:
1. PRAMC 在相应的读取数据中执行单次纠错(SEC)/两次错误检测(DED)。
2. 写入数据与之前的读取数据合并,只更改写入数据的位。
3. 根据新的 64 位生成新的 ECC 代码。
4. 新的双字和 ECC 被写入 RAM。
6、用例
6.1、Flash\TCM\SRAM存储器比较
S32K3 系列芯片的一个主要特性是 TCM 存储器实施。 TCM 存储器的一个优势是,处理任务或使用该存储器中存储的数据的时间是确定的,以避免在内核与 SRAM 或 Flash 存储器之间传输数据时出现任何延迟。
下面的用例演示了从 Flash 运行代码与从 TCM 和 RAM 存储器运行代码的主要区别。此外,该示例帮助用户了解如何运行加载到 TCM 或 RAM 而不是 Flash 存储器中的函数或数据。
链接文件
第一步是检查我们的 Linker 文件是否声明了 TCM、 Flash 和 SRAM 存储器的区域及其相应的大小。 Linker 文件上的存储器区域应如下所示:
我们需要在每个存储器区域中定义一些部分,用于加载函数或数据。对于该示例,我们将定义以下部分:
ITCM_code (0x0000_0000):ITCM 中用于加载和运行 TCM_Function 的部分。
DTCM_data (0x2000_0000):DTCM 中用于加载一些阵列和变量的部分。
SRAM_Function 将使用 SRAM 中的 standard .data 部分(起始地址为 0x2040_8000)。
Flash 函数将默认加载到 .text 部分。
Linker 文件将如下所示
coderom_start__ = .;
.ITCM_code : AT (__coderom_start__)
{
. = ALIGN(8);
coderam_start__ = .;
*(.ITCM_code*)
. = ALIGN(8);
coderam_end__ = .;
} > ITCM
. = __coderom_start__ + ( __coderam_end__ - __coderam_start__ );
coderom_end__ = .;
etext = ALIGN(8);
.DTCM_data :
{
. = ALIGN(4);
DTCM_data__ = .;
} > DTCM
.standby_ram :
{
*(.standby_ram)
} > SRAM0_STDBY
/* Due ECC initialization sequence __data_start__ and __data_end__ should be aligned on 8 bytes */
.data : AT (__etext)
{
. = ALIGN(8);
data_start__ = .;
*(vtable)
*(.data)
*(.SRAM_Function)
*(.data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array))
启动
为了避免 ECC 错误,ITCM、 DTCM 和 SRAM 进行了初始化。此外,需要将函数代码复制到 ITCM 和 SRAM。该流程根据 Linker 文件进行,并由 startup_code 进行处理。
STATIC_FORCEINLINE void __cmsis_cpu_init(void)
{
#if defined (__ECC_PRESENT) && (__ECC_PRESENT == 1U)
typedef struct {
uint64_t* dest;
uint64_t wlen;
} __ecc_table_t;
extern const __ecc_table_t __ecc_table_start__;
extern const __ecc_table_t __ecc_table_end__;
for (__ecc_table_t const* pTable = &__ecc_table_start__; pTable < &__ecc_table_end__; ++pTable) {
for(uint64_t i=0u; i<pTable->wlen; ++i) { pTable-
>dest[i] = 0xDEADBEEFFEEDCAFEUL;
}
}
启动时也会处理将代码和数据从 Flash 复制到 ITCM 和 SRAM 的流程。
将代码复制到 ITCM、 DTCM 和 SRAM
STATIC_FORCEINLINE __NO_RETURN void __cmsis_start(void)
{
extern void _start(void) __NO_RETURN;
typedef struct {
uint32_t const* src;
uint32_t* dest;
uint32_t wlen;
} __copy_table_t;
typedef struct {
uint32_t* dest;
uint32_t wlen;
} __zero_table_t;
typedef struct {
uint32_t const* src;
uint32_t* dest;
uint32_t wlen;
} __copycode_table_t;
extern const __copy_table_t __copy_table_start__;
extern const __copy_table_t __copy_table_end__;
extern const __zero_table_t __zero_table_start__;
extern const __zero_table_t __zero_table_end__;
extern const __copycode_table_t __copycode_table_start__;
extern const __copycode_table_t __copycode_table_end__;
for (__copy_table_t const* pTable = &__copy_table_start__; pTable < &__copy_table_end__; ++pTable) {
for(uint32_t i=0u; i<pTable->wlen; ++i) {
pTable->dest[i] = pTable->src[i];
}
}
for (__zero_table_t const* pTable = &__zero_table_start__; pTable < &__zero_table_end__; ++pTable) {
for(uint32_t i=0u; i<pTable->wlen; ++i) {
pTable->dest[i] = 0u;
}
}
for (__copycode_table_t const* pTable = &__copycode_table_start__; pTable < __copycode_table_end__;
++pTable)
{
for(uint32_t i=0u; i<pTable->wlen; ++i) {
pTable->dest[i] = pTable->src[i];
}
}
_start();
}
分配代码
函数属性部分用于那些要分配给 SRAM、 ITCM 或 DTCM 的函数和数据 attribute (( section (".Section_Name")))
其中,Section_Name 是之前在 Linker 文件中声明的部分。
在函数或变量之前使用该属性。
使用属性部分在 ITCM、 DTCM 和 SRAM 中分配函数
attribute__((__section__(".DTCM_data")))uint32_t ClkCycleCounter[18] = {0};
attribute__((__section__(".DTCM_data")))static uint8_t Counter;
/*********************************************************************************************/
/* ITCM Function */
/* Function loaded from ITCM. This function clean pDest and pSrc and copy the */
/* content of the array dummy[1024] stored in flash to pDest and pSrc */
/* Inputs: *pDest = Pointer to Destination array */
/* *pSrc = Pointer to Source array */
/* size = Array size to copy */
/* Output: ClkCycleCounter Array = Store the number of cycles taken since the */
/* beginning of the function until the end. There are 18 positions */
/* to store all function results */
/********************************************************************************************/
attribute__((__section__(".ITCM_code")))
static void TCM_Function(uint8_t *pDest, uint8_t *pSrc, uint32_t size)
{
/* Function example content */
uint32_t i;
for(i=0u; i<size; i++)
{
pSrc[i] = 0u;
pDest[i] = 0u;
}
for(i=0u; i<size; i++)
{
pSrc[i] = pBuffer[i];
}
}
attribute__((__section__(".SRAM_Function")))
static void SRAM_Function(uint8_t *pDest, uint8_t *pSrc, uint32_t size)
{
/* Function example content */
uint32_t i;
for(i=0u; i<size; i++)
{
pSrc[i] = 0u;
pDest[i] = 0u;
}
for(i=0u; i<size; i++)
{
pSrc[i] = pBuffer[i];
}
}
结果
下图 展示了不同存储器上函数和数据所占用的周期数。需要注意,与从 Flash 运行相比,从 ITCM运行时的周期数更小。这一优势可用于为需要确定时间或需要避免延迟的任务减少周期数和计时。
下图还展示了启用 Cache、 Instruction Cache 和 Data Cache 的用法。当启用 D_Cache 和 I_Cache时,周期数会显著减少。这是因为 Cache 获取了其区域中的所有代码和数据。 S32K3 系列芯片的 Flash 和 SRAM 可缓存,TCM 存储器为零等待访问,因此 ITCM 和 DTCM 不需要 Cache。这是一个简单的代码,Cache 足以存储所有代码。然而,在实际应用中,不太可能将全部代码放在 Cache 中,因此可以利用 TCM 的特点。
当从 ITCM 运行代码并将数据传输到 DTCM 时,我们得到的结果与 I-Cache 运行类似。这是因为 TCM 和内核的周期次数与 Cache 相同。
6.2、SRAM Standby
当 MCU 处于 Standby 模式时,SRAM 内存中有 32 KB 可用。此存储器区域允许存储 MCU 从 Standby 模式唤醒时需要的关键数据或重要代码。 MCU 有两种工作模式:运行模式和 Standby
模式。在运行模式下运行时,MCU 可使用所有外设和模块。在 Standby 模式下,MCU 只能使用
其中的几个,因为一些电源域与芯片内的电源断开了。其目的是实现低功耗模式,以节省能耗,
并在事件、上电复位、破坏性复位或功能性复位时唤醒。
下一个用例是一个简单的实践,展示了 Standby SRAM 存储器的特性。
LINK文件
首先,需要在 Linker 文件中声明 SRAM Standby 区域,请参考上面示例 Linker 中的存储器定义和大小。如前所述,Standby SRAM 分配给了 SRAM 存储器的前 32KB。
Main文件
在本示例中,我们将使用 3 个缓冲区:
dummy_array[1024]:这是一个位于 Flash 中的阵列,大小为 1024 字节。
const uint8_t dummy_array[1024UL] =
{
0x00,0x01,0x02,0x03,…
}
SRAM_buffer[BUFFER_SIZE]:这是一个位于 SRAM 存储器中的阵列,在 0x20408000
地址之后的任何区域。
uint8_t SRAM_buffer [BUFFER_SIZE];
SRAM_SB_buffer[BUFFER_SIZE]:这是一个位于 Standby 存储器中的阵列
attribute((section(".standby_ram"))) uint8_t SRAM_SB_buffer [BUFFER_SIZE];
其中 BUFFER_SIZE 被定义为 1024
#define BUFFER_SIZE 1024
在 Main 文件中,阵列缓冲区被清除,dummy 阵列字节被复制到 SRAM_buffer[]和 SRAM_SB_buffer[]中。
Uint32_t *pDest_SRAM_SB;
pDest_SRAM_SB = &SRAM_SB_buffer;
/* Clear buffer */
for(i=0; i<BUFFER_SIZE; i++)
{
SRAM_SB_buffer[i] = 0;
SRAM_buffer[i] = 0;
}
/* Copy dummy array into SRAM_buffer and SRAM_SB_buffer*/
for(i=0u; i<BUFFER_SIZE; i++)
{
pDest_SRAM_SB[i] = dummy_array[i];
SRAM_buffer[i] = dummy_array[i];
}
之后执行 dummy 读取,以验证数据是否可从 Standby SRAM 和 SRAM 存储器中获取。
伪读取到 Standby SRAM 阵列和 SRAM 阵列
read_SRAM_SB = *(uint32_t*) 0x20400000;
read_SRAM = *(uint32_t*) 0x20408830;
在中断处理程序中按下 SW4,执行 Standby 输入序列。
SW4 配置生成中断,进入 Standby 状态
/* Pin Configuration for PTB26 (SW4) */
SIUL2->IREER0 &= ~SIUL2_IREER0_IREE13_MASK; /* Disable Rising Edge event */
SIUL2->IFEER0 |= SIUL2_IFEER0_IFEE13_MASK; /* Enable Falling Edge event */
SIUL2->IMCR[541-512] |= SIUL2_IMCR_SSS(0b010); /* Enable Source Signal EIRQ[13] */
SIUL2->MSCR[58] |= SIUL2_MSCR_IBE_MASK; /* IBE=1: Input Buffer Enabled */
SIUL2->DIRSR0 &= ~SIUL2_DIRSR0_DIRSR13_MASK; /* Select Interrupt Request for PTB26 */
SIUL2->DISR0 = 0xFFFFFFFFU; /* Clear Status Flag Interrupt */
SIUL2->DIRER0 |= SIUL2_DIRER0_EIRE13_MASK; /* External Interrupt enable for EIRQ[13] */
中断例程,进入 Standby 模式
void SIUL_1_Handler (void)
{
示例 17. 中断例程,进入 Standby 模式
/* Wait until SW4 (PTB26) release */
while ((SIUL2->GPDI58 & SIUL2_GPDI58_PDI_n_MASK) == 1) { }
/* Drive low on PTB30 to turn-off LED D32 */
SIUL2->GPDO30 &= ~SIUL2_GPDO30_PDO_n_MASK;
Standby_Entry_Sequence();
}
执行了 Standby_Entry_Sequence 后,S32K3 进入 Standby 模式,调试器被断开。
唤醒
按下被配置为产生中断命令的 SW5 执行唤醒流程。
SW5 配置,用于从 Standby 模式下唤醒
/* Pin Configuration for PTB19 (SW5) */
// WKPU[38]
SIUL2->MSCR[51] = SIUL2_MSCR_IBE_MASK /* IBE=1: Input Buffer Enabled */
| SIUL2_MSCR_PUE_MASK;
WKPU->WIFEER_64 &= ~0x00000400; /* Disable WKPU[38] falling edge */
WKPU->WIREER_64 |= 0x00000400; /* Enable WKPU[38] rising edge */
WKPU->WIFER_64 |= 0x00000400; /* WKPU[38] glitch filter enabled */
WKPU->WISR_64 = 0x00000400; /* Write 1 to Clear WKPU[38] flag */
WKPU->IRER_64 |= 0x00000400; /* Write 1 to Interrupt request WKPU[38] flag */
WKPU->NCR &= ~(WKPU_NCR_NDSS0_MASK | WKPU_NCR_NDSS1_MASK); /* NMI Destination Source Select */
WKPU->NCR |= WKPU_NCR_NWRE0_MASK | WKPU_NCR_NWRE1_MASK; /* NMI Wakeup Request Enable */
WKPU->WRER_64 |= 0x0000400; /* Enable WKPU[38] input */
唤醒后,MCU 可重新连接到调试器,在 S32DS 中选择以下标注的选项。
唤醒后参加中断例程
void WKPU_Handler (void)
{
for(uint32_t i=0; i<0x02FFFFFF; i++)
{
NOP();
}
read_SRAM_SB = *(uint32_t*) 0x20400000;
read_SRAM = *(uint32_t*) 0x20408830;
while(1);
}
结果
如上所述,当 MCU 处于 Standby 模式时,存储在由 Standby(Standby)域供电的 Standby SRAM 存储器中的数据被保留,在唤醒后可用。但运行(Run)域供电的 SRAM 中的数据不可
用,需要在唤醒后进行初始化,以避免 ECC 错误。需要注意,唤醒后,Standby SRAM 不需要
进行初始化以避免 ECC 错误,但 SRAM 的其余部分却需要进行初始化,因此应在启动(Startup)代码中进行适当的区分。下面的代码描述了进行这种区分的示例
仅在 POR 后对 Standby RAM 进行初始化
/* Initialize STANDBY RAM if chip comes from POR */
if (MC_RGM->DES & MC_RGM_DES_F_POR_MASK)
{
/* Initialize STANDBY RAM */
cnt = (( uint32_t)(&__STDBYRAM_SIZE)) / 8U;
pDest = (uint64_t *)(&__STDBYRAM_START);
while (cnt--)
{
*pDest = (uint64_t)0xDEADBEEFCAFEFEEDULL;
pDest++;
}
MC_RGM->DES = MC_RGM_DES_F_POR_MASK; /* Write 1 to clear F_POR */
}