이전 과정에서 U-Boot 오픈소스를 clone한 뒤, LS1046ARDB 보드와 QSPI boot 방식에 맞는 defconfig를 적용하여 빌드를 진행하였다. config 설정에 맞게 빌드하면 u-boot.elf, u-boot.bin, u-boot-dtb.bin 등 여러 결과물이 생성된다. 이 중 QSPI boot 방식에서는 RCW와 u-boot-dtb.bin을 QSPI flash의 정해진 offset에 기록한다. 이렇게 보드의 flash에 부트 이미지를 기록하고 실제 보드에서 동작하도록 올리는 과정을 포팅 과정의 일부로 볼 수 있다.
이번 목표는 직접 빌드한 U-Boot를 LS1046ARDB-PB 보드의 QSPI flash에 올려 부팅시키는 것이다.
U-Boot를 올리기 전에 먼저 RCW를 QSPI flash에 기록해야 한다. RCW는 Reset Configuration Word의 약자로, 보드가 부팅될 때 SoC의 초기 동작 조건을 설정하기 위한 초기 설정 데이터이다. 쉽게 말하면 하드웨어가 부팅을 시작할 수 있도록 클럭, PLL, SerDes, 부트 소스, 부트 위치 포인터 같은 기본 설정을 제공하는 역할을 한다.
보드에 전원이 인가되면 SoC 내부의 BootROM/PBL이 부트 모드 설정을 확인하고, 설정된 부트 소스에서 RCW를 읽는다. 현재는 QSPI boot를 사용할 것이므로 BootROM/PBL은 QSPI flash의 시작 영역, 즉 offset 0x0 근처에서 RCW를 읽는다. RCW는 CPU가 직접 실행하는 프로그램이라기보다는 BootROM/PBL이 해석하여 SoC 내부 레지스터 설정에 사용하는 설정값과 PBI 명령들로 구성된다.
LS1046ARDB의 QSPI flash map에서는 보통 flash 앞쪽 약 1MB 영역을 RCW용 영역으로 예약하지만, 실제 RCW 바이너리 크기는 매우 작다. 현재 사용한 rcw_1800_qspiboot.bin.swapped 파일도 실제 크기는 약 208 bytes 정도이다.
RCW 설정이 끝나면 PBI 명령에 의해 다음 부트 단계의 위치가 지정된다. 현재 사용한 rcw_1800_qspiboot.rcw 안에는 boot location pointer가 0x40100000으로 설정되어 있었다. LS1046A에서 QSPI flash가 0x40000000 기준으로 매핑된다고 보면, 이는 QSPI flash offset 0x00100000을 의미한다. 따라서 U-Boot 이미지는 QSPI flash의 0x00100000 위치에 기록해야 한다.
U-Boot가 실행되면 보드 초기화가 계속 진행된다. 이 과정에서 DDR/RAM 초기화가 수행되고, U-Boot는 자신을 RAM으로 relocate하여 RAM 위에서 동작을 이어간다. 이후 UART 콘솔을 통해 U-Boot banner와 prompt가 출력되고, 사용자는 U-Boot 명령어를 입력할 수 있다. 최종적으로 U-Boot는 kernel image, device tree, initramfs 등을 저장 장치나 네트워크에서 불러와 Linux kernel로 제어를 넘긴다.
현재 LS1046ARDB QSPI boot 기준 flash 배치는 다음과 같이 정리할 수 있다.
RCW:
File : rcw_1800_qspiboot.bin.swapped
Offset : 0x00000000
U-Boot:
File : u-boot-dtb.bin
Offset : 0x00100000
즉 전체 부팅 흐름은 다음과 같다.
Power On
→ BootROM/PBL 실행
→ QSPI flash offset 0x0에서 RCW 읽기
→ RCW/PBI 설정 적용
→ QSPI flash offset 0x00100000의 U-Boot 위치로 진행
→ U-Boot 실행
→ DDR/RAM 초기화
→ U-Boot relocate
→ UART 콘솔 출력
→ Linux kernel 로드 및 부팅
U-Boot를 포팅하기 위해 CodeWarrior IDE를 사용하였다. CodeWarrior IDE에서 Target Connection을 보드에 맞게 LS1046A_RDB로 지정한다.

