[임베디드 레시피 요약 및 정리] Chapter 2. ARM ①

Embedded June·2022년 12월 22일
2

임베디드레시피

목록 보기
4/10
post-thumbnail

1. 들어가며

  • 본문에서는 ARM의 종류, 버전, 특징에 대한 상세한 얘기는 다루지 않고 중요한 부분만 배운다.
  • ARM 프로세서가 어떻게 동작하는지만 알아도 다른 여러 프로세서에 대해서도 자신감이 붙으니 잘 공부하자.
  • ARM을 공부하는 것은 마치 대중목욕탕의 뜨거운 물에 들어가는 것과 같다. 아이들은 너무 뜨거워 발끝을 몇 번 담가볼 뿐 주위만 맴돌지만, 어른들은 ‘시원하다’라는 놀라운 멘트와 아무렇지도 않은 얼굴로 곧바로 뜨거운물에 들어간다. 마찬가지로 ARM은 잘 모르는 사람에게는 어디서부터 시작해야 할지 막막한 세상이지만, 아는 사람에게는 별거 아니다.

  • Chapter 1에서 배웠던 CPU의 구조와 매우 비슷한 ARM7TDMI의 내부 구조다.
    • Register Bank: 32-bit 크기의 레지스터 37개를 통칭하는 말이다.
    • Barrel shifter: 1-Word를 입력받아 shift 또는 rotate 연산을 수행하는 용도로 사용한다.
    • 나머지는 chapter 1에서 다룬 내용 그대로다.

2. ARM modes

2.1. ARM mode VS Thumb mode

  • Thumb mode는 ARM mode의 반쪽짜리 버전이라 볼 수 있으며 16-bit instrunction set이다.
  • ARM은 32-bit RISC 이므로 32-bit를 1-Word로 동작하는 게 최상의 성능을 제공할 수 있는데도 불구하고 thumb mode를 만든 이유는 ‘비즈니스’ 때문이다. ARM이 처음 등장했던 시절에는 임베디드 시스템 시장을 장악하던 메모리가 16-bit data line 위주였기 때문이다.
  • 물론 ARM mode로도 당시 유행하던 메모리를 사용할 수 있었지만, 하나의 instruction을 사용하기 위해 매번 2회 fetch하므로 성능면에서 굉장히 낭비였을 것이다. 따라서 thumb mode를 만든 것이다.

2.2. ARM 동작 modes

  • ARM은 위와같이 총 7가지 mode로 동작한다. 각 Mode의 특성을 잘 이해한다면, 조금 더 적합한 mode를 선택해서 SW를 구성할 수 있게 된다.
  • ARM은 크게 User mode와 Privileged mode로 나뉜다. Privileged mode는 내부적으로 6개의 mode로 나뉜다.
    1. Privileged mode는 서로간에 mode 변경이 자유롭지만, User mode → privileged mode는 불가능하다.
    2. Privileged mode는 IRQ/FIQ 등의 인터럽트 사용 가능 유무를 포함해 다양한 시스템 기능을 직접 설정할 수 있다.
  • ARM의 default mode는 ‘SVC(Supervisor)’ 모드다. Boot-up 시에 ARM의 모든 기능을 사용하기 위해서 SVC 모드로 진입해야 하기 때문이다.
  • ARM mode와 관련된 이야기는 앞으로 계속해서 등장하므로 여기서는 개념만 이해하자.

