타이머 인터럽트 (재)

개요



이전에 타이머 인터럽트가 실현되었지만 인터럽트 전, 인터럽트 처리 및 인터럽트 후 컨텍스트 정보는 전혀 고려하지 않았습니다. 즉, 인터럽트가 들어간 후 인터럽트 처리로 레지스터가 파괴 될 가능성이있었습니다. 이전의 예에서는, 거의 타이머 인터럽트의 처리 밖에 없었기 때문에 파괴되는 일도 없고, 파손된 곳에서 어떻게 하지 않았지만, 향후 이 타이머 인터럽트로 thread를 전환하는 것을 생각하면, 문맥을 보존해 둘 필요가 있다.
따라서 컨텍스트를 저장하도록 타이머 인터럽트를 개선합니다. 또한 인터럽트 처리는 다른 스택을 사용하는 개선도 수행합니다.

컨텍스트 저장



컨텍스트 저장은 레지스터 값을 저장하는 것을 의미합니다. atmega328p에는 r0 ~ r31의 32 개의 레지스터가 있습니다. 또한, SREG라는 플래그 레지스터로 상태를 관리하고 있으므로, 어떻게 하면 된다.
  • 인터럽트시 스택에 이러한 값을 PUSH
  • 인터럽트 처리 함수 호출
  • 인터럽트 복귀 후 스택에 누적 된 레지스터의 값을 반환합니다.

  • 이 처리는 인터럽트 벡터에 등록 된 함수로 수행됩니다.

    인터럽트 벡터 개선 1



    컨텍스트를 저장하도록 인터럽트 벡터의 함수를 변경합니다. 이전에는 start.s에 함수를 정의했지만 인터럽트 전용 파일 (intr.s)을 준비합니다.

    intr.s
    .global intr_time
    .type   intr_time, @function
    
    intr_time:
        cli
        push r31
        push r30
        push r29
        push r28
        push r27
        push r26
        push r25
        push r24
        push r23
        push r22
        push r21
        push r20
        push r19
        push r18
        push r17
        push r16
        push r15
        push r14
        push r13
        push r12
        push r11
        push r10
        push r9
        push r8
        push r7
        push r6
        push r5
        push r4
        push r3
        push r2
        push r1
        push r0
        in r31, 0x3f ; save SREG to r31
        push r31     ; push SREG
        rcall t0a    ; t0aの呼び出し
        pop r31        ; pop SREG to r31
        out 0x3f, r31  ; set SREG
        pop r0
        pop r1
        pop r2
        pop r3
        pop r4
        pop r5
        pop r6
        pop r7
        pop r8
        pop r9
        pop r10
        pop r11
        pop r12
        pop r13
        pop r14
        pop r15
        pop r16
        pop r17
        pop r18
        pop r19
        pop r20
        pop r21
        pop r22
        pop r23
        pop r24
        pop r25
        pop r26
        pop r27
        pop r28
        pop r29
        pop r30
        pop r31
        reti
    

    앞에서 설명한 것처럼 r31에서 r0까지 SREG (플래그 레지스터) 스택에 쌓인 다음 t0a를 호출하고 반환되면 쌓인 순서와 반대로 스택에서 POP합니다. SREG는 메모리 0x3f에 존재합니다 (사양서에서).

    이 때의 스택의 모습은 다음과 같습니다.


    이와 같이, 인터럽트 처리를 하기 위해, 직전까지 실행하고 있던 메인의 스택을 그대로 이용하고 있다.

    인터럽트 벡터 개선 2



    위에서 설명한 것처럼 작동하지만 향후 스레드를 구현하는 것을 고려할 때 인터럽트 프로세스는 항상 스레드의 스택을 사용합니다. 실해는 없지만 인터럽트 처리는 전용 스택을 사용하고 싶다. 구체적으로는 다음과 같이 스택을 전환하여 사용하고 싶습니다.



    이 예에서는 t0a를 호출하기 전에 스택을 전환 (intstack)하고 t0a를 처리 한 후 다시 원래의 스택으로 되돌려 인터럽트 처리를 완료합니다. 이렇게하려면 intrstack를 링커 스크립트로 정의하십시오.

    ld.scr
    
    MEMORY{ 
    (略)
        ram(rwx)        : o = 0x800100, l = 0x800600 - 0x800100
        userstack(rw)   : o = 0x800600, l = 0x000000
        intrstack(rw)   : o = 0x800700, l = 0x000000 ; 割り込み用スタック
        bootstack(rw)   : o = 0x8007fc, l = 0x000000
    }
    
    SECTIONS
    {
    (略)
    
        . = ALIGN(4);
        _end = . ;
    
        .userstack : {
            _userstack = .;
        } > userstack
    
        .intrstack : {
            _intrstack = .;
        } > intrstack
    
      .bootstack : {
            _bootstack = .;
        } > bootstack
    }
    

    그런 다음 그림과 같이 처리를 수행하기 위해 intr_time를 다시 씁니다.

    intr.s
    intr_time:
            cli
            push r31
    (略)
            push r0
            in   r31, 0x3f ; SREG
            push r31
            in   r24, 0x3d ; save current sp low  to r22
            in   r25, 0x3e ; save current sp high to r23
            ldi  r28, lo8(_intrstack) ; save intrrstack lo byte to r28
            ldi  r29, hi8(_intrstack) ; save intrstack hi byte to r29
            out  0x3d, r28 ; save intrstack low   ; change sp to intrstack
            out  0x3e, r29 ; save intrstack high
            push r24       ; 旧SPのlowをintrstackに積む
            push r25       ; 旧SPのhiをintrstackに積む
            eor  r1, r1
    
            rcall t0a
    
            pop r29        ; pop old sp hi  from intrstack
            pop r28        ; pop old sp low from intrstack
            out 0x3d, r28  ; set sp low
            out 0x3e, r29  ; set sp high   -> change sp to original
            pop r31        ; restore SREG to r31
            out 0x3f, r31  ; set SREG
            pop r0
    (略)
            pop r31
            reti
    

    이 구현은 다음 명령으로 시도 할 수 있습니다.
    >git clone https://github.com/hiro4669/iosv.git
    >cd iosv
    >git branch interpt_ver1 origin/interpt_ver1
    >git checkout interpt_ver1
    >cd interpt
    >make
    >make write
    

    좋은 웹페이지 즐겨찾기