레이블이 Lang-LinkerScript인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Lang-LinkerScript인 게시물을 표시합니다. 모든 게시물 표시

4/04/2022

ESP32 의 Memory 구성-5 (Software 분석 )

1. ESP32의 Software Memory 분석


  • ESP32의 HW Memory 관련분석 
ESP32 Memory Address Map 과 전체구성 


ESP32 External Memory 관련구성내용  


상위내용들은 ESP32의 HW입장에서 Memory 사용분석이며, SW입장에서의 Memory 구성을 Linker Script 와 Map파일로 알아보기로 한다.  

Linker Script의 기본이해 


1.1. ESP32의 지원 Macro attribute 확인  

Linker Script와 같이 ESP32에서 제공해주는 MACRO로  _attribute_ 을 이용하여 Section을 정의하고 거기에 할당하는 것인데, 
각 이름을 쉽게 변경하여 제공해주고 있다.  
예를들면 IRAM_DATA_ATTR 은 __attribute__((section(".iram.data"))) 이런식으로 Linker Script에서 정의한 특정영역에 배치가 가능하다. 

물론 본인이 원하면, 새로운 영역을 만들어서, attribute로 정해 별도의 할당도 가능하다. 
오래전에 리눅스에서 Linker Script와 같이 attribute를 이용하여 많이 설정 사용하던 방법이나, 이미 ESP32에서 만들어놓은 것들이 있음므로, 
가급적 아래의 룰을 지키면서 하면 거의 수정할 필요는 없다. 

  • ESP32 Memory Type (Macro Attribuite)
각 Attribute에 정의된 Section는 Linker Script와 같이 연결되어 동작되므로 반드시 숙지해야하며 아래의 Manual 참고 

  • ESP32 Memory Tyep (Attribuite 의 Section)
IRAM 
IRAM_ATTR".iram1" 시작영역, (Interrupt Handler 주로사용)
이외 IRAM을 DATA 로 정의해서 사용하는 부분도 상위 링크에서 확인가능 

DRAM
DRAM_ATTR".dram1" 시작영역, Data 저장용으로 SRAM 

DMA
DMA_ATTR:  ".dram1" 4byte align의 DRAM_ATTR이용, User는 MALLOC_CAP_DMA 이용

External BSS영역 
EXT_RAM_ATTR ".ext_ram.bss"로 시작영역, SPIRAM의 BSS 영역지정

RTC 관련영역들 
RTC_DATA_ATTR : ".rtc.data"  시작영역
RTC_RODATA_ATTR : ".rtc.rodata" 시작영역 
RTC_SLOW_ATTR : ".rtc.force_slow" 시작영역
RTC_FAST_ATTR : ".rtc.force_fast" 시작영역

NOINIT 영역들 
__NOINIT_ATTR :  ".noinit" 시작영역, SRAM 내부의 noinit Section
RTC_NOINIT_ATTR".rtc_noinit" 시작영역, RTC를 위한 noinit Section 

noinit Section은 보통 Loader가 이영역을 건들지 않기 때문에 유용하게 사용가능하다.
예를들면, System Reset하여도 초기화가 되지 않아 보관하고자 하는 정보를 저장가능  

  • system.map 기반으로 쉽게파악 
상위영역을 간단히 찾는 방법은 본인이 build한 system.map에서 쉽게 찾을 수 있으며, 상위영역은 prefix이며 조건에따라 naming이 되어진다. 

  • attribuite 의 section 과 Linker script 
Linker Script는 초기부팅하고 전체구성이므로, 보통 중요한 것들은 ".iram0" 과 ".dram0 기반으로 Assembly와 함께 거의 다 구현되어있다. 
그리고, 각 영역들을 찾아보며 구성이 어떻게되어있는지 쉽게 파악가능하다. 
아래는 현재 가장 많이 사용하는 EXT_RAM_ATTR와 Linker Script 연결구성이며,본인도 다 찾기가 귀찮다. 
  
  • ESP32 IDFv4.2 attribuite 의 Section 확인 
상위 attribute들의 각 Section들을 ESP32 IDF 소스에서 직접 확인하며, 각 버전마다 다를 수 있으므로 반드시 아래에서 확인 


  • ESP32 각 영역 의 예로 IRAM (세부내용은 아래링크 참고)