2.3. ARM 레지스터와 context

  • ARM core는 총 37개의 레지스터를 가지고 있는데, 동작 mode가 바뀌면 사용하는 레지스터 set도 바뀐다.
    • USR와 SYS는 같은 레지스터를 사용한다. R0~R14까지 15개를 사용한다.
    • FIQ는 자신만의 R8~R14 레지스터를 갖고있다. SPSR 까지 합쳐 8개를 사용한다.
      • 다른 mode들과 달리 FIQ만 레지스터를 많이 갖고 있는 이유는 빠른 연산을 위해서다. FIQ는 빠르게 인터럽트를 처리해줘야 하기 때문에 레지스터를 많이 가지고 있으면 있을수록 빠른 처리가 가능하다.
    • SVC, ABT, IRQ, UND는 각각 자신만의 R13, R14, SPSR를 사용해 총 12개를 사용한다.
    • 마지막으로 PCCPSR 레지스터를 합치면 총 37개다.
    • 모든 mode가 공유하는 레지스터는 PC, SPSR, R0~R7이다.
  • 여기서 CPSR과 SPSR이라는 처음 보는 레지스터가 등장하는데, PSR(Program Status Register)의 두 종류로, CPSR은 Current PSR, 즉 현재 시스템의 상태를 나타내고 SPSR은 Saved PSR, 즉 이전 시스템 상태를 백업하는 레지스터다.
  • Thumb mode를 사용한다면 R8~R12는 사용하지 않는다.
  • OS 전공수업에서 숱하게 들었던 ‘context switching’의 context가 바로 저 register set을 의미한다.
    • 프로세스 A를 실행할 때의 R0~R14, CPSR값을 stack과 SPSR에 백업한 뒤에
    • 프로세스 B의 R0~R14, CPSR값을 stack에서 꺼내서 덮어씌우면,
    • 프로세스 B에서 뭘 하려고 했었는지, 뭘 하고 있었는지, 뭘 했는지 등을 다 파악할 수 있는 것이다.
    • 즉, 특정 순간의 context를 저장/복원하면 그 특정 순간으로 돌아갈 수 있다는게 context switching이다.
    • 이때 각 프로세스 또는 함수가 어떤 레지스터를 사용해서 작업할지는 컴파일 단계에서 결정된다. ([※] 그래야 어떤 레지스터를 스택에 저장할지 알 수 있고 그거에 맞게 기계어를 만들겠지요?)
  • ([※] 얼마전에 적은 내용 또 적으려니 힘드네요 ㅠ
    (링크) 게시글 3.1절에 ARM mode 및 PSR에 대해 더 자세한 내용을 정리 및 요약 해놨습니다!)

AAPCS (ARM Architecture Procedure Call Standard)

  • ARM은 각 레지스터의 사용법에 대한 표준을 만들었고 이를 표준 버전에 따라 PCS, APCS, AAPCS라고 부른다.
  • ARM 컴파일러는 이 표준에 맞춰 기계어를 만든다.
  • ‘Synonym’이란, 기존 레지스터의 별명 같은 것이다.
  1. R0~R3 (a1~a4) (Argument, Result, Scratch)
    • R0~R3는 함수에서 argument(parameter)로 넘길 때 사용한다.
    • 예들 들어, int foo(int a, int b, int c)라고 함수를 정의하고 foo(10, 20, 30)이라고 call하면, R0에 10, R1에 20, R3에 30이 들어간다.
    • 이때 return 값은 보통 R0에 들어가는데, return값이 여러 개인 경우 R0~R3을 사용한다.
    • 만일 4개를 초과하는 argument나 result는 어떻게 할까? 어쩔 수 없이 stack에 넣게 된다.
    • 또한, R0~R3는 scratch 레지스터라고 부르기도 한다. 말그대로 연습장이라는 의미인데, CPU가 마치 연습장처럼 연산 중간중간에 임시저장 용도로 마구 사용한다는 의미다.
    • 따라서 R0~R3는 callee(호출된 함수)가 마음대로 수정할 권리를 가지기 때문에 여기에는 caller(호출하는 함수)가 중요한 정보를 넣어두면 안 되고, 설령 넣어놨더라도 복귀될 수 있도록 스택에 넣고 복구하는 추가작업을 따로 해야 한다.
  2. R4~R11 (v1~v8) (Variable)
    • Synonym을 통해 유추할 수 있겠지만, local 변수를 저장하는 용도로 사용한다.
    • 이것 역시 한정된 개수를 넘어가게 되면 stack에 저장한다.
  3. R12~R15 (Special purpose registers)
    • 사실 R13, R14, R15는 이미 여러번 반복해서 다뤘기 때문에 어떤 용도로 사용되는지 알 거다.
    • R12도 특수용도 레지스터다. ARM-Thumb interworking 기능에서 long branch 또는 vaneer를 사용할 때 주소의 임시 저장소로 활용한다. Interworking과 vaneer에 대해서는 나중에 배우게 된다.
    • 물론 R12~R15도 억지로 임시저장소로 사용할 수 있지만, 얘기치 못한 동작을 할 수도 있고 R13, R14 같은 경우 항상 유효한 스택 포인터 또는 복귀 주소라고 가정하고 실행하는 OS도 있기 때문에 권장하지 않는다.
  • 모든 레지스터는 사용하기 전에 기존에 들어있는 값을 stack에 저장한 뒤 사용해야 하고, 서브루틴 끝난 후 복귀할 때는 다시 stack에서 꺼내 복구해줘야 한다.

