PYNQ 框架 - 中断(INTR)驱动-3. PYNQ 代码

时间:2024-11-13 15:03:24

3.1 AXI Timer

3.1.1 Timer 寄存器映射

查看该 IP 的所有寄存器映射,非常方便。

print(timer0.register_map)
print(timer1.register_map)
---
RegisterMap {
  TCSR0 = Register(MDT0=0, UDT0=1, GENT0=1, CAPT0=0, ARHT0=0, LOAD0=0, ENIT0=1, ENT0=0, T0INT=0, PWMA0=0, ENALL=0, CASC=0),
  TLR0 = Register(TCLR0=100000000),
  TCR0 = Register(TCR0=4294967295),
  TCSR1 = Register(MDT1=0, UDT1=0, GENT1=0, CAPT1=0, ARHT1=0, LOAD1=0, ENIT1=0, ENT1=0, T1INT=0, PWMA1=0, ENALL=0),
  TLR1 = Register(TCLR1=0),
  TCR1 = Register(TCR1=0)
}
RegisterMap {
  TCSR0 = Register(MDT0=0, UDT0=1, GENT0=0, CAPT0=0, ARHT0=0, LOAD0=0, ENIT0=1, ENT0=0, T0INT=0, PWMA0=0, ENALL=0, CASC=0),
  TLR0 = Register(TCLR0=200000000),
  TCR0 = Register(TCR0=4294967295),
  TCSR1 = Register(MDT1=0, UDT1=0, GENT1=0, CAPT1=0, ARHT1=0, LOAD1=0, ENIT1=0, ENT1=0, T1INT=0, PWMA1=0, ENALL=0),
  TLR1 = Register(TCLR1=0),
  TCR1 = Register(TCR1=0)
}

3.1.2 Timer 中断信息

本例中使用了两个 Timer,可以查看各自的信息:

timer0 = ol.axi_timer_0
timer0._interrupts
---
{'interrupt': {'controller': 'axi_intc_0',
  'index': 0,
  'fullpath': 'axi_timer_0/interrupt'}}
timer1 = ol.axi_timer_1
timer1._interrupts
---
{'interrupt': {'controller': 'axi_intc_0',
  'index': 1,
  'fullpath': 'axi_timer_1/interrupt'}}

或者通过 ol.interrupt_pins 查看:

ol.interrupt_pins
---
controller:"axi_intc_0"
index:0
fullpath:"axi_timer_0/interrupt"
controller:"axi_intc_0"
index:0
fullpath:"xlconcat_0/In0"
controller:"axi_intc_0"
index:1
fullpath:"axi_timer_1/interrupt"
controller:"axi_intc_0"
index:1
fullpath:"xlconcat_0/In1"

3.1.3 脉冲信号

1)单个脉冲信号 

timer0.register_map.TLR0 = 100_000_000   # Timmer Load Register
timer0.register_map.TCSR0.LOAD0 = 1 # TLR -> TCR
timer0.register_map.TCSR0.LOAD0 = 0 # 启动计数(TCSR0.ENT0)前,应清除此位
timer0.register_map.TCSR0.ARHT0 = 0 # 0 = Hold counter
timer0.register_map.TCSR0.GENT0 = 1 # 1 = Enables generate_out
timer0.register_map.TCSR0.ENIT0 = 1 # 1 = Enable Interrupt
timer0.register_map.TCSR0.UDT0  = 1 # 1 = Down Count
timer0.register_map.TCSR0.ENT0  = 1 # 1 = Enable Timmer
print('timer0 configured')

2)生成周期性的脉冲信号,参考以下代码:

timer0.register_map.TLR0 = 100_000_000   # Timmer Load Register
timer0.register_map.TCSR0.LOAD0 = 1 # TLR -> TCR
timer0.register_map.TCSR0.LOAD0 = 0 # 启动计数(TCSR0.ENT0)前,应清除此位
timer0.register_map.TCSR0.ARHT0 = 1 # 1 = Reload generate value
timer0.register_map.TCSR0.GENT0 = 1 # 1 = Enables generate_out
timer0.register_map.TCSR0.ENIT0 = 1 # 1 = Enable Interrupt
timer0.register_map.TCSR0.UDT0  = 1 # 1 = Down Count
timer0.register_map.TCSR0.ENT0  = 1 # 1 = Enable Timmer
print('timer0 configured')

3.2 PYNQ 中断示例

import pynq
import asyncio
import nest_asyncio

# 允许嵌套的事件循环
nest_asyncio.apply()

ol = pynq.Overlay('min6.bit')
ol.ip_dict

timer0 = ol.axi_timer_0
timer1 = ol.axi_timer_1
intc = ol.axi_intc_0

def intr_done():
    timer0.register_map.TCSR0.T0INT = 1 # Clear T0INT,清除中断标志
    timer0.register_map.TCSR0.ENT0  = 0 # 0 = Disable Timmer
    timer1.register_map.TCSR0.T0INT = 1 # Clear T0INT,清除中断标志
    timer1.register_map.TCSR0.ENT0  = 0 # 0 = Disable Timmer
    
    intc.register_map.IAR = 0x03 # Clear interrupt

async def wait_for_timer0(cycles):
    print('timer0 start')
    timer0.register_map.TLR0 = cycles   # Timmer Load Register
    timer0.register_map.TCSR0.LOAD0 = 1 # TLR -> TCR
    timer0.register_map.TCSR0.LOAD0 = 0 # 启动计数(TCSR0.ENT0)前,应清除此位
    timer0.register_map.TCSR0.ARHT0 = 0 # 0 = Hold counter
    timer0.register_map.TCSR0.GENT0 = 1 # 1 = Enable generate_out
    timer0.register_map.TCSR0.ENIT0 = 1 # 1 = Enable Interrupt
    timer0.register_map.TCSR0.UDT0  = 1 # 1 = Down Count
    timer0.register_map.TCSR0.ENT0  = 1 # 1 = Enable Timmer
    await timer0.interrupt.wait()
    timer0.register_map.TCSR0.ENT0  = 0 # 0 = Disable Timmer
    timer0.register_map.TCSR0.T0INT = 1 # Clear T0INT,清除中断标志
    print('timer0 done')

async def wait_for_timer1(cycles):
    print('timer1 start')
    timer1.register_map.TLR0 = cycles   # Timmer Load Register
    timer1.register_map.TCSR0.LOAD0 = 1 # TLR -> TCR
    timer1.register_map.TCSR0.LOAD0 = 0 # 启动计数(TCSR0.ENT0)前,应清除此位
    timer1.register_map.TCSR0.ARHT0 = 0 # 0 = Hold counter
    timer1.register_map.TCSR0.GENT0 = 0 # 0 = Disable generate_out
    timer1.register_map.TCSR0.ENIT0 = 1 # 1 = Enable Interrupt
    timer1.register_map.TCSR0.UDT0  = 1 # 1 = Down Count
    timer1.register_map.TCSR0.ENT0  = 1 # 1 = Enable Timmer
    await timer1.interrupt.wait()
    timer1.register_map.TCSR0.ENT0  = 0 # 0 = Disable Timmer
    timer1.register_map.TCSR0.T0INT = 1 # Clear T0INT,清除中断标志
    print('timer1 done')

async def main():
    task0 = asyncio.create_task(wait_for_timer0(100_000_000)) # 2s Max 4_294_967_295 cycles
    task1 = asyncio.create_task(wait_for_timer1(200_000_000)) # 4s

    await task0
    await task1
    intr_done()

asyncio.run(main())

PYNQ 框架中的硬件中断,不同于传统微控制器的中断服务程序(ISR),PYNQ 使用 Python 的异步编程模式来处理中断,这种方法更加灵活和高级。

PYNQ 中的中断处理机制:使用异步等待。

通过使用 asyncio 库中的 await 关键字,可以异步等待中断事件,Python 代码可以在不阻塞整个程序的情况下,等待硬件事件的发生。

await timer0.interrupt.wait()

这行代码实际上是在等待来自 timer 的中断信号。当中断发生时(计时器达到预设的计数值),这个等待状态会结束,程序将继续执行下一行代码。这与传统的中断服务程序在概念上有些相似,但在实现上更加现代和适合高级语言。

优势:

非阻塞:使用异步等待中断使得CPU可以在中断未发生时处理其他任务,这在多任务环境中非常有用。

简化的错误处理和资源管理:在 Python 的异步环境中,错误处理和资源管理可以通过现代编程实践(如上下文管理、异常处理等)来实现,这比传统的 ISR 中的错误处理和资源管理要简单和安全。