// SPDX-License-Identifier: CERN-OHL-S-2.0 /* Copyright (c) 2026 FPGA Ninja, LLC Authors: - Alex Forencich */ `resetall `timescale 1ns / 1ps `default_nettype none /* * Datapath manager */ module cndm_micro_dp_mgr # ( // FW ID parameter FPGA_ID = 32'hDEADBEEF, parameter FW_ID = 32'h0000C002, parameter FW_VER = 32'h000_01_000, parameter BOARD_ID = 32'h1234_0000, parameter BOARD_VER = 32'h001_00_000, parameter BUILD_DATE = 32'd602976000, parameter GIT_HASH = 32'h5f87c2e8, parameter RELEASE_INFO = 32'h00000000, // Structural configuration parameter PORTS = 2, parameter logic BRD_CTRL_EN = 1'b0, parameter SYS_CLK_PER_NS_NUM = 4, parameter SYS_CLK_PER_NS_DEN = 1, // Queue configuration parameter EQN_W = 5, parameter CQN_W = 5, parameter SQN_W = 5, parameter RQN_W = 5, parameter EQE_VER = 1, parameter CQE_VER = 1, parameter SQE_VER = 1, parameter RQE_VER = 1, parameter LOG_MAX_WQ_SZ = 15, // PTP configuration parameter logic PTP_EN = 1'b1, parameter PTP_CLK_PER_NS_NUM = 512, parameter PTP_CLK_PER_NS_DEN = 165, // Addressing parameter PTP_BASE_ADDR_DP = 0, parameter PORT_BASE_ADDR_DP = 0, parameter PORT_BASE_ADDR_HOST = 0, parameter PORT_STRIDE = 'h10000, parameter WQ_REG_STRIDE = 32, parameter EQM_OFFSET = 'h4000, parameter CQM_OFFSET = 'h4000, parameter SQM_OFFSET = 'h0000, parameter RQM_OFFSET = 'h0000, parameter PORT_CTRL_OFFSET = 'h8000 ) ( input wire logic clk, input wire logic rst, /* * Command interface */ taxi_axis_if.snk s_axis_cmd, taxi_axis_if.src m_axis_rsp, /* * APB master interface (datapath control) */ taxi_apb_if.mst m_apb_dp_ctrl, /* * Board control */ taxi_axis_if.src m_axis_brd_ctrl_cmd, taxi_axis_if.snk s_axis_brd_ctrl_rsp ); // extract parameters localparam CMD_ID_W = s_axis_cmd.ID_W; localparam DP_APB_ADDR_W = m_apb_dp_ctrl.ADDR_W; localparam DP_APB_DATA_W = m_apb_dp_ctrl.DATA_W; localparam DP_APB_STRB_W = m_apb_dp_ctrl.STRB_W; localparam BRD_CMD_ID_W = m_axis_brd_ctrl_cmd.ID_W; typedef enum logic [15:0] { CMD_OP_NOP = 16'h0000, CMD_OP_CFG = 16'h0100, CMD_OP_ACCESS_REG = 16'h0180, CMD_OP_PTP = 16'h0190, CMD_OP_HWID = 16'h01A0, CMD_OP_HWMON = 16'h01B0, CMD_OP_PLL = 16'h01C0, CMD_OP_CREATE_EQ = 16'h0200, CMD_OP_MODIFY_EQ = 16'h0201, CMD_OP_QUERY_EQ = 16'h0202, CMD_OP_DESTROY_EQ = 16'h0203, CMD_OP_CREATE_CQ = 16'h0210, CMD_OP_MODIFY_CQ = 16'h0211, CMD_OP_QUERY_CQ = 16'h0212, CMD_OP_DESTROY_CQ = 16'h0213, CMD_OP_CREATE_SQ = 16'h0220, CMD_OP_MODIFY_SQ = 16'h0221, CMD_OP_QUERY_SQ = 16'h0222, CMD_OP_DESTROY_SQ = 16'h0223, CMD_OP_CREATE_RQ = 16'h0230, CMD_OP_MODIFY_RQ = 16'h0231, CMD_OP_QUERY_RQ = 16'h0232, CMD_OP_DESTROY_RQ = 16'h0233, CMD_OP_CREATE_QP = 16'h0240, CMD_OP_MODIFY_QP = 16'h0241, CMD_OP_QUERY_QP = 16'h0242, CMD_OP_DESTROY_QP = 16'h0243 } cmd_opcode_t; typedef enum logic [15:0] { CMD_STS_OK = 16'h0000, CMD_STS_EPERM = 16'h0001, CMD_STS_EIO = 16'h0005, CMD_STS_ENXIO = 16'h0006, CMD_STS_EAGAIN = 16'h000B, CMD_STS_ENOMEM = 16'h000C, CMD_STS_EACCESS = 16'h000D, CMD_STS_EFAULT = 16'h000E, CMD_STS_EBUSY = 16'h0010, CMD_STS_ENODEV = 16'h0013, CMD_STS_EINVAL = 16'h0016, CMD_STS_ENOSPC = 16'h001C, CMD_STS_EDOM = 16'h0021, CMD_STS_ERANGE = 16'h0022, CMD_STS_ENOTSUP = 16'h005F, CMD_STS_ETIMEDOUT = 16'h006E } cmd_status_t; typedef enum logic [2:0] { QTYPE_EQ, QTYPE_CQ, QTYPE_SQ, QTYPE_RQ } qtype_t; typedef enum logic [4:0] { STATE_IDLE, STATE_START, STATE_CFG_READ, STATE_REG_1, STATE_REG_2, STATE_REG_3, STATE_CREATE_Q_FIND_1, STATE_CREATE_Q_FIND_2, STATE_CREATE_Q_RESET_1, STATE_CREATE_Q_RESET_2, STATE_CREATE_Q_RESET_3, STATE_CREATE_Q_SET_BASE_L, STATE_CREATE_Q_SET_BASE_H, STATE_CREATE_Q_SET_DQN, STATE_CREATE_Q_ENABLE, STATE_CREATE_Q_PORT_CONFIG, STATE_DESTROY_Q_DISABLE, STATE_PTP_READ_1, STATE_PTP_READ_2, STATE_PTP_SET, STATE_BOARD_CMD, STATE_BOARD_RSP, STATE_SEND_RSP, STATE_PAD_RSP } state_t; state_t state_reg = STATE_IDLE, state_next; logic s_axis_cmd_tready_reg = 1'b0, s_axis_cmd_tready_next; logic [31:0] m_axis_rsp_tdata_reg = '0, m_axis_rsp_tdata_next; logic m_axis_rsp_tvalid_reg = 1'b0, m_axis_rsp_tvalid_next; logic m_axis_rsp_tlast_reg = 1'b0, m_axis_rsp_tlast_next; logic [CMD_ID_W-1:0] m_axis_rsp_tid_reg = '0, m_axis_rsp_tid_next; logic [DP_APB_ADDR_W-1:0] m_apb_dp_ctrl_paddr_reg = '0, m_apb_dp_ctrl_paddr_next; logic m_apb_dp_ctrl_psel_reg = 1'b0, m_apb_dp_ctrl_psel_next; logic m_apb_dp_ctrl_penable_reg = 1'b0, m_apb_dp_ctrl_penable_next; logic m_apb_dp_ctrl_pwrite_reg = 1'b0, m_apb_dp_ctrl_pwrite_next; logic [DP_APB_DATA_W-1:0] m_apb_dp_ctrl_pwdata_reg = '0, m_apb_dp_ctrl_pwdata_next; logic [DP_APB_STRB_W-1:0] m_apb_dp_ctrl_pstrb_reg = '0, m_apb_dp_ctrl_pstrb_next; logic [31:0] m_axis_brd_ctrl_cmd_tdata_reg = '0, m_axis_brd_ctrl_cmd_tdata_next; logic m_axis_brd_ctrl_cmd_tvalid_reg = 1'b0, m_axis_brd_ctrl_cmd_tvalid_next; logic m_axis_brd_ctrl_cmd_tlast_reg = 1'b0, m_axis_brd_ctrl_cmd_tlast_next; logic [BRD_CMD_ID_W-1:0] m_axis_brd_ctrl_cmd_tid_reg = '0, m_axis_brd_ctrl_cmd_tid_next; logic s_axis_brd_ctrl_rsp_tready_reg = 1'b0, s_axis_brd_ctrl_rsp_tready_next; // command RAM localparam CMD_AW = 4; logic [31:0] cmd_ram[2**CMD_AW] = '{default: '0}; logic [31:0] cmd_ram_wr_data; logic [CMD_AW-1:0] cmd_ram_wr_addr; logic cmd_ram_wr_en; logic [CMD_AW-1:0] cmd_ram_rd_addr; wire [31:0] cmd_ram_rd_data = cmd_ram[cmd_ram_rd_addr]; // ID ROM localparam ID_PAGES = 3; localparam ID_AW = $clog2((ID_PAGES+1)*8); // detect sharing between EQ/CQ and SQ/RQ (same queue manager) localparam EQ_POOL = 1; localparam CQ_POOL = EQ_POOL + (EQM_OFFSET == CQM_OFFSET ? 0 : 1); localparam SQ_POOL = CQ_POOL + 1; localparam RQ_POOL = SQ_POOL + (SQM_OFFSET == RQM_OFFSET ? 0 : 1); logic [31:0] id_rom[(ID_PAGES+1)*8] = '{ // Common 0, // 0: status 0, // 1: flags { // 2 16'(ID_PAGES-1), // [31:16] cfg_page_max 16'd0 // [15:0] cfg_page (replaced) }, 32'h000_01_000, // 3: CMD_VER FW_VER, // 4 { // 5 8'd0, // [31:24] 8'd0, // [23:16] 8'd0, // [15:8] 8'(PORTS) // [7:0] }, 0, // 6 0, // 7 // Page 0: FW ID FPGA_ID, // 8 FW_ID, // 9 FW_VER, // 10 BOARD_ID, // 11 BOARD_VER, // 12 BUILD_DATE, // 13 GIT_HASH, // 14 RELEASE_INFO, // 15 // Page 1: HW config { // 16 16'd0, // [31:16] 16'(PORTS) // [15:0] }, 0, // 17 0, // 18 0, // 19 { // 20 16'(SYS_CLK_PER_NS_NUM), // [31:16] 16'(SYS_CLK_PER_NS_DEN) // [15:0] }, { // 21 16'(PTP_CLK_PER_NS_NUM), // [31:16] 16'(PTP_CLK_PER_NS_DEN) // [15:0] }, 0, // 22 0, // 23 // Page 2: Resources { // 24 8'(EQE_VER), // [31:24] EQE_VER 8'(EQ_POOL), // [23:16] EQ_POOL 8'(LOG_MAX_WQ_SZ), // [15:8] LOG_MAX_EQ_SZ 8'(EQN_W) // [7:0] LOG_MAX_EQ }, { // 25 8'(CQE_VER), // [31:24] CQE_VER 8'(CQ_POOL), // [23:16] CQ_POOL 8'(LOG_MAX_WQ_SZ), // [15:8] LOG_MAX_CQ_SZ 8'(CQN_W) // [7:0] LOG_MAX_CQ }, { // 26 8'(SQE_VER), // [31:24] SQE_VER 8'(SQ_POOL), // [23:16] SQ_POOL 8'(LOG_MAX_WQ_SZ), // [15:8] LOG_MAX_SQ_SZ 8'(SQN_W) // [7:0] LOG_MAX_SQ }, { // 27 8'(RQE_VER), // [31:24] RQE_VER 8'(RQ_POOL), // [23:16] RQ_POOL 8'(LOG_MAX_WQ_SZ), // [15:8] LOG_MAX_RQ_SZ 8'(RQN_W) // [7:0] LOG_MAX_RQ }, 0, // 28 0, // 29 0, // 30 0 // 31 }; logic [ID_AW-1:0] id_rom_rd_addr; wire [31:0] id_rom_rd_data = id_rom[id_rom_rd_addr]; assign s_axis_cmd.tready = s_axis_cmd_tready_reg; assign m_axis_rsp.tdata = m_axis_rsp_tdata_reg; assign m_axis_rsp.tkeep = '1; assign m_axis_rsp.tstrb = m_axis_rsp.tkeep; assign m_axis_rsp.tvalid = m_axis_rsp_tvalid_reg; assign m_axis_rsp.tlast = m_axis_rsp_tlast_reg; assign m_axis_rsp.tid = m_axis_rsp_tid_reg; assign m_axis_rsp.tdest = '0; assign m_axis_rsp.tuser = '0; assign m_apb_dp_ctrl.paddr = m_apb_dp_ctrl_paddr_reg; assign m_apb_dp_ctrl.pprot = 3'b010; assign m_apb_dp_ctrl.psel = m_apb_dp_ctrl_psel_reg; assign m_apb_dp_ctrl.penable = m_apb_dp_ctrl_penable_reg; assign m_apb_dp_ctrl.pwrite = m_apb_dp_ctrl_pwrite_reg; assign m_apb_dp_ctrl.pwdata = m_apb_dp_ctrl_pwdata_reg; assign m_apb_dp_ctrl.pstrb = m_apb_dp_ctrl_pstrb_reg; assign m_apb_dp_ctrl.pauser = '0; assign m_apb_dp_ctrl.pwuser = '0; assign m_axis_brd_ctrl_cmd.tdata = m_axis_brd_ctrl_cmd_tdata_reg; assign m_axis_brd_ctrl_cmd.tkeep = '1; assign m_axis_brd_ctrl_cmd.tstrb = m_axis_brd_ctrl_cmd.tkeep; assign m_axis_brd_ctrl_cmd.tvalid = m_axis_brd_ctrl_cmd_tvalid_reg; assign m_axis_brd_ctrl_cmd.tlast = m_axis_brd_ctrl_cmd_tlast_reg; assign m_axis_brd_ctrl_cmd.tid = m_axis_brd_ctrl_cmd_tid_reg; assign m_axis_brd_ctrl_cmd.tdest = '0; assign m_axis_brd_ctrl_cmd.tuser = '0; assign s_axis_brd_ctrl_rsp.tready = s_axis_brd_ctrl_rsp_tready_reg; logic cmd_frame_reg = 1'b0, cmd_frame_next; logic [3:0] cmd_wr_ptr_reg = '0, cmd_wr_ptr_next; logic rsp_frame_reg = 1'b0, rsp_frame_next; logic [3:0] rsp_rd_ptr_reg = '0, rsp_rd_ptr_next; logic drop_cmd_reg = 1'b0, drop_cmd_next; logic [15:0] opcode_reg = '0, opcode_next; logic [31:0] flags_reg = '0, flags_next; logic [31:0] dw2_reg = '0, dw2_next; logic [31:0] dw3_reg = '0, dw3_next; logic [31:0] dw4_reg = '0, dw4_next; logic [2:0] qtype_reg = '0, qtype_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; logic [31:0] host_ptr_reg = '0, host_ptr_next; logic [15:0] cnt_reg = '0, cnt_next; always_comb begin state_next = STATE_IDLE; s_axis_cmd_tready_next = 1'b0; m_axis_rsp_tdata_next = m_axis_rsp_tdata_reg; m_axis_rsp_tvalid_next = m_axis_rsp_tvalid_reg && !m_axis_rsp.tready; m_axis_rsp_tlast_next = m_axis_rsp_tlast_reg; m_axis_rsp_tid_next = m_axis_rsp_tid_reg; m_apb_dp_ctrl_paddr_next = m_apb_dp_ctrl_paddr_reg; m_apb_dp_ctrl_psel_next = m_apb_dp_ctrl_psel_reg && !m_apb_dp_ctrl.pready; m_apb_dp_ctrl_penable_next = m_apb_dp_ctrl_psel_reg && !m_apb_dp_ctrl.pready; m_apb_dp_ctrl_pwrite_next = m_apb_dp_ctrl_pwrite_reg; m_apb_dp_ctrl_pwdata_next = m_apb_dp_ctrl_pwdata_reg; m_apb_dp_ctrl_pstrb_next = m_apb_dp_ctrl_pstrb_reg; m_axis_brd_ctrl_cmd_tdata_next = m_axis_brd_ctrl_cmd_tdata_reg; m_axis_brd_ctrl_cmd_tvalid_next = m_axis_brd_ctrl_cmd_tvalid_reg && !m_axis_brd_ctrl_cmd.tready; m_axis_brd_ctrl_cmd_tlast_next = m_axis_brd_ctrl_cmd_tlast_reg; m_axis_brd_ctrl_cmd_tid_next = m_axis_brd_ctrl_cmd_tid_reg; s_axis_brd_ctrl_rsp_tready_next = s_axis_brd_ctrl_rsp_tready_reg; cmd_ram_wr_data = s_axis_cmd.tdata; cmd_ram_wr_addr = cmd_wr_ptr_reg; cmd_ram_wr_en = 1'b0; cmd_ram_rd_addr = '0; id_rom_rd_addr = ID_AW'(cnt_reg); cmd_frame_next = cmd_frame_reg; cmd_wr_ptr_next = cmd_wr_ptr_reg; rsp_frame_next = rsp_frame_reg; rsp_rd_ptr_next = rsp_rd_ptr_reg; drop_cmd_next = drop_cmd_reg; opcode_next = opcode_reg; flags_next = flags_reg; dw2_next = dw2_reg; dw3_next = dw3_reg; dw4_next = dw4_reg; qtype_next = qtype_reg; cmd_ptr_next = cmd_ptr_reg; dp_ptr_next = dp_ptr_reg; host_ptr_next = host_ptr_reg; cnt_next = cnt_reg; if (s_axis_cmd.tready && s_axis_cmd.tvalid) begin if (s_axis_cmd.tlast) begin cmd_frame_next = 1'b0; cmd_wr_ptr_next = '0; end else begin cmd_wr_ptr_next = cmd_wr_ptr_reg + 1; cmd_frame_next = 1'b1; end end case (state_reg) STATE_IDLE: begin s_axis_cmd_tready_next = !m_axis_rsp_tvalid_reg && !rsp_frame_reg; cmd_ram_wr_data = s_axis_cmd.tdata; cmd_ram_wr_addr = cmd_wr_ptr_reg; cmd_ram_wr_en = 1'b1; // save some important fields case (cmd_wr_ptr_reg) 4'd0: opcode_next = s_axis_cmd.tdata[31:16]; 4'd1: flags_next = s_axis_cmd.tdata; 4'd2: dw2_next = s_axis_cmd.tdata; 4'd3: dw3_next = s_axis_cmd.tdata; 4'd4: dw4_next = s_axis_cmd.tdata; default: begin end endcase if (s_axis_cmd.tready && s_axis_cmd.tvalid && !drop_cmd_reg) begin if (s_axis_cmd.tlast || &cmd_wr_ptr_reg) begin drop_cmd_next = !s_axis_cmd.tlast; state_next = STATE_START; end else begin state_next = STATE_IDLE; end end else begin state_next = STATE_IDLE; end end STATE_START: begin cmd_ptr_next = '0; dp_ptr_next = '0; host_ptr_next = '0; cnt_next = '0; // determine block base address case (opcode_reg) // EQ CMD_OP_CREATE_EQ: begin cnt_next = 2**EQN_W-1; qtype_next = QTYPE_EQ; dp_ptr_next = DP_APB_ADDR_W'((dw2_reg[15:0] * PORT_STRIDE) + EQM_OFFSET + PORT_BASE_ADDR_DP); host_ptr_next = (dw2_reg[15:0] * PORT_STRIDE) + EQM_OFFSET + PORT_BASE_ADDR_HOST; end CMD_OP_MODIFY_EQ, CMD_OP_QUERY_EQ, CMD_OP_DESTROY_EQ: begin qtype_next = QTYPE_EQ; dp_ptr_next = DP_APB_ADDR_W'((dw2_reg[15:0] * PORT_STRIDE) + EQM_OFFSET + (dw3_reg[15:0] * WQ_REG_STRIDE) + PORT_BASE_ADDR_DP); host_ptr_next = (dw2_reg[15:0] * PORT_STRIDE) + EQM_OFFSET + (dw3_reg[15:0] * WQ_REG_STRIDE) + PORT_BASE_ADDR_HOST; end // CQ CMD_OP_CREATE_CQ: begin cnt_next = 2**CQN_W-1; qtype_next = QTYPE_CQ; dp_ptr_next = DP_APB_ADDR_W'((dw2_reg[15:0] * PORT_STRIDE) + CQM_OFFSET + PORT_BASE_ADDR_DP); host_ptr_next = (dw2_reg[15:0] * PORT_STRIDE) + CQM_OFFSET + PORT_BASE_ADDR_HOST; end CMD_OP_MODIFY_CQ, CMD_OP_QUERY_CQ, CMD_OP_DESTROY_CQ: begin qtype_next = QTYPE_CQ; dp_ptr_next = DP_APB_ADDR_W'((dw2_reg[15:0] * PORT_STRIDE) + CQM_OFFSET + (dw3_reg[15:0] * WQ_REG_STRIDE) + PORT_BASE_ADDR_DP); host_ptr_next = (dw2_reg[15:0] * PORT_STRIDE) + CQM_OFFSET + (dw3_reg[15:0] * WQ_REG_STRIDE) + PORT_BASE_ADDR_HOST; end // SQ CMD_OP_CREATE_SQ: begin cnt_next = 2**SQN_W-1; qtype_next = QTYPE_SQ; dp_ptr_next = DP_APB_ADDR_W'((dw2_reg[15:0] * PORT_STRIDE) + SQM_OFFSET + PORT_BASE_ADDR_DP); host_ptr_next = (dw2_reg[15:0] * PORT_STRIDE) + SQM_OFFSET + PORT_BASE_ADDR_HOST; end CMD_OP_MODIFY_SQ, CMD_OP_QUERY_SQ, CMD_OP_DESTROY_SQ: begin qtype_next = QTYPE_SQ; dp_ptr_next = DP_APB_ADDR_W'((dw2_reg[15:0] * PORT_STRIDE) + SQM_OFFSET + (dw3_reg[15:0] * WQ_REG_STRIDE) + PORT_BASE_ADDR_DP); host_ptr_next = (dw2_reg[15:0] * PORT_STRIDE) + SQM_OFFSET + (dw3_reg[15:0] * WQ_REG_STRIDE) + PORT_BASE_ADDR_HOST; end // RQ CMD_OP_CREATE_RQ: begin cnt_next = 2**RQN_W-1; qtype_next = QTYPE_RQ; dp_ptr_next = DP_APB_ADDR_W'((dw2_reg[15:0] * PORT_STRIDE) + RQM_OFFSET + PORT_BASE_ADDR_DP); host_ptr_next = (dw2_reg[15:0] * PORT_STRIDE) + RQM_OFFSET + PORT_BASE_ADDR_HOST; end CMD_OP_MODIFY_RQ, CMD_OP_QUERY_RQ, CMD_OP_DESTROY_RQ: begin qtype_next = QTYPE_RQ; dp_ptr_next = DP_APB_ADDR_W'((dw2_reg[15:0] * PORT_STRIDE) + RQM_OFFSET + (dw3_reg[15:0] * WQ_REG_STRIDE) + PORT_BASE_ADDR_DP); host_ptr_next = (dw2_reg[15:0] * PORT_STRIDE) + RQM_OFFSET + (dw3_reg[15:0] * WQ_REG_STRIDE) + PORT_BASE_ADDR_HOST; end default: begin end endcase case (opcode_reg) CMD_OP_NOP: begin // NOP m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_OK; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = 1'b0; state_next = STATE_SEND_RSP; end CMD_OP_CFG: begin // dump config page cmd_ptr_next = 2; cnt_next = 2; state_next = STATE_CFG_READ; end CMD_OP_ACCESS_REG: begin // access register state_next = STATE_REG_1; end CMD_OP_PTP: begin // PTP control if (PTP_EN) begin if (flags_reg[15:0] != 0) begin // update something cmd_ptr_next = 2; dp_ptr_next = DP_APB_ADDR_W'(PTP_BASE_ADDR_DP + 'h50); cnt_next = '0; state_next = STATE_PTP_SET; end else begin // dump state cmd_ptr_next = 2; dp_ptr_next = DP_APB_ADDR_W'(PTP_BASE_ADDR_DP + 'h30); cnt_next = '0; state_next = STATE_PTP_READ_1; end end else begin // PTP not enabled m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_ENOTSUP; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = 1'b0; state_next = STATE_SEND_RSP; end end CMD_OP_HWID, CMD_OP_HWMON, CMD_OP_PLL: begin if (BRD_CTRL_EN) begin // Forward board command cmd_ptr_next = 2; state_next = STATE_BOARD_CMD; end else begin // PTP not enabled m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_ENOTSUP; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = 1'b0; state_next = STATE_SEND_RSP; end end CMD_OP_CREATE_EQ, CMD_OP_CREATE_CQ, CMD_OP_CREATE_SQ, CMD_OP_CREATE_RQ: begin // create queue operation dw3_next = '0; state_next = STATE_CREATE_Q_FIND_1; end CMD_OP_MODIFY_EQ, CMD_OP_MODIFY_CQ, CMD_OP_MODIFY_SQ, CMD_OP_MODIFY_RQ: begin // modify queue operation m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_ENOTSUP; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = 1'b0; // determine base address state_next = STATE_PAD_RSP; end CMD_OP_QUERY_EQ, CMD_OP_QUERY_CQ, CMD_OP_QUERY_SQ, CMD_OP_QUERY_RQ: begin // query queue operation m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_ENOTSUP; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = 1'b0; // determine base address state_next = STATE_PAD_RSP; end CMD_OP_DESTROY_EQ, CMD_OP_DESTROY_CQ, CMD_OP_DESTROY_SQ, CMD_OP_DESTROY_RQ: begin // destroy queue operation state_next = STATE_DESTROY_Q_DISABLE; end default: begin // unknown opcode m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_EINVAL; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = 1'b0; state_next = STATE_PAD_RSP; end endcase end STATE_CFG_READ: begin // read config page data id_rom_rd_addr = ID_AW'(cnt_reg); cmd_ram_wr_data = id_rom_rd_data; cmd_ram_wr_addr = cmd_ptr_reg; cmd_ram_wr_en = 1'b1; if (cmd_ptr_reg == 2) begin cmd_ram_wr_data[15:0] = dw2_reg[15:0]; end cnt_next = cnt_reg + 1; cmd_ptr_next = cmd_ptr_reg + 1; if (cmd_ptr_reg == 15) begin // done m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_OK; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = 1'b0; state_next = STATE_SEND_RSP; end else if (cmd_ptr_reg == 7) begin // jump to selected page cnt_next = 16'((dw2_reg + 1) * 8); state_next = STATE_CFG_READ; end else begin // more to read state_next = STATE_CFG_READ; end end STATE_REG_1: begin // register access 1 cmd_ram_rd_addr = 7; if (!m_apb_dp_ctrl_psel_reg) begin m_apb_dp_ctrl_paddr_next = DP_APB_ADDR_W'(cmd_ram_rd_data); state_next = STATE_REG_2; end else begin state_next = STATE_REG_1; end end STATE_REG_2: begin // register access 2 cmd_ram_rd_addr = 8; if (!m_apb_dp_ctrl_psel_reg) begin m_apb_dp_ctrl_psel_next = 1'b1; m_apb_dp_ctrl_pwrite_next = flags_reg[0]; m_apb_dp_ctrl_pwdata_next = cmd_ram_rd_data; m_apb_dp_ctrl_pstrb_next = '1; state_next = STATE_REG_3; end else begin state_next = STATE_REG_2; end end STATE_REG_3: begin // register access 3 cmd_ram_wr_data = m_apb_dp_ctrl.prdata; cmd_ram_wr_addr = 10; cmd_ram_wr_en = 1'b1; if (m_apb_dp_ctrl.pready) begin m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_OK; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = 1'b0; state_next = STATE_SEND_RSP; end else begin state_next = STATE_REG_3; end end 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 dw3_next = dw3_reg + 1; dp_ptr_next = dp_ptr_reg + WQ_REG_STRIDE; host_ptr_next = host_ptr_reg + WQ_REG_STRIDE; if (cnt_reg == 0) begin // no more queues m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_ENOMEM; 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 = dw3_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; m_apb_dp_ctrl_pwrite_next = 1'b1; m_apb_dp_ctrl_pwdata_next = 32'h00000000; m_apb_dp_ctrl_pstrb_next = '1; state_next = STATE_CREATE_Q_RESET_2; end else begin state_next = STATE_CREATE_Q_RESET_1; end end STATE_CREATE_Q_RESET_2: begin // reset queue 2 // store doorbell offset 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_en = 1'b1; if (!m_apb_dp_ctrl_psel_reg) begin m_apb_dp_ctrl_paddr_next = dp_ptr_reg + 'h0008; m_apb_dp_ctrl_psel_next = 1'b1; m_apb_dp_ctrl_pwrite_next = 1'b1; m_apb_dp_ctrl_pwdata_next = 32'h00000000; m_apb_dp_ctrl_pstrb_next = '1; state_next = STATE_CREATE_Q_RESET_3; end else begin state_next = STATE_CREATE_Q_RESET_2; end end STATE_CREATE_Q_RESET_3: begin // reset queue 2 if (!m_apb_dp_ctrl_psel_reg) begin m_apb_dp_ctrl_paddr_next = dp_ptr_reg + 'h000c; m_apb_dp_ctrl_psel_next = 1'b1; m_apb_dp_ctrl_pwrite_next = 1'b1; m_apb_dp_ctrl_pwdata_next = 32'h00000000; m_apb_dp_ctrl_pstrb_next = '1; state_next = STATE_CREATE_Q_SET_BASE_L; end else begin state_next = STATE_CREATE_Q_RESET_3; end end 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 m_apb_dp_ctrl_paddr_next = dp_ptr_reg + 'h0018; m_apb_dp_ctrl_psel_next = 1'b1; m_apb_dp_ctrl_pwrite_next = 1'b1; m_apb_dp_ctrl_pwdata_next = cmd_ram_rd_data; m_apb_dp_ctrl_pstrb_next = '1; state_next = STATE_CREATE_Q_SET_BASE_H; end else begin state_next = STATE_CREATE_Q_SET_BASE_L; end end 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 m_apb_dp_ctrl_paddr_next = dp_ptr_reg + 'h001C; m_apb_dp_ctrl_psel_next = 1'b1; m_apb_dp_ctrl_pwrite_next = 1'b1; m_apb_dp_ctrl_pwdata_next = cmd_ram_rd_data; m_apb_dp_ctrl_pstrb_next = '1; state_next = STATE_CREATE_Q_ENABLE; end else begin state_next = STATE_CREATE_Q_SET_DQN; end end STATE_CREATE_Q_SET_DQN: begin // set CQN/EQN/IRQN cmd_ram_rd_addr = 4; if (!m_apb_dp_ctrl_psel_reg) begin m_apb_dp_ctrl_paddr_next = dp_ptr_reg + 'h0004; m_apb_dp_ctrl_psel_next = 1'b1; m_apb_dp_ctrl_pwrite_next = 1'b1; m_apb_dp_ctrl_pwdata_next = cmd_ram_rd_data; m_apb_dp_ctrl_pstrb_next = '1; state_next = STATE_CREATE_Q_ENABLE; end else begin state_next = STATE_CREATE_Q_SET_DQN; end end STATE_CREATE_Q_ENABLE: begin // enable queue cmd_ram_rd_addr = 6; 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'b1; m_apb_dp_ctrl_pwdata_next = '0; m_apb_dp_ctrl_pwdata_next[23:20] = 4'(qtype_reg); // type m_apb_dp_ctrl_pwdata_next[19:16] = cmd_ram_rd_data[3:0]; // size m_apb_dp_ctrl_pwdata_next[0] = 1'b1; // enable m_apb_dp_ctrl_pstrb_next = '1; state_next = STATE_CREATE_Q_PORT_CONFIG; end else begin state_next = STATE_CREATE_Q_ENABLE; end end STATE_CREATE_Q_PORT_CONFIG: begin // set up port if (!m_apb_dp_ctrl_psel_reg) begin if (qtype_reg == QTYPE_SQ) begin m_apb_dp_ctrl_paddr_next = DP_APB_ADDR_W'(PORT_BASE_ADDR_DP + (dw2_reg[15:0] * PORT_STRIDE) + PORT_CTRL_OFFSET + 'h0010); end else begin m_apb_dp_ctrl_paddr_next = DP_APB_ADDR_W'(PORT_BASE_ADDR_DP + (dw2_reg[15:0] * PORT_STRIDE) + PORT_CTRL_OFFSET + 'h0020); end m_apb_dp_ctrl_psel_next = 1'b1; m_apb_dp_ctrl_pwrite_next = qtype_reg == QTYPE_SQ || qtype_reg == QTYPE_RQ; m_apb_dp_ctrl_pwdata_next = dw3_reg; m_apb_dp_ctrl_pstrb_next = '1; m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_OK; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = 1'b0; state_next = STATE_SEND_RSP; end else begin state_next = STATE_CREATE_Q_PORT_CONFIG; end end 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; m_apb_dp_ctrl_psel_next = 1'b1; m_apb_dp_ctrl_pwrite_next = 1'b1; m_apb_dp_ctrl_pwdata_next = 32'h00000000; m_apb_dp_ctrl_pstrb_next = '1; m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_OK; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = 1'b0; state_next = STATE_SEND_RSP; end else begin state_next = STATE_DESTROY_Q_DISABLE; end end STATE_PTP_READ_1: begin // read PTP register if (!m_apb_dp_ctrl_psel_reg) begin m_apb_dp_ctrl_paddr_next = dp_ptr_reg; m_apb_dp_ctrl_psel_next = 1'b1; m_apb_dp_ctrl_pwrite_next = 1'b0; m_apb_dp_ctrl_pwdata_next = '0; m_apb_dp_ctrl_pstrb_next = '1; state_next = STATE_PTP_READ_2; end else begin state_next = STATE_PTP_READ_1; end end STATE_PTP_READ_2: begin // store read value and iterate cmd_ram_wr_data = m_apb_dp_ctrl.prdata; cmd_ram_wr_addr = cmd_ptr_reg; cmd_ram_wr_en = 1'b1; if (m_apb_dp_ctrl.pready) begin cnt_next = cnt_reg + 1; cmd_ptr_next = cmd_ptr_reg + 1; dp_ptr_next = dp_ptr_reg + 4; if (cnt_reg == 11) begin // done m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_OK; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = 1'b0; state_next = STATE_SEND_RSP; end else if (cnt_reg == 7) begin // jump to period registers dp_ptr_next = DP_APB_ADDR_W'(PTP_BASE_ADDR_DP + 'h70); state_next = STATE_PTP_READ_1; end else begin // more to read state_next = STATE_PTP_READ_1; end end else begin state_next = STATE_PTP_READ_2; end end STATE_PTP_SET: begin // update PTP registers if (!m_apb_dp_ctrl_psel_reg) begin cnt_next = cnt_reg + 1; dp_ptr_next = dp_ptr_reg + 4; case (cnt_reg[3:0]) 4'd0: begin // offset ToD cmd_ram_rd_addr = 3; m_apb_dp_ctrl_psel_next = flags_reg[1]; end 4'd1: begin // set ToD ns cmd_ram_rd_addr = 3; m_apb_dp_ctrl_psel_next = flags_reg[0]; end 4'd2: begin // set ToD sec l cmd_ram_rd_addr = 4; m_apb_dp_ctrl_psel_next = flags_reg[0]; end 4'd3: begin // set ToD sec h cmd_ram_rd_addr = 5; m_apb_dp_ctrl_psel_next = flags_reg[0]; end 4'd4: begin // set rel ns l cmd_ram_rd_addr = 6; m_apb_dp_ctrl_psel_next = flags_reg[2]; end 4'd5: begin // set rel ns h cmd_ram_rd_addr = 7; m_apb_dp_ctrl_psel_next = flags_reg[2]; end 4'd6: begin // offset rel cmd_ram_rd_addr = 6; m_apb_dp_ctrl_psel_next = flags_reg[3]; end 4'd7: begin // offset FNS cmd_ram_rd_addr = 2; m_apb_dp_ctrl_psel_next = flags_reg[4]; end 4'd10: begin // period fns cmd_ram_rd_addr = 12; m_apb_dp_ctrl_psel_next = flags_reg[7]; end 4'd11: begin // period ns cmd_ram_rd_addr = 13; m_apb_dp_ctrl_psel_next = flags_reg[7]; end default: begin // skip m_apb_dp_ctrl_psel_next = 1'b0; end endcase m_apb_dp_ctrl_paddr_next = dp_ptr_reg; m_apb_dp_ctrl_pwrite_next = 1'b1; m_apb_dp_ctrl_pwdata_next = cmd_ram_rd_data; m_apb_dp_ctrl_pstrb_next = '1; if (cnt_reg == 11) begin // done m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_OK; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = 1'b0; state_next = STATE_SEND_RSP; end else begin // loop state_next = STATE_PTP_SET; end end else begin state_next = STATE_PTP_SET; end end STATE_BOARD_CMD: begin // send board command cmd_ram_rd_addr = cmd_ptr_reg; if (m_axis_brd_ctrl_cmd.tready || !m_axis_brd_ctrl_cmd.tvalid) begin m_axis_brd_ctrl_cmd_tdata_next = cmd_ram_rd_data; m_axis_brd_ctrl_cmd_tvalid_next = 1'b1; m_axis_brd_ctrl_cmd_tlast_next = &cmd_ptr_reg; cmd_ptr_next = cmd_ptr_reg + 1; if (&cmd_ptr_reg) begin cmd_ptr_next = 2; state_next = STATE_BOARD_RSP; end else begin state_next = STATE_BOARD_CMD; end end else begin state_next = STATE_BOARD_CMD; end end STATE_BOARD_RSP: begin // store response s_axis_brd_ctrl_rsp_tready_next = 1'b1; cmd_ram_wr_data = s_axis_brd_ctrl_rsp.tdata; cmd_ram_wr_addr = cmd_ptr_reg; cmd_ram_wr_en = 1'b1; if (s_axis_brd_ctrl_rsp.tready && s_axis_brd_ctrl_rsp.tvalid) begin cmd_ptr_next = cmd_ptr_reg + 1; if (s_axis_brd_ctrl_rsp.tlast) begin m_axis_rsp_tdata_next[15:0] = '0; // rsvd m_axis_rsp_tdata_next[31:16] = CMD_STS_OK; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = 1'b0; state_next = STATE_SEND_RSP; end else begin state_next = STATE_BOARD_RSP; end end else begin state_next = STATE_BOARD_RSP; end end STATE_SEND_RSP: begin // send response in the form of an edited command cmd_ram_rd_addr = rsp_rd_ptr_reg; if (m_axis_rsp.tready || !m_axis_rsp.tvalid) begin m_axis_rsp_tdata_next = cmd_ram_rd_data; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = &rsp_rd_ptr_reg; if (&rsp_rd_ptr_reg) begin state_next = STATE_IDLE; end else begin state_next = STATE_SEND_RSP; end end else begin state_next = STATE_SEND_RSP; end end STATE_PAD_RSP: begin // zero pad response if (m_axis_rsp.tready || !m_axis_rsp.tvalid) begin m_axis_rsp_tdata_next = '0; m_axis_rsp_tvalid_next = 1'b1; m_axis_rsp_tlast_next = &rsp_rd_ptr_reg; if (&rsp_rd_ptr_reg) begin state_next = STATE_IDLE; end else begin state_next = STATE_PAD_RSP; end end else begin state_next = STATE_PAD_RSP; end end default: begin // unknown state; return to idle state_next = STATE_IDLE; end endcase if (drop_cmd_reg) begin s_axis_cmd_tready_next = 1'b1; if (s_axis_cmd.tready && s_axis_cmd.tvalid) begin drop_cmd_next = !s_axis_cmd.tlast; end end if (m_axis_rsp_tvalid_next && (!m_axis_rsp_tvalid_reg || m_axis_rsp.tready)) begin if (m_axis_rsp_tlast_next) begin rsp_rd_ptr_next = '0; end else begin rsp_rd_ptr_next = rsp_rd_ptr_reg + 1; rsp_frame_next = 1'b1; end end if (m_axis_rsp.tready && m_axis_rsp.tvalid) begin if (m_axis_rsp.tlast) begin rsp_frame_next = 1'b0; rsp_rd_ptr_next = '0; end end end always_ff @(posedge clk) begin if (cmd_ram_wr_en) begin cmd_ram[cmd_ram_wr_addr] = cmd_ram_wr_data; end end always_ff @(posedge clk) begin state_reg <= state_next; s_axis_cmd_tready_reg <= s_axis_cmd_tready_next; m_axis_rsp_tdata_reg <= m_axis_rsp_tdata_next; m_axis_rsp_tvalid_reg <= m_axis_rsp_tvalid_next; m_axis_rsp_tlast_reg <= m_axis_rsp_tlast_next; m_axis_rsp_tid_reg <= m_axis_rsp_tid_next; m_apb_dp_ctrl_paddr_reg <= m_apb_dp_ctrl_paddr_next; m_apb_dp_ctrl_psel_reg <= m_apb_dp_ctrl_psel_next; m_apb_dp_ctrl_penable_reg <= m_apb_dp_ctrl_penable_next; m_apb_dp_ctrl_pwrite_reg <= m_apb_dp_ctrl_pwrite_next; m_apb_dp_ctrl_pwdata_reg <= m_apb_dp_ctrl_pwdata_next; m_apb_dp_ctrl_pstrb_reg <= m_apb_dp_ctrl_pstrb_next; m_axis_brd_ctrl_cmd_tdata_reg <= m_axis_brd_ctrl_cmd_tdata_next; m_axis_brd_ctrl_cmd_tvalid_reg <= m_axis_brd_ctrl_cmd_tvalid_next; m_axis_brd_ctrl_cmd_tlast_reg <= m_axis_brd_ctrl_cmd_tlast_next; m_axis_brd_ctrl_cmd_tid_reg <= m_axis_brd_ctrl_cmd_tid_next; s_axis_brd_ctrl_rsp_tready_reg <= s_axis_brd_ctrl_rsp_tready_next; cmd_frame_reg <= cmd_frame_next; cmd_wr_ptr_reg <= cmd_wr_ptr_next; rsp_frame_reg <= rsp_frame_next; rsp_rd_ptr_reg <= rsp_rd_ptr_next; drop_cmd_reg <= drop_cmd_next; opcode_reg <= opcode_next; flags_reg <= flags_next; dw2_reg <= dw2_next; dw3_reg <= dw3_next; dw4_reg <= dw4_next; qtype_reg <= qtype_next; cmd_ptr_reg <= cmd_ptr_next; dp_ptr_reg <= dp_ptr_next; host_ptr_reg <= host_ptr_next; cnt_reg <= cnt_next; if (rst) begin state_reg <= STATE_IDLE; s_axis_cmd_tready_reg <= 1'b0; m_axis_rsp_tvalid_reg <= 1'b0; m_apb_dp_ctrl_psel_reg <= 1'b0; m_apb_dp_ctrl_penable_reg <= 1'b0; m_axis_brd_ctrl_cmd_tvalid_reg <= 1'b0; s_axis_brd_ctrl_rsp_tready_reg <= 1'b0; cmd_frame_reg <= 1'b0; cmd_wr_ptr_reg <= '0; rsp_frame_reg <= 1'b0; rsp_rd_ptr_reg <= '0; drop_cmd_reg <= 1'b0; end end endmodule `resetall