IRAM(Instruction RAM) Manual 
  1. iram0_0_seg 영역 
  2. 0x4008000 - 0x400A0000 : 0x2000
  3. PRO/APP CPU를 위해서 64K MMU로 사용 


1.2. ESP32 의 Linker Script 기본구조파악    

  • A. ESP32 Core  전체구성확인
ESP32 Core의 기본구성파일로 Cmake/Makefile/Linker Scirpt 들 비롯하여 가장 필수적인 파일들이 존재하므로 구성파악 

  • B. ESP32 Cmake/Makefile 확인
예전에는 순수 Makefile기반으로 했는데, Cmake라서 보기도 편하지만, 다만 component 개념이 있어 좀 그렇다.  
아래의 Cmake/Makefile/Component.mk 에서 Linker Script가 어떤 조건일때 각각 연결되는지만 대충파악하면 된다. 

ESP32 Linker Script 파일들 확인  
상위 Cmake/Makefile/Component.mk와 같이 어떻게 연결되는지를 확인 

ESP32의 Core 기본 Linker Script 
ESP32의 기본 Core Linker Script로 Memory Segment가 기본구성되어있으므로 반드시 이해 
상위 System.map에서 언급된 Segment 중심으로 Linker Script 보도록 하자. 


1.3. Linker Script 의 Memory

우선적으로 Linker Script의 Memory 에서 각 Segment의 주소와 길이 및 기본역할들을 파악하자. 
  • MEMORY Command의  Segment 확인 
가장먼저 확인해야 할 것이 Segment 기반으로 주소/길이 와 R/W/X 확인 될 것 같다.
그리고, 이 안에 무엇을 넣을지는 각 Section 에서 정의된 부분을 찾아 확인하면된다. 
  1. iram0_0_seg(RX) : PRO CPU위한 공간이며 MMU로 이용 
  2. iram_0_2_seg(RX) : SPI Flash를 Mapping하기 위해서 사용
  3. dram0_0_seg(RW): 공유되어진 DRAM이며 BT사용할 경우 변경  
  4. drom0_0_seg(R): DRAM으로 Flash Mapping 
  5. rtc_iram_seg(RWX): RTC FAST용 IRAM
  6. rtc_data_seg(RW): RTC FAST 용 DRAM
  7. rtc_slow_seg(RW): RTC SLOW 용 ULP용으로 사용 
  8. extern_ram_seg(RWX): External Memory로 Text /data  ESP기준으로 보면 I/DRAM
R: Read / X: Execute /W:Write 

  • Segment alias

  • Linker Script Manual 
MEMORY Command (Segment)  


1.4. Linker Script 의 Segment  

상위에서 파악된 Segment가 실제로 어떻게 구성이 되는지를 파악하자. 
아래와 같이 각 Section을 파악하고 이와 연결하는 구성을 파악하자. 
더불어 ESP32에서 제공하는 mapping의 의미도 알아보자 

  • iram0_0_seg 의 Section 구성의 예  
iram0_0_seg의 구성을 간단히 보는 것이며, 이외 segment들은 생략 

.iram0.vectors: ISR 구성확인 

.iram0.text : Code 영역

  • Linker Script Manual 
SECTION Manual 

  • ESP32 Linker Script Manual
ESP32의 Linker Script Manul로 내부에서 제공되는 별도의 내용이 있으므로 반드시 확인

  • ESP32에서 사용하는 mapping
Linker Script Template 기능이라고하며, 미리 이 Template를 만들고 나서 쉽게 연결시키는 구조이다. 
ESP32의 Compiler도 GCC기반인데, GCC에서도 이기능을 지원을 해주는 것 같다.

Linker Script Template
Default scheme 정보확인 



2. build/system.map 분석  

Linker Script 결과를 가장 쉽게 확인하는 방법은 역시 map파일로 보면되며, 실제 어떻게 연결되었는지 자세히 알아볼수 있다. 
각 Segment 뿐만 아니라 Section들도 각 다 확인가능하며, Symbol Table도 다 확인가능하다.
 
  • map파일 분석 
$ cat build/*.map  // Memory Map 구성확인 
....

Memory Configuration    //Linker Script에서 각 Segment 확인 (Embedded Memory:SRAM) 

Name             Origin             Length             Attributes
iram0_0_seg      0x0000000040080000 0x0000000000020000 xr    // 131072  Instruction        SRAM0 MMU (Table 117) (Table 2)
iram0_2_seg      0x00000000400d0020 0x000000000032ffe0 xr    // 3.3M    Instruction        External Memory (Table 1) 
dram0_0_seg      0x000000003ffb0000 0x000000000002c200 rw    // 180736  Data               SRAM2 MMU (Table 117)(상위 SRAM1/2의 Data Segment) 와 dram heap
drom0_0_seg      0x000000003f400020 0x00000000003fffe0 r     // 4M      Data               External Memory (Table 1)
rtc_iram_seg     0x00000000400c0000 0x0000000000002000 xrw   // 8K      Instruction        Embedded Memory (Table 1)  RTC FAST MPU  (Table 117)
rtc_data_seg     0x000000003ff80000 0x0000000000002000 rw    // 8K      Data               Embedded Memory (Table 1)  RTC FAST MPU  (Table 117)
rtc_slow_seg     0x0000000050000000 0x0000000000001000 rw    // 4K      Data/Instruction   Embedded Memory (Table 1)  RTC SLOW MPU  (Table 117)
extern_ram_seg   0x000000003f800000 0x0000000000400000 xrw   // 4M      Data               External Memory (Table 1)
*default*        0x0000000000000000 0xffffffffffffffff
.....
_heap_start                                       esp-idf/soc/libsoc.a(soc_memory_layout.c.obj)
_init                                             c:/users/jhlee/.espressif/tools/xtensa-esp32-elf/esp-2020r3-8.4.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/esp32-psram/no-rtti/crti.o
_init_start                                       esp-idf/esp32/libesp32.a(cpu_start.c.obj)

....
//SRAM1/2의 dram0_0_seg 의 heap Start 과 End 
.dram0.heap_start
                0x000000003ffbfbe0        0x0
                0x000000003ffbfbe0                . = ALIGN (0x8)
                0x000000003ffbfbe0                _heap_start = ABSOLUTE (.)
                0x0000000000000001                ASSERT (((_iram_end - ORIGIN (iram0_0_seg)) <= LENGTH (iram0_0_seg)), IRAM0 segment data does not fit.)
                0x0000000000000001                ASSERT (((_heap_start - ORIGIN (dram0_0_seg)) <= LENGTH (dram0_0_seg)), DRAM segment data does not fit.)
                0x000000003ff40000                PROVIDE (UART0 = 0x3ff40000)
                0x000000003ff42000                PROVIDE (SPI1 = 0x3ff42000)
                0x000000003ff43000                PROVIDE (SPI0 = 0x3ff43000)
.......
                0x000000003ff96530                __mb_cur_max = 0x3ff96530
                0x000000003ff96458                __sf_fake_stderr = 0x3ff96458
                0x000000003ff96498                __sf_fake_stdin = 0x3ff96498
                0x000000003ff96478                __sf_fake_stdout = 0x3ff96478
                0x000000003ff96540                __wctomb = 0x3ff96540
                0x0000000040001778                close = 0x40001778
                0x000000004000178c                open = 0x4000178c
                0x00000000400017dc                read = 0x400017dc
                0x00000000400017f4                sbrk = 0x400017f4
                0x0000000040001808                times = 0x40001808
                0x000000004000181c                write = 0x4000181c
                0x000000003ffbfbe0                _static_data_end = _bss_end
                0x0000000040000000                _heap_end = 0x40000000
                0x000000003ff80000                _data_seg_org = ORIGIN (rtc_data_seg)
... 

상위 시작주소는 이미 ESP32 기본 Linker Script에 의해 정해져있고, Lens도 정해져있다.
그러므로, 각각의 좌측 Segment Name을 클릭해보면 알수 있다. 
주의: 동적으로 변한다면 sdkconfig의 각 CONFIG 의해 변경. 

esptool.py 의 Memory Map 

  • buuld 후 idy.py로 size 분석 
$ idf.py size   // *.elf 생성 후 *.map 기반으로 SIZE 분석 
.....
Total sizes:
 DRAM .data size:   24808 bytes
 DRAM .bss  size:   48160 bytes
Used static DRAM:   72968 bytes ( 107768 available, 40.4% used)      // 72968+107768  = 180736 -> 0x2C200 (dram0_0_seg)
Used static IRAM:  115369 bytes (  15703 available, 88.0% used)      // 115369+15703  = 131072 -> 0x20000 (iram0_0_seg)
      Flash code:  869907 bytes
    Flash rodata:  185032 bytes
Total image size:~1195116 bytes (.bin may be padded larger)

$ idf.py size-files      // 상위보다 좀 더 자세히 각 Build된 파일 별로 볼 수 있음 
$ idf.py size-components // ESP32의 각 Components 별로 볼 수 있음 


3. 소스에서 사용되어지는 Memory Map

ESP32의 Memory Map 정의 파일로 Linker Script 와 같이 사용되지 않는 것으로 보이며,
ESP32 IDF 소스에서 직접 Address Map 주소가  필요할때 이용하는 것으로 보인다.

실제 프로그램에서는 아래 주소를 사용을 한다는 말이며, 각 부분이 어떻게 사용되는지는 ESP32 IDF를 전체 분석해야하나, 생략한다. 
Virtual Addresss 관련부분 추후 찾기  

/* Overall memory map */
#define SOC_DROM_LOW            0x3F400000         //V Addr0
#define SOC_DROM_HIGH           0x3F800000         //V Addr0
#define SOC_DRAM_LOW            0x3FFAE000         //V Addr0
#define SOC_DRAM_HIGH           0x40000000         //V Addr0
#define SOC_IROM_LOW            0x400D0000         //V Addr1
#define SOC_IROM_HIGH           0x40400000
#define SOC_IROM_MASK_LOW       0x40000000
#define SOC_IROM_MASK_HIGH      0x40064F00


