mirror of
https://github.com/corundum/corundum.git
synced 2025-01-16 08:12:53 +08:00
dd2853bf40
Signed-off-by: Alex Forencich <alex@alexforencich.com>
1540 lines
56 KiB
Python
1540 lines
56 KiB
Python
"""
|
|
|
|
Copyright 2019, The Regents of the University of California.
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice,
|
|
this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS
|
|
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR
|
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
|
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
|
OF SUCH DAMAGE.
|
|
|
|
The views and conclusions contained in the software and documentation are those
|
|
of the authors and should not be interpreted as representing official policies,
|
|
either expressed or implied, of The Regents of the University of California.
|
|
|
|
"""
|
|
|
|
import datetime
|
|
from collections import deque
|
|
|
|
import cocotb
|
|
from cocotb.log import SimLog
|
|
from cocotb.queue import Queue
|
|
from cocotb.triggers import Event, Edge, RisingEdge
|
|
|
|
from cocotbext.axi import Window
|
|
|
|
import struct
|
|
|
|
MQNIC_MAX_EVENT_RINGS = 1
|
|
MQNIC_MAX_TX_RINGS = 32
|
|
MQNIC_MAX_TX_CPL_RINGS = 32
|
|
MQNIC_MAX_RX_RINGS = 8
|
|
MQNIC_MAX_RX_CPL_RINGS = 8
|
|
|
|
# Register blocks
|
|
MQNIC_RB_REG_TYPE = 0x00
|
|
MQNIC_RB_REG_VER = 0x04
|
|
MQNIC_RB_REG_NEXT_PTR = 0x08
|
|
|
|
MQNIC_RB_FW_ID_TYPE = 0xFFFFFFFF
|
|
MQNIC_RB_FW_ID_VER = 0x00000100
|
|
MQNIC_RB_FW_ID_REG_FPGA_ID = 0x0C
|
|
MQNIC_RB_FW_ID_REG_FW_ID = 0x10
|
|
MQNIC_RB_FW_ID_REG_FW_VER = 0x14
|
|
MQNIC_RB_FW_ID_REG_BOARD_ID = 0x18
|
|
MQNIC_RB_FW_ID_REG_BOARD_VER = 0x1C
|
|
MQNIC_RB_FW_ID_REG_BUILD_DATE = 0x20
|
|
MQNIC_RB_FW_ID_REG_GIT_HASH = 0x24
|
|
MQNIC_RB_FW_ID_REG_REL_INFO = 0x28
|
|
|
|
MQNIC_RB_GPIO_TYPE = 0x0000C100
|
|
MQNIC_RB_GPIO_VER = 0x00000100
|
|
MQNIC_RB_GPIO_REG_GPIO_IN = 0x0C
|
|
MQNIC_RB_GPIO_REG_GPIO_OUT = 0x10
|
|
|
|
MQNIC_RB_I2C_TYPE = 0x0000C110
|
|
MQNIC_RB_I2C_VER = 0x00000100
|
|
MQNIC_RB_I2C_REG_CTRL = 0x0C
|
|
|
|
MQNIC_RB_SPI_FLASH_TYPE = 0x0000C120
|
|
MQNIC_RB_SPI_FLASH_VER = 0x00000100
|
|
MQNIC_RB_SPI_FLASH_REG_FORMAT = 0x0C
|
|
MQNIC_RB_SPI_FLASH_REG_CTRL_0 = 0x10
|
|
MQNIC_RB_SPI_FLASH_REG_CTRL_1 = 0x14
|
|
|
|
MQNIC_RB_BPI_FLASH_TYPE = 0x0000C121
|
|
MQNIC_RB_BPI_FLASH_VER = 0x00000100
|
|
MQNIC_RB_BPI_FLASH_REG_FORMAT = 0x0C
|
|
MQNIC_RB_BPI_FLASH_REG_ADDR = 0x10
|
|
MQNIC_RB_BPI_FLASH_REG_DATA = 0x14
|
|
MQNIC_RB_BPI_FLASH_REG_CTRL = 0x18
|
|
|
|
MQNIC_RB_ALVEO_BMC_TYPE = 0x0000C140
|
|
MQNIC_RB_ALVEO_BMC_VER = 0x00000100
|
|
MQNIC_RB_ALVEO_BMC_REG_ADDR = 0x0C
|
|
MQNIC_RB_ALVEO_BMC_REG_DATA = 0x10
|
|
|
|
MQNIC_RB_GECKO_BMC_TYPE = 0x0000C141
|
|
MQNIC_RB_GECKO_BMC_VER = 0x00000100
|
|
MQNIC_RB_GECKO_BMC_REG_STATUS = 0x0C
|
|
MQNIC_RB_GECKO_BMC_REG_DATA = 0x10
|
|
MQNIC_RB_GECKO_BMC_REG_CMD = 0x14
|
|
|
|
MQNIC_RB_STATS_TYPE = 0x0000C004
|
|
MQNIC_RB_STATS_VER = 0x00000100
|
|
MQNIC_RB_STATS_REG_OFFSET = 0x0C
|
|
MQNIC_RB_STATS_REG_COUNT = 0x10
|
|
MQNIC_RB_STATS_REG_STRIDE = 0x14
|
|
MQNIC_RB_STATS_REG_FLAGS = 0x18
|
|
|
|
MQNIC_RB_PHC_TYPE = 0x0000C080
|
|
MQNIC_RB_PHC_VER = 0x00000100
|
|
MQNIC_RB_PHC_REG_CTRL = 0x0C
|
|
MQNIC_RB_PHC_REG_CUR_FNS = 0x10
|
|
MQNIC_RB_PHC_REG_CUR_NS = 0x14
|
|
MQNIC_RB_PHC_REG_CUR_SEC_L = 0x18
|
|
MQNIC_RB_PHC_REG_CUR_SEC_H = 0x1C
|
|
MQNIC_RB_PHC_REG_GET_FNS = 0x20
|
|
MQNIC_RB_PHC_REG_GET_NS = 0x24
|
|
MQNIC_RB_PHC_REG_GET_SEC_L = 0x28
|
|
MQNIC_RB_PHC_REG_GET_SEC_H = 0x2C
|
|
MQNIC_RB_PHC_REG_SET_FNS = 0x30
|
|
MQNIC_RB_PHC_REG_SET_NS = 0x34
|
|
MQNIC_RB_PHC_REG_SET_SEC_L = 0x38
|
|
MQNIC_RB_PHC_REG_SET_SEC_H = 0x3C
|
|
MQNIC_RB_PHC_REG_PERIOD_FNS = 0x40
|
|
MQNIC_RB_PHC_REG_PERIOD_NS = 0x44
|
|
MQNIC_RB_PHC_REG_NOM_PERIOD_FNS = 0x48
|
|
MQNIC_RB_PHC_REG_NOM_PERIOD_NS = 0x4C
|
|
MQNIC_RB_PHC_REG_ADJ_FNS = 0x50
|
|
MQNIC_RB_PHC_REG_ADJ_NS = 0x54
|
|
MQNIC_RB_PHC_REG_ADJ_COUNT = 0x58
|
|
MQNIC_RB_PHC_REG_ADJ_ACTIVE = 0x5C
|
|
|
|
MQNIC_RB_PHC_PEROUT_TYPE = 0x0000C081
|
|
MQNIC_RB_PHC_PEROUT_VER = 0x00000100
|
|
MQNIC_RB_PHC_PEROUT_REG_CTRL = 0x0C
|
|
MQNIC_RB_PHC_PEROUT_REG_START_FNS = 0x10
|
|
MQNIC_RB_PHC_PEROUT_REG_START_NS = 0x14
|
|
MQNIC_RB_PHC_PEROUT_REG_START_SEC_L = 0x18
|
|
MQNIC_RB_PHC_PEROUT_REG_START_SEC_H = 0x1C
|
|
MQNIC_RB_PHC_PEROUT_REG_PERIOD_FNS = 0x20
|
|
MQNIC_RB_PHC_PEROUT_REG_PERIOD_NS = 0x24
|
|
MQNIC_RB_PHC_PEROUT_REG_PERIOD_SEC_L = 0x28
|
|
MQNIC_RB_PHC_PEROUT_REG_PERIOD_SEC_H = 0x2C
|
|
MQNIC_RB_PHC_PEROUT_REG_WIDTH_FNS = 0x30
|
|
MQNIC_RB_PHC_PEROUT_REG_WIDTH_NS = 0x34
|
|
MQNIC_RB_PHC_PEROUT_REG_WIDTH_SEC_L = 0x38
|
|
MQNIC_RB_PHC_PEROUT_REG_WIDTH_SEC_H = 0x3C
|
|
|
|
MQNIC_RB_IF_TYPE = 0x0000C000
|
|
MQNIC_RB_IF_VER = 0x00000100
|
|
MQNIC_RB_IF_REG_OFFSET = 0x0C
|
|
MQNIC_RB_IF_REG_COUNT = 0x10
|
|
MQNIC_RB_IF_REG_STRIDE = 0x14
|
|
MQNIC_RB_IF_REG_CSR_OFFSET = 0x18
|
|
|
|
MQNIC_RB_IF_CTRL_TYPE = 0x0000C001
|
|
MQNIC_RB_IF_CTRL_VER = 0x00000400
|
|
MQNIC_RB_IF_CTRL_REG_FEATURES = 0x0C
|
|
MQNIC_RB_IF_CTRL_REG_PORT_COUNT = 0x10
|
|
MQNIC_RB_IF_CTRL_REG_SCHED_COUNT = 0x14
|
|
MQNIC_RB_IF_CTRL_REG_MAX_TX_MTU = 0x20
|
|
MQNIC_RB_IF_CTRL_REG_MAX_RX_MTU = 0x24
|
|
MQNIC_RB_IF_CTRL_REG_TX_MTU = 0x28
|
|
MQNIC_RB_IF_CTRL_REG_RX_MTU = 0x2C
|
|
|
|
MQNIC_IF_FEATURE_RSS = (1 << 0)
|
|
MQNIC_IF_FEATURE_PTP_TS = (1 << 4)
|
|
MQNIC_IF_FEATURE_TX_CSUM = (1 << 8)
|
|
MQNIC_IF_FEATURE_RX_CSUM = (1 << 9)
|
|
MQNIC_IF_FEATURE_RX_HASH = (1 << 10)
|
|
|
|
MQNIC_RB_RX_QUEUE_MAP_TYPE = 0x0000C090
|
|
MQNIC_RB_RX_QUEUE_MAP_VER = 0x00000100
|
|
MQNIC_RB_RX_QUEUE_MAP_REG_PORTS = 0x0C
|
|
MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET = 0x10
|
|
MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE = 0x10
|
|
MQNIC_RB_RX_QUEUE_MAP_CH_REG_OFFSET = 0x00
|
|
MQNIC_RB_RX_QUEUE_MAP_CH_REG_RSS_MASK = 0x04
|
|
MQNIC_RB_RX_QUEUE_MAP_CH_REG_APP_MASK = 0x08
|
|
|
|
MQNIC_RB_EVENT_QM_TYPE = 0x0000C010
|
|
MQNIC_RB_EVENT_QM_VER = 0x00000100
|
|
MQNIC_RB_EVENT_QM_REG_OFFSET = 0x0C
|
|
MQNIC_RB_EVENT_QM_REG_COUNT = 0x10
|
|
MQNIC_RB_EVENT_QM_REG_STRIDE = 0x14
|
|
|
|
MQNIC_RB_TX_QM_TYPE = 0x0000C020
|
|
MQNIC_RB_TX_QM_VER = 0x00000100
|
|
MQNIC_RB_TX_QM_REG_OFFSET = 0x0C
|
|
MQNIC_RB_TX_QM_REG_COUNT = 0x10
|
|
MQNIC_RB_TX_QM_REG_STRIDE = 0x14
|
|
|
|
MQNIC_RB_TX_CQM_TYPE = 0x0000C030
|
|
MQNIC_RB_TX_CQM_VER = 0x00000100
|
|
MQNIC_RB_TX_CQM_REG_OFFSET = 0x0C
|
|
MQNIC_RB_TX_CQM_REG_COUNT = 0x10
|
|
MQNIC_RB_TX_CQM_REG_STRIDE = 0x14
|
|
|
|
MQNIC_RB_RX_QM_TYPE = 0x0000C021
|
|
MQNIC_RB_RX_QM_VER = 0x00000100
|
|
MQNIC_RB_RX_QM_REG_OFFSET = 0x0C
|
|
MQNIC_RB_RX_QM_REG_COUNT = 0x10
|
|
MQNIC_RB_RX_QM_REG_STRIDE = 0x14
|
|
|
|
MQNIC_RB_RX_CQM_TYPE = 0x0000C031
|
|
MQNIC_RB_RX_CQM_VER = 0x00000100
|
|
MQNIC_RB_RX_CQM_REG_OFFSET = 0x0C
|
|
MQNIC_RB_RX_CQM_REG_COUNT = 0x10
|
|
MQNIC_RB_RX_CQM_REG_STRIDE = 0x14
|
|
|
|
MQNIC_RB_PORT_TYPE = 0x0000C002
|
|
MQNIC_RB_PORT_VER = 0x00000200
|
|
MQNIC_RB_PORT_REG_OFFSET = 0x0C
|
|
|
|
MQNIC_RB_PORT_CTRL_TYPE = 0x0000C003
|
|
MQNIC_RB_PORT_CTRL_VER = 0x00000200
|
|
MQNIC_RB_PORT_CTRL_REG_FEATURES = 0x0C
|
|
MQNIC_RB_PORT_CTRL_REG_TX_STATUS = 0x10
|
|
MQNIC_RB_PORT_CTRL_REG_RX_STATUS = 0x14
|
|
|
|
MQNIC_RB_SCHED_BLOCK_TYPE = 0x0000C004
|
|
MQNIC_RB_SCHED_BLOCK_VER = 0x00000300
|
|
MQNIC_RB_SCHED_BLOCK_REG_OFFSET = 0x0C
|
|
|
|
MQNIC_RB_SCHED_RR_TYPE = 0x0000C040
|
|
MQNIC_RB_SCHED_RR_VER = 0x00000100
|
|
MQNIC_RB_SCHED_RR_REG_OFFSET = 0x0C
|
|
MQNIC_RB_SCHED_RR_REG_CH_COUNT = 0x10
|
|
MQNIC_RB_SCHED_RR_REG_CH_STRIDE = 0x14
|
|
MQNIC_RB_SCHED_RR_REG_CTRL = 0x18
|
|
MQNIC_RB_SCHED_RR_REG_DEST = 0x1C
|
|
|
|
MQNIC_RB_SCHED_CTRL_TDMA_TYPE = 0x0000C050
|
|
MQNIC_RB_SCHED_CTRL_TDMA_VER = 0x00000100
|
|
MQNIC_RB_SCHED_CTRL_TDMA_REG_OFFSET = 0x0C
|
|
MQNIC_RB_SCHED_CTRL_TDMA_REG_CH_COUNT = 0x10
|
|
MQNIC_RB_SCHED_CTRL_TDMA_REG_CH_STRIDE = 0x14
|
|
MQNIC_RB_SCHED_CTRL_TDMA_REG_CTRL = 0x18
|
|
MQNIC_RB_SCHED_CTRL_TDMA_REG_TS_COUNT = 0x1C
|
|
|
|
MQNIC_RB_TDMA_SCH_TYPE = 0x0000C060
|
|
MQNIC_RB_TDMA_SCH_VER = 0x00000100
|
|
MQNIC_RB_TDMA_SCH_REG_TS_COUNT = 0x0C
|
|
MQNIC_RB_TDMA_SCH_REG_CTRL = 0x10
|
|
MQNIC_RB_TDMA_SCH_REG_STATUS = 0x14
|
|
MQNIC_RB_TDMA_SCH_REG_SCH_START_FNS = 0x20
|
|
MQNIC_RB_TDMA_SCH_REG_SCH_START_NS = 0x24
|
|
MQNIC_RB_TDMA_SCH_REG_SCH_START_SEC_L = 0x28
|
|
MQNIC_RB_TDMA_SCH_REG_SCH_START_SEC_H = 0x2C
|
|
MQNIC_RB_TDMA_SCH_REG_SCH_PERIOD_FNS = 0x30
|
|
MQNIC_RB_TDMA_SCH_REG_SCH_PERIOD_NS = 0x34
|
|
MQNIC_RB_TDMA_SCH_REG_SCH_PERIOD_SEC_L = 0x38
|
|
MQNIC_RB_TDMA_SCH_REG_SCH_PERIOD_SEC_H = 0x3C
|
|
MQNIC_RB_TDMA_SCH_REG_TS_PERIOD_FNS = 0x40
|
|
MQNIC_RB_TDMA_SCH_REG_TS_PERIOD_NS = 0x44
|
|
MQNIC_RB_TDMA_SCH_REG_TS_PERIOD_SEC_L = 0x48
|
|
MQNIC_RB_TDMA_SCH_REG_TS_PERIOD_SEC_H = 0x4C
|
|
MQNIC_RB_TDMA_SCH_REG_ACTIVE_PERIOD_FNS = 0x50
|
|
MQNIC_RB_TDMA_SCH_REG_ACTIVE_PERIOD_NS = 0x54
|
|
MQNIC_RB_TDMA_SCH_REG_ACTIVE_PERIOD_SEC_L = 0x58
|
|
MQNIC_RB_TDMA_SCH_REG_ACTIVE_PERIOD_SEC_H = 0x5C
|
|
|
|
MQNIC_RB_APP_INFO_TYPE = 0x0000C005
|
|
MQNIC_RB_APP_INFO_VER = 0x00000200
|
|
MQNIC_RB_APP_INFO_REG_ID = 0x0C
|
|
|
|
MQNIC_QUEUE_BASE_ADDR_REG = 0x00
|
|
MQNIC_QUEUE_ACTIVE_LOG_SIZE_REG = 0x08
|
|
MQNIC_QUEUE_CPL_QUEUE_INDEX_REG = 0x0C
|
|
MQNIC_QUEUE_HEAD_PTR_REG = 0x10
|
|
MQNIC_QUEUE_TAIL_PTR_REG = 0x18
|
|
|
|
MQNIC_QUEUE_ACTIVE_MASK = 0x80000000
|
|
|
|
MQNIC_CPL_QUEUE_BASE_ADDR_REG = 0x00
|
|
MQNIC_CPL_QUEUE_ACTIVE_LOG_SIZE_REG = 0x08
|
|
MQNIC_CPL_QUEUE_INTERRUPT_INDEX_REG = 0x0C
|
|
MQNIC_CPL_QUEUE_HEAD_PTR_REG = 0x10
|
|
MQNIC_CPL_QUEUE_TAIL_PTR_REG = 0x18
|
|
|
|
MQNIC_CPL_QUEUE_ACTIVE_MASK = 0x80000000
|
|
|
|
MQNIC_CPL_QUEUE_ARM_MASK = 0x80000000
|
|
MQNIC_CPL_QUEUE_CONT_MASK = 0x40000000
|
|
|
|
MQNIC_EVENT_QUEUE_BASE_ADDR_REG = 0x00
|
|
MQNIC_EVENT_QUEUE_ACTIVE_LOG_SIZE_REG = 0x08
|
|
MQNIC_EVENT_QUEUE_INTERRUPT_INDEX_REG = 0x0C
|
|
MQNIC_EVENT_QUEUE_HEAD_PTR_REG = 0x10
|
|
MQNIC_EVENT_QUEUE_TAIL_PTR_REG = 0x18
|
|
|
|
MQNIC_EVENT_QUEUE_ACTIVE_MASK = 0x80000000
|
|
|
|
MQNIC_EVENT_QUEUE_ARM_MASK = 0x80000000
|
|
MQNIC_EVENT_QUEUE_CONT_MASK = 0x40000000
|
|
|
|
MQNIC_EVENT_TYPE_TX_CPL = 0x0000
|
|
MQNIC_EVENT_TYPE_RX_CPL = 0x0001
|
|
|
|
MQNIC_DESC_SIZE = 16
|
|
MQNIC_CPL_SIZE = 32
|
|
MQNIC_EVENT_SIZE = 32
|
|
|
|
|
|
class RegBlock(Window):
|
|
def __init__(self, parent, offset, size, base=0, **kwargs):
|
|
super().__init__(parent, offset, size, base, **kwargs)
|
|
self._offset = offset
|
|
self.type = 0
|
|
self.version = 0
|
|
|
|
|
|
class RegBlockList:
|
|
def __init__(self):
|
|
self.blocks = []
|
|
|
|
async def enumerate_reg_blocks(self, window, offset=0):
|
|
while True:
|
|
rb_type = await window.read_dword(offset+MQNIC_RB_REG_TYPE)
|
|
rb_version = await window.read_dword(offset+MQNIC_RB_REG_VER)
|
|
rb = window.create_window(offset, window_type=RegBlock)
|
|
rb.type = rb_type
|
|
rb.version = rb_version
|
|
print(f"Block ID {rb_type:#010x} version {rb_version:#010x} at offset {offset:#010x}")
|
|
self.blocks.append(rb)
|
|
offset = await window.read_dword(offset+MQNIC_RB_REG_NEXT_PTR)
|
|
if offset == 0:
|
|
return
|
|
assert offset & 0x3 == 0, "Register block not aligned"
|
|
for block in self.blocks:
|
|
assert block.offset != offset, "Register blocks form a loop"
|
|
|
|
def find(self, rb_type, version=None, index=0):
|
|
for block in self.blocks:
|
|
if block.type == rb_type and (not version or block.version == version):
|
|
if index <= 0:
|
|
return block
|
|
else:
|
|
index -= 1
|
|
return None
|
|
|
|
def __getitem__(self, key):
|
|
return self.blocks[key]
|
|
|
|
def __len__(self):
|
|
return len(self.blocks)
|
|
|
|
|
|
class Packet:
|
|
def __init__(self, data=b''):
|
|
self.data = data
|
|
self.queue = None
|
|
self.timestamp_s = None
|
|
self.timestamp_ns = None
|
|
self.rx_checksum = None
|
|
|
|
def __repr__(self):
|
|
return (
|
|
f'{type(self).__name__}(data={self.data}, '
|
|
f'queue={self.queue}, '
|
|
f'timestamp_s={self.timestamp_s}, '
|
|
f'timestamp_ns={self.timestamp_ns}, '
|
|
f'rx_checksum={self.rx_checksum:#06x})'
|
|
)
|
|
|
|
def __iter__(self):
|
|
return self.data.__iter__()
|
|
|
|
def __len__(self):
|
|
return len(self.data)
|
|
|
|
def __bytes__(self):
|
|
return bytes(self.data)
|
|
|
|
|
|
class EqRing:
|
|
def __init__(self, interface, size, stride, index, hw_regs):
|
|
self.interface = interface
|
|
self.log = interface.log
|
|
self.driver = interface.driver
|
|
self.log_size = size.bit_length() - 1
|
|
self.size = 2**self.log_size
|
|
self.size_mask = self.size-1
|
|
self.stride = stride
|
|
self.index = index
|
|
self.interrupt_index = 0
|
|
|
|
self.head_ptr = 0
|
|
self.tail_ptr = 0
|
|
|
|
self.hw_ptr_mask = 0xffff
|
|
self.hw_regs = hw_regs
|
|
|
|
async def init(self):
|
|
self.log.info("Init EqRing %d (interface %d)", self.index, self.interface.index)
|
|
|
|
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
|
|
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_ACTIVE_LOG_SIZE_REG, 0) # active, log size
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_BASE_ADDR_REG, self.buf_dma & 0xffffffff) # base address
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_BASE_ADDR_REG+4, self.buf_dma >> 32) # base address
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_INTERRUPT_INDEX_REG, 0) # interrupt index
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_HEAD_PTR_REG, self.head_ptr & self.hw_ptr_mask) # head pointer
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_TAIL_PTR_REG, self.tail_ptr & self.hw_ptr_mask) # tail pointer
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_ACTIVE_LOG_SIZE_REG, self.log_size) # active, log size
|
|
|
|
async def activate(self, int_index):
|
|
self.log.info("Activate EqRing %d (interface %d)", self.index, self.interface.index)
|
|
|
|
self.interrupt_index = int_index
|
|
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_ACTIVE_LOG_SIZE_REG, 0) # active, log size
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_INTERRUPT_INDEX_REG, int_index) # interrupt index
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_HEAD_PTR_REG, self.head_ptr & self.hw_ptr_mask) # head pointer
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_TAIL_PTR_REG, self.tail_ptr & self.hw_ptr_mask) # tail pointer
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_ACTIVE_LOG_SIZE_REG, self.log_size | MQNIC_EVENT_QUEUE_ACTIVE_MASK) # active, log size
|
|
|
|
async def deactivate(self):
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_ACTIVE_LOG_SIZE_REG, self.log_size) # active, log size
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_INTERRUPT_INDEX_REG, self.interrupt_index) # interrupt index
|
|
|
|
def empty(self):
|
|
return self.head_ptr == self.tail_ptr
|
|
|
|
def full(self):
|
|
return self.head_ptr - self.tail_ptr >= self.size
|
|
|
|
async def read_head_ptr(self):
|
|
val = await self.hw_regs.read_dword(MQNIC_EVENT_QUEUE_HEAD_PTR_REG)
|
|
self.head_ptr += (val - self.head_ptr) & self.hw_ptr_mask
|
|
|
|
async def write_tail_ptr(self):
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_TAIL_PTR_REG, self.tail_ptr & self.hw_ptr_mask)
|
|
|
|
async def arm(self):
|
|
await self.hw_regs.write_dword(MQNIC_EVENT_QUEUE_INTERRUPT_INDEX_REG, self.interrupt_index | MQNIC_EVENT_QUEUE_ARM_MASK) # interrupt index
|
|
|
|
async def process(self):
|
|
if not self.interface.port_up:
|
|
return
|
|
|
|
self.log.info("Process event queue")
|
|
|
|
await self.read_head_ptr()
|
|
|
|
eq_tail_ptr = self.tail_ptr
|
|
eq_index = eq_tail_ptr & self.size_mask
|
|
|
|
self.log.info("%d events in queue", self.head_ptr - eq_tail_ptr)
|
|
|
|
while (self.head_ptr != eq_tail_ptr):
|
|
event_data = struct.unpack_from("<HH", self.buf, eq_index*self.stride)
|
|
|
|
self.log.info("Event data: %s", repr(event_data))
|
|
|
|
if event_data[0] == 0:
|
|
# transmit completion
|
|
cq = self.interface.tx_cpl_queues[event_data[1]]
|
|
await self.interface.process_tx_cq(cq)
|
|
await cq.arm()
|
|
elif event_data[0] == 1:
|
|
# receive completion
|
|
cq = self.interface.rx_cpl_queues[event_data[1]]
|
|
await self.interface.process_rx_cq(cq)
|
|
await cq.arm()
|
|
|
|
eq_tail_ptr += 1
|
|
eq_index = eq_tail_ptr & self.size_mask
|
|
|
|
self.tail_ptr = eq_tail_ptr
|
|
await self.write_tail_ptr()
|
|
|
|
|
|
class CqRing:
|
|
def __init__(self, interface, size, stride, index, hw_regs):
|
|
self.interface = interface
|
|
self.log = interface.log
|
|
self.driver = interface.driver
|
|
self.log_size = size.bit_length() - 1
|
|
self.size = 2**self.log_size
|
|
self.size_mask = self.size-1
|
|
self.stride = stride
|
|
self.index = index
|
|
self.interrupt_index = 0
|
|
self.ring_index = 0
|
|
|
|
self.head_ptr = 0
|
|
self.tail_ptr = 0
|
|
|
|
self.hw_ptr_mask = 0xffff
|
|
self.hw_regs = hw_regs
|
|
|
|
async def init(self):
|
|
self.log.info("Init CqRing %d (interface %d)", self.index, self.interface.index)
|
|
|
|
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
|
|
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_ACTIVE_LOG_SIZE_REG, 0) # active, log size
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_BASE_ADDR_REG, self.buf_dma & 0xffffffff) # base address
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_BASE_ADDR_REG+4, self.buf_dma >> 32) # base address
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_INTERRUPT_INDEX_REG, 0) # event index
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_HEAD_PTR_REG, self.head_ptr & self.hw_ptr_mask) # head pointer
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_TAIL_PTR_REG, self.tail_ptr & self.hw_ptr_mask) # tail pointer
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_ACTIVE_LOG_SIZE_REG, self.log_size) # active, log size
|
|
|
|
async def activate(self, int_index):
|
|
self.log.info("Activate CqRing %d (interface %d)", self.index, self.interface.index)
|
|
|
|
self.interrupt_index = int_index
|
|
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_ACTIVE_LOG_SIZE_REG, 0) # active, log size
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_INTERRUPT_INDEX_REG, int_index) # event index
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_HEAD_PTR_REG, self.head_ptr & self.hw_ptr_mask) # head pointer
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_TAIL_PTR_REG, self.tail_ptr & self.hw_ptr_mask) # tail pointer
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_ACTIVE_LOG_SIZE_REG, self.log_size | MQNIC_CPL_QUEUE_ACTIVE_MASK) # active, log size
|
|
|
|
async def deactivate(self):
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_ACTIVE_LOG_SIZE_REG, self.log_size) # active, log size
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_INTERRUPT_INDEX_REG, self.interrupt_index) # event index
|
|
|
|
def empty(self):
|
|
return self.head_ptr == self.tail_ptr
|
|
|
|
def full(self):
|
|
return self.head_ptr - self.tail_ptr >= self.size
|
|
|
|
async def read_head_ptr(self):
|
|
val = await self.hw_regs.read_dword(MQNIC_CPL_QUEUE_HEAD_PTR_REG)
|
|
self.head_ptr += (val - self.head_ptr) & self.hw_ptr_mask
|
|
|
|
async def write_tail_ptr(self):
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_TAIL_PTR_REG, self.tail_ptr & self.hw_ptr_mask)
|
|
|
|
async def arm(self):
|
|
await self.hw_regs.write_dword(MQNIC_CPL_QUEUE_INTERRUPT_INDEX_REG, self.interrupt_index | MQNIC_CPL_QUEUE_ARM_MASK) # event index
|
|
|
|
|
|
class TxRing:
|
|
def __init__(self, interface, size, stride, index, hw_regs):
|
|
self.interface = interface
|
|
self.log = interface.log
|
|
self.driver = interface.driver
|
|
self.log_queue_size = size.bit_length() - 1
|
|
self.log_desc_block_size = int(stride/MQNIC_DESC_SIZE).bit_length() - 1
|
|
self.desc_block_size = 2**self.log_desc_block_size
|
|
self.size = 2**self.log_queue_size
|
|
self.size_mask = self.size-1
|
|
self.full_size = self.size >> 1
|
|
self.stride = stride
|
|
self.index = index
|
|
self.cpl_index = 0
|
|
|
|
self.head_ptr = 0
|
|
self.tail_ptr = 0
|
|
self.clean_tail_ptr = 0
|
|
|
|
self.clean_event = Event()
|
|
|
|
self.packets = 0
|
|
self.bytes = 0
|
|
|
|
self.hw_ptr_mask = 0xffff
|
|
self.hw_regs = hw_regs
|
|
|
|
async def init(self):
|
|
self.log.info("Init TxRing %d (interface %d)", self.index, self.interface.index)
|
|
|
|
self.tx_info = [None]*self.size
|
|
|
|
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
|
|
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_ACTIVE_LOG_SIZE_REG, 0) # active, log size
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_BASE_ADDR_REG, self.buf_dma & 0xffffffff) # base address
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_BASE_ADDR_REG+4, self.buf_dma >> 32) # base address
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CPL_QUEUE_INDEX_REG, 0) # completion queue index
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_HEAD_PTR_REG, self.head_ptr & self.hw_ptr_mask) # head pointer
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_TAIL_PTR_REG, self.tail_ptr & self.hw_ptr_mask) # tail pointer
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_ACTIVE_LOG_SIZE_REG, self.log_queue_size | (self.log_desc_block_size << 8)) # active, log desc block size, log queue size
|
|
|
|
async def activate(self, cpl_index):
|
|
self.log.info("Activate TxRing %d (interface %d)", self.index, self.interface.index)
|
|
|
|
self.cpl_index = cpl_index
|
|
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_ACTIVE_LOG_SIZE_REG, 0) # active, log size
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CPL_QUEUE_INDEX_REG, cpl_index) # completion queue index
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_HEAD_PTR_REG, self.head_ptr & self.hw_ptr_mask) # head pointer
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_TAIL_PTR_REG, self.tail_ptr & self.hw_ptr_mask) # tail pointer
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_ACTIVE_LOG_SIZE_REG, self.log_queue_size | (self.log_desc_block_size << 8) | MQNIC_QUEUE_ACTIVE_MASK) # active, log desc block size, log queue size
|
|
|
|
async def deactivate(self):
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_ACTIVE_LOG_SIZE_REG, self.log_queue_size | (self.log_desc_block_size << 8)) # active, log desc block size, log queue size
|
|
|
|
def empty(self):
|
|
return self.head_ptr == self.clean_tail_ptr
|
|
|
|
def full(self):
|
|
return self.head_ptr - self.clean_tail_ptr >= self.full_size
|
|
|
|
async def read_tail_ptr(self):
|
|
val = await self.hw_regs.read_dword(MQNIC_QUEUE_TAIL_PTR_REG)
|
|
self.tail_ptr += (val - self.tail_ptr) & self.hw_ptr_mask
|
|
|
|
async def write_head_ptr(self):
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_HEAD_PTR_REG, self.head_ptr & self.hw_ptr_mask)
|
|
|
|
def free_desc(self, index):
|
|
pkt = self.tx_info[index]
|
|
self.driver.free_pkt(pkt)
|
|
self.tx_info[index] = None
|
|
|
|
def free_buf(self):
|
|
while not self.empty():
|
|
index = self.clean_tail_ptr & self.size_mask
|
|
self.free_desc(index)
|
|
self.clean_tail_ptr += 1
|
|
|
|
|
|
class RxRing:
|
|
def __init__(self, interface, size, stride, index, hw_regs):
|
|
self.interface = interface
|
|
self.log = interface.log
|
|
self.driver = interface.driver
|
|
self.log_queue_size = size.bit_length() - 1
|
|
self.log_desc_block_size = int(stride/MQNIC_DESC_SIZE).bit_length() - 1
|
|
self.desc_block_size = 2**self.log_desc_block_size
|
|
self.size = 2**self.log_queue_size
|
|
self.size_mask = self.size-1
|
|
self.full_size = self.size >> 1
|
|
self.stride = stride
|
|
self.index = index
|
|
self.cpl_index = 0
|
|
|
|
self.head_ptr = 0
|
|
self.tail_ptr = 0
|
|
self.clean_tail_ptr = 0
|
|
|
|
self.packets = 0
|
|
self.bytes = 0
|
|
|
|
self.hw_ptr_mask = 0xffff
|
|
self.hw_regs = hw_regs
|
|
|
|
async def init(self):
|
|
self.log.info("Init RxRing %d (interface %d)", self.index, self.interface.index)
|
|
|
|
self.rx_info = [None]*self.size
|
|
|
|
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
|
|
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_ACTIVE_LOG_SIZE_REG, 0) # active, log size
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_BASE_ADDR_REG, self.buf_dma & 0xffffffff) # base address
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_BASE_ADDR_REG+4, self.buf_dma >> 32) # base address
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CPL_QUEUE_INDEX_REG, 0) # completion queue index
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_HEAD_PTR_REG, self.head_ptr & self.hw_ptr_mask) # head pointer
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_TAIL_PTR_REG, self.tail_ptr & self.hw_ptr_mask) # tail pointer
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_ACTIVE_LOG_SIZE_REG, self.log_queue_size | (self.log_desc_block_size << 8)) # active, log desc block size, log queue size
|
|
|
|
async def activate(self, cpl_index):
|
|
self.log.info("Activate RxRing %d (interface %d)", self.index, self.interface.index)
|
|
|
|
self.cpl_index = cpl_index
|
|
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_ACTIVE_LOG_SIZE_REG, 0) # active, log size
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CPL_QUEUE_INDEX_REG, cpl_index) # completion queue index
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_HEAD_PTR_REG, self.head_ptr & self.hw_ptr_mask) # head pointer
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_TAIL_PTR_REG, self.tail_ptr & self.hw_ptr_mask) # tail pointer
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_ACTIVE_LOG_SIZE_REG, self.log_queue_size | (self.log_desc_block_size << 8) | MQNIC_QUEUE_ACTIVE_MASK) # active, log desc block size, log queue size
|
|
|
|
await self.refill_buffers()
|
|
|
|
async def deactivate(self):
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_ACTIVE_LOG_SIZE_REG, self.log_queue_size | (self.log_desc_block_size << 8)) # active, log desc block size, log queue size
|
|
|
|
def empty(self):
|
|
return self.head_ptr == self.clean_tail_ptr
|
|
|
|
def full(self):
|
|
return self.head_ptr - self.clean_tail_ptr >= self.full_size
|
|
|
|
async def read_tail_ptr(self):
|
|
val = await self.hw_regs.read_dword(MQNIC_QUEUE_TAIL_PTR_REG)
|
|
self.tail_ptr += (val - self.tail_ptr) & self.hw_ptr_mask
|
|
|
|
async def write_head_ptr(self):
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_HEAD_PTR_REG, self.head_ptr & self.hw_ptr_mask)
|
|
|
|
def free_desc(self, index):
|
|
pkt = self.rx_info[index]
|
|
self.driver.free_pkt(pkt)
|
|
self.rx_info[index] = None
|
|
|
|
def free_buf(self):
|
|
while not self.empty():
|
|
index = self.clean_tail_ptr & self.size_mask
|
|
self.free_desc(index)
|
|
self.clean_tail_ptr += 1
|
|
|
|
def prepare_desc(self, index):
|
|
pkt = self.driver.alloc_pkt()
|
|
self.rx_info[index] = pkt
|
|
|
|
length = pkt.size
|
|
ptr = pkt.get_absolute_address(0)
|
|
offset = 0
|
|
|
|
# write descriptors
|
|
for k in range(0, self.desc_block_size):
|
|
seg = min(length-offset, 4096) if k < self.desc_block_size-1 else length-offset
|
|
struct.pack_into("<LLQ", self.buf, index*self.stride+k*MQNIC_DESC_SIZE, 0, seg, ptr+offset if seg else 0)
|
|
offset += seg
|
|
|
|
async def refill_buffers(self):
|
|
missing = self.size - (self.head_ptr - self.clean_tail_ptr)
|
|
|
|
if missing < 8:
|
|
return
|
|
|
|
for k in range(missing):
|
|
self.prepare_desc(self.head_ptr & self.size_mask)
|
|
self.head_ptr += 1
|
|
|
|
await self.write_head_ptr()
|
|
|
|
|
|
class BaseScheduler:
|
|
def __init__(self, port, index, rb):
|
|
self.port = port
|
|
self.log = port.log
|
|
self.interface = port.interface
|
|
self.driver = port.interface.driver
|
|
self.index = index
|
|
self.rb = rb
|
|
self.hw_regs = None
|
|
|
|
async def init(self):
|
|
pass
|
|
|
|
|
|
class SchedulerRoundRobin(BaseScheduler):
|
|
def __init__(self, port, index, rb):
|
|
super().__init__(port, index, rb)
|
|
|
|
async def init(self):
|
|
offset = await self.rb.read_dword(MQNIC_RB_SCHED_RR_REG_OFFSET)
|
|
self.hw_regs = self.rb.parent.create_window(offset)
|
|
|
|
|
|
class SchedulerControlTdma(BaseScheduler):
|
|
def __init__(self, port, index, rb):
|
|
super().__init__(port, index, rb)
|
|
|
|
async def init(self):
|
|
offset = await self.rb.read_dword(MQNIC_RB_SCHED_CTRL_TDMA_REG_OFFSET)
|
|
self.hw_regs = self.rb.parent.create_window(offset)
|
|
|
|
|
|
class SchedulerBlock:
|
|
def __init__(self, interface, index, rb):
|
|
self.interface = interface
|
|
self.log = interface.log
|
|
self.driver = interface.driver
|
|
self.index = index
|
|
|
|
self.block_rb = rb
|
|
self.reg_blocks = RegBlockList()
|
|
|
|
self.sched_count = None
|
|
|
|
self.schedulers = []
|
|
|
|
async def init(self):
|
|
# Read ID registers
|
|
|
|
offset = await self.block_rb.read_dword(MQNIC_RB_SCHED_BLOCK_REG_OFFSET)
|
|
await self.reg_blocks.enumerate_reg_blocks(self.block_rb.parent, offset)
|
|
|
|
self.schedulers = []
|
|
|
|
self.sched_count = 0
|
|
for rb in self.reg_blocks:
|
|
if rb.type == MQNIC_RB_SCHED_RR_TYPE and rb.version == MQNIC_RB_SCHED_RR_VER:
|
|
s = SchedulerRoundRobin(self, self.sched_count, rb)
|
|
await s.init()
|
|
self.schedulers.append(s)
|
|
|
|
self.sched_count += 1
|
|
elif rb.type == MQNIC_RB_SCHED_CTRL_TDMA_TYPE and rb.version == MQNIC_RB_SCHED_CTRL_TDMA_VER:
|
|
s = SchedulerControlTdma(self, self.sched_count, rb)
|
|
await s.init()
|
|
self.schedulers.append(s)
|
|
|
|
self.sched_count += 1
|
|
|
|
self.log.info("Scheduler count: %d", self.sched_count)
|
|
|
|
|
|
class Port:
|
|
def __init__(self, interface, index, rb):
|
|
self.interface = interface
|
|
self.log = interface.log
|
|
self.driver = interface.driver
|
|
self.index = index
|
|
|
|
self.port_rb = rb
|
|
self.reg_blocks = RegBlockList()
|
|
self.port_ctrl_rb = None
|
|
|
|
self.port_features = None
|
|
|
|
async def init(self):
|
|
# Read ID registers
|
|
|
|
offset = await self.port_rb.read_dword(MQNIC_RB_PORT_REG_OFFSET)
|
|
await self.reg_blocks.enumerate_reg_blocks(self.port_rb.parent, offset)
|
|
|
|
self.port_ctrl_rb = self.reg_blocks.find(MQNIC_RB_PORT_CTRL_TYPE, MQNIC_RB_PORT_CTRL_VER)
|
|
|
|
self.port_features = await self.port_ctrl_rb.read_dword(MQNIC_RB_PORT_CTRL_REG_FEATURES)
|
|
|
|
self.log.info("Port features: 0x%08x", self.port_features)
|
|
|
|
async def get_tx_status(self, port):
|
|
return await self.port_ctrl_rb.read_dword(MQNIC_RB_PORT_CTRL_REG_TX_STATUS)
|
|
|
|
async def get_rx_status(self, port):
|
|
return await self.port_ctrl_rb.read_dword(MQNIC_RB_PORT_CTRL_REG_RX_STATUS)
|
|
|
|
|
|
class Interface:
|
|
def __init__(self, driver, index, hw_regs):
|
|
self.driver = driver
|
|
self.log = driver.log
|
|
self.index = index
|
|
self.hw_regs = hw_regs
|
|
self.csr_hw_regs = hw_regs.create_window(driver.if_csr_offset)
|
|
self.port_up = False
|
|
|
|
self.reg_blocks = RegBlockList()
|
|
self.if_ctrl_rb = None
|
|
self.event_queue_rb = None
|
|
self.tx_queue_rb = None
|
|
self.tx_cpl_queue_rb = None
|
|
self.rx_queue_rb = None
|
|
self.rx_cpl_queue_rb = None
|
|
self.rx_queue_map_rb = None
|
|
|
|
self.if_features = None
|
|
|
|
self.max_tx_mtu = 0
|
|
self.max_rx_mtu = 0
|
|
|
|
self.event_queue_offset = None
|
|
self.event_queue_count = None
|
|
self.event_queue_stride = None
|
|
self.tx_queue_offset = None
|
|
self.tx_queue_count = None
|
|
self.tx_queue_stride = None
|
|
self.tx_cpl_queue_offset = None
|
|
self.tx_cpl_queue_count = None
|
|
self.tx_cpl_queue_stride = None
|
|
self.rx_queue_offset = None
|
|
self.rx_queue_count = None
|
|
self.rx_queue_stride = None
|
|
self.rx_cpl_queue_offset = None
|
|
self.rx_cpl_queue_count = None
|
|
self.rx_cpl_queue_stride = None
|
|
|
|
self.port_count = None
|
|
self.sched_block_count = None
|
|
|
|
self.event_queues = []
|
|
|
|
self.tx_queues = []
|
|
self.tx_cpl_queues = []
|
|
self.rx_queues = []
|
|
self.rx_cpl_queues = []
|
|
self.ports = []
|
|
self.sched_blocks = []
|
|
|
|
self.interrupt_running = False
|
|
self.interrupt_pending = 0
|
|
|
|
self.pkt_rx_queue = deque()
|
|
self.pkt_rx_sync = Event()
|
|
|
|
async def init(self):
|
|
# Read ID registers
|
|
|
|
# Enumerate registers
|
|
await self.reg_blocks.enumerate_reg_blocks(self.hw_regs, self.driver.if_csr_offset)
|
|
|
|
self.if_ctrl_rb = self.reg_blocks.find(MQNIC_RB_IF_CTRL_TYPE, MQNIC_RB_IF_CTRL_VER)
|
|
|
|
self.if_features = await self.if_ctrl_rb.read_dword(MQNIC_RB_IF_CTRL_REG_FEATURES)
|
|
self.port_count = await self.if_ctrl_rb.read_dword(MQNIC_RB_IF_CTRL_REG_PORT_COUNT)
|
|
self.sched_block_count = await self.if_ctrl_rb.read_dword(MQNIC_RB_IF_CTRL_REG_SCHED_COUNT)
|
|
self.max_tx_mtu = await self.if_ctrl_rb.read_dword(MQNIC_RB_IF_CTRL_REG_MAX_TX_MTU)
|
|
self.max_rx_mtu = await self.if_ctrl_rb.read_dword(MQNIC_RB_IF_CTRL_REG_MAX_RX_MTU)
|
|
|
|
self.log.info("IF features: 0x%08x", self.if_features)
|
|
self.log.info("Port count: %d", self.port_count)
|
|
self.log.info("Scheduler block count: %d", self.sched_block_count)
|
|
self.log.info("Max TX MTU: %d", self.max_tx_mtu)
|
|
self.log.info("Max RX MTU: %d", self.max_rx_mtu)
|
|
|
|
await self.set_mtu(min(self.max_tx_mtu, self.max_rx_mtu, 9214))
|
|
|
|
self.event_queue_rb = self.reg_blocks.find(MQNIC_RB_EVENT_QM_TYPE, MQNIC_RB_EVENT_QM_VER)
|
|
|
|
self.event_queue_offset = await self.event_queue_rb.read_dword(MQNIC_RB_EVENT_QM_REG_OFFSET)
|
|
self.event_queue_count = await self.event_queue_rb.read_dword(MQNIC_RB_EVENT_QM_REG_COUNT)
|
|
self.event_queue_stride = await self.event_queue_rb.read_dword(MQNIC_RB_EVENT_QM_REG_STRIDE)
|
|
|
|
self.log.info("Event queue offset: 0x%08x", self.event_queue_offset)
|
|
self.log.info("Event queue count: %d", self.event_queue_count)
|
|
self.log.info("Event queue stride: 0x%08x", self.event_queue_stride)
|
|
|
|
self.event_queue_count = min(self.event_queue_count, MQNIC_MAX_EVENT_RINGS)
|
|
|
|
self.tx_queue_rb = self.reg_blocks.find(MQNIC_RB_TX_QM_TYPE, MQNIC_RB_TX_QM_VER)
|
|
|
|
self.tx_queue_offset = await self.tx_queue_rb.read_dword(MQNIC_RB_TX_QM_REG_OFFSET)
|
|
self.tx_queue_count = await self.tx_queue_rb.read_dword(MQNIC_RB_TX_QM_REG_COUNT)
|
|
self.tx_queue_stride = await self.tx_queue_rb.read_dword(MQNIC_RB_TX_QM_REG_STRIDE)
|
|
|
|
self.log.info("TX queue offset: 0x%08x", self.tx_queue_offset)
|
|
self.log.info("TX queue count: %d", self.tx_queue_count)
|
|
self.log.info("TX queue stride: 0x%08x", self.tx_queue_stride)
|
|
|
|
self.tx_queue_count = min(self.tx_queue_count, MQNIC_MAX_TX_RINGS)
|
|
|
|
self.tx_cpl_queue_rb = self.reg_blocks.find(MQNIC_RB_TX_CQM_TYPE, MQNIC_RB_TX_CQM_VER)
|
|
|
|
self.tx_cpl_queue_offset = await self.tx_cpl_queue_rb.read_dword(MQNIC_RB_TX_CQM_REG_OFFSET)
|
|
self.tx_cpl_queue_count = await self.tx_cpl_queue_rb.read_dword(MQNIC_RB_TX_CQM_REG_COUNT)
|
|
self.tx_cpl_queue_stride = await self.tx_cpl_queue_rb.read_dword(MQNIC_RB_TX_CQM_REG_STRIDE)
|
|
|
|
self.log.info("TX completion queue offset: 0x%08x", self.tx_cpl_queue_offset)
|
|
self.log.info("TX completion queue count: %d", self.tx_cpl_queue_count)
|
|
self.log.info("TX completion queue stride: 0x%08x", self.tx_cpl_queue_stride)
|
|
|
|
self.tx_cpl_queue_count = min(self.tx_cpl_queue_count, MQNIC_MAX_TX_CPL_RINGS)
|
|
|
|
self.rx_queue_rb = self.reg_blocks.find(MQNIC_RB_RX_QM_TYPE, MQNIC_RB_RX_QM_VER)
|
|
|
|
self.rx_queue_offset = await self.rx_queue_rb.read_dword(MQNIC_RB_RX_QM_REG_OFFSET)
|
|
self.rx_queue_count = await self.rx_queue_rb.read_dword(MQNIC_RB_RX_QM_REG_COUNT)
|
|
self.rx_queue_stride = await self.rx_queue_rb.read_dword(MQNIC_RB_RX_QM_REG_STRIDE)
|
|
|
|
self.log.info("RX queue offset: 0x%08x", self.rx_queue_offset)
|
|
self.log.info("RX queue count: %d", self.rx_queue_count)
|
|
self.log.info("RX queue stride: 0x%08x", self.rx_queue_stride)
|
|
|
|
self.rx_queue_count = min(self.rx_queue_count, MQNIC_MAX_RX_RINGS)
|
|
|
|
self.rx_cpl_queue_rb = self.reg_blocks.find(MQNIC_RB_RX_CQM_TYPE, MQNIC_RB_RX_CQM_VER)
|
|
|
|
self.rx_cpl_queue_offset = await self.rx_cpl_queue_rb.read_dword(MQNIC_RB_RX_CQM_REG_OFFSET)
|
|
self.rx_cpl_queue_count = await self.rx_cpl_queue_rb.read_dword(MQNIC_RB_RX_CQM_REG_COUNT)
|
|
self.rx_cpl_queue_stride = await self.rx_cpl_queue_rb.read_dword(MQNIC_RB_RX_CQM_REG_STRIDE)
|
|
|
|
self.log.info("RX completion queue offset: 0x%08x", self.rx_cpl_queue_offset)
|
|
self.log.info("RX completion queue count: %d", self.rx_cpl_queue_count)
|
|
self.log.info("RX completion queue stride: 0x%08x", self.rx_cpl_queue_stride)
|
|
|
|
self.rx_cpl_queue_count = min(self.rx_cpl_queue_count, MQNIC_MAX_RX_CPL_RINGS)
|
|
|
|
self.rx_queue_map_rb = self.reg_blocks.find(MQNIC_RB_RX_QUEUE_MAP_TYPE, MQNIC_RB_RX_QUEUE_MAP_VER)
|
|
|
|
for k in range(self.port_count):
|
|
await self.set_rx_queue_map_offset(k, 0)
|
|
await self.set_rx_queue_map_rss_mask(k, 0)
|
|
await self.set_rx_queue_map_app_mask(k, 0)
|
|
|
|
self.event_queues = []
|
|
|
|
self.tx_queues = []
|
|
self.tx_cpl_queues = []
|
|
self.rx_queues = []
|
|
self.rx_cpl_queues = []
|
|
self.ports = []
|
|
self.sched_blocks = []
|
|
|
|
for k in range(self.event_queue_count):
|
|
q = EqRing(self, 1024, MQNIC_EVENT_SIZE, self.index,
|
|
self.hw_regs.create_window(self.event_queue_offset + k*self.event_queue_stride, self.event_queue_stride))
|
|
await q.init()
|
|
self.event_queues.append(q)
|
|
|
|
for k in range(self.tx_queue_count):
|
|
q = TxRing(self, 1024, MQNIC_DESC_SIZE*4, k,
|
|
self.hw_regs.create_window(self.tx_queue_offset + k*self.tx_queue_stride, self.tx_queue_stride))
|
|
await q.init()
|
|
self.tx_queues.append(q)
|
|
|
|
for k in range(self.tx_cpl_queue_count):
|
|
q = CqRing(self, 1024, MQNIC_CPL_SIZE, k,
|
|
self.hw_regs.create_window(self.tx_cpl_queue_offset + k*self.tx_cpl_queue_stride, self.tx_cpl_queue_stride))
|
|
await q.init()
|
|
self.tx_cpl_queues.append(q)
|
|
|
|
for k in range(self.rx_queue_count):
|
|
q = RxRing(self, 1024, MQNIC_DESC_SIZE*4, k,
|
|
self.hw_regs.create_window(self.rx_queue_offset + k*self.rx_queue_stride, self.rx_queue_stride))
|
|
await q.init()
|
|
self.rx_queues.append(q)
|
|
|
|
for k in range(self.rx_cpl_queue_count):
|
|
q = CqRing(self, 1024, MQNIC_CPL_SIZE, k,
|
|
self.hw_regs.create_window(self.rx_cpl_queue_offset + k*self.rx_cpl_queue_stride, self.rx_cpl_queue_stride))
|
|
await q.init()
|
|
self.rx_cpl_queues.append(q)
|
|
|
|
for k in range(self.port_count):
|
|
rb = self.reg_blocks.find(MQNIC_RB_PORT_TYPE, MQNIC_RB_PORT_VER, index=k)
|
|
|
|
p = Port(self, k, rb)
|
|
await p.init()
|
|
self.ports.append(p)
|
|
|
|
for k in range(self.sched_block_count):
|
|
rb = self.reg_blocks.find(MQNIC_RB_SCHED_BLOCK_TYPE, MQNIC_RB_SCHED_BLOCK_VER, index=k)
|
|
|
|
s = SchedulerBlock(self, k, rb)
|
|
await s.init()
|
|
self.sched_blocks.append(s)
|
|
|
|
assert self.sched_block_count == len(self.sched_blocks)
|
|
|
|
# wait for all writes to complete
|
|
await self.hw_regs.read_dword(0)
|
|
|
|
async def open(self):
|
|
for q in self.event_queues:
|
|
await q.activate(self.index) # TODO?
|
|
q.handler = None # TODO
|
|
await q.arm()
|
|
|
|
for q in self.rx_cpl_queues:
|
|
await q.activate(q.index % self.event_queue_count)
|
|
q.ring_index = q.index
|
|
q.handler = None # TODO
|
|
await q.arm()
|
|
|
|
for q in self.rx_queues:
|
|
await q.activate(q.index)
|
|
|
|
for q in self.tx_cpl_queues:
|
|
await q.activate(q.index % self.event_queue_count)
|
|
q.ring_index = q.index
|
|
q.handler = None # TODO
|
|
await q.arm()
|
|
|
|
for q in self.tx_queues:
|
|
await q.activate(q.index)
|
|
|
|
# wait for all writes to complete
|
|
await self.hw_regs.read_dword(0)
|
|
|
|
self.port_up = True
|
|
|
|
async def close(self):
|
|
self.port_up = False
|
|
|
|
for q in self.tx_queues:
|
|
await q.deactivate()
|
|
|
|
for q in self.tx_cpl_queues:
|
|
await q.deactivate()
|
|
|
|
for q in self.rx_queues:
|
|
await q.deactivate()
|
|
|
|
for q in self.rx_cpl_queues:
|
|
await q.deactivate()
|
|
|
|
for q in self.event_queues:
|
|
await q.deactivate()
|
|
|
|
# wait for all writes to complete
|
|
await self.hw_regs.read_dword(0)
|
|
|
|
for q in self.tx_queues:
|
|
await q.free_buf()
|
|
|
|
for q in self.rx_queues:
|
|
await q.free_buf()
|
|
|
|
async def interrupt(self):
|
|
self.log.info("Interface interrupt (interface %d)", self.index)
|
|
if self.interrupt_running:
|
|
self.interrupt_pending += 1
|
|
self.log.info("************************ interrupt was running")
|
|
return
|
|
self.interrupt_running = True
|
|
for eq in self.event_queues:
|
|
await eq.process()
|
|
await eq.arm()
|
|
self.interrupt_running = False
|
|
self.log.info("Interface interrupt done (interface %d)", self.index)
|
|
|
|
while self.interrupt_pending:
|
|
self.interrupt_pending -= 1
|
|
await self.interrupt()
|
|
|
|
async def process_tx_cq(self, cq_ring):
|
|
self.log.info("Process TX CQ %d (interface %d)", cq_ring.ring_index, self.index)
|
|
|
|
ring = self.tx_queues[cq_ring.ring_index]
|
|
|
|
if not self.port_up:
|
|
return
|
|
|
|
# process completion queue
|
|
await cq_ring.read_head_ptr()
|
|
|
|
cq_tail_ptr = cq_ring.tail_ptr
|
|
cq_index = cq_tail_ptr & cq_ring.size_mask
|
|
|
|
while (cq_ring.head_ptr != cq_tail_ptr):
|
|
cpl_data = struct.unpack_from("<HHHxxQ", cq_ring.buf, cq_index*cq_ring.stride)
|
|
ring_index = cpl_data[1] & ring.size_mask
|
|
|
|
self.log.info("CPL data: %s", cpl_data)
|
|
|
|
self.log.info("Ring index: %d", ring_index)
|
|
|
|
ring.free_desc(ring_index)
|
|
|
|
cq_tail_ptr += 1
|
|
cq_index = cq_tail_ptr & cq_ring.size_mask
|
|
|
|
cq_ring.tail_ptr = cq_tail_ptr
|
|
await cq_ring.write_tail_ptr()
|
|
|
|
# process ring
|
|
await ring.read_tail_ptr()
|
|
|
|
ring_clean_tail_ptr = ring.clean_tail_ptr
|
|
ring_index = ring_clean_tail_ptr & ring.size_mask
|
|
|
|
while (ring_clean_tail_ptr != ring.tail_ptr):
|
|
if ring.tx_info[ring_index]:
|
|
break
|
|
|
|
ring_clean_tail_ptr += 1
|
|
ring_index = ring_clean_tail_ptr & ring.size_mask
|
|
|
|
ring.clean_tail_ptr = ring_clean_tail_ptr
|
|
|
|
ring.clean_event.set()
|
|
|
|
async def process_rx_cq(self, cq_ring):
|
|
self.log.info("Process RX CQ %d (interface %d)", cq_ring.ring_index, self.index)
|
|
|
|
ring = self.rx_queues[cq_ring.ring_index]
|
|
|
|
if not self.port_up:
|
|
return
|
|
|
|
# process completion queue
|
|
await cq_ring.read_head_ptr()
|
|
|
|
cq_tail_ptr = cq_ring.tail_ptr
|
|
cq_index = cq_tail_ptr & cq_ring.size_mask
|
|
|
|
while (cq_ring.head_ptr != cq_tail_ptr):
|
|
cpl_data = struct.unpack_from("<HHHxxLHH", cq_ring.buf, cq_index*cq_ring.stride)
|
|
ring_index = cpl_data[1] & ring.size_mask
|
|
|
|
self.log.info("CPL data: %s", cpl_data)
|
|
|
|
self.log.info("Ring index: %d", ring_index)
|
|
pkt = ring.rx_info[ring_index]
|
|
|
|
length = cpl_data[2]
|
|
|
|
skb = Packet()
|
|
skb.data = pkt[:length]
|
|
skb.queue = ring.index
|
|
skb.timestamp_ns = cpl_data[3]
|
|
skb.timestamp_s = cpl_data[4]
|
|
skb.rx_checksum = cpl_data[5]
|
|
|
|
self.log.info("Packet: %s", skb)
|
|
|
|
self.pkt_rx_queue.append(skb)
|
|
self.pkt_rx_sync.set()
|
|
|
|
ring.free_desc(ring_index)
|
|
|
|
cq_tail_ptr += 1
|
|
cq_index = cq_tail_ptr & cq_ring.size_mask
|
|
|
|
cq_ring.tail_ptr = cq_tail_ptr
|
|
await cq_ring.write_tail_ptr()
|
|
|
|
# process ring
|
|
await ring.read_tail_ptr()
|
|
|
|
ring_clean_tail_ptr = ring.clean_tail_ptr
|
|
ring_index = ring_clean_tail_ptr & ring.size_mask
|
|
|
|
while (ring_clean_tail_ptr != ring.tail_ptr):
|
|
if ring.rx_info[ring_index]:
|
|
break
|
|
|
|
ring_clean_tail_ptr += 1
|
|
ring_index = ring_clean_tail_ptr & ring.size_mask
|
|
|
|
ring.clean_tail_ptr = ring_clean_tail_ptr
|
|
|
|
# replenish buffers
|
|
await ring.refill_buffers()
|
|
|
|
async def start_xmit(self, skb, tx_ring=None, csum_start=None, csum_offset=None):
|
|
if not self.port_up:
|
|
return
|
|
|
|
data = bytes(skb)
|
|
|
|
assert len(data) < self.max_tx_mtu
|
|
|
|
if tx_ring is not None:
|
|
ring_index = tx_ring
|
|
else:
|
|
ring_index = 0
|
|
|
|
ring = self.tx_queues[ring_index]
|
|
|
|
while True:
|
|
# check for space in ring
|
|
if ring.head_ptr - ring.clean_tail_ptr < ring.full_size:
|
|
break
|
|
|
|
# wait for space
|
|
ring.clean_event.clear()
|
|
await ring.clean_event.wait()
|
|
|
|
index = ring.head_ptr & ring.size_mask
|
|
|
|
ring.packets += 1
|
|
ring.bytes += len(data)
|
|
|
|
pkt = self.driver.alloc_pkt()
|
|
|
|
assert not ring.tx_info[index]
|
|
ring.tx_info[index] = pkt
|
|
|
|
# put data in packet buffer
|
|
pkt[10:len(data)+10] = data
|
|
|
|
csum_cmd = 0
|
|
|
|
if csum_start is not None and csum_offset is not None:
|
|
csum_cmd = 0x8000 | (csum_offset << 8) | csum_start
|
|
|
|
length = len(data)
|
|
ptr = pkt.get_absolute_address(0)+10
|
|
offset = 0
|
|
|
|
# write descriptors
|
|
seg = min(length-offset, 42) if ring.desc_block_size > 1 else length-offset
|
|
struct.pack_into("<HHLQ", ring.buf, index*ring.stride, 0, csum_cmd, seg, ptr+offset if seg else 0)
|
|
offset += seg
|
|
for k in range(1, ring.desc_block_size):
|
|
seg = min(length-offset, 4096) if k < ring.desc_block_size-1 else length-offset
|
|
struct.pack_into("<4xLQ", ring.buf, index*ring.stride+k*MQNIC_DESC_SIZE, seg, ptr+offset if seg else 0)
|
|
offset += seg
|
|
|
|
ring.head_ptr += 1
|
|
|
|
await ring.write_head_ptr()
|
|
|
|
async def set_mtu(self, mtu):
|
|
await self.if_ctrl_rb.write_dword(MQNIC_RB_IF_CTRL_REG_TX_MTU, mtu)
|
|
await self.if_ctrl_rb.write_dword(MQNIC_RB_IF_CTRL_REG_RX_MTU, mtu)
|
|
|
|
async def get_rx_queue_map_offset(self, port):
|
|
return await self.rx_queue_map_rb.read_dword(MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET +
|
|
MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE*port + MQNIC_RB_RX_QUEUE_MAP_CH_REG_OFFSET)
|
|
|
|
async def set_rx_queue_map_offset(self, port, val):
|
|
await self.rx_queue_map_rb.write_dword(MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET +
|
|
MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE*port + MQNIC_RB_RX_QUEUE_MAP_CH_REG_OFFSET, val)
|
|
|
|
async def get_rx_queue_map_rss_mask(self, port):
|
|
return await self.rx_queue_map_rb.read_dword(MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET +
|
|
MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE*port + MQNIC_RB_RX_QUEUE_MAP_CH_REG_RSS_MASK)
|
|
|
|
async def set_rx_queue_map_rss_mask(self, port, val):
|
|
await self.rx_queue_map_rb.write_dword(MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET +
|
|
MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE*port + MQNIC_RB_RX_QUEUE_MAP_CH_REG_RSS_MASK, val)
|
|
|
|
async def get_rx_queue_map_app_mask(self, port):
|
|
return await self.rx_queue_map_rb.read_dword(MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET +
|
|
MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE*port + MQNIC_RB_RX_QUEUE_MAP_CH_REG_APP_MASK)
|
|
|
|
async def set_rx_queue_map_app_mask(self, port, val):
|
|
await self.rx_queue_map_rb.write_dword(MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET +
|
|
MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE*port + MQNIC_RB_RX_QUEUE_MAP_CH_REG_APP_MASK, val)
|
|
|
|
async def recv(self):
|
|
if not self.pkt_rx_queue:
|
|
self.pkt_rx_sync.clear()
|
|
await self.pkt_rx_sync.wait()
|
|
return self.recv_nowait()
|
|
|
|
def recv_nowait(self):
|
|
if self.pkt_rx_queue:
|
|
return self.pkt_rx_queue.popleft()
|
|
return None
|
|
|
|
async def wait(self):
|
|
if not self.pkt_rx_queue:
|
|
self.pkt_rx_sync.clear()
|
|
await self.pkt_rx_sync.wait()
|
|
|
|
|
|
class Interrupt:
|
|
def __init__(self, index, handler=None):
|
|
self.index = index
|
|
self.queue = Queue()
|
|
self.handler = handler
|
|
|
|
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)
|
|
self.interrupt()
|
|
|
|
|
|
class Driver:
|
|
def __init__(self):
|
|
self.log = SimLog("cocotb.mqnic")
|
|
|
|
self.dev = None
|
|
self.pool = None
|
|
|
|
self.hw_regs = None
|
|
self.app_hw_regs = None
|
|
self.ram_hw_regs = None
|
|
|
|
self.irq_sig = None
|
|
self.irq_list = []
|
|
|
|
self.reg_blocks = RegBlockList()
|
|
self.fw_id_rb = None
|
|
self.if_rb = None
|
|
self.phc_rb = None
|
|
|
|
self.fpga_id = None
|
|
self.fw_id = None
|
|
self.fw_ver = None
|
|
self.board_id = None
|
|
self.board_ver = None
|
|
self.build_date = None
|
|
self.build_time = None
|
|
self.git_hash = None
|
|
self.rel_info = None
|
|
|
|
self.app_id = None
|
|
|
|
self.if_offset = None
|
|
self.if_count = None
|
|
self.if_stride = None
|
|
self.if_csr_offset = None
|
|
|
|
self.initialized = False
|
|
self.interrupt_running = False
|
|
|
|
self.if_count = 1
|
|
self.interfaces = []
|
|
|
|
self.pkt_buf_size = 16384
|
|
self.allocated_packets = []
|
|
self.free_packets = deque()
|
|
|
|
async def init_pcie_dev(self, dev):
|
|
assert not self.initialized
|
|
self.initialized = True
|
|
|
|
self.dev = dev
|
|
|
|
self.pool = self.dev.rc.mem_pool
|
|
|
|
await self.dev.enable_device()
|
|
await self.dev.set_master()
|
|
await self.dev.alloc_irq_vectors(1, MQNIC_MAX_EVENT_RINGS)
|
|
|
|
self.hw_regs = self.dev.bar_window[0]
|
|
self.app_hw_regs = self.dev.bar_window[2]
|
|
self.ram_hw_regs = self.dev.bar_window[4]
|
|
|
|
# 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()
|
|
|
|
async def init_axi_dev(self, pool, hw_regs, app_hw_regs=None, irq=None):
|
|
assert not self.initialized
|
|
self.initialized = True
|
|
|
|
self.pool = pool
|
|
|
|
self.hw_regs = hw_regs
|
|
self.app_hw_regs = app_hw_regs
|
|
|
|
# set up edge-triggered interrupts
|
|
if irq:
|
|
for index in range(len(irq)):
|
|
self.irq_list.append(Interrupt(index, self.interrupt_handler))
|
|
cocotb.start_soon(self._run_edge_interrupts(irq))
|
|
|
|
await self.init_common()
|
|
|
|
async def init_common(self):
|
|
self.log.info("Control BAR size: %d", self.hw_regs.size)
|
|
if self.app_hw_regs:
|
|
self.log.info("Application BAR size: %d", self.app_hw_regs.size)
|
|
if self.ram_hw_regs:
|
|
self.log.info("RAM BAR size: %d", self.ram_hw_regs.size)
|
|
|
|
# Enumerate registers
|
|
await self.reg_blocks.enumerate_reg_blocks(self.hw_regs)
|
|
|
|
# Read ID registers
|
|
self.fw_id_rb = self.reg_blocks.find(MQNIC_RB_FW_ID_TYPE, MQNIC_RB_FW_ID_VER)
|
|
|
|
self.fpga_id = await self.fw_id_rb.read_dword(MQNIC_RB_FW_ID_REG_FPGA_ID)
|
|
self.log.info("FPGA JTAG ID: 0x%08x", self.fpga_id)
|
|
self.fw_id = await self.fw_id_rb.read_dword(MQNIC_RB_FW_ID_REG_FW_ID)
|
|
self.log.info("FW ID: 0x%08x", self.fw_id)
|
|
self.fw_ver = await self.fw_id_rb.read_dword(MQNIC_RB_FW_ID_REG_FW_VER)
|
|
self.log.info("FW version: %d.%d.%d.%d", *self.fw_ver.to_bytes(4, 'big'))
|
|
self.board_id = await self.fw_id_rb.read_dword(MQNIC_RB_FW_ID_REG_BOARD_ID)
|
|
self.log.info("Board ID: 0x%08x", self.board_id)
|
|
self.board_ver = await self.fw_id_rb.read_dword(MQNIC_RB_FW_ID_REG_BOARD_VER)
|
|
self.log.info("Board version: %d.%d.%d.%d", *self.board_ver.to_bytes(4, 'big'))
|
|
self.build_date = await self.fw_id_rb.read_dword(MQNIC_RB_FW_ID_REG_BUILD_DATE)
|
|
self.log.info("Build date: %s UTC (raw: 0x%08x)", datetime.datetime.utcfromtimestamp(self.build_date).isoformat(' '), self.build_date)
|
|
self.git_hash = await self.fw_id_rb.read_dword(MQNIC_RB_FW_ID_REG_GIT_HASH)
|
|
self.log.info("Git hash: %08x", self.git_hash)
|
|
self.rel_info = await self.fw_id_rb.read_dword(MQNIC_RB_FW_ID_REG_REL_INFO)
|
|
self.log.info("Release info: %d", self.rel_info)
|
|
|
|
rb = self.reg_blocks.find(MQNIC_RB_APP_INFO_TYPE, MQNIC_RB_APP_INFO_VER)
|
|
|
|
if rb:
|
|
self.app_id = await rb.read_dword(MQNIC_RB_APP_INFO_REG_ID)
|
|
self.log.info("Application ID: 0x%08x", self.app_id)
|
|
|
|
self.phc_rb = self.reg_blocks.find(MQNIC_RB_PHC_TYPE, MQNIC_RB_PHC_VER)
|
|
|
|
# Enumerate interfaces
|
|
self.if_rb = self.reg_blocks.find(MQNIC_RB_IF_TYPE, MQNIC_RB_IF_VER)
|
|
self.interfaces = []
|
|
|
|
if self.if_rb:
|
|
self.if_offset = await self.if_rb.read_dword(MQNIC_RB_IF_REG_OFFSET)
|
|
self.log.info("IF offset: %d", self.if_offset)
|
|
self.if_count = await self.if_rb.read_dword(MQNIC_RB_IF_REG_COUNT)
|
|
self.log.info("IF count: %d", self.if_count)
|
|
self.if_stride = await self.if_rb.read_dword(MQNIC_RB_IF_REG_STRIDE)
|
|
self.log.info("IF stride: 0x%08x", self.if_stride)
|
|
self.if_csr_offset = await self.if_rb.read_dword(MQNIC_RB_IF_REG_CSR_OFFSET)
|
|
self.log.info("IF CSR offset: 0x%08x", self.if_csr_offset)
|
|
|
|
for k in range(self.if_count):
|
|
i = Interface(self, k, self.hw_regs.create_window(self.if_offset + k*self.if_stride, self.if_stride))
|
|
await i.init()
|
|
self.interfaces.append(i)
|
|
|
|
else:
|
|
self.log.warning("No interface block found")
|
|
|
|
async def _run_edge_interrupts(self, signal):
|
|
last_val = 0
|
|
count = len(signal)
|
|
while True:
|
|
await Edge(signal)
|
|
val = signal.value.integer
|
|
edge = val & ~last_val
|
|
for index in (x for x in range(count) if edge & (1 << x)):
|
|
await self.irq_list[index].interrupt()
|
|
|
|
async def interrupt_handler(self, index):
|
|
self.log.info("Interrupt handler start (IRQ %d)", index)
|
|
for i in self.interfaces:
|
|
for eq in i.event_queues:
|
|
if eq.interrupt_index == index:
|
|
await eq.process()
|
|
await eq.arm()
|
|
self.log.info("Interrupt handler end (IRQ %d)", index)
|
|
|
|
def alloc_pkt(self):
|
|
if self.free_packets:
|
|
return self.free_packets.popleft()
|
|
|
|
pkt = self.pool.alloc_region(self.pkt_buf_size)
|
|
self.allocated_packets.append(pkt)
|
|
return pkt
|
|
|
|
def free_pkt(self, pkt):
|
|
assert pkt is not None
|
|
assert pkt in self.allocated_packets
|
|
self.free_packets.append(pkt)
|