티스토리 뷰

ARM Cortex-M

DMA

Just4Fun 2017. 5. 13. 18:55

DMA(Direct Memory Access)는 "직접 메모리 접근"이라고 번역이 된다.

그렇다면 누가 메모리에 직접 접근한다는 뜻일까?

답부터 바로 얘기하자면, DMA controller라는 주변 장치가 메모리에 직접 접근하여 값을 읽거나 쓸수 있다는 것이다.

그럼, 왜 DMA controller라는 주변장치를 이용하여 메모리에 접근하도록 하는 것일까?

C 표준 라이브러리 함수 중에 memcpy()라는 것이 있다.  이 함수는 source address가 가리키는 주소에서 데이터를 읽어 destination address 주소가 가리키는 곳에 주어진 크기만큼 데이터를 복사해주는 동작을 수행한다.  매우 단순한 동작인데, 복사하고자 하는 크기가 작을 때는 별 문제가 없지만, 만약 그 크기가 커지게 되면 그만큼 많은 시간이 필요하게 된다.

임베디드 시스템에 사용되는 대부분의 CPU는 RISC 구조를 가지고 있는데, RISC의 대표적 특징은 모든 동작이 load, store 방식으로 이루어 진다는 것이다.

어셈블리 코드를 보면 확실히 볼 수 있는데, 메모리의 어떤 주소에 있는 데이터를 다른 주소에 복사하기 위해서는 반드시 CPU 내부에 있는 register에 데이터를 읽어 온 다음(load), 목적지 주소에 읽어 온 데이터를 써주게(store) 된다.

100바이트의 데이터를 복사하기 위해서는 100번의 load, store 동작을 수행하게 되고, 100번이 수행되었는지 확인하기 위하여 카운트 기능과 비교 구문도 같이 수행할 수 밖에 없게 된다.

이러한 단순 메모리 복사 기능을 수행하기 위하여 CPU의 자원을 낭비하기 보다는, 이러한 기능만 수행하는 주변장치를 만들어서 메모리 복사 기능을 수행하도록 하고, 메모리 복사가 진행되는 시간동안 CPU는 다른 동작을 수행하게 되면 CPU의 효율을 높이는 효과를 볼 수 있게 되는 것이다.

이러한 메모리 복사 기능을 좀 더 확장하게 되면 메모리에 저장된 데이터를 주변 장치로도 내 보내고, 주변 장치로 들어온 데이터를 메모리에 직접 써주게 되면 더욱 CPU를 효과적으로 사용할 수 있게 되는것이다.

예를 들어, 9600bps로 설정된 UART를 통해서 32바이트 데이터를 송신하기 위한 시간을 계산해 보면 그 시간동안 CPU가 얼마만큼이나 많은 명령어를 처리할 수 있는지 놀랄것이다.   그러므로, UART 송신 기능을 DMA에게 처리하도록 하고, CPU는 그 시간동안 다른 동작을 수행하면 되는것이다.

stm32f103에는 위의 그림과 같이 하나의 범용 DMA controller가 내장되어 있다.  여기서 말하는 범용이라는 뜻은 DMA controller를 여러가지 옵션을 적용하여 다양한 용도로 사용할 수 있다는 것이다.  때로는 주변 장치 내부에 별도의 DMA controller가 들어 있어 주어진 용도로만 사용할 수 있는것에 대비되어 범용이라고 표현하는 것이다.

DMA controller안에는 다시 7개의 DMA channel이 있어서 동시에 7개의 DMA 동작을 수행할 수 있다.

DMA가 SRAM뿐만 아니라, 주변장치와도 연결되고 있음을 볼 수 있다.

DSP처럼 signal processing에 특화된 CPU의 경우는 메모리에서 메모리로 복사하는 경우가 많지만, stm32f103처럼 MCU의 경우에는 대부분 주변장치를 효과적으로 사용하기 위하여 DMA를 주로 사용하게 된다.