//DRAM (Data RAM)          bss, init, data, heap 할당
//IRAM (Instruction RAM)   text 할당 

#define SOC_CACHE_PRO_LOW       0x40070000     // SRAM0
#define SOC_CACHE_PRO_HIGH      0x40078000     // SRAM0   
#define SOC_CACHE_APP_LOW       0x40078000     // SRAM0  
#define SOC_CACHE_APP_HIGH      0x40080000     // SRAM0 iram0_0_seg     

#define SOC_IRAM_LOW            0x40080000     // SRAM0 iram0_0_seg  MMU, pagesize기반으로 mapping (세부사항, Datasheet) 
#define SOC_IRAM_HIGH           0x400A0000     // SRAM0 iram0_0_seg  MMU, pagesize기반으로 mapping (세부사항, Datasheet)    

#define SOC_RTC_IRAM_LOW        0x400C0000
#define SOC_RTC_IRAM_HIGH       0x400C2000
#define SOC_RTC_DRAM_LOW        0x3FF80000
#define SOC_RTC_DRAM_HIGH       0x3FF82000
#define SOC_RTC_DATA_LOW        0x50000000
#define SOC_RTC_DATA_HIGH       0x50002000
#define SOC_EXTRAM_DATA_LOW     0x3F800000     //V AddrRAM
#define SOC_EXTRAM_DATA_HIGH    0x3FC00000


//First and last words of the D/IRAM region, for both the DRAM address as well as the IRAM alias.
#define SOC_DIRAM_IRAM_LOW      0x400A0000     // SRAM1
#define SOC_DIRAM_IRAM_HIGH     0x400C0000     // SRAM1   
#define SOC_DIRAM_DRAM_LOW      0x3FFE0000     // SRAM1,2    
#define SOC_DIRAM_DRAM_HIGH     0x40000000     // SRAM1,2


// Region of memory accessible via DMA. See esp_ptr_dma_capable().
#define SOC_DMA_LOW  0x3FFAE000
#define SOC_DMA_HIGH 0x40000000

// Region of memory that is byte-accessible. See esp_ptr_byte_accessible().
#define SOC_BYTE_ACCESSIBLE_LOW     0x3FF90000
#define SOC_BYTE_ACCESSIBLE_HIGH    0x40000000

//Region of memory that is internal, as in on the same silicon die as the ESP32 CPUs
//(excluding RTC data region, that's checked separately.) See esp_ptr_internal().
#define SOC_MEM_INTERNAL_LOW        0x3FF90000
#define SOC_MEM_INTERNAL_HIGH       0x400C2000

// Start (highest address) of ROM boot stack, only relevant during early boot
#define SOC_ROM_STACK_START         0x3ffe3f20

ESP32의 soc.h Memory Map  


  • 상위 값을 간단히 정리 
SOC_CACHE_PRO   :  0x40078000 - 0x40070000  = 0x8000   // SRAM0
SOC_CACHE_APP   :  0x40080000 - 0x40078000  = 0x8000   // SRAM0

SOC_IRAM        :  0x400A0000 - 0x40080000  = 0x20000  // SRAM0
SOC_DIRAM_IRAM  :  0x400C0000 - 0x400A0000  = 0x20000  // SRAM1
SOC_DIRAM_DRAM  :  0x40000000 - 0x3FFE0000  = 0x20000  // SRAM1-> SRAM2


4. ESP32 Memory 최적화방법 

ESP32 Manual에서 자세히 설명을 해주고 있으며, Memory 최적화들은 아래의 링크들로 확인하자. 

  • ESP32 IRAM 최적화방법 
WIFI /BT를 동시에 사용하거나, IRAM 전용으로 선언으로 사용시 이를 최적화해야함

  • ESP32 SRAM의 Heap 최적화방법 
이미 상위 그림에서 있듯이 BT 혹은 다른 CONFIG 설정들을 통해 최적화가 가능하며, Heap을 디버깅도 가능하다.  

  • ESP32 SRAM의 IRAM 문제 및 메모리최적화 
iram0 이 사이즈 문제가 되는 경우들이지만, Linker Script에서 수정하는 것은 아닌것 같다. 

  • ESP32의 RAM 사용법 
RAM을 적게 사용하려면, Dynamic으로 사용하고 각 TaskCreat를 할때 Stack은 Heap에서 오기때문에, 적은양의 Stack을 정하라는 이야기이다.  
그리고 사용하지 않은 CONFIG들은 설정을 하지말자 

  • SRAM의 DRAM의 제약사항
각 할당되어지는 Max 사이즈가 있으므로 확인 

4/07/2018

Linker script

1. Linker Script 란? 

Linker Script or Linker Command 라고 불리우며, Compiler에 따라 이 이름은 달라질 수 있다.
기능은 Linker에게 Command/Script로 명령을 주어 관련 Program을 환경설정을 변경하는 것이 주목적이다

일반 애플리케이션 사용자는 사용할 일은 거의 없을 것이라고 생각되며, Kernel / Uboot 같은 소스를 수정하는 사람들이 주로 사용하지만 
요즘은 거의 사용할 일은 없다.
예를 들면, 주로 수정하여 고친다면, Interrupt Service Routine 을 새로 만들어서 SRAM에 올리거나, 
특정 Program 안에, 특정 Address 에 특정 Section을 만들어 할당하거나, 
나만의 Section을 만들어 특정 Address 할당 하는데 주로 사용한다. (ROM/RAM/SRAM)


1.1. Linker Script 기본 Manual

일반적으로 컴파일러는 자동으로 Linker Script를 생성해서 만들어주므로, 크게 신경을 쓰지 않아도 된다. 
하지만, 일반적으로 OS / Boot Loader 및 MCU 기반의 Compiler들은 ELF format을 직접이용하기보다는 특정영역에 확보하고 사용해야하는 프로그램은
관련 세부설정 부분들을 직접정의하고 각 소스에 이를 설정하여 연결한다.

주로확장자는 .ld or lds로 되어있지만, 컴파일러마다 조금씩 다를수 있으며, 이 파일들을 찾아 아래 메뉴얼들과 같이 보며 
기본동작 방식을 이해하면 될 것 같다.
항상 세부적인것은 현재 사용중인 컴파일러 메뉴얼기반으로 봐야함



SECTIONS
{
  . = 0x10000;
  .text : { *(.text) }
  . = 0x8000000;
  .data : { *(.data) }
  .bss : { *(.bss) }
}

  • 상위 SECTIONS 구성
  1. .text: code의 영역
  2. .data: 초기화된 전역변수 or static 변수
  3. .bss : 비초기화된 전역변수 or static 변수
  4. heap: bss 다음으로 연결되어지며, malloc/calloc과 연결 
  5. .noinit: init를 하지 않는 전역변수   
heap은 동적 Memory 저장하며, 일반적으로 stack은 함수호출 및 일반변수의 저장을 담당하며, heap기반으로 구성 
  https://en.wikipedia.org/wiki/Data_segment
  

1.2  GCC의 MACRO attribute

GCC Compiler의 MACRO인 attribute 설정과 Linker Script 밀접한 관계에 있으며, 이는 곳 특정영역에 넣는 것이 가능하다. 
예를드면,  C/C++ Code에 MACRO인 attribute를 설정하여 Linker script와 연결하여 동작가능하다.
이는 GCC 뿐만 아니라 다른 Compiler라도 사용방법은 얼추 비슷하며, Linker Script의 이름은 변경될 수가 있다. (TI Compiler는 Linker Command)
이외 Compiler 전용 MACRO가 있으므로, 그 부분은 각 Complier Manual 참조 

Attribute는 Function 과 Variable 에 사용될 수 있으며, 각각 설정방법은 Manual을 보자.

  • GCC Manual Index
GCC 전체 Manual 에 Index 기능으로 쉽게 보고자하는 파트를 찾을수 있다.
  https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/

  • MACRO  __attribute__
__attribute__로 Section을 설정하여, 함수(Function)과 변수(variable)를 Linker Script의 특정 Section에 할당가능하다. 
Function에 속성을 설정하여 Linker Script와 같이 연동하여 특정영역에 놓을 수도 있으며, 할당크기 및 다양한 설정이 가능하다
Variable , 즉 변수도 Linker Script와 같이 연동하여 특정영역뿐만 아니라 다양한 속성설정이 가능하다.
e.g. __atrribute__(section)
  https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/Function-Attributes.html#Function-Attributes
  https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/Variable-Attributes.html#Variable-Attributes


  • MACRO #pragmas
  https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/Pragmas.html#Pragmas
  https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/ARM-Pragmas.html#ARM-Pragmas



1.3 Kernel Linker Script 분석예제

Uboot/Kernel의 Linker Script를 분석들을 기본적으로 보고 이해를 하면될 것이고 더나아가 ISR 수정하거나, 변경한다면, 관련부분 전용 Linker Script 수정하면 될거 같다.

$ find . -name *.lds
$ vi kernel/arch/arm/boot/compressed/vmlinux.lds
$ vi kernel/arch/arm/kernel/vmlinux.lds

Linker Script 및 Kernel에 자료는 인터넷에 풍부하므로, 정리하지 않고, Link만 연결


  • Makefile 과 Kernel Linker Script 분석
Makefile 과 Linker Script는 밀접하게 연관되어 있으며, 우선적으로 Makefile부터 분석해야, 현재 사용중인 Linker Script도 확인가능 
  https://wiki.kldp.org/KoreanDoc/html/EmbeddedKernel-KLDP/arm.makefile.html

  • Kernel Linker Script 분석한 Blog들
Kernel Linker Script를 보면, 다양한 Section들이 나오며, 각 Section 의미를 알아보고 분석하자.
본인이 원하는 새로운 Section에 새로운 이름으로 만들어 추가도 가능하다. 
아래보기전에, Linker Script Manual부터 확인 
LDFLAGS 와 각 CONFIG기반의 Address 와 소스확인 
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/Makefile

ARM Kernel Image
build 후에는 System.map기반으로 확인하고, boot 관련부분을 확인 
반드시 Kernel CONFIG와 같이 확인
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/boot
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/boot/compressed/Makefile
MMU/SMP/XIP/TCM 등 각 설정기반의 설정확인 
  http://elixir.free-electrons.com/linux/v3.19.7/source/arch/arm/boot/compressed/vmlinux.lds.S

ARM Kernel Vmlinux
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/kernel
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/kernel/Makefile
  http://elixir.free-electrons.com/linux/v3.19.7/source/arch/arm/kernel/vmlinux.lds.S

빌드후, 아래와 같이 arch/arm/kernel/vmlinux.lds 생성

ARM IRQ 관련 부분
  http://elixir.free-electrons.com/linux/v3.19.7/source/arch/arm/kernel/irq.c
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/include/asm/exception.h#L14
  http://elixir.free-electrons.com/linux/v3.19.7/source/arch/arm/kernel/fiq.c

