mirror of
https://github.com/corundum/corundum.git
synced 2025-01-30 08:32:52 +08:00
2a7d0e0947
Signed-off-by: Alex Forencich <alex@alexforencich.com>
1792 lines
59 KiB
Python
1792 lines
59 KiB
Python
# SPDX-License-Identifier: BSD-2-Clause-Views
|
|
# Copyright (c) 2019-2023 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_EQ = 1
|
|
MQNIC_MAX_TXQ = 32
|
|
MQNIC_MAX_RXQ = 8
|
|
MQNIC_MAX_CQ = MQNIC_MAX_TXQ*2
|
|
|
|
# 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 = 0x0000C006
|
|
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_IRQ_TYPE = 0x0000C007
|
|
MQNIC_RB_IRQ_VER = 0x00000100
|
|
|
|
MQNIC_RB_CLK_INFO_TYPE = 0x0000C008
|
|
MQNIC_RB_CLK_INFO_VER = 0x00000100
|
|
MQNIC_RB_CLK_INFO_COUNT = 0x0C
|
|
MQNIC_RB_CLK_INFO_REF_NOM_PER = 0x10
|
|
MQNIC_RB_CLK_INFO_CLK_NOM_PER = 0x18
|
|
MQNIC_RB_CLK_INFO_CLK_FREQ = 0x1C
|
|
MQNIC_RB_CLK_INFO_FREQ_BASE = 0x20
|
|
|
|
MQNIC_RB_PHC_TYPE = 0x0000C080
|
|
MQNIC_RB_PHC_VER = 0x00000200
|
|
MQNIC_RB_PHC_REG_CTRL = 0x0C
|
|
MQNIC_RB_PHC_REG_CUR_FNS = 0x10
|
|
MQNIC_RB_PHC_REG_CUR_TOD_NS = 0x14
|
|
MQNIC_RB_PHC_REG_CUR_TOD_SEC_L = 0x18
|
|
MQNIC_RB_PHC_REG_CUR_TOD_SEC_H = 0x1C
|
|
MQNIC_RB_PHC_REG_CUR_REL_NS_L = 0x20
|
|
MQNIC_RB_PHC_REG_CUR_REL_NS_H = 0x24
|
|
MQNIC_RB_PHC_REG_CUR_PTM_NS_L = 0x28
|
|
MQNIC_RB_PHC_REG_CUR_PTM_NS_H = 0x2C
|
|
MQNIC_RB_PHC_REG_SNAP_FNS = 0x30
|
|
MQNIC_RB_PHC_REG_SNAP_TOD_NS = 0x34
|
|
MQNIC_RB_PHC_REG_SNAP_TOD_SEC_L = 0x38
|
|
MQNIC_RB_PHC_REG_SNAP_TOD_SEC_H = 0x3C
|
|
MQNIC_RB_PHC_REG_SNAP_REL_NS_L = 0x40
|
|
MQNIC_RB_PHC_REG_SNAP_REL_NS_H = 0x44
|
|
MQNIC_RB_PHC_REG_SNAP_PTM_NS_L = 0x48
|
|
MQNIC_RB_PHC_REG_SNAP_PTM_NS_H = 0x4C
|
|
MQNIC_RB_PHC_REG_OFFSET_TOD_NS = 0x50
|
|
MQNIC_RB_PHC_REG_SET_TOD_NS = 0x54
|
|
MQNIC_RB_PHC_REG_SET_TOD_SEC_L = 0x58
|
|
MQNIC_RB_PHC_REG_SET_TOD_SEC_H = 0x5C
|
|
MQNIC_RB_PHC_REG_SET_REL_NS_L = 0x60
|
|
MQNIC_RB_PHC_REG_SET_REL_NS_H = 0x64
|
|
MQNIC_RB_PHC_REG_OFFSET_REL_NS = 0x68
|
|
MQNIC_RB_PHC_REG_OFFSET_FNS = 0x6C
|
|
MQNIC_RB_PHC_REG_NOM_PERIOD_FNS = 0x70
|
|
MQNIC_RB_PHC_REG_NOM_PERIOD_NS = 0x74
|
|
MQNIC_RB_PHC_REG_PERIOD_FNS = 0x78
|
|
MQNIC_RB_PHC_REG_PERIOD_NS = 0x7C
|
|
|
|
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_RB_IF_CTRL_REG_TX_FIFO_DEPTH = 0x20
|
|
MQNIC_RB_IF_CTRL_REG_RX_FIFO_DEPTH = 0x24
|
|
|
|
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_IF_FEATURE_LFC = (1 << 11)
|
|
MQNIC_IF_FEATURE_PFC = (1 << 12)
|
|
|
|
MQNIC_RB_RX_QUEUE_MAP_TYPE = 0x0000C090
|
|
MQNIC_RB_RX_QUEUE_MAP_VER = 0x00000200
|
|
MQNIC_RB_RX_QUEUE_MAP_REG_CFG = 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_EQM_TYPE = 0x0000C010
|
|
MQNIC_RB_EQM_VER = 0x00000400
|
|
MQNIC_RB_EQM_REG_OFFSET = 0x0C
|
|
MQNIC_RB_EQM_REG_COUNT = 0x10
|
|
MQNIC_RB_EQM_REG_STRIDE = 0x14
|
|
|
|
MQNIC_RB_CQM_TYPE = 0x0000C020
|
|
MQNIC_RB_CQM_VER = 0x00000400
|
|
MQNIC_RB_CQM_REG_OFFSET = 0x0C
|
|
MQNIC_RB_CQM_REG_COUNT = 0x10
|
|
MQNIC_RB_CQM_REG_STRIDE = 0x14
|
|
|
|
MQNIC_RB_TX_QM_TYPE = 0x0000C030
|
|
MQNIC_RB_TX_QM_VER = 0x00000400
|
|
MQNIC_RB_TX_QM_REG_OFFSET = 0x0C
|
|
MQNIC_RB_TX_QM_REG_COUNT = 0x10
|
|
MQNIC_RB_TX_QM_REG_STRIDE = 0x14
|
|
|
|
MQNIC_RB_RX_QM_TYPE = 0x0000C031
|
|
MQNIC_RB_RX_QM_VER = 0x00000400
|
|
MQNIC_RB_RX_QM_REG_OFFSET = 0x0C
|
|
MQNIC_RB_RX_QM_REG_COUNT = 0x10
|
|
MQNIC_RB_RX_QM_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 = 0x00000300
|
|
MQNIC_RB_PORT_CTRL_REG_FEATURES = 0x0C
|
|
MQNIC_RB_PORT_CTRL_REG_TX_CTRL = 0x10
|
|
MQNIC_RB_PORT_CTRL_REG_RX_CTRL = 0x14
|
|
MQNIC_RB_PORT_CTRL_REG_LFC_CTRL = 0x1C
|
|
MQNIC_RB_PORT_CTRL_REG_PFC_CTRL0 = 0x20
|
|
MQNIC_RB_PORT_CTRL_REG_PFC_CTRL1 = 0x24
|
|
MQNIC_RB_PORT_CTRL_REG_PFC_CTRL2 = 0x28
|
|
MQNIC_RB_PORT_CTRL_REG_PFC_CTRL3 = 0x2C
|
|
MQNIC_RB_PORT_CTRL_REG_PFC_CTRL4 = 0x30
|
|
MQNIC_RB_PORT_CTRL_REG_PFC_CTRL5 = 0x34
|
|
MQNIC_RB_PORT_CTRL_REG_PFC_CTRL6 = 0x38
|
|
MQNIC_RB_PORT_CTRL_REG_PFC_CTRL7 = 0x3C
|
|
|
|
MQNIC_PORT_FEATURE_LFC = (1 << 0)
|
|
MQNIC_PORT_FEATURE_PFC = (1 << 1)
|
|
MQNIC_PORT_FEATURE_INT_MAC_CTRL = (1 << 2)
|
|
|
|
MQNIC_PORT_TX_CTRL_EN = (1 << 0)
|
|
MQNIC_PORT_TX_CTRL_PAUSE = (1 << 8)
|
|
MQNIC_PORT_TX_CTRL_STATUS = (1 << 16)
|
|
MQNIC_PORT_TX_CTRL_RESET = (1 << 17)
|
|
MQNIC_PORT_TX_CTRL_PAUSE_REQ = (1 << 24)
|
|
MQNIC_PORT_TX_CTRL_PAUSE_ACK = (1 << 25)
|
|
|
|
MQNIC_PORT_RX_CTRL_EN = (1 << 0)
|
|
MQNIC_PORT_RX_CTRL_PAUSE = (1 << 8)
|
|
MQNIC_PORT_RX_CTRL_STATUS = (1 << 16)
|
|
MQNIC_PORT_RX_CTRL_RESET = (1 << 17)
|
|
MQNIC_PORT_RX_CTRL_PAUSE_REQ = (1 << 24)
|
|
MQNIC_PORT_RX_CTRL_PAUSE_ACK = (1 << 25)
|
|
|
|
MQNIC_PORT_LFC_CTRL_TX_LFC_EN = (1 << 24)
|
|
MQNIC_PORT_LFC_CTRL_RX_LFC_EN = (1 << 25)
|
|
MQNIC_PORT_LFC_CTRL_TX_LFC_REQ = (1 << 28)
|
|
MQNIC_PORT_LFC_CTRL_RX_LFC_REQ = (1 << 29)
|
|
|
|
MQNIC_PORT_PFC_CTRL_TX_PFC_EN = (1 << 24)
|
|
MQNIC_PORT_PFC_CTRL_RX_PFC_EN = (1 << 25)
|
|
MQNIC_PORT_PFC_CTRL_TX_PFC_REQ = (1 << 28)
|
|
MQNIC_PORT_PFC_CTRL_RX_PFC_REQ = (1 << 29)
|
|
|
|
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_VF_REG = 0x00
|
|
MQNIC_QUEUE_CTRL_STATUS_REG = 0x08
|
|
MQNIC_QUEUE_SIZE_CQN_REG = 0x0C
|
|
MQNIC_QUEUE_PTR_REG = 0x10
|
|
MQNIC_QUEUE_PROD_PTR_REG = 0x10
|
|
MQNIC_QUEUE_CONS_PTR_REG = 0x12
|
|
|
|
MQNIC_QUEUE_ENABLE_MASK = 0x00000001
|
|
MQNIC_QUEUE_ACTIVE_MASK = 0x00000008
|
|
MQNIC_QUEUE_PTR_MASK = 0xFFFF
|
|
|
|
MQNIC_QUEUE_CMD_SET_VF_ID = 0x80010000
|
|
MQNIC_QUEUE_CMD_SET_SIZE = 0x80020000
|
|
MQNIC_QUEUE_CMD_SET_CQN = 0xC0000000
|
|
MQNIC_QUEUE_CMD_SET_PROD_PTR = 0x80800000
|
|
MQNIC_QUEUE_CMD_SET_CONS_PTR = 0x80900000
|
|
MQNIC_QUEUE_CMD_SET_ENABLE = 0x40000100
|
|
|
|
MQNIC_CQ_BASE_ADDR_VF_REG = 0x00
|
|
MQNIC_CQ_CTRL_STATUS_REG = 0x08
|
|
MQNIC_CQ_PTR_REG = 0x0C
|
|
MQNIC_CQ_PROD_PTR_REG = 0x0C
|
|
MQNIC_CQ_CONS_PTR_REG = 0x0E
|
|
|
|
MQNIC_CQ_ENABLE_MASK = 0x00010000
|
|
MQNIC_CQ_ARM_MASK = 0x00020000
|
|
MQNIC_CQ_ACTIVE_MASK = 0x00080000
|
|
MQNIC_CQ_PTR_MASK = 0xFFFF
|
|
|
|
MQNIC_CQ_CMD_SET_VF_ID = 0x80010000
|
|
MQNIC_CQ_CMD_SET_SIZE = 0x80020000
|
|
MQNIC_CQ_CMD_SET_EQN = 0xC0000000
|
|
MQNIC_CQ_CMD_SET_PROD_PTR = 0x80800000
|
|
MQNIC_CQ_CMD_SET_CONS_PTR = 0x80900000
|
|
MQNIC_CQ_CMD_SET_CONS_PTR_ARM = 0x80910000
|
|
MQNIC_CQ_CMD_SET_ENABLE = 0x40000100
|
|
MQNIC_CQ_CMD_SET_ARM = 0x40000200
|
|
|
|
MQNIC_EQ_BASE_ADDR_VF_REG = 0x00
|
|
MQNIC_EQ_CTRL_STATUS_REG = 0x08
|
|
MQNIC_EQ_PTR_REG = 0x0C
|
|
MQNIC_EQ_PROD_PTR_REG = 0x0C
|
|
MQNIC_EQ_CONS_PTR_REG = 0x0E
|
|
|
|
MQNIC_EQ_ENABLE_MASK = 0x00010000
|
|
MQNIC_EQ_ARM_MASK = 0x00020000
|
|
MQNIC_EQ_ACTIVE_MASK = 0x00080000
|
|
MQNIC_EQ_PTR_MASK = 0xFFFF
|
|
|
|
MQNIC_EQ_CMD_SET_VF_ID = 0x80010000
|
|
MQNIC_EQ_CMD_SET_SIZE = 0x80020000
|
|
MQNIC_EQ_CMD_SET_IRQN = 0xC0000000
|
|
MQNIC_EQ_CMD_SET_PROD_PTR = 0x80800000
|
|
MQNIC_EQ_CMD_SET_CONS_PTR = 0x80900000
|
|
MQNIC_EQ_CMD_SET_CONS_PTR_ARM = 0x80910000
|
|
MQNIC_EQ_CMD_SET_ENABLE = 0x40000100
|
|
MQNIC_EQ_CMD_SET_ARM = 0x40000200
|
|
|
|
MQNIC_EVENT_TYPE_CPL = 0x0000
|
|
|
|
MQNIC_DESC_SIZE = 16
|
|
MQNIC_CPL_SIZE = 32
|
|
MQNIC_EVENT_SIZE = 32
|
|
|
|
|
|
class Resource:
|
|
def __init__(self, count, parent, stride):
|
|
self.count = count
|
|
self.parent = parent
|
|
self.stride = stride
|
|
|
|
self.windows = {}
|
|
self.free_list = list(range(count))
|
|
|
|
def alloc(self):
|
|
return self.free_list.pop(0)
|
|
|
|
def free(self, index):
|
|
self.free_list.append(index)
|
|
self.free_list.sort()
|
|
|
|
def get_count(self):
|
|
return self.count
|
|
|
|
def get_window(self, index):
|
|
if index not in self.windows:
|
|
self.windows[index] = self.parent.create_window(index*self.stride, self.stride)
|
|
return self.windows[index]
|
|
|
|
|
|
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 Eq:
|
|
def __init__(self, interface):
|
|
self.interface = interface
|
|
self.log = interface.log
|
|
self.driver = interface.driver
|
|
self.log_size = 0
|
|
self.size = 0
|
|
self.size_mask = 0
|
|
self.stride = 0
|
|
self.eqn = None
|
|
self.enabled = False
|
|
|
|
self.buf_size = 0
|
|
self.buf_region = None
|
|
self.buf_dma = 0
|
|
self.buf = None
|
|
|
|
self.irq = None
|
|
|
|
self.cq_table = {}
|
|
|
|
self.prod_ptr = 0
|
|
self.cons_ptr = 0
|
|
|
|
self.hw_regs = None
|
|
|
|
async def open(self, irq, size):
|
|
if self.hw_regs:
|
|
raise Exception("Already open")
|
|
|
|
self.eqn = self.interface.eq_res.alloc()
|
|
|
|
self.log.info("Open EQ %d (interface %d)", self.eqn, self.interface.index)
|
|
|
|
self.log_size = size.bit_length() - 1
|
|
self.size = 2**self.log_size
|
|
self.size_mask = self.size-1
|
|
self.stride = MQNIC_EVENT_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
|
|
|
|
self.buf[0:self.buf_size] = b'\x00'*self.buf_size
|
|
|
|
self.prod_ptr = 0
|
|
self.cons_ptr = 0
|
|
|
|
self.irq = irq
|
|
|
|
self.cq_table = {}
|
|
|
|
self.hw_regs = self.interface.eq_res.get_window(self.eqn)
|
|
|
|
await self.hw_regs.write_dword(MQNIC_EQ_CTRL_STATUS_REG, MQNIC_EQ_CMD_SET_ENABLE | 0)
|
|
await self.hw_regs.write_dword(MQNIC_EQ_BASE_ADDR_VF_REG, self.buf_dma & 0xfffff000)
|
|
await self.hw_regs.write_dword(MQNIC_EQ_BASE_ADDR_VF_REG+4, self.buf_dma >> 32)
|
|
await self.hw_regs.write_dword(MQNIC_EQ_CTRL_STATUS_REG, MQNIC_EQ_CMD_SET_SIZE | self.log_size)
|
|
await self.hw_regs.write_dword(MQNIC_EQ_CTRL_STATUS_REG, MQNIC_EQ_CMD_SET_IRQN | self.irq)
|
|
await self.hw_regs.write_dword(MQNIC_EQ_CTRL_STATUS_REG, MQNIC_EQ_CMD_SET_PROD_PTR | (self.prod_ptr & MQNIC_EQ_PTR_MASK))
|
|
await self.hw_regs.write_dword(MQNIC_EQ_CTRL_STATUS_REG, MQNIC_EQ_CMD_SET_CONS_PTR | (self.cons_ptr & MQNIC_EQ_PTR_MASK))
|
|
await self.hw_regs.write_dword(MQNIC_EQ_CTRL_STATUS_REG, MQNIC_EQ_CMD_SET_ENABLE | 1)
|
|
|
|
self.enabled = True
|
|
|
|
async def close(self):
|
|
if not self.hw_regs:
|
|
return
|
|
|
|
await self.hw_regs.write_dword(MQNIC_EQ_CTRL_STATUS_REG, MQNIC_EQ_CMD_SET_ENABLE | 0)
|
|
|
|
# TODO free buffer
|
|
|
|
self.irq = None
|
|
|
|
self.enabled = False
|
|
|
|
self.hw_regs = None
|
|
|
|
self.interface.eq_res.free(self.eqn)
|
|
self.eqn = None
|
|
|
|
def attach_cq(self, cq):
|
|
self.cq_table[cq.cqn] = cq
|
|
|
|
def detach_cq(self, cq):
|
|
del self.cq_table[cq.cqn]
|
|
|
|
async def read_prod_ptr(self):
|
|
val = await self.hw_regs.read_dword(MQNIC_EQ_PTR_REG)
|
|
self.prod_ptr += ((val & MQNIC_EQ_PTR_MASK) - self.prod_ptr) & MQNIC_EQ_PTR_MASK
|
|
|
|
async def write_cons_ptr(self):
|
|
await self.hw_regs.write_dword(MQNIC_EQ_CTRL_STATUS_REG, MQNIC_EQ_CMD_SET_CONS_PTR | (self.cons_ptr & MQNIC_EQ_PTR_MASK))
|
|
|
|
async def arm(self):
|
|
if not self.hw_regs:
|
|
return
|
|
|
|
await self.hw_regs.write_dword(MQNIC_EQ_CTRL_STATUS_REG, MQNIC_EQ_CMD_SET_ARM | 1)
|
|
|
|
async def process_eq(self):
|
|
if not self.interface.port_up:
|
|
return
|
|
|
|
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)
|
|
|
|
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[0] == MQNIC_EVENT_TYPE_CPL:
|
|
# completion
|
|
cq = self.cq_table[event_data[1]]
|
|
await cq.handler(cq)
|
|
await cq.arm()
|
|
|
|
eq_cons_ptr += 1
|
|
eq_index = eq_cons_ptr & self.size_mask
|
|
|
|
self.cons_ptr = eq_cons_ptr
|
|
await self.write_cons_ptr()
|
|
|
|
|
|
class Cq:
|
|
def __init__(self, interface):
|
|
self.interface = interface
|
|
self.log = interface.log
|
|
self.driver = interface.driver
|
|
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.src_ring = None
|
|
self.handler = None
|
|
|
|
self.prod_ptr = 0
|
|
self.cons_ptr = 0
|
|
|
|
self.hw_regs = None
|
|
|
|
async def open(self, eq, size):
|
|
if self.hw_regs:
|
|
raise Exception("Already open")
|
|
|
|
self.cqn = self.interface.cq_res.alloc()
|
|
|
|
self.log.info("Open CQ %d (interface %d)", self.cqn, self.interface.index)
|
|
|
|
self.log_size = size.bit_length() - 1
|
|
self.size = 2**self.log_size
|
|
self.size_mask = self.size-1
|
|
self.stride = MQNIC_EVENT_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
|
|
|
|
self.buf[0:self.buf_size] = b'\x00'*self.buf_size
|
|
|
|
self.prod_ptr = 0
|
|
self.cons_ptr = 0
|
|
|
|
eq.attach_cq(self)
|
|
self.eq = eq
|
|
|
|
self.hw_regs = self.interface.cq_res.get_window(self.cqn)
|
|
|
|
await self.hw_regs.write_dword(MQNIC_CQ_CTRL_STATUS_REG, MQNIC_CQ_CMD_SET_ENABLE | 0)
|
|
await self.hw_regs.write_dword(MQNIC_CQ_BASE_ADDR_VF_REG, self.buf_dma & 0xfffff000)
|
|
await self.hw_regs.write_dword(MQNIC_CQ_BASE_ADDR_VF_REG+4, self.buf_dma >> 32)
|
|
await self.hw_regs.write_dword(MQNIC_CQ_CTRL_STATUS_REG, MQNIC_CQ_CMD_SET_SIZE | self.log_size)
|
|
await self.hw_regs.write_dword(MQNIC_CQ_CTRL_STATUS_REG, MQNIC_CQ_CMD_SET_EQN | self.eq.eqn)
|
|
await self.hw_regs.write_dword(MQNIC_CQ_CTRL_STATUS_REG, MQNIC_CQ_CMD_SET_PROD_PTR | (self.prod_ptr & MQNIC_CQ_PTR_MASK))
|
|
await self.hw_regs.write_dword(MQNIC_CQ_CTRL_STATUS_REG, MQNIC_CQ_CMD_SET_CONS_PTR | (self.cons_ptr & MQNIC_CQ_PTR_MASK))
|
|
await self.hw_regs.write_dword(MQNIC_CQ_CTRL_STATUS_REG, MQNIC_CQ_CMD_SET_ENABLE | 1)
|
|
|
|
self.enabled = True
|
|
|
|
async def close(self):
|
|
if not self.hw_regs:
|
|
return
|
|
|
|
await self.hw_regs.write_dword(MQNIC_CQ_CTRL_STATUS_REG, MQNIC_CQ_CMD_SET_ENABLE | 0)
|
|
|
|
# TODO free buffer
|
|
|
|
self.eq.detach_cq(self)
|
|
self.eq = None
|
|
|
|
self.enabled = False
|
|
|
|
self.hw_regs = None
|
|
|
|
self.interface.cq_res.free(self.cqn)
|
|
self.cqn = None
|
|
|
|
async def read_prod_ptr(self):
|
|
val = await self.hw_regs.read_dword(MQNIC_CQ_PTR_REG)
|
|
self.prod_ptr += ((val & MQNIC_CQ_PTR_MASK) - self.prod_ptr) & MQNIC_CQ_PTR_MASK
|
|
|
|
async def write_cons_ptr(self):
|
|
await self.hw_regs.write_dword(MQNIC_CQ_CTRL_STATUS_REG, MQNIC_CQ_CMD_SET_CONS_PTR | (self.cons_ptr & MQNIC_CQ_PTR_MASK))
|
|
|
|
async def arm(self):
|
|
if not self.hw_regs:
|
|
return
|
|
|
|
await self.hw_regs.write_dword(MQNIC_CQ_CTRL_STATUS_REG, MQNIC_CQ_CMD_SET_ARM | 1)
|
|
|
|
|
|
class Txq:
|
|
def __init__(self, interface):
|
|
self.interface = interface
|
|
self.log = interface.log
|
|
self.driver = interface.driver
|
|
self.log_queue_size = 0
|
|
self.log_desc_block_size = 0
|
|
self.desc_block_size = 0
|
|
self.size = 0
|
|
self.size_mask = 0
|
|
self.full_size = 0
|
|
self.stride = 0
|
|
self.index = None
|
|
self.enabled = False
|
|
|
|
self.buf_size = 0
|
|
self.buf_region = None
|
|
self.buf_dma = 0
|
|
self.buf = None
|
|
|
|
self.cq = None
|
|
|
|
self.prod_ptr = 0
|
|
self.cons_ptr = 0
|
|
|
|
self.clean_event = Event()
|
|
|
|
self.packets = 0
|
|
self.bytes = 0
|
|
|
|
self.hw_regs = None
|
|
|
|
async def open(self, cq, size, desc_block_size):
|
|
if self.hw_regs:
|
|
raise Exception("Already open")
|
|
|
|
self.index = self.interface.txq_res.alloc()
|
|
|
|
self.log.info("Open TXQ %d (interface %d)", self.index, self.interface.index)
|
|
|
|
self.log_queue_size = size.bit_length() - 1
|
|
self.log_desc_block_size = desc_block_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 = MQNIC_DESC_SIZE*self.desc_block_size
|
|
|
|
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
|
|
|
|
self.prod_ptr = 0
|
|
self.cons_ptr = 0
|
|
|
|
self.cq = cq
|
|
self.cq.src_ring = self
|
|
self.cq.handler = Txq.process_tx_cq
|
|
|
|
self.hw_regs = self.interface.txq_res.get_window(self.index)
|
|
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_ENABLE | 0)
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_BASE_ADDR_VF_REG, self.buf_dma & 0xfffff000)
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_BASE_ADDR_VF_REG+4, self.buf_dma >> 32)
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_SIZE | (self.log_desc_block_size << 8) | self.log_queue_size)
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_CQN | self.cq.cqn)
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_PROD_PTR | (self.prod_ptr & MQNIC_QUEUE_PTR_MASK))
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_CONS_PTR | (self.cons_ptr & MQNIC_QUEUE_PTR_MASK))
|
|
|
|
async def close(self):
|
|
if not self.hw_regs:
|
|
return
|
|
|
|
await self.disable()
|
|
|
|
# TODO free buffer
|
|
|
|
if self.cq:
|
|
self.cq.src_ring = None
|
|
self.cq.handler = None
|
|
|
|
self.cq = None
|
|
|
|
self.hw_regs = None
|
|
|
|
self.interface.txq_res.free(self.index)
|
|
self.index = None
|
|
|
|
async def enable(self):
|
|
if not self.hw_regs:
|
|
raise Exception("Not open")
|
|
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_ENABLE | 1)
|
|
|
|
self.enabled = True
|
|
|
|
async def disable(self):
|
|
if not self.hw_regs:
|
|
return
|
|
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_ENABLE | 0)
|
|
|
|
self.enabled = False
|
|
|
|
def empty(self):
|
|
return self.prod_ptr == self.cons_ptr
|
|
|
|
def full(self):
|
|
return self.prod_ptr - self.cons_ptr >= self.full_size
|
|
|
|
async def read_cons_ptr(self):
|
|
val = await self.hw_regs.read_dword(MQNIC_QUEUE_PTR_REG)
|
|
self.cons_ptr += ((val >> 16) - self.cons_ptr) & MQNIC_QUEUE_PTR_MASK
|
|
|
|
async def write_prod_ptr(self):
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_PROD_PTR | (self.prod_ptr & MQNIC_QUEUE_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.cons_ptr & self.size_mask
|
|
self.free_desc(index)
|
|
self.cons_ptr += 1
|
|
|
|
@staticmethod
|
|
async def process_tx_cq(cq):
|
|
interface = cq.interface
|
|
|
|
interface.log.info("Process CQ %d for TXQ %d (interface %d)", cq.cqn, cq.src_ring.index, interface.index)
|
|
|
|
ring = cq.src_ring
|
|
|
|
if not interface.port_up:
|
|
return
|
|
|
|
# process completion queue
|
|
cq_cons_ptr = cq.cons_ptr
|
|
cq_index = cq_cons_ptr & cq.size_mask
|
|
|
|
while True:
|
|
cpl_data = struct.unpack_from("<HHHxxLHHLBBHLL", cq.buf, cq_index*cq.stride)
|
|
ring_index = cpl_data[1] & ring.size_mask
|
|
|
|
interface.log.info("CQ %d index %d data: %s", cq.cqn, cq_index, repr(cpl_data))
|
|
|
|
if bool(cpl_data[-1] & 0x80000000) == bool(cq_cons_ptr & cq.size):
|
|
interface.log.info("CQ %d empty", cq.cqn)
|
|
break
|
|
|
|
interface.log.info("Ring index: %d", ring_index)
|
|
|
|
ring.free_desc(ring_index)
|
|
|
|
cq_cons_ptr += 1
|
|
cq_index = cq_cons_ptr & cq.size_mask
|
|
|
|
cq.cons_ptr = cq_cons_ptr
|
|
await cq.write_cons_ptr()
|
|
|
|
# process ring
|
|
ring_cons_ptr = ring.cons_ptr
|
|
ring_index = ring_cons_ptr & ring.size_mask
|
|
|
|
while (ring_cons_ptr != ring.prod_ptr):
|
|
if ring.tx_info[ring_index]:
|
|
break
|
|
|
|
ring_cons_ptr += 1
|
|
ring_index = ring_cons_ptr & ring.size_mask
|
|
|
|
ring.cons_ptr = ring_cons_ptr
|
|
|
|
ring.clean_event.set()
|
|
|
|
|
|
class Rxq:
|
|
def __init__(self, interface):
|
|
self.interface = interface
|
|
self.log = interface.log
|
|
self.driver = interface.driver
|
|
self.log_queue_size = 0
|
|
self.log_desc_block_size = 0
|
|
self.desc_block_size = 0
|
|
self.size = 0
|
|
self.size_mask = 0
|
|
self.full_size = 0
|
|
self.stride = 0
|
|
self.index = None
|
|
self.enabled = False
|
|
|
|
self.buf_size = 0
|
|
self.buf_region = None
|
|
self.buf_dma = 0
|
|
self.buf = None
|
|
|
|
self.cq = None
|
|
|
|
self.prod_ptr = 0
|
|
self.cons_ptr = 0
|
|
|
|
self.packets = 0
|
|
self.bytes = 0
|
|
|
|
self.hw_regs = None
|
|
|
|
async def open(self, cq, size, desc_block_size):
|
|
if self.hw_regs:
|
|
raise Exception("Already open")
|
|
|
|
self.index = self.interface.rxq_res.alloc()
|
|
|
|
self.log.info("Open RXQ %d (interface %d)", self.index, self.interface.index)
|
|
|
|
self.log_queue_size = size.bit_length() - 1
|
|
self.log_desc_block_size = desc_block_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 = MQNIC_DESC_SIZE*self.desc_block_size
|
|
|
|
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
|
|
|
|
self.prod_ptr = 0
|
|
self.cons_ptr = 0
|
|
|
|
self.cq = cq
|
|
self.cq.src_ring = self
|
|
self.cq.handler = Rxq.process_rx_cq
|
|
|
|
self.hw_regs = self.interface.rxq_res.get_window(self.index)
|
|
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_ENABLE | 0)
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_BASE_ADDR_VF_REG, self.buf_dma & 0xfffff000)
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_BASE_ADDR_VF_REG+4, self.buf_dma >> 32)
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_SIZE | (self.log_desc_block_size << 8) | self.log_queue_size)
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_CQN | self.cq.cqn)
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_PROD_PTR | (self.prod_ptr & MQNIC_QUEUE_PTR_MASK))
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_CONS_PTR | (self.cons_ptr & MQNIC_QUEUE_PTR_MASK))
|
|
|
|
await self.refill_buffers()
|
|
|
|
async def close(self):
|
|
if not self.hw_regs:
|
|
return
|
|
|
|
await self.disable()
|
|
|
|
# TODO free buffer
|
|
|
|
if self.cq:
|
|
self.cq.src_ring = None
|
|
self.cq.handler = None
|
|
|
|
self.cq = None
|
|
|
|
self.hw_regs = None
|
|
|
|
self.interface.rxq_res.free(self.index)
|
|
self.index = None
|
|
|
|
async def enable(self):
|
|
if not self.hw_regs:
|
|
raise Exception("Not open")
|
|
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_ENABLE | 1)
|
|
|
|
self.enabled = True
|
|
|
|
async def disable(self):
|
|
if not self.hw_regs:
|
|
return
|
|
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_ENABLE | 0)
|
|
|
|
self.enabled = False
|
|
|
|
def empty(self):
|
|
return self.prod_ptr == self.cons_ptr
|
|
|
|
def full(self):
|
|
return self.prod_ptr - self.cons_ptr >= self.full_size
|
|
|
|
async def read_cons_ptr(self):
|
|
val = await self.hw_regs.read_dword(MQNIC_QUEUE_PTR_REG)
|
|
self.cons_ptr += ((val >> 16) - self.cons_ptr) & MQNIC_QUEUE_PTR_MASK
|
|
|
|
async def write_prod_ptr(self):
|
|
await self.hw_regs.write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_PROD_PTR | (self.prod_ptr & MQNIC_QUEUE_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.cons_ptr & self.size_mask
|
|
self.free_desc(index)
|
|
self.cons_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.prod_ptr - self.cons_ptr)
|
|
|
|
if missing < 8:
|
|
return
|
|
|
|
for k in range(missing):
|
|
self.prepare_desc(self.prod_ptr & self.size_mask)
|
|
self.prod_ptr += 1
|
|
|
|
await self.write_prod_ptr()
|
|
|
|
@staticmethod
|
|
async def process_rx_cq(cq):
|
|
interface = cq.interface
|
|
|
|
interface.log.info("Process CQ %d for RXQ %d (interface %d)", cq.cqn, cq.src_ring.index, interface.index)
|
|
|
|
ring = cq.src_ring
|
|
|
|
if not interface.port_up:
|
|
return
|
|
|
|
# process completion queue
|
|
cq_cons_ptr = cq.cons_ptr
|
|
cq_index = cq_cons_ptr & cq.size_mask
|
|
|
|
while True:
|
|
cpl_data = struct.unpack_from("<HHHxxLHHLBBHLL", cq.buf, cq_index*cq.stride)
|
|
ring_index = cpl_data[1] & ring.size_mask
|
|
|
|
interface.log.info("CQ %d index %d data: %s", cq.cqn, cq_index, repr(cpl_data))
|
|
|
|
if bool(cpl_data[-1] & 0x80000000) == bool(cq_cons_ptr & cq.size):
|
|
interface.log.info("CQ %d empty", cq.cqn)
|
|
break
|
|
|
|
interface.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]
|
|
|
|
interface.log.info("Packet: %s", skb)
|
|
|
|
interface.pkt_rx_queue.append(skb)
|
|
interface.pkt_rx_sync.set()
|
|
|
|
ring.free_desc(ring_index)
|
|
|
|
cq_cons_ptr += 1
|
|
cq_index = cq_cons_ptr & cq.size_mask
|
|
|
|
cq.cons_ptr = cq_cons_ptr
|
|
await cq.write_cons_ptr()
|
|
|
|
# process ring
|
|
ring_cons_ptr = ring.cons_ptr
|
|
ring_index = ring_cons_ptr & ring.size_mask
|
|
|
|
while (ring_cons_ptr != ring.prod_ptr):
|
|
if ring.rx_info[ring_index]:
|
|
break
|
|
|
|
ring_cons_ptr += 1
|
|
ring_index = ring_cons_ptr & ring.size_mask
|
|
|
|
ring.cons_ptr = ring_cons_ptr
|
|
|
|
# replenish buffers
|
|
await ring.refill_buffers()
|
|
|
|
|
|
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
|
|
self.port_feature_lfc = None
|
|
self.port_feature_pfc = None
|
|
self.port_feature_int_mac_ctrl = 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.port_feature_lfc = bool(self.port_features & MQNIC_PORT_FEATURE_LFC)
|
|
self.port_feature_pfc = bool(self.port_features & MQNIC_PORT_FEATURE_PFC)
|
|
self.port_feature_int_mac_ctrl = bool(self.port_features & MQNIC_PORT_FEATURE_INT_MAC_CTRL)
|
|
|
|
self.log.info("Port features: 0x%08x", self.port_features)
|
|
|
|
await self.set_tx_ctrl(0)
|
|
await self.set_rx_ctrl(0)
|
|
await self.set_lfc_ctrl(0)
|
|
|
|
for k in range(8):
|
|
await self.set_pfc_ctrl(k, 0)
|
|
|
|
async def get_tx_ctrl(self):
|
|
return await self.port_ctrl_rb.read_dword(MQNIC_RB_PORT_CTRL_REG_TX_CTRL)
|
|
|
|
async def set_tx_ctrl(self, val):
|
|
await self.port_ctrl_rb.write_dword(MQNIC_RB_PORT_CTRL_REG_TX_CTRL, val)
|
|
|
|
async def get_rx_ctrl(self):
|
|
return await self.port_ctrl_rb.read_dword(MQNIC_RB_PORT_CTRL_REG_RX_CTRL)
|
|
|
|
async def set_rx_ctrl(self, val):
|
|
await self.port_ctrl_rb.write_dword(MQNIC_RB_PORT_CTRL_REG_RX_CTRL, val)
|
|
|
|
async def get_lfc_ctrl(self):
|
|
return await self.port_ctrl_rb.read_dword(MQNIC_RB_PORT_CTRL_REG_LFC_CTRL)
|
|
|
|
async def set_lfc_ctrl(self, val):
|
|
await self.port_ctrl_rb.write_dword(MQNIC_RB_PORT_CTRL_REG_LFC_CTRL, val)
|
|
|
|
async def get_pfc_ctrl(self, index):
|
|
return await self.port_ctrl_rb.read_dword(MQNIC_RB_PORT_CTRL_REG_PFC_CTRL0 + 4*index)
|
|
|
|
async def set_pfc_ctrl(self, index, val):
|
|
await self.port_ctrl_rb.write_dword(MQNIC_RB_PORT_CTRL_REG_PFC_CTRL0 + 4*index, val)
|
|
|
|
|
|
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.eq_rb = None
|
|
self.cq_rb = None
|
|
self.txq_rb = None
|
|
self.rxq_rb = None
|
|
self.rx_queue_map_rb = None
|
|
|
|
self.if_features = None
|
|
self.if_feature_rss = None
|
|
self.if_feature_ptp_ts = None
|
|
self.if_feature_tx_csum = None
|
|
self.if_feature_rx_csum = None
|
|
self.if_feature_rx_hash = None
|
|
|
|
self.max_tx_mtu = 0
|
|
self.max_rx_mtu = 0
|
|
self.tx_fifo_depth = 0
|
|
self.rx_fifo_depth = 0
|
|
|
|
self.eq_res = None
|
|
self.cq_res = None
|
|
self.txq_res = None
|
|
self.rxq_res = None
|
|
|
|
self.port_count = None
|
|
self.sched_block_count = None
|
|
|
|
self.rx_queue_map_indir_table_size = None
|
|
self.rx_queue_map_indir_table = []
|
|
|
|
self.eq = []
|
|
|
|
self.txq = []
|
|
self.rxq = []
|
|
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.tx_fifo_depth = await self.if_ctrl_rb.read_dword(MQNIC_RB_IF_CTRL_REG_TX_FIFO_DEPTH)
|
|
self.rx_fifo_depth = await self.if_ctrl_rb.read_dword(MQNIC_RB_IF_CTRL_REG_RX_FIFO_DEPTH)
|
|
|
|
self.if_feature_rss = bool(self.if_features & MQNIC_IF_FEATURE_RSS)
|
|
self.if_feature_ptp_ts = bool(self.if_features & MQNIC_IF_FEATURE_PTP_TS)
|
|
self.if_feature_tx_csum = bool(self.if_features & MQNIC_IF_FEATURE_TX_CSUM)
|
|
self.if_feature_rx_csum = bool(self.if_features & MQNIC_IF_FEATURE_RX_CSUM)
|
|
self.if_feature_rx_hash = bool(self.if_features & MQNIC_IF_FEATURE_RX_HASH)
|
|
self.if_feature_lfc = bool(self.if_features & MQNIC_IF_FEATURE_LFC)
|
|
self.if_feature_pfc = bool(self.if_features & MQNIC_IF_FEATURE_PFC)
|
|
|
|
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.eq_rb = self.reg_blocks.find(MQNIC_RB_EQM_TYPE, MQNIC_RB_EQM_VER)
|
|
|
|
offset = await self.eq_rb.read_dword(MQNIC_RB_EQM_REG_OFFSET)
|
|
count = await self.eq_rb.read_dword(MQNIC_RB_EQM_REG_COUNT)
|
|
stride = await self.eq_rb.read_dword(MQNIC_RB_EQM_REG_STRIDE)
|
|
|
|
self.log.info("EQ offset: 0x%08x", offset)
|
|
self.log.info("EQ count: %d", count)
|
|
self.log.info("EQ stride: 0x%08x", stride)
|
|
|
|
count = min(count, MQNIC_MAX_EQ)
|
|
|
|
self.eq_res = Resource(count, self.hw_regs.create_window(offset), stride)
|
|
|
|
self.cq_rb = self.reg_blocks.find(MQNIC_RB_CQM_TYPE, MQNIC_RB_CQM_VER)
|
|
|
|
offset = await self.cq_rb.read_dword(MQNIC_RB_CQM_REG_OFFSET)
|
|
count = await self.cq_rb.read_dword(MQNIC_RB_CQM_REG_COUNT)
|
|
stride = await self.cq_rb.read_dword(MQNIC_RB_CQM_REG_STRIDE)
|
|
|
|
self.log.info("CQ offset: 0x%08x", offset)
|
|
self.log.info("CQ count: %d", count)
|
|
self.log.info("CQ stride: 0x%08x", stride)
|
|
|
|
count = min(count, MQNIC_MAX_CQ)
|
|
|
|
self.cq_res = Resource(count, self.hw_regs.create_window(offset), stride)
|
|
|
|
self.txq_rb = self.reg_blocks.find(MQNIC_RB_TX_QM_TYPE, MQNIC_RB_TX_QM_VER)
|
|
|
|
offset = await self.txq_rb.read_dword(MQNIC_RB_TX_QM_REG_OFFSET)
|
|
count = await self.txq_rb.read_dword(MQNIC_RB_TX_QM_REG_COUNT)
|
|
stride = await self.txq_rb.read_dword(MQNIC_RB_TX_QM_REG_STRIDE)
|
|
|
|
self.log.info("TXQ offset: 0x%08x", offset)
|
|
self.log.info("TXQ count: %d", count)
|
|
self.log.info("TXQ stride: 0x%08x", stride)
|
|
|
|
count = min(count, MQNIC_MAX_TXQ)
|
|
|
|
self.txq_res = Resource(count, self.hw_regs.create_window(offset), stride)
|
|
|
|
self.rxq_rb = self.reg_blocks.find(MQNIC_RB_RX_QM_TYPE, MQNIC_RB_RX_QM_VER)
|
|
|
|
offset = await self.rxq_rb.read_dword(MQNIC_RB_RX_QM_REG_OFFSET)
|
|
count = await self.rxq_rb.read_dword(MQNIC_RB_RX_QM_REG_COUNT)
|
|
stride = await self.rxq_rb.read_dword(MQNIC_RB_RX_QM_REG_STRIDE)
|
|
|
|
self.log.info("RXQ offset: 0x%08x", offset)
|
|
self.log.info("RXQ count: %d", count)
|
|
self.log.info("RXQ stride: 0x%08x", stride)
|
|
|
|
count = min(count, MQNIC_MAX_RXQ)
|
|
|
|
self.rxq_res = Resource(count, self.hw_regs.create_window(offset), stride)
|
|
|
|
self.rx_queue_map_rb = self.reg_blocks.find(MQNIC_RB_RX_QUEUE_MAP_TYPE, MQNIC_RB_RX_QUEUE_MAP_VER)
|
|
|
|
val = await self.rx_queue_map_rb.read_dword(MQNIC_RB_RX_QUEUE_MAP_REG_CFG)
|
|
self.rx_queue_map_indir_table_size = 2**((val >> 8) & 0xff)
|
|
self.rx_queue_map_indir_table = []
|
|
for k in range(self.port_count):
|
|
offset = await self.rx_queue_map_rb.read_dword(MQNIC_RB_RX_QUEUE_MAP_CH_OFFSET +
|
|
MQNIC_RB_RX_QUEUE_MAP_CH_STRIDE*k + MQNIC_RB_RX_QUEUE_MAP_CH_REG_OFFSET)
|
|
self.rx_queue_map_indir_table.append(self.rx_queue_map_rb.parent.create_window(offset))
|
|
|
|
await self.set_rx_queue_map_rss_mask(k, 0)
|
|
await self.set_rx_queue_map_app_mask(k, 0)
|
|
await self.set_rx_queue_map_indir_table(k, 0, 0)
|
|
|
|
# ensure all queues are disabled
|
|
for k in range(self.eq_res.get_count()):
|
|
await self.eq_res.get_window(k).write_dword(MQNIC_EQ_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_ENABLE | 0)
|
|
|
|
for k in range(self.cq_res.get_count()):
|
|
await self.cq_res.get_window(k).write_dword(MQNIC_CQ_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_ENABLE | 0)
|
|
|
|
for k in range(self.txq_res.get_count()):
|
|
await self.txq_res.get_window(k).write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_ENABLE | 0)
|
|
|
|
for k in range(self.rxq_res.get_count()):
|
|
await self.rxq_res.get_window(k).write_dword(MQNIC_QUEUE_CTRL_STATUS_REG, MQNIC_QUEUE_CMD_SET_ENABLE | 0)
|
|
|
|
# create ports
|
|
self.ports = []
|
|
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)
|
|
|
|
# create schedulers
|
|
self.sched_blocks = []
|
|
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)
|
|
|
|
# create EQs
|
|
self.eq = []
|
|
for k in range(self.eq_res.get_count()):
|
|
eq = Eq(self)
|
|
await eq.open(self.index, 1024)
|
|
self.eq.append(eq)
|
|
await eq.arm()
|
|
|
|
self.txq = []
|
|
self.rxq = []
|
|
|
|
# wait for all writes to complete
|
|
await self.hw_regs.read_dword(0)
|
|
|
|
async def open(self):
|
|
for k in range(self.rxq_res.get_count()):
|
|
cq = Cq(self)
|
|
await cq.open(self.eq[k % len(self.eq)], 1024)
|
|
await cq.arm()
|
|
rxq = Rxq(self)
|
|
await rxq.open(cq, 1024, 4)
|
|
await rxq.enable()
|
|
self.rxq.append(rxq)
|
|
|
|
for k in range(self.txq_res.get_count()):
|
|
cq = Cq(self)
|
|
await cq.open(self.eq[k % len(self.eq)], 1024)
|
|
await cq.arm()
|
|
txq = Txq(self)
|
|
await txq.open(cq, 1024, 4)
|
|
await txq.enable()
|
|
self.txq.append(txq)
|
|
|
|
# wait for all writes to complete
|
|
await self.hw_regs.read_dword(0)
|
|
|
|
await self.ports[0].set_tx_ctrl(MQNIC_PORT_TX_CTRL_EN)
|
|
await self.ports[0].set_rx_ctrl(MQNIC_PORT_RX_CTRL_EN)
|
|
|
|
self.port_up = True
|
|
|
|
async def close(self):
|
|
self.port_up = False
|
|
|
|
await self.ports[0].set_rx_ctrl(0)
|
|
|
|
for q in self.txq:
|
|
q.disable()
|
|
|
|
for q in self.rxq:
|
|
q.disable()
|
|
|
|
# wait for all writes to complete
|
|
await self.hw_regs.read_dword(0)
|
|
|
|
for q in self.txq:
|
|
cq = q.cq
|
|
await q.free_buf()
|
|
await q.close()
|
|
await cq.close()
|
|
|
|
for q in self.rxq:
|
|
cq = q.cq
|
|
await q.free_buf()
|
|
await q.close()
|
|
await cq.close()
|
|
|
|
self.txq = []
|
|
self.rxq = []
|
|
|
|
await self.ports[0].set_tx_ctrl(0)
|
|
|
|
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.txq[ring_index]
|
|
|
|
while True:
|
|
# check for space in ring
|
|
if ring.prod_ptr - ring.cons_ptr < ring.full_size:
|
|
break
|
|
|
|
# wait for space
|
|
ring.clean_event.clear()
|
|
await ring.clean_event.wait()
|
|
|
|
index = ring.prod_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.prod_ptr += 1
|
|
|
|
await ring.write_prod_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_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 get_rx_queue_map_indir_table(self, port, index):
|
|
return await self.rx_queue_map_indir_table[port].read_dword(index*4)
|
|
|
|
async def set_rx_queue_map_indir_table(self, port, index, val):
|
|
await self.rx_queue_map_indir_table[port].write_dword(index*4, 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_EQ)
|
|
|
|
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.eq:
|
|
if eq.irq == index:
|
|
await eq.process_eq()
|
|
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)
|