그러나, 이번 글에서는 memory to memory 기능을 구현하면서 DMA의 기본 동작을 구현해 보도록 하겠다.

위의 그림은 DMA controller 안에 있는 각 채널별 register가 어떤 것들이 있는지 보여주고 있다.

기본적으로 대부분의 DMA는 비슷한 register를 가지고 있는데, source address, destination address, length, control, status 이렇게 5개의 register가 있다.  위의 그림에는 status register가 안보이지만, stm32f103은 7 채널의 DMA에 대한 status register를 모두 합쳐서 제공하고 있다.

CH1 ~ CH7까지 채널별로 4개의 비트값을 이용하여 현재 DMA 채널의 상태를 확인할 수 있게 해준다.

DMA 동작을 확인해 보기 위한 코드를 위와 같이 만들어 보았다.

5~18까지는 source buffer와 destination buffer를 heap 에서 할당 받아, source 메모리 영역은 모두 0xAA로 채우고, destination buffer영역은 0x55로 채운다.

24번 줄에서 dma_copy() 함수를 불러서 source에 있는 데이터를 destination으로 복사한 후에 두 메모리 영역을 dump 해 본다.

dma_copy() 함수는 위와 같이 만든다.  예제에서는 7개의 DMA 채널 중에서 1번 채널을 사용하는것으로 하였다.

7번 줄에서 cpar 레지스터에 source address를 넣어 준다.

8번 줄은 cmar 레지스터에 destination address를 넣어 준다.

9번 줄은 cndtr 레지스터에 복사할 데이터 크기를 넣어 주고 있는데 한번의 데이터 전송에 4바이트씩 수행할 것이므로 전체 length를 4로 나누어 주었다.

11번 줄은 memory to memory 동작을 한다는 것을 알려주고, 한번씩 데이터가 전송될때마다 source와 destination 주소를 증가하도록 설정해주는 것이다.

12번 줄은 DMA 채널의 priority level을 설정해 주고 있는데, 의미는 없지만 여기에서는 Medium priority로 설정해 보았다.

13,14는 source, destination 모두 4바이트씩 메모리 크기를 설정해 주고 있다.

15번 줄은 DMA가 동작이 완료 되거나 에러가 발생할 때 이를 status register에 반영해 주기 위하여 각 비트를 설정해 주고 있는 것이다.

17번 줄은 메모리 복사 과정을 수행하기 위하여 DMA 채널을 enable 해주는 것이다.  이때부터 메모리 복사 기능이 시작 되는 것이다.

19번 줄은 DMA 동작이 완료 되는지를 polling 하고 있는 것이다.  정상적으로 DMA동작을 수행하든지, 중간에 문제가 발생하여 에러가 나든지 GIF 비트가 1로 변하게 된다.

이번 예제에서는 인터럽트를 사용하지 않기 때문에 DMA가 완료 될 때까지 기다리고 있는 것으로 만들었지만, OS를 사용하거나 인터럽트를 사용하게 되면 polling 하는 시간 동안 다른 작업을 수행하면 된다.  DMA 동작이 끝나게 되면 인터럽트를 통해서 그 사실을 알려 줄수 있기 때문이다.

Status register를 확인하여 정상적으로 끝났는지, 에러가 발생했는지 체크하고, status register에서 읽은 값을 ifcr 레지스터에 써주어 isr 레지스터를 clear 시켜주어야 한다.

프로그램을 컴파일 하여 보드에서 실행해 보고 원하는 결과가 나오는지 확인해 본다.


위의 그림처럼 destination buffer의 값이 0x55에서 0xAA로 바뀐것을 볼 수 있다.


dma_mem.zip



'ARM Cortex-M' 카테고리의 다른 글

DMA - SPI  (2) 2017.05.27
DMA - UART  (0) 2017.05.22
SPI  (3) 2017.05.03
I2C  (0) 2017.04.22
시스템 클럭 72MHz로 동작 시키는 방법  (0) 2017.04.19
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함