IRQENTRY_TEST
  https://elixir.bootlin.com/linux/v3.19.7/source/arch/arm/kernel/vmlinux.lds.S
  https://elixir.bootlin.com/linux/v3.19.7/source/include/asm-generic/vmlinux.lds.h#L440


  • ARM의 IRQ 부분 
관련된 소스를 간단하게 역추적하면서 어떻게 사용하는 지 분석해보자.

---------------------------arch/arm/kernel/irq.c
asmlinkage void __exception_irq_entry       // Exception 영역으로 
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
 handle_IRQ(irq, regs);
}
---------------------------arch/arm/include/asm/exception.h
#define __exception __attribute__((section(".exception.text")))  // GCC attribute 관련부분 참조 
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
#define __exception_irq_entry __irq_entry
#else
#define __exception_irq_entry __exception
#endif
 
---------------------------include/asm-generic/vmlinux.lds.h

#ifdef CONFIG_FUNCTION_GRAPH_TRACER   // CONFIG가 존재하면 사용함 
#define IRQENTRY_TEXT       \
  ALIGN_FUNCTION();     \
  VMLINUX_SYMBOL(__irqentry_text_start) = .;  \
  *(.irqentry.text)     \
  VMLINUX_SYMBOL(__irqentry_text_end) = .;
#else
#define IRQENTRY_TEXT
#endif

---------------------------arch/arm/kernel/vmlinux.lds.S

.text : {   /* Real text segment  */
  _stext = .;  /* Text and read-only data */
   __exception_text_start = .;
   *(.exception.text)         // 상위 exception 영역 
   __exception_text_end = .;
   IRQENTRY_TEXT      // header file define 되어있음 
   TEXT_TEXT
   SCHED_TEXT
   LOCK_TEXT
   KPROBES_TEXT
   IDMAP_TEXT
#ifdef CONFIG_MMU
   *(.fixup)
#endif
   *(.gnu.warning)
   *(.glue_7)
   *(.glue_7t)
  . = ALIGN(4);
  *(.got)   /* Global offset table  */
   ARM_CPU_KEEP(PROC_INFO)
 }


asmlikage 의 의미 
  http://egloos.zum.com/studyfoss/v/4951809

  • ARM9에서 많이 사용했던 TCM 관련부분 
ARM9에서 TCM(Tightly Coupled Memory)는 주로 SRAM용도로 사용하며, 아래와 같이 ITCM(Instruction)/DTCM(Data)으로 분리하여 사용한다.
ARM9뿐만아니라, Cortex에서도 사용되며, 예를 들면, Deep Sleep용 ISR 기능 뿐만아니라, 다양한 빠른 Latency 목적으로 사용가능하다고 한다.
 
#ifdef CONFIG_HAVE_TCM
 /*
  * We align everything to a page boundary so we can
  * free it after init has commenced and TCM contents have
  * been copied to its destination.
  */
 .tcm_start : {  //.tcm_start는 Instruction으로 시작
  . = ALIGN(PAGE_SIZE);              //PAGE SIZE 할당
  __tcm_start = .;            //__tcm_start  : . 현재 Address 할당 
  __itcm_start = .;                  //__itcm_start : . 현재 Address 할당 (아래참고)   
 }

 /*
  * Link these to the ITCM RAM
  * Put VMA to the TCM address and LMA to the common RAM
  * and we'll upload the contents from RAM to TCM and free
  * the used RAM after that.
  */
 .text_itcm ITCM_OFFSET : AT(__itcm_start)   // AT(ADDRESS)이므로, .text_itcm을 __itcm_start로 연결
 {
  __sitcm_text = .;    // Start ITCM TEXT 
  *(.tcm.text)         // ITCM TEXT로 Instruction  
  *(.tcm.rodata)
  . = ALIGN(4);        // ALIGN 2의 지수로만 할당(공백할당) 
  __eitcm_text = .;    // End ITCM TEXT 
 }

 /*
  * Reset the dot pointer, this is needed to create the
  * relative __dtcm_start below (to be used as extern in code).
  */
 . = ADDR(.tcm_start) + SIZEOF(.tcm_start) + SIZEOF(.text_itcm);  // 현재 Address를 변경 사이즈기반 

 .dtcm_start : {   // .tcm_start 뒤에 .dtcm_start 구성 (현재 Address 할당) 
  __dtcm_start = .;
 }

 /* TODO: add remainder of ITCM as well, that can be used for data! */
 .data_dtcm DTCM_OFFSET : AT(__dtcm_start)  // AT(ADDRESS)이므로, .data_dtcm을 __dtcm_start 연결
 {
  . = ALIGN(4);       // 공백할당 2의 지수로만 가능 
  __sdtcm_data = .;   // Start DTCM Data 
  *(.tcm.data)       // TCM Data 
  . = ALIGN(4);
  __edtcm_data = .;  // End DTCM Data  
 }

 /* Reset the dot pointer or the linker gets confused */
 . = ADDR(.dtcm_start) + SIZEOF(.data_dtcm);  // 사이즈 측정하여 현재 Address 변경 

 /* End marker for freeing TCM copy in linked object */
 .tcm_end : AT(ADDR(.dtcm_start) + SIZEOF(.data_dtcm)){  // .tcm_end에 itcm 과 dtcm 끝나는 곳으로 설정 
  . = ALIGN(PAGE_SIZE);
  __tcm_end = .;
 }
#endif



  • Davinci Deep Sleep Source (TCM이용)
오래전에, 이것때문에 TI에서 교육을 받은 적이 있는데, 이렇게 오픈되어있다니 좋다 
소스의 내용은 Sleep할때는 DRAM을 Self Refresh로 변경하여 Deep Sleep으로 가며, 
깨어날때는 DRAM을 사용할 수 없으니, TCM기반의 ISR로 SDRAM 설정변경해주는고 원래대로 돌아가는 방식 
  https://www.mail-archive.com/davinci-linux-open-source@linux.davincidsp.com/msg04407.html

  • setup_irq
  http://elixir.free-electrons.com/linux/v3.19.7/source/kernel/irq
  http://elixir.free-electrons.com/linux/v3.19.7/source/kernel/irq/manage.c
  http://elixir.free-electrons.com/linux/v3.19.7/ident/setup_irq

  • request_irq
  http://elixir.free-electrons.com/linux/v3.19.7/ident/request_irq
  http://elixir.free-electrons.com/linux/v3.19.7/source/include/linux/interrupt.h#L128
  https://free-electrons.com/docs/

  • IRQ Proc 정보 등록
  http://elixir.free-electrons.com/linux/v3.19.7/source/kernel/irq/proc.c



2. 특정 Address에 원하는 Data 넣기 

variable attribute 와 Linker script를 이용하여, 본인이 원하는 위치에 데이타를 저장이 가능하다.


  • 저장할 Data를 Code에 Variable Attribute를 이용하여 Section 선언
 unsigned char data[48]  __attribute__((section(".mySection")))  = {0}; 


  • Variable Attribute 와 같이 Linker Script 수정 
Linker Script에서 관련 Section에 0x80200000 에 위치하도록 설정하고 KEEP를 사용하자.

SECTIONS    //SECTIONS Command로 나만의 Region 만들고 이를 Memory 내에 구성된 이름에 할당 
{
  .mySegment 0x80200000 : 
  {
     KEEP(*(.mySection))
  } 
}

SECTIONS Command 
  http://korea.gnu.org/manual/release/ld/ld-mahajjh/ld_3.html#SEC18

  • MPU의 예제구성 
MPU의 경우는 아래와 같이 MEMORY Layout이 정의되어있으므로, 이 부분 확인 

MEMORY           //MEMORY Command기반으로 각 Segment/Name 정의 (주소,길이,RWX) 
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 64K           // RAM Segment (Read and Write and Excute) 
FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 64K        // FLASH Segment (Read and Excute) 
}

SECTIONS
{
  .... // 다른 Segment로 구성하고 나의 영역만들고, 이를 FLASH Segment/Name 할당해서 연결 
  .mySegment 0x80200000 : 
  {
     KEEP(*(.mySection))                // 상위소스에서  __attribute__를 이용하여 이곳에 할당 
  } >FLASH 
} 

MEMORY Command

상위내용
  https://stackoverflow.com/questions/4067811/how-to-place-a-variable-at-a-given-absolute-address-in-memory-with-gcc