Zynq SoC Training – Interrupts

Acknowledgement (Korean)
아래의 글은 Ali Aljaani가 운영하는 블로그인 embedded centric의 “Zynq SoC Training”을 번역한 문서입니다. 한글 번역 및 공개를 허용한 Ali Aljaani에게 감사의 말을 전합니다.

Acknowledgement (English)
This post is a translated version of “Zynq SoC training” in embedded centric. Thank you Ali Aljaani for allowing me to translate nice posts. I’m Taekyung Heo, and I translated this post into Korean.

Contact information (Author)
Ali Aljaani’s blog: http://embeddedcentric.com/
Ali Aljaani’s mail: alialjaani@embeddedcentric.com
Ali Aljaani’s LinkedIn: https://www.linkedin.com/in/ali-aljaani-a7130099
Ali Aljaani’s github: https://github.com/ama142


이번 실습에서는 Zynq SoC의 인터럽트 시스템 구조에 대해 학습한다. Zynq SoC는 Generic Interrupt Controller (GIC)를 사용해 인터럽트를 처리한다. 인터럽트 기반의 시스템을 구현하기 위한 방법을 이 실습에서 설명할 것이다. 인터럽트 기반의 어플리케이션은 interrupt handlers (ISRs)과 interrupt 설치 함수를 사용해 개발할 수 있다.

lab4-design-flow

Lab4 Design Flow

lab4-block-diagram1

Lab4 Block Diagram

lab4-physical-connections-physical-connections

Lab4 Board Interface

GIC는 아래 그림에서 붉은 원으로 표시된 부분이다:

zynq-architecture-gic-circled-in-red

Zynq Architecture- GIC circled in red

GIC는 다음의 인터럽트들을 처리할 수 있다.

  • Software-generated interrupts – 각 프로세서에 16개의 이 종류의 인터럽트가 있다. 해당 인터럽트를 사용해 Zynq SoC의 ARM® Cortex™-A9프로세서 코어 모두 또는 하나를 인터럽트할 수 있다.
  • Shared peripheral interrupts – 입출력 주변장치 또는 programmable logic (PL)로 오가는 총 60개의 인터럽트가 있다. 이들은 Zynq SoC의 두 CPU에 의해 공유된다.
  • Private peripheral interrupts – 각 CPU에 이 종류에 해당하는 다섯 개의 인터럽트가 있다. CPU timer, CPU watchdog timer 그리고 PL-to-CPU 인터럽트가 이에 해당한다.
interrupt-system-structure-for-the-zynq

Interrupt System Structure for the Zynq

Zynq SoC에서 인터럽트가 발생하면 관계된 프로세서는 다음과 같은 단계를 통해 처리한다:
1. 인터럽트를 pending 상태에 둔다.
2. 프로세서는 현재 실행하고 있는 프로그램을 잠시 정지한다.
3. 프로세서는 현재 프로그램의 상태를 스택에 저장한다. 이는 나중에 인터럽트 처리 이후에 사용한다.
4. 프로세서는 인터럽트를 어떻게 처리할 것인지 정의하고 있는 루틴인 인터럽트 서비스 루틴 (ISR)을 실행한다.
5. 프로세서는 인터럽트된 프로그램의 정보를 스택에서 복구하고 실행을 계속한다.

인터럽트가 비동기 이벤트이기 때문에, 여러 개의 인터럽트가 동시에 발생할 수 있다. 이 문제를 해결하기 위해, 프로세서는 인터럽트의 우선순위를 정하고, 대기 중인 인터럽트 중에 가장 우선순위가 높은 인터럽트를 먼저 처리한다. 따라서 인터럽트 처리를 위해서는 두 개의 함수를 작성해야 한다. 하나는 인터럽트를 처리하기 위한 인터럽트 서비스 루틴 (ISR)이고, 다른 하나는 인터럽트를 설정하기 위한 인터럽트 설정 함수이다 (우선순위, 하드웨어 인터럽트 민감도 등).

