From 88310fd348e4928c3e81a1ebdc606a02ee64c2d2 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Sat, 14 Feb 2026 00:44:42 -0800 Subject: [PATCH] cndm: Add PTP support Signed-off-by: Alex Forencich --- src/cndm/board/AS02MC04/fpga/fpga/Makefile | 3 + .../board/AS02MC04/fpga/fpga_10g/Makefile | 3 + src/cndm/board/AS02MC04/fpga/rtl/fpga.sv | 3 + src/cndm/board/AS02MC04/fpga/rtl/fpga_core.sv | 65 +++++- .../board/AS02MC04/fpga/tb/fpga_core/Makefile | 1 + .../fpga/tb/fpga_core/test_fpga_core.py | 1 + .../fpga/tb/fpga_core/test_fpga_core.sv | 4 + src/cndm/modules/cndm/Makefile | 1 + src/cndm/modules/cndm/cndm.h | 17 +- src/cndm/modules/cndm/cndm_ethtool.c | 32 +++ src/cndm/modules/cndm/cndm_main.c | 4 + src/cndm/modules/cndm/cndm_netdev.c | 82 ++++++++ src/cndm/modules/cndm/cndm_ptp.c | 192 ++++++++++++++++++ src/cndm/modules/cndm/cndm_rx.c | 7 +- src/cndm/modules/cndm/cndm_tx.c | 21 ++ src/cndm/rtl/cndm_micro_core.f | 4 +- src/cndm/rtl/cndm_micro_core.sv | 129 ++++++------ src/cndm/rtl/cndm_micro_pcie_us.sv | 46 ++++- src/cndm/rtl/cndm_micro_port.sv | 34 +++- src/cndm/rtl/cndm_micro_rx.sv | 96 ++++++++- src/cndm/rtl/cndm_micro_tx.sv | 79 ++++++- src/cndm/tb/cndm_micro_pcie_us/Makefile | 4 + .../test_cndm_micro_pcie_us.py | 17 ++ .../test_cndm_micro_pcie_us.sv | 44 +++- 24 files changed, 804 insertions(+), 85 deletions(-) create mode 100644 src/cndm/modules/cndm/cndm_ptp.c diff --git a/src/cndm/board/AS02MC04/fpga/fpga/Makefile b/src/cndm/board/AS02MC04/fpga/fpga/Makefile index c960bba..85c982a 100644 --- a/src/cndm/board/AS02MC04/fpga/fpga/Makefile +++ b/src/cndm/board/AS02MC04/fpga/fpga/Makefile @@ -28,6 +28,9 @@ SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_signal.sv XDC_FILES = ../fpga.xdc XDC_FILES += $(TAXI_SRC_DIR)/eth/syn/vivado/taxi_eth_mac_fifo.tcl XDC_FILES += $(TAXI_SRC_DIR)/axis/syn/vivado/taxi_axis_async_fifo.tcl +XDC_FILES += $(TAXI_SRC_DIR)/ptp/syn/vivado/taxi_ptp_td_leaf.tcl +XDC_FILES += $(TAXI_SRC_DIR)/ptp/syn/vivado/taxi_ptp_td_phc_regs.tcl +XDC_FILES += $(TAXI_SRC_DIR)/ptp/syn/vivado/taxi_ptp_td_rel2tod.tcl XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_reset.tcl XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_signal.tcl diff --git a/src/cndm/board/AS02MC04/fpga/fpga_10g/Makefile b/src/cndm/board/AS02MC04/fpga/fpga_10g/Makefile index 1229d9e..7ea2ef8 100644 --- a/src/cndm/board/AS02MC04/fpga/fpga_10g/Makefile +++ b/src/cndm/board/AS02MC04/fpga/fpga_10g/Makefile @@ -28,6 +28,9 @@ SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_signal.sv XDC_FILES = ../fpga.xdc XDC_FILES += $(TAXI_SRC_DIR)/eth/syn/vivado/taxi_eth_mac_fifo.tcl XDC_FILES += $(TAXI_SRC_DIR)/axis/syn/vivado/taxi_axis_async_fifo.tcl +XDC_FILES += $(TAXI_SRC_DIR)/ptp/syn/vivado/taxi_ptp_td_leaf.tcl +XDC_FILES += $(TAXI_SRC_DIR)/ptp/syn/vivado/taxi_ptp_td_phc_regs.tcl +XDC_FILES += $(TAXI_SRC_DIR)/ptp/syn/vivado/taxi_ptp_td_rel2tod.tcl XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_reset.tcl XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_signal.tcl diff --git a/src/cndm/board/AS02MC04/fpga/rtl/fpga.sv b/src/cndm/board/AS02MC04/fpga/rtl/fpga.sv index c18d1e8..340e7cc 100644 --- a/src/cndm/board/AS02MC04/fpga/rtl/fpga.sv +++ b/src/cndm/board/AS02MC04/fpga/rtl/fpga.sv @@ -23,6 +23,8 @@ module fpga # parameter string VENDOR = "XILINX", // device family parameter string FAMILY = "kintexuplus", + // PTP configuration + parameter logic PTP_TS_EN = 1'b1, // 10G/25G MAC configuration parameter logic CFG_LOW_LATENCY = 1'b1, parameter logic COMBINED_MAC_PCS = 1'b1, @@ -491,6 +493,7 @@ fpga_core #( .SIM(SIM), .VENDOR(VENDOR), .FAMILY(FAMILY), + .PTP_TS_EN(PTP_TS_EN), .CFG_LOW_LATENCY(CFG_LOW_LATENCY), .COMBINED_MAC_PCS(COMBINED_MAC_PCS), .MAC_DATA_W(MAC_DATA_W) diff --git a/src/cndm/board/AS02MC04/fpga/rtl/fpga_core.sv b/src/cndm/board/AS02MC04/fpga/rtl/fpga_core.sv index be48c6b..9d94853 100644 --- a/src/cndm/board/AS02MC04/fpga/rtl/fpga_core.sv +++ b/src/cndm/board/AS02MC04/fpga/rtl/fpga_core.sv @@ -24,6 +24,8 @@ module fpga_core # // device family parameter string FAMILY = "kintexuplus", parameter RQ_SEQ_NUM_W = 6, + // PTP configuration + parameter logic PTP_TS_EN = 1'b1, // 10G/25G MAC configuration parameter logic CFG_LOW_LATENCY = 1'b1, parameter logic COMBINED_MAC_PCS = 1'b1, @@ -114,6 +116,9 @@ module fpga_core # output wire [7:0] cfg_interrupt_msi_function_number ); +localparam logic PTP_TS_FMT_TOD = 1'b0; +localparam PTP_TS_W = PTP_TS_FMT_TOD ? 96 : 48; + // SFP+ wire sfp_tx_clk[2]; wire sfp_tx_rst[2]; @@ -126,7 +131,7 @@ assign sfp_led[0] = !sfp_rx_status[0]; assign sfp_led[1] = !sfp_rx_status[1]; assign led = '1; assign led_r = 1'b1; -assign led_g = 1'b1; +// assign led_g = 1'b1; localparam HB_COUNT = 62500000; localparam CL_HB_COUNT = $clog2(HB_COUNT); @@ -160,8 +165,8 @@ assign sfp_mgt_refclk_out = sfp_mgt_refclk_bufg; wire sfp_rst; taxi_axis_if #(.DATA_W(MAC_DATA_W), .ID_W(8), .USER_EN(1), .USER_W(1)) axis_sfp_tx[2](); -taxi_axis_if #(.DATA_W(96), .KEEP_W(1), .ID_W(8)) axis_sfp_tx_cpl[2](); -taxi_axis_if #(.DATA_W(MAC_DATA_W), .ID_W(8), .USER_EN(1), .USER_W(1)) axis_sfp_rx[2](); +taxi_axis_if #(.DATA_W(PTP_TS_W), .KEEP_W(1), .ID_W(8)) axis_sfp_tx_cpl[2](); +taxi_axis_if #(.DATA_W(MAC_DATA_W), .ID_W(8), .USER_EN(1), .USER_W(1+PTP_TS_W)) axis_sfp_rx[2](); taxi_axis_if #(.DATA_W(16), .KEEP_W(1), .KEEP_EN(0), .LAST_EN(0), .USER_EN(1), .USER_W(1), .ID_EN(1), .ID_W(8)) axis_sfp_stat(); if (SIM) begin @@ -207,6 +212,15 @@ taxi_apb_if #( ) gt_apb_ctrl(); +wire ptp_clk = sfp_mgt_refclk_bufg; +wire ptp_rst = sfp_rst; +wire ptp_sample_clk = clk_125mhz; +wire ptp_td_sd; +wire ptp_pps; +wire ptp_pps_str; + +assign led_g = ptp_pps_str; + taxi_eth_mac_25g_us #( .SIM(SIM), .VENDOR(VENDOR), @@ -230,9 +244,11 @@ taxi_eth_mac_25g_us #( .PADDING_EN(1'b1), .DIC_EN(1'b1), .MIN_FRAME_LEN(64), - .PTP_TS_EN(1'b0), - .PTP_TS_FMT_TOD(1'b1), - .PTP_TS_W(96), + .PTP_TS_EN(PTP_TS_EN), + .PTP_TD_EN(PTP_TS_EN), + .PTP_TS_FMT_TOD(PTP_TS_FMT_TOD), + .PTP_TS_W(PTP_TS_W), + .PTP_TD_SDI_PIPELINE(2), .PRBS31_EN(1'b0), .TX_SERDES_PIPELINE(1), .RX_SERDES_PIPELINE(1), @@ -284,7 +300,6 @@ sfp_mac_inst ( .tx_clk(sfp_tx_clk), .tx_rst_in('{2{1'b0}}), .tx_rst_out(sfp_tx_rst), - .ptp_sample_clk('{2{1'b0}}), /* * Transmit interface (AXI stream) @@ -300,10 +315,18 @@ sfp_mac_inst ( /* * PTP clock */ - .tx_ptp_ts('{2{'0}}), - .tx_ptp_ts_step('{2{1'b0}}), - .rx_ptp_ts('{2{'0}}), - .rx_ptp_ts_step('{2{1'b0}}), + .ptp_clk(ptp_clk), + .ptp_rst(ptp_rst), + .ptp_sample_clk(ptp_sample_clk), + .ptp_td_sdi(ptp_td_sd), + .tx_ptp_ts_in('{2{'0}}), + .tx_ptp_ts_out(), + .tx_ptp_ts_step_out(), + .tx_ptp_locked(), + .rx_ptp_ts_in('{2{'0}}), + .rx_ptp_ts_out(), + .rx_ptp_ts_step_out(), + .rx_ptp_locked(), /* * Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE) @@ -440,6 +463,9 @@ cndm_micro_pcie_us #( .VENDOR(VENDOR), .FAMILY(FAMILY), .PORTS(2), + .PTP_TS_EN(PTP_TS_EN), + .PTP_CLK_PER_NS_NUM(32), + .PTP_CLK_PER_NS_DENOM(5), .RQ_SEQ_NUM_W(RQ_SEQ_NUM_W), .BAR0_APERTURE(24) ) @@ -497,6 +523,23 @@ cndm_inst ( .cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag), .cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number), + /* + * PTP + */ + .ptp_clk(ptp_clk), + .ptp_rst(ptp_rst), + .ptp_sample_clk(ptp_sample_clk), + .ptp_td_sdo(ptp_td_sd), + .ptp_pps(ptp_pps), + .ptp_pps_str(ptp_pps_str), + .ptp_sync_locked(), + .ptp_sync_ts_rel(), + .ptp_sync_ts_rel_step(), + .ptp_sync_ts_tod(), + .ptp_sync_ts_tod_step(), + .ptp_sync_pps(), + .ptp_sync_pps_str(), + /* * Ethernet */ diff --git a/src/cndm/board/AS02MC04/fpga/tb/fpga_core/Makefile b/src/cndm/board/AS02MC04/fpga/tb/fpga_core/Makefile index 5fbbbac..a250103 100644 --- a/src/cndm/board/AS02MC04/fpga/tb/fpga_core/Makefile +++ b/src/cndm/board/AS02MC04/fpga/tb/fpga_core/Makefile @@ -40,6 +40,7 @@ VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES))) export PARAM_SIM := "1'b1" export PARAM_VENDOR := "\"XILINX\"" export PARAM_FAMILY := "\"kintexuplus\"" +export PARAM_PTP_TS_EN := "1'b1" export PARAM_CFG_LOW_LATENCY := "1'b1" export PARAM_COMBINED_MAC_PCS := "1'b1" export PARAM_MAC_DATA_W := "64" diff --git a/src/cndm/board/AS02MC04/fpga/tb/fpga_core/test_fpga_core.py b/src/cndm/board/AS02MC04/fpga/tb/fpga_core/test_fpga_core.py index fc6318d..6951259 100644 --- a/src/cndm/board/AS02MC04/fpga/tb/fpga_core/test_fpga_core.py +++ b/src/cndm/board/AS02MC04/fpga/tb/fpga_core/test_fpga_core.py @@ -486,6 +486,7 @@ def test_fpga_core(request, mac_data_w): parameters['SIM'] = "1'b1" parameters['VENDOR'] = "\"XILINX\"" parameters['FAMILY'] = "\"kintexuplus\"" + parameters['PTP_TS_EN'] = "1'b1" parameters['CFG_LOW_LATENCY'] = "1'b1" parameters['COMBINED_MAC_PCS'] = "1'b1" parameters['MAC_DATA_W'] = mac_data_w diff --git a/src/cndm/board/AS02MC04/fpga/tb/fpga_core/test_fpga_core.sv b/src/cndm/board/AS02MC04/fpga/tb/fpga_core/test_fpga_core.sv index 00866d9..e6e8b5f 100644 --- a/src/cndm/board/AS02MC04/fpga/tb/fpga_core/test_fpga_core.sv +++ b/src/cndm/board/AS02MC04/fpga/tb/fpga_core/test_fpga_core.sv @@ -26,6 +26,8 @@ module test_fpga_core # parameter AXIS_PCIE_RQ_USER_W = AXIS_PCIE_DATA_W < 512 ? 62 : 137, parameter AXIS_PCIE_CQ_USER_W = AXIS_PCIE_DATA_W < 512 ? 85 : 183, parameter AXIS_PCIE_CC_USER_W = AXIS_PCIE_DATA_W < 512 ? 33 : 81, + // PTP configuration + parameter logic PTP_TS_EN = 1'b1, // 10G/25G MAC configuration parameter logic CFG_LOW_LATENCY = 1'b1, parameter logic COMBINED_MAC_PCS = 1'b1, @@ -137,6 +139,8 @@ fpga_core #( .VENDOR(VENDOR), .FAMILY(FAMILY), .RQ_SEQ_NUM_W(RQ_SEQ_NUM_W), + // PTP configuration + .PTP_TS_EN(PTP_TS_EN), // 10G/25G MAC configuration .CFG_LOW_LATENCY(CFG_LOW_LATENCY), .COMBINED_MAC_PCS(COMBINED_MAC_PCS), diff --git a/src/cndm/modules/cndm/Makefile b/src/cndm/modules/cndm/Makefile index 91d6e40..d66c86e 100644 --- a/src/cndm/modules/cndm/Makefile +++ b/src/cndm/modules/cndm/Makefile @@ -10,6 +10,7 @@ cndm-y += cndm_irq.o 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 diff --git a/src/cndm/modules/cndm/cndm.h b/src/cndm/modules/cndm/cndm.h index 5187e97..7a36b9d 100644 --- a/src/cndm/modules/cndm/cndm.h +++ b/src/cndm/modules/cndm/cndm.h @@ -14,6 +14,7 @@ Authors: #include #include #include +#include #include #include #include @@ -52,7 +53,7 @@ struct cndm_dev { u32 port_offset; u32 port_stride; - void __iomem *ptp_regs; + void __iomem *phc_regs; struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_clock_info; }; @@ -61,6 +62,7 @@ struct cndm_tx_info { struct sk_buff *skb; dma_addr_t dma_addr; u32 len; + int ts_requested; }; struct cndm_rx_info { @@ -86,6 +88,10 @@ struct cndm_priv { 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; @@ -140,7 +146,9 @@ struct cndm_desc { struct cndm_cpl { __u8 rsvd[4]; __le32 len; - __u8 rsvd2[7]; + __le32 ts_ns; + __le16 ts_fns; + __u8 ts_s; __u8 phase; }; @@ -162,6 +170,11 @@ extern const struct file_operations cndm_fops; // cndm_ethtool.c 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); +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); int cndm_poll_tx_cq(struct napi_struct *napi, int budget); diff --git a/src/cndm/modules/cndm/cndm_ethtool.c b/src/cndm/modules/cndm/cndm_ethtool.c index 9b4d0f8..30f30c8 100644 --- a/src/cndm/modules/cndm/cndm_ethtool.c +++ b/src/cndm/modules/cndm/cndm_ethtool.c @@ -11,6 +11,7 @@ Authors: #include "cndm.h" #include +#include static void cndm_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *drvinfo) @@ -24,6 +25,37 @@ static void cndm_get_drvinfo(struct net_device *ndev, strscpy(drvinfo->bus_info, dev_name(cdev->dev), sizeof(drvinfo->bus_info)); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0) +static int cndm_get_ts_info(struct net_device *ndev, + struct kernel_ethtool_ts_info *info) +#else +static int cndm_get_ts_info(struct net_device *ndev, + struct ethtool_ts_info *info) +#endif +{ + struct cndm_priv *priv = netdev_priv(ndev); + struct cndm_dev *cdev = priv->cdev; + + ethtool_op_get_ts_info(ndev, info); + + if (cdev->ptp_clock) + info->phc_index = ptp_clock_index(cdev->ptp_clock); + + // if (!(priv->if_features & cndm_IF_FEATURE_PTP_TS) || !cdev->ptp_clock) + // return 0; + + info->so_timestamping |= SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE; + + info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); + + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL); + + return 0; +} + const struct ethtool_ops cndm_ethtool_ops = { .get_drvinfo = cndm_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_ts_info = cndm_get_ts_info, }; diff --git a/src/cndm/modules/cndm/cndm_main.c b/src/cndm/modules/cndm/cndm_main.c index a7d012b..c0c11d6 100644 --- a/src/cndm/modules/cndm/cndm_main.c +++ b/src/cndm/modules/cndm/cndm_main.c @@ -63,6 +63,8 @@ static int cndm_common_probe(struct cndm_dev *cdev) if (cdev->port_count > ARRAY_SIZE(cdev->ndev)) cdev->port_count = ARRAY_SIZE(cdev->ndev); + cndm_register_phc(cdev); + for (k = 0; k < cdev->port_count; k++) { struct net_device *ndev; @@ -113,6 +115,8 @@ static void cndm_common_remove(struct cndm_dev *cdev) } } + cndm_unregister_phc(cdev); + devlink_unregister(devlink); } diff --git a/src/cndm/modules/cndm/cndm_netdev.c b/src/cndm/modules/cndm/cndm_netdev.c index 9eeb58d..d562260 100644 --- a/src/cndm/modules/cndm/cndm_netdev.c +++ b/src/cndm/modules/cndm/cndm_netdev.c @@ -52,6 +52,67 @@ static int cndm_close(struct net_device *ndev) return 0; } +static int cndm_hwtstamp_set(struct net_device *ndev, struct ifreq *ifr) +{ + struct cndm_priv *priv = netdev_priv(ndev); + struct hwtstamp_config hwts_config; + + if (copy_from_user(&hwts_config, ifr->ifr_data, sizeof(hwts_config))) + return -EFAULT; + + if (hwts_config.flags) + return -EINVAL; + + switch (hwts_config.tx_type) { + case HWTSTAMP_TX_OFF: + case HWTSTAMP_TX_ON: + break; + default: + return -ERANGE; + } + + switch (hwts_config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_SOME: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_NTP_ALL: + hwts_config.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + return -ERANGE; + } + + memcpy(&priv->hwts_config, &hwts_config, sizeof(hwts_config)); + + if (copy_to_user(ifr->ifr_data, &hwts_config, sizeof(hwts_config))) + return -EFAULT; + + return 0; +} + +static int cndm_hwtstamp_get(struct net_device *ndev, struct ifreq *ifr) +{ + struct cndm_priv *priv = netdev_priv(ndev); + + if (copy_to_user(ifr->ifr_data, &priv->hwts_config, sizeof(priv->hwts_config))) + return -EFAULT; + + return 0; +} + static int cndm_set_mac(struct net_device *ndev, void *addr) { struct sockaddr *saddr = addr; @@ -70,12 +131,29 @@ static int cndm_set_mac(struct net_device *ndev, void *addr) return 0; } +static int cndm_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCSHWTSTAMP: + return cndm_hwtstamp_set(ndev, ifr); + case SIOCGHWTSTAMP: + return cndm_hwtstamp_get(ndev, ifr); + default: + return -EOPNOTSUPP; + } +} + static const struct net_device_ops cndm_netdev_ops = { .ndo_open = cndm_open, .ndo_stop = cndm_close, .ndo_start_xmit = cndm_start_xmit, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = cndm_set_mac, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + .ndo_eth_ioctl = cndm_ioctl, +#else + .ndo_do_ioctl = cndm_ioctl, +#endif }; static int cndm_netdev_irq(struct notifier_block *nb, unsigned long action, void *data) @@ -124,6 +202,10 @@ struct net_device *cndm_create_netdev(struct cndm_dev *cdev, int port, void __io eth_hw_addr_random(ndev); + priv->hwts_config.flags = 0; + priv->hwts_config.tx_type = HWTSTAMP_TX_OFF; + priv->hwts_config.rx_filter = HWTSTAMP_FILTER_NONE; + ndev->netdev_ops = &cndm_netdev_ops; ndev->ethtool_ops = &cndm_ethtool_ops; diff --git a/src/cndm/modules/cndm/cndm_ptp.c b/src/cndm/modules/cndm/cndm_ptp.c new file mode 100644 index 0000000..a9e7c9b --- /dev/null +++ b/src/cndm/modules/cndm/cndm_ptp.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL +/* + +Copyright (c) 2025 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#include "cndm.h" +#include + +ktime_t cndm_read_cpl_ts(struct cndm_priv *priv, const struct cndm_cpl *cpl) +{ + struct cndm_dev *cdev = priv->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)) { + // seconds MSBs do not match, update cached timestamp + if (cdev->phc_regs) { + priv->ts_s = ioread32(cdev->phc_regs + 0x18); + priv->ts_s |= (u64) ioread32(cdev->phc_regs + 0x1C) << 32; + priv->ts_valid = 1; + } + } + + ts_s |= priv->ts_s & 0xfffffffffffffff0; + + return ktime_set(ts_s, ts_ns); +} + +static int cndm_phc_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct cndm_dev *cdev = container_of(ptp, struct cndm_dev, ptp_clock_info); + + bool neg = false; + u64 nom_per_fns, adj; + + dev_dbg(cdev->dev, "%s: scaled_ppm: %ld", __func__, scaled_ppm); + + if (scaled_ppm < 0) { + neg = true; + scaled_ppm = -scaled_ppm; + } + + nom_per_fns = ioread32(cdev->phc_regs + 0x70); + nom_per_fns |= (u64) ioread32(cdev->phc_regs + 0x74) << 32; + + if (nom_per_fns == 0) + nom_per_fns = 0x4ULL << 32; + + adj = div_u64(((nom_per_fns >> 16) * scaled_ppm) + 500000, 1000000); + + if (neg) + adj = nom_per_fns - adj; + else + adj = nom_per_fns + adj; + + iowrite32(adj & 0xffffffff, cdev->phc_regs + 0x78); + iowrite32(adj >> 32, cdev->phc_regs + 0x7C); + + dev_dbg(cdev->dev, "%s adj: 0x%llx", __func__, adj); + + return 0; +} + +static int cndm_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +{ + struct cndm_dev *cdev = container_of(ptp, struct cndm_dev, ptp_clock_info); + + ioread32(cdev->phc_regs + 0x30); + ts->tv_nsec = ioread32(cdev->phc_regs + 0x34); + ts->tv_sec = ioread32(cdev->phc_regs + 0x38); + ts->tv_sec |= (u64) ioread32(cdev->phc_regs + 0x3C) << 32; + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) +static int cndm_phc_gettimex(struct ptp_clock_info *ptp, struct timespec64 *ts, struct ptp_system_timestamp *sts) +{ + struct cndm_dev *cdev = container_of(ptp, struct cndm_dev, ptp_clock_info); + + ptp_read_system_prets(sts); + ioread32(cdev->phc_regs + 0x30); + ptp_read_system_postts(sts); + ts->tv_nsec = ioread32(cdev->phc_regs + 0x34); + ts->tv_sec = ioread32(cdev->phc_regs + 0x38); + ts->tv_sec |= (u64) ioread32(cdev->phc_regs + 0x3C) << 32; + + return 0; +} +#endif + +static int cndm_phc_settime(struct ptp_clock_info *ptp, const struct timespec64 *ts) +{ + struct cndm_dev *cdev = container_of(ptp, struct cndm_dev, ptp_clock_info); + + iowrite32(ts->tv_nsec, cdev->phc_regs + 0x54); + iowrite32(ts->tv_sec & 0xffffffff, cdev->phc_regs + 0x58); + iowrite32(ts->tv_sec >> 32, cdev->phc_regs + 0x5C); + + return 0; +} + +static int cndm_phc_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct cndm_dev *cdev = container_of(ptp, struct cndm_dev, ptp_clock_info); + struct timespec64 ts; + + dev_dbg(cdev->dev, "%s: delta: %lld", __func__, delta); + + if (delta > 536000000 || delta < -536000000) { + // for a large delta, perform a non-precision step + cndm_phc_gettime(ptp, &ts); + ts = timespec64_add(ts, ns_to_timespec64(delta)); + cndm_phc_settime(ptp, &ts); + } else { + // for a small delta, perform a precision atomic offset + iowrite32(delta & 0xffffffff, cdev->phc_regs + 0x50); + } + + return 0; +} + +static void cndm_phc_set_from_system_clock(struct ptp_clock_info *ptp) +{ + struct timespec64 ts; + +#ifdef ktime_get_clocktai_ts64 + ktime_get_clocktai_ts64(&ts); +#else + ts = ktime_to_timespec64(ktime_get_clocktai()); +#endif + + cndm_phc_settime(ptp, &ts); +} + +void cndm_register_phc(struct cndm_dev *cdev) +{ + if (cdev->ptp_clock) { + dev_warn(cdev->dev, "PTP clock already registered"); + return; + } + + // TODO + if (cdev->port_offset == 0x10000) { + dev_info(cdev->dev, "PTP clock not present"); + return; + } + + cdev->phc_regs = cdev->hw_addr + 0x10000; // TODO + + cdev->ptp_clock_info.owner = THIS_MODULE; + snprintf(cdev->ptp_clock_info.name, sizeof(cdev->ptp_clock_info.name), "%s_phc", cdev->name); + cdev->ptp_clock_info.max_adj = 1000000000; + cdev->ptp_clock_info.n_alarm = 0; + cdev->ptp_clock_info.n_ext_ts = 0; + cdev->ptp_clock_info.n_per_out = 0; + cdev->ptp_clock_info.n_pins = 0; + cdev->ptp_clock_info.pps = 0; + cdev->ptp_clock_info.adjfine = cndm_phc_adjfine; + cdev->ptp_clock_info.adjtime = cndm_phc_adjtime; + cdev->ptp_clock_info.gettime64 = cndm_phc_gettime; + cdev->ptp_clock_info.gettimex64 = cndm_phc_gettimex; + cdev->ptp_clock_info.settime64 = cndm_phc_settime; + + cdev->ptp_clock = ptp_clock_register(&cdev->ptp_clock_info, cdev->dev); + + if (IS_ERR(cdev->ptp_clock)) { + dev_err(cdev->dev, "failed to register PHC"); + cdev->ptp_clock = NULL; + return; + } + + dev_info(cdev->dev, "registered PHC (index %d)", ptp_clock_index(cdev->ptp_clock)); + + cndm_phc_set_from_system_clock(&cdev->ptp_clock_info); +} + +void cndm_unregister_phc(struct cndm_dev *cdev) +{ + if (cdev->ptp_clock) { + ptp_clock_unregister(cdev->ptp_clock); + cdev->ptp_clock = NULL; + dev_info(cdev->dev, "unregistered PHC"); + } +} diff --git a/src/cndm/modules/cndm/cndm_rx.c b/src/cndm/modules/cndm/cndm_rx.c index a4856d6..63f5fbd 100644 --- a/src/cndm/modules/cndm/cndm_rx.c +++ b/src/cndm/modules/cndm/cndm_rx.c @@ -143,15 +143,20 @@ static int cndm_process_rx_cq(struct net_device *ndev, int napi_budget) 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); - break; + __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; diff --git a/src/cndm/modules/cndm/cndm_tx.c b/src/cndm/modules/cndm/cndm_tx.c index 549b51f..f4cb8c6 100644 --- a/src/cndm/modules/cndm/cndm_tx.c +++ b/src/cndm/modules/cndm/cndm_tx.c @@ -43,7 +43,9 @@ int cndm_free_tx_buf(struct cndm_priv *priv) 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; @@ -64,6 +66,14 @@ static int cndm_process_tx_cq(struct net_device *ndev, int napi_budget) 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); @@ -100,6 +110,7 @@ int cndm_poll_tx_cq(struct napi_struct *napi, int budget) 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; @@ -123,6 +134,14 @@ int cndm_start_xmit(struct sk_buff *skb, struct net_device *ndev) 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); @@ -143,6 +162,8 @@ int cndm_start_xmit(struct sk_buff *skb, struct net_device *ndev) 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); diff --git a/src/cndm/rtl/cndm_micro_core.f b/src/cndm/rtl/cndm_micro_core.f index bc63c49..33b90a4 100644 --- a/src/cndm/rtl/cndm_micro_core.f +++ b/src/cndm/rtl/cndm_micro_core.f @@ -12,5 +12,5 @@ cndm_micro_cpl_wr.sv ../lib/taxi/src/axis/rtl/taxi_axis_async_fifo.f ../lib/taxi/src/axis/rtl/taxi_axis_arb_mux.f ../lib/taxi/src/axis/rtl/taxi_axis_demux.sv -../lib/taxi/src/ptp/rtl/taxi_ptp_td_phc.sv -../lib/taxi/src/ptp/rtl/taxi_ptp_td_leaf.sv +../lib/taxi/src/ptp/rtl/taxi_ptp_td_phc_axil.f +../lib/taxi/src/ptp/rtl/taxi_ptp_td_rel2tod.sv diff --git a/src/cndm/rtl/cndm_micro_core.sv b/src/cndm/rtl/cndm_micro_core.sv index e53fc50..23d71da 100644 --- a/src/cndm/rtl/cndm_micro_core.sv +++ b/src/cndm/rtl/cndm_micro_core.sv @@ -16,10 +16,11 @@ Authors: * Corundum-micro core logic */ module cndm_micro_core #( - parameter PORTS = 2//, - // parameter logic PTP_TS_EN = 1'b1, - // parameter PTP_CLK_PER_NS_NUM = 512, - // parameter PTP_CLK_PER_NS_DENOM = 165 + parameter PORTS = 2, + parameter logic PTP_TS_EN = 1'b1, + parameter logic PTP_TS_FMT_TOD = 1'b0, + parameter PTP_CLK_PER_NS_NUM = 512, + parameter PTP_CLK_PER_NS_DENOM = 165 ) ( input wire logic clk, @@ -46,19 +47,20 @@ module cndm_micro_core #( /* * PTP */ - // input wire logic ptp_clk = 1'b0, - // input wire logic ptp_rst = 1'b0, - // input wire logic ptp_sample_clk = 1'b0, - // output wire logic ptp_td_sdo, - // output wire logic ptp_pps, - // output wire logic ptp_pps_str, - // output wire logic ptp_sync_locked, - // output wire logic [63:0] ptp_sync_ts_rel, - // output wire logic ptp_sync_ts_rel_step, - // output wire logic [95:0] ptp_sync_ts_tod, - // output wire logic ptp_sync_ts_tod_step, - // output wire logic ptp_sync_pps, - // output wire logic ptp_sync_pps_str, + input wire logic ptp_clk = 1'b0, + input wire logic ptp_rst = 1'b0, + input wire logic ptp_sample_clk = 1'b0, + input wire logic ptp_td_sdi = 1'b0, + output wire logic ptp_td_sdo, + output wire logic ptp_pps, + output wire logic ptp_pps_str, + output wire logic ptp_sync_locked, + output wire logic [63:0] ptp_sync_ts_rel, + output wire logic ptp_sync_ts_rel_step, + output wire logic [95:0] ptp_sync_ts_tod, + output wire logic ptp_sync_ts_tod_step, + output wire logic ptp_sync_pps, + output wire logic ptp_sync_pps_str, /* * Ethernet @@ -84,8 +86,7 @@ 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; -localparam PORT_OFFSET = 1; -// localparam PORT_OFFSET = PTP_TS_EN ? 2 : 1; +localparam PORT_OFFSET = PTP_TS_EN ? 2 : 1; taxi_axil_if #( .DATA_W(s_axil_wr.DATA_W), @@ -182,8 +183,7 @@ always_ff @(posedge clk) begin case ({s_axil_ctrl[0].araddr[15:2], 2'b00}) 16'h0100: s_axil_rdata_reg <= PORTS; // port count - 16'h0104: s_axil_rdata_reg <= 32'h00010000; // port offset - // 16'h0104: s_axil_rdata_reg <= PTP_TS_EN ? 32'h00020000 : 32'h00010000; // port offset + 16'h0104: s_axil_rdata_reg <= PTP_TS_EN ? 32'h00020000 : 32'h00010000; // port offset 16'h0108: s_axil_rdata_reg <= 32'h00010000; // port stride default: begin end endcase @@ -199,47 +199,54 @@ always_ff @(posedge clk) begin end end -// if (PTP_TS_EN) begin : ptp +if (PTP_TS_EN) begin : ptp -// cndm_micro_ptp #( -// .PTP_CLK_PER_NS_NUM(PTP_CLK_PER_NS_NUM), -// .PTP_CLK_PER_NS_DENOM(PTP_CLK_PER_NS_DENOM) -// ) -// ptp_inst ( -// .clk(clk), -// .rst(rst), + taxi_ptp_td_phc_axil #( + .PTP_CLK_PER_NS_NUM(PTP_CLK_PER_NS_NUM), + .PTP_CLK_PER_NS_DENOM(PTP_CLK_PER_NS_DENOM) + ) + ptp_inst ( + .clk(clk), + .rst(rst), -// /* -// * Control register interface -// */ -// .s_axil_wr(s_axil_ctrl[1]), -// .s_axil_rd(s_axil_ctrl[1]), + /* + * Control register interface + */ + .s_axil_wr(s_axil_ctrl[1]), + .s_axil_rd(s_axil_ctrl[1]), -// /* -// * PTP -// */ -// .ptp_clk(ptp_clk), -// .ptp_rst(ptp_rst), -// .ptp_sample_clk(ptp_sample_clk), -// .ptp_td_sdo(ptp_td_sdo), -// .ptp_pps(ptp_pps), -// .ptp_pps_str(ptp_pps_str), -// .ptp_sync_locked(ptp_sync_locked), -// .ptp_sync_ts_rel(ptp_sync_ts_rel), -// .ptp_sync_ts_rel_step(ptp_sync_ts_rel_step), -// .ptp_sync_ts_tod(ptp_sync_ts_tod), -// .ptp_sync_ts_tod_step(ptp_sync_ts_tod_step), -// .ptp_sync_pps(ptp_sync_pps), -// .ptp_sync_pps_str(ptp_sync_pps_str) -// ); + /* + * PTP + */ + .ptp_clk(ptp_clk), + .ptp_rst(ptp_rst), + .ptp_sample_clk(ptp_sample_clk), + .ptp_td_sdo(ptp_td_sdo), + .ptp_pps(ptp_pps), + .ptp_pps_str(ptp_pps_str), + .ptp_sync_locked(ptp_sync_locked), + .ptp_sync_ts_rel(ptp_sync_ts_rel), + .ptp_sync_ts_rel_step(ptp_sync_ts_rel_step), + .ptp_sync_ts_tod(ptp_sync_ts_tod), + .ptp_sync_ts_tod_step(ptp_sync_ts_tod_step), + .ptp_sync_pps(ptp_sync_pps), + .ptp_sync_pps_str(ptp_sync_pps_str) + ); -// end else begin : ptp +end else begin : ptp -// assign ptp_td_sdo = 1'b0; -// assign ptp_pps = 1'b0; -// assign ptp_pps_str = 1'b0; + assign ptp_td_sdo = 1'b0; + assign ptp_pps = 1'b0; + assign ptp_pps_str = 1'b0; + assign ptp_sync_locked = 1'b0; + assign ptp_sync_ts_rel = '0; + assign ptp_sync_ts_rel_step = 1'b0; + assign ptp_sync_ts_tod = '0; + assign ptp_sync_ts_tod_step = 1'b0; + assign ptp_sync_pps = 1'b0; + assign ptp_sync_pps_str = 1'b0; -// end +end taxi_dma_desc_if #( .SRC_ADDR_W(dma_rd_desc_req.SRC_ADDR_W), @@ -325,7 +332,8 @@ dma_mux_inst ( for (genvar p = 0; p < PORTS; p = p + 1) begin : port cndm_micro_port #( - // .PTP_TS_EN(PTP_TS_EN) + .PTP_TS_EN(PTP_TS_EN), + .PTP_TS_FMT_TOD(PTP_TS_FMT_TOD) ) port_inst ( .clk(clk), @@ -349,6 +357,13 @@ for (genvar p = 0; p < PORTS; p = p + 1) begin : port .irq(irq[p]), + /* + * PTP + */ + .ptp_clk(ptp_clk), + .ptp_rst(ptp_rst), + .ptp_td_sdi(ptp_td_sdo), + /* * Ethernet */ diff --git a/src/cndm/rtl/cndm_micro_pcie_us.sv b/src/cndm/rtl/cndm_micro_pcie_us.sv index fb56f11..cad06c7 100644 --- a/src/cndm/rtl/cndm_micro_pcie_us.sv +++ b/src/cndm/rtl/cndm_micro_pcie_us.sv @@ -23,6 +23,10 @@ module cndm_micro_pcie_us #( // device family parameter string FAMILY = "virtexuplus", parameter PORTS = 2, + parameter logic PTP_TS_EN = 1'b1, + parameter logic PTP_TS_FMT_TOD = 1'b0, + parameter PTP_CLK_PER_NS_NUM = 512, + parameter PTP_CLK_PER_NS_DENOM = 165, parameter RQ_SEQ_NUM_W = 6, parameter BAR0_APERTURE = 24 ) @@ -80,6 +84,24 @@ module cndm_micro_pcie_us #( output wire [7:0] cfg_interrupt_msi_tph_st_tag, output wire [7:0] cfg_interrupt_msi_function_number, + /* + * PTP + */ + input wire logic ptp_clk = 1'b0, + input wire logic ptp_rst = 1'b0, + input wire logic ptp_sample_clk = 1'b0, + input wire logic ptp_td_sdi = 1'b0, + output wire logic ptp_td_sdo, + output wire logic ptp_pps, + output wire logic ptp_pps_str, + output wire logic ptp_sync_locked, + output wire logic [63:0] ptp_sync_ts_rel, + output wire logic ptp_sync_ts_rel_step, + output wire logic [95:0] ptp_sync_ts_tod, + output wire logic ptp_sync_ts_tod_step, + output wire logic ptp_sync_pps, + output wire logic ptp_sync_pps_str, + /* * Ethernet */ @@ -455,7 +477,11 @@ msi_inst ( ); cndm_micro_core #( - .PORTS(PORTS) + .PORTS(PORTS), + .PTP_TS_EN(PTP_TS_EN), + .PTP_TS_FMT_TOD(PTP_TS_FMT_TOD), + .PTP_CLK_PER_NS_NUM(PTP_CLK_PER_NS_NUM), + .PTP_CLK_PER_NS_DENOM(PTP_CLK_PER_NS_DENOM) ) core_inst ( .clk(pcie_clk), @@ -479,6 +505,24 @@ core_inst ( .irq(irq), + /* + * PTP + */ + .ptp_clk(ptp_clk), + .ptp_rst(ptp_rst), + .ptp_sample_clk(ptp_sample_clk), + .ptp_td_sdi(ptp_td_sdi), + .ptp_td_sdo(ptp_td_sdo), + .ptp_pps(ptp_pps), + .ptp_pps_str(ptp_pps_str), + .ptp_sync_locked(ptp_sync_locked), + .ptp_sync_ts_rel(ptp_sync_ts_rel), + .ptp_sync_ts_rel_step(ptp_sync_ts_rel_step), + .ptp_sync_ts_tod(ptp_sync_ts_tod), + .ptp_sync_ts_tod_step(ptp_sync_ts_tod_step), + .ptp_sync_pps(ptp_sync_pps), + .ptp_sync_pps_str(ptp_sync_pps_str), + /* * Ethernet */ diff --git a/src/cndm/rtl/cndm_micro_port.sv b/src/cndm/rtl/cndm_micro_port.sv index 1a477b4..22b322b 100644 --- a/src/cndm/rtl/cndm_micro_port.sv +++ b/src/cndm/rtl/cndm_micro_port.sv @@ -16,7 +16,8 @@ Authors: * Corundum-micro port module */ module cndm_micro_port #( - parameter logic PTP_TS_EN = 1'b1 + parameter logic PTP_TS_EN = 1'b1, + parameter logic PTP_TS_FMT_TOD = 1'b0 ) ( input wire logic clk, @@ -40,6 +41,13 @@ module cndm_micro_port #( output wire logic irq, + /* + * PTP + */ + input wire logic ptp_clk = 1'b0, + input wire logic ptp_rst = 1'b0, + input wire logic ptp_td_sdi = 1'b0, + /* * Ethernet */ @@ -454,7 +462,7 @@ taxi_axis_if #( .KEEP_EN(mac_axis_tx_cpl.KEEP_EN), .KEEP_W(mac_axis_tx_cpl.KEEP_W), .USER_EN(1), - .USER_W(1) + .USER_W(mac_axis_tx_cpl.USER_W) ) mac_tx_cpl_int(); @@ -507,12 +515,20 @@ tx_cpl_fifo ( ); cndm_micro_tx #( - .PTP_TS_EN(PTP_TS_EN) + .PTP_TS_EN(PTP_TS_EN), + .PTP_TS_FMT_TOD(PTP_TS_FMT_TOD) ) tx_inst ( .clk(clk), .rst(rst), + /* + * PTP + */ + .ptp_clk(ptp_clk), + .ptp_rst(ptp_rst), + .ptp_td_sdi(ptp_td_sdi), + /* * DMA */ @@ -530,7 +546,7 @@ tx_inst ( taxi_axis_if #( .DATA_W(mac_axis_rx.DATA_W), .USER_EN(1), - .USER_W(1) + .USER_W(mac_axis_rx.USER_W) ) mac_rx_int(); taxi_axis_async_fifo #( @@ -582,12 +598,20 @@ rx_fifo ( ); cndm_micro_rx #( - .PTP_TS_EN(PTP_TS_EN) + .PTP_TS_EN(PTP_TS_EN), + .PTP_TS_FMT_TOD(PTP_TS_FMT_TOD) ) rx_inst ( .clk(clk), .rst(rst), + /* + * PTP + */ + .ptp_clk(ptp_clk), + .ptp_rst(ptp_rst), + .ptp_td_sdi(ptp_td_sdi), + /* * DMA */ diff --git a/src/cndm/rtl/cndm_micro_rx.sv b/src/cndm/rtl/cndm_micro_rx.sv index 4c0463e..2f68269 100644 --- a/src/cndm/rtl/cndm_micro_rx.sv +++ b/src/cndm/rtl/cndm_micro_rx.sv @@ -16,12 +16,20 @@ Authors: * Corundum-micro receive datapath */ module cndm_micro_rx #( - parameter logic PTP_TS_EN = 1'b1 + parameter logic PTP_TS_EN = 1'b1, + parameter logic PTP_TS_FMT_TOD = 1'b0 ) ( input wire logic clk, input wire logic rst, + /* + * PTP + */ + input wire logic ptp_clk = 1'b0, + input wire logic ptp_rst = 1'b0, + input wire logic ptp_td_sdi = 1'b0, + /* * DMA */ @@ -50,7 +58,7 @@ taxi_dma_desc_if #( .ID_EN(0), .DEST_EN(0), .USER_EN(1), - .USER_W(1) + .USER_W(rx_data.USER_W) ) dma_desc(); localparam [2:0] @@ -65,6 +73,84 @@ logic desc_req_reg = 1'b0; assign desc_req = desc_req_reg; +wire [95:0] rx_ptp_ts; +wire rx_ptp_ts_valid; + +if (PTP_TS_EN) begin + + if (PTP_TS_FMT_TOD) begin + + assign rx_ptp_ts = dma_desc.sts_user[dma_desc.USER_W-1:1]; + assign rx_ptp_ts_valid = dma_desc.sts_valid; + + end else begin + + taxi_axis_if #( + .DATA_W(48), + .KEEP_EN(0), + .KEEP_W(1), + .STRB_EN(0), + .LAST_EN(0), + .ID_EN(0), + .DEST_EN(0), + .USER_EN(1), + .USER_W(1) + ) ptp_ts_rel(); + + assign ptp_ts_rel.tdata = dma_desc.sts_user[dma_desc.USER_W-1:1]; + assign ptp_ts_rel.tuser = dma_desc.sts_user[0]; + assign ptp_ts_rel.tvalid = dma_desc.sts_valid; + + taxi_axis_if #( + .DATA_W(96), + .KEEP_EN(0), + .KEEP_W(1), + .STRB_EN(0), + .LAST_EN(0), + .ID_EN(0), + .DEST_EN(0), + .USER_EN(1), + .USER_W(1) + ) ptp_ts_tod(); + + assign rx_ptp_ts = ptp_ts_tod.tdata; + assign rx_ptp_ts_valid = ptp_ts_tod.tvalid; + + taxi_ptp_td_rel2tod #( + .TS_FNS_W(16), + .TS_REL_NS_W(ptp_ts_rel.DATA_W-16), + .TS_TOD_S_W(48), + .TS_REL_W(ptp_ts_rel.DATA_W), + .TS_TOD_W(96), + .TD_SDI_PIPELINE(2) + ) + rel2tod_inst ( + .clk(clk), + .rst(rst), + + /* + * PTP clock interface + */ + .ptp_clk(ptp_clk), + .ptp_rst(ptp_rst), + .ptp_td_sdi(ptp_td_sdi), + + /* + * Timestamp conversion + */ + .s_axis_ts_rel(ptp_ts_rel), + .m_axis_ts_tod(ptp_ts_tod) + ); + + end + +end else begin + + assign rx_ptp_ts = '0; + assign rx_ptp_ts_valid = 1'b0; + +end + always_ff @(posedge clk) begin desc_req_reg <= 1'b0; @@ -104,6 +190,12 @@ always_ff @(posedge clk) begin axis_cpl.tlast <= 1'b1; axis_cpl.tvalid <= axis_cpl.tvalid && !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 + end + case (state_reg) STATE_IDLE: begin dma_desc.req_valid <= 1'b1; diff --git a/src/cndm/rtl/cndm_micro_tx.sv b/src/cndm/rtl/cndm_micro_tx.sv index 81632a9..bed6d07 100644 --- a/src/cndm/rtl/cndm_micro_tx.sv +++ b/src/cndm/rtl/cndm_micro_tx.sv @@ -16,12 +16,20 @@ Authors: * Corundum-micro transmit datapath */ module cndm_micro_tx #( - parameter logic PTP_TS_EN = 1'b1 + parameter logic PTP_TS_EN = 1'b1, + parameter logic PTP_TS_FMT_TOD = 1'b0 ) ( input wire logic clk, input wire logic rst, + /* + * PTP + */ + input wire logic ptp_clk = 1'b0, + input wire logic ptp_rst = 1'b0, + input wire logic ptp_td_sdi = 1'b0, + /* * DMA */ @@ -66,6 +74,68 @@ logic desc_req_reg = 1'b0; assign desc_req = desc_req_reg; +wire [95:0] tx_cpl_ptp_ts; +wire tx_cpl_valid; + +if (PTP_TS_EN) begin + + if (PTP_TS_FMT_TOD) begin + + assign tx_cpl_ptp_ts = tx_cpl.tdata; + assign tx_cpl_valid = tx_cpl.tvalid; + + end else begin + + taxi_axis_if #( + .DATA_W(96), + .KEEP_EN(0), + .KEEP_W(1), + .STRB_EN(0), + .LAST_EN(0), + .ID_EN(0), + .DEST_EN(0), + .USER_EN(1), + .USER_W(1) + ) tx_cpl_tod(); + + assign tx_cpl_ptp_ts = tx_cpl_tod.tdata; + assign tx_cpl_valid = tx_cpl_tod.tvalid; + + taxi_ptp_td_rel2tod #( + .TS_FNS_W(16), + .TS_REL_NS_W(tx_cpl.DATA_W-16), + .TS_TOD_S_W(48), + .TS_REL_W(tx_cpl.DATA_W), + .TS_TOD_W(96), + .TD_SDI_PIPELINE(2) + ) + rel2tod_inst ( + .clk(clk), + .rst(rst), + + /* + * PTP clock interface + */ + .ptp_clk(ptp_clk), + .ptp_rst(ptp_rst), + .ptp_td_sdi(ptp_td_sdi), + + /* + * Timestamp conversion + */ + .s_axis_ts_rel(tx_cpl), + .m_axis_ts_tod(tx_cpl_tod) + ); + + end + +end else begin + + assign tx_cpl_ptp_ts = '0; + assign tx_cpl_valid = tx_cpl.tvalid; + +end + always_ff @(posedge clk) begin desc_req_reg <= 1'b0; @@ -118,6 +188,8 @@ always_ff @(posedge clk) begin dma_desc.req_src_addr <= '0; dma_desc.req_len <= axis_desc.tdata[47:32]; + axis_cpl.tdata[47:32] <= axis_desc.tdata[47:32]; + if (axis_desc.tvalid && axis_desc.tready) begin if (axis_desc.tuser) begin // failed to read desc @@ -135,7 +207,10 @@ always_ff @(posedge clk) begin end end STATE_TX_DATA: begin - if (dma_desc.sts_valid) 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 + if (tx_cpl_valid) begin axis_cpl.tvalid <= 1'b1; state_reg <= STATE_IDLE; end diff --git a/src/cndm/tb/cndm_micro_pcie_us/Makefile b/src/cndm/tb/cndm_micro_pcie_us/Makefile index cdadccc..3262a8f 100644 --- a/src/cndm/tb/cndm_micro_pcie_us/Makefile +++ b/src/cndm/tb/cndm_micro_pcie_us/Makefile @@ -39,6 +39,10 @@ export PARAM_SIM := "1'b1" export PARAM_VENDOR := "\"XILINX\"" export PARAM_FAMILY := "\"virtexuplus\"" export PARAM_PORTS := 2 +export PARAM_PTP_TS_EN := 1 +export PARAM_PTP_TS_FMT_TOD := 0 +export PARAM_PTP_CLK_PER_NS_NUM := 512 +export PARAM_PTP_CLK_PER_NS_DENOM := 165 export PARAM_MAC_DATA_W := 32 export PARAM_AXIS_PCIE_DATA_W := 256 export PARAM_BAR0_APERTURE := 24 diff --git a/src/cndm/tb/cndm_micro_pcie_us/test_cndm_micro_pcie_us.py b/src/cndm/tb/cndm_micro_pcie_us/test_cndm_micro_pcie_us.py index 6184cfd..bfd2c1c 100644 --- a/src/cndm/tb/cndm_micro_pcie_us/test_cndm_micro_pcie_us.py +++ b/src/cndm/tb/cndm_micro_pcie_us/test_cndm_micro_pcie_us.py @@ -271,6 +271,10 @@ class TB: self.dev.functions[0].configure_bar(0, 2**int(dut.uut.axil_ctrl_bar.ADDR_W)) + # PTP + cocotb.start_soon(Clock(dut.ptp_clk, 3.102, units="ns").start()) + cocotb.start_soon(Clock(dut.ptp_sample_clk, 8, units="ns").start()) + # Ethernet self.port_mac = [] @@ -291,6 +295,9 @@ class TB: rx_clk=dut.mac_rx_clk[k], rx_rst=dut.mac_rx_rst[k], rx_bus=AxiStreamBus.from_entity(dut.mac_axis_rx[k]), + tx_ptp_time=dut.mac_axis_tx[k].tid, # TODO + tx_ptp_ts=dut.mac_axis_tx_cpl[k].tdata, # TODO + tx_ptp_ts_valid=dut.mac_axis_tx_cpl[k].tvalid, # TODO ifg=12, speed=eth_speed ) self.port_mac.append(mac) @@ -347,6 +354,8 @@ class TB: async def init(self): + self.dut.ptp_rst.setimmediatevalue(0) + for mac in self.port_mac: mac.rx.reset.setimmediatevalue(0) mac.tx.reset.setimmediatevalue(0) @@ -357,6 +366,8 @@ class TB: for k in range(10): await RisingEdge(self.dut.pcie_clk) + self.dut.ptp_rst.value = 1 + for mac in self.port_mac: mac.rx.reset.value = 1 mac.tx.reset.value = 1 @@ -364,6 +375,8 @@ class TB: for k in range(10): await RisingEdge(self.dut.pcie_clk) + self.dut.ptp_rst.value = 0 + for mac in self.port_mac: mac.rx.reset.value = 0 mac.tx.reset.value = 0 @@ -499,6 +512,10 @@ def test_cndm_micro_pcie_us(request, mac_data_w): parameters['VENDOR'] = "\"XILINX\"" parameters['FAMILY'] = "\"virtexuplus\"" parameters['PORTS'] = 2 + parameters["PTP_TS_EN"] = 1 + parameters["PTP_TS_FMT_TOD"] = 0 + parameters["PTP_CLK_PER_NS_NUM"] = 512 + parameters["PTP_CLK_PER_NS_DENOM"] = 165 parameters['MAC_DATA_W'] = mac_data_w parameters['AXIS_PCIE_DATA_W'] = 256 parameters['BAR0_APERTURE'] = 24 diff --git a/src/cndm/tb/cndm_micro_pcie_us/test_cndm_micro_pcie_us.sv b/src/cndm/tb/cndm_micro_pcie_us/test_cndm_micro_pcie_us.sv index 5ba24be..3fdb96e 100644 --- a/src/cndm/tb/cndm_micro_pcie_us/test_cndm_micro_pcie_us.sv +++ b/src/cndm/tb/cndm_micro_pcie_us/test_cndm_micro_pcie_us.sv @@ -22,6 +22,10 @@ module test_cndm_micro_pcie_us # parameter string VENDOR = "XILINX", parameter string FAMILY = "virtexuplus", parameter PORTS = 2, + parameter logic PTP_TS_EN = 1'b1, + parameter logic PTP_TS_FMT_TOD = 1'b0, + parameter PTP_CLK_PER_NS_NUM = 512, + parameter PTP_CLK_PER_NS_DENOM = 165, parameter MAC_DATA_W = 32, parameter AXIS_PCIE_DATA_W = 256, parameter AXIS_PCIE_RC_USER_W = AXIS_PCIE_DATA_W < 512 ? 75 : 161, @@ -33,6 +37,8 @@ module test_cndm_micro_pcie_us # ) (); +localparam PTP_TS_W = PTP_TS_FMT_TOD ? 96 : 48; + localparam AXIS_PCIE_KEEP_W = (AXIS_PCIE_DATA_W/32); localparam RQ_SEQ_NUM_W = AXIS_PCIE_RQ_USER_W == 60 ? 4 : 6; @@ -122,6 +128,20 @@ logic [1:0] cfg_interrupt_msi_tph_type; logic [7:0] cfg_interrupt_msi_tph_st_tag; logic [7:0] cfg_interrupt_msi_function_number; +logic ptp_rst; +logic ptp_clk; +logic ptp_sample_clk; +logic ptp_td_sdo; +logic ptp_pps; +logic ptp_pps_str; +logic ptp_sync_locked; +logic [63:0] ptp_sync_ts_rel; +logic ptp_sync_ts_rel_step; +logic [95:0] ptp_sync_ts_tod; +logic ptp_sync_ts_tod_step; +logic ptp_sync_pps; +logic ptp_sync_pps_str; + logic mac_tx_clk[PORTS]; logic mac_tx_rst[PORTS]; @@ -136,7 +156,7 @@ logic mac_rx_clk[PORTS]; logic mac_rx_rst[PORTS]; taxi_axis_if #( - .DATA_W(96), + .DATA_W(PTP_TS_W), .KEEP_W(1), .ID_W(8) ) mac_axis_tx_cpl[PORTS](); @@ -145,7 +165,7 @@ taxi_axis_if #( .DATA_W(MAC_DATA_W), .ID_W(8), .USER_EN(1), - .USER_W(1) + .USER_W(PTP_TS_W+1) ) mac_axis_rx[PORTS](); cndm_micro_pcie_us #( @@ -153,6 +173,9 @@ cndm_micro_pcie_us #( .VENDOR(VENDOR), .FAMILY(FAMILY), .PORTS(PORTS), + .PTP_TS_EN(PTP_TS_EN), + .PTP_CLK_PER_NS_NUM(PTP_CLK_PER_NS_NUM), + .PTP_CLK_PER_NS_DENOM(PTP_CLK_PER_NS_DENOM), .RQ_SEQ_NUM_W(RQ_SEQ_NUM_W), .BAR0_APERTURE(BAR0_APERTURE) ) @@ -210,6 +233,23 @@ uut ( .cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag), .cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number), + /* + * PTP + */ + .ptp_clk(ptp_clk), + .ptp_rst(ptp_rst), + .ptp_sample_clk(ptp_sample_clk), + .ptp_td_sdo(ptp_td_sdo), + .ptp_pps(ptp_pps), + .ptp_pps_str(ptp_pps_str), + .ptp_sync_locked(ptp_sync_locked), + .ptp_sync_ts_rel(ptp_sync_ts_rel), + .ptp_sync_ts_rel_step(ptp_sync_ts_rel_step), + .ptp_sync_ts_tod(ptp_sync_ts_tod), + .ptp_sync_ts_tod_step(ptp_sync_ts_tod_step), + .ptp_sync_pps(ptp_sync_pps), + .ptp_sync_pps_str(ptp_sync_pps_str), + /* * Ethernet: SFP+ */