打印
[开发工具]

Keil sct分散加载文件(转)

[复制链接]
2651|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
huangcunxiake|  楼主 | 2015-12-28 15:48 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
keil, se, IO, TI, TE

首先介绍几个概念:

1.ARM映像文件

ARM映像文件是一个层次性结构的文件,其中包含了域(region)、输出段(output section)和输入段(input section)。各部分关系如下:

  • 一个映像文件由一个或多个域组成
  • 每个域包含一个或多个输出段
  • 每个输出段包含一个或多个输入段
  • 各输入段包含了目标文件中的代码和数据

输入段中包含了4类内容:代码、已经初始化的数据、未经初始化的存储区域、内容初始化成0的存储区域。每个输入段有相应的属性,可以为只读的(RO)、可读写的(RW)以及初始化成0的(ZI)。ARM连接器根据各输入段的属性将这些输入段分组,再组成不同的输出段以及域。

一个输出段中包含了一系列的具有相同的RO、RW和ZI属性的输入段。输出段的属性与其中包含的输入段的属性相同。在一个输出段内部,各输入段是按照一定的规则排序的,这个后面再补充。

一个域中包含了1~3个输出段,其中各输出段的属性各不相同。各输出段的排列顺序是由其属性决定的。其中,RO属性的输出段排在最前面,其次是RW属性的输出段,最后是ZI属性的输出段。一个域通常映射到一个物理存储器上,如ROM和RAM等。

2.ARM映像文件各组成部分的地址映射

分散加载机制允许为链接器指定映像的存储器映射信息,可实现对映像组件分组和布局的全面控制。分散加载通常仅用于具有复杂存储器映射的映像(尽管也可用于简单映像),也就是适合加载和执行时内存映射中的多个区是分散的情况。

要构建映像的存储器映射,链接器必须有:描述节如何分组成区的分组信息、描述映像区在存储器映射中的放置地址的放置信息。

分散加载区域分两类:

  • 加载区:该映像文件开始运行前存放的区域,即当系统启动或加载时应用程序存放的区域。
  • 执行区:映像文件运行时的区域,即系统启动后,应用程序进行执行和数据访问的存储器区域,系统在实时运行时可以有一个或多个执行块。
3.分散加载文件(即scatter file,后缀为.scf)

分散加载文件是一个文本文件,通过编写一个分散加载文件来指定ARM连接器在生成映像文件时如何分配RO,RW,ZI等数据的存放地址。如果不用SCATTER文件指定,那么ARM连接器会按照默认的方式来生成映像文件,一般情况下我们是不需要使用分散加载文件的。

但在某些场合,我们希望把某些数据放在指定的地址处,那么这时候SCATTER文件就发挥了非常大的作用。而且SCATTER文件用起来非常简单好用。

举个例子:比如像LPC2378芯片具有多个不连续的SRAM,通用的RAM是32KB,可是32KB不够用,我想把某个.C中的RW数据放在USB的SRAM中,那么就可以通过SCATTER文件来完成这个功能。

分散加载文件的语法:

load_region_name  start_address | "+"offset  [attributes] [max_size]
{
    execution_region_name  start_address | "+"offset  [attributes][max_size]
    {
        module_select_pattern  ["("
                                    ("+" input_section_attr | input_section_pattern)
                                    ([","] "+" input_section_attr | "," input_section_pattern)) *
                               ")"]
    }
}
  • load_region:          加载区,用来保存永久性数据(程序和只读变量)的区域;
  • execution_region:     执行区,程序执行时,从加载区域将数据复制到相应执行区后才能被正确执行;
  • load_region_name:     加载区域名,用于“Linker”区别不同的加载区域,最多31个字符;
  • start_address:        起始地址,指示区域的首地址;
  • +offset:              前一个加载区域尾地址+offset 做为当前的起始地址,且“offset”应为“0”或“4”的倍数;
  • attributes:           区域属性,可设置如下属性:

                           PI       与地址无关方式存放;
                           RELOC    重新部署,保留定位信息,以便重新定位该段到新的执行区;
                           OVERLAY  覆盖,允许多个可执行区域在同一个地址,ADS不支持;
                           ABSOLUTE 绝对地址(默认);

  • max_size:                 该区域的大小;
  • execution_region_name:执行区域名;
  • start_address:        该执行区的首地址,必须字对齐;
  • +offset:              同上;
  • attributes:           同上;

                           PI          与地址无关,该区域的代码可任意移动后执行;
                           OVERLAY     覆盖;
                           ABSOLUTE    绝对地址(默认);
                           FIXED       固定地址;
                           UNINIT      不用初始化该区域的ZI段;

  • module_select_pattern: 目标文件滤波器,支持通配符“*”和“?”;

                        *.o匹配所有目标,* (或“.ANY”)匹配所有目标文件和库。

  • input_section_attr:    每个input_section_attr必须跟随在“+”后;且大小写不敏感;

                        RO-CODE 或 CODE
                        RO-DATA 或 CONST
                        RO或TEXT, selects both RO-CODE and RO-DATA
                        RW-DATA
                        RW-CODE
                        RW 或 DATA, selects both RW-CODE and RW-DATA
                        ZI 或 BSS
                        ENTRY, that is a section containing an ENTRY point.
                        FIRST,用于指定存放在一个执行区域的第一个或最后一个区域;
                        LAST,同上;

  • input_section_pattern: 段名;

汇编中指定段:
     AREA    vectors, CODE, READONLY
C中指定段:
#pragma arm section [sort_type[[=]"name"]] [,sort_type="name"]*
sort_type:      code、rwdata、rodata、zidata
                如果“sort_type”指定了但没有指定“name”,那么之前的修改的段名将被恢复成默认值。
#pragma arm section     // 恢复所有段名为默认设置。
应用:
    #pragma arm section rwdata = "SRAM",zidata = "SRAM"
        static OS_STK  SecondTaskStk[256];              // “rwdata”“zidata”将定位在“sram”段中。
    #pragma arm section                                 // 恢复默认设置




沙发
huangcunxiake|  楼主 | 2015-12-28 15:49 | 只看该作者

样例:

简单存储器映射实例

LOAD_ROM 0x0000 0x8000       //Name of load region, Start address for load region, Maximum size of load region
{
    EXEC_ROM 0x0000 0x8000   //Name of first exec region, Start address for exec region, Maximum size of this region
    {
        *(+RO)               //Place all code and RO data into this exec region
    }
    RAM 0x10000 0x60000      //Start of second exec region
    {
        *(+RW, +ZI)          //Place all RW and ZI data into this exec region
    }
}


使用特权

评论回复
板凳
huangcunxiake|  楼主 | 2015-12-28 15:49 | 只看该作者

复杂存储器映射实例:

LOAD_ROM_1 0x0000 //Start address for first load region
{
EXEC_ROM_1 0x0000 //Start address for first exec region
{
program1.o (+RO) //Place all code and RO data from program1.o into this exec region
}
DRAM 0x18000 0x8000 //Start address for this exec region Maximum size of this exec region
{
program1.o (+RW, +ZI) //Place all RW and ZI data from program1.o into this exec region
}
}

LOAD_ROM_2 0x4000 //Start address for second load region
{
EXEC_ROM_2 0x4000
{
program2.o (+RO) //Place all code and RO data from program2.o into this exec region
}
SRAM 0x8000 0x8000
{
program2.o (+RW, +ZI) //Place all RW and ZI data from program2.o into this exec region
}
}

使用特权

评论回复
地板
huangcunxiake|  楼主 | 2015-12-28 15:50 | 只看该作者

具体格式描述请参考资料: 分散加载描述文件

一个具体的例子:

; *************************************************************
; * Scatter-Loading Description File generated by uVision *
; *************************************************************

LR_IROM1 0x00000000 0x00080000 { ; 第一个加载域,名字是LR_IROM1,起始地址0x00000000 大小是0x00080000
ER_IROM1 0x00000000 0x00080000 { ; 第一个运行时域,名字是ER_IROM1 起始地址0x00000000 大小事0x00080000
*.o (RESET, +First) ; IAP第一阶段在FLASH中运行
*(InRoot$Sections) ; All library sections that must be in a root region
.ANY (+RO) ; .ANY与*功能相似,用.ANY可以把已经被指定的具有RW,ZI属性的数据排除
}
RW_IRAM1 0x10000000 0x00010000 { ; RW data
.ANY(+RW +ZI)
}
RW_SDRAM1 0xA0000000 0x00800000 { ; RW data
STARTUP_LPC177X_8X.o (HEAP) ;HEAP用来定位堆栈的底
*.LIB(+RW +ZI)
}
}