2.4. ARM modes와 exception

  • Exception 이란, interrupt를 포함한 큰 사건(외부요청, 내부오류 등)을 의미한다.
  • Exception이 발생하면 진행하던 동작을 범추고 exception을 처리하는 데 특화된 privileged mode로 진입한 뒤 exception handler의 주소로 jump해 처리 및 복원을 수행한다.
  • 이렇게 exception이 발생했을 때 jump하는 address들을 모아 ‘Exception Vector Table’이라고 부른다.

  • Exception이 발생했을 때 어떤 mode로 변경되는지 알아보자.
    1. Power-on & Reset : SVC mode
    2. HW interrupt : IRQ mode
    3. FIQ에 등록된 HW interrupt : FIQ mode
    4. 접근 할 수 없는 주소로의 접근시도 : ABT mode
    5. 이해할 수 없는 명령어(opcode): UND mode
  • CPSR의 하위 5-bit로 mode 정보를 알 수 있는데, 위에 노란색으로 표시해둔 hex값으로 구분하자. 외워두면 좋다.

  • 애플리케이션 실행 중 exception이 발생했다면, 어떤 조치를 취해야 할까?
    1. 현재 진행 중인 애플리케이션에 대한 context를 백업한다.
      1. SPSR에 CPSR값 저장.
      2. R13 움직여서 R0~R12, R14값 저장
      3. R14에 실행중이던 line 주소 저장. (이때 pipeline에 의해 현재 exception이 발생한 주소와 PC값은 2개 명령어 차이가 나기 때문에 저장할 때 또는 복귀할 때 주소 보정 작업이 필요하다. Exception이 발생한 곳으로 돌아가 재실행하거나, 그 뒤 주소로 이동해서 이어서 실행하거나 할 수 있다.)
    2. PC를 exception vector table 시작 주소로 바꾸고 jump한다.
  • 애플리케이션 실행 중 interrupt가 발생했다면, 어떻게 알아채고 조치를 취할까?
    1. MCU 안에는 interrupt controller라는 HW가 있는데 이쪽으로 pin을 통해 전기신호를 주면 ARM core가 알아챈다.
    2. IRQ mode로 진입한 후 vector table → IRQ handler로 jump해서 대응하는 ISR을 확인한다.
    3. ISR로 jump한 후 interrupt를 처리한다. (이때 ISR은 간결하게 짧게 써야 starvation 같은 문제를 막을 수 있기 때문에 DPC, APC, Bottom half 같은 기법을 사용하기도 하는데, 이건 RTOS에서 설명하겠다.)
  • 이처럼 mode마다 자신만의 register set을 갖고 있으면 mode 전환이 빠르고, USR 모드의 애플리케이션이 함부로 ARM 설정을 바꾸지 못하게 된다는 보안상 장점도 있다.
  • 이와같이 mode에 대한 이해가 잘 돼있지 않다면 디버깅을 잘 하지 못하게 된다.
  • Q. 왜 USR와 SYS는 SPSR이 없을까?
    A. Exception vector table을 보면, USR과 SYS에 대한 handler가 없는 것을 볼 수 있다. 따라서 USR, SYS로 들어가는 exception은 없다는 것이다. 따라서 SPSR에 CPSR을 저장할 기회도 없다. 또한, kernel이 탑재된 시스템에서는 거의 항상 기본적으로 동작하는 모드가 USR이기 때문에 어쩌다가 USR로 진입했는지 알 필요가 없기도 하다. 뭐 굳이 알고싶다면 SPSR이 아니라 stack에 저장하는 방법도 있다.
  • Q. User mode → Privileged mode가 안 되면, 어떻게 USR 모드로 진입하거나 다른 모드로 변경함?
    A. 설명했다시피 exception 발생하면 privileged mode로 변경되고, privileged mode에서는 CPSR의 하위 5-bit를 바꿔서 USR 모드로 진입할 수 있음.

