分散加载
MXRT1052/1064芯片的RAM空间分为四种类型:ITCM,DTCM,OCRAM以及外部SDRAM,其中前面三个属于芯片内部RAM,RT1052有512KB,RT1064为1MB,支持用户静态分配,SDRAM属于外置RAM,最大支持到1.5G扩展空间。ITCM和DTCM是直接挂在芯片内核总线,速度可以达到与内核同频的600M,OCRAM挂在Sys AXI 64位总线,速度只能到达133M,外部SDRAM速度则可达到166M,而同时RT1050/1064内又有各32K的指令cache和数据cache,用于提高代码在外部Nor Flash中XIP执行的效率。从速度的角度看,将所有的用户代码分配ITCM/DTCM,能够发挥到最大性能,从存储空间大小的角度看,代码存放在SDRAM或者外部Flash最简单,而从USB/DMA使用的角度来看,RAM空间分配在OCRAM空间最为方便。所以这些不同RAM类型速度/大小的差异和cache的存在,就决定了要想让IMXRT性能发挥到最大,就需要用户根据客户实际应用手动修改内部RAM空间中ITCM/DTCM/OCRAM的大小分配,并定位关键代码和数据到指定RAM空间中运行。
1.如何将关键代码和数据到指定RAM中运行?
① IAR中首先找到使用的对应分散加载文件,用记事本打开;
② 确定自己的内存分配,如下是默认的一种分配方式。
/* 中断向量表 */
define symbol m_interrupts_start = 0x70002000;
define symbol m_interrupts_end = 0x700023FF;
// 代码存放位置
define symbol m_text_start = 0x70002400;
define symbol m_text_end = 0x703FFFFF;
// DTCRAM 128K
define symbol dtcram_start = 0x20000000;
define symbol dtcram_end = 0x2001FFFF;
// OCRAM 768K
define symbol ocram_start = 0x20200000;
define symbol ocram_end = 0x202BFFFF;
// itcram 128K 注意不要从0x00000000开始
define symbol itcram_start = 0x00000004;
define symbol itcram_end = 0x0001FFFF;
/* FLASH配置和ivt等信息 */
define exported symbol m_boot_hdr_conf_start = 0x70000000;
define symbol m_boot_hdr_ivt_start = 0x70001000;
define symbol m_boot_hdr_boot_data_start = 0x70001020;
define symbol m_boot_hdr_dcd_data_start = 0x70001030;
③ 规定堆栈区域大小,根据自己的实际情况分配,堆一般是用malloc函数动态分配内存区域,栈是程序的局部变量存储空间,可以适当分配大一点。
// 栈大小 局部变量
if (isdefinedsymbol(__stack_size__)) {
define symbol __size_cstack__ = __stack_size__;
} else {
define symbol __size_cstack__ = 0x4000;
}
// 堆大小 动态分配malloc分配的空间
if (isdefinedsymbol(__heap_size__)) {
define symbol __size_heap__ = __heap_size__;
} else {
define symbol __size_heap__ = 0x8000;
}
// RAM中的中断向量表 这里没有使用
define exported symbol __VECTOR_TABLE = m_interrupts_start;
define exported symbol __VECTOR_RAM = m_interrupts_start;
define exported symbol __RAM_VECTOR_TABLE_SIZE = 0x0;
define memory mem with size = 4G;
④ 定义一些储存区域和地址块
// 定义存储地址区域(region)
define region TEXT_region = mem:[from m_interrupts_start to m_interrupts_end]
| mem:[from m_text_start to m_text_end];
define region DTCRAM_region = mem:[from dtcram_start to dtcram_end - __size_cstack__];
define region ITCRAM_region = mem:[from itcram_start to itcram_end];
define region OCRAM_region = mem:[from ocram_start to ocram_end];
// 栈段 栈最好分配在DTCRAM中
define region CSTACK_region = mem:[from dtcram_end - __size_cstack__+1 to dtcram_end];
// 定义地址块
define block CSTACK with alignment = 8, size = __size_cstack__ { };
define block HEAP with alignment = 8, size = __size_heap__ { };
define block RW { readwrite };
define block ZI { zi };
define block NCACHE_VAR { section NonCacheable , section NonCacheable.init };
define block ITCRAM with alignment = 8{ section ITCRAMAccess};
define block DTCRAM with alignment = 8{ section DTCRAMAccess};
define block OTCRAM with alignment = 8{ section OTCRAMAccess};
⑤ 添加IAR自动初始化区域
// 初始化sections
initialize by copy { readwrite, section .textrw};
initialize by copy { section ITCRAMAccess, section DTCRAMAccess,
section OCRAMAccess};
do not initialize { section .noinit };
//IAR的ICF文件中宏导出给程序使用
place at address mem: m_interrupts_start { readonly section .intvec };
place at address mem:m_boot_hdr_conf_start { section .boot_hdr.conf };
place at address mem:m_boot_hdr_ivt_start { section .boot_hdr.ivt };
place at address mem:m_boot_hdr_boot_data_start { readonly section .boot_hdr.boot_data };
place at address mem:m_boot_hdr_dcd_data_start { readonly section .boot_hdr.dcd_data };
keep{ section .boot_hdr.conf, section .boot_hdr.ivt, section .boot_hdr.boot_data, section .boot_hdr.dcd_data };
⑥ 将步骤4中定义的储存区域和地址块关联起来
// 把一系列sections和blocks放置在某个region中。sections和blocks将按任意顺序放置。
place in TEXT_region { readonly }; //代码存放区域
place in DTCRAM_region { block RW }; //RW段存放区域(全局或静态不为0变量)
place in DTCRAM_region { block ZI }; //ZI段存放区域(全局或静态为0变量)
place in OCRAM_region { last block HEAP }; //malloc 动态申请内存区域
place in DTCRAM_region { block NCACHE_VAR }; //NCACHE区域 需要配合MPU使用
place in CSTACK_region { block CSTACK }; //站区域 局部变量
place in ITCRAM_region { block ITCRAM }; //ITCRAM区域
place in DTCRAM_region { block DTCRAM }; //DTCRAM区域
place in OCRAM_region { block OCRAM }; //OCRAM区域
⑦ 我们上面在步骤4中定义了地址块ITCRAMAccess、 DTCRAMAccess、OCRAMAccess,如何在IAR中将变量放到对应地址块呢?
⑧ 打开fsl_common.h,仿照官方AT_NONCACHEABLE_SECTION宏定义添加如下宏定义。
#if (defined(__ICCARM__))
#define AT_ITCRAM_SECTION(func) func @"ITCRAMAccess"
#define AT_DTCRAM_SECTION(func) func @"DTCRAMAccess"
#define AT_OCRAM_SECTION(func) func @"OCRAMAccess"
#elif(defined(__ARMCC_VERSION))
#define AT_ITCRAM_SECTION(func) __attribute__((section("ITCRAMAccess"))) func
#define AT_DTCRAM_SECTION(func) __attribute__((section("DTCRAMAccess"))) func
#define AT_OCRAM_SECTION(func) __attribute__((section("OCRAMAccess"))) func
#elif(defined(__GNUC__))
#define AT_ITCRAM_SECTION(func) __attribute__((section("ITCRAMAccess"))) func
#define AT_DTCRAM_SECTION(func) __attribute__((section("DTCRAMAccess"))) func
#define AT_OCRAM_SECTION(func) __attribute__((section("OCRAMAccess"))) func
#endif
⑨ 打开BOARD_ConfigMPU(); 函数修改MPU配置,验证对应的数据分配到了指定位置。
使用宏定义修饰函数或者变量,将函数和变量存放到指定位置。
AT_ITCRAM_SECTION(void delayms(uint16_t ms))
{
volatile uint32_t i = 0;
while(ms--)
{
for (i = 0; i < 30000; ++i)
{
__asm("NOP"); /* delay */
}
}
}
AT_DTCRAM_SECTION(uint8_t dcrambuf[100][1024]);
AT_OCRAM_SECTION(uint8_t orambuf[100][1024]);
⑩ MDK中类似IAR,打开分散加载文件,在里面添加块ITCRAMAccess、 DTCRAMAccess、OCRAMAccess。通过步骤8中的宏定义可以将关键代码和变量存放到指定位置。
/* FLASH配置和ivt等信息 */
#define m_flash_config_start 0x70000000
#define m_flash_config_size 0x00001000
#define m_ivt_start 0x70001000
#define m_ivt_size 0x00001000
/* 中断向量表 */
#define m_interrupts_start 0x70002000
#define m_interrupts_size 0x00000400
// 代码存放位置
#define m_text_start 0x70002400
#define m_text_size 0x003FDC00
// DTCRAM 128K
#define dtcram_start 0x20000000
#define dtcram_size 0x00020000
// OCRAM 768K
#define ocram_start 0x20200000
#define ocram_size 0x000C0000
// itcram 128K 注意从0x00000004开始
#define itcram_start 0x00000004
#define itcram_size 0x00020000
// 栈大小 局部变量
#if (defined(__stack_size__))
#define Stack_Size __stack_size__
#else
#define Stack_Size 0x4000
#endif
// 堆大小 动态分配malloc分配的空间
#if (defined(__heap_size__))
#define Heap_Size __heap_size__
#else
#define Heap_Size 0x8000
#endif
#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1)
LR_m_text m_flash_config_start m_text_start+m_text_size-m_flash_config_start { ; load region size_region
RW_m_config_text m_flash_config_start FIXED m_flash_config_size { ; load address = execution address
* (.boot_hdr.conf, +FIRST)
}
RW_m_ivt_text m_ivt_start FIXED m_ivt_size { ; load address = execution address
* (.boot_hdr.ivt, +FIRST)
* (.boot_hdr.boot_data)
* (.boot_hdr.dcd_data)
}
#else
LR_m_text m_interrupts_start m_text_start+m_text_size-m_interrupts_start { ; load region size_region
#endif
/* 中断向量表存放位置 */
VECTOR_ROM m_interrupts_start FIXED m_interrupts_size { ; load address = execution address
* (RESET,+FIRST)
}
/* 代码段存放位置 */
TEXT_region m_text_start FIXED m_text_size { ; load address = execution address
* (InRoot$$Sections)
.ANY (+RO)
}
/* DTCRAM段 */
DTCRAM_region dtcram_start dtcram_size-Stack_Size{ ; RW data
.ANY (+RW)
.ANY (+ZI)
* (DTCRAMAccess)
* (NonCacheable.init)
* (NonCacheable)
}
/* ITCRAM段 */
ITCRAM_region itcram_start itcram_size{ ; RW data
* (ITCRAMAccess)
}
/* OCRAM段 */
OCRAM_region ocram_start ocram_size{ ;
* (OCRAMAccess)
}
/* 堆区域 */
ARM_LIB_HEAP +0 EMPTY Heap_Size { ; Heap region growing up
}
/* 栈区域 栈是向下生长的 */
ARM_LIB_STACK dtcram_start+dtcram_size EMPTY -Stack_Size { ; Stack region growing down
}
}
动态修改FlexRAM,修改TCRAM大小
RT1052片内的512KB RAM,可以自由分配为ITCRAM、DTCRAM和OCRAM(OCRAM最少32KB)。RT1064相比RT1052,多了512KB的OCRAM,另外的512KB RAM和RT1052一样,可以自由分配为ITCRAM、DTCRAM和OCRAM。
IMXRT的片内RAM可以根据用户需求动态分配,512KB的RAM分成16个BANK,每个BANK 由32位寄存器IOMUXC_GPR->GPR17(0x400AC044)中的两位来确定类型。
• 00b—bank is not used.
• 01b—bank is configured for OCRAM.
• 10b—bank is configured for DTCM.
• 11b—bank is configured for ITCM.
注意ITCRAM、DTCRAM应该为2的幂(32、64、128、256、512)
下图就是一个简单的例子,配置DTCRAM 128K、ITCRAM 256K、OCRAM 128K。
配置完IOMUXC_GPR->GPR17(0x400AC044)寄存器,确定每个BANK的类型后,修改寄存器IOMUXC_GPR->GPR14(0x400AC038)确定对应TCM的空间大小,并设置IOMUXC_GPR->GPR16(0x400AC040)开启动态分配的内存空间。
下面是一个DTCRAM 256K、ITCRAM 128K、OCRAM 128K的分配例子。
/* RT1052/1064 内部SRAM一共分为16个blank 每个blank 都可以选择类型
* A bank size of 512 kB / 16 = 32 kB
* IOMUXC_GPR_GPR17寄存器一共32位, 每两位控制一个blank的类型
* 00b—bank is not used.
* 01b—bank is configured for OCRAM.
* 10b—bank is configured for DTCM.
* 11b—bank is configured for ITCM.
*/
IOMUXC_GPR->GPR17 = IOMUXC_GPR_GPR17_FLEXRAM_BANK_CFG(0x55AAAAFF);
/*! CM7_CFGITCMSZ
* 0b0000..0 KB (No ITCM)
* 0b0011..4 KB
* 0b0100..8 KB
* 0b0101..16 KB
* 0b0110..32 KB
* 0b0111..64 KB
* 0b1000..128 KB
* 0b1001..256 KB
* 0b1010..512 KB
*/
IOMUXC_GPR->GPR14 &= ~IOMUXC_GPR_GPR14_CM7_CFGITCMSZ_MASK;
IOMUXC_GPR->GPR14 |= IOMUXC_GPR_GPR14_CM7_CFGITCMSZ(8);
IOMUXC_GPR->GPR16 |= IOMUXC_GPR_GPR16_INIT_ITCM_EN(1);
/*! CM7_CFGDTCMSZ
* 0b0000..0 KB (No DTCM)
* 0b0011..4 KB
* 0b0100..8 KB
* 0b0101..16 KB
* 0b0110..32 KB
* 0b0111..64 KB
* 0b1000..128 KB
* 0b1001..256 KB
* 0b1010..512 KB
*/
IOMUXC_GPR->GPR14 &= ~IOMUXC_GPR_GPR14_CM7_CFGDTCMSZ_MASK;
IOMUXC_GPR->GPR14 |= IOMUXC_GPR_GPR14_CM7_CFGDTCMSZ(9);
IOMUXC_GPR->GPR16 |= IOMUXC_GPR_GPR16_INIT_DTCM_EN(1);
/* 使能配置 */
IOMUXC_GPR->GPR16 &= ~IOMUXC_GPR_GPR16_FLEXRAM_BANK_CFG_SEL_MASK;
IOMUXC_GPR->GPR16 |= IOMUXC_GPR_GPR16_FLEXRAM_BANK_CFG_SEL(1);
动态内存分配最好在堆栈初始化之前,因此我们需要将上面的代码转换为对应的RAM汇编代码,放在复位中断服务函数堆栈初始化前面,打开启动文件startup_MIMXRT10XX.s文件,找到Reset_Handler的汇编函数,添加对应汇编代码。
FLEXRAM_BANK_CFG EQU 0x55AAAAFF
ITCRAM_SIZE EQU 0x8;128KB 0: 0KB 6: 32KB 7: 64KB 8: 128KB 9: 256KB 10: 512KB
DTCRAM_SIZE EQU 0x9;256KB 0: 0KB 6: 32KB 7: 64KB 8: 128KB 9: 256KB 10: 512KB
Reset_Handler
CPSID I ; Mask interrupts
;IOMUXC_GPR->GPR17 = IOMUXC_GPR_GPR17_FLEXRAM_BANK_CFG(0x55AAAAFF);
LDR R0, = 0x400AC044 ; 将IOMUXC_GPR->GPR17的地址放到 寄存器R0中
LDR R1, = FLEXRAM_BANK_CFG ; 将BANK划分结果放到 寄存器R1中
STR R1, [R0] ; 将R1 存放到IOMUXC_GPR->GPR17中
;IOMUXC_GPR->GPR14 &= ~IOMUXC_GPR_GPR14_CM7_CFGITCMSZ_MASK;
;IOMUXC_GPR->GPR14 &= ~IOMUXC_GPR_GPR14_CM7_CFGDTCMSZ_MASK;
LDR R0, = 0x400AC038 ; 将IOMUXC_GPR->GPR14的地址放到 寄存器R0中
LDR R2, [R0]
LDR R3, = 0x00FFFF
AND R1, R2, R3 ; R1 = IOMUXC_GPR->GPR14 &= ~IOMUXC_GPR_GPR14_CM7_CFGITCMSZ_MASK中
STR R1, [R0] ; 将R1 存放到IOMUXC_GPR->GPR14中
;IOMUXC_GPR->GPR14 |= IOMUXC_GPR_GPR14_CM7_CFGITCMSZ(ITCRAM_SIZE);
;IOMUXC_GPR->GPR14 |= IOMUXC_GPR_GPR14_CM7_CFGDTCMSZ(DTCRAM_SIZE);
LDR R1, = ITCRAM_SIZE
MOV R2, R1,LSL#16
LDR R1, = DTCRAM_SIZE
MOV R3, R1,LSL#20
ORR R1, R2, R3
LDR R2, [R0]
ORR R1, R1, R2
STR R1, [R0]
;IOMUXC_GPR->GPR16 |= IOMUXC_GPR_GPR16_INIT_ITCM_EN(1);
;IOMUXC_GPR->GPR16 |= IOMUXC_GPR_GPR16_INIT_DTCM_EN(1);
;IOMUXC_GPR->GPR16 |= IOMUXC_GPR_GPR16_FLEXRAM_BANK_CFG_SEL(1);
LDR R0, = 0x400AC040
LDR R1, [R0]
ORR R1, R1, #0x7
STR R1, [R0]
LDR R0, =0xE000ED08
LDR R1, =__vector_table
STR R1, [R0]
LDR R2, [R1]
MSR MSP, R2
LDR R0, =SystemInit
BLX R0
CPSIE I ; Unmask interrupts
LDR R0, =__iar_program_start
BX R0
最后修改分散加载文件中地址空间分配、修改MPU配置