使用特权

评论回复
5
yklstudent| | 2015-12-28 15:53 | 只看该作者
分散加载文件在IAP下还是蛮好有用的,可以实现IAP和APP两个工程文件整合成一个工程文件

使用特权

评论回复
6
huangcunxiake|  楼主 | 2015-12-28 15:54 | 只看该作者
yklstudent 发表于 2015-12-28 15:53
分散加载文件在IAP下还是蛮好有用的,可以实现IAP和APP两个工程文件整合成一个工程文件 ...

对,是的,这个最近我才了解,之前没有搞过这个功能,都是Keil默认那些。

使用特权

评论回复
7
dongnanxibei| | 2015-12-28 17:46 | 只看该作者
如果不用SCATTER文件指定,那么ARM连接器会按照默认的方式来生成映像文件,一般情况下我们是不需要使用分散加载文件的。

使用特权

评论回复
8
huangcunxiake|  楼主 | 2015-12-30 23:15 | 只看该作者
分散加载文件是一个文本文件,通过编写一个分散加载文件来指定ARM连接器在生成映像文件时如何分配RO,RW,ZI等数据的存放地址。

使用特权

评论回复
9
gejigeji521| | 2015-12-30 23:27 | 只看该作者
执行区:映像文件运行时的区域,即系统启动后,应用程序进行执行和数据访问的存储器区域,系统在实时运行时可以有一个或多个执行块。

使用特权

评论回复
10
huangcunxiake|  楼主 | 2016-1-6 14:58 | 只看该作者
469的Discovery的例程
/**
  ******************************************************************************
  * [url=home.php?mod=space&uid=288409]@file[/url]    STemWin/STemWin_HelloWorld/Src/main.c
  * [url=home.php?mod=space&uid=187600]@author[/url]  MCD Application Team
  * [url=home.php?mod=space&uid=895143]@version[/url] V1.0.0
  * [url=home.php?mod=space&uid=212281]@date[/url]    14-August-2015
  * [url=home.php?mod=space&uid=247401]@brief[/url]   This file provides main program functions
  ******************************************************************************
  * @attention
  *
  * <h2><center>© COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "WM.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef TimHandle;
uint32_t uwPrescalerValue = 0;

/* Private function prototypes -----------------------------------------------*/
static void BSP_Config(void);
static void Error_Handler(void);
static void SystemClock_Config(void);
void BSP_Background(void);

extern void MainTask(void);

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Main program
  * @param  None
  * @retval None
  */
int main(void)
{  
  /* STM32F4xx HAL library initialization:
       - Configure the Flash prefetch, instruction and Data caches
       - Configure the Systick to generate an interrupt each 1 msec
       - Set NVIC Group Priority to 4
       - Global MSP (MCU Support Package) initialization
     */
  HAL_Init();
  
  /* Configure the system clock to 180 MHz */
  SystemClock_Config();
  
  /* Initialize LCD and LEDs */
  BSP_Config();
  
  /***********************************************************/
  
   /* Compute the prescaler value to have TIM3 counter clock equal to 10 KHz */
  uwPrescalerValue = (uint32_t) ((SystemCoreClock /2) / 10000) - 1;
  
  /* Set TIMx instance */
  TimHandle.Instance = TIM3;
   
  /* Initialize TIM3 peripheral as follows:
       + Period = 500 - 1
       + Prescaler = ((SystemCoreClock/2)/10000) - 1
       + ClockDivision = 0
       + Counter direction = Up
  */
  TimHandle.Init.Period = 500 - 1;
  TimHandle.Init.Prescaler = uwPrescalerValue;
  TimHandle.Init.ClockDivision = 0;
  TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
  if(HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
  {
    while(1)
    {
    }
  }
  
  /*##-2- Start the TIM Base generation in interrupt mode ####################*/
  /* Start Channel1 */
  if(HAL_TIM_Base_Start_IT(&TimHandle) != HAL_OK)
  {
    while(1)
    {
    }
  }
  
  /***********************************************************/
  /* Initializes the SDRAM device */
  BSP_SDRAM_Init();
  
  /* Enable the CRC Module */
  __HAL_RCC_CRC_CLK_ENABLE();
  
   /* Init the STemWin GUI Library */
  GUI_Init();
  
  GUI_DispStringAt("Starting...", 0, 0);
  
  /* Initialize LCD and LEDs */
  GUI_DispStringAt("Initializing lcd...", 0, 12);
  
  /* Activate the use of memory device feature */
  WM_SetCreateFlags(WM_CF_MEMDEV);
  
  MainTask();

  /* Infinite loop */  
  while (1)
  {
  }
}

/**
  * @brief  Period elapsed callback in non blocking mode
  * @param  htim: TIM handle
  * @retval None
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  BSP_Background();
}

/**
  * @brief TIM MSP Initialization
  *        This function configures the hardware resources used in this application:
  *           - Peripheral's clock enable
  *           - Peripheral's GPIO Configuration  
  * @param htim: TIM handle pointer
  * @retval None
  */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
  /*##-1- Enable peripherals and GPIO Clocks #################################*/
  /* TIMx Peripheral clock enable */
  __HAL_RCC_TIM3_CLK_ENABLE();

  /*##-2- Configure the NVIC for TIMx #########################################*/
  /* Set the TIMx priority */
  HAL_NVIC_SetPriority(TIM3_IRQn, 0, 1);
  
  /* Enable the TIMx global Interrupt */
  HAL_NVIC_EnableIRQ(TIM3_IRQn);
}

/**
  * @brief  Initializes the STM32F469I-DISCO's LCD and LEDs resources.
  * @param  None
  * @retval None
  */
static void BSP_Config(void)
{
  /* Initialize STM32F469I-DISCO's LEDs */
  BSP_LED_Init(LED3);
  BSP_LED_Init(LED4);

}

/**
  * @brief  BSP_Background.
  * @param  None
  * @retval None
  */
void BSP_Background(void)
{
  BSP_LED_Toggle(LED3);
  BSP_LED_Toggle(LED4);

}

/**
  * @brief  This function is executed in case of error occurrence.
  * @param  None
  * @retval None
  */
static void Error_Handler(void)
{
  while(1)
  {
    /* Insert a delay */
    HAL_Delay(50);
  }
}

/**
  * @brief  System Clock Configuration
  *         The system Clock is configured as follow :
  *            System Clock source            = PLL (HSE)
  *            SYSCLK(Hz)                     = 180000000
  *            HCLK(Hz)                       = 180000000
  *            AHB Prescaler                  = 1
  *            APB1 Prescaler                 = 4
  *            APB2 Prescaler                 = 2
  *            HSE Frequency(Hz)              = 8000000
  *            PLL_M                          = 8
  *            PLL_N                          = 360
  *            PLL_P                          = 2
  *            PLL_Q                          = 7
  *            PLL_R                          = 6
  *            VDD(V)                         = 3.3
  *            Main regulator output voltage  = Scale1 mode
  *            Flash Latency(WS)              = 5
  * @param  None
  * @retval None
  */
static void SystemClock_Config(void)
{
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
  RCC_OscInitTypeDef RCC_OscInitStruct;
  HAL_StatusTypeDef ret = HAL_OK;
  
  /* Enable Power Control clock */
  __HAL_RCC_PWR_CLK_ENABLE();
  
  /* The voltage scaling allows optimizing the power consumption when the device is
     clocked below the maximum system frequency, to update the voltage scaling value
     regarding system frequency refer to product datasheet.  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  
  /* Enable HSE Oscillator and activate PLL with HSE as source */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
#if defined(USE_STM32469I_DISCO_REVA)
  RCC_OscInitStruct.PLL.PLLM = 25;
#else
  RCC_OscInitStruct.PLL.PLLM = 8;
#endif /* USE_STM32469I_DISCO_REVA */
  RCC_OscInitStruct.PLL.PLLN = 360;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  RCC_OscInitStruct.PLL.PLLR = 6;
  
  ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
  
  if(ret != HAL_OK)
  {
    Error_Handler();
  }
  /* activate the OverDrive to reach the 180 Mhz Frequency */  
  ret = HAL_PWREx_EnableOverDrive();
  if(ret != HAL_OK)
  {
    Error_Handler();
  }
  
  /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
  clocks dividers */
  RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;  
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;  
  ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
  if(ret != HAL_OK)
  {
    Error_Handler();
  }
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *   where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


使用特权

评论回复
11
达海8622| | 2016-1-6 20:09 | 只看该作者
貌似很高大上的样子!赞一个!

使用特权

评论回复
12
huangqi412| | 2016-1-6 21:21 | 只看该作者
yklstudent 发表于 2015-12-28 15:53
分散加载文件在IAP下还是蛮好有用的,可以实现IAP和APP两个工程文件整合成一个工程文件 ...

想知道如何合并的,能讲讲么。

使用特权

评论回复
13
yklstudent| | 2016-1-7 13:42 | 只看该作者
huangqi412 发表于 2016-1-6 21:21
想知道如何合并的,能讲讲么。

这个主要在keil arm环境下,
1、设置Linker项采用自定义的SCT分散加载文件;
2、修改SCT分散加载文件;
2.1、把FLASH区分两个或者三个部分,其中一部分存储BOOT程序,剩余的就分配给APP程序;
2.2、把RAM区分成两部分,一部分给BOOT区程序,剩余的就分配给APP程序;
3、修改启动startup_stm32fXXX.s文件;把BOOT升级函数在main函数之前执行;
4、所有中断函数也需要处理
总之实现起来还是挺麻烦的

使用特权

评论回复
14
yklstudent| | 2016-1-7 13:42 | 只看该作者
huangqi412 发表于 2016-1-6 21:21
想知道如何合并的,能讲讲么。

这个主要在keil arm环境下,
1、设置Linker项采用自定义的SCT分散加载文件;
2、修改SCT分散加载文件;
2.1、把FLASH区分两个或者三个部分,其中一部分存储BOOT程序,剩余的就分配给APP程序;
2.2、把RAM区分成两部分,一部分给BOOT区程序,剩余的就分配给APP程序;
3、修改启动startup_stm32fXXX.s文件;把BOOT升级函数在main函数之前执行;
4、所有中断函数也需要处理
总之实现起来还是挺麻烦的

使用特权

评论回复
15
huangqi412| | 2016-1-7 13:52 | 只看该作者
yklstudent 发表于 2016-1-7 13:42
这个主要在keil arm环境下,
1、设置Linker项采用自定义的SCT分散加载文件;
2、修改SCT分散加载文件;

谢谢指点,还是没太明白。
1  BOOT和APP是分时执行的,ROM要分别存储,但RAM应该不用分隔,可以共用整个RAM区域。
2  修改STARTYUP,将BOOT升级函数在MAIN前执行没看明白。  用一个工程包含BOOT和APP功能代码? APP的主函数MAIN,   BOOT主函数MAIN_1, 将MAIN_1在MAIN前执行?
3  BOOT和APP都有中断函数  BOOT和APP各自要中断向量表,中断向量表在STARTUP里面,只有一份STARTUP的话只有一份中断向量表,

使用特权

评论回复
16
yklstudent| | 2016-1-7 17:44 | 只看该作者
huangqi412 发表于 2016-1-7 13:52
谢谢指点,还是没太明白。
1  BOOT和APP是分时执行的,ROM要分别存储,但RAM应该不用分隔,可以共用整个R ...

1、实际使用时,RAM还是需要分开;
2、startup文件内进行如下修改,修改范例如下所示:
2.1 增加IMPORT sFlashCheck(Reset_Handler处)
2.2 增加LDR R0,=sFlashCheck(ApplicationStart处)
BLX R0(这个要放在__main之前)
3、中断就让给APP吧,BOOT就别用了;

使用特权

评论回复
17
huangqi412| | 2016-1-7 22:18 | 只看该作者
yklstudent 发表于 2016-1-7 17:44
1、实际使用时,RAM还是需要分开;
2、startup文件内进行如下修改,修改范例如下所示:
2.1 增加IMPORT s ...

这。。。usb boot呢  不用中断不可能

使用特权

评论回复
18
yklstudent| | 2016-1-8 08:41 | 只看该作者
huangqi412 发表于 2016-1-7 22:18
这。。。usb boot呢  不用中断不可能

boot和APP区都需要用?
那你就想好怎么区分然后分别处理

使用特权

评论回复
19
huangcunxiake|  楼主 | 2016-1-11 15:32 | 只看该作者
分散加载机制允许为链接器指定映像的存储器映射信息,可实现对映像组件分组和布局的全面控制

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

192

主题

3417

帖子

10

粉丝