이번 실습에서는 GPIO 컨트롤러와 Zedboard OLED 컨트롤러 모두 AXI 인터커넥트를 통해 PS에 연결되어 있다. PS는 PL로부터 오는 인터럽트를 받을 수 있도록 설정되어 있다. 칩의 PL 부분에서 GPIO는 인터럽트를 지원하고, 푸쉬 버튼을 누를 때마다 인터럽트 신호를 발생시키도록 설정한다. OLED는 터미널 또는 모니터로 작동하며, 이는 실행 흐름을 확인하는데 도움을 주게 된다. 메인 프로그램은 간단한 무한 루프를 돌면서 영문자를 차례대로 (A-Z) 출력한다. 그리고 “Z”에 도달하면 “A”로 돌아가게 된다. 메인 프로그램은 인터럽트가 발생해 인터럽트 서비스 핸들러 (ISR)가 호출되면 중지한다 (푸쉬 버튼이 눌리게 되면). ISR에서는 다섯 개의 푸쉬 버튼 중에서 무엇이 눌렸는지 확인하기 위해 GPIO의 데이터 레지스터를 읽어낸다. 그리고 눌린 푸쉬 버튼에 대한 정보가 print_message() 함수를 통해 OLED에 출력된다. 인터럽트 서비스가 완료되면, 메인 프로그램은 멈췄던 지점부터 다시 메인 프로그램을 실행한다. 예를 들어, “D”가 OLED에 출력되고 인터럽트가 발생하면, 메인 프로그램은 인터럽트 서비스 이후 “E”를 출력해야 한다. 다음 그림이 어플리케이션의 행위를 보여준다 (전형적인 인터럽트 기반 행위).

lab4-application-flow-chart

Lab4 Application Flow Chart

목표
1. 인터럽트 기반의 임베디드 시스템의 구현
2. Zynq SoC의 generic interrupt controller의 탐구
3. Zynq SoC에서 인터럽트 소스가 어디인지 확인하는 방법 이해
4. GPIO 컨트롤러를 사용해 인터럽트를 지원하기
5. 인터럽트 핸들러 (ISR)와 인터럽트 설정 함수 개발
6. 프로그램 실행에서 스택과 힙 메모리를 증가시키는 방법 이해

실습 단계
A. Vivado에서 프로젝트를 생성하고 target board로 ZedBoard 선택
실습 1의 실습 단계 A의 다섯 단계를 동일하게 수행한다. 단, 프로젝트 이름은 “lab4″로 한다.
B. 인터럽트 지원하는 ARM processor system 생성
1. Vivado 좌측 상단에 있는 Create Block Design in IP Integrator을 클릭한다.
2. 모듈 이름을 입력하고 OK를 클릭한다. 모듈 이름으로는 “zynq_interrupt_system”을 사용한다.

create-a-block-design

Create a Block Design

3. Add IP wizard를 클릭하고 “zynq”를 입력해 ZYNQ7 Processing System을 추가한다.

zynq7-processing-system

ZYNQ7 Processing System

4. ZYNQ7 Processing System 블록을 더블 클릭해, Re-Customize IP 창을 연다. 그리고 좌측에 있는 Page Navigator에서 Interrupts를 클릭하고 우측의 메뉴를 확장한다. 이 실습에서 우리는 programmable logic에서 processing system으로의 인터럽트를 허용하고자 하므로, Fabric Interrupts를 활성화한다. Fabric은 PL의 또다른 이름이다. 그리고 IRQ_F2P[15:0](Interrupt Request_Fabric to Processing System)를 활성화한다. 이는 PL이 Zynq PS에 있는 인터럽트 컨트롤러에 연결될 수 있음을 의미한다. 그리고 MIO Configration에서 UART1을 활성화한다.

enable-fabric-interrupts

Enable Fabric Interrupts

지금까지 올바르게 따라왔다면, 블록 다이어그램의 모양은 다음과 같다.

zynq7-processing-system-with-fabric-shared-interrupt-ports-enabled1.jpg

ZYNQ7 Processing System with Fabric Shared Interrupt Port Enabled

만약 동일하지 않다면, 블록을 더블 클릭해서 re-customize해서 관련된 설정을 조절해야 한다. shared interrupt port가 활성화되도록 해야 한다는 점에 유의해야 한다. 이는 PL에서 발생된 인터럽트가 연결된 포트이다.

초록색 정보 바에 있는 Block Automation을 실행한다. 그리고 Apply Board Preset을 체크 해제하여 수정이 무효화되지 않도록 한다. OK를 클릭해 진행한다.

generating-external-connections-for-the-uart

Block Automation

FIXED_IO와 DDR이 이제 외부 포트에 연결된 것을 확인할 수 있다.

zynq7-processing-system-after-block-automation

ZYNQ7 Processing System after Block Automation

C. GPIO 컨트롤러를 추가하고 인터럽트 포트 활성화하기
1. Add IP wizard를 클릭하고 “gpio”를 입력해 GPIO 컨트롤러를 추가한다.
2. Run Connection Automation를 클릭하고 GPIO를 선택해, Select Board Part Interface에서 btns_5bits를 선택한다.

run-connection-automation1

Run Connection Automation – GPIO Page

마찬가지 방식으로 S_AXI를 선택하고 Clock ConnectionAuto로 설정한다.

run-connection-automation-s_axi-page

Run Connection Automation- S_AXI Page

OK를 클릭하면 자동적으로 GPIO의 슬레이브 인터페이스가 PS의 마스터 인터페이스에 AXI 인터커넥트 블록을 사용해 연결된다. 그리고 외부 포트는 다섯 개의 푸쉬 버튼에 연결된다.

Regenerate Layout을 클릭해 블록 디자인을 재정렬한다.

block-diagram-after-adding-gpio

Block Diagram After Adding GPIO

3. GPIO 블록을 더블 클릭해 Re-customize IP 창을 연다. IP Configuration 탭을 클릭하고 아래 그림에서 하이라이트된 박스를 클릭해 인터럽트를 활성화한다. 그리고 OK를 클릭한다. 이를 통해 GPIO 블록에 인터럽트 포트를 추가할 수 있다.

enable-interrupt-on-gpio

Enable Interrupt on GPIO

4. GPIO 블록에 새롭게 생성한 인터럽트 포트와 Zynq PS의 공유 인터럽트 포트 사이에 연결을 생성한다. 마우스 커서를 드래깅함으로써 생성할 수 있다.

connect-gpio-interrupt-request-port-to-the-shared-interrupt-port-of-ps

Connect GPIO interrupt request port to the shared interrupt port of PS

D. ZedboardOLED 컨트롤러 추가하기
지난 실습(Customized Hardware Integration)에서의 실습 단계 D와 E의 모든 단계를 따라해 Zedboard OLED 컨트롤러를 시스템에 추가한다. 모든 단계가 끝난 뒤의 블록 다이어그램은 아래와 같다.

block-diagram-after-adding-zedboard-oled

Block Diagram After Adding Zedboard OLED

E. Bitstream 생성
1. Sources 창에서 zynq_interrupt_system.bd를 우클릭하고, Create HDL Wrapper를 클릭해 블록 다이어그램에 해당하는 Verilog 파일을 생성한다. 다음 메시지가 뜨면 Let Vivado manage wrapper and auto-update를 선택한다. zynq_interrput_system_wrapper.v가 생성되고 소스 계층 상 맨 위에 위치하는 것을 확인할 수 있다.

2. Flow NavigatorProgram and Debug 창에서 Generate Bitstream을 클릭한다. 수정한 것을 반영하겠느냐는 메시지가 뜨면 Save를 클릭한다.

generating-bitstream1

Generating Bitstream

Bitstream 생성에는 컴퓨터 성능에 따라 약간이 시간이 소요될 수 있다. Bitstream 생성이 완료되면 대화 상자에서 View Reports를 클릭한다. 그리고 OK를 클릭해 계속 진행한다.

F. 하드웨어 디자인을 SDK에 내보내기
1. File > Export > Export Hardware를 클릭한다. Include bitstream를 체크하도록 한다.

export-hardware-to-sdk

Export Hardware to SDK

2. File > Launch SDK를 클릭한다. Launch SDK 대화창이 뜰 것인데, 여기에서 기본 옵션으로 두고 OK를 클릭한다.

이제 시스템에 대한 하드웨어 설정은 마쳤다. Vivado를 닫아도 된다.

G. SDK에서 작업하기
1. SDK에서 File > New > Application Project를 선택한다.
2. 다음 창에서 아래 화면과 같이 인자값을 입력한다.

new-standalone-c-project

New Standalone C Project

이 다음에 뜨는 창에서 Empty Application을 클릭하고 Finish를 클릭한다. SDK는 이제 BSP와 관련 드라이버를 컴파일한다.

3. interrupt_test 디렉토리를 확장하고, src 디렉토리를 우클릭한다. 그리고 New->Source File을 선택한다.

new-c-source-file1

New C Source File

