mirror of
https://github.com/fpganinja/taxi.git
synced 2026-04-07 04:38:42 -07:00
cndm: Add consumer pointer and arm bit to completion queue
Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
@@ -235,12 +235,17 @@ struct cndm_cq *cndm_create_cq(struct cndm_priv *priv);
|
|||||||
void cndm_destroy_cq(struct cndm_cq *cq);
|
void cndm_destroy_cq(struct cndm_cq *cq);
|
||||||
int cndm_open_cq(struct cndm_cq *cq, int irqn, int size);
|
int cndm_open_cq(struct cndm_cq *cq, int irqn, int size);
|
||||||
void cndm_close_cq(struct cndm_cq *cq);
|
void cndm_close_cq(struct cndm_cq *cq);
|
||||||
|
void cndm_cq_write_cons_ptr(const struct cndm_cq *cq);
|
||||||
|
void cndm_cq_write_cons_ptr_arm(const struct cndm_cq *cq);
|
||||||
|
|
||||||
// cndm_sq.c
|
// cndm_sq.c
|
||||||
struct cndm_ring *cndm_create_sq(struct cndm_priv *priv);
|
struct cndm_ring *cndm_create_sq(struct cndm_priv *priv);
|
||||||
void cndm_destroy_sq(struct cndm_ring *sq);
|
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);
|
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);
|
void cndm_close_sq(struct cndm_ring *sq);
|
||||||
|
bool cndm_is_sq_ring_empty(const struct cndm_ring *sq);
|
||||||
|
bool cndm_is_sq_ring_full(const struct cndm_ring *sq);
|
||||||
|
void cndm_sq_write_prod_ptr(const struct cndm_ring *sq);
|
||||||
int cndm_free_tx_buf(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_poll_tx_cq(struct napi_struct *napi, int budget);
|
||||||
int cndm_start_xmit(struct sk_buff *skb, struct net_device *ndev);
|
int cndm_start_xmit(struct sk_buff *skb, struct net_device *ndev);
|
||||||
@@ -250,6 +255,9 @@ struct cndm_ring *cndm_create_rq(struct cndm_priv *priv);
|
|||||||
void cndm_destroy_rq(struct cndm_ring *rq);
|
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);
|
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);
|
void cndm_close_rq(struct cndm_ring *rq);
|
||||||
|
bool cndm_is_rq_ring_empty(const struct cndm_ring *rq);
|
||||||
|
bool cndm_is_rq_ring_full(const struct cndm_ring *rq);
|
||||||
|
void cndm_rq_write_prod_ptr(const struct cndm_ring *rq);
|
||||||
int cndm_free_rx_buf(struct cndm_ring *rq);
|
int cndm_free_rx_buf(struct cndm_ring *rq);
|
||||||
int cndm_refill_rx_buffers(struct cndm_ring *rq);
|
int cndm_refill_rx_buffers(struct cndm_ring *rq);
|
||||||
int cndm_poll_rx_cq(struct napi_struct *napi, int budget);
|
int cndm_poll_rx_cq(struct napi_struct *napi, int budget);
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ int cndm_open_cq(struct cndm_cq *cq, int irqn, int size)
|
|||||||
|
|
||||||
cq->enabled = 1;
|
cq->enabled = 1;
|
||||||
|
|
||||||
|
cndm_cq_write_cons_ptr_arm(cq);
|
||||||
|
|
||||||
netdev_dbg(cq->priv->ndev, "Opened CQ %d", cq->cqn);
|
netdev_dbg(cq->priv->ndev, "Opened CQ %d", cq->cqn);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -125,3 +127,13 @@ void cndm_close_cq(struct cndm_cq *cq)
|
|||||||
cq->buf_dma_addr = 0;
|
cq->buf_dma_addr = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cndm_cq_write_cons_ptr(const struct cndm_cq *cq)
|
||||||
|
{
|
||||||
|
iowrite32(cq->cons_ptr & 0xffff, cq->db_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cndm_cq_write_cons_ptr_arm(const struct cndm_cq *cq)
|
||||||
|
{
|
||||||
|
iowrite32((cq->cons_ptr & 0xffff) | 0x80000000, cq->db_addr);
|
||||||
|
}
|
||||||
|
|||||||
@@ -163,6 +163,21 @@ void cndm_close_rq(struct cndm_ring *rq)
|
|||||||
rq->priv = NULL;
|
rq->priv = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cndm_is_rq_ring_empty(const struct cndm_ring *rq)
|
||||||
|
{
|
||||||
|
return rq->prod_ptr == rq->cons_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cndm_is_rq_ring_full(const struct cndm_ring *rq)
|
||||||
|
{
|
||||||
|
return (rq->prod_ptr - rq->cons_ptr) >= rq->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cndm_rq_write_prod_ptr(const struct cndm_ring *rq)
|
||||||
|
{
|
||||||
|
iowrite32(rq->prod_ptr & 0xffff, rq->db_addr);
|
||||||
|
}
|
||||||
|
|
||||||
static void cndm_free_rx_desc(struct cndm_ring *rq, int index)
|
static void cndm_free_rx_desc(struct cndm_ring *rq, int index)
|
||||||
{
|
{
|
||||||
struct cndm_priv *priv = rq->priv;
|
struct cndm_priv *priv = rq->priv;
|
||||||
@@ -185,7 +200,7 @@ int cndm_free_rx_buf(struct cndm_ring *rq)
|
|||||||
u32 index;
|
u32 index;
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
|
|
||||||
while (rq->prod_ptr != rq->cons_ptr) {
|
while (!cndm_is_rq_ring_empty(rq)) {
|
||||||
index = rq->cons_ptr & rq->size_mask;
|
index = rq->cons_ptr & rq->size_mask;
|
||||||
cndm_free_rx_desc(rq, index);
|
cndm_free_rx_desc(rq, index);
|
||||||
rq->cons_ptr++;
|
rq->cons_ptr++;
|
||||||
@@ -247,7 +262,7 @@ int cndm_refill_rx_buffers(struct cndm_ring *rq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
dma_wmb();
|
dma_wmb();
|
||||||
iowrite32(rq->prod_ptr & 0xffff, rq->db_addr);
|
cndm_rq_write_prod_ptr(rq);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -332,6 +347,7 @@ rx_drop:
|
|||||||
rq->cons_ptr = cons_ptr;
|
rq->cons_ptr = cons_ptr;
|
||||||
|
|
||||||
cndm_refill_rx_buffers(rq);
|
cndm_refill_rx_buffers(rq);
|
||||||
|
cndm_cq_write_cons_ptr(cq);
|
||||||
|
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
@@ -349,6 +365,7 @@ int cndm_poll_rx_cq(struct napi_struct *napi, int budget)
|
|||||||
napi_complete(napi);
|
napi_complete(napi);
|
||||||
|
|
||||||
// TODO re-enable interrupts
|
// TODO re-enable interrupts
|
||||||
|
cndm_cq_write_cons_ptr_arm(cq);
|
||||||
|
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,6 +152,21 @@ void cndm_close_sq(struct cndm_ring *sq)
|
|||||||
sq->priv = NULL;
|
sq->priv = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cndm_is_sq_ring_empty(const struct cndm_ring *sq)
|
||||||
|
{
|
||||||
|
return sq->prod_ptr == sq->cons_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cndm_is_sq_ring_full(const struct cndm_ring *sq)
|
||||||
|
{
|
||||||
|
return (sq->prod_ptr - sq->cons_ptr) >= sq->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cndm_sq_write_prod_ptr(const struct cndm_ring *sq)
|
||||||
|
{
|
||||||
|
iowrite32(sq->prod_ptr & 0xffff, sq->db_addr);
|
||||||
|
}
|
||||||
|
|
||||||
static void cndm_free_tx_desc(struct cndm_ring *sq, int index, int napi_budget)
|
static void cndm_free_tx_desc(struct cndm_ring *sq, int index, int napi_budget)
|
||||||
{
|
{
|
||||||
struct cndm_priv *priv = sq->priv;
|
struct cndm_priv *priv = sq->priv;
|
||||||
@@ -173,7 +188,7 @@ int cndm_free_tx_buf(struct cndm_ring *sq)
|
|||||||
u32 index;
|
u32 index;
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
|
|
||||||
while (sq->prod_ptr != sq->cons_ptr) {
|
while (!cndm_is_sq_ring_empty(sq)) {
|
||||||
index = sq->cons_ptr & sq->size_mask;
|
index = sq->cons_ptr & sq->size_mask;
|
||||||
cndm_free_tx_desc(sq, index, 0);
|
cndm_free_tx_desc(sq, index, 0);
|
||||||
sq->cons_ptr++;
|
sq->cons_ptr++;
|
||||||
@@ -229,6 +244,8 @@ static int cndm_process_tx_cq(struct cndm_cq *cq, int napi_budget)
|
|||||||
cq->cons_ptr = cq_cons_ptr;
|
cq->cons_ptr = cq_cons_ptr;
|
||||||
sq->cons_ptr = cons_ptr;
|
sq->cons_ptr = cons_ptr;
|
||||||
|
|
||||||
|
cndm_cq_write_cons_ptr(cq);
|
||||||
|
|
||||||
if (netif_tx_queue_stopped(sq->tx_queue) && (done != 0 || sq->prod_ptr == sq->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);
|
netif_tx_wake_queue(sq->tx_queue);
|
||||||
|
|
||||||
@@ -248,6 +265,7 @@ int cndm_poll_tx_cq(struct napi_struct *napi, int budget)
|
|||||||
napi_complete(napi);
|
napi_complete(napi);
|
||||||
|
|
||||||
// TODO re-enable interrupts
|
// TODO re-enable interrupts
|
||||||
|
cndm_cq_write_cons_ptr_arm(cq);
|
||||||
|
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
@@ -315,7 +333,7 @@ int cndm_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
dma_wmb();
|
dma_wmb();
|
||||||
iowrite32(sq->prod_ptr & 0xffff, sq->db_addr);
|
cndm_sq_write_prod_ptr(sq);
|
||||||
|
|
||||||
return NETDEV_TX_OK;
|
return NETDEV_TX_OK;
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ module cndm_micro_cpl_wr #(
|
|||||||
*/
|
*/
|
||||||
taxi_axis_if.src m_axis_irq,
|
taxi_axis_if.src m_axis_irq,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Completion input
|
||||||
|
*/
|
||||||
taxi_axis_if.snk s_axis_cpl
|
taxi_axis_if.snk s_axis_cpl
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -58,6 +61,7 @@ logic cq_req_ready;
|
|||||||
logic [IRQN_W-1:0] cq_rsp_irqn;
|
logic [IRQN_W-1:0] cq_rsp_irqn;
|
||||||
logic [DMA_ADDR_W-1:0] cq_rsp_addr;
|
logic [DMA_ADDR_W-1:0] cq_rsp_addr;
|
||||||
logic cq_rsp_phase_tag;
|
logic cq_rsp_phase_tag;
|
||||||
|
logic cq_rsp_arm;
|
||||||
logic cq_rsp_error;
|
logic cq_rsp_error;
|
||||||
logic cq_rsp_valid;
|
logic cq_rsp_valid;
|
||||||
logic cq_rsp_ready_reg = 1'b0;
|
logic cq_rsp_ready_reg = 1'b0;
|
||||||
@@ -96,6 +100,7 @@ cq_mgr_inst (
|
|||||||
.rsp_dqn(cq_rsp_irqn),
|
.rsp_dqn(cq_rsp_irqn),
|
||||||
.rsp_addr(cq_rsp_addr),
|
.rsp_addr(cq_rsp_addr),
|
||||||
.rsp_phase_tag(cq_rsp_phase_tag),
|
.rsp_phase_tag(cq_rsp_phase_tag),
|
||||||
|
.rsp_arm(cq_rsp_arm),
|
||||||
.rsp_error(cq_rsp_error),
|
.rsp_error(cq_rsp_error),
|
||||||
.rsp_valid(cq_rsp_valid),
|
.rsp_valid(cq_rsp_valid),
|
||||||
.rsp_ready(cq_rsp_ready_reg)
|
.rsp_ready(cq_rsp_ready_reg)
|
||||||
@@ -110,6 +115,7 @@ typedef enum logic [1:0] {
|
|||||||
state_t state_reg = STATE_IDLE;
|
state_t state_reg = STATE_IDLE;
|
||||||
|
|
||||||
logic phase_tag_reg = 1'b0;
|
logic phase_tag_reg = 1'b0;
|
||||||
|
logic arm_reg = 1'b0;
|
||||||
|
|
||||||
logic [IRQN_W-1:0] m_axis_irq_irqn_reg = '0;
|
logic [IRQN_W-1:0] m_axis_irq_irqn_reg = '0;
|
||||||
logic m_axis_irq_tvalid_reg = 1'b0;
|
logic m_axis_irq_tvalid_reg = 1'b0;
|
||||||
@@ -167,6 +173,7 @@ always_ff @(posedge clk) begin
|
|||||||
m_axis_irq_irqn_reg <= cq_rsp_irqn;
|
m_axis_irq_irqn_reg <= cq_rsp_irqn;
|
||||||
dma_wr_desc_req.req_dst_addr <= cq_rsp_addr;
|
dma_wr_desc_req.req_dst_addr <= cq_rsp_addr;
|
||||||
phase_tag_reg <= cq_rsp_phase_tag;
|
phase_tag_reg <= cq_rsp_phase_tag;
|
||||||
|
arm_reg <= cq_rsp_arm;
|
||||||
|
|
||||||
if (cq_rsp_error) begin
|
if (cq_rsp_error) begin
|
||||||
// drop completion
|
// drop completion
|
||||||
@@ -181,7 +188,7 @@ always_ff @(posedge clk) begin
|
|||||||
STATE_WRITE_DATA: begin
|
STATE_WRITE_DATA: begin
|
||||||
if (dma_wr_desc_sts.sts_valid) begin
|
if (dma_wr_desc_sts.sts_valid) begin
|
||||||
s_axis_cpl.tready <= 1'b1;
|
s_axis_cpl.tready <= 1'b1;
|
||||||
m_axis_irq_tvalid_reg <= 1'b1;
|
m_axis_irq_tvalid_reg <= arm_reg; // only generate interrupt when armed
|
||||||
state_reg <= STATE_IDLE;
|
state_reg <= STATE_IDLE;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ wq_mgr_inst (
|
|||||||
.rsp_dqn(wq_rsp_cqn),
|
.rsp_dqn(wq_rsp_cqn),
|
||||||
.rsp_addr(wq_rsp_addr),
|
.rsp_addr(wq_rsp_addr),
|
||||||
.rsp_phase_tag(),
|
.rsp_phase_tag(),
|
||||||
|
.rsp_arm(),
|
||||||
.rsp_error(wq_rsp_error),
|
.rsp_error(wq_rsp_error),
|
||||||
.rsp_valid(wq_rsp_valid),
|
.rsp_valid(wq_rsp_valid),
|
||||||
.rsp_ready(wq_rsp_ready_reg)
|
.rsp_ready(wq_rsp_ready_reg)
|
||||||
|
|||||||
@@ -694,7 +694,11 @@ always_comb begin
|
|||||||
// reset queue 2
|
// reset queue 2
|
||||||
|
|
||||||
// store doorbell offset
|
// store doorbell offset
|
||||||
cmd_ram_wr_data = host_ptr_reg + 'h0008;
|
if (qtype_reg == QTYPE_SQ || qtype_reg == QTYPE_RQ) begin
|
||||||
|
cmd_ram_wr_data = host_ptr_reg + 'h0008;
|
||||||
|
end else begin
|
||||||
|
cmd_ram_wr_data = host_ptr_reg + 'h000C;
|
||||||
|
end
|
||||||
cmd_ram_wr_addr = 7;
|
cmd_ram_wr_addr = 7;
|
||||||
cmd_ram_wr_en = 1'b1;
|
cmd_ram_wr_en = 1'b1;
|
||||||
|
|
||||||
|
|||||||
@@ -513,6 +513,9 @@ cpl_wr_inst (
|
|||||||
*/
|
*/
|
||||||
.m_axis_irq(m_axis_irq),
|
.m_axis_irq(m_axis_irq),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Completion input
|
||||||
|
*/
|
||||||
.s_axis_cpl(axis_cpl)
|
.s_axis_cpl(axis_cpl)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ module cndm_micro_queue_state #(
|
|||||||
output wire logic [DQN_W-1:0] rsp_dqn,
|
output wire logic [DQN_W-1:0] rsp_dqn,
|
||||||
output wire logic [DMA_ADDR_W-1:0] rsp_addr,
|
output wire logic [DMA_ADDR_W-1:0] rsp_addr,
|
||||||
output wire logic rsp_phase_tag,
|
output wire logic rsp_phase_tag,
|
||||||
|
output wire logic rsp_arm,
|
||||||
output wire logic rsp_error,
|
output wire logic rsp_error,
|
||||||
output wire logic rsp_valid,
|
output wire logic rsp_valid,
|
||||||
input wire logic rsp_ready
|
input wire logic rsp_ready
|
||||||
@@ -119,6 +120,7 @@ logic [QN_W-1:0] rsp_qn_reg = '0, rsp_qn_next;
|
|||||||
logic [DQN_W-1:0] rsp_dqn_reg = '0, rsp_dqn_next;
|
logic [DQN_W-1:0] rsp_dqn_reg = '0, rsp_dqn_next;
|
||||||
logic [DMA_ADDR_W-1:0] rsp_addr_reg = '0, rsp_addr_next;
|
logic [DMA_ADDR_W-1:0] rsp_addr_reg = '0, rsp_addr_next;
|
||||||
logic rsp_phase_tag_reg = 1'b0, rsp_phase_tag_next;
|
logic rsp_phase_tag_reg = 1'b0, rsp_phase_tag_next;
|
||||||
|
logic rsp_arm_reg = 1'b0, rsp_arm_next;
|
||||||
logic rsp_error_reg = 1'b0, rsp_error_next;
|
logic rsp_error_reg = 1'b0, rsp_error_next;
|
||||||
logic rsp_valid_reg = 1'b0, rsp_valid_next;
|
logic rsp_valid_reg = 1'b0, rsp_valid_next;
|
||||||
|
|
||||||
@@ -127,11 +129,14 @@ assign rsp_qn = rsp_qn_reg;
|
|||||||
assign rsp_dqn = rsp_dqn_reg;
|
assign rsp_dqn = rsp_dqn_reg;
|
||||||
assign rsp_addr = rsp_addr_reg;
|
assign rsp_addr = rsp_addr_reg;
|
||||||
assign rsp_phase_tag = rsp_phase_tag_reg;
|
assign rsp_phase_tag = rsp_phase_tag_reg;
|
||||||
|
assign rsp_arm = IS_CQ ? rsp_arm_reg : 1'b0;
|
||||||
assign rsp_error = rsp_error_reg;
|
assign rsp_error = rsp_error_reg;
|
||||||
assign rsp_valid = rsp_valid_reg;
|
assign rsp_valid = rsp_valid_reg;
|
||||||
|
|
||||||
logic [2**QN_W-1:0] queue_enable_reg = '0;
|
logic [2**QN_W-1:0] queue_enable_reg = '0;
|
||||||
(* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *)
|
(* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *)
|
||||||
|
logic queue_mem_arm[2**QN_W] = '{default: '0};
|
||||||
|
(* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *)
|
||||||
logic [2:0] queue_mem_qtype[2**QN_W] = '{default: '0};
|
logic [2:0] queue_mem_qtype[2**QN_W] = '{default: '0};
|
||||||
(* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *)
|
(* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *)
|
||||||
logic [DQN_W-1:0] queue_mem_dqn[2**QN_W] = '{default: '0};
|
logic [DQN_W-1:0] queue_mem_dqn[2**QN_W] = '{default: '0};
|
||||||
@@ -148,6 +153,7 @@ logic queue_mem_wr_en;
|
|||||||
logic [QN_W-1:0] queue_mem_addr;
|
logic [QN_W-1:0] queue_mem_addr;
|
||||||
|
|
||||||
wire queue_mem_rd_enable = queue_enable_reg[queue_mem_addr];
|
wire queue_mem_rd_enable = queue_enable_reg[queue_mem_addr];
|
||||||
|
wire queue_mem_rd_arm = queue_mem_arm[queue_mem_addr];
|
||||||
wire [2:0] queue_mem_rd_qtype = queue_mem_qtype[queue_mem_addr];
|
wire [2:0] queue_mem_rd_qtype = queue_mem_qtype[queue_mem_addr];
|
||||||
wire [DQN_W-1:0] queue_mem_rd_dqn = queue_mem_dqn[queue_mem_addr];
|
wire [DQN_W-1:0] queue_mem_rd_dqn = queue_mem_dqn[queue_mem_addr];
|
||||||
wire [3:0] queue_mem_rd_log_size = queue_mem_log_size[queue_mem_addr];
|
wire [3:0] queue_mem_rd_log_size = queue_mem_log_size[queue_mem_addr];
|
||||||
@@ -155,7 +161,11 @@ wire [DMA_ADDR_W-1:0] queue_mem_rd_base_addr = queue_mem_base_addr[queue_mem_add
|
|||||||
wire [PTR_W-1:0] queue_mem_rd_prod_ptr = queue_mem_prod_ptr[queue_mem_addr];
|
wire [PTR_W-1:0] queue_mem_rd_prod_ptr = queue_mem_prod_ptr[queue_mem_addr];
|
||||||
wire [PTR_W-1:0] queue_mem_rd_cons_ptr = queue_mem_cons_ptr[queue_mem_addr];
|
wire [PTR_W-1:0] queue_mem_rd_cons_ptr = queue_mem_cons_ptr[queue_mem_addr];
|
||||||
|
|
||||||
|
wire queue_mem_rd_status_empty = queue_mem_rd_prod_ptr == queue_mem_rd_cons_ptr;
|
||||||
|
wire queue_mem_rd_status_full = ($unsigned(queue_mem_rd_prod_ptr - queue_mem_rd_cons_ptr) & ({PTR_W{1'b1}} << queue_mem_rd_log_size)) != 0;
|
||||||
|
|
||||||
logic queue_mem_wr_enable;
|
logic queue_mem_wr_enable;
|
||||||
|
logic queue_mem_wr_arm;
|
||||||
logic [2:0] queue_mem_wr_qtype;
|
logic [2:0] queue_mem_wr_qtype;
|
||||||
logic [DQN_W-1:0] queue_mem_wr_dqn;
|
logic [DQN_W-1:0] queue_mem_wr_dqn;
|
||||||
logic [3:0] queue_mem_wr_log_size;
|
logic [3:0] queue_mem_wr_log_size;
|
||||||
@@ -180,6 +190,7 @@ always_comb begin
|
|||||||
rsp_dqn_next = rsp_dqn_reg;
|
rsp_dqn_next = rsp_dqn_reg;
|
||||||
rsp_addr_next = rsp_addr_reg;
|
rsp_addr_next = rsp_addr_reg;
|
||||||
rsp_phase_tag_next = rsp_phase_tag_reg;
|
rsp_phase_tag_next = rsp_phase_tag_reg;
|
||||||
|
rsp_arm_next = rsp_arm_reg;
|
||||||
rsp_error_next = rsp_error_reg;
|
rsp_error_next = rsp_error_reg;
|
||||||
rsp_valid_next = rsp_valid_reg && !rsp_ready;
|
rsp_valid_next = rsp_valid_reg && !rsp_ready;
|
||||||
|
|
||||||
@@ -187,6 +198,7 @@ always_comb begin
|
|||||||
queue_mem_addr = '0;
|
queue_mem_addr = '0;
|
||||||
|
|
||||||
queue_mem_wr_enable = queue_mem_rd_enable;
|
queue_mem_wr_enable = queue_mem_rd_enable;
|
||||||
|
queue_mem_wr_arm = queue_mem_rd_arm;
|
||||||
queue_mem_wr_qtype = queue_mem_rd_qtype;
|
queue_mem_wr_qtype = queue_mem_rd_qtype;
|
||||||
queue_mem_wr_dqn = queue_mem_rd_dqn;
|
queue_mem_wr_dqn = queue_mem_rd_dqn;
|
||||||
queue_mem_wr_log_size = queue_mem_rd_log_size;
|
queue_mem_wr_log_size = queue_mem_rd_log_size;
|
||||||
@@ -194,13 +206,6 @@ always_comb begin
|
|||||||
queue_mem_wr_prod_ptr = queue_mem_rd_prod_ptr;
|
queue_mem_wr_prod_ptr = queue_mem_rd_prod_ptr;
|
||||||
queue_mem_wr_cons_ptr = queue_mem_rd_cons_ptr;
|
queue_mem_wr_cons_ptr = queue_mem_rd_cons_ptr;
|
||||||
|
|
||||||
// terminate AXI lite writes
|
|
||||||
if (IS_CQ && s_axil_ctrl_wr.awvalid && s_axil_ctrl_wr.wvalid && !s_axil_ctrl_bvalid_reg) begin
|
|
||||||
s_axil_ctrl_awready_next = 1'b1;
|
|
||||||
s_axil_ctrl_wready_next = 1'b1;
|
|
||||||
s_axil_ctrl_bvalid_next = 1'b1;
|
|
||||||
end
|
|
||||||
|
|
||||||
// terminate AXI lite reads
|
// terminate AXI lite reads
|
||||||
if (s_axil_ctrl_rd.arvalid && !s_axil_ctrl_rvalid_reg) begin
|
if (s_axil_ctrl_rd.arvalid && !s_axil_ctrl_rvalid_reg) begin
|
||||||
s_axil_ctrl_rdata_next = '0;
|
s_axil_ctrl_rdata_next = '0;
|
||||||
@@ -209,7 +214,7 @@ always_comb begin
|
|||||||
s_axil_ctrl_rvalid_next = 1'b1;
|
s_axil_ctrl_rvalid_next = 1'b1;
|
||||||
end
|
end
|
||||||
|
|
||||||
if (!IS_CQ && s_axil_ctrl_wr.awvalid && s_axil_ctrl_wr.wvalid && !s_axil_ctrl_bvalid_reg) begin
|
if (s_axil_ctrl_wr.awvalid && s_axil_ctrl_wr.wvalid && !s_axil_ctrl_bvalid_reg) begin
|
||||||
// AXI lite write
|
// AXI lite write
|
||||||
s_axil_ctrl_awready_next = 1'b1;
|
s_axil_ctrl_awready_next = 1'b1;
|
||||||
s_axil_ctrl_wready_next = 1'b1;
|
s_axil_ctrl_wready_next = 1'b1;
|
||||||
@@ -219,7 +224,19 @@ always_comb begin
|
|||||||
queue_mem_addr = s_axil_ctrl_awaddr_queue_index;
|
queue_mem_addr = s_axil_ctrl_awaddr_queue_index;
|
||||||
|
|
||||||
case (s_axil_ctrl_awaddr_reg_index)
|
case (s_axil_ctrl_awaddr_reg_index)
|
||||||
3'd2: queue_mem_wr_prod_ptr = s_axil_ctrl_wr.wdata[15:0];
|
3'd2: begin
|
||||||
|
if (!IS_CQ) begin
|
||||||
|
queue_mem_wr_prod_ptr = s_axil_ctrl_wr.wdata[15:0];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
3'd3: begin
|
||||||
|
if (IS_CQ) begin
|
||||||
|
queue_mem_wr_cons_ptr = s_axil_ctrl_wr.wdata[15:0];
|
||||||
|
if (s_axil_ctrl_wr.wdata[31]) begin
|
||||||
|
queue_mem_wr_arm = 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
default: begin end
|
default: begin end
|
||||||
endcase
|
endcase
|
||||||
|
|
||||||
@@ -236,12 +253,18 @@ always_comb begin
|
|||||||
case (s_apb_dp_ctrl_paddr_reg_index)
|
case (s_apb_dp_ctrl_paddr_reg_index)
|
||||||
3'd0: begin
|
3'd0: begin
|
||||||
queue_mem_wr_enable = s_apb_dp_ctrl.pwdata[0];
|
queue_mem_wr_enable = s_apb_dp_ctrl.pwdata[0];
|
||||||
|
queue_mem_wr_arm = s_apb_dp_ctrl.pwdata[1];
|
||||||
queue_mem_wr_log_size = s_apb_dp_ctrl.pwdata[19:16];
|
queue_mem_wr_log_size = s_apb_dp_ctrl.pwdata[19:16];
|
||||||
queue_mem_wr_qtype = 3'(s_apb_dp_ctrl.pwdata[23:20]);
|
queue_mem_wr_qtype = 3'(s_apb_dp_ctrl.pwdata[23:20]);
|
||||||
end
|
end
|
||||||
3'd1: queue_mem_wr_dqn = s_apb_dp_ctrl.pwdata[DQN_W-1:0];
|
3'd1: queue_mem_wr_dqn = s_apb_dp_ctrl.pwdata[DQN_W-1:0];
|
||||||
3'd2: queue_mem_wr_prod_ptr = s_apb_dp_ctrl.pwdata[15:0];
|
3'd2: queue_mem_wr_prod_ptr = s_apb_dp_ctrl.pwdata[15:0];
|
||||||
3'd3: queue_mem_wr_cons_ptr = s_apb_dp_ctrl.pwdata[15:0];
|
3'd3: begin
|
||||||
|
queue_mem_wr_cons_ptr = s_apb_dp_ctrl.pwdata[15:0];
|
||||||
|
if (s_apb_dp_ctrl.pwdata[31]) begin
|
||||||
|
queue_mem_wr_arm = 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
3'd6: queue_mem_wr_base_addr[31:0] = s_apb_dp_ctrl.pwdata;
|
3'd6: queue_mem_wr_base_addr[31:0] = s_apb_dp_ctrl.pwdata;
|
||||||
3'd7: queue_mem_wr_base_addr[63:32] = s_apb_dp_ctrl.pwdata;
|
3'd7: queue_mem_wr_base_addr[63:32] = s_apb_dp_ctrl.pwdata;
|
||||||
default: begin end
|
default: begin end
|
||||||
@@ -251,12 +274,13 @@ always_comb begin
|
|||||||
case (s_apb_dp_ctrl_paddr_reg_index)
|
case (s_apb_dp_ctrl_paddr_reg_index)
|
||||||
3'd0: begin
|
3'd0: begin
|
||||||
s_apb_dp_ctrl_prdata_next[0] = queue_mem_rd_enable;
|
s_apb_dp_ctrl_prdata_next[0] = queue_mem_rd_enable;
|
||||||
|
s_apb_dp_ctrl_prdata_next[1] = IS_CQ ? queue_mem_rd_arm : 1'b0;
|
||||||
s_apb_dp_ctrl_prdata_next[19:16] = queue_mem_rd_log_size;
|
s_apb_dp_ctrl_prdata_next[19:16] = queue_mem_rd_log_size;
|
||||||
s_apb_dp_ctrl_prdata_next[23:20] = 4'(queue_mem_rd_qtype);
|
s_apb_dp_ctrl_prdata_next[23:20] = QTYPE_EN ? 4'(queue_mem_rd_qtype) : '0;
|
||||||
end
|
end
|
||||||
3'd1: s_apb_dp_ctrl_prdata_next = 32'(queue_mem_rd_dqn);
|
3'd1: s_apb_dp_ctrl_prdata_next = 32'(queue_mem_rd_dqn);
|
||||||
3'd2: s_apb_dp_ctrl_prdata_next[15:0] = queue_mem_rd_prod_ptr;
|
3'd2: s_apb_dp_ctrl_prdata_next = 32'(queue_mem_rd_prod_ptr);
|
||||||
3'd3: s_apb_dp_ctrl_prdata_next[15:0] = IS_CQ ? '0 : queue_mem_rd_cons_ptr;
|
3'd3: s_apb_dp_ctrl_prdata_next = 32'(queue_mem_rd_cons_ptr);
|
||||||
3'd6: s_apb_dp_ctrl_prdata_next = queue_mem_rd_base_addr[31:0];
|
3'd6: s_apb_dp_ctrl_prdata_next = queue_mem_rd_base_addr[31:0];
|
||||||
3'd7: s_apb_dp_ctrl_prdata_next = queue_mem_rd_base_addr[63:32];
|
3'd7: s_apb_dp_ctrl_prdata_next = queue_mem_rd_base_addr[63:32];
|
||||||
default: begin end
|
default: begin end
|
||||||
@@ -267,17 +291,21 @@ always_comb begin
|
|||||||
req_ready_next = 1'b1;
|
req_ready_next = 1'b1;
|
||||||
|
|
||||||
queue_mem_addr = req_qn;
|
queue_mem_addr = req_qn;
|
||||||
|
queue_mem_wr_arm = 1'b0;
|
||||||
|
|
||||||
|
rsp_arm_next = queue_mem_rd_arm;
|
||||||
rsp_qn_next = req_qn;
|
rsp_qn_next = req_qn;
|
||||||
rsp_dqn_next = queue_mem_rd_dqn;
|
rsp_dqn_next = queue_mem_rd_dqn;
|
||||||
rsp_error_next = !queue_mem_rd_enable || (QTYPE_EN && req_qtype != queue_mem_rd_qtype);
|
rsp_error_next = !queue_mem_rd_enable || (QTYPE_EN && req_qtype != queue_mem_rd_qtype);
|
||||||
if (IS_CQ) begin
|
if (IS_CQ) begin
|
||||||
rsp_addr_next = queue_mem_rd_base_addr + DMA_ADDR_W'(16'(queue_mem_rd_prod_ptr & ({16{1'b1}} >> (16 - queue_mem_rd_log_size))) * QE_SIZE);
|
rsp_addr_next = queue_mem_rd_base_addr + DMA_ADDR_W'(16'(queue_mem_rd_prod_ptr & ({16{1'b1}} >> (16 - queue_mem_rd_log_size))) * QE_SIZE);
|
||||||
rsp_phase_tag_next = !queue_mem_rd_prod_ptr[queue_mem_rd_log_size];
|
rsp_phase_tag_next = !queue_mem_rd_prod_ptr[queue_mem_rd_log_size];
|
||||||
|
if (queue_mem_rd_status_full)
|
||||||
|
rsp_error_next = 1'b1;
|
||||||
queue_mem_wr_prod_ptr = queue_mem_rd_prod_ptr + 1;
|
queue_mem_wr_prod_ptr = queue_mem_rd_prod_ptr + 1;
|
||||||
end else begin
|
end else begin
|
||||||
rsp_addr_next = queue_mem_rd_base_addr + DMA_ADDR_W'(16'(queue_mem_rd_cons_ptr & ({16{1'b1}} >> (16 - queue_mem_rd_log_size))) * QE_SIZE);
|
rsp_addr_next = queue_mem_rd_base_addr + DMA_ADDR_W'(16'(queue_mem_rd_cons_ptr & ({16{1'b1}} >> (16 - queue_mem_rd_log_size))) * QE_SIZE);
|
||||||
if (queue_mem_rd_prod_ptr == queue_mem_rd_cons_ptr)
|
if (queue_mem_rd_status_empty)
|
||||||
rsp_error_next = 1'b1;
|
rsp_error_next = 1'b1;
|
||||||
queue_mem_wr_cons_ptr = queue_mem_rd_cons_ptr + 1;
|
queue_mem_wr_cons_ptr = queue_mem_rd_cons_ptr + 1;
|
||||||
end
|
end
|
||||||
@@ -306,11 +334,13 @@ always @(posedge clk) begin
|
|||||||
rsp_dqn_reg <= rsp_dqn_next;
|
rsp_dqn_reg <= rsp_dqn_next;
|
||||||
rsp_addr_reg <= rsp_addr_next;
|
rsp_addr_reg <= rsp_addr_next;
|
||||||
rsp_phase_tag_reg <= rsp_phase_tag_next;
|
rsp_phase_tag_reg <= rsp_phase_tag_next;
|
||||||
|
rsp_arm_reg <= rsp_arm_next;
|
||||||
rsp_error_reg <= rsp_error_next;
|
rsp_error_reg <= rsp_error_next;
|
||||||
rsp_valid_reg <= rsp_valid_next;
|
rsp_valid_reg <= rsp_valid_next;
|
||||||
|
|
||||||
if (queue_mem_wr_en) begin
|
if (queue_mem_wr_en) begin
|
||||||
queue_enable_reg[queue_mem_addr] <= queue_mem_wr_enable;
|
queue_enable_reg[queue_mem_addr] <= queue_mem_wr_enable;
|
||||||
|
queue_mem_arm[queue_mem_addr] <= queue_mem_wr_arm;
|
||||||
queue_mem_qtype[queue_mem_addr] <= queue_mem_wr_qtype;
|
queue_mem_qtype[queue_mem_addr] <= queue_mem_wr_qtype;
|
||||||
queue_mem_dqn[queue_mem_addr] <= queue_mem_wr_dqn;
|
queue_mem_dqn[queue_mem_addr] <= queue_mem_wr_dqn;
|
||||||
queue_mem_log_size[queue_mem_addr] <= queue_mem_wr_log_size;
|
queue_mem_log_size[queue_mem_addr] <= queue_mem_wr_log_size;
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ class Cq:
|
|||||||
self.cons_ptr = None
|
self.cons_ptr = None
|
||||||
|
|
||||||
self.db_offset = None
|
self.db_offset = None
|
||||||
|
|
||||||
self.hw_regs = self.driver.hw_regs
|
self.hw_regs = self.driver.hw_regs
|
||||||
|
|
||||||
async def open(self, irqn, size):
|
async def open(self, irqn, size):
|
||||||
@@ -141,7 +140,10 @@ class Cq:
|
|||||||
self.log.error("Failed to allocate CQ")
|
self.log.error("Failed to allocate CQ")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
await self.write_cons_ptr_arm()
|
||||||
|
|
||||||
self.log.info("Opened CQ %d", self.cqn)
|
self.log.info("Opened CQ %d", self.cqn)
|
||||||
|
self.log.info("Using doorbell at offset 0x%08x", self.db_offset)
|
||||||
|
|
||||||
self.enabled = True
|
self.enabled = True
|
||||||
|
|
||||||
@@ -173,6 +175,12 @@ class Cq:
|
|||||||
|
|
||||||
# TODO free buffer
|
# TODO free buffer
|
||||||
|
|
||||||
|
async def write_cons_ptr(self):
|
||||||
|
await self.hw_regs.write_dword(self.db_offset, self.cons_ptr & 0xffff)
|
||||||
|
|
||||||
|
async def write_cons_ptr_arm(self):
|
||||||
|
await self.hw_regs.write_dword(self.db_offset, (self.cons_ptr & 0xffff) | 0x80000000)
|
||||||
|
|
||||||
|
|
||||||
class Sq:
|
class Sq:
|
||||||
def __init__(self, driver, port):
|
def __init__(self, driver, port):
|
||||||
@@ -203,7 +211,6 @@ class Sq:
|
|||||||
self.bytes = 0
|
self.bytes = 0
|
||||||
|
|
||||||
self.db_offset = None
|
self.db_offset = None
|
||||||
|
|
||||||
self.hw_regs = self.driver.hw_regs
|
self.hw_regs = self.driver.hw_regs
|
||||||
|
|
||||||
async def open(self, cq, size):
|
async def open(self, cq, size):
|
||||||
@@ -261,6 +268,7 @@ class Sq:
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.log.info("Opened SQ %d (CQ %d)", self.sqn, cq.cqn)
|
self.log.info("Opened SQ %d (CQ %d)", self.sqn, cq.cqn)
|
||||||
|
self.log.info("Using doorbell at offset 0x%08x", self.db_offset)
|
||||||
|
|
||||||
self.enabled = True
|
self.enabled = True
|
||||||
|
|
||||||
@@ -292,6 +300,15 @@ class Sq:
|
|||||||
|
|
||||||
# TODO free buffer
|
# TODO free buffer
|
||||||
|
|
||||||
|
def is_ring_empty(self):
|
||||||
|
return self.prod_ptr == self.cons_ptr
|
||||||
|
|
||||||
|
def is_ring_full(self):
|
||||||
|
return ((self.prod_ptr - self.cons_ptr) & 0xffffffff) > self.size
|
||||||
|
|
||||||
|
async def write_prod_ptr(self):
|
||||||
|
await self.hw_regs.write_dword(self.db_offset, self.prod_ptr & 0xffff)
|
||||||
|
|
||||||
async def start_xmit(self, data):
|
async def start_xmit(self, data):
|
||||||
headroom = 10
|
headroom = 10
|
||||||
tx_buf = self.driver.alloc_pkt()
|
tx_buf = self.driver.alloc_pkt()
|
||||||
@@ -301,7 +318,7 @@ class Sq:
|
|||||||
struct.pack_into('<xxxxLQ', self.buf, 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.tx_info[index] = tx_buf
|
||||||
self.prod_ptr += 1
|
self.prod_ptr += 1
|
||||||
await self.hw_regs.write_dword(self.db_offset, self.prod_ptr & 0xffff)
|
await self.write_prod_ptr()
|
||||||
|
|
||||||
def free_tx_desc(self, index):
|
def free_tx_desc(self, index):
|
||||||
pkt = self.tx_info[index]
|
pkt = self.tx_info[index]
|
||||||
@@ -309,7 +326,7 @@ class Sq:
|
|||||||
self.tx_info[index] = None
|
self.tx_info[index] = None
|
||||||
|
|
||||||
def free_tx_buf(self):
|
def free_tx_buf(self):
|
||||||
while self.cons_ptr != self.txq_prod:
|
while not self.is_ring_empty():
|
||||||
index = self.cons_ptr & self.size_mask
|
index = self.cons_ptr & self.size_mask
|
||||||
self.free_tx_desc(index)
|
self.free_tx_desc(index)
|
||||||
self.cons_ptr += 1
|
self.cons_ptr += 1
|
||||||
@@ -345,6 +362,8 @@ class Sq:
|
|||||||
cq.cons_ptr = cq_cons_ptr
|
cq.cons_ptr = cq_cons_ptr
|
||||||
sq.cons_ptr = cons_ptr
|
sq.cons_ptr = cons_ptr
|
||||||
|
|
||||||
|
await cq.write_cons_ptr_arm()
|
||||||
|
|
||||||
|
|
||||||
class Rq:
|
class Rq:
|
||||||
def __init__(self, driver, port):
|
def __init__(self, driver, port):
|
||||||
@@ -375,7 +394,6 @@ class Rq:
|
|||||||
self.bytes = 0
|
self.bytes = 0
|
||||||
|
|
||||||
self.db_offset = None
|
self.db_offset = None
|
||||||
|
|
||||||
self.hw_regs = self.driver.hw_regs
|
self.hw_regs = self.driver.hw_regs
|
||||||
|
|
||||||
async def open(self, cq, size):
|
async def open(self, cq, size):
|
||||||
@@ -433,6 +451,7 @@ class Rq:
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.log.info("Opened RQ %d (CQ %d)", self.rqn, cq.cqn)
|
self.log.info("Opened RQ %d (CQ %d)", self.rqn, cq.cqn)
|
||||||
|
self.log.info("Using doorbell at offset 0x%08x", self.db_offset)
|
||||||
|
|
||||||
self.enabled = True
|
self.enabled = True
|
||||||
|
|
||||||
@@ -466,13 +485,22 @@ class Rq:
|
|||||||
|
|
||||||
# TODO free buffer
|
# TODO free buffer
|
||||||
|
|
||||||
|
def is_ring_empty(self):
|
||||||
|
return self.prod_ptr == self.cons_ptr
|
||||||
|
|
||||||
|
def is_ring_full(self):
|
||||||
|
return ((self.prod_ptr - self.cons_ptr) & 0xffffffff) > self.size
|
||||||
|
|
||||||
|
async def write_prod_ptr(self):
|
||||||
|
await self.hw_regs.write_dword(self.db_offset, self.prod_ptr & 0xffff)
|
||||||
|
|
||||||
def free_rx_desc(self, index):
|
def free_rx_desc(self, index):
|
||||||
pkt = self.rx_info[index]
|
pkt = self.rx_info[index]
|
||||||
self.driver.free_pkt(pkt)
|
self.driver.free_pkt(pkt)
|
||||||
self.rx_info[index] = None
|
self.rx_info[index] = None
|
||||||
|
|
||||||
def free_rx_buf(self):
|
def free_rx_buf(self):
|
||||||
while self.cons_ptr != self.prod_ptr:
|
while not self.is_ring_empty():
|
||||||
index = self.cons_ptr & self.size_mask
|
index = self.cons_ptr & self.size_mask
|
||||||
self.free_rx_desc(index)
|
self.free_rx_desc(index)
|
||||||
self.cons_ptr += 1
|
self.cons_ptr += 1
|
||||||
@@ -496,7 +524,7 @@ class Rq:
|
|||||||
self.prepare_rx_desc(self.prod_ptr & self.size_mask)
|
self.prepare_rx_desc(self.prod_ptr & self.size_mask)
|
||||||
self.prod_ptr += 1
|
self.prod_ptr += 1
|
||||||
|
|
||||||
await self.hw_regs.write_dword(self.db_offset, self.prod_ptr & 0xffff)
|
await self.write_prod_ptr()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def process_rx_cq(cq):
|
async def process_rx_cq(cq):
|
||||||
@@ -537,6 +565,7 @@ class Rq:
|
|||||||
rq.cons_ptr = cons_ptr
|
rq.cons_ptr = cons_ptr
|
||||||
|
|
||||||
await rq.refill_rx_buffers()
|
await rq.refill_rx_buffers()
|
||||||
|
await cq.write_cons_ptr_arm()
|
||||||
|
|
||||||
|
|
||||||
class Port:
|
class Port:
|
||||||
|
|||||||
Reference in New Issue
Block a user