CodeWarrior TAP 연결 방식은 USB 또는 Ethernet 중 하나를 선택할 수 있다. 필요하면 JTAG Speed와 Timeout 값을 조정할 수 있지만, 연결이 불안정할 경우에는 먼저 기본값 또는 낮은 JTAG Speed로 확인하는 것이 좋다.
먼저 U-Boot를 올리기 전에 RCW를 준비해야 한다. RCW는 NXP에서 제공하는 rcw 오픈소스 저장소(https://github.com/nxp-qoriq/rcw)를 사용하였다. 저장소를 clone한 뒤 ls1046ardb 보드에 맞는 디렉토리로 이동한다.

README를 확인하면 디렉토리 이름이 어떤 의미를 가지는지 알 수 있다. LS1046ARDB의 RCW는 RGMII 구성, SerDes lane 구성, SerDes protocol 값, boot source, core clock 등에 따라 여러 조합으로 나뉘어 있다.


LS1046ARDB Reference Manual을 참고하여 현재 보드의 SerDes protocol과 boot source를 확인한다.

내가 사용하는 보드는 SerDes 구성이 RR_FFSSPPPH_1133_5559에 해당하고, QSPI boot를 사용할 것이므로 해당 디렉토리의 rcw_1800_qspiboot.bin.swapped 파일을 사용하였다.

여기서 .swapped 파일은 QSPI flash에 기록할 때 필요한 endian 형식으로 변환된 RCW 바이너리이다. 일반 .bin을 사용했을 때 RCW가 정상적으로 인식되지 않았고, .bin.swapped 파일을 사용했을 때 정상적으로 동작하였다.
다음은 RCW를 QSPI flash에 기록하기 위해 먼저 보드를 복구 가능한 상태로 설정한다.

DIP switch를 Hard-coded RCW 모드로 설정하고, RESET_REQ_B assertion으로 인한 reset을 무시하도록 SW4[8]을 0으로 설정한다. 이 설정은 flash 안의 RCW가 잘못되어 있어도 CodeWarrior가 보드에 접근할 수 있게 하기 위한 복구용 설정이다.

이 상태에서 Flash Programmer를 실행하여 RCW를 QSPI flash에 기록한다.
RCW 기록 설정은 다음과 같다.
`Device`: S25FS512S (QSPI)
`Action`: Program
`File`: rcw_1800_qspiboot.bin.swapped
`Offset`: 0x00000000
`Options`: Unprotect, Erase, Verify
Action을 추가한 뒤 실행하면 RCW가 QSPI flash의 시작 위치에 기록된다. 기록이 완료되면 보드 전원을 끄고 DIP switch를 다시 QSPI boot 모드로 변경한다.
현재 사용한 rcw_1800_qspiboot.rcw 안에는 다음과 같은 PBI 설정이 있다.
write 0x570604, 0x40100000
LS1046A에서 QSPI flash가 CPU 주소 공간의 0x40000000 기준으로 매핑된다고 보면, 0x40100000은 QSPI flash 내부 offset 0x00100000을 의미한다.
0x40100000 - 0x40000000 = 0x00100000
따라서 이 RCW는 다음 단계 부트 이미지인 U-Boot를 QSPI flash offset 0x00100000에서 실행하도록 설정되어 있다.
다음은 U-Boot이다. 최신 U-Boot master branch에는 ls1046ardb_qspi_defconfig가 없고 ls1046ardb_tfa_defconfig만 존재하였다. 하지만 이번 목표는 TF-A 기반 부팅이 아니라 RCW 다음에 QSPI flash의 U-Boot를 직접 실행하는 방식이므로, QSPI defconfig가 있는 브랜치를 사용해야 한다.
공식 U-Boot 저장소의 u-boot-2023.07.y 브랜치에는 ls1046ardb_qspi_defconfig가 존재한다. 해당 config 설정에 맞게 rcw flashing 파이프라인과 동일한 방법으로 진행하면 아래와 같은 결과를 확인할 수 있다.

추가적으로 CodeWarrior에서 CCS 관련 문제가 자주 발생하였다. 특히 Target Connection에서 JTAG Speed와 Timeout 값을 수정한 뒤 CCS: server network timeout, TAP USB open failure같은 에러가 발생하는 경우가 있었다. 이 경우 CodeWarrior와 관련된 프로세스를 종료하고, CodeWarrior TAP USB driver를 다시 설치하거나, TAP과 보드를 power cycle한 뒤 다시 연결하여 해결하였다. 다만 이 문제는 RCW나 U-Boot 이미지 자체의 문제라기보다는 CodeWarrior Connection Server, TAP driver, USB/Ethernet 연결 상태가 꼬이면서 발생한 문제로 추측하지만 정확한 원인을 찾지는 못했다.