Problem 1:
기업체에서 설계한 Arm 프로세서 기반의 임베디드 시스템에 안드로이드 오퍼레이팅 시스템 포팅하려 합니다.
시스템은 삼성전자에서 설계한 Arm 프로세서 코어 기반의 Exynos 프로세서를 사용합니다.
시스템의 하드웨어를 초기화하고 리눅스 기반의 안드로이드 커널을 로딩하는 부트로더는 u-boot를 사용합니다.
그러나 부팅 과정에서 시스템이 자주 멈추는 현상이 발생하기 때문에 이 문제의 원인을 분석하고 해결 방법을 찾아야 합니다.
원인 분석
시스템이 멈추는 현상을 분석하기 위해, 오실로스코드를 이용한 제어 신호(Reset, Address bus, Data bus, etc.)의 파형 분석과 ICE (In-Circuit Emulator) 장비를 시스템에 연결한 다음 디버그 모드에서 코드 실행 등을 통하여 시스템이 멈추는 위치를 특정할 수 있었습니다.
u-boot 코드의 lowlevel_init() 함수를 실행한 후, ROM 영역의 u-boot 코드를 DRAM 영역으로 복 사하는 코드 실행 시에 자주 시스템이 멈추는 현상이 발생하는 것을 알 수 있었습니다.
**lowlevel_init()** 함수는 시스템 하드웨어의 초기화를 담당하고, 캐시 사용을 비활성화 하는 기능을 수 행합니다.
일부 하드웨어의 경우에는 사용하기 전에 하드웨어 설정 값을 입력하고, 입력 값에 대한 결과가 반영되기 까지 얼마간의 대기 시간이 필요할 수 있습니다.
그러나 기업체에서 설계한 시스템의 경우, PCB에 장착된 프로세서와 메모리 시스템 간의 물리적 거리가 멀어서 주어진 시간 내에 설정 값이 반영되지 않는 현상이 발생하는 것으로 판단되었습니다.
lowlevel_init()
copy_codeblock2dram()
💡 lowlevel_init()으로 메모리를 초기화하려했는데 PCB에 물리적 거리가 멀어서 초기화가 안된 상태로copy_codeblock2dram() 실행되서 dram 초기화 전에 복사해서 넣으려고 하니까 뻑?나가서 멈춘 그런 상태.
문제 해결
이 문제를 해결하기 위해 lowlevel_init() 함수와 프로그램 코드 블록을 DRAM 영역으로 복사하는 함 수 사이에 40ms의 지연 시간을 추가하기로 했습니다. 실제 하드웨어 시스템에 적용하기 전에 테스트 환경을 만들어 코드를 작성하고 기능을 검증합니다.
테스트 환경으로 MDK-ARM 툴에서 제공하는 시뮬레이터를 사용합니다.
- 시뮬레이터는 Arm 7 프로세서 코어를 기반으로 합니다. 이 프로세서 코어의 동작 속도는 60 Mhz 이며, 3 stages pipeline을 사용하므로 branch 명령 등으로 인한 pipeline refill 에 소비되는 사이클을 고려합니다.
- 우리가 배운 instruction set을 이용하여 지연 시간을 추가하는 코드를 만들도록 합니다.
- 참고로, Keil MDK uVision을 이용할 경우, 디버그 세션에서 "registers - internal - states" 항목을 보면 시뮬레이터 상에서 실행한 사이클 수를 볼 수 있습니다.
Problem2:
프로그램의 실행 성능 향상을 위해 ROM 영역의 실행 코드를 RAM 영역으로 복사한 다음, 제어권을 복사한 영역의 코드로 넘겨 실행을 계속하도록 합니다.
이는 많은 임베디드 시스템에서 성능 향상을 위해 취하는 일반적인 방법입니다.
개발 중인 시스템에도 이 같은 기능을 적용합니다.
문제 해결
실제 하드웨어 시스템을 대신하여 시뮬레이터 상에서 먼저 기능을 검증하도록 합니다.
- 프로그램은 ROM 영역의 0x0 번지부터 시작합니다. 코드가 동작을 제대로 할 수 있는 지를 검증하기 때문에 복사할 코드 크기를 0x1000 으로 제한합니다. 실제 u-boot의 코드 크기는 bss 시작 주소에서 코드 시작 주소를 빼주면 됩니다.
- 코드 블록을 복사할 주소는 시뮬레이터의 RAM 시작 주소인 0x4000_0000 으로 합니다. 검증이 완료된 후, 실제 하드웨어에 적용할 때는 하드웨어에 적재된 코드 크기와 메모리 주소로 변경해야 합니다.
- 코드 블록을 복사한 후에는 복사한 RAM 영역으로 실행 위치를 옮겨서 코드 블록을 복사하는 명령에 이어서 프로그램을 실행할 수 있도록 합니다.(self-modifying code)
- 코드 블록을 복사하는 기능을 구현할 때 LDM/STM instructions를 사용하도록 합니다.
참고 1:
코드 블록을 메모리로 복사한 것을 확인하기 위해 uVision의 디버그 명령 창에서 memset 명령으로 코드 시작 전에 미리 SRAM 영역을 특정 값으로 채웁니다. 또는 좀 더 편리한 방법으로 debug 메뉴에서 ini 파일을 만들어 사용할 수 있습니다.
options for target... - debug 메뉴로 이동한 다음, 다음과 같이 ini 파일을 만들어 등록해 둡 니다.
memset (0x40000000, 0x40001000, 'a') ; 0x4000_0000 - 0x4000_1000 범위를 'a' 값으로 채운다. LOAD ex4.axf ; 컴파일 후 만들어진 실행 파일 이름이 ex4.axf 인 경우
참고 2:
코드 블록을 복사한 후에 0x4000_0000 번지 영역의 코드를 실행할 수는 없을 것입니다.
이는 시뮬레이터의 SRAM이 read/write 속성을 갖지만 execute 속성을 갖지 않기 때문입니다.
코드의 제어권이 정확한 위치로 이동해서 실행 대기 상태인지 확인하기 위해 디버그 세션의 registers 창과 disassembly 창에서 PC 값이 처음 의도한 주소의 명령을 가리키는 지, 명령어의 메 모리 주소는 정확한지 확인해야 합니다. (화면 캡처)디버그 명령을 이용하여 메모리 속성을 실행 가능 으로 변경할 수 있습니다.
위, ini 파일에 다음 라인을 추가합니다.
map 0x40000000, 0x40001000 read write exec
'기타' 카테고리의 다른 글
CV (Curriculum Vitae) - EJ (0) | 2023.06.28 |
---|---|
[ARM 컴퓨터 구조] delay_loop 구현 및 가상 머신 테스트 구현 과제 - 해결 과정 - 도전1트 (0) | 2023.06.12 |
댓글