3. ARM SoC

3.1. SoC

  • ARM은 processor를 만들어서 파는 회사가 아니라 CPU architecture를 파는 회사다.
  • ARM이 design한 architecture를 가져다 쓰는 회사로부터 로열티를 받아 비즈니스를 하는 회사다.
  • 퀄컴, 삼성, 인텔 등이 ARM의 라이센스를 구매해 자신들의 MCU 및 MPU에 맞게 재-design 하기도 한다.
  • 이렇듯 ARM이 Core를 판매하니, core에 여러 기능을 덧붙혀 하나의 chip으로 만들어 판매하는 SoC(System On Chip) 시장이 생겨나게 됐다.
  • SoC 내부의 각각의 기능을 가진 block을 IP(Intellectual Property)라고 명명하고 IP를 판매하는 회사도 생겨났다. HDL 또는 verilog 같은 HW description language를 지원하는 design tool을 사용해 모듈을 설계하고 ASIC 라이브러리로 최종 netlist를 만들어 IP를 만든다.
  • 이렇게 ARM core와 여러 IP를 묶어서 SoC로 엮으면 한 개의 MCU가 만들어 진다.

3.2. AMBA

  • 이런 흐름으로 시장이 전개되면서 엔지니어들은 SoC로 엮을 때 ‘내부에서 서로 다른 IP 및 core가 어떻게 연결돼야 할까?’ 라는 고민이 생기게 됐다.
  • ARM은 내부 IP들끼리 잘 통신하면서 동시에 ARM core를 가장 효율적으로 활용할 수 있는 방법을 적용한 Bus 프로토콜을 제안했는데 이것이 바로 AMBA (Advanced Microcontroller Bus Architecture) 프로토콜이다.
  • Bus 프로토콜은 bus 위로 data를 어떻게 송수신 할 것인지에 대한 약속을 말한다.
  • AMBA는 AHB, ASB, APB 3가지 인터페이스로 나뉜다.
    • AHB(AMBA : High performance): 이름대로 ‘burst mode’를 지원해 data의 빠른 전송이 가능한 고성능 bus 인터페이스다.
    • APB(AMBA : Peripheral): AHB는 고속 bus이므로 상대적으로 저속인 peripheral IP가 중간에 끼면 전체속도가 느려지게 된다. 따라서 이런 저속의 IP들을 묶어서 사용하기 위한 bus 인터페이스를 만들어 효율성을 올렸다. Mux 기반 bus이므로 address, control, data line이 모두 하나의 bus로 통합돼 시분할로 운용한다. 예를들어, control 신호 쏘고, address 쏘고, data를 연속해서 쏴주는 burst mode를 사용한다.
    • ASB(AMBA : System)
  • Bus를 사용하므로 필연적으로 중재해주는 존재가 필요한데 이게 chapter 1에서 배운 ‘아비터(Arbiter)’다.
  • 간단하게 master와 slave 사이의 AMBA 프로토콜을 AHB를 예로 알아보자.

  1. Master는 arbiter에게 ‘나 bus 쓰고 싶어’라고 HBUSREQx신호를 보낸다.
  2. Arbiter는 다른 누군가 bus를 쓰고 싶다면 읽씹하고, 쓰고 있지 않다면 HGRANTx신호로 대답한다.
  3. Master는 ‘허락받았으니 이제 내가 bus 쓸게’라고 HLOCKx신호를 arbiter에게 보낸다. 이제 arbiter는 master보다 priority가 낮은 IP에게는 bus 사용을 허가하지 않을 것이다.
  4. Master는 원하는 slave의 address인 HADDR을 decoder에게 준다. Decoder는 해당 slave에게 HSELx신호를 줘서 ‘누가 너한테 접근하고 싶대’라고 알려 활성화시킨다.
  5. Master는 원하는 slave에게 control 신호인 HWRITE을 전달한다.
  6. Slave는 master에게 ‘알았어 나 준비 끝났어’라고 HREADY로 대답한다.
  7. Master는 HWDATA신호에 write하고 싶은 data를 써서 slave에게 전달하거나
    Slave는 master가 요청한 data를 read하도록 master에게 전달한다. (양방향)
  • 위 7가지 과정을 타이밍도로 나타낸 것이 오른쪽 그림이다.
    • Transaction(트랜젝션) 과정은 address phase와 data phase로 나뉜다는 점도 기억해두자.
    • Address phase 때 master가 HADDRHWRITE을 전달한다.
    • 이후 2번의 clock cycle동안 준비를 한다. (wait) 이때 master는 slave가 읽을 때까지 계속 HWDATA를 유지하고 있어야한다.
    • 준비가 끝난 slave가 HREADY를 보내면 그때 master의 HWDATA를 인식한 뒤 write할 것이다. 또는, 이때 준비한 HRDATA를 master에게 전달할 것이다.

  • 다음은 HTRANS신호에 대해서 알아보자. HTRANS는 트랜잭션의 mode를 알려주는 역할을 하며 burst mode를 지원하는 AHB의 경우 현재 데이터와 다음 데이터 사이의 연관성을 나타내는 역할을 하기도 한다.
  • 우측 타이밍도를 보자.
    1. HADDR0x20을 가리키며 HTRANSNONSEQ을 날린다. 즉, ‘나 새롭게 데이터 전송 시작할거야’라고 알려준다.
    2. HREADY로 대답한 slave는 1-cycle 뒤에 (0x20에 대한) HWDATA를 받거나 HRDATA를 제공한다.
    3. 근데 master에 무슨 문제가 생겨 다음 전송을 이어서 할 수 없게됐다. 1-cycle만 쉬기 위해 HTRANSBUSY를 날리면, slave는 알겠다고 대답한다.
    4. 이제 master는 계속 데이터 전송을 이어서 할 수 있다. HADDR0x24를 가리키며 HTRANSSEQ을 날린다. 즉, ‘나 burst mode 사용 중이고, 지금 내가 요청한 데이터는 아까 데이터랑 연관돼있어’라고 알려주는 것이다.
  • 앞서 우리는 하나의 트랜잭션이 address phase와 data phase로 구성된다고 배웠지만, burst mode를 사용하면 한 번의 address phase 이후 연속해서 data를 주르륵 접근할 수 있다.
  • 이때 address phase를 생략하기 위해서는 HADDR이 자동으로 증가하거나 감소해야 한다. HBURST신호를 통해 증가하거나 감소하는 bit수를 조절할 수 있다.

  • HBURST는 3-bit로 다양한 burst mode를 표현한다.
    • INCR x는 이름 그대로 x-bit씩 주소가 증가하는 것을 말한다.
    • WRAP x는 주소가 wrapping 돼 boundary 내부에서만 burst 한다는 뜻인데 조금 난해하다.
      • WRAP 뒤에 붙은 정수 x가 Word 단위의 address boundary를 나타낸다.
      • WRAP 4의 경우 word 4개가 address boundary라는 의미고 ARM core는 1-word가 4-byte니까 4-word면 16-byte = 0x10이니까 0x10단위로 boundary가 만들어지는 것이다.
      • 예를 들어, 주소 0x38에서 burst mode - WRAP 4를 시작했다면, 0x38 - 0x3C - 0x30 - 0x34에 대해 접근하게 된다. Boundary가 0x10단위기 때문에 0x30~0x40가 boundary일 것이며 이 영역 내에서 다시 시작지점으로 돌아올 때까지 burst transfer 한다.
  • AMBA가 발전하며 AXI도 나왔다.
    • Burst mode 기반 bus이며 시작주소만으로도 burst transfer 가능함.
    • Response channel이 추가됨.
    • R/W가 동시에 가능함.
    • ARM11 이상의 backbone bus로 이용되고 있음.
profile
임베디드 시스템 공학자를 지망하는 컴퓨터공학+전자공학 복수전공 학부생입니다. 타인의 피드백을 수용하고 숙고하고 대응하며 자극과 반응 사이의 간격을 늘리며 스스로 반응을 컨트롤 할 수 있는 주도적인 사람이 되는 것이 저의 20대의 목표입니다.

0개의 댓글