mirror of
https://github.com/fpganinja/taxi.git
synced 2026-04-07 12:38:44 -07:00
cndm: Use event queues in driver model
Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
@@ -14,7 +14,9 @@ import logging
|
|||||||
import struct
|
import struct
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
|
import cocotb
|
||||||
from cocotb.queue import Queue
|
from cocotb.queue import Queue
|
||||||
|
from cocotb.triggers import RisingEdge
|
||||||
|
|
||||||
|
|
||||||
# Command opcodes
|
# Command opcodes
|
||||||
@@ -61,19 +63,18 @@ CNDM_CMD_PTP_FLG_OFFSET_FNS = 0x00000010
|
|||||||
CNDM_CMD_PTP_FLG_SET_PERIOD = 0x00000080
|
CNDM_CMD_PTP_FLG_SET_PERIOD = 0x00000080
|
||||||
|
|
||||||
|
|
||||||
class Cq:
|
class Eq:
|
||||||
def __init__(self, driver, port):
|
def __init__(self, driver, port):
|
||||||
self.driver = driver
|
self.driver = driver
|
||||||
self.log = driver.log
|
self.log = driver.log
|
||||||
|
|
||||||
self.port = port
|
self.port = port
|
||||||
self.irqn = None
|
|
||||||
|
|
||||||
self.log_size = 0
|
self.log_size = 0
|
||||||
self.size = 0
|
self.size = 0
|
||||||
self.size_mask = 0
|
self.size_mask = 0
|
||||||
self.stride = 0
|
self.stride = 0
|
||||||
self.cqn = None
|
self.eqn = None
|
||||||
self.enabled = False
|
self.enabled = False
|
||||||
|
|
||||||
self.buf_size = 0
|
self.buf_size = 0
|
||||||
@@ -81,10 +82,9 @@ class Cq:
|
|||||||
self.buf_dma = 0
|
self.buf_dma = 0
|
||||||
self.buf = None
|
self.buf = None
|
||||||
|
|
||||||
self.eq = None
|
self.irqn = None
|
||||||
|
|
||||||
self.src_ring = None
|
self.cq_table = {}
|
||||||
self.handler = None
|
|
||||||
|
|
||||||
self.cons_ptr = None
|
self.cons_ptr = None
|
||||||
|
|
||||||
@@ -92,11 +92,9 @@ class Cq:
|
|||||||
self.hw_regs = self.driver.hw_regs
|
self.hw_regs = self.driver.hw_regs
|
||||||
|
|
||||||
async def open(self, irqn, size):
|
async def open(self, irqn, size):
|
||||||
if self.cqn is not None:
|
if self.eqn is not None:
|
||||||
raise Exception("Already open")
|
raise Exception("Already open")
|
||||||
|
|
||||||
self.irqn = irqn
|
|
||||||
|
|
||||||
self.log_size = size.bit_length() - 1
|
self.log_size = size.bit_length() - 1
|
||||||
self.size = 2**self.log_size
|
self.size = 2**self.log_size
|
||||||
self.size_mask = self.size-1
|
self.size_mask = self.size-1
|
||||||
@@ -111,13 +109,177 @@ class Cq:
|
|||||||
|
|
||||||
self.cons_ptr = 0
|
self.cons_ptr = 0
|
||||||
|
|
||||||
|
self.irqn = irqn
|
||||||
|
|
||||||
|
self.cq_table = {}
|
||||||
|
|
||||||
|
rsp = await self.driver.exec_cmd(struct.pack("<HHLLLLLLLQQLLLL",
|
||||||
|
0, # rsvd
|
||||||
|
CNDM_CMD_OP_CREATE_EQ, # opcode
|
||||||
|
0x00000000, # flags
|
||||||
|
self.port.index, # port
|
||||||
|
0, # eqn
|
||||||
|
self.irqn, # irqn
|
||||||
|
0, # pd
|
||||||
|
self.log_size, # size
|
||||||
|
0, # dboffs
|
||||||
|
self.buf_dma, # base addr
|
||||||
|
0, # ptr2
|
||||||
|
0, # prod_ptr
|
||||||
|
0, # cons_ptr
|
||||||
|
0, # rsvd
|
||||||
|
0, # rsvd
|
||||||
|
))
|
||||||
|
|
||||||
|
rsp_unpacked = struct.unpack("<HHLLLLLLLQQLLLL", rsp)
|
||||||
|
print(rsp_unpacked)
|
||||||
|
self.eqn = rsp_unpacked[4]
|
||||||
|
self.db_offset = rsp_unpacked[8]
|
||||||
|
|
||||||
|
if self.db_offset == 0:
|
||||||
|
self.eqn = None
|
||||||
|
self.db_offset = None
|
||||||
|
self.log.error("Failed to allocate EQ")
|
||||||
|
return
|
||||||
|
|
||||||
|
await self.write_cons_ptr_arm()
|
||||||
|
|
||||||
|
self.log.info("Opened EQ %d", self.eqn)
|
||||||
|
self.log.info("Using doorbell at offset 0x%08x", self.db_offset)
|
||||||
|
|
||||||
|
self.enabled = True
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
if self.eqn is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.enabled = False
|
||||||
|
|
||||||
|
rsp = await self.driver.exec_cmd(struct.pack("<HHLLLLLLLQQLLLL",
|
||||||
|
0, # rsvd
|
||||||
|
CNDM_CMD_OP_DESTROY_EQ, # opcode
|
||||||
|
0x00000000, # flags
|
||||||
|
self.port.index, # port
|
||||||
|
self.eqn, # eqn
|
||||||
|
0, # irqn
|
||||||
|
0, # pd
|
||||||
|
0, # size
|
||||||
|
0, # dboffs
|
||||||
|
0, # base addr
|
||||||
|
0, # ptr2
|
||||||
|
0, # prod_ptr
|
||||||
|
0, # cons_ptr
|
||||||
|
0, # rsvd
|
||||||
|
0, # rsvd
|
||||||
|
))
|
||||||
|
|
||||||
|
self.eqn = None
|
||||||
|
|
||||||
|
# TODO free buffer
|
||||||
|
|
||||||
|
def attach_cq(self, cq):
|
||||||
|
self.cq_table[cq.cqn] = cq
|
||||||
|
|
||||||
|
def detach_cq(self, cq):
|
||||||
|
del self.cq_table[cq.cqn]
|
||||||
|
|
||||||
|
async def write_cons_ptr(self):
|
||||||
|
await self.hw_regs.write_dword(self.db_offset, self.cons_ptr & 0xffff)
|
||||||
|
|
||||||
|
async def write_cons_ptr_arm(self):
|
||||||
|
await self.hw_regs.write_dword(self.db_offset, (self.cons_ptr & 0xffff) | 0x80000000)
|
||||||
|
|
||||||
|
async def process_eq(self):
|
||||||
|
self.log.info("Process EQ")
|
||||||
|
|
||||||
|
eq_cons_ptr = self.cons_ptr
|
||||||
|
eq_index = eq_cons_ptr & self.size_mask
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# event_data = struct.unpack_from("<HHLLLLLLL", self.buf, eq_index*self.stride)
|
||||||
|
event_data = struct.unpack_from("<HHLLL", self.buf, eq_index*self.stride)
|
||||||
|
|
||||||
|
self.log.info("EQ %d index %d data: %s", self.eqn, eq_index, repr(event_data))
|
||||||
|
|
||||||
|
if bool(event_data[-1] & 0x80000000) == bool(eq_cons_ptr & self.size):
|
||||||
|
self.log.info("EQ %d empty", self.eqn)
|
||||||
|
break
|
||||||
|
|
||||||
|
if event_data[1] == 0x0000:
|
||||||
|
# completion
|
||||||
|
self.log.info("Event from CQ %d", event_data[2])
|
||||||
|
cq = self.cq_table[event_data[2]]
|
||||||
|
await cq.handler(cq)
|
||||||
|
|
||||||
|
eq_cons_ptr += 1
|
||||||
|
eq_index = eq_cons_ptr & self.size_mask
|
||||||
|
|
||||||
|
self.cons_ptr = eq_cons_ptr
|
||||||
|
await self.write_cons_ptr_arm()
|
||||||
|
|
||||||
|
|
||||||
|
class Cq:
|
||||||
|
def __init__(self, driver, port):
|
||||||
|
self.driver = driver
|
||||||
|
self.log = driver.log
|
||||||
|
|
||||||
|
self.port = port
|
||||||
|
|
||||||
|
self.log_size = 0
|
||||||
|
self.size = 0
|
||||||
|
self.size_mask = 0
|
||||||
|
self.stride = 0
|
||||||
|
self.cqn = None
|
||||||
|
self.enabled = False
|
||||||
|
|
||||||
|
self.buf_size = 0
|
||||||
|
self.buf_region = None
|
||||||
|
self.buf_dma = 0
|
||||||
|
self.buf = None
|
||||||
|
|
||||||
|
self.eq = None
|
||||||
|
self.irqn = None
|
||||||
|
|
||||||
|
self.src_ring = None
|
||||||
|
self.handler = None
|
||||||
|
|
||||||
|
self.cons_ptr = None
|
||||||
|
|
||||||
|
self.db_offset = None
|
||||||
|
self.hw_regs = self.driver.hw_regs
|
||||||
|
|
||||||
|
async def open(self, eq, size):
|
||||||
|
if self.cqn is not None:
|
||||||
|
raise Exception("Already open")
|
||||||
|
|
||||||
|
self.log_size = size.bit_length() - 1
|
||||||
|
self.size = 2**self.log_size
|
||||||
|
self.size_mask = self.size-1
|
||||||
|
self.stride = 16
|
||||||
|
|
||||||
|
self.buf_size = self.size*self.stride
|
||||||
|
self.buf_region = self.driver.pool.alloc_region(self.buf_size)
|
||||||
|
self.buf_dma = self.buf_region.get_absolute_address(0)
|
||||||
|
self.buf = self.buf_region.mem
|
||||||
|
|
||||||
|
self.buf[0:self.buf_size] = b'\x00'*self.buf_size
|
||||||
|
|
||||||
|
self.cons_ptr = 0
|
||||||
|
|
||||||
|
if isinstance(eq, Eq):
|
||||||
|
self.eq = eq
|
||||||
|
dqn = eq.eqn
|
||||||
|
else:
|
||||||
|
self.irqn = eq
|
||||||
|
dqn = eq | 0x80000000
|
||||||
|
|
||||||
rsp = await self.driver.exec_cmd(struct.pack("<HHLLLLLLLQQLLLL",
|
rsp = await self.driver.exec_cmd(struct.pack("<HHLLLLLLLQQLLLL",
|
||||||
0, # rsvd
|
0, # rsvd
|
||||||
CNDM_CMD_OP_CREATE_CQ, # opcode
|
CNDM_CMD_OP_CREATE_CQ, # opcode
|
||||||
0x00000000, # flags
|
0x00000000, # flags
|
||||||
self.port.index, # port
|
self.port.index, # port
|
||||||
0, # cqn
|
0, # cqn
|
||||||
self.irqn, # eqn
|
dqn, # eqn
|
||||||
0, # pd
|
0, # pd
|
||||||
self.log_size, # size
|
self.log_size, # size
|
||||||
0, # dboffs
|
0, # dboffs
|
||||||
@@ -140,6 +302,9 @@ class Cq:
|
|||||||
self.log.error("Failed to allocate CQ")
|
self.log.error("Failed to allocate CQ")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.eq:
|
||||||
|
self.eq.attach_cq(self)
|
||||||
|
|
||||||
await self.write_cons_ptr_arm()
|
await self.write_cons_ptr_arm()
|
||||||
|
|
||||||
self.log.info("Opened CQ %d", self.cqn)
|
self.log.info("Opened CQ %d", self.cqn)
|
||||||
@@ -575,6 +740,9 @@ class Port:
|
|||||||
self.index = index
|
self.index = index
|
||||||
self.hw_regs = driver.hw_regs
|
self.hw_regs = driver.hw_regs
|
||||||
|
|
||||||
|
self.eq_count = 1
|
||||||
|
self.eq = []
|
||||||
|
|
||||||
self.rxq_count = 1
|
self.rxq_count = 1
|
||||||
self.rxq = []
|
self.rxq = []
|
||||||
|
|
||||||
@@ -584,12 +752,17 @@ class Port:
|
|||||||
self.rx_queue = Queue()
|
self.rx_queue = Queue()
|
||||||
|
|
||||||
async def init(self):
|
async def init(self):
|
||||||
|
for k in range(self.eq_count):
|
||||||
|
eq = Eq(self.driver, self)
|
||||||
|
await eq.open(self.index, 256)
|
||||||
|
self.eq.append(eq)
|
||||||
|
|
||||||
await self.open()
|
await self.open()
|
||||||
|
|
||||||
async def open(self):
|
async def open(self):
|
||||||
for k in range(self.rxq_count):
|
for k in range(self.rxq_count):
|
||||||
cq = Cq(self.driver, self)
|
cq = Cq(self.driver, self)
|
||||||
await cq.open(self.index, 256)
|
await cq.open(self.eq[0], 256)
|
||||||
|
|
||||||
q = Rq(self.driver, self)
|
q = Rq(self.driver, self)
|
||||||
await q.open(cq, 256)
|
await q.open(cq, 256)
|
||||||
@@ -598,7 +771,7 @@ class Port:
|
|||||||
|
|
||||||
for k in range(self.txq_count):
|
for k in range(self.txq_count):
|
||||||
cq = Cq(self.driver, self)
|
cq = Cq(self.driver, self)
|
||||||
await cq.open(self.index, 256)
|
await cq.open(self.eq[0], 256)
|
||||||
|
|
||||||
q = Sq(self.driver, self)
|
q = Sq(self.driver, self)
|
||||||
await q.open(cq, 256)
|
await q.open(cq, 256)
|
||||||
@@ -614,12 +787,36 @@ class Port:
|
|||||||
async def recv_nowait(self):
|
async def recv_nowait(self):
|
||||||
return self.rx_queue.get_nowait()
|
return self.rx_queue.get_nowait()
|
||||||
|
|
||||||
async def interrupt_handler(self):
|
|
||||||
self.log.info("Interrupt")
|
class Interrupt:
|
||||||
for q in self.rxq:
|
def __init__(self, index, handler=None):
|
||||||
await q.cq.handler(q.cq)
|
self.index = index
|
||||||
for q in self.txq:
|
self.queue = Queue()
|
||||||
await q.cq.handler(q.cq)
|
self.handler = handler
|
||||||
|
self.signal = None
|
||||||
|
|
||||||
|
cocotb.start_soon(self._run())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_edge(cls, index, signal, handler=None):
|
||||||
|
obj = cls(index, handler)
|
||||||
|
obj.signal = signal
|
||||||
|
cocotb.start_soon(obj._run_edge())
|
||||||
|
return obj
|
||||||
|
|
||||||
|
async def interrupt(self):
|
||||||
|
self.queue.put_nowait(None)
|
||||||
|
|
||||||
|
async def _run(self):
|
||||||
|
while True:
|
||||||
|
await self.queue.get()
|
||||||
|
if self.handler:
|
||||||
|
await self.handler(self.index)
|
||||||
|
|
||||||
|
async def _run_edge(self):
|
||||||
|
while True:
|
||||||
|
await RisingEdge(self.signal)
|
||||||
|
await self.interrupt()
|
||||||
|
|
||||||
|
|
||||||
class Driver:
|
class Driver:
|
||||||
@@ -630,6 +827,8 @@ class Driver:
|
|||||||
self.pool = None
|
self.pool = None
|
||||||
self.hw_regs = None
|
self.hw_regs = None
|
||||||
|
|
||||||
|
self.irq_list = []
|
||||||
|
|
||||||
self.port_count = None
|
self.port_count = None
|
||||||
|
|
||||||
self.ports = []
|
self.ports = []
|
||||||
@@ -685,6 +884,12 @@ class Driver:
|
|||||||
|
|
||||||
self.hw_regs = dev.bar_window[0]
|
self.hw_regs = dev.bar_window[0]
|
||||||
|
|
||||||
|
# set up MSI
|
||||||
|
for index in range(32):
|
||||||
|
irq = Interrupt(index, self.interrupt_handler)
|
||||||
|
self.dev.request_irq(index, irq.interrupt)
|
||||||
|
self.irq_list.append(irq)
|
||||||
|
|
||||||
await self.init_common()
|
await self.init_common()
|
||||||
|
|
||||||
async def init_common(self):
|
async def init_common(self):
|
||||||
@@ -882,7 +1087,6 @@ class Driver:
|
|||||||
for k in range(self.port_count):
|
for k in range(self.port_count):
|
||||||
port = Port(self, k)
|
port = Port(self, k)
|
||||||
await port.init()
|
await port.init()
|
||||||
self.dev.request_irq(k, port.interrupt_handler)
|
|
||||||
|
|
||||||
self.ports.append(port)
|
self.ports.append(port)
|
||||||
|
|
||||||
@@ -941,6 +1145,24 @@ class Driver:
|
|||||||
a[k] = await self.hw_regs.read_dword(0x10040+k*4)
|
a[k] = await self.hw_regs.read_dword(0x10040+k*4)
|
||||||
return a.tobytes()
|
return a.tobytes()
|
||||||
|
|
||||||
|
async def interrupt_handler(self, irqn):
|
||||||
|
self.log.info("Interrupt handler start (IRQ %d)", irqn)
|
||||||
|
for p in self.ports:
|
||||||
|
if p.eq:
|
||||||
|
# using EQs
|
||||||
|
for eq in p.eq:
|
||||||
|
if eq.irqn == irqn:
|
||||||
|
await eq.process_eq()
|
||||||
|
else:
|
||||||
|
# using IRQs directly from CQs
|
||||||
|
for q in p.rxq:
|
||||||
|
if q.cq.irqn == irqn:
|
||||||
|
await q.cq.handler(q.cq)
|
||||||
|
for q in p.txq:
|
||||||
|
if q.cq.irqn == irqn:
|
||||||
|
await q.cq.handler(q.cq)
|
||||||
|
self.log.info("Interrupt handler end (IRQ %d)", irqn)
|
||||||
|
|
||||||
def alloc_pkt(self):
|
def alloc_pkt(self):
|
||||||
if self.free_packets:
|
if self.free_packets:
|
||||||
return self.free_packets.popleft()
|
return self.free_packets.popleft()
|
||||||
|
|||||||
Reference in New Issue
Block a user