4. 다음 창이 나타나면 소스 파일 이름에 “lab4.c”를 입력하고 Finish를 클릭한다. 이는 src 디렉토리에 한 개의 빈 소스 파일을 생성할 것이다.

lab4-c-empty-source-file-created-in-sdk

lab4.c empty source file created in SDK

5. SDK의 편집 창에서 lab4.c의 내용을 붙여넣는다 (내 Github 페이지에서 다운로드 할 수 있다). 그리고 Save를 클릭하거나 Ctrl+S를 눌러 저장한다. 이렇게 함으로써 BSP가 컴파일되고 실행 파일이 생성된다 (SDK의 기본 설정에 따르면, 저장과 동시에 빌드한다). 이 실행 파일을 Zynq의 PS에서 실행할 수 있다.

lab4.c에는 네 개의 함수가 있다: main() 함수는 우선 주변 장치를 초기화하고 gpio의 입출력 방향을 설정한다. 그리고 IntcInitFunction() 함수를 호출해 인터럽트 컨트롤러를 초기화하고 GPIO 인터럽트 핸들러를 등록한다 (GPIO에서 오는 인터럽트와 연관된 함수). 그리고 GPIO 자체와 GIC에서의 GPIO 인터럽트를 활성화한다. IntcInitFunction() 함수는 또한 내부적으로 InterruptSystemSetup() 함수를 호출해 GIC 인터럽트 핸들러를 등록한다.

InterruptSystemSetup() 함수가 반환하면 main() 함수는 무한 루프에 들어가게 된다 (140~145번 줄). 무한 루프에서 메인 함수는 print_message()를 호출해 OLED에 알파벳을 출력한다. 인터럽트가 발생하면 (푸쉬 버튼을 누른다면) 프로세서는 main() 함수의 실행을 잠시 멈추고 인터럽트 발생과 연관된 함수를 호출한다 (이 경우에는 BTN_Intr_Handler이다). BTN_Intr_Handler() 함수는 GPIO의 데이터 레지스터를 읽어 다섯 개의 버튼 (BTNC, BTND, BTNR, BTNL, BTNU) 중에서 어떤 것이 눌렸는지 확인한다. 그리고 이 버튼을 print_message() 함수를 사용해 OLED에 출력한다.

lab4-c-application

Lab4.c Application

이번 실습에서, 스택은 main 함수와 ISR 함수 코드의 일부를 빼내고 다시 넣는데 광범위하게 사용된다. 따라서 우리는 스택을 위한 영역을 확장해야 한다. 이는 interrupt_test 디렉토리를 우클릭하고 Generate Linker Script를 클릭함으로써 진행할 수 있다. Linker script에서 우리는 우리의 프로그램이 메모리의 어떤 부분에 위치할지(DDR memory 또는 RAM) 결정할 수 있고 어플리케이션을 위한 힙 또는 스택 메모리를 할당할 수 있다.

generate-a-linker-script

Generate a linker script

스택과 힙 크기를 1KB에서 10KB로 늘린다. 스택과 힙 메모리는 함수 호출과 반환을 위한 정보 저장에 사용된다. Generate를 클릭하고 Yes를 클릭해 현재 존재하는 파일을 덮어쓴다.

H. Bitstream을 다운로드하고 어플리케이션을 실행하기 (하드웨어 검증)
1. Xilinx Tools-> Program FPGA을 클릭해 Bitstream을 다운로드한다 (수 초 정도가 걸릴 것이다).

program-fpga

Program FPGA

2. interrupt_test 디렉토리를 선택하고 Run As-> Launch on Hardware (GDB)을 클릭해 oled_test 어플리케이션을 ARM 프로세서에서 실행한다.

어플리케이션이 로드되면 영문자 알파벳이 OLED page 0의 0번째 위치에 출력될 것이다. Zedboard의 푸쉬 버튼 중 하나가 눌리면 이는 GPIO의 ISR을 호출하고 어떤 버튼이 눌렸는지 출력한 뒤 다시 메인 프로그램으로 돌아와 영문자 출력을 재개한다. 다음의 데모 동영상을 보자.


이로써 네 번째 실습인 Interrupts을 마친다. 이번 실습에 대한 해답은 여기에 있다.


Advertisements
Tagged with: , , , , , , , , , , , , , , ,
Posted in FPGA

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

누적 방문자 수
  • 96,190 hits
%d bloggers like this: