[컴퓨터구조 요약 정리] 16. SW for SoC 5

Embedded June·2021년 5월 21일
1
post-thumbnail

16.1. Cortex-M0+의 Timer와 Counter

16.1.1. 정의

  • Timer는 실제로 시간을 측정하기 위해 사용하는 회로를 말한다.
  • Counter는 pulse의 개수를 측정하는 회로를 말한다.
  • 구분하는 것이 맞으나 일반적으로는 timer나 counter나 비슷한 의미로 사용한다.

16.1.2. 용도

Timer나 counter를 사용하는 가장 대표적인 세 가지 용도는 아래와 같다.

  1. Elapsed time (소요 시간)을 측정한다.
  2. Event의 횟수를 세거나 일정 횟수에 도달할 때 event를 발생시킨다.
  3. 시간을 측정하거나 일정 시간에 도달할 때 event를 발생시킨다.

16.1.3. 사용 예시

  1. Periodic Timer Tick
    • 가장 일반적인 용도로, timer로 시간을 측정해서 일정 주기마다 특정 event나 interrupt를 발생시킨다.
    • HW적으로 동작하는 timer이므로 SW에서 생기는 delay에 영향을 받지 않는다. 따라서 busy-waiting이 생기는 경우에도 timer를 사용할 수 있다.
  2. Watchdog Timer (WDT)
    • 정말 많이 사용하는 timer로, 시스템이 정상적으로 작동하는지 여부를 검사하는 역할을 한다.
    • 주기적으로 HW 내부에서 들어오는 신호와 소요시간(elapsed time)을 검사하는데, 예상했던 시간 안에 감지되지 않을 경우 프로세서를 리셋한다.
    • Cortex-M0+에는 KL25Z COP라는 WDT가 있다.
  3. Time and frequency measurement
    • 시간을 측정하기 위해 frequency를 측정한다.
    • Clock의 rising edge를 counting 해서 이를 시간으로 변환한다.
  4. PWM signal generation
    • PWM(Pulse-Width Modulation) 신호도 timer와 counter로 만들어진다.
    • PWM의 period에 따라 정해진 주기마다 single digital output을 내보내는 것으로 유사 analog 신호를 만든다.

16.2. SysTick Timer

Cortex-M0+를 비롯한 M-series에는 프로세서에 SysTick이라는 periodic timer tick 모듈이 내장돼있다. 이 timer는 24-bit 길이로 돼있으며 다음과 같은 registers로 구성돼있다.

image-20210520223148989

  • VAL: 현재 timer의 value를 저장하고 있다.
  • LOAD: Timer의 초기값이 저장돼있다. Timer 값을 reload할 때 사용하는 값이다.
  • CRTL: Timer의 상태 정보가 저장돼있으며 timer를 제어한다.
    image-20210520223555023
    • ENABLE bit: Counter를 사용할지 여부를 의미한다. (Enable or disable)
    • TICKINT bit: Counter가 계속 감소하다가 0이 되면 인터럽트를 걸지 말지 여부를 의미한다.
    • CLKSOURCE bit: 프로세서의 clock을 사용할지 외부에 연결된 clock을 사용할지 결정한다.

SysTick Timer를 사용하는 예시 코드는 아래와 같다.

void Init_SysTick(void) {
    SysTick->LOAD = (48000000L / 16);	// 48MHz / 16 = 3MHz
    NVIC_SetPriority(SysTick_IRQn, 3);	// Set interrupt priority
    SysTick->VAL = 0;	// Clear current value
    // Enable timer and interrupt. Use external clock source
    SysTick->CTRL = SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
}
  • 프로세서는 48MHz로 작동하고, 연결된 외부 clock은 3MHz로 작동한다고 가정하면, 1초를 측정하기 위해서는 프로세서와 외부 clock의 주파수가 같아야 한다. 따라서 LOAD를 48MHz에서 16으로 나눈 3MHz를 저장하는 것이다.

16.3. Direct Memory Access (DMA)

16.3.1. 소개

단순히 작은 data를 memory로부터 가져오거나 쓰는 과정은 CPU가 해도 되지만, 큰 data stream을 옮기는 과정을 CPU가 처리한다면 굉장히 큰 CPU time 낭비다. Data를 단순히 옮기는 과정은 간단한 operation이므로 굳이 CPU가 나설 필요가 없다. 이 역할을 대신 수행해주는 게 DMA다.

16.3.2. 과정

  1. DMA에게 data transfer를 시작하는 event를 trigger한다. (“DMA야 너 일 시작해라”)
  2. DMA controller는 source와 destination의 address와 data bus에 대한 제어권을 받고, source와 destination의 address를 한 칸씩 증가시키면서 연속해서 data를 옮긴다.
  3. DMA controller는 counter를 이용해서 data를 몇 개나 transfer 했는지 update 한다.
  4. DMA controller는 마지막 transfer이 끝나면 인터럽트를 건다. (“나 일 다 끝냈어!”)

16.3.3. 작동 Mode

  • Continuous (burst) mode
    • DMA가 빠르게 data stream을 옮기기 위한 mode로, DMA는 시작부터 transfer이 끝날 때 까지 멈추지않고 계속 data를 옮긴다.
    • Data 또는 address bus의 제어권을 DMA가 가지고 있기 때문에, CPU가 긴급하게 memory access가 필요할 때는 stall 해야 하는 상황이 발생한다는 단점이 있다.
  • Cycle-stealing (Time-sharing) mode
    • CPU가 필요할 때는 잠시 bus 제어권을 뺏어서 사용한 뒤 다시 DMA에게 넘겨준다.
    • DMA는 data 하나를 옮긴 뒤 누가 bus를 사용하려 하지 않는지 검사한다. 아무도 없다면 계속 data를 옮긴다.
    • DMA는 잠시동안 data를 hold 할 수 있어야 한다.
    • 매 data 전송마다 bus 제어권 요청을 검사해야하므로 조금 느리다는 단점이 있다.

16.3.4. 구조

image-20210520231128414

DMA는 n개의 채널로 구성돼있고, 위 그림은 하나의 채널이 어떤 구조를 가지고 있는지 블록 다이어그램으로 나타낸 그림이다.
위에서 소개한 DMA 작동 과정을 바탕으로, 어떤 하드웨어가 어떤 순서로 어떻게 작동하는지 알아보자.

DMA_MUX

  • DMA가 transfer를 시작하기 위해서는 먼저 start trigger 신호와 함께 source와 destination의 address, data stream이 들어와야 한다.
  • Start trigger ERQ 신호는 DMA 바깥에 있는 DMA_MUX라는 MUX에서 들어온다.
  • DMA_MUX는 대용량 data stream이 필요한 여러 source를 입력으로 받는다. 예를들어 UART 통신, I2C 통신, SPI 통신, TPM 통신 등이 대표적이다.
  • DMA_MUX_CHCFGn 이라는 control register가 DMA_MUX를 제어한다.
    • image-20210521112648082
    • SOURCE는 6-bit로 표현하니 최대 64개의 source가 DMA에게 data stream을 요청할 수 있다.
    • TRIG는 SW적으로 DMA를 enable 하는 기능을 허용할 것인지를 선택하는 bit다.
    • ENBL는 DMA를 enable할지 말지 결정하는 bit다.

DMA Controller

  • DMA controller는 data transfer operation을 위해 여러 register를 사용한다.
  • SAR: Source address register는 이름 그대로 data를 주는 쪽 (source)의 address를 저장한다.
  • DAR: Destination address register도 이름 그대로 data를 받는 쪽 (destination)의 address를 저장한다.
  • DMA_DCRn: DMA controller는 각 채널마다 DCR register를 가지고 있는데, 해당 채널에서 DMA의 operation을 정의한다.
    • image-20210521113626248
  • DMA_DSR_BCRn: DMA controller는 각 채널마다 DSR_BCR register를 가지고 있는데, DMA의 상태 정보를 저장한다.
    • image-20210521113635692
  • 각 register의 bit에 대한 상세 정보는 필요할 때 datasheet를 찾아보는 것으로 한다. (내용이 너무 방대함.)

16.3.5. 사용법

Basic configuration

  1. GPIO 때와 마찬가지로 SIM register의 SCGC7 register를 조작해서 DMA에게 clock을 enable 해준다.
  2. SIM register의 SCGC6 register를 조작해서 DMA_MUX에게 clock을 enable 해준다.
  3. 사용할 DMA_MUX와 channel에 호응하는 DMA_MUXx_CHCFGn register를 clear해준다.
  4. SARnDARn 각각에 address를 load한다.
  5. BCRn에 몇 byte 정보를 transfer 할 것인지 load한다.
  6. DSRnDONE flag bit를 clear해준다.
  7. DMA_MUXx_CHCFGn register의 SOURCE bits에 source number를 적어주고, ERQ 를 set 해서 DMA를 trigger한다.

코드

#define ARR_SIZE (256)
uint32_t s[ARR_SIZE], d[ARR_SIZE];
void Test_SW_Copy(void) {
    uint32_t *ps, *pd;
    ps = s;
    pd = d;
    for (i = 0; i < ARR_SIZE; ++i) *pd++ = *ps++;
}

위와 같이 1KB 데이터를 source에서 data로 옮기는 작업을 수행하는 단순한 C코드가 있다고 생각해보자. CPU가 아니라 DMA를 사용해서 이 transfer를 효율적으로 수행하고 싶다면 아래와 같은 DMA 설정 코드를 만들어서 활용할 수 있다.

#define ARR_SIZE (256)
uint32_t s[ARR_SIZE], d[ARR_SIZE];

void Init_DMA_To_Copy(void) {
    SIM->SCGC7 |= SIM_SCGC7_DMA_MASK;
    DMA0->DMA[0].DCR = DMA_DCR_SINC_MASK | DMA_DCR_SSIZE(0) | 
        				DMA_DCR_DINC_MASK | DMA_DCR_DSIZE(0);
}

void Copy_Longwords(uint32_t* source, uint32_t* dest, uint32_t count) {
    // Load source and destination address (by pointer)
    DMA0->DMA[0].SAR = DMA_SAR_SAR((uint32_t)source);
    DMA0->DMA[0].DAR = DMA_DAR_DAR((uint32_t)dest);
    // Data byte count
    DMA0->DMA[0].DSR_BCR = DMA_DSR_BCR_BCR(4 * count);
    // Verify DONE flag is cleared
    DMA0->DMA[0].DSR_BCR &= ~DMA_DSR_BCR_DONE_MASK;
    // Start transfer!
    DMA0->DMA[0].DCR |= DMA_DCR_START_MASK;
    // Wait until transfer done.
    while (!(DMA0->DMA[0].DSR_BCR & DMA_DSR_BCR_DONE_MASK));
}

void Test_DMA_Copy(void) {
    uint16_t i;
    Init_DMA_To_Copy();
    // Initializing 
    for (i = 0; i < ARR_SIZE; ++i) s[i] = i, d[i] = 0;
    Copy_Longwords(s, d, ARR_SIZE);
}

아 끝났다


profile
임베디드 시스템 공학자를 지망하는 컴퓨터공학+전자공학 복수전공 학부생입니다. 타인의 피드백을 수용하고 숙고하고 대응하며 자극과 반응 사이의 간격을 늘리며 스스로 반응을 컨트롤 할 수 있는 주도적인 사람이 되는 것이 저의 20대의 목표입니다.

0개의 댓글