Compare commits

...

8 Commits

Author SHA1 Message Date
Alex Forencich
f8f73ea570 cndm: Reorganize driver
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2026-03-04 01:19:37 -08:00
Alex Forencich
9f56b9febd cndm: Reorganize driver model
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2026-03-03 20:43:09 -08:00
Alex Forencich
6bf7240686 cndm: Rework desc/cpl mux/demux logic, add support for CQNs, implement queue allocation
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2026-03-03 18:09:38 -08:00
Alex Forencich
8494e734a8 axis: Add TID_ROUTE parameter to taxi_axis_demux to faciliate routing replies by TID
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2026-03-03 16:14:32 -08:00
Alex Forencich
4d8f0cfece cndm: Move control registers out of port module
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2026-03-03 13:31:16 -08:00
Alex Forencich
191f7940b3 cndm_proto: Use SV enums for state machines
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2026-03-03 12:05:35 -08:00
Alex Forencich
0ab7538e24 cndm: Use SV enums for state machines
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2026-03-03 12:05:14 -08:00
Alex Forencich
902996e3bd cndm: Add wmb to ensure command is fully written in mailbox before executing it
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2026-03-03 00:46:15 -08:00
25 changed files with 2158 additions and 1297 deletions

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2018-2025 FPGA Ninja, LLC
Copyright (c) 2018-2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
@@ -19,6 +19,8 @@ module taxi_axis_demux #
(
// Number of AXI stream outputs
parameter M_COUNT = 4,
// route via tid
parameter logic TID_ROUTE = 1'b0,
// route via tdest
parameter logic TDEST_ROUTE = 1'b0
)
@@ -53,6 +55,8 @@ localparam logic LAST_EN = s_axis.LAST_EN && m_axis[0].LAST_EN;
localparam logic ID_EN = s_axis.ID_EN && m_axis[0].ID_EN;
localparam ID_W = s_axis.ID_W;
localparam logic DEST_EN = s_axis.DEST_EN && m_axis[0].DEST_EN;
localparam S_ID_W = s_axis.ID_W;
localparam M_ID_W = m_axis[0].ID_W;
localparam S_DEST_W = s_axis.DEST_W;
localparam M_DEST_W = m_axis[0].DEST_W;
localparam logic USER_EN = s_axis.USER_EN && m_axis[0].USER_EN;
@@ -61,6 +65,7 @@ localparam USER_W = s_axis.USER_W;
localparam CL_M_COUNT = $clog2(M_COUNT);
localparam M_DEST_W_INT = M_DEST_W > 0 ? M_DEST_W : 1;
localparam M_ID_W_INT = M_ID_W > 0 ? M_ID_W : 1;
// check configuration
if (m_axis[0].DATA_W != DATA_W)
@@ -69,6 +74,17 @@ if (m_axis[0].DATA_W != DATA_W)
if (KEEP_EN && m_axis[0].KEEP_W != KEEP_W)
$fatal(0, "Error: Interface KEEP_W parameter mismatch (instance %m)");
if (TID_ROUTE) begin
if (!ID_EN)
$fatal(0, "Error: TID_ROUTE set requires ID_EN set (instance %m)");
if (S_ID_W < CL_M_COUNT)
$fatal(0, "Error: S_ID_W too small for port count (instance %m)");
if (TDEST_ROUTE)
$fatal(0, "Error: Cannot enable both TID_ROUTE and TDEST_ROUTE (instance %m)");
end
if (TDEST_ROUTE) begin
if (!DEST_EN)
$fatal(0, "Error: TDEST_ROUTE set requires DEST_EN set (instance %m)");
@@ -90,7 +106,7 @@ logic [KEEP_W-1:0] m_axis_tstrb_int;
logic [M_COUNT-1:0] m_axis_tvalid_int;
logic m_axis_tready_int_reg = 1'b0;
logic m_axis_tlast_int;
logic [ID_W-1:0] m_axis_tid_int;
logic [M_ID_W-1:0] m_axis_tid_int;
logic [M_DEST_W-1:0] m_axis_tdest_int;
logic [USER_W-1:0] m_axis_tuser_int;
wire m_axis_tready_int_early;
@@ -115,7 +131,15 @@ always_comb begin
if (!frame_reg && s_axis.tvalid && s_axis.tready) begin
// start of frame, grab select value
if (TDEST_ROUTE) begin
if (TID_ROUTE) begin
if (M_COUNT > 1) begin
select_ctl = s_axis.tid[S_ID_W-1:S_ID_W-CL_M_COUNT];
drop_ctl = (CL_M_COUNT+1)'(select_ctl) >= (CL_M_COUNT+1)'(M_COUNT);
end else begin
select_ctl = '0;
drop_ctl = 1'b0;
end
end else if (TDEST_ROUTE) begin
if (M_COUNT > 1) begin
select_ctl = s_axis.tdest[S_DEST_W-1:S_DEST_W-CL_M_COUNT];
drop_ctl = (CL_M_COUNT+1)'(select_ctl) >= (CL_M_COUNT+1)'(M_COUNT);
@@ -141,7 +165,7 @@ always_comb begin
m_axis_tvalid_int = '0;
m_axis_tvalid_int[select_ctl] = s_axis.tvalid && s_axis.tready && !drop_ctl;
m_axis_tlast_int = s_axis.tlast;
m_axis_tid_int = s_axis.tid;
m_axis_tid_int = M_ID_W'(s_axis.tid);
m_axis_tdest_int = M_DEST_W'(s_axis.tdest);
m_axis_tuser_int = s_axis.tuser;
end
@@ -170,7 +194,7 @@ logic [KEEP_W-1:0] m_axis_tkeep_reg = '0;
logic [KEEP_W-1:0] m_axis_tstrb_reg = '0;
logic [M_COUNT-1:0] m_axis_tvalid_reg = '0, m_axis_tvalid_next;
logic m_axis_tlast_reg = 1'b0;
logic [ID_W-1:0] m_axis_tid_reg = '0;
logic [M_ID_W-1:0] m_axis_tid_reg = '0;
logic [M_DEST_W-1:0] m_axis_tdest_reg = '0;
logic [USER_W-1:0] m_axis_tuser_reg = '0;
@@ -179,7 +203,7 @@ logic [KEEP_W-1:0] temp_m_axis_tkeep_reg = '0;
logic [KEEP_W-1:0] temp_m_axis_tstrb_reg = '0;
logic [M_COUNT-1:0] temp_m_axis_tvalid_reg = '0, temp_m_axis_tvalid_next;
logic temp_m_axis_tlast_reg = 1'b0;
logic [ID_W-1:0] temp_m_axis_tid_reg = '0;
logic [M_ID_W-1:0] temp_m_axis_tid_reg = '0;
logic [M_DEST_W-1:0] temp_m_axis_tdest_reg = '0;
logic [USER_W-1:0] temp_m_axis_tuser_reg = '0;

View File

@@ -40,12 +40,14 @@ export PARAM_KEEP_W := $(shell expr \( $(PARAM_DATA_W) + 7 \) / 8 )
export PARAM_STRB_EN := 0
export PARAM_LAST_EN := 1
export PARAM_ID_EN := 1
export PARAM_ID_W := 8
export PARAM_M_ID_W := 8
export PARAM_S_ID_W := $(shell python -c "print($(PARAM_M_ID_W) + ($(PARAM_M_COUNT)-1).bit_length())")
export PARAM_DEST_EN := 1
export PARAM_M_DEST_W := 8
export PARAM_S_DEST_W := $(shell python -c "print($(PARAM_M_DEST_W) + ($(PARAM_M_COUNT)-1).bit_length())")
export PARAM_USER_EN := 1
export PARAM_USER_W := 1
export PARAM_TID_ROUTE := 0
export PARAM_TDEST_ROUTE := 1
ifeq ($(SIM), icarus)

View File

@@ -65,11 +65,11 @@ async def run_test(dut, payload_lengths=None, payload_data=None, idle_inserter=N
tb = TB(dut)
id_width = len(tb.source.bus.tid)
id_width = len(tb.sink[0].bus.tid)
id_count = 2**id_width
id_mask = id_count-1
dest_width = len(tb.sink[0].bus.tid)
dest_width = len(tb.sink[0].bus.tdest)
dest_count = 2**dest_width
dest_mask = dest_count-1
@@ -183,12 +183,14 @@ def test_taxi_axis_demux(request, m_count, data_w, tdest_route):
parameters['STRB_EN'] = 0
parameters['LAST_EN'] = 1
parameters['ID_EN'] = 1
parameters['ID_W'] = 8
parameters['M_ID_W'] = 8
parameters['S_ID_W'] = parameters['M_ID_W'] + (m_count-1).bit_length()
parameters['DEST_EN'] = 1
parameters['M_DEST_W'] = 8
parameters['S_DEST_W'] = parameters['M_DEST_W'] + (m_count-1).bit_length()
parameters['USER_EN'] = 1
parameters['USER_W'] = 1
parameters['TID_ROUTE'] = 0
parameters['TDEST_ROUTE'] = tdest_route
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}

View File

@@ -25,12 +25,14 @@ module test_taxi_axis_demux #
parameter logic STRB_EN = 1'b0,
parameter logic LAST_EN = 1'b1,
parameter logic ID_EN = 1'b0,
parameter ID_W = 8,
parameter M_ID_W = 8,
parameter S_ID_W = M_ID_W+$clog2(M_COUNT),
parameter logic DEST_EN = 1'b0,
parameter M_DEST_W = 8,
parameter S_DEST_W = M_DEST_W+$clog2(M_COUNT),
parameter logic USER_EN = 1'b1,
parameter USER_W = 1,
parameter logic TID_ROUTE = 1'b0,
parameter logic TDEST_ROUTE = 1'b0
/* verilator lint_on WIDTHTRUNC */
)
@@ -46,7 +48,7 @@ taxi_axis_if #(
.STRB_EN(STRB_EN),
.LAST_EN(LAST_EN),
.ID_EN(ID_EN),
.ID_W(ID_W),
.ID_W(S_ID_W),
.DEST_EN(DEST_EN),
.DEST_W(S_DEST_W),
.USER_EN(USER_EN),
@@ -60,7 +62,7 @@ taxi_axis_if #(
.STRB_EN(STRB_EN),
.LAST_EN(LAST_EN),
.ID_EN(ID_EN),
.ID_W(ID_W),
.ID_W(M_ID_W),
.DEST_EN(DEST_EN),
.DEST_W(M_DEST_W),
.USER_EN(USER_EN),
@@ -73,6 +75,7 @@ logic [$clog2(M_COUNT)-1:0] select;
taxi_axis_demux #(
.M_COUNT(M_COUNT),
.TID_ROUTE(TID_ROUTE),
.TDEST_ROUTE(TDEST_ROUTE)
)
uut (

View File

@@ -12,8 +12,9 @@ cndm-y += cndm_dev.o
cndm-y += cndm_netdev.o
cndm-y += cndm_ethtool.o
cndm-y += cndm_ptp.o
cndm-y += cndm_tx.o
cndm-y += cndm_rx.o
cndm-y += cndm_cq.o
cndm-y += cndm_sq.o
cndm-y += cndm_rq.o
ifneq ($(DEBUG),)
ccflags-y += -DDEBUG

View File

@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL */
/*
Copyright (c) 2025 FPGA Ninja, LLC
Copyright (c) 2025-2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
@@ -73,6 +73,71 @@ struct cndm_rx_info {
u32 len;
};
struct cndm_ring {
// written on enqueue
u32 prod_ptr;
u64 bytes;
u64 packet;
u64 dropped_packets;
struct netdev_queue *tx_queue;
// written from completion
u32 cons_ptr ____cacheline_aligned_in_smp;
u64 ts_s;
u8 ts_valid;
// mostly constant
u32 size;
u32 full_size;
u32 size_mask;
u32 stride;
u32 mtu;
size_t buf_size;
u8 *buf;
dma_addr_t buf_dma_addr;
union {
struct cndm_tx_info *tx_info;
struct cndm_rx_info *rx_info;
};
struct device *dev;
struct cndm_dev *cdev;
struct cndm_priv *priv;
int index;
int enabled;
struct cndm_cq *cq;
u32 db_offset;
u8 __iomem *db_addr;
} ____cacheline_aligned_in_smp;
struct cndm_cq {
u32 cons_ptr;
u32 size;
u32 size_mask;
u32 stride;
size_t buf_size;
u8 *buf;
dma_addr_t buf_dma_addr;
struct device *dev;
struct cndm_dev *cdev;
struct cndm_priv *priv;
struct napi_struct napi;
int cqn;
int enabled;
struct cndm_ring *src_ring;
void (*handler)(struct cndm_cq *cq);
};
struct cndm_priv {
struct device *dev;
struct net_device *ndev;
@@ -83,62 +148,18 @@ struct cndm_priv {
void __iomem *hw_addr;
size_t txq_region_len;
void *txq_region;
dma_addr_t txq_region_addr;
struct cndm_irq *irq;
struct notifier_block irq_nb;
struct hwtstamp_config hwts_config;
u64 ts_s;
u8 ts_valid;
struct cndm_tx_info *tx_info;
struct cndm_rx_info *rx_info;
int rxq_count;
int txq_count;
struct netdev_queue *tx_queue;
struct napi_struct tx_napi;
struct napi_struct rx_napi;
u32 txq_log_size;
u32 txq_size;
u32 txq_mask;
u32 txq_prod;
u32 txq_cons;
u32 txq_db_offs;
size_t rxq_region_len;
void *rxq_region;
dma_addr_t rxq_region_addr;
u32 rxq_log_size;
u32 rxq_size;
u32 rxq_mask;
u32 rxq_prod;
u32 rxq_cons;
u32 rxq_db_offs;
size_t txcq_region_len;
void *txcq_region;
dma_addr_t txcq_region_addr;
u32 txcq_log_size;
u32 txcq_size;
u32 txcq_mask;
u32 txcq_prod;
u32 txcq_cons;
size_t rxcq_region_len;
void *rxcq_region;
dma_addr_t rxcq_region_addr;
u32 rxcq_log_size;
u32 rxcq_size;
u32 rxcq_mask;
u32 rxcq_prod;
u32 rxcq_cons;
struct cndm_ring *txq;
struct cndm_cq *txcq;
struct cndm_ring *rxq;
struct cndm_cq *rxcq;
};
// cndm_cmd.c
@@ -165,18 +186,32 @@ extern const struct file_operations cndm_fops;
extern const struct ethtool_ops cndm_ethtool_ops;
// cndm_ptp.c
ktime_t cndm_read_cpl_ts(struct cndm_priv *priv, const struct cndm_cpl *cpl);
ktime_t cndm_read_cpl_ts(struct cndm_ring *ring, const struct cndm_cpl *cpl);
void cndm_register_phc(struct cndm_dev *cdev);
void cndm_unregister_phc(struct cndm_dev *cdev);
// cndm_tx.c
int cndm_free_tx_buf(struct cndm_priv *priv);
// cndm_cq.c
struct cndm_cq *cndm_create_cq(struct cndm_priv *priv);
void cndm_destroy_cq(struct cndm_cq *cq);
int cndm_open_cq(struct cndm_cq *cq, int irqn, int size);
void cndm_close_cq(struct cndm_cq *cq);
// cndm_sq.c
struct cndm_ring *cndm_create_sq(struct cndm_priv *priv);
void cndm_destroy_sq(struct cndm_ring *sq);
int cndm_open_sq(struct cndm_ring *sq, struct cndm_priv *priv, struct cndm_cq *cq, int size);
void cndm_close_sq(struct cndm_ring *sq);
int cndm_free_tx_buf(struct cndm_ring *sq);
int cndm_poll_tx_cq(struct napi_struct *napi, int budget);
int cndm_start_xmit(struct sk_buff *skb, struct net_device *ndev);
// cndm_rx.c
int cndm_free_rx_buf(struct cndm_priv *priv);
int cndm_refill_rx_buffers(struct cndm_priv *priv);
// cndm_rq.c
struct cndm_ring *cndm_create_rq(struct cndm_priv *priv);
void cndm_destroy_rq(struct cndm_ring *rq);
int cndm_open_rq(struct cndm_ring *rq, struct cndm_priv *priv, struct cndm_cq *cq, int size);
void cndm_close_rq(struct cndm_ring *rq);
int cndm_free_rx_buf(struct cndm_ring *rq);
int cndm_refill_rx_buffers(struct cndm_ring *rq);
int cndm_poll_rx_cq(struct napi_struct *napi, int budget);
#endif

View File

@@ -24,6 +24,9 @@ int cndm_exec_mbox_cmd(struct cndm_dev *cdev, void *cmd, void *rsp)
iowrite32(*((u32 *)(cmd + k*4)), cdev->hw_addr + 0x10000 + k*4);
}
// ensure the command is completely written
wmb();
// execute it
iowrite32(0x00000001, cdev->hw_addr + 0x0200);

View File

@@ -0,0 +1,112 @@
// SPDX-License-Identifier: GPL
/*
Copyright (c) 2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#include "cndm.h"
struct cndm_cq *cndm_create_cq(struct cndm_priv *priv)
{
struct cndm_cq *cq;
cq = kzalloc(sizeof(*cq), GFP_KERNEL);
if (!cq)
return ERR_PTR(-ENOMEM);
cq->cdev = priv->cdev;
cq->dev = priv->dev;
cq->priv = priv;
cq->cqn = -1;
cq->enabled = 0;
cq->cons_ptr = 0;
return cq;
}
void cndm_destroy_cq(struct cndm_cq *cq)
{
cndm_close_cq(cq);
kfree(cq);
}
int cndm_open_cq(struct cndm_cq *cq, int irqn, int size)
{
int ret = 0;
struct cndm_cmd_queue cmd;
struct cndm_cmd_queue rsp;
if (cq->enabled || cq->buf)
return -EINVAL;
cq->size = roundup_pow_of_two(size);
cq->size_mask = cq->size - 1;
cq->stride = 16;
cq->buf_size = cq->size * cq->stride;
cq->buf = dma_alloc_coherent(cq->dev, cq->buf_size, &cq->buf_dma_addr, GFP_KERNEL);
if (!cq->buf)
return -ENOMEM;
cq->cons_ptr = 0;
// clear all phase tag bits
memset(cq->buf, 0, cq->buf_size);
cmd.opcode = CNDM_CMD_OP_CREATE_CQ;
cmd.flags = 0x00000000;
cmd.port = cq->priv->ndev->dev_port;
cmd.qn = 0;
cmd.qn2 = irqn; // TODO
cmd.pd = 0;
cmd.size = ilog2(cq->size);
cmd.dboffs = 0;
cmd.ptr1 = cq->buf_dma_addr;
cmd.ptr2 = 0;
cndm_exec_cmd(cq->cdev, &cmd, &rsp);
cq->cqn = rsp.qn;
cq->enabled = 1;
return 0;
fail:
cndm_close_cq(cq);
return ret;
}
void cndm_close_cq(struct cndm_cq *cq)
{
struct cndm_dev *cdev = cq->cdev;
struct cndm_cmd_queue cmd;
struct cndm_cmd_queue rsp;
cq->enabled = 0;
if (cq->cqn != -1) {
cmd.opcode = CNDM_CMD_OP_DESTROY_CQ;
cmd.flags = 0x00000000;
cmd.port = cq->priv->ndev->dev_port;
cmd.qn = cq->cqn;
cndm_exec_cmd(cdev, &cmd, &rsp);
cq->cqn = -1;
}
if (cq->buf) {
dma_free_coherent(cq->dev, cq->buf_size, cq->buf, cq->buf_dma_addr);
cq->buf = NULL;
cq->buf_dma_addr = 0;
}
}

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL
/*
Copyright (c) 2025 FPGA Ninja, LLC
Copyright (c) 2025-2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
@@ -17,14 +17,14 @@ static int cndm_open(struct net_device *ndev)
{
struct cndm_priv *priv = netdev_priv(ndev);
cndm_refill_rx_buffers(priv);
cndm_refill_rx_buffers(priv->rxq);
priv->tx_queue = netdev_get_tx_queue(ndev, 0);
priv->txq->tx_queue = netdev_get_tx_queue(ndev, 0);
netif_napi_add_tx(ndev, &priv->tx_napi, cndm_poll_tx_cq);
napi_enable(&priv->tx_napi);
netif_napi_add(ndev, &priv->rx_napi, cndm_poll_rx_cq);
napi_enable(&priv->rx_napi);
netif_napi_add_tx(ndev, &priv->txcq->napi, cndm_poll_tx_cq);
napi_enable(&priv->txcq->napi);
netif_napi_add(ndev, &priv->rxcq->napi, cndm_poll_rx_cq);
napi_enable(&priv->rxcq->napi);
netif_tx_start_all_queues(ndev);
netif_carrier_on(ndev);
@@ -39,12 +39,19 @@ static int cndm_close(struct net_device *ndev)
{
struct cndm_priv *priv = netdev_priv(ndev);
if (!priv->port_up)
return 0;
priv->port_up = 0;
napi_disable(&priv->tx_napi);
netif_napi_del(&priv->tx_napi);
napi_disable(&priv->rx_napi);
netif_napi_del(&priv->rx_napi);
if (priv->txcq) {
napi_disable(&priv->txcq->napi);
netif_napi_del(&priv->txcq->napi);
}
if (priv->rxcq) {
napi_disable(&priv->rxcq->napi);
netif_napi_del(&priv->rxcq->napi);
}
netif_tx_stop_all_queues(ndev);
netif_carrier_off(ndev);
@@ -164,8 +171,8 @@ static int cndm_netdev_irq(struct notifier_block *nb, unsigned long action, void
netdev_dbg(priv->ndev, "Interrupt");
if (priv->port_up) {
napi_schedule_irqoff(&priv->tx_napi);
napi_schedule_irqoff(&priv->rx_napi);
napi_schedule_irqoff(&priv->txcq->napi);
napi_schedule_irqoff(&priv->rxcq->napi);
}
return NOTIFY_DONE;
@@ -178,9 +185,6 @@ struct net_device *cndm_create_netdev(struct cndm_dev *cdev, int port)
struct cndm_priv *priv;
int ret = 0;
struct cndm_cmd_queue cmd;
struct cndm_cmd_queue rsp;
ndev = alloc_etherdev_mqs(sizeof(*priv), 1, 1);
if (!ndev) {
dev_err(dev, "Failed to allocate net_device");
@@ -199,6 +203,9 @@ struct net_device *cndm_create_netdev(struct cndm_dev *cdev, int port)
priv->hw_addr = cdev->hw_addr;
priv->rxq_count = 1;
priv->txq_count = 1;
netif_set_real_num_tx_queues(ndev, 1);
netif_set_real_num_rx_queues(ndev, 1);
@@ -219,128 +226,54 @@ struct net_device *cndm_create_netdev(struct cndm_dev *cdev, int port)
ndev->min_mtu = ETH_MIN_MTU;
ndev->max_mtu = 1500;
priv->rxq_log_size = ilog2(256);
priv->rxq_size = 1 << priv->rxq_log_size;
priv->rxq_mask = priv->rxq_size-1;
priv->rxq_prod = 0;
priv->rxq_cons = 0;
priv->txq_log_size = ilog2(256);
priv->txq_size = 1 << priv->txq_log_size;
priv->txq_mask = priv->txq_size-1;
priv->txq_prod = 0;
priv->txq_cons = 0;
priv->rxcq_log_size = ilog2(256);
priv->rxcq_size = 1 << priv->rxcq_log_size;
priv->rxcq_mask = priv->rxcq_size-1;
priv->rxcq_prod = 0;
priv->rxcq_cons = 0;
priv->txcq_log_size = ilog2(256);
priv->txcq_size = 1 << priv->txcq_log_size;
priv->txcq_mask = priv->txcq_size-1;
priv->txcq_prod = 0;
priv->txcq_cons = 0;
// allocate DMA buffers
priv->txq_region_len = priv->txq_size*16;
priv->txq_region = dma_alloc_coherent(dev, priv->txq_region_len, &priv->txq_region_addr, GFP_KERNEL | __GFP_ZERO);
if (!priv->txq_region) {
ret = -ENOMEM;
priv->rxcq = cndm_create_cq(priv);
if (IS_ERR_OR_NULL(priv->rxcq)) {
ret = PTR_ERR(priv->rxcq);
goto fail;
}
ret = cndm_open_cq(priv->rxcq, 0, 256);
if (ret) {
cndm_destroy_cq(priv->rxcq);
priv->rxcq = NULL;
goto fail;
}
priv->rxq_region_len = priv->rxq_size*16;
priv->rxq_region = dma_alloc_coherent(dev, priv->rxq_region_len, &priv->rxq_region_addr, GFP_KERNEL | __GFP_ZERO);
if (!priv->rxq_region) {
ret = -ENOMEM;
priv->rxq = cndm_create_rq(priv);
if (IS_ERR_OR_NULL(priv->rxq)) {
ret = PTR_ERR(priv->rxq);
goto fail;
}
ret = cndm_open_rq(priv->rxq, priv, priv->rxcq, 256);
if (ret) {
cndm_destroy_rq(priv->rxq);
priv->rxq = NULL;
goto fail;
}
priv->txcq_region_len = priv->txcq_size*16;
priv->txcq_region = dma_alloc_coherent(dev, priv->txcq_region_len, &priv->txcq_region_addr, GFP_KERNEL | __GFP_ZERO);
if (!priv->txcq_region) {
ret = -ENOMEM;
priv->txcq = cndm_create_cq(priv);
if (IS_ERR_OR_NULL(priv->txcq)) {
ret = PTR_ERR(priv->txcq);
goto fail;
}
ret = cndm_open_cq(priv->txcq, 0, 256);
if (ret) {
cndm_destroy_cq(priv->txcq);
priv->txcq = NULL;
goto fail;
}
priv->rxcq_region_len = priv->rxcq_size*16;
priv->rxcq_region = dma_alloc_coherent(dev, priv->rxcq_region_len, &priv->rxcq_region_addr, GFP_KERNEL | __GFP_ZERO);
if (!priv->rxcq_region) {
ret = -ENOMEM;
priv->txq = cndm_create_sq(priv);
if (IS_ERR_OR_NULL(priv->txq)) {
ret = PTR_ERR(priv->txq);
goto fail;
}
// allocate info rings
priv->tx_info = kvzalloc(sizeof(*priv->tx_info) * priv->txq_size, GFP_KERNEL);
if (!priv->tx_info) {
ret = -ENOMEM;
ret = cndm_open_sq(priv->txq, priv, priv->txcq, 256);
if (ret) {
cndm_destroy_sq(priv->txq);
priv->txq = NULL;
goto fail;
}
priv->rx_info = kvzalloc(sizeof(*priv->rx_info) * priv->rxq_size, GFP_KERNEL);
if (!priv->tx_info) {
ret = -ENOMEM;
goto fail;
}
cmd.opcode = CNDM_CMD_OP_CREATE_CQ;
cmd.flags = 0x00000000;
cmd.port = port;
cmd.qn = 0;
cmd.qn2 = 0;
cmd.pd = 0;
cmd.size = priv->rxcq_log_size;
cmd.dboffs = 0;
cmd.ptr1 = priv->rxcq_region_addr;
cmd.ptr2 = 0;
cndm_exec_cmd(cdev, &cmd, &rsp);
cmd.opcode = CNDM_CMD_OP_CREATE_RQ;
cmd.flags = 0x00000000;
cmd.port = port;
cmd.qn = 0;
cmd.qn2 = 0;
cmd.pd = 0;
cmd.size = priv->rxq_log_size;
cmd.dboffs = 0;
cmd.ptr1 = priv->rxq_region_addr;
cmd.ptr2 = 0;
cndm_exec_cmd(cdev, &cmd, &rsp);
priv->rxq_db_offs = rsp.dboffs;
cmd.opcode = CNDM_CMD_OP_CREATE_CQ;
cmd.flags = 0x00000000;
cmd.port = port;
cmd.qn = 1;
cmd.qn2 = 0;
cmd.pd = 0;
cmd.size = priv->txcq_log_size;
cmd.dboffs = 0;
cmd.ptr1 = priv->txcq_region_addr;
cmd.ptr2 = 0;
cndm_exec_cmd(cdev, &cmd, &rsp);
cmd.opcode = CNDM_CMD_OP_CREATE_SQ;
cmd.flags = 0x00000000;
cmd.port = port;
cmd.qn = 0;
cmd.qn2 = 0;
cmd.pd = 0;
cmd.size = priv->txq_log_size;
cmd.dboffs = 0;
cmd.ptr1 = priv->txq_region_addr;
cmd.ptr2 = 0;
cndm_exec_cmd(cdev, &cmd, &rsp);
priv->txq_db_offs = rsp.dboffs;
netif_carrier_off(ndev);
ret = register_netdev(ndev);
@@ -370,41 +303,33 @@ fail:
void cndm_destroy_netdev(struct net_device *ndev)
{
struct cndm_priv *priv = netdev_priv(ndev);
struct cndm_dev *cdev = priv->cdev;
struct device *dev = priv->dev;
struct cndm_cmd_queue cmd;
struct cndm_cmd_queue rsp;
if (priv->port_up)
cndm_close(ndev);
cmd.opcode = CNDM_CMD_OP_DESTROY_CQ;
cmd.flags = 0x00000000;
cmd.port = ndev->dev_port;
cmd.qn = 0;
if (priv->txq) {
cndm_close_sq(priv->txq);
cndm_destroy_sq(priv->txq);
priv->txq = NULL;
}
cndm_exec_cmd(cdev, &cmd, &rsp);
if (priv->txcq) {
cndm_close_cq(priv->txcq);
cndm_destroy_cq(priv->txcq);
priv->txcq = NULL;
}
cmd.opcode = CNDM_CMD_OP_DESTROY_RQ;
cmd.flags = 0x00000000;
cmd.port = ndev->dev_port;
cmd.qn = 0;
if (priv->rxq) {
cndm_close_rq(priv->rxq);
cndm_destroy_rq(priv->rxq);
priv->rxq = NULL;
}
cndm_exec_cmd(cdev, &cmd, &rsp);
priv->rxq_db_offs = rsp.dboffs;
cmd.opcode = CNDM_CMD_OP_DESTROY_CQ;
cmd.flags = 0x00000000;
cmd.port = ndev->dev_port;
cmd.qn = 1;
cndm_exec_cmd(cdev, &cmd, &rsp);
cmd.opcode = CNDM_CMD_OP_DESTROY_SQ;
cmd.flags = 0x00000000;
cmd.port = ndev->dev_port;
cmd.qn = 0;
cndm_exec_cmd(cdev, &cmd, &rsp);
if (priv->rxcq) {
cndm_close_cq(priv->rxcq);
cndm_destroy_cq(priv->rxcq);
priv->rxcq = NULL;
}
if (priv->irq)
atomic_notifier_chain_unregister(&priv->irq->nh, &priv->irq_nb);
@@ -414,22 +339,5 @@ void cndm_destroy_netdev(struct net_device *ndev)
if (priv->registered)
unregister_netdev(ndev);
if (priv->tx_info) {
cndm_free_tx_buf(priv);
kvfree(priv->tx_info);
}
if (priv->rx_info) {
cndm_free_rx_buf(priv);
kvfree(priv->rx_info);
}
if (priv->txq_region)
dma_free_coherent(dev, priv->txq_region_len, priv->txq_region, priv->txq_region_addr);
if (priv->rxq_region)
dma_free_coherent(dev, priv->rxq_region_len, priv->rxq_region, priv->rxq_region_addr);
if (priv->txcq_region)
dma_free_coherent(dev, priv->txcq_region_len, priv->txcq_region, priv->txcq_region_addr);
if (priv->rxcq_region)
dma_free_coherent(dev, priv->rxcq_region_len, priv->rxcq_region, priv->rxcq_region_addr);
free_netdev(ndev);
}

View File

@@ -11,22 +11,22 @@ Authors:
#include "cndm.h"
#include <linux/version.h>
ktime_t cndm_read_cpl_ts(struct cndm_priv *priv, const struct cndm_cpl *cpl)
ktime_t cndm_read_cpl_ts(struct cndm_ring *ring, const struct cndm_cpl *cpl)
{
struct cndm_dev *cdev = priv->cdev;
struct cndm_dev *cdev = ring->cdev;
// u64 ts_s = le16_to_cpu(cpl->ts_s);
u64 ts_s = cpl->ts_s;
u32 ts_ns = le32_to_cpu(cpl->ts_ns);
if (unlikely(!priv->ts_valid || (priv->ts_s ^ ts_s) & 0xf0)) {
if (unlikely(!ring->ts_valid || (ring->ts_s ^ ts_s) & 0xf0)) {
// seconds MSBs do not match, update cached timestamp
priv->ts_s = ioread32(cdev->hw_addr + 0x0308);
priv->ts_s |= (u64) ioread32(cdev->hw_addr + 0x030C) << 32;
priv->ts_valid = 1;
ring->ts_s = ioread32(cdev->hw_addr + 0x0308);
ring->ts_s |= (u64) ioread32(cdev->hw_addr + 0x030C) << 32;
ring->ts_valid = 1;
}
ts_s |= priv->ts_s & 0xfffffffffffffff0;
ts_s |= ring->ts_s & 0xfffffffffffffff0;
dev_dbg(cdev->dev, "%s: Read timestamp: %lld.%09d", __func__, ts_s, ts_ns);

View File

@@ -0,0 +1,344 @@
// SPDX-License-Identifier: GPL
/*
Copyright (c) 2025-2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#include "cndm.h"
struct cndm_ring *cndm_create_rq(struct cndm_priv *priv)
{
struct cndm_ring *rq;
rq = kzalloc(sizeof(*rq), GFP_KERNEL);
if (!rq)
return ERR_PTR(-ENOMEM);
rq->cdev = priv->cdev;
rq->dev = priv->dev;
rq->priv = priv;
rq->index = -1;
rq->enabled = 0;
rq->prod_ptr = 0;
rq->cons_ptr = 0;
rq->db_offset = 0;
rq->db_addr = NULL;
return rq;
}
void cndm_destroy_rq(struct cndm_ring *rq)
{
cndm_close_rq(rq);
kfree(rq);
}
int cndm_open_rq(struct cndm_ring *rq, struct cndm_priv *priv, struct cndm_cq *cq, int size)
{
int ret = 0;
struct cndm_cmd_queue cmd;
struct cndm_cmd_queue rsp;
if (rq->enabled || rq->buf || !priv || !cq)
return -EINVAL;
rq->size = roundup_pow_of_two(size);
rq->size_mask = rq->size - 1;
rq->stride = 16;
rq->rx_info = kvzalloc(sizeof(*rq->rx_info) * rq->size, GFP_KERNEL);
if (!rq->rx_info)
ret = -ENOMEM;
rq->buf_size = rq->size * rq->stride;
rq->buf = dma_alloc_coherent(rq->dev, rq->buf_size, &rq->buf_dma_addr, GFP_KERNEL);
if (!rq->buf) {
return -ENOMEM;
goto fail;
}
rq->priv = priv;
rq->cq = cq;
cq->src_ring = rq;
// cq->handler = cndm_rx_irq;
rq->prod_ptr = 0;
rq->cons_ptr = 0;
cmd.opcode = CNDM_CMD_OP_CREATE_RQ;
cmd.flags = 0x00000000;
cmd.port = rq->priv->ndev->dev_port;
cmd.qn = 0;
cmd.qn2 = cq->cqn;
cmd.pd = 0;
cmd.size = ilog2(rq->size);
cmd.dboffs = 0;
cmd.ptr1 = rq->buf_dma_addr;
cmd.ptr2 = 0;
cndm_exec_cmd(rq->cdev, &cmd, &rsp);
rq->index = rsp.qn;
rq->db_offset = rsp.dboffs;
rq->db_addr = priv->cdev->hw_addr + rsp.dboffs;
rq->enabled = 1;
ret = cndm_refill_rx_buffers(rq);
if (ret) {
netdev_err(priv->ndev, "failed to allocate RX buffer for RX queue index %d (of %u total) entry index %u (of %u total)",
rq->index, priv->rxq_count, rq->prod_ptr, rq->size);
if (ret == -ENOMEM)
netdev_err(priv->ndev, "machine might not have enough DMA-capable RAM; try to decrease number of RX channels (currently %u) and/or RX ring parameters (entries; currently %u)",
priv->rxq_count, rq->size);
goto fail;
}
return 0;
fail:
cndm_close_rq(rq);
return ret;
}
void cndm_close_rq(struct cndm_ring *rq)
{
struct cndm_dev *cdev = rq->cdev;
struct cndm_cmd_queue cmd;
struct cndm_cmd_queue rsp;
rq->enabled = 0;
if (rq->cq) {
rq->cq->src_ring = NULL;
rq->cq->handler = NULL;
}
rq->cq = NULL;
if (rq->index != -1) {
cmd.opcode = CNDM_CMD_OP_DESTROY_RQ;
cmd.flags = 0x00000000;
cmd.port = rq->priv->ndev->dev_port;
cmd.qn = rq->index;
cndm_exec_cmd(cdev, &cmd, &rsp);
rq->index = -1;
}
if (rq->buf) {
cndm_free_rx_buf(rq);
dma_free_coherent(rq->dev, rq->buf_size, rq->buf, rq->buf_dma_addr);
rq->buf = NULL;
rq->buf_dma_addr = 0;
}
if (rq->rx_info) {
kvfree(rq->rx_info);
rq->rx_info = NULL;
}
rq->priv = NULL;
}
static void cndm_free_rx_desc(struct cndm_ring *rq, int index)
{
struct cndm_priv *priv = rq->priv;
struct device *dev = priv->dev;
struct cndm_rx_info *rx_info = &rq->rx_info[index];
netdev_dbg(priv->ndev, "Free RX desc index %d", index);
if (!rx_info->page)
return;
dma_unmap_page(dev, rx_info->dma_addr, rx_info->len, DMA_FROM_DEVICE);
rx_info->dma_addr = 0;
__free_pages(rx_info->page, 0);
rx_info->page = NULL;
}
int cndm_free_rx_buf(struct cndm_ring *rq)
{
u32 index;
int cnt = 0;
while (rq->prod_ptr != rq->cons_ptr) {
index = rq->cons_ptr & rq->size_mask;
cndm_free_rx_desc(rq, index);
rq->cons_ptr++;
cnt++;
}
return cnt;
}
static int cndm_prepare_rx_desc(struct cndm_ring *rq, int index)
{
struct cndm_priv *priv = rq->priv;
struct device *dev = rq->dev;
struct cndm_rx_info *rx_info = &rq->rx_info[index];
struct cndm_desc *rx_desc = (struct cndm_desc *)(rq->buf + index*16);
struct page *page;
u32 len = PAGE_SIZE;
dma_addr_t dma_addr;
netdev_dbg(priv->ndev, "Prepare RX desc index %d", index);
page = dev_alloc_pages(0);
if (unlikely(!page)) {
netdev_err(priv->ndev, "Failed to allocate page");
return -ENOMEM;
}
dma_addr = dma_map_page(dev, page, 0, len, DMA_FROM_DEVICE);
if (unlikely(dma_mapping_error(dev, dma_addr))) {
netdev_err(priv->ndev, "Mapping failed");
__free_pages(page, 0);
return -1;
}
rx_desc->len = cpu_to_le32(len);
rx_desc->addr = cpu_to_le64(dma_addr);
rx_info->page = page;
rx_info->len = len;
rx_info->dma_addr = dma_addr;
return 0;
}
int cndm_refill_rx_buffers(struct cndm_ring *rq)
{
u32 missing = 128 - (rq->prod_ptr - rq->cons_ptr); // TODO
int ret = 0;
if (missing < 8)
return 0;
for (; missing-- > 0;) {
ret = cndm_prepare_rx_desc(rq, rq->prod_ptr & rq->size_mask);
if (ret)
break;
rq->prod_ptr++;
}
dma_wmb();
iowrite32(rq->prod_ptr & 0xffff, rq->db_addr);
return ret;
}
static int cndm_process_rx_cq(struct cndm_cq *cq, int napi_budget)
{
struct cndm_priv *priv = cq->priv;
struct cndm_ring *rq = cq->src_ring;
struct cndm_cpl *cpl;
struct cndm_rx_info *rx_info;
struct sk_buff *skb;
struct page *page;
int done = 0;
u32 len;
u32 cq_cons_ptr;
u32 cq_index;
u32 cons_ptr;
u32 index;
cq_cons_ptr = cq->cons_ptr;
cons_ptr = rq->cons_ptr;
while (done < napi_budget) {
cq_index = cq_cons_ptr & cq->size_mask;
cpl = (struct cndm_cpl *)(cq->buf + cq_index * 16);
if (!!(cpl->phase & 0x80) == !!(cq_cons_ptr & cq->size))
break;
dma_rmb();
index = cons_ptr & rq->size_mask;
rx_info = &rq->rx_info[index];
page = rx_info->page;
len = min_t(u32, le16_to_cpu(cpl->len), rx_info->len);
netdev_dbg(priv->ndev, "Process RX cpl index %d", index);
if (!page) {
netdev_err(priv->ndev, "Null page at index %d", index);
break;
}
dma_unmap_page(priv->dev, rx_info->dma_addr, rx_info->len, DMA_FROM_DEVICE);
rx_info->dma_addr = 0;
rx_info->page = NULL;
if (len < ETH_HLEN) {
netdev_warn(priv->ndev, "Dropping short frame (len %d)", len);
__free_pages(page, 0);
goto rx_drop;
}
skb = napi_get_frags(&cq->napi);
if (!skb) {
netdev_err(priv->ndev, "Failed to allocate skb %d", index);
__free_pages(page, 0);
goto rx_drop;
}
// RX hardware timestamp
skb_hwtstamps(skb)->hwtstamp = cndm_read_cpl_ts(rq, cpl);
__skb_fill_page_desc(skb, 0, page, 0, len);
skb_shinfo(skb)->nr_frags = 1;
skb->len = len;
skb->data_len = len;
skb->truesize = rx_info->len;
napi_gro_frags(&cq->napi);
rx_drop:
done++;
cq_cons_ptr++;
cons_ptr++;
}
cq->cons_ptr = cq_cons_ptr;
rq->cons_ptr = cons_ptr;
cndm_refill_rx_buffers(rq);
return done;
}
int cndm_poll_rx_cq(struct napi_struct *napi, int budget)
{
struct cndm_cq *cq = container_of(napi, struct cndm_cq, napi);
int done;
done = cndm_process_rx_cq(cq, budget);
if (done == budget)
return done;
napi_complete(napi);
// TODO re-enable interrupts
return done;
}

View File

@@ -1,198 +0,0 @@
// SPDX-License-Identifier: GPL
/*
Copyright (c) 2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#include "cndm.h"
static void cndm_free_rx_desc(struct cndm_priv *priv, int index)
{
struct device *dev = priv->dev;
struct cndm_rx_info *rx_info = &priv->rx_info[index];
netdev_dbg(priv->ndev, "Free RX desc index %d", index);
if (!rx_info->page)
return;
dma_unmap_page(dev, rx_info->dma_addr, rx_info->len, DMA_FROM_DEVICE);
rx_info->dma_addr = 0;
__free_pages(rx_info->page, 0);
rx_info->page = NULL;
}
int cndm_free_rx_buf(struct cndm_priv *priv)
{
u32 index;
int cnt = 0;
while (priv->rxq_prod != priv->rxq_cons) {
index = priv->rxq_cons & priv->rxq_mask;
cndm_free_rx_desc(priv, index);
priv->rxq_cons++;
cnt++;
}
return cnt;
}
static int cndm_prepare_rx_desc(struct cndm_priv *priv, int index)
{
struct device *dev = priv->dev;
struct cndm_rx_info *rx_info = &priv->rx_info[index];
struct cndm_desc *rx_desc = (struct cndm_desc *)(priv->rxq_region + index*16);
struct page *page;
u32 len = PAGE_SIZE;
dma_addr_t dma_addr;
netdev_dbg(priv->ndev, "Prepare RX desc index %d", index);
page = dev_alloc_pages(0);
if (unlikely(!page)) {
netdev_err(priv->ndev, "Failed to allocate page");
return -ENOMEM;
}
dma_addr = dma_map_page(dev, page, 0, len, DMA_FROM_DEVICE);
if (unlikely(dma_mapping_error(dev, dma_addr))) {
netdev_err(priv->ndev, "Mapping failed");
__free_pages(page, 0);
return -1;
}
rx_desc->len = cpu_to_le32(len);
rx_desc->addr = cpu_to_le64(dma_addr);
rx_info->page = page;
rx_info->len = len;
rx_info->dma_addr = dma_addr;
return 0;
}
int cndm_refill_rx_buffers(struct cndm_priv *priv)
{
u32 missing = 128 - (priv->rxq_prod - priv->rxq_cons); // TODO
int ret = 0;
if (missing < 8)
return 0;
for (; missing-- > 0;) {
ret = cndm_prepare_rx_desc(priv, priv->rxq_prod & priv->rxq_mask);
if (ret)
break;
priv->rxq_prod++;
}
dma_wmb();
iowrite32(priv->rxq_prod & 0xffff, priv->hw_addr + priv->rxq_db_offs);
return ret;
}
static int cndm_process_rx_cq(struct net_device *ndev, int napi_budget)
{
struct cndm_priv *priv = netdev_priv(ndev);
struct cndm_cpl *cpl;
struct cndm_rx_info *rx_info;
struct sk_buff *skb;
struct page *page;
int done = 0;
u32 len;
u32 cq_cons_ptr;
u32 cq_index;
u32 cons_ptr;
u32 index;
cq_cons_ptr = priv->rxcq_cons;
cons_ptr = priv->rxq_cons;
while (done < napi_budget) {
cq_index = cq_cons_ptr & priv->rxcq_mask;
cpl = (struct cndm_cpl *)(priv->rxcq_region + cq_index * 16);
if (!!(cpl->phase & 0x80) == !!(cq_cons_ptr & priv->rxcq_size))
break;
dma_rmb();
index = cons_ptr & priv->rxq_mask;
rx_info = &priv->rx_info[index];
page = rx_info->page;
len = min_t(u32, le16_to_cpu(cpl->len), rx_info->len);
netdev_dbg(priv->ndev, "Process RX cpl index %d", index);
if (!page) {
netdev_err(priv->ndev, "Null page at index %d", index);
break;
}
dma_unmap_page(priv->dev, rx_info->dma_addr, rx_info->len, DMA_FROM_DEVICE);
rx_info->dma_addr = 0;
rx_info->page = NULL;
if (len < ETH_HLEN) {
netdev_warn(priv->ndev, "Dropping short frame (len %d)", len);
__free_pages(page, 0);
goto rx_drop;
}
skb = napi_get_frags(&priv->rx_napi);
if (!skb) {
netdev_err(priv->ndev, "Failed to allocate skb %d", index);
__free_pages(page, 0);
goto rx_drop;
}
// RX hardware timestamp
skb_hwtstamps(skb)->hwtstamp = cndm_read_cpl_ts(priv, cpl);
__skb_fill_page_desc(skb, 0, page, 0, len);
skb_shinfo(skb)->nr_frags = 1;
skb->len = len;
skb->data_len = len;
skb->truesize = rx_info->len;
napi_gro_frags(&priv->rx_napi);
rx_drop:
done++;
cq_cons_ptr++;
cons_ptr++;
}
priv->rxcq_cons = cq_cons_ptr;
priv->rxq_cons = cons_ptr;
cndm_refill_rx_buffers(priv);
return done;
}
int cndm_poll_rx_cq(struct napi_struct *napi, int budget)
{
struct cndm_priv *priv = container_of(napi, struct cndm_priv, rx_napi);
int done;
done = cndm_process_rx_cq(priv->ndev, budget);
if (done == budget)
return done;
napi_complete(napi);
// TODO re-enable interrupts
return done;
}

View File

@@ -0,0 +1,315 @@
// SPDX-License-Identifier: GPL
/*
Copyright (c) 2025-2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#include "cndm.h"
struct cndm_ring *cndm_create_sq(struct cndm_priv *priv)
{
struct cndm_ring *sq;
sq = kzalloc(sizeof(*sq), GFP_KERNEL);
if (!sq)
return ERR_PTR(-ENOMEM);
sq->cdev = priv->cdev;
sq->dev = priv->dev;
sq->priv = priv;
sq->index = -1;
sq->enabled = 0;
sq->prod_ptr = 0;
sq->cons_ptr = 0;
sq->db_offset = 0;
sq->db_addr = NULL;
return sq;
}
void cndm_destroy_sq(struct cndm_ring *sq)
{
cndm_close_sq(sq);
kfree(sq);
}
int cndm_open_sq(struct cndm_ring *sq, struct cndm_priv *priv, struct cndm_cq *cq, int size)
{
int ret = 0;
struct cndm_cmd_queue cmd;
struct cndm_cmd_queue rsp;
if (sq->enabled || sq->buf || !priv || !cq)
return -EINVAL;
sq->size = roundup_pow_of_two(size);
sq->size_mask = sq->size - 1;
sq->stride = 16;
sq->tx_info = kvzalloc(sizeof(*sq->tx_info) * sq->size, GFP_KERNEL);
if (!sq->tx_info)
ret = -ENOMEM;
sq->buf_size = sq->size * sq->stride;
sq->buf = dma_alloc_coherent(sq->dev, sq->buf_size, &sq->buf_dma_addr, GFP_KERNEL);
if (!sq->buf) {
return -ENOMEM;
goto fail;
}
sq->priv = priv;
sq->cq = cq;
cq->src_ring = sq;
// cq->handler = cndm_tx_irq;
sq->prod_ptr = 0;
sq->cons_ptr = 0;
cmd.opcode = CNDM_CMD_OP_CREATE_SQ;
cmd.flags = 0x00000000;
cmd.port = sq->priv->ndev->dev_port;
cmd.qn = 0;
cmd.qn2 = cq->cqn;
cmd.pd = 0;
cmd.size = ilog2(sq->size);
cmd.dboffs = 0;
cmd.ptr1 = sq->buf_dma_addr;
cmd.ptr2 = 0;
cndm_exec_cmd(sq->cdev, &cmd, &rsp);
sq->index = rsp.qn;
sq->db_offset = rsp.dboffs;
sq->db_addr = priv->cdev->hw_addr + rsp.dboffs;
sq->enabled = 1;
return 0;
fail:
cndm_close_sq(sq);
return ret;
}
void cndm_close_sq(struct cndm_ring *sq)
{
struct cndm_dev *cdev = sq->cdev;
struct cndm_cmd_queue cmd;
struct cndm_cmd_queue rsp;
sq->enabled = 0;
if (sq->cq) {
sq->cq->src_ring = NULL;
sq->cq->handler = NULL;
}
sq->cq = NULL;
if (sq->index != -1) {
cmd.opcode = CNDM_CMD_OP_DESTROY_SQ;
cmd.flags = 0x00000000;
cmd.port = sq->priv->ndev->dev_port;
cmd.qn = sq->index;
cndm_exec_cmd(cdev, &cmd, &rsp);
sq->index = -1;
}
if (sq->buf) {
cndm_free_tx_buf(sq);
dma_free_coherent(sq->dev, sq->buf_size, sq->buf, sq->buf_dma_addr);
sq->buf = NULL;
sq->buf_dma_addr = 0;
}
if (sq->tx_info) {
kvfree(sq->tx_info);
sq->tx_info = NULL;
}
sq->priv = NULL;
}
static void cndm_free_tx_desc(struct cndm_ring *sq, int index, int napi_budget)
{
struct cndm_priv *priv = sq->priv;
struct device *dev = priv->dev;
struct cndm_tx_info *tx_info = &sq->tx_info[index];
struct sk_buff *skb = tx_info->skb;
netdev_dbg(priv->ndev, "Free TX desc index %d", index);
dma_unmap_single(dev, tx_info->dma_addr, tx_info->len, DMA_TO_DEVICE);
tx_info->dma_addr = 0;
napi_consume_skb(skb, napi_budget);
tx_info->skb = NULL;
}
int cndm_free_tx_buf(struct cndm_ring *sq)
{
u32 index;
int cnt = 0;
while (sq->prod_ptr != sq->cons_ptr) {
index = sq->cons_ptr & sq->size_mask;
cndm_free_tx_desc(sq, index, 0);
sq->cons_ptr++;
cnt++;
}
return cnt;
}
static int cndm_process_tx_cq(struct cndm_cq *cq, int napi_budget)
{
struct cndm_priv *priv = cq->priv;
struct cndm_ring *sq = cq->src_ring;
struct cndm_tx_info *tx_info;
struct cndm_cpl *cpl;
struct skb_shared_hwtstamps hwts;
int done = 0;
u32 cq_cons_ptr;
u32 cq_index;
u32 cons_ptr;
u32 index;
cq_cons_ptr = cq->cons_ptr;
cons_ptr = sq->cons_ptr;
while (done < napi_budget) {
cq_index = cq_cons_ptr & cq->size_mask;
cpl = (struct cndm_cpl *)(cq->buf + cq_index * 16);
if (!!(cpl->phase & 0x80) == !!(cq_cons_ptr & cq->size))
break;
dma_rmb();
index = cons_ptr & sq->size_mask;
tx_info = &sq->tx_info[index];
// TX hardware timestamp
if (unlikely(tx_info->ts_requested)) {
netdev_dbg(priv->ndev, "%s: TX TS requested", __func__);
hwts.hwtstamp = cndm_read_cpl_ts(sq, cpl);
skb_tstamp_tx(tx_info->skb, &hwts);
}
cndm_free_tx_desc(sq, index, napi_budget);
done++;
cq_cons_ptr++;
cons_ptr++;
}
cq->cons_ptr = cq_cons_ptr;
sq->cons_ptr = cons_ptr;
if (netif_tx_queue_stopped(sq->tx_queue) && (done != 0 || sq->prod_ptr == sq->cons_ptr))
netif_tx_wake_queue(sq->tx_queue);
return done;
}
int cndm_poll_tx_cq(struct napi_struct *napi, int budget)
{
struct cndm_cq *cq = container_of(napi, struct cndm_cq, napi);
int done;
done = cndm_process_tx_cq(cq, budget);
if (done == budget)
return done;
napi_complete(napi);
// TODO re-enable interrupts
return done;
}
int cndm_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct skb_shared_info *shinfo = skb_shinfo(skb);
struct cndm_priv *priv = netdev_priv(ndev);
struct cndm_ring *sq = priv->txq;
struct device *dev = priv->dev;
u32 index;
u32 cons_ptr;
u32 len;
dma_addr_t dma_addr;
struct cndm_desc *tx_desc;
struct cndm_tx_info *tx_info;
netdev_dbg(ndev, "Got packet for TX");
if (skb->len < ETH_HLEN) {
netdev_warn(ndev, "Dropping short frame");
goto tx_drop;
}
cons_ptr = READ_ONCE(sq->cons_ptr);
index = sq->prod_ptr & sq->size_mask;
tx_desc = (struct cndm_desc *)(sq->buf + index*16);
tx_info = &sq->tx_info[index];
// TX hardware timestamp
tx_info->ts_requested = 0;
if (unlikely(shinfo->tx_flags & SKBTX_HW_TSTAMP)) {
netdev_dbg(ndev, "%s: TX TS requested", __func__);
shinfo->tx_flags |= SKBTX_IN_PROGRESS;
tx_info->ts_requested = 1;
}
len = skb_headlen(skb);
dma_addr = dma_map_single(dev, skb->data, len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev, dma_addr))) {
netdev_err(ndev, "Mapping failed");
goto tx_drop;
}
tx_desc->len = cpu_to_le32(len);
tx_desc->addr = cpu_to_le64(dma_addr);
tx_info->skb = skb;
tx_info->len = len;
tx_info->dma_addr = dma_addr;
netdev_dbg(ndev, "Write desc index %d len %d", index, len);
sq->prod_ptr++;
skb_tx_timestamp(skb);
if (sq->prod_ptr - sq->cons_ptr >= 128) {
netdev_dbg(ndev, "TX ring full");
netif_tx_stop_queue(sq->tx_queue);
}
dma_wmb();
iowrite32(sq->prod_ptr & 0xffff, sq->db_addr);
return NETDEV_TX_OK;
tx_drop:
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}

View File

@@ -1,180 +0,0 @@
// SPDX-License-Identifier: GPL
/*
Copyright (c) 2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#include "cndm.h"
static void cndm_free_tx_desc(struct cndm_priv *priv, int index, int napi_budget)
{
struct device *dev = priv->dev;
struct cndm_tx_info *tx_info = &priv->tx_info[index];
struct sk_buff *skb = tx_info->skb;
netdev_dbg(priv->ndev, "Free TX desc index %d", index);
dma_unmap_single(dev, tx_info->dma_addr, tx_info->len, DMA_TO_DEVICE);
tx_info->dma_addr = 0;
napi_consume_skb(skb, napi_budget);
tx_info->skb = NULL;
}
int cndm_free_tx_buf(struct cndm_priv *priv)
{
u32 index;
int cnt = 0;
while (priv->txq_prod != priv->txq_cons) {
index = priv->txq_cons & priv->txq_mask;
cndm_free_tx_desc(priv, index, 0);
priv->txq_cons++;
cnt++;
}
return cnt;
}
static int cndm_process_tx_cq(struct net_device *ndev, int napi_budget)
{
struct cndm_priv *priv = netdev_priv(ndev);
struct cndm_tx_info *tx_info;
struct cndm_cpl *cpl;
struct skb_shared_hwtstamps hwts;
int done = 0;
u32 cq_cons_ptr;
u32 cq_index;
u32 cons_ptr;
u32 index;
cq_cons_ptr = priv->txcq_cons;
cons_ptr = priv->txq_cons;
while (done < napi_budget) {
cq_index = cq_cons_ptr & priv->txcq_mask;
cpl = (struct cndm_cpl *)(priv->txcq_region + cq_index * 16);
if (!!(cpl->phase & 0x80) == !!(cq_cons_ptr & priv->txcq_size))
break;
dma_rmb();
index = cons_ptr & priv->txq_mask;
tx_info = &priv->tx_info[index];
// TX hardware timestamp
if (unlikely(tx_info->ts_requested)) {
netdev_dbg(priv->ndev, "%s: TX TS requested", __func__);
hwts.hwtstamp = cndm_read_cpl_ts(priv, cpl);
skb_tstamp_tx(tx_info->skb, &hwts);
}
cndm_free_tx_desc(priv, index, napi_budget);
done++;
cq_cons_ptr++;
cons_ptr++;
}
priv->txcq_cons = cq_cons_ptr;
priv->txq_cons = cons_ptr;
if (netif_tx_queue_stopped(priv->tx_queue) && (done != 0 || priv->txq_prod == priv->txq_cons))
netif_tx_wake_queue(priv->tx_queue);
return done;
}
int cndm_poll_tx_cq(struct napi_struct *napi, int budget)
{
struct cndm_priv *priv = container_of(napi, struct cndm_priv, tx_napi);
int done;
done = cndm_process_tx_cq(priv->ndev, budget);
if (done == budget)
return done;
napi_complete(napi);
// TODO re-enable interrupts
return done;
}
int cndm_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct skb_shared_info *shinfo = skb_shinfo(skb);
struct cndm_priv *priv = netdev_priv(ndev);
struct device *dev = priv->dev;
u32 index;
u32 cons_ptr;
u32 len;
dma_addr_t dma_addr;
struct cndm_desc *tx_desc;
struct cndm_tx_info *tx_info;
netdev_dbg(ndev, "Got packet for TX");
if (skb->len < ETH_HLEN) {
netdev_warn(ndev, "Dropping short frame");
goto tx_drop;
}
cons_ptr = READ_ONCE(priv->txq_cons);
index = priv->txq_prod & priv->txq_mask;
tx_desc = (struct cndm_desc *)(priv->txq_region + index*16);
tx_info = &priv->tx_info[index];
// TX hardware timestamp
tx_info->ts_requested = 0;
if (unlikely(shinfo->tx_flags & SKBTX_HW_TSTAMP)) {
netdev_dbg(ndev, "%s: TX TS requested", __func__);
shinfo->tx_flags |= SKBTX_IN_PROGRESS;
tx_info->ts_requested = 1;
}
len = skb_headlen(skb);
dma_addr = dma_map_single(dev, skb->data, len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev, dma_addr))) {
netdev_err(ndev, "Mapping failed");
goto tx_drop;
}
tx_desc->len = cpu_to_le32(len);
tx_desc->addr = cpu_to_le64(dma_addr);
tx_info->skb = skb;
tx_info->len = len;
tx_info->dma_addr = dma_addr;
netdev_dbg(ndev, "Write desc index %d len %d", index, len);
priv->txq_prod++;
skb_tx_timestamp(skb);
if (priv->txq_prod - priv->txq_cons >= 128) {
netdev_dbg(ndev, "TX ring full");
netif_tx_stop_queue(priv->tx_queue);
}
dma_wmb();
iowrite32(priv->txq_prod & 0xffff, priv->hw_addr + priv->txq_db_offs);
return NETDEV_TX_OK;
tx_drop:
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}

View File

@@ -20,6 +20,17 @@ module cndm_micro_cpl_wr
input wire logic clk,
input wire logic rst,
/*
* Control register interface
*/
taxi_axil_if.wr_slv s_axil_ctrl_wr,
taxi_axil_if.rd_slv s_axil_ctrl_rd,
/*
* Datapath control register interface
*/
taxi_apb_if.slv s_apb_dp_ctrl,
/*
* DMA
*/
@@ -27,54 +38,185 @@ module cndm_micro_cpl_wr
taxi_dma_desc_if.sts_snk dma_wr_desc_sts,
taxi_dma_ram_if.rd_slv dma_ram_rd,
input wire logic txcq_en,
input wire logic [3:0] txcq_size,
input wire logic [63:0] txcq_base_addr,
output wire logic [15:0] txcq_prod,
input wire logic rxcq_en,
input wire logic [3:0] rxcq_size,
input wire logic [63:0] rxcq_base_addr,
output wire logic [15:0] rxcq_prod,
taxi_axis_if.snk axis_cpl[2],
taxi_axis_if.snk s_axis_cpl,
output wire logic irq
);
taxi_axis_if #(
.DATA_W(axis_cpl[0].DATA_W),
.KEEP_EN(axis_cpl[0].KEEP_EN),
.KEEP_W(axis_cpl[0].KEEP_W),
.STRB_EN(axis_cpl[0].STRB_EN),
.LAST_EN(axis_cpl[0].LAST_EN),
.ID_EN(1),
.ID_W(1),
.DEST_EN(axis_cpl[0].DEST_EN),
.DEST_W(axis_cpl[0].DEST_W),
.USER_EN(axis_cpl[0].USER_EN),
.USER_W(axis_cpl[0].USER_W)
) cpl_comb();
localparam AXIL_ADDR_W = s_axil_ctrl_wr.ADDR_W;
localparam AXIL_DATA_W = s_axil_ctrl_wr.DATA_W;
localparam [2:0]
STATE_IDLE = 0,
STATE_RX_CPL = 1,
STATE_WRITE_DATA = 2;
localparam APB_ADDR_W = s_apb_dp_ctrl.ADDR_W;
localparam APB_DATA_W = s_apb_dp_ctrl.DATA_W;
logic [2:0] state_reg = STATE_IDLE;
logic txcq_en_reg = '0;
logic [3:0] txcq_size_reg = '0;
logic [63:0] txcq_base_addr_reg = '0;
logic rxcq_en_reg = '0;
logic [3:0] rxcq_size_reg = '0;
logic [63:0] rxcq_base_addr_reg = '0;
logic [15:0] txcq_prod_ptr_reg = '0;
logic [15:0] rxcq_prod_ptr_reg = '0;
logic s_axil_ctrl_awready_reg = 1'b0;
logic s_axil_ctrl_wready_reg = 1'b0;
logic s_axil_ctrl_bvalid_reg = 1'b0;
logic s_axil_ctrl_arready_reg = 1'b0;
logic [AXIL_DATA_W-1:0] s_axil_ctrl_rdata_reg = '0;
logic s_axil_ctrl_rvalid_reg = 1'b0;
assign s_axil_ctrl_wr.awready = s_axil_ctrl_awready_reg;
assign s_axil_ctrl_wr.wready = s_axil_ctrl_wready_reg;
assign s_axil_ctrl_wr.bresp = '0;
assign s_axil_ctrl_wr.buser = '0;
assign s_axil_ctrl_wr.bvalid = s_axil_ctrl_bvalid_reg;
assign s_axil_ctrl_rd.arready = s_axil_ctrl_arready_reg;
assign s_axil_ctrl_rd.rdata = s_axil_ctrl_rdata_reg;
assign s_axil_ctrl_rd.rresp = '0;
assign s_axil_ctrl_rd.ruser = '0;
assign s_axil_ctrl_rd.rvalid = s_axil_ctrl_rvalid_reg;
logic s_apb_dp_ctrl_pready_reg = 1'b0;
logic [AXIL_DATA_W-1:0] s_apb_dp_ctrl_prdata_reg = '0;
assign s_apb_dp_ctrl.pready = s_apb_dp_ctrl_pready_reg;
assign s_apb_dp_ctrl.prdata = s_apb_dp_ctrl_prdata_reg;
assign s_apb_dp_ctrl.pslverr = 1'b0;
assign s_apb_dp_ctrl.pruser = '0;
assign s_apb_dp_ctrl.pbuser = '0;
always_ff @(posedge clk) begin
s_axil_ctrl_awready_reg <= 1'b0;
s_axil_ctrl_wready_reg <= 1'b0;
s_axil_ctrl_bvalid_reg <= s_axil_ctrl_bvalid_reg && !s_axil_ctrl_wr.bready;
s_axil_ctrl_arready_reg <= 1'b0;
s_axil_ctrl_rvalid_reg <= s_axil_ctrl_rvalid_reg && !s_axil_ctrl_rd.rready;
s_apb_dp_ctrl_pready_reg <= 1'b0;
if (s_axil_ctrl_wr.awvalid && s_axil_ctrl_wr.wvalid && !s_axil_ctrl_bvalid_reg) begin
s_axil_ctrl_awready_reg <= 1'b1;
s_axil_ctrl_wready_reg <= 1'b1;
s_axil_ctrl_bvalid_reg <= 1'b1;
// case ({s_axil_ctrl_wr.awaddr[9:2], 2'b00})
// 10'h000: begin
// txcq_en_reg <= s_axil_ctrl_wr.wdata[0];
// txcq_size_reg <= s_axil_ctrl_wr.wdata[19:16];
// end
// 10'h008: txcq_base_addr_reg[31:0] <= s_axil_ctrl_wr.wdata;
// 10'h00c: txcq_base_addr_reg[63:32] <= s_axil_ctrl_wr.wdata;
// 10'h100: begin
// rxcq_en_reg <= s_axil_ctrl_wr.wdata[0];
// rxcq_size_reg <= s_axil_ctrl_wr.wdata[19:16];
// end
// 10'h108: rxcq_base_addr_reg[31:0] <= s_axil_ctrl_wr.wdata;
// 10'h10c: rxcq_base_addr_reg[63:32] <= s_axil_ctrl_wr.wdata;
// default: begin end
// endcase
end
if (s_axil_ctrl_rd.arvalid && !s_axil_ctrl_rvalid_reg) begin
s_axil_ctrl_rdata_reg <= '0;
s_axil_ctrl_arready_reg <= 1'b1;
s_axil_ctrl_rvalid_reg <= 1'b1;
// case ({s_axil_ctrl_rd.araddr[9:2], 2'b00})
// 10'h000: begin
// s_axil_ctrl_rdata_reg[0] <= txcq_en_reg;
// s_axil_ctrl_rdata_reg[19:16] <= txcq_size_reg;
// end
// 10'h004: s_axil_ctrl_rdata_reg[15:0] <= txcq_prod_ptr_reg;
// 10'h008: s_axil_ctrl_rdata_reg <= txcq_base_addr_reg[31:0];
// 10'h00c: s_axil_ctrl_rdata_reg <= txcq_base_addr_reg[63:32];
// 10'h100: begin
// s_axil_ctrl_rdata_reg[0] <= rxcq_en_reg;
// s_axil_ctrl_rdata_reg[19:16] <= rxcq_size_reg;
// end
// 10'h104: s_axil_ctrl_rdata_reg[15:0] <= rxcq_prod_ptr_reg;
// 10'h108: s_axil_ctrl_rdata_reg <= rxcq_base_addr_reg[31:0];
// 10'h10c: s_axil_ctrl_rdata_reg <= rxcq_base_addr_reg[63:32];
// default: begin end
// endcase
end
if (s_apb_dp_ctrl.penable && s_apb_dp_ctrl.psel && !s_apb_dp_ctrl_pready_reg) begin
s_apb_dp_ctrl_pready_reg <= 1'b1;
s_apb_dp_ctrl_prdata_reg <= '0;
if (s_apb_dp_ctrl.pwrite) begin
case ({s_apb_dp_ctrl.paddr[9:2], 2'b00})
10'h000: begin
txcq_en_reg <= s_apb_dp_ctrl.pwdata[0];
txcq_size_reg <= s_apb_dp_ctrl.pwdata[19:16];
end
10'h008: txcq_base_addr_reg[31:0] <= s_apb_dp_ctrl.pwdata;
10'h00c: txcq_base_addr_reg[63:32] <= s_apb_dp_ctrl.pwdata;
10'h100: begin
rxcq_en_reg <= s_apb_dp_ctrl.pwdata[0];
rxcq_size_reg <= s_apb_dp_ctrl.pwdata[19:16];
end
10'h108: rxcq_base_addr_reg[31:0] <= s_apb_dp_ctrl.pwdata;
10'h10c: rxcq_base_addr_reg[63:32] <= s_apb_dp_ctrl.pwdata;
default: begin end
endcase
end
case ({s_apb_dp_ctrl.paddr[9:2], 2'b00})
10'h000: begin
s_apb_dp_ctrl_prdata_reg[0] <= txcq_en_reg;
s_apb_dp_ctrl_prdata_reg[19:16] <= txcq_size_reg;
end
10'h004: s_apb_dp_ctrl_prdata_reg[15:0] <= txcq_prod_ptr_reg;
10'h008: s_apb_dp_ctrl_prdata_reg <= txcq_base_addr_reg[31:0];
10'h00c: s_apb_dp_ctrl_prdata_reg <= txcq_base_addr_reg[63:32];
10'h100: begin
s_apb_dp_ctrl_prdata_reg[0] <= rxcq_en_reg;
s_apb_dp_ctrl_prdata_reg[19:16] <= rxcq_size_reg;
end
10'h104: s_apb_dp_ctrl_prdata_reg[15:0] <= rxcq_prod_ptr_reg;
10'h108: s_apb_dp_ctrl_prdata_reg <= rxcq_base_addr_reg[31:0];
10'h10c: s_apb_dp_ctrl_prdata_reg <= rxcq_base_addr_reg[63:32];
default: begin end
endcase
end
if (rst) begin
s_axil_ctrl_awready_reg <= 1'b0;
s_axil_ctrl_wready_reg <= 1'b0;
s_axil_ctrl_bvalid_reg <= 1'b0;
s_axil_ctrl_arready_reg <= 1'b0;
s_axil_ctrl_rvalid_reg <= 1'b0;
s_apb_dp_ctrl_pready_reg <= 1'b0;
end
end
typedef enum logic [1:0] {
STATE_IDLE,
STATE_RX_CPL,
STATE_WRITE_DATA
} state_t;
state_t state_reg = STATE_IDLE;
logic phase_tag_reg = 1'b0;
logic irq_reg = 1'b0;
assign txcq_prod = txcq_prod_ptr_reg;
assign rxcq_prod = rxcq_prod_ptr_reg;
assign irq = irq_reg;
always_ff @(posedge clk) begin
cpl_comb.tready <= 1'b0;
s_axis_cpl.tready <= 1'b0;
dma_wr_desc_req.req_src_sel <= '0;
dma_wr_desc_req.req_src_asid <= '0;
@@ -89,11 +231,11 @@ always_ff @(posedge clk) begin
dma_wr_desc_req.req_user <= '0;
dma_wr_desc_req.req_valid <= dma_wr_desc_req.req_valid && !dma_wr_desc_req.req_ready;
if (!txcq_en) begin
if (!txcq_en_reg) begin
txcq_prod_ptr_reg <= '0;
end
if (!rxcq_en) begin
if (!rxcq_en_reg) begin
rxcq_prod_ptr_reg <= '0;
end
@@ -103,12 +245,12 @@ always_ff @(posedge clk) begin
STATE_IDLE: begin
dma_wr_desc_req.req_src_addr <= '0;
if (cpl_comb.tid == 0) begin
dma_wr_desc_req.req_dst_addr <= txcq_base_addr + 64'(16'(txcq_prod_ptr_reg & ({16{1'b1}} >> (16 - txcq_size))) * 16);
phase_tag_reg <= !txcq_prod_ptr_reg[txcq_size];
if (cpl_comb.tvalid && !cpl_comb.tready) begin
if (s_axis_cpl.tdest == 0) begin
dma_wr_desc_req.req_dst_addr <= txcq_base_addr_reg + 64'(16'(txcq_prod_ptr_reg & ({16{1'b1}} >> (16 - txcq_size_reg))) * 16);
phase_tag_reg <= !txcq_prod_ptr_reg[txcq_size_reg];
if (s_axis_cpl.tvalid && !s_axis_cpl.tready) begin
txcq_prod_ptr_reg <= txcq_prod_ptr_reg + 1;
if (txcq_en) begin
if (txcq_en_reg) begin
dma_wr_desc_req.req_valid <= 1'b1;
state_reg <= STATE_WRITE_DATA;
end else begin
@@ -116,11 +258,11 @@ always_ff @(posedge clk) begin
end
end
end else begin
dma_wr_desc_req.req_dst_addr <= rxcq_base_addr + 64'(16'(rxcq_prod_ptr_reg & ({16{1'b1}} >> (16 - rxcq_size))) * 16);
phase_tag_reg <= !rxcq_prod_ptr_reg[rxcq_size];
if (cpl_comb.tvalid && !cpl_comb.tready) begin
dma_wr_desc_req.req_dst_addr <= rxcq_base_addr_reg + 64'(16'(rxcq_prod_ptr_reg & ({16{1'b1}} >> (16 - rxcq_size_reg))) * 16);
phase_tag_reg <= !rxcq_prod_ptr_reg[rxcq_size_reg];
if (s_axis_cpl.tvalid && !s_axis_cpl.tready) begin
rxcq_prod_ptr_reg <= rxcq_prod_ptr_reg + 1;
if (rxcq_en) begin
if (rxcq_en_reg) begin
dma_wr_desc_req.req_valid <= 1'b1;
state_reg <= STATE_WRITE_DATA;
end else begin
@@ -131,7 +273,7 @@ always_ff @(posedge clk) begin
end
STATE_WRITE_DATA: begin
if (dma_wr_desc_sts.sts_valid) begin
cpl_comb.tready <= 1'b1;
s_axis_cpl.tready <= 1'b1;
irq_reg <= 1'b1;
state_reg <= STATE_IDLE;
end
@@ -149,27 +291,6 @@ always_ff @(posedge clk) begin
end
end
taxi_axis_arb_mux #(
.S_COUNT(2),
.UPDATE_TID(1),
.ARB_ROUND_ROBIN(1),
.ARB_LSB_HIGH_PRIO(1)
)
mux_inst (
.clk(clk),
.rst(rst),
/*
* AXI4-Stream input (sink)
*/
.s_axis(axis_cpl),
/*
* AXI4-Stream output (source)
*/
.m_axis(cpl_comb)
);
// extract parameters
localparam SEGS = dma_ram_rd.SEGS;
localparam SEG_ADDR_W = dma_ram_rd.SEG_ADDR_W;
@@ -179,7 +300,7 @@ localparam SEG_BE_W = dma_ram_rd.SEG_BE_W;
if (SEGS*SEG_DATA_W < 128)
$fatal(0, "Total segmented interface width must be at least 128 (instance %m)");
wire [SEGS-1:0][SEG_DATA_W-1:0] ram_data = (SEG_DATA_W*SEGS)'({phase_tag_reg, cpl_comb.tdata[126:0]});
wire [SEGS-1:0][SEG_DATA_W-1:0] ram_data = (SEG_DATA_W*SEGS)'({phase_tag_reg, s_axis_cpl.tdata[126:0]});
for (genvar n = 0; n < SEGS; n = n + 1) begin

View File

@@ -20,6 +20,17 @@ module cndm_micro_desc_rd
input wire logic clk,
input wire logic rst,
/*
* Control register interface
*/
taxi_axil_if.wr_slv s_axil_ctrl_wr,
taxi_axil_if.rd_slv s_axil_ctrl_rd,
/*
* Datapath control register interface
*/
taxi_apb_if.slv s_apb_dp_ctrl,
/*
* DMA
*/
@@ -27,23 +38,195 @@ module cndm_micro_desc_rd
taxi_dma_desc_if.sts_snk dma_rd_desc_sts,
taxi_dma_ram_if.wr_slv dma_ram_wr,
input wire logic txq_en,
input wire logic [3:0] txq_size,
input wire logic [63:0] txq_base_addr,
input wire logic [15:0] txq_prod,
output wire logic [15:0] txq_cons,
input wire logic rxq_en,
input wire logic [3:0] rxq_size,
input wire logic [63:0] rxq_base_addr,
input wire logic [15:0] rxq_prod,
output wire logic [15:0] rxq_cons,
input wire logic [1:0] desc_req,
taxi_axis_if.src axis_desc[2]
taxi_axis_if.src m_axis_desc
);
localparam AXIL_ADDR_W = s_axil_ctrl_wr.ADDR_W;
localparam AXIL_DATA_W = s_axil_ctrl_wr.DATA_W;
localparam APB_ADDR_W = s_apb_dp_ctrl.ADDR_W;
localparam APB_DATA_W = s_apb_dp_ctrl.DATA_W;
localparam RAM_ADDR_W = 16;
logic txq_en_reg = '0;
logic [3:0] txq_size_reg = '0;
logic [7:0] txq_cqn_reg = '0;
logic [63:0] txq_base_addr_reg = '0;
logic [15:0] txq_prod_reg = '0;
logic rxq_en_reg = '0;
logic [3:0] rxq_size_reg = '0;
logic [7:0] rxq_cqn_reg = '0;
logic [63:0] rxq_base_addr_reg = '0;
logic [15:0] rxq_prod_reg = '0;
logic [15:0] txq_cons_ptr_reg = '0;
logic [15:0] rxq_cons_ptr_reg = '0;
logic s_axil_ctrl_awready_reg = 1'b0;
logic s_axil_ctrl_wready_reg = 1'b0;
logic s_axil_ctrl_bvalid_reg = 1'b0;
logic s_axil_ctrl_arready_reg = 1'b0;
logic [AXIL_DATA_W-1:0] s_axil_ctrl_rdata_reg = '0;
logic s_axil_ctrl_rvalid_reg = 1'b0;
assign s_axil_ctrl_wr.awready = s_axil_ctrl_awready_reg;
assign s_axil_ctrl_wr.wready = s_axil_ctrl_wready_reg;
assign s_axil_ctrl_wr.bresp = '0;
assign s_axil_ctrl_wr.buser = '0;
assign s_axil_ctrl_wr.bvalid = s_axil_ctrl_bvalid_reg;
assign s_axil_ctrl_rd.arready = s_axil_ctrl_arready_reg;
assign s_axil_ctrl_rd.rdata = s_axil_ctrl_rdata_reg;
assign s_axil_ctrl_rd.rresp = '0;
assign s_axil_ctrl_rd.ruser = '0;
assign s_axil_ctrl_rd.rvalid = s_axil_ctrl_rvalid_reg;
logic s_apb_dp_ctrl_pready_reg = 1'b0;
logic [AXIL_DATA_W-1:0] s_apb_dp_ctrl_prdata_reg = '0;
assign s_apb_dp_ctrl.pready = s_apb_dp_ctrl_pready_reg;
assign s_apb_dp_ctrl.prdata = s_apb_dp_ctrl_prdata_reg;
assign s_apb_dp_ctrl.pslverr = 1'b0;
assign s_apb_dp_ctrl.pruser = '0;
assign s_apb_dp_ctrl.pbuser = '0;
always_ff @(posedge clk) begin
s_axil_ctrl_awready_reg <= 1'b0;
s_axil_ctrl_wready_reg <= 1'b0;
s_axil_ctrl_bvalid_reg <= s_axil_ctrl_bvalid_reg && !s_axil_ctrl_wr.bready;
s_axil_ctrl_arready_reg <= 1'b0;
s_axil_ctrl_rvalid_reg <= s_axil_ctrl_rvalid_reg && !s_axil_ctrl_rd.rready;
s_apb_dp_ctrl_pready_reg <= 1'b0;
if (s_axil_ctrl_wr.awvalid && s_axil_ctrl_wr.wvalid && !s_axil_ctrl_bvalid_reg) begin
s_axil_ctrl_awready_reg <= 1'b1;
s_axil_ctrl_wready_reg <= 1'b1;
s_axil_ctrl_bvalid_reg <= 1'b1;
case ({s_axil_ctrl_wr.awaddr[9:2], 2'b00})
// 10'h000: begin
// txq_en_reg <= s_axil_ctrl_wr.wdata[0];
// txq_size_reg <= s_axil_ctrl_wr.wdata[19:16];
// end
10'h004: txq_prod_reg <= s_axil_ctrl_wr.wdata[15:0];
// 10'h008: txq_base_addr_reg[31:0] <= s_axil_ctrl_wr.wdata;
// 10'h00c: txq_base_addr_reg[63:32] <= s_axil_ctrl_wr.wdata;
// 10'h100: begin
// rxq_en_reg <= s_axil_ctrl_wr.wdata[0];
// rxq_size_reg <= s_axil_ctrl_wr.wdata[19:16];
// end
10'h104: rxq_prod_reg <= s_axil_ctrl_wr.wdata[15:0];
// 10'h108: rxq_base_addr_reg[31:0] <= s_axil_ctrl_wr.wdata;
// 10'h10c: rxq_base_addr_reg[63:32] <= s_axil_ctrl_wr.wdata;
default: begin end
endcase
end
if (s_axil_ctrl_rd.arvalid && !s_axil_ctrl_rvalid_reg) begin
s_axil_ctrl_rdata_reg <= '0;
s_axil_ctrl_arready_reg <= 1'b1;
s_axil_ctrl_rvalid_reg <= 1'b1;
// case ({s_axil_ctrl_rd.araddr[9:2], 2'b00})
// 10'h000: begin
// s_axil_ctrl_rdata_reg[0] <= txq_en_reg;
// s_axil_ctrl_rdata_reg[19:16] <= txq_size_reg;
// end
// 10'h004: begin
// s_axil_ctrl_rdata_reg[15:0] <= txq_prod_reg;
// s_axil_ctrl_rdata_reg[31:16] <= txq_cons_ptr_reg;
// end
// 10'h008: s_axil_ctrl_rdata_reg <= txq_base_addr_reg[31:0];
// 10'h00c: s_axil_ctrl_rdata_reg <= txq_base_addr_reg[63:32];
// 10'h100: begin
// s_axil_ctrl_rdata_reg[0] <= rxq_en_reg;
// s_axil_ctrl_rdata_reg[19:16] <= rxq_size_reg;
// end
// 10'h104: begin
// s_axil_ctrl_rdata_reg[15:0] <= rxq_prod_reg;
// s_axil_ctrl_rdata_reg[31:16] <= rxq_cons_ptr_reg;
// end
// 10'h108: s_axil_ctrl_rdata_reg <= rxq_base_addr_reg[31:0];
// 10'h10c: s_axil_ctrl_rdata_reg <= rxq_base_addr_reg[63:32];
// default: begin end
// endcase
end
if (s_apb_dp_ctrl.penable && s_apb_dp_ctrl.psel && !s_apb_dp_ctrl_pready_reg) begin
s_apb_dp_ctrl_pready_reg <= 1'b1;
s_apb_dp_ctrl_prdata_reg <= '0;
if (s_apb_dp_ctrl.pwrite) begin
case ({s_apb_dp_ctrl.paddr[9:2], 2'b00})
10'h000: begin
txq_en_reg <= s_apb_dp_ctrl.pwdata[0];
txq_size_reg <= s_apb_dp_ctrl.pwdata[19:16];
txq_cqn_reg <= s_apb_dp_ctrl.pwdata[31:24];
end
10'h004: txq_prod_reg <= s_apb_dp_ctrl.pwdata[15:0];
10'h008: txq_base_addr_reg[31:0] <= s_apb_dp_ctrl.pwdata;
10'h00c: txq_base_addr_reg[63:32] <= s_apb_dp_ctrl.pwdata;
10'h100: begin
rxq_en_reg <= s_apb_dp_ctrl.pwdata[0];
rxq_size_reg <= s_apb_dp_ctrl.pwdata[19:16];
rxq_cqn_reg <= s_apb_dp_ctrl.pwdata[31:24];
end
10'h104: rxq_prod_reg <= s_apb_dp_ctrl.pwdata[15:0];
10'h108: rxq_base_addr_reg[31:0] <= s_apb_dp_ctrl.pwdata;
10'h10c: rxq_base_addr_reg[63:32] <= s_apb_dp_ctrl.pwdata;
default: begin end
endcase
end
case ({s_apb_dp_ctrl.paddr[9:2], 2'b00})
10'h000: begin
s_apb_dp_ctrl_prdata_reg[0] <= txq_en_reg;
s_apb_dp_ctrl_prdata_reg[19:16] <= txq_size_reg;
s_apb_dp_ctrl_prdata_reg[31:24] <= txq_cqn_reg;
end
10'h004: begin
s_apb_dp_ctrl_prdata_reg[15:0] <= txq_prod_reg;
s_apb_dp_ctrl_prdata_reg[31:16] <= txq_cons_ptr_reg;
end
10'h008: s_apb_dp_ctrl_prdata_reg <= txq_base_addr_reg[31:0];
10'h00c: s_apb_dp_ctrl_prdata_reg <= txq_base_addr_reg[63:32];
10'h100: begin
s_apb_dp_ctrl_prdata_reg[0] <= rxq_en_reg;
s_apb_dp_ctrl_prdata_reg[19:16] <= rxq_size_reg;
s_apb_dp_ctrl_prdata_reg[31:24] <= rxq_cqn_reg;
end
10'h104: begin
s_apb_dp_ctrl_prdata_reg[15:0] <= rxq_prod_reg;
s_apb_dp_ctrl_prdata_reg[31:16] <= rxq_cons_ptr_reg;
end
10'h108: s_apb_dp_ctrl_prdata_reg <= rxq_base_addr_reg[31:0];
10'h10c: s_apb_dp_ctrl_prdata_reg <= rxq_base_addr_reg[63:32];
default: begin end
endcase
end
if (rst) begin
s_axil_ctrl_awready_reg <= 1'b0;
s_axil_ctrl_wready_reg <= 1'b0;
s_axil_ctrl_bvalid_reg <= 1'b0;
s_axil_ctrl_arready_reg <= 1'b0;
s_axil_ctrl_rvalid_reg <= 1'b0;
s_apb_dp_ctrl_pready_reg <= 1'b0;
end
end
taxi_dma_desc_if #(
.SRC_ADDR_W(RAM_ADDR_W),
.SRC_SEL_EN(1'b0),
@@ -54,29 +237,25 @@ taxi_dma_desc_if #(
.IMM_EN(1'b0),
.LEN_W(5),
.TAG_W(1),
.ID_EN(0),
.DEST_EN(1),
.DEST_W(1),
.USER_EN(1),
.USER_W(1)
.ID_EN(m_axis_desc.ID_EN),
.ID_W(m_axis_desc.ID_W),
.DEST_EN(m_axis_desc.DEST_EN),
.DEST_W(m_axis_desc.DEST_W),
.USER_EN(m_axis_desc.USER_EN),
.USER_W(m_axis_desc.USER_W)
) dma_desc();
localparam [2:0]
STATE_IDLE = 0,
STATE_READ_DESC = 1,
STATE_READ_DATA = 2,
STATE_TX_DESC = 3;
typedef enum logic [1:0] {
STATE_IDLE,
STATE_READ_DESC,
STATE_READ_DATA,
STATE_TX_DESC
} state_t;
logic [2:0] state_reg = STATE_IDLE;
state_t state_reg = STATE_IDLE;
logic [1:0] desc_req_reg = '0;
logic [15:0] txq_cons_ptr_reg = '0;
logic [15:0] rxq_cons_ptr_reg = '0;
assign txq_cons = txq_cons_ptr_reg;
assign rxq_cons = rxq_cons_ptr_reg;
always_ff @(posedge clk) begin
// axis_desc.tready <= 1'b0;
@@ -102,27 +281,27 @@ always_ff @(posedge clk) begin
dma_desc.req_imm_en <= '0;
dma_desc.req_len <= 16;
dma_desc.req_tag <= '0;
dma_desc.req_id <= '0;
dma_desc.req_user <= '0;
dma_desc.req_valid <= dma_desc.req_valid && !dma_desc.req_ready;
desc_req_reg <= desc_req_reg | desc_req;
if (!txq_en) begin
if (!txq_en_reg) begin
txq_cons_ptr_reg <= '0;
end
if (!rxq_en) begin
if (!rxq_en_reg) begin
rxq_cons_ptr_reg <= '0;
end
case (state_reg)
STATE_IDLE: begin
if (desc_req_reg[1]) begin
dma_rd_desc_req.req_src_addr <= rxq_base_addr + 64'(16'(rxq_cons_ptr_reg & ({16{1'b1}} >> (16 - rxq_size))) * 16);
dma_desc.req_dest <= 1'b1;
dma_rd_desc_req.req_src_addr <= rxq_base_addr_reg + 64'(16'(rxq_cons_ptr_reg & ({16{1'b1}} >> (16 - rxq_size_reg))) * 16);
dma_desc.req_id <= 1'b1;
dma_desc.req_dest <= rxq_cqn_reg;
desc_req_reg[1] <= 1'b0;
if (rxq_cons_ptr_reg == rxq_prod || !rxq_en) begin
if (rxq_cons_ptr_reg == rxq_prod_reg || !rxq_en_reg) begin
dma_desc.req_user <= 1'b1;
dma_desc.req_valid <= 1'b1;
state_reg <= STATE_TX_DESC;
@@ -133,10 +312,11 @@ always_ff @(posedge clk) begin
state_reg <= STATE_READ_DESC;
end
end else if (desc_req_reg[0]) begin
dma_rd_desc_req.req_src_addr <= txq_base_addr + 64'(16'(txq_cons_ptr_reg & ({16{1'b1}} >> (16 - txq_size))) * 16);
dma_desc.req_dest <= 1'b0;
dma_rd_desc_req.req_src_addr <= txq_base_addr_reg + 64'(16'(txq_cons_ptr_reg & ({16{1'b1}} >> (16 - txq_size_reg))) * 16);
dma_desc.req_id <= 1'b0;
dma_desc.req_dest <= txq_cqn_reg;
desc_req_reg[0] <= 1'b0;
if (txq_cons_ptr_reg == txq_prod || !txq_en) begin
if (txq_cons_ptr_reg == txq_prod_reg || !txq_en_reg) begin
dma_desc.req_user <= 1'b1;
dma_desc.req_valid <= 1'b1;
state_reg <= STATE_TX_DESC;
@@ -195,19 +375,6 @@ ram_inst (
.dma_ram_rd(dma_ram_rd)
);
taxi_axis_if #(
.DATA_W(axis_desc[0].DATA_W),
.KEEP_EN(axis_desc[0].KEEP_EN),
.KEEP_W(axis_desc[0].KEEP_W),
.LAST_EN(axis_desc[0].LAST_EN),
.ID_EN(axis_desc[0].ID_EN),
.ID_W(axis_desc[0].ID_W),
.DEST_EN(1),
.DEST_W(1),
.USER_EN(axis_desc[0].USER_EN),
.USER_W(axis_desc[0].USER_W)
) m_axis_rd_data();
taxi_dma_client_axis_source
dma_inst (
.clk(clk),
@@ -222,7 +389,7 @@ dma_inst (
/*
* AXI stream read data output
*/
.m_axis_rd_data(m_axis_rd_data),
.m_axis_rd_data(m_axis_desc),
/*
* RAM interface
@@ -235,32 +402,6 @@ dma_inst (
.enable(1'b1)
);
taxi_axis_demux #(
.M_COUNT(2),
.TDEST_ROUTE(1)
)
demux_inst (
.clk(clk),
.rst(rst),
/*
* AXI4-Stream input (sink)
*/
.s_axis(m_axis_rd_data),
/*
* AXI4-Stream output (source)
*/
.m_axis(axis_desc),
/*
* Control
*/
.enable(1'b1),
.drop(1'b0),
.select('0)
);
endmodule
`resetall

View File

@@ -82,12 +82,14 @@ typedef enum logic [4:0] {
STATE_REG_1,
STATE_REG_2,
STATE_REG_3,
STATE_Q_RESET_1,
STATE_Q_RESET_2,
STATE_Q_SET_BASE_L,
STATE_Q_SET_BASE_H,
STATE_Q_ENABLE,
STATE_Q_DISABLE,
STATE_CREATE_Q_FIND_1,
STATE_CREATE_Q_FIND_2,
STATE_CREATE_Q_RESET_1,
STATE_CREATE_Q_RESET_2,
STATE_CREATE_Q_SET_BASE_L,
STATE_CREATE_Q_SET_BASE_H,
STATE_CREATE_Q_ENABLE,
STATE_DESTROY_Q_DISABLE,
STATE_PTP_READ_1,
STATE_PTP_READ_2,
STATE_PTP_SET,
@@ -152,6 +154,7 @@ logic [15:0] opcode_reg = '0, opcode_next;
logic [31:0] flags_reg = '0, flags_next;
logic [15:0] port_reg = '0, port_next;
logic [23:0] qn_reg = '0, qn_next;
logic [23:0] qn2_reg = '0, qn2_next;
logic [3:0] cmd_ptr_reg = '0, cmd_ptr_next;
logic [DP_APB_ADDR_W-1:0] dp_ptr_reg = '0, dp_ptr_next;
@@ -190,6 +193,7 @@ always_comb begin
flags_next = flags_reg;
port_next = port_reg;
qn_next = qn_reg;
qn2_next = qn2_reg;
cmd_ptr_next = cmd_ptr_reg;
dp_ptr_next = dp_ptr_reg;
@@ -220,6 +224,7 @@ always_comb begin
4'd1: flags_next = s_axis_cmd.tdata;
4'd2: port_next = s_axis_cmd.tdata[15:0];
4'd3: qn_next = s_axis_cmd.tdata[23:0];
4'd4: qn2_next = s_axis_cmd.tdata[23:0];
default: begin end
endcase
@@ -242,46 +247,65 @@ always_comb begin
// determine block base address
case (opcode_reg)
CMD_OP_CREATE_EQ,
CMD_OP_MODIFY_EQ,
CMD_OP_QUERY_EQ,
CMD_OP_DESTROY_EQ:
// CMD_OP_CREATE_EQ:
// begin
// // EQ
// qn_next = 0;
// dp_ptr_next = DP_APB_ADDR_W'({port_reg, 16'd0} | 'h8000) + DP_APB_ADDR_W'(PORT_BASE_ADDR_DP);
// host_ptr_next = 32'({port_reg, 16'd0} | 'h8000) + PORT_BASE_ADDR_HOST;
// end
// CMD_OP_MODIFY_EQ,
// CMD_OP_QUERY_EQ,
// CMD_OP_DESTROY_EQ:
// begin
// // EQ
// dp_ptr_next = DP_APB_ADDR_W'({port_reg, 16'd0} | 'h8000) + DP_APB_ADDR_W'(PORT_BASE_ADDR_DP);
// host_ptr_next = 32'({port_reg, 16'd0} | 'h8000) + PORT_BASE_ADDR_HOST;
// end
CMD_OP_CREATE_CQ:
begin
// EQ
dp_ptr_next = DP_APB_ADDR_W'({port_reg, 16'd0} | 'h0000) + DP_APB_ADDR_W'(PORT_BASE_ADDR_DP);
host_ptr_next = 32'({port_reg, 16'd0} | 'h0000) + PORT_BASE_ADDR_HOST;
// CQ
cnt_next = 1;
dp_ptr_next = DP_APB_ADDR_W'({port_reg, 16'd0} | 'h8000) + DP_APB_ADDR_W'(PORT_BASE_ADDR_DP);
host_ptr_next = 32'({port_reg, 16'd0} | 'h8000) + PORT_BASE_ADDR_HOST;
end
CMD_OP_CREATE_CQ,
CMD_OP_MODIFY_CQ,
CMD_OP_QUERY_CQ,
CMD_OP_DESTROY_CQ:
begin
// CQ
if (qn_reg[0]) begin
dp_ptr_next = DP_APB_ADDR_W'({port_reg, 16'd0} | 'h0300) + DP_APB_ADDR_W'(PORT_BASE_ADDR_DP);
host_ptr_next = 32'({port_reg, 16'd0} | 'h0300) + PORT_BASE_ADDR_HOST;
end else begin
dp_ptr_next = DP_APB_ADDR_W'({port_reg, 16'd0} | 'h0400) + DP_APB_ADDR_W'(PORT_BASE_ADDR_DP);
host_ptr_next = 32'({port_reg, 16'd0} | 'h0400) + PORT_BASE_ADDR_HOST;
end
dp_ptr_next = DP_APB_ADDR_W'({port_reg, 16'd0} | 'h8000 | {qn_reg, 8'd00}) + DP_APB_ADDR_W'(PORT_BASE_ADDR_DP);
host_ptr_next = 32'({port_reg, 16'd0} | 'h8000 | {qn_reg, 8'd00}) + PORT_BASE_ADDR_HOST;
end
CMD_OP_CREATE_SQ:
begin
// SQ
cnt_next = 0;
dp_ptr_next = DP_APB_ADDR_W'({port_reg, 16'd0} | 'h0000) + DP_APB_ADDR_W'(PORT_BASE_ADDR_DP);
host_ptr_next = 32'({port_reg, 16'd0} | 'h0000) + PORT_BASE_ADDR_HOST;
end
CMD_OP_CREATE_SQ,
CMD_OP_MODIFY_SQ,
CMD_OP_QUERY_SQ,
CMD_OP_DESTROY_SQ:
begin
// SQ
dp_ptr_next = DP_APB_ADDR_W'({port_reg, 16'd0} | 'h0000) + DP_APB_ADDR_W'(PORT_BASE_ADDR_DP);
host_ptr_next = 32'({port_reg, 16'd0} | 'h0000) + PORT_BASE_ADDR_HOST;
end
CMD_OP_CREATE_RQ:
begin
// RQ
cnt_next = 0;
dp_ptr_next = DP_APB_ADDR_W'({port_reg, 16'd0} | 'h0100) + DP_APB_ADDR_W'(PORT_BASE_ADDR_DP);
host_ptr_next = 32'({port_reg, 16'd0} | 'h0100) + PORT_BASE_ADDR_HOST;
end
CMD_OP_CREATE_RQ,
CMD_OP_MODIFY_RQ,
CMD_OP_QUERY_RQ,
CMD_OP_DESTROY_RQ:
begin
// RQ
dp_ptr_next = DP_APB_ADDR_W'({port_reg, 16'd0} | 'h0200) + DP_APB_ADDR_W'(PORT_BASE_ADDR_DP);
host_ptr_next = 32'({port_reg, 16'd0} | 'h0200) + PORT_BASE_ADDR_HOST;
dp_ptr_next = DP_APB_ADDR_W'({port_reg, 16'd0} | 'h0100) + DP_APB_ADDR_W'(PORT_BASE_ADDR_DP);
host_ptr_next = 32'({port_reg, 16'd0} | 'h0100) + PORT_BASE_ADDR_HOST;
end
default: begin end
endcase
@@ -330,7 +354,8 @@ always_comb begin
CMD_OP_CREATE_RQ:
begin
// create queue operation
state_next = STATE_Q_RESET_1;
qn_next = '0;
state_next = STATE_CREATE_Q_FIND_1;
end
CMD_OP_MODIFY_EQ,
CMD_OP_MODIFY_CQ,
@@ -366,7 +391,7 @@ always_comb begin
CMD_OP_DESTROY_RQ:
begin
// destroy queue operation
state_next = STATE_Q_DISABLE;
state_next = STATE_DESTROY_Q_DISABLE;
end
default: begin
// unknown opcode
@@ -420,8 +445,56 @@ always_comb begin
state_next = STATE_REG_3;
end
end
STATE_Q_RESET_1: begin
STATE_CREATE_Q_FIND_1: begin
// read queue enable bit
if (!m_apb_dp_ctrl_psel_reg) begin
m_apb_dp_ctrl_paddr_next = dp_ptr_reg + 'h0000;
m_apb_dp_ctrl_psel_next = 1'b1;
m_apb_dp_ctrl_pwrite_next = 1'b0;
m_apb_dp_ctrl_pwdata_next = 32'h00000000;
m_apb_dp_ctrl_pstrb_next = '1;
state_next = STATE_CREATE_Q_FIND_2;
end else begin
state_next = STATE_CREATE_Q_FIND_1;
end
end
STATE_CREATE_Q_FIND_2: begin
// check queue enable bit
if (m_apb_dp_ctrl.pready) begin
cnt_next = cnt_reg - 1;
if (m_apb_dp_ctrl.prdata[0] == 0) begin
// queue is inactive
state_next = STATE_CREATE_Q_RESET_1;
end else begin
// queue is active
qn_next = qn_reg + 1;
dp_ptr_next = dp_ptr_reg + 'h100;
if (cnt_reg == 0) begin
// no more queues
m_axis_rsp_tdata_next = '0; // TODO
m_axis_rsp_tvalid_next = 1'b1;
m_axis_rsp_tlast_next = 1'b0;
state_next = STATE_PAD_RSP;
end else begin
// try next queue
state_next = STATE_CREATE_Q_FIND_1;
end
end
end else begin
state_next = STATE_CREATE_Q_FIND_2;
end
end
STATE_CREATE_Q_RESET_1: begin
// reset queue 1
// store queue number
cmd_ram_wr_data = 32'(qn_reg);
cmd_ram_wr_addr = 3;
cmd_ram_wr_en = 1'b1;
if (!m_apb_dp_ctrl_psel_reg) begin
m_apb_dp_ctrl_paddr_next = dp_ptr_reg + 'h0000;
m_apb_dp_ctrl_psel_next = 1'b1;
@@ -429,14 +502,15 @@ always_comb begin
m_apb_dp_ctrl_pwdata_next = 32'h00000000;
m_apb_dp_ctrl_pstrb_next = '1;
state_next = STATE_Q_RESET_2;
state_next = STATE_CREATE_Q_RESET_2;
end else begin
state_next = STATE_Q_RESET_1;
state_next = STATE_CREATE_Q_RESET_1;
end
end
STATE_Q_RESET_2: begin
STATE_CREATE_Q_RESET_2: begin
// reset queue 2
// store doorbell offset
cmd_ram_wr_data = host_ptr_reg + 'h0004;
cmd_ram_wr_addr = 7;
cmd_ram_wr_en = 1'b1;
@@ -448,12 +522,12 @@ always_comb begin
m_apb_dp_ctrl_pwdata_next = 32'h00000000;
m_apb_dp_ctrl_pstrb_next = '1;
state_next = STATE_Q_SET_BASE_L;
state_next = STATE_CREATE_Q_SET_BASE_L;
end else begin
state_next = STATE_Q_RESET_2;
state_next = STATE_CREATE_Q_RESET_2;
end
end
STATE_Q_SET_BASE_L: begin
STATE_CREATE_Q_SET_BASE_L: begin
// set queue base addr (LSB)
cmd_ram_rd_addr = 8;
if (!m_apb_dp_ctrl_psel_reg) begin
@@ -463,12 +537,12 @@ always_comb begin
m_apb_dp_ctrl_pwdata_next = cmd_ram_rd_data;
m_apb_dp_ctrl_pstrb_next = '1;
state_next = STATE_Q_SET_BASE_H;
state_next = STATE_CREATE_Q_SET_BASE_H;
end else begin
state_next = STATE_Q_SET_BASE_L;
state_next = STATE_CREATE_Q_SET_BASE_L;
end
end
STATE_Q_SET_BASE_H: begin
STATE_CREATE_Q_SET_BASE_H: begin
// set queue base addr (MSB)
cmd_ram_rd_addr = 9;
if (!m_apb_dp_ctrl_psel_reg) begin
@@ -478,12 +552,12 @@ always_comb begin
m_apb_dp_ctrl_pwdata_next = cmd_ram_rd_data;
m_apb_dp_ctrl_pstrb_next = '1;
state_next = STATE_Q_ENABLE;
state_next = STATE_CREATE_Q_ENABLE;
end else begin
state_next = STATE_Q_SET_BASE_H;
state_next = STATE_CREATE_Q_SET_BASE_H;
end
end
STATE_Q_ENABLE: begin
STATE_CREATE_Q_ENABLE: begin
// enable queue
cmd_ram_rd_addr = 6;
if (!m_apb_dp_ctrl_psel_reg) begin
@@ -491,6 +565,7 @@ always_comb begin
m_apb_dp_ctrl_psel_next = 1'b1;
m_apb_dp_ctrl_pwrite_next = 1'b1;
m_apb_dp_ctrl_pwdata_next = '0;
m_apb_dp_ctrl_pwdata_next[31:24] = qn2_reg[7:0];
m_apb_dp_ctrl_pwdata_next[19:16] = cmd_ram_rd_data[3:0];
m_apb_dp_ctrl_pwdata_next[0] = 1'b1;
m_apb_dp_ctrl_pstrb_next = '1;
@@ -501,10 +576,10 @@ always_comb begin
state_next = STATE_SEND_RSP;
end else begin
state_next = STATE_Q_ENABLE;
state_next = STATE_CREATE_Q_ENABLE;
end
end
STATE_Q_DISABLE: begin
STATE_DESTROY_Q_DISABLE: begin
// disable queue
if (!m_apb_dp_ctrl_psel_reg) begin
m_apb_dp_ctrl_paddr_next = dp_ptr_reg + 'h0000;
@@ -519,7 +594,7 @@ always_comb begin
state_next = STATE_SEND_RSP;
end else begin
state_next = STATE_Q_DISABLE;
state_next = STATE_DESTROY_Q_DISABLE;
end
end
STATE_PTP_READ_1: begin
@@ -746,6 +821,7 @@ always_ff @(posedge clk) begin
flags_reg <= flags_next;
port_reg <= port_next;
qn_reg <= qn_next;
qn2_reg <= qn2_next;
cmd_ptr_reg <= cmd_ptr_next;
dp_ptr_reg <= dp_ptr_next;

View File

@@ -75,244 +75,76 @@ localparam RAM_SEG_DATA_W = dma_ram_wr.SEG_DATA_W;
localparam RAM_SEG_BE_W = dma_ram_wr.SEG_BE_W;
localparam RAM_SEL_W = dma_ram_wr.SEL_W;
logic txq_en_reg = '0;
logic [3:0] txq_size_reg = '0;
logic [63:0] txq_base_addr_reg = '0;
logic [15:0] txq_prod_reg = '0;
wire [15:0] txq_cons;
logic rxq_en_reg = '0;
logic [3:0] rxq_size_reg = '0;
logic [63:0] rxq_base_addr_reg = '0;
logic [15:0] rxq_prod_reg = '0;
wire [15:0] rxq_cons;
taxi_axil_if #(
.DATA_W(s_axil_ctrl_wr.DATA_W),
.ADDR_W(15),
.STRB_W(s_axil_ctrl_wr.STRB_W),
.AWUSER_EN(s_axil_ctrl_wr.AWUSER_EN),
.AWUSER_W(s_axil_ctrl_wr.AWUSER_W),
.WUSER_EN(s_axil_ctrl_wr.WUSER_EN),
.WUSER_W(s_axil_ctrl_wr.WUSER_W),
.BUSER_EN(s_axil_ctrl_wr.BUSER_EN),
.BUSER_W(s_axil_ctrl_wr.BUSER_W),
.ARUSER_EN(s_axil_ctrl_wr.ARUSER_EN),
.ARUSER_W(s_axil_ctrl_wr.ARUSER_W),
.RUSER_EN(s_axil_ctrl_wr.RUSER_EN),
.RUSER_W(s_axil_ctrl_wr.RUSER_W)
)
axil_ctrl[2]();
logic txcq_en_reg = '0;
logic [3:0] txcq_size_reg = '0;
logic [63:0] txcq_base_addr_reg = '0;
wire [15:0] txcq_prod;
logic rxcq_en_reg = '0;
logic [3:0] rxcq_size_reg = '0;
logic [63:0] rxcq_base_addr_reg = '0;
wire [15:0] rxcq_prod;
taxi_axil_interconnect_1s #(
.M_COUNT($size(axil_ctrl)),
.ADDR_W(s_axil_ctrl_wr.ADDR_W),
.M_REGIONS(1),
.M_BASE_ADDR('0),
.M_ADDR_W({$size(axil_ctrl){{1{32'd15}}}}),
.M_SECURE({$size(axil_ctrl){1'b0}})
)
port_intercon_inst (
.clk(clk),
.rst(rst),
logic s_axil_ctrl_awready_reg = 1'b0;
logic s_axil_ctrl_wready_reg = 1'b0;
logic s_axil_ctrl_bvalid_reg = 1'b0;
/*
* AXI4-lite slave interface
*/
.s_axil_wr(s_axil_ctrl_wr),
.s_axil_rd(s_axil_ctrl_rd),
logic s_axil_ctrl_arready_reg = 1'b0;
logic [AXIL_DATA_W-1:0] s_axil_ctrl_rdata_reg = '0;
logic s_axil_ctrl_rvalid_reg = 1'b0;
/*
* AXI4-lite master interfaces
*/
.m_axil_wr(axil_ctrl),
.m_axil_rd(axil_ctrl)
);
assign s_axil_ctrl_wr.awready = s_axil_ctrl_awready_reg;
assign s_axil_ctrl_wr.wready = s_axil_ctrl_wready_reg;
assign s_axil_ctrl_wr.bresp = '0;
assign s_axil_ctrl_wr.buser = '0;
assign s_axil_ctrl_wr.bvalid = s_axil_ctrl_bvalid_reg;
taxi_apb_if #(
.DATA_W(32),
.ADDR_W(15)
)
apb_dp_ctrl[2]();
assign s_axil_ctrl_rd.arready = s_axil_ctrl_arready_reg;
assign s_axil_ctrl_rd.rdata = s_axil_ctrl_rdata_reg;
assign s_axil_ctrl_rd.rresp = '0;
assign s_axil_ctrl_rd.ruser = '0;
assign s_axil_ctrl_rd.rvalid = s_axil_ctrl_rvalid_reg;
taxi_apb_interconnect #(
.M_CNT($size(apb_dp_ctrl)),
.ADDR_W(s_apb_dp_ctrl.ADDR_W),
.M_REGIONS(1),
.M_BASE_ADDR('0),
.M_ADDR_W({$size(apb_dp_ctrl){{1{32'd15}}}}),
.M_SECURE({$size(apb_dp_ctrl){1'b0}})
)
port_dp_intercon_inst (
.clk(clk),
.rst(rst),
logic s_apb_dp_ctrl_pready_reg = 1'b0;
logic [AXIL_DATA_W-1:0] s_apb_dp_ctrl_prdata_reg = '0;
/*
* APB slave interface
*/
.s_apb(s_apb_dp_ctrl),
assign s_apb_dp_ctrl.pready = s_apb_dp_ctrl_pready_reg;
assign s_apb_dp_ctrl.prdata = s_apb_dp_ctrl_prdata_reg;
assign s_apb_dp_ctrl.pslverr = 1'b0;
assign s_apb_dp_ctrl.pruser = '0;
assign s_apb_dp_ctrl.pbuser = '0;
always_ff @(posedge clk) begin
s_axil_ctrl_awready_reg <= 1'b0;
s_axil_ctrl_wready_reg <= 1'b0;
s_axil_ctrl_bvalid_reg <= s_axil_ctrl_bvalid_reg && !s_axil_ctrl_wr.bready;
s_axil_ctrl_arready_reg <= 1'b0;
s_axil_ctrl_rvalid_reg <= s_axil_ctrl_rvalid_reg && !s_axil_ctrl_rd.rready;
s_apb_dp_ctrl_pready_reg <= 1'b0;
if (s_axil_ctrl_wr.awvalid && s_axil_ctrl_wr.wvalid && !s_axil_ctrl_bvalid_reg) begin
s_axil_ctrl_awready_reg <= 1'b1;
s_axil_ctrl_wready_reg <= 1'b1;
s_axil_ctrl_bvalid_reg <= 1'b1;
case ({s_axil_ctrl_wr.awaddr[15:2], 2'b00})
16'h0100: begin
txq_en_reg <= s_axil_ctrl_wr.wdata[0];
txq_size_reg <= s_axil_ctrl_wr.wdata[19:16];
end
16'h0104: txq_prod_reg <= s_axil_ctrl_wr.wdata[15:0];
16'h0108: txq_base_addr_reg[31:0] <= s_axil_ctrl_wr.wdata;
16'h010c: txq_base_addr_reg[63:32] <= s_axil_ctrl_wr.wdata;
16'h0200: begin
rxq_en_reg <= s_axil_ctrl_wr.wdata[0];
rxq_size_reg <= s_axil_ctrl_wr.wdata[19:16];
end
16'h0204: rxq_prod_reg <= s_axil_ctrl_wr.wdata[15:0];
16'h0208: rxq_base_addr_reg[31:0] <= s_axil_ctrl_wr.wdata;
16'h020c: rxq_base_addr_reg[63:32] <= s_axil_ctrl_wr.wdata;
16'h0300: begin
txcq_en_reg <= s_axil_ctrl_wr.wdata[0];
txcq_size_reg <= s_axil_ctrl_wr.wdata[19:16];
end
16'h0308: txcq_base_addr_reg[31:0] <= s_axil_ctrl_wr.wdata;
16'h030c: txcq_base_addr_reg[63:32] <= s_axil_ctrl_wr.wdata;
16'h0400: begin
rxcq_en_reg <= s_axil_ctrl_wr.wdata[0];
rxcq_size_reg <= s_axil_ctrl_wr.wdata[19:16];
end
16'h0408: rxcq_base_addr_reg[31:0] <= s_axil_ctrl_wr.wdata;
16'h040c: rxcq_base_addr_reg[63:32] <= s_axil_ctrl_wr.wdata;
default: begin end
endcase
end
if (s_axil_ctrl_rd.arvalid && !s_axil_ctrl_rvalid_reg) begin
s_axil_ctrl_rdata_reg <= '0;
s_axil_ctrl_arready_reg <= 1'b1;
s_axil_ctrl_rvalid_reg <= 1'b1;
case ({s_axil_ctrl_rd.araddr[15:2], 2'b00})
16'h0100: begin
s_axil_ctrl_rdata_reg[0] <= txq_en_reg;
s_axil_ctrl_rdata_reg[19:16] <= txq_size_reg;
end
16'h0104: begin
s_axil_ctrl_rdata_reg[15:0] <= txq_prod_reg;
s_axil_ctrl_rdata_reg[31:16] <= txq_cons;
end
16'h0108: s_axil_ctrl_rdata_reg <= txq_base_addr_reg[31:0];
16'h010c: s_axil_ctrl_rdata_reg <= txq_base_addr_reg[63:32];
16'h0200: begin
s_axil_ctrl_rdata_reg[0] <= rxq_en_reg;
s_axil_ctrl_rdata_reg[19:16] <= rxq_size_reg;
end
16'h0204: begin
s_axil_ctrl_rdata_reg[15:0] <= rxq_prod_reg;
s_axil_ctrl_rdata_reg[31:16] <= rxq_cons;
end
16'h0208: s_axil_ctrl_rdata_reg <= rxq_base_addr_reg[31:0];
16'h020c: s_axil_ctrl_rdata_reg <= rxq_base_addr_reg[63:32];
16'h0300: begin
s_axil_ctrl_rdata_reg[0] <= txcq_en_reg;
s_axil_ctrl_rdata_reg[19:16] <= txcq_size_reg;
end
16'h0304: s_axil_ctrl_rdata_reg[15:0] <= txcq_prod;
16'h0308: s_axil_ctrl_rdata_reg <= txcq_base_addr_reg[31:0];
16'h030c: s_axil_ctrl_rdata_reg <= txcq_base_addr_reg[63:32];
16'h0400: begin
s_axil_ctrl_rdata_reg[0] <= rxcq_en_reg;
s_axil_ctrl_rdata_reg[19:16] <= rxcq_size_reg;
end
16'h0404: s_axil_ctrl_rdata_reg[15:0] <= rxcq_prod;
16'h0408: s_axil_ctrl_rdata_reg <= rxcq_base_addr_reg[31:0];
16'h040c: s_axil_ctrl_rdata_reg <= rxcq_base_addr_reg[63:32];
default: begin end
endcase
end
if (s_apb_dp_ctrl.penable && s_apb_dp_ctrl.psel && !s_apb_dp_ctrl_pready_reg) begin
s_apb_dp_ctrl_pready_reg <= 1'b1;
s_apb_dp_ctrl_prdata_reg <= '0;
if (s_apb_dp_ctrl.pwrite) begin
case ({s_apb_dp_ctrl.paddr[15:2], 2'b00})
16'h0100: begin
txq_en_reg <= s_apb_dp_ctrl.pwdata[0];
txq_size_reg <= s_apb_dp_ctrl.pwdata[19:16];
end
16'h0104: txq_prod_reg <= s_apb_dp_ctrl.pwdata[15:0];
16'h0108: txq_base_addr_reg[31:0] <= s_apb_dp_ctrl.pwdata;
16'h010c: txq_base_addr_reg[63:32] <= s_apb_dp_ctrl.pwdata;
16'h0200: begin
rxq_en_reg <= s_apb_dp_ctrl.pwdata[0];
rxq_size_reg <= s_apb_dp_ctrl.pwdata[19:16];
end
16'h0204: rxq_prod_reg <= s_apb_dp_ctrl.pwdata[15:0];
16'h0208: rxq_base_addr_reg[31:0] <= s_apb_dp_ctrl.pwdata;
16'h020c: rxq_base_addr_reg[63:32] <= s_apb_dp_ctrl.pwdata;
16'h0300: begin
txcq_en_reg <= s_apb_dp_ctrl.pwdata[0];
txcq_size_reg <= s_apb_dp_ctrl.pwdata[19:16];
end
16'h0308: txcq_base_addr_reg[31:0] <= s_apb_dp_ctrl.pwdata;
16'h030c: txcq_base_addr_reg[63:32] <= s_apb_dp_ctrl.pwdata;
16'h0400: begin
rxcq_en_reg <= s_apb_dp_ctrl.pwdata[0];
rxcq_size_reg <= s_apb_dp_ctrl.pwdata[19:16];
end
16'h0408: rxcq_base_addr_reg[31:0] <= s_apb_dp_ctrl.pwdata;
16'h040c: rxcq_base_addr_reg[63:32] <= s_apb_dp_ctrl.pwdata;
default: begin end
endcase
end
case ({s_apb_dp_ctrl.paddr[15:2], 2'b00})
16'h0100: begin
s_apb_dp_ctrl_prdata_reg[0] <= txq_en_reg;
s_apb_dp_ctrl_prdata_reg[19:16] <= txq_size_reg;
end
16'h0104: begin
s_apb_dp_ctrl_prdata_reg[15:0] <= txq_prod_reg;
s_apb_dp_ctrl_prdata_reg[31:16] <= txq_cons;
end
16'h0108: s_apb_dp_ctrl_prdata_reg <= txq_base_addr_reg[31:0];
16'h010c: s_apb_dp_ctrl_prdata_reg <= txq_base_addr_reg[63:32];
16'h0200: begin
s_apb_dp_ctrl_prdata_reg[0] <= rxq_en_reg;
s_apb_dp_ctrl_prdata_reg[19:16] <= rxq_size_reg;
end
16'h0204: begin
s_apb_dp_ctrl_prdata_reg[15:0] <= rxq_prod_reg;
s_apb_dp_ctrl_prdata_reg[31:16] <= rxq_cons;
end
16'h0208: s_apb_dp_ctrl_prdata_reg <= rxq_base_addr_reg[31:0];
16'h020c: s_apb_dp_ctrl_prdata_reg <= rxq_base_addr_reg[63:32];
16'h0300: begin
s_apb_dp_ctrl_prdata_reg[0] <= txcq_en_reg;
s_apb_dp_ctrl_prdata_reg[19:16] <= txcq_size_reg;
end
16'h0304: s_apb_dp_ctrl_prdata_reg[15:0] <= txcq_prod;
16'h0308: s_apb_dp_ctrl_prdata_reg <= txcq_base_addr_reg[31:0];
16'h030c: s_apb_dp_ctrl_prdata_reg <= txcq_base_addr_reg[63:32];
16'h0400: begin
s_apb_dp_ctrl_prdata_reg[0] <= rxcq_en_reg;
s_apb_dp_ctrl_prdata_reg[19:16] <= rxcq_size_reg;
end
16'h0404: s_apb_dp_ctrl_prdata_reg[15:0] <= rxcq_prod;
16'h0408: s_apb_dp_ctrl_prdata_reg <= rxcq_base_addr_reg[31:0];
16'h040c: s_apb_dp_ctrl_prdata_reg <= rxcq_base_addr_reg[63:32];
default: begin end
endcase
end
if (rst) begin
s_axil_ctrl_awready_reg <= 1'b0;
s_axil_ctrl_wready_reg <= 1'b0;
s_axil_ctrl_bvalid_reg <= 1'b0;
s_axil_ctrl_arready_reg <= 1'b0;
s_axil_ctrl_rvalid_reg <= 1'b0;
s_apb_dp_ctrl_pready_reg <= 1'b0;
end
end
/*
* APB master interfaces
*/
.m_apb(apb_dp_ctrl)
);
taxi_dma_desc_if #(
.SRC_ADDR_W(dma_rd_desc_req.SRC_ADDR_W),
@@ -429,32 +261,37 @@ wr_dma_mux_inst (
.client_ram_rd(dma_ram_rd_int)
);
// descriptor fetch
wire [1:0] desc_req;
taxi_axis_if #(
.DATA_W(16*8),
.KEEP_EN(1),
.LAST_EN(1),
.ID_EN(0),
.DEST_EN(1), // TODO
.ID_EN(1),
.ID_W(1),
.DEST_EN(1),
.DEST_W(8),
.USER_EN(1),
.USER_W(1)
) axis_desc[2]();
taxi_axis_if #(
.DATA_W(16*8),
.KEEP_EN(1),
.LAST_EN(1),
.ID_EN(1), // TODO
.DEST_EN(0),
.USER_EN(0)
) axis_cpl[2]();
) axis_desc();
cndm_micro_desc_rd
desc_rd_inst (
.clk(clk),
.rst(rst),
/*
* Control register interface
*/
.s_axil_ctrl_wr(axil_ctrl[0]),
.s_axil_ctrl_rd(axil_ctrl[0]),
/*
* Datapath control register interface
*/
.s_apb_dp_ctrl(apb_dp_ctrl[0]),
/*
* DMA
*/
@@ -462,19 +299,92 @@ desc_rd_inst (
.dma_rd_desc_sts(dma_rd_desc_int[0]),
.dma_ram_wr(dma_ram_wr_int[0]),
.txq_en(txq_en_reg),
.txq_size(txq_size_reg),
.txq_base_addr(txq_base_addr_reg),
.txq_prod(txq_prod_reg),
.txq_cons(txq_cons),
.rxq_en(rxq_en_reg),
.rxq_size(rxq_size_reg),
.rxq_base_addr(rxq_base_addr_reg),
.rxq_prod(rxq_prod_reg),
.rxq_cons(rxq_cons),
.desc_req(desc_req),
.axis_desc(axis_desc)
.m_axis_desc(axis_desc)
);
// desc demux
taxi_axis_if #(
.DATA_W(axis_desc.DATA_W),
.KEEP_EN(axis_desc.KEEP_EN),
.KEEP_W(axis_desc.KEEP_W),
.LAST_EN(axis_desc.LAST_EN),
.ID_EN(axis_desc.ID_EN),
.ID_W(axis_desc.ID_W),
.DEST_EN(axis_desc.DEST_EN),
.DEST_W(axis_desc.DEST_W),
.USER_EN(axis_desc.USER_EN),
.USER_W(axis_desc.USER_W)
) axis_desc_txrx[2]();
taxi_axis_demux #(
.M_COUNT(2),
.TID_ROUTE(1)
)
desc_demux_inst (
.clk(clk),
.rst(rst),
/*
* AXI4-Stream input (sink)
*/
.s_axis(axis_desc),
/*
* AXI4-Stream output (source)
*/
.m_axis(axis_desc_txrx),
/*
* Control
*/
.enable(1'b1),
.drop(1'b0),
.select('0)
);
// completion write
taxi_axis_if #(
.DATA_W(16*8),
.KEEP_EN(1),
.LAST_EN(1),
.ID_EN(0),
.DEST_EN(1),
.DEST_W(8),
.USER_EN(0)
) axis_cpl();
taxi_axis_if #(
.DATA_W(axis_cpl.DATA_W),
.KEEP_EN(axis_cpl.KEEP_EN),
.KEEP_W(axis_cpl.KEEP_W),
.LAST_EN(axis_cpl.LAST_EN),
.ID_EN(axis_cpl.ID_EN),
.ID_W(axis_cpl.ID_W),
.DEST_EN(axis_cpl.DEST_EN),
.DEST_W(axis_cpl.DEST_W),
.USER_EN(axis_cpl.USER_EN),
.USER_W(axis_cpl.USER_W)
) axis_cpl_txrx[2]();
taxi_axis_arb_mux #(
.S_COUNT(2),
.ARB_ROUND_ROBIN(1),
.ARB_LSB_HIGH_PRIO(1)
)
cpl_mux_inst (
.clk(clk),
.rst(rst),
/*
* AXI4-Stream input (sink)
*/
.s_axis(axis_cpl_txrx),
/*
* AXI4-Stream output (source)
*/
.m_axis(axis_cpl)
);
cndm_micro_cpl_wr
@@ -482,6 +392,17 @@ cpl_wr_inst (
.clk(clk),
.rst(rst),
/*
* Control register interface
*/
.s_axil_ctrl_wr(axil_ctrl[1]),
.s_axil_ctrl_rd(axil_ctrl[1]),
/*
* Datapath control register interface
*/
.s_apb_dp_ctrl(apb_dp_ctrl[1]),
/*
* DMA
*/
@@ -489,19 +410,11 @@ cpl_wr_inst (
.dma_wr_desc_sts(dma_wr_desc_int[0]),
.dma_ram_rd(dma_ram_rd_int[0]),
.txcq_en(txcq_en_reg),
.txcq_size(txcq_size_reg),
.txcq_base_addr(txcq_base_addr_reg),
.txcq_prod(txcq_prod),
.rxcq_en(rxcq_en_reg),
.rxcq_size(rxcq_size_reg),
.rxcq_base_addr(rxcq_base_addr_reg),
.rxcq_prod(rxcq_prod),
.axis_cpl(axis_cpl),
.s_axis_cpl(axis_cpl),
.irq(irq)
);
// TX path
taxi_axis_if #(
.DATA_W(mac_axis_tx.DATA_W),
.USER_EN(1),
@@ -636,12 +549,13 @@ tx_inst (
.dma_ram_wr(dma_ram_wr_int[1]),
.desc_req(desc_req[0]),
.axis_desc(axis_desc[0]),
.s_axis_desc(axis_desc_txrx[0]),
.tx_data(mac_tx_int),
.tx_cpl(mac_tx_cpl_int),
.axis_cpl(axis_cpl[0])
.m_axis_cpl(axis_cpl_txrx[0])
);
// RX path
taxi_axis_if #(
.DATA_W(mac_axis_rx.DATA_W),
.USER_EN(1),
@@ -720,8 +634,8 @@ rx_inst (
.rx_data(mac_rx_int),
.desc_req(desc_req[1]),
.axis_desc(axis_desc[1]),
.axis_cpl(axis_cpl[1])
.s_axis_desc(axis_desc_txrx[1]),
.m_axis_cpl(axis_cpl_txrx[1])
);
endmodule

View File

@@ -39,8 +39,8 @@ module cndm_micro_rx #(
taxi_axis_if.snk rx_data,
output wire logic desc_req,
taxi_axis_if.snk axis_desc,
taxi_axis_if.src axis_cpl
taxi_axis_if.snk s_axis_desc,
taxi_axis_if.src m_axis_cpl
);
localparam RAM_ADDR_W = 16;
@@ -61,13 +61,14 @@ taxi_dma_desc_if #(
.USER_W(rx_data.USER_W)
) dma_desc();
localparam [2:0]
STATE_IDLE = 0,
STATE_RX_DATA = 1,
STATE_READ_DESC = 2,
STATE_WRITE_DATA = 3;
typedef enum logic [1:0] {
STATE_IDLE,
STATE_RX_DATA,
STATE_READ_DESC,
STATE_WRITE_DATA
} state_t;
logic [2:0] state_reg = STATE_IDLE;
state_t state_reg = STATE_IDLE;
logic desc_req_reg = 1'b0;
@@ -154,7 +155,7 @@ end
always_ff @(posedge clk) begin
desc_req_reg <= 1'b0;
axis_desc.tready <= 1'b0;
s_axis_desc.tready <= 1'b0;
dma_wr_desc_req.req_src_sel <= '0;
dma_wr_desc_req.req_src_asid <= '0;
@@ -183,17 +184,16 @@ always_ff @(posedge clk) begin
dma_desc.req_user <= '0;
dma_desc.req_valid <= dma_desc.req_valid && !dma_desc.req_ready;
axis_cpl.tkeep <= '0;
axis_cpl.tid <= '0;
axis_cpl.tdest <= '0;
axis_cpl.tuser <= '0;
axis_cpl.tlast <= 1'b1;
axis_cpl.tvalid <= axis_cpl.tvalid && !axis_cpl.tready;
m_axis_cpl.tkeep <= '0;
m_axis_cpl.tid <= '0;
m_axis_cpl.tuser <= '0;
m_axis_cpl.tlast <= 1'b1;
m_axis_cpl.tvalid <= m_axis_cpl.tvalid && !m_axis_cpl.tready;
if (rx_ptp_ts_valid) begin
axis_cpl.tdata[127:112] <= rx_ptp_ts[63:48]; // sec
axis_cpl.tdata[95:64] <= rx_ptp_ts[47:16]; // ns
axis_cpl.tdata[111:96] <= rx_ptp_ts[15:0]; // fns
m_axis_cpl.tdata[127:112] <= rx_ptp_ts[63:48]; // sec
m_axis_cpl.tdata[95:64] <= rx_ptp_ts[47:16]; // ns
m_axis_cpl.tdata[111:96] <= rx_ptp_ts[15:0]; // fns
end
case (state_reg)
@@ -203,24 +203,26 @@ always_ff @(posedge clk) begin
end
STATE_RX_DATA: begin
dma_wr_desc_req.req_len <= 20'(dma_desc.sts_len);
axis_cpl.tdata[47:32] <= 16'(dma_desc.sts_len);
m_axis_cpl.tdata[47:32] <= 16'(dma_desc.sts_len);
if (dma_desc.sts_valid) begin
desc_req_reg <= 1'b1;
state_reg <= STATE_READ_DESC;
end
end
STATE_READ_DESC: begin
axis_desc.tready <= 1'b1;
s_axis_desc.tready <= 1'b1;
dma_wr_desc_req.req_src_addr <= '0;
dma_wr_desc_req.req_dst_addr <= axis_desc.tdata[127:64];
dma_wr_desc_req.req_dst_addr <= s_axis_desc.tdata[127:64];
if (axis_desc.tvalid && axis_desc.tready) begin
if (dma_wr_desc_req.req_len > 20'(axis_desc.tdata[47:32])) begin
dma_wr_desc_req.req_len <= 20'(axis_desc.tdata[47:32]);
m_axis_cpl.tdest <= s_axis_desc.tdest; // CQN
if (s_axis_desc.tvalid && s_axis_desc.tready) begin
if (dma_wr_desc_req.req_len > 20'(s_axis_desc.tdata[47:32])) begin
dma_wr_desc_req.req_len <= 20'(s_axis_desc.tdata[47:32]);
end
if (axis_desc.tuser) begin
if (s_axis_desc.tuser) begin
// failed to read desc
state_reg <= STATE_IDLE;
end else begin
@@ -231,7 +233,7 @@ always_ff @(posedge clk) begin
end
STATE_WRITE_DATA: begin
if (dma_wr_desc_sts.sts_valid) begin
axis_cpl.tvalid <= 1'b1;
m_axis_cpl.tvalid <= 1'b1;
state_reg <= STATE_IDLE;
end
end

View File

@@ -38,10 +38,10 @@ module cndm_micro_tx #(
taxi_dma_ram_if.wr_slv dma_ram_wr,
output wire logic desc_req,
taxi_axis_if.snk axis_desc,
taxi_axis_if.snk s_axis_desc,
taxi_axis_if.src tx_data,
taxi_axis_if.snk tx_cpl,
taxi_axis_if.src axis_cpl
taxi_axis_if.src m_axis_cpl
);
localparam RAM_ADDR_W = 16;
@@ -62,13 +62,14 @@ taxi_dma_desc_if #(
.USER_W(1)
) dma_desc();
localparam [2:0]
STATE_IDLE = 0,
STATE_READ_DESC = 1,
STATE_READ_DATA = 2,
STATE_TX_DATA = 3;
typedef enum logic [1:0] {
STATE_IDLE,
STATE_READ_DESC,
STATE_READ_DATA,
STATE_TX_DATA
} state_t;
logic [2:0] state_reg = STATE_IDLE;
state_t state_reg = STATE_IDLE;
logic desc_req_reg = 1'b0;
@@ -139,7 +140,7 @@ end
always_ff @(posedge clk) begin
desc_req_reg <= 1'b0;
axis_desc.tready <= 1'b0;
s_axis_desc.tready <= 1'b0;
dma_rd_desc_req.req_src_sel <= '0;
dma_rd_desc_req.req_src_asid <= '0;
@@ -166,12 +167,11 @@ always_ff @(posedge clk) begin
dma_desc.req_user <= '0;
dma_desc.req_valid <= dma_desc.req_valid && !dma_desc.req_ready;
axis_cpl.tkeep <= '0;
axis_cpl.tid <= '0;
axis_cpl.tdest <= '0;
axis_cpl.tuser <= '0;
axis_cpl.tlast <= 1'b1;
axis_cpl.tvalid <= axis_cpl.tvalid && !axis_cpl.tready;
m_axis_cpl.tkeep <= '0;
m_axis_cpl.tid <= '0;
m_axis_cpl.tuser <= '0;
m_axis_cpl.tlast <= 1'b1;
m_axis_cpl.tvalid <= m_axis_cpl.tvalid && !m_axis_cpl.tready;
case (state_reg)
STATE_IDLE: begin
@@ -179,19 +179,21 @@ always_ff @(posedge clk) begin
state_reg <= STATE_READ_DESC;
end
STATE_READ_DESC: begin
axis_desc.tready <= 1'b1;
s_axis_desc.tready <= 1'b1;
dma_rd_desc_req.req_src_addr <= axis_desc.tdata[127:64];
dma_rd_desc_req.req_src_addr <= s_axis_desc.tdata[127:64];
dma_rd_desc_req.req_dst_addr <= '0;
dma_rd_desc_req.req_len <= 20'(axis_desc.tdata[47:32]);
dma_rd_desc_req.req_len <= 20'(s_axis_desc.tdata[47:32]);
dma_desc.req_src_addr <= '0;
dma_desc.req_len <= axis_desc.tdata[47:32];
dma_desc.req_len <= s_axis_desc.tdata[47:32];
axis_cpl.tdata[47:32] <= axis_desc.tdata[47:32];
m_axis_cpl.tdata[47:32] <= s_axis_desc.tdata[47:32];
if (axis_desc.tvalid && axis_desc.tready) begin
if (axis_desc.tuser) begin
m_axis_cpl.tdest <= s_axis_desc.tdest; // CQN
if (s_axis_desc.tvalid && s_axis_desc.tready) begin
if (s_axis_desc.tuser) begin
// failed to read desc
state_reg <= STATE_IDLE;
end else begin
@@ -207,11 +209,11 @@ always_ff @(posedge clk) begin
end
end
STATE_TX_DATA: begin
axis_cpl.tdata[127:112] <= tx_cpl_ptp_ts[63:48]; // sec
axis_cpl.tdata[95:64] <= tx_cpl_ptp_ts[47:16]; // ns
axis_cpl.tdata[111:96] <= tx_cpl_ptp_ts[15:0]; // fns
m_axis_cpl.tdata[127:112] <= tx_cpl_ptp_ts[63:48]; // sec
m_axis_cpl.tdata[95:64] <= tx_cpl_ptp_ts[47:16]; // ns
m_axis_cpl.tdata[111:96] <= tx_cpl_ptp_ts[15:0]; // fns
if (tx_cpl_valid) begin
axis_cpl.tvalid <= 1'b1;
m_axis_cpl.tvalid <= 1'b1;
state_reg <= STATE_IDLE;
end
end

View File

@@ -57,165 +57,233 @@ CNDM_CMD_PTP_FLG_OFFSET_REL = 0x00000008
CNDM_CMD_PTP_FLG_OFFSET_FNS = 0x00000010
CNDM_CMD_PTP_FLG_SET_PERIOD = 0x00000080
class Port:
def __init__(self, driver, index):
class Cq:
def __init__(self, driver, port):
self.driver = driver
self.log = driver.log
self.index = index
self.hw_regs = driver.hw_regs
self.rxq_log_size = (256).bit_length()-1
self.rxq_size = 2**self.rxq_log_size
self.rxq_mask = self.rxq_size-1
self.rxq = None
self.rxq_prod = 0
self.rxq_cons = 0
self.rx_rqn = 0
self.rxq_db_offs = 0
self.port = port
self.irqn = None
self.rx_info = [None] * self.rxq_size
self.log_size = 0
self.size = 0
self.size_mask = 0
self.stride = 0
self.cqn = None
self.enabled = False
self.rxcq_log_size = (256).bit_length()-1
self.rxcq_size = 2**self.rxcq_log_size
self.rxcq_mask = self.rxcq_size-1
self.rxcq = None
self.rxcq_prod = 0
self.rxcq_cons = 0
self.rx_cqn = 0
self.buf_size = 0
self.buf_region = None
self.buf_dma = 0
self.buf = None
self.txq_log_size = (256).bit_length()-1
self.txq_size = 2**self.txq_log_size
self.txq_mask = self.txq_size-1
self.txq = None
self.txq_prod = 0
self.txq_cons = 0
self.tx_sqn = 0
self.txq_db_offs = 0
self.eq = None
self.tx_info = [None] * self.txq_size
self.src_ring = None
self.handler = None
self.txcq_log_size = (256).bit_length()-1
self.txcq_size = 2**self.txcq_log_size
self.txcq_mask = self.txcq_size-1
self.txcq = None
self.txcq_prod = 0
self.txcq_cons = 0
self.tx_cqn = 0
self.cons_ptr = None
self.rx_queue = Queue()
self.hw_regs = self.driver.hw_regs
async def init(self):
async def open(self, irqn, size):
if self.cqn is not None:
raise Exception("Already open")
self.rxcq = self.driver.pool.alloc_region(self.rxcq_size*16)
addr = self.rxcq.get_absolute_address(0)
self.irqn = irqn
self.log_size = size.bit_length() - 1
self.size = 2**self.log_size
self.size_mask = self.size-1
self.stride = 16
self.buf_size = self.size*self.stride
self.buf_region = self.driver.pool.alloc_region(self.buf_size)
self.buf_dma = self.buf_region.get_absolute_address(0)
self.buf = self.buf_region.mem
self.buf[0:self.buf_size] = b'\x00'*self.buf_size
self.cons_ptr = 0
rsp = await self.driver.exec_cmd(struct.pack("<HHLLLLLLLQQLLLL",
0, # rsvd
CNDM_CMD_OP_CREATE_CQ, # opcode
0x00000000, # flags
self.index, # port
self.port.index, # port
0, # cqn
0, # eqn
self.irqn, # eqn
0, # pd
self.rxcq_log_size, # size
self.log_size, # size
0, # dboffs
addr, # base addr
self.buf_dma, # base addr
0, # ptr2
0, # prod_ptr
0, # cons_ptr
0, # rsvd
0, # rsvd
))
print(rsp)
self.rxq = self.driver.pool.alloc_region(self.rxq_size*16)
addr = self.rxq.get_absolute_address(0)
rsp_unpacked = struct.unpack("<HHLLLLLLLQQLLLL", rsp)
print(rsp_unpacked)
self.cqn = rsp_unpacked[4]
self.log.info("Opened CQ %d", self.cqn)
self.enabled = True
async def close(self):
if self.cqn is None:
return
self.enabled = False
rsp = await self.driver.exec_cmd(struct.pack("<HHLLLLLLLQQLLLL",
0, # rsvd
CNDM_CMD_OP_CREATE_RQ, # opcode
CNDM_CMD_OP_DESTROY_CQ, # opcode
0x00000000, # flags
self.index, # port
0, # rqn
0, # cqn
0, # pd
self.rxq_log_size, # size
0, # dboffs
addr, # base addr
0, # ptr2
0, # prod_ptr
0, # cons_ptr
0, # rsvd
0, # rsvd
))
print(rsp)
self.rxq_db_offs = struct.unpack_from("<L", rsp, 7*4)[0]
self.txcq = self.driver.pool.alloc_region(self.txcq_size*16)
addr = self.txcq.get_absolute_address(0)
rsp = await self.driver.exec_cmd(struct.pack("<HHLLLLLLLQQLLLL",
0, # rsvd
CNDM_CMD_OP_CREATE_CQ, # opcode
0x00000000, # flags
self.index, # port
1, # cqn
self.port.index, # port
self.cqn, # cqn
0, # eqn
0, # pd
self.txcq_log_size, # size
0, # size
0, # dboffs
addr, # base addr
0, # base addr
0, # ptr2
0, # prod_ptr
0, # cons_ptr
0, # rsvd
0, # rsvd
))
print(rsp)
self.txq = self.driver.pool.alloc_region(self.txq_size*16)
addr = self.txq.get_absolute_address(0)
self.cqn = None
# TODO free buffer
class Sq:
def __init__(self, driver, port):
self.driver = driver
self.log = driver.log
self.port = port
self.log_size = 0
self.size = 0
self.size_mask = 0
self.full_size = 0
self.stride = 0
self.sqn = 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 = None
self.cons_ptr = None
self.packets = 0
self.bytes = 0
self.db_offset = None
self.hw_regs = self.driver.hw_regs
async def open(self, cq, size):
if self.sqn is not None:
raise Exception("Already open")
self.log_size = size.bit_length() - 1
self.size = 2**self.log_size
self.size_mask = self.size-1
self.stride = 16
self.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.buf[0:self.buf_size] = b'\x00'*self.buf_size
self.prod_ptr = 0
self.cons_ptr = 0
self.cq = cq
self.cq.src_ring = self
self.cq.handler = Sq.process_tx_cq
rsp = await self.driver.exec_cmd(struct.pack("<HHLLLLLLLQQLLLL",
0, # rsvd
CNDM_CMD_OP_CREATE_SQ, # opcode
0x00000000, # flags
self.index, # port
self.port.index, # port
0, # sqn
1, # cqn
self.cq.cqn, # cqn
0, # pd
self.txq_log_size, # size
self.log_size, # size
0, # dboffs
addr, # base addr
self.buf_dma, # base addr
0, # ptr2
0, # prod_ptr
0, # cons_ptr
0, # rsvd
0, # rsvd
))
print(rsp)
self.txq_db_offs = struct.unpack_from("<L", rsp, 7*4)[0]
rsp_unpacked = struct.unpack("<HHLLLLLLLQQLLLL", rsp)
print(rsp_unpacked)
self.sqn = rsp_unpacked[4]
self.db_offset = rsp_unpacked[8]
await self.refill_rx_buffers()
self.log.info("Opened SQ %d", self.sqn)
self.enabled = True
async def close(self):
if self.sqn is None:
return
self.enabled = False
rsp = await self.driver.exec_cmd(struct.pack("<HHLLLLLLLQQLLLL",
0, # rsvd
CNDM_CMD_OP_DESTROY_SQ, # opcode
0x00000000, # flags
self.port.index, # port
self.sqn, # sqn
0, # eqn
0, # pd
0, # size
0, # dboffs
0, # base addr
0, # ptr2
0, # prod_ptr
0, # cons_ptr
0, # rsvd
0, # rsvd
))
self.sqn = None
# TODO free buffer
async def start_xmit(self, data):
headroom = 10
tx_buf = self.driver.alloc_pkt()
await tx_buf.write(headroom, data)
index = self.txq_prod & self.txq_mask
index = self.prod_ptr & self.size_mask
ptr = tx_buf.get_absolute_address(0)
struct.pack_into('<xxxxLQ', self.txq.mem, 16*index, len(data), ptr+headroom)
struct.pack_into('<xxxxLQ', self.buf, 16*index, len(data), ptr+headroom)
self.tx_info[index] = tx_buf
self.txq_prod += 1
await self.hw_regs.write_dword(self.txq_db_offs, self.txq_prod & 0xffff)
async def recv(self):
return await self.rx_queue.get()
async def recv_nowait(self):
return self.rx_queue.get_nowait()
self.prod_ptr += 1
await self.hw_regs.write_dword(self.db_offset, self.prod_ptr & 0xffff)
def free_tx_desc(self, index):
pkt = self.tx_info[index]
@@ -223,37 +291,156 @@ class Port:
self.tx_info[index] = None
def free_tx_buf(self):
while self.txq_cons != self.txq_prod:
index = self.txq_cons & self.txq_mask
while self.cons_ptr != self.txq_prod:
index = self.cons_ptr & self.size_mask
self.free_tx_desc(index)
self.txq_cons += 1
self.cons_ptr += 1
async def process_tx_cq(self):
@staticmethod
async def process_tx_cq(cq):
sq = cq.src_ring
cq_cons_ptr = self.txcq_cons
cons_ptr = self.txq_cons
cq.log.info("Process CQ %d for SQ %d", cq.cqn, sq.sqn)
cq_cons_ptr = cq.cons_ptr
cons_ptr = sq.cons_ptr
while True:
cq_index = cq_cons_ptr & self.txcq_mask
index = cons_ptr & self.txq_mask
cq_index = cq_cons_ptr & cq.size_mask
index = cons_ptr & sq.size_mask
cpl_data = struct.unpack_from("<LLLL", self.txcq.mem, cq_index*16)
cpl_data = struct.unpack_from("<LLLL", cq.buf, cq_index*16)
self.log.info("TX CQ index %d data %s", cq_index, cpl_data)
cq.log.info("TX CQ index %d data %s", cq_index, cpl_data)
if bool(cpl_data[-1] & 0x80000000) == bool(cq_cons_ptr & self.txcq_size):
self.log.info("CQ empty")
if bool(cpl_data[-1] & 0x80000000) == bool(cq_cons_ptr & cq.size):
cq.log.info("CQ empty")
break
pkt = self.tx_info[index]
pkt = sq.tx_info[index]
self.free_tx_desc(index)
sq.free_tx_desc(index)
cq_cons_ptr += 1
cons_ptr += 1
self.txcq_cons = cq_cons_ptr
self.txq_cons = cons_ptr
cq.cons_ptr = cq_cons_ptr
sq.cons_ptr = cons_ptr
class Rq:
def __init__(self, driver, port):
self.driver = driver
self.log = driver.log
self.port = port
self.log_size = 0
self.size = 0
self.size_mask = 0
self.full_size = 0
self.stride = 0
self.rqn = 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 = None
self.cons_ptr = None
self.packets = 0
self.bytes = 0
self.db_offset = None
self.hw_regs = self.driver.hw_regs
async def open(self, cq, size):
if self.rqn is not None:
raise Exception("Already open")
self.log_size = size.bit_length() - 1
self.size = 2**self.log_size
self.size_mask = self.size-1
self.stride = 16
self.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.buf[0:self.buf_size] = b'\x00'*self.buf_size
self.prod_ptr = 0
self.cons_ptr = 0
self.cq = cq
self.cq.src_ring = self
self.cq.handler = Rq.process_rx_cq
rsp = await self.driver.exec_cmd(struct.pack("<HHLLLLLLLQQLLLL",
0, # rsvd
CNDM_CMD_OP_CREATE_RQ, # opcode
0x00000000, # flags
self.port.index, # port
0, # rqn
self.cq.cqn, # cqn
0, # pd
self.log_size, # size
0, # dboffs
self.buf_dma, # base addr
0, # ptr2
0, # prod_ptr
0, # cons_ptr
0, # rsvd
0, # rsvd
))
rsp_unpacked = struct.unpack("<HHLLLLLLLQQLLLL", rsp)
print(rsp_unpacked)
self.rqn = rsp_unpacked[4]
self.db_offset = rsp_unpacked[8]
self.log.info("Opened RQ %d", self.rqn)
self.enabled = True
await self.refill_rx_buffers()
async def close(self):
if self.rqn is None:
return
self.enabled = False
rsp = await self.driver.exec_cmd(struct.pack("<HHLLLLLLLQQLLLL",
0, # rsvd
CNDM_CMD_OP_DESTROY_RQ, # opcode
0x00000000, # flags
self.port.index, # port
self.rqn, # rqn
0, # eqn
0, # pd
0, # size
0, # dboffs
0, # base addr
0, # ptr2
0, # prod_ptr
0, # cons_ptr
0, # rsvd
0, # rsvd
))
self.rqn = None
# TODO free buffer
def free_rx_desc(self, index):
pkt = self.rx_info[index]
@@ -261,10 +448,10 @@ class Port:
self.rx_info[index] = None
def free_rx_buf(self):
while self.rxq_cons != self.rxq_prod:
index = self.rxq_cons & self.rxq_mask
while self.cons_ptr != self.prod_ptr:
index = self.cons_ptr & self.size_mask
self.free_rx_desc(index)
self.rxq_cons += 1
self.cons_ptr += 1
def prepare_rx_desc(self, index):
pkt = self.driver.alloc_pkt()
@@ -273,60 +460,103 @@ class Port:
length = pkt.size
ptr = pkt.get_absolute_address(0)
struct.pack_into('<xxxxLQ', self.rxq.mem, 16*index, length, ptr)
struct.pack_into('<xxxxLQ', self.buf, 16*index, length, ptr)
async def refill_rx_buffers(self):
missing = self.rxq_size - (self.rxq_prod - self.rxq_cons)
missing = self.size - (self.prod_ptr - self.cons_ptr)
if missing < 8:
return
for k in range(missing):
self.prepare_rx_desc(self.rxq_prod & self.rxq_mask)
self.rxq_prod += 1
self.prepare_rx_desc(self.prod_ptr & self.size_mask)
self.prod_ptr += 1
await self.hw_regs.write_dword(self.rxq_db_offs, self.rxq_prod & 0xffff)
await self.hw_regs.write_dword(self.db_offset, self.prod_ptr & 0xffff)
async def process_rx_cq(self):
@staticmethod
async def process_rx_cq(cq):
rq = cq.src_ring
cq_cons_ptr = self.rxcq_cons
cons_ptr = self.rxq_cons
cq.log.info("Process CQ %d for RQ %d", cq.cqn, rq.rqn)
cq_cons_ptr = cq.cons_ptr
cons_ptr = rq.cons_ptr
while True:
cq_index = cq_cons_ptr & self.rxcq_mask
index = cons_ptr & self.rxq_mask
cq_index = cq_cons_ptr & cq.size_mask
index = cons_ptr & rq.size_mask
cpl_data = struct.unpack_from("<LLLL", self.rxcq.mem, cq_index*16)
cpl_data = struct.unpack_from("<LLLL", cq.buf, cq_index*16)
self.log.info("RX CQ index %d data %s", cq_index, cpl_data)
rq.log.info("RX CQ index %d data %s", cq_index, cpl_data)
if bool(cpl_data[-1] & 0x80000000) == bool(cq_cons_ptr & self.rxcq_size):
self.log.info("CQ empty")
if bool(cpl_data[-1] & 0x80000000) == bool(cq_cons_ptr & cq.size):
rq.log.info("CQ empty")
break
pkt = self.rx_info[index]
pkt = rq.rx_info[index]
length = cpl_data[1]
data = pkt[:length]
self.log.info("Packet: %s", data)
rq.log.info("Packet: %s", data)
self.rx_queue.put_nowait(data)
rq.port.rx_queue.put_nowait(data)
self.free_rx_desc(index)
rq.free_rx_desc(index)
cq_cons_ptr += 1
cons_ptr += 1
self.rxcq_cons = cq_cons_ptr
self.rxq_cons = cons_ptr
cq.cons_ptr = cq_cons_ptr
rq.cons_ptr = cons_ptr
await self.refill_rx_buffers()
await rq.refill_rx_buffers()
class Port:
def __init__(self, driver, index):
self.driver = driver
self.log = driver.log
self.index = index
self.hw_regs = driver.hw_regs
self.rxq = None
self.rxcq = None
self.txq = None
self.txcq = None
self.rx_queue = Queue()
async def init(self):
self.rxcq = Cq(self.driver, self)
await self.rxcq.open(self.index, 256)
self.rxq = Rq(self.driver, self)
await self.rxq.open(self.rxcq, 256)
self.txcq = Cq(self.driver, self)
await self.txcq.open(self.index, 256)
self.txq = Sq(self.driver, self)
await self.txq.open(self.txcq, 256)
async def start_xmit(self, data):
await self.txq.start_xmit(data)
async def recv(self):
return await self.rx_queue.get()
async def recv_nowait(self):
return self.rx_queue.get_nowait()
async def interrupt_handler(self):
self.log.info("Interrupt")
await self.process_rx_cq()
await self.process_tx_cq()
await self.rxcq.handler(self.rxcq)
await self.txcq.handler(self.txcq)
class Driver:

View File

@@ -66,12 +66,13 @@ taxi_axis_if #(
) cpl_comb();
// Completion write control state machine
localparam [2:0]
STATE_IDLE = 0,
STATE_RX_CPL = 1,
STATE_WRITE_DATA = 2;
typedef enum logic [1:0] {
STATE_IDLE,
STATE_RX_CPL,
STATE_WRITE_DATA
} state_t;
logic [2:0] state_reg = STATE_IDLE;
state_t state_reg = STATE_IDLE;
logic [15:0] txcq_prod_ptr_reg = '0;
logic [15:0] rxcq_prod_ptr_reg = '0;

View File

@@ -69,13 +69,14 @@ taxi_dma_desc_if #(
) dma_desc();
// Descriptor read control state machine
localparam [2:0]
STATE_IDLE = 0,
STATE_READ_DESC = 1,
STATE_READ_DATA = 2,
STATE_TX_DESC = 3;
typedef enum logic [1:0] {
STATE_IDLE,
STATE_READ_DESC,
STATE_READ_DATA,
STATE_TX_DESC
} state_t;
logic [2:0] state_reg = STATE_IDLE;
state_t state_reg = STATE_IDLE;
logic [1:0] desc_req_reg = '0;

View File

@@ -64,13 +64,14 @@ taxi_dma_desc_if #(
) dma_desc();
// Receive datapath control state machine
localparam [2:0]
STATE_IDLE = 0,
STATE_RX_DATA = 1,
STATE_READ_DESC = 2,
STATE_WRITE_DATA = 3;
typedef enum logic [1:0] {
STATE_IDLE,
STATE_RX_DATA,
STATE_READ_DESC,
STATE_WRITE_DATA
} state_t;
logic [2:0] state_reg = STATE_IDLE;
state_t state_reg = STATE_IDLE;
logic desc_req_reg = 1'b0;

View File

@@ -64,13 +64,14 @@ taxi_dma_desc_if #(
) dma_desc();
// Transmit datapath control state machine
localparam [2:0]
STATE_IDLE = 0,
STATE_READ_DESC = 1,
STATE_READ_DATA = 2,
STATE_TX_DATA = 3;
typedef enum logic [1:0] {
STATE_IDLE,
STATE_READ_DESC,
STATE_READ_DATA,
STATE_TX_DATA
} state_t;
logic [2:0] state_reg = STATE_IDLE;
state_t state_reg = STATE_IDLE;
logic desc_req_reg = 1'b0;