Skip to content

Instantly share code, notes, and snippets.

@Bigyin1
Last active May 19, 2025 18:27
Show Gist options
  • Save Bigyin1/32e9fc6485ef409c8443caf8349ebc7b to your computer and use it in GitHub Desktop.
Save Bigyin1/32e9fc6485ef409c8443caf8349ebc7b to your computer and use it in GitHub Desktop.

Логика работы IPIC

Входом IPIC являются 16 линий прерываний(IRQ lines). С помощью регистра IPIC_ICSR каждая из линий может быть выключена, также можно выбрать уровень активного сигнала линии.

Линия может быть настроена как:

  1. level-triggered - при активном уровне линии, соответсвующее ей прерывание переходит в состояние Pending. Отмена активного уровня переводит обратно в Idle.

  2. edge-triggered - прерывание переходит в состояние Pending по активному фронту линии. Дальнейшего поддержания уровня линии не требуется.

Поддерживаются вложенные прерывания. Больший приоритет у прерывания с меньшим номером. Все 16 IPIC прерываний мультиплексируются на один вектор прерывания в scr1 - Machine external interrupt. Номер конкретного прерывания становится известен из регистра IPIC_CISV.

Диаграмма состояний прерывания с кратким указанием причин смены состояний:

stateDiagram-v2
    direction LR
    state Serviced {
        Running: In Service
        Running--> Preempted : by higher priority IRQ
        Preempted --> Running : end of higher priority IRQ
    }


    Idle --> Pending: Triggered
    Pending --> Idle: Pending clear or line inactive level
    Pending --> Serviced: No IRQs in service or IRQ has higher priority

    Serviced -->  [*] : Idle
Loading

Pending - состояние, в которое переходит прерывание из состояния Idle и пребывает в нем до момента запуска. В этом соcтоянии могут находиться несколько прерываний. Перечислены в регистре IPIC_IPR. Состояние может быть отменено путем записи в соответсвующее поле IPIC_IPR. В случае level-triggered линии, происходит отмна состояния при переводе линии на неактивный уровень.

In Service - cостояние, в которое переходит прерывание из состояния Pending. В нём может находиться только одно прерывание, обработчик которого в данный момент исполняется(или будет исполняться первым в случае наличия прерываний от, например, таймера). Регистр IPIC_CISV содержит номер прерывания в этом состоянии.

Preempted - состояние, в которое переходит прерывание при вытеснении его другим с более высоким приоритетом. По завершении последнего, переходит обратно в In Service. В этом соcтоянии могут находиться несколько прерываний.

Serviced - здесь находятся уже запущенные прерывания, т.е для которых верно In Service || Preempted. Перечислены в регистре IPIC_ISVR.

В scr1 возможность прерывания(смена контекста) зависит от регистров mstatus.MIE и mie.MEIE. В IPIC включение каждой из IRQ линий контролируется регистром IPIC_ICSR. Прерывания от IPIC должны быть включены, иначе переход в In Service будет задержан до их включения.

После перехода нового прерывания в состояние Pending происходит следующее:

  1. При отсутствии прерывания в состоянии In Service:
flowchart LR
    Init["`**Pending**`"] --> E

    E[У нас наивысший приоритет среди IRQ в состоянии Pending?]
    E -->|Нет| Init
    E -->|Да| F

    F["`Переход нового IRQ в **In Service**`"]

Loading
  1. При наличии прерывания в состоянии In Service:
flowchart LR
    Init["`**Pending**`"] --> C

    C[Приоритет у нового IRQ более высокий?]

    C -->|Да| D
    C -->|Нет| Init 


    D["`Переход нового IRQ в 
    **In Service**, а
    старого в **Preempted**`"]

Loading

Переход Pending -> In Service:

  1. CSR MIP.MEIP выставляется в 1. Этого регистр не зависит от состояния регистров mstatus.MIE и mie.MEIE, а определяется только в IPIC. Фактически он сигнализирует он наличии хотя бы одного IRQ в состоянии Pending.
  2. При выполнении условия MIP.MEIP == 1 && mstatus.MIE == 1 && mie.MEIE == 1, в ядре scr1 происходит непосредственно прерывание со сменой контекста и установкой PC на вектор Machine external interrupt.

Важное замечание:

Сразу после начала прерывания(смены контекста) scr1 выставляет регистр mstatus.MIE в 0 и возвращает его в 1 после mret. То есть, происходит запрет любых прерываний в scr1(при этом IPIC продолжает работать в штатном режиме). В документации к scr1 в п. 7.5 допущена неточность: на временной диаграмме mstatus.MIE после смены контекста там остается в 1.

  1. Обработчик прерывания должен осуществить запись любого значения в регистр IPIC_SOI. Именно после этой записи прерывание переходит в состояние In Service. Интересно то, что до момента записи уже работающее прерывание может быть ассоциировано с другим IPIC IRQ более высокого приоритета, успевшего придти после смены контекста.
  2. Прерывание удаляется из регистра IPIC_IPR и попадает в регистры IPIC_CISV и IPIC_ISVR.
  3. CSR MIP.MEIP выставляется в 0. Он может остаться в 1, если одновременно с записью в IPIC_SOI придет новое прерывание с более высоким приоритетом.
  4. Вытесненное прерывание(если есть) удаляется из регистра IPIC_CISV. Его состояние остается на стеке, а выше него расположится стек новой ISR(Interrupt Service Routine).

Завершение работы ISR:

В конце своей работы ISR должна осуществить запись любого значения в регистр IPIC_EOI. Тогда прерывание стирается из регистров IPIC_CISV и IPIC_ISVR. При этом из состояния Preempted в In Service будет переведено прерывание с наивысшим приоритетом. Восстановленное прерывание попадает в IPIC_CISV.

Временные диаграммы работы IPIC в некоторых ситуациях

Одно прерывание

One IRQ

irq_lines - входы IPIC. Здесь они edge-triggered и новое прерывание в 39-м такте.

idx и vd - индекс наиболее приоритетного прерывания в состоянии Pending и его валидность, т.е vd == 0 означает отсутсвие прерывываний в состоянии Pending.

csr_mip_meip - регистр MIP.MEIP.

ipic_cisv_ff - регистр IPIC_CISV. Значение 16 означает отсутствие прерываний в состоянии In Service.

ipic_ipr_ff - регистр IPIC_IPR. Видно, как поле с индексом 5 сбрасывается после записи в IPIC_SOI.

ipic_soi_req - сигнал о выполнении записи в IPIC_SOI.

ipic_eoi_req - сигнал о выполнении записи в IPIC_EOI.

csr2exu_irq_o - сигнал о смене контекста ядром на обработчик прерывания.

csr_mstatus_mie_ff - регистр mstatus.MIE. Виден его переход в 0 после смены контекста.

e_mret - сигнал о выполении инструкции mret.

Два близких прерывания :

Two IRQs

Видно как в регистре IPIC_IPR меняются поля, соответсвующие прерываниям 4 и 6.

Второе прерывание во время выполнения первого:

Two IRQs

Второе прерывание с индексом 6 произошло на 59-м такте, а соответсвующая ему смена контекса в 74-м. Так происходит из-за глобального запрета прерываний(смены контекста) регистром mstatus.MIE.

Случай сохранения значения регитром MIP.MEIP :

Close IRQs

Вложенные прерывания:

Nested IRQs

Пример обработчика, который поддерживает вложенные прерывания. В нем необходимо сохранить регистр mepc, снять глобальный запрет на прерывания и перед mret восстановить mepc.

vec_machine_ext_handler:
    csrr            a1, mcause
    li              a5, MCAUSE_EXT_IRQ
    li              a0, -1
    bne             a1, a5, check_fail
    csrr            t1, mip
    li              t0, MIP_MEIP
    and             t0, t1, t0
    beqz            t0, check_fail

    csrw            IPIC_SOI, zero

    addi sp, sp, -4
    csrr a1, mepc
    sw a1, (sp)

    csrs                mstatus, MSTATUS_MIE

    nop
    nop
    nop
    nop

    csrw            IPIC_EOI, zero

    lw a1, (sp)
    csrw mepc, a1
    addi sp, sp, 4
    mret
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment