cndm: Add PTP support

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2026-02-14 00:44:42 -08:00
parent eb289eb045
commit 88310fd348
24 changed files with 804 additions and 85 deletions

View File

@@ -28,6 +28,9 @@ SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_signal.sv
XDC_FILES = ../fpga.xdc XDC_FILES = ../fpga.xdc
XDC_FILES += $(TAXI_SRC_DIR)/eth/syn/vivado/taxi_eth_mac_fifo.tcl 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)/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_reset.tcl
XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_signal.tcl XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_signal.tcl

View File

@@ -28,6 +28,9 @@ SYN_FILES += $(TAXI_SRC_DIR)/sync/rtl/taxi_sync_signal.sv
XDC_FILES = ../fpga.xdc XDC_FILES = ../fpga.xdc
XDC_FILES += $(TAXI_SRC_DIR)/eth/syn/vivado/taxi_eth_mac_fifo.tcl 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)/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_reset.tcl
XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_signal.tcl XDC_FILES += $(TAXI_SRC_DIR)/sync/syn/vivado/taxi_sync_signal.tcl

View File

@@ -23,6 +23,8 @@ module fpga #
parameter string VENDOR = "XILINX", parameter string VENDOR = "XILINX",
// device family // device family
parameter string FAMILY = "kintexuplus", parameter string FAMILY = "kintexuplus",
// PTP configuration
parameter logic PTP_TS_EN = 1'b1,
// 10G/25G MAC configuration // 10G/25G MAC configuration
parameter logic CFG_LOW_LATENCY = 1'b1, parameter logic CFG_LOW_LATENCY = 1'b1,
parameter logic COMBINED_MAC_PCS = 1'b1, parameter logic COMBINED_MAC_PCS = 1'b1,
@@ -491,6 +493,7 @@ fpga_core #(
.SIM(SIM), .SIM(SIM),
.VENDOR(VENDOR), .VENDOR(VENDOR),
.FAMILY(FAMILY), .FAMILY(FAMILY),
.PTP_TS_EN(PTP_TS_EN),
.CFG_LOW_LATENCY(CFG_LOW_LATENCY), .CFG_LOW_LATENCY(CFG_LOW_LATENCY),
.COMBINED_MAC_PCS(COMBINED_MAC_PCS), .COMBINED_MAC_PCS(COMBINED_MAC_PCS),
.MAC_DATA_W(MAC_DATA_W) .MAC_DATA_W(MAC_DATA_W)

View File

@@ -24,6 +24,8 @@ module fpga_core #
// device family // device family
parameter string FAMILY = "kintexuplus", parameter string FAMILY = "kintexuplus",
parameter RQ_SEQ_NUM_W = 6, parameter RQ_SEQ_NUM_W = 6,
// PTP configuration
parameter logic PTP_TS_EN = 1'b1,
// 10G/25G MAC configuration // 10G/25G MAC configuration
parameter logic CFG_LOW_LATENCY = 1'b1, parameter logic CFG_LOW_LATENCY = 1'b1,
parameter logic COMBINED_MAC_PCS = 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 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+ // SFP+
wire sfp_tx_clk[2]; wire sfp_tx_clk[2];
wire sfp_tx_rst[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 sfp_led[1] = !sfp_rx_status[1];
assign led = '1; assign led = '1;
assign led_r = 1'b1; assign led_r = 1'b1;
assign led_g = 1'b1; // assign led_g = 1'b1;
localparam HB_COUNT = 62500000; localparam HB_COUNT = 62500000;
localparam CL_HB_COUNT = $clog2(HB_COUNT); localparam CL_HB_COUNT = $clog2(HB_COUNT);
@@ -160,8 +165,8 @@ assign sfp_mgt_refclk_out = sfp_mgt_refclk_bufg;
wire sfp_rst; 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(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(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)) axis_sfp_rx[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(); 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 if (SIM) begin
@@ -207,6 +212,15 @@ taxi_apb_if #(
) )
gt_apb_ctrl(); 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 #( taxi_eth_mac_25g_us #(
.SIM(SIM), .SIM(SIM),
.VENDOR(VENDOR), .VENDOR(VENDOR),
@@ -230,9 +244,11 @@ taxi_eth_mac_25g_us #(
.PADDING_EN(1'b1), .PADDING_EN(1'b1),
.DIC_EN(1'b1), .DIC_EN(1'b1),
.MIN_FRAME_LEN(64), .MIN_FRAME_LEN(64),
.PTP_TS_EN(1'b0), .PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(1'b1), .PTP_TD_EN(PTP_TS_EN),
.PTP_TS_W(96), .PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
.PTP_TS_W(PTP_TS_W),
.PTP_TD_SDI_PIPELINE(2),
.PRBS31_EN(1'b0), .PRBS31_EN(1'b0),
.TX_SERDES_PIPELINE(1), .TX_SERDES_PIPELINE(1),
.RX_SERDES_PIPELINE(1), .RX_SERDES_PIPELINE(1),
@@ -284,7 +300,6 @@ sfp_mac_inst (
.tx_clk(sfp_tx_clk), .tx_clk(sfp_tx_clk),
.tx_rst_in('{2{1'b0}}), .tx_rst_in('{2{1'b0}}),
.tx_rst_out(sfp_tx_rst), .tx_rst_out(sfp_tx_rst),
.ptp_sample_clk('{2{1'b0}}),
/* /*
* Transmit interface (AXI stream) * Transmit interface (AXI stream)
@@ -300,10 +315,18 @@ sfp_mac_inst (
/* /*
* PTP clock * PTP clock
*/ */
.tx_ptp_ts('{2{'0}}), .ptp_clk(ptp_clk),
.tx_ptp_ts_step('{2{1'b0}}), .ptp_rst(ptp_rst),
.rx_ptp_ts('{2{'0}}), .ptp_sample_clk(ptp_sample_clk),
.rx_ptp_ts_step('{2{1'b0}}), .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) * Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
@@ -440,6 +463,9 @@ cndm_micro_pcie_us #(
.VENDOR(VENDOR), .VENDOR(VENDOR),
.FAMILY(FAMILY), .FAMILY(FAMILY),
.PORTS(2), .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), .RQ_SEQ_NUM_W(RQ_SEQ_NUM_W),
.BAR0_APERTURE(24) .BAR0_APERTURE(24)
) )
@@ -497,6 +523,23 @@ cndm_inst (
.cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag), .cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag),
.cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number), .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 * Ethernet
*/ */

View File

@@ -40,6 +40,7 @@ VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES)))
export PARAM_SIM := "1'b1" export PARAM_SIM := "1'b1"
export PARAM_VENDOR := "\"XILINX\"" export PARAM_VENDOR := "\"XILINX\""
export PARAM_FAMILY := "\"kintexuplus\"" export PARAM_FAMILY := "\"kintexuplus\""
export PARAM_PTP_TS_EN := "1'b1"
export PARAM_CFG_LOW_LATENCY := "1'b1" export PARAM_CFG_LOW_LATENCY := "1'b1"
export PARAM_COMBINED_MAC_PCS := "1'b1" export PARAM_COMBINED_MAC_PCS := "1'b1"
export PARAM_MAC_DATA_W := "64" export PARAM_MAC_DATA_W := "64"

View File

@@ -486,6 +486,7 @@ def test_fpga_core(request, mac_data_w):
parameters['SIM'] = "1'b1" parameters['SIM'] = "1'b1"
parameters['VENDOR'] = "\"XILINX\"" parameters['VENDOR'] = "\"XILINX\""
parameters['FAMILY'] = "\"kintexuplus\"" parameters['FAMILY'] = "\"kintexuplus\""
parameters['PTP_TS_EN'] = "1'b1"
parameters['CFG_LOW_LATENCY'] = "1'b1" parameters['CFG_LOW_LATENCY'] = "1'b1"
parameters['COMBINED_MAC_PCS'] = "1'b1" parameters['COMBINED_MAC_PCS'] = "1'b1"
parameters['MAC_DATA_W'] = mac_data_w parameters['MAC_DATA_W'] = mac_data_w

View File

@@ -26,6 +26,8 @@ module test_fpga_core #
parameter AXIS_PCIE_RQ_USER_W = AXIS_PCIE_DATA_W < 512 ? 62 : 137, 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_CQ_USER_W = AXIS_PCIE_DATA_W < 512 ? 85 : 183,
parameter AXIS_PCIE_CC_USER_W = AXIS_PCIE_DATA_W < 512 ? 33 : 81, 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 // 10G/25G MAC configuration
parameter logic CFG_LOW_LATENCY = 1'b1, parameter logic CFG_LOW_LATENCY = 1'b1,
parameter logic COMBINED_MAC_PCS = 1'b1, parameter logic COMBINED_MAC_PCS = 1'b1,
@@ -137,6 +139,8 @@ fpga_core #(
.VENDOR(VENDOR), .VENDOR(VENDOR),
.FAMILY(FAMILY), .FAMILY(FAMILY),
.RQ_SEQ_NUM_W(RQ_SEQ_NUM_W), .RQ_SEQ_NUM_W(RQ_SEQ_NUM_W),
// PTP configuration
.PTP_TS_EN(PTP_TS_EN),
// 10G/25G MAC configuration // 10G/25G MAC configuration
.CFG_LOW_LATENCY(CFG_LOW_LATENCY), .CFG_LOW_LATENCY(CFG_LOW_LATENCY),
.COMBINED_MAC_PCS(COMBINED_MAC_PCS), .COMBINED_MAC_PCS(COMBINED_MAC_PCS),

View File

@@ -10,6 +10,7 @@ cndm-y += cndm_irq.o
cndm-y += cndm_dev.o cndm-y += cndm_dev.o
cndm-y += cndm_netdev.o cndm-y += cndm_netdev.o
cndm-y += cndm_ethtool.o cndm-y += cndm_ethtool.o
cndm-y += cndm_ptp.o
cndm-y += cndm_tx.o cndm-y += cndm_tx.o
cndm-y += cndm_rx.o cndm-y += cndm_rx.o

View File

@@ -14,6 +14,7 @@ Authors:
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/net_tstamp.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/ptp_clock_kernel.h> #include <linux/ptp_clock_kernel.h>
@@ -52,7 +53,7 @@ struct cndm_dev {
u32 port_offset; u32 port_offset;
u32 port_stride; u32 port_stride;
void __iomem *ptp_regs; void __iomem *phc_regs;
struct ptp_clock *ptp_clock; struct ptp_clock *ptp_clock;
struct ptp_clock_info ptp_clock_info; struct ptp_clock_info ptp_clock_info;
}; };
@@ -61,6 +62,7 @@ struct cndm_tx_info {
struct sk_buff *skb; struct sk_buff *skb;
dma_addr_t dma_addr; dma_addr_t dma_addr;
u32 len; u32 len;
int ts_requested;
}; };
struct cndm_rx_info { struct cndm_rx_info {
@@ -86,6 +88,10 @@ struct cndm_priv {
struct cndm_irq *irq; struct cndm_irq *irq;
struct notifier_block irq_nb; struct notifier_block irq_nb;
struct hwtstamp_config hwts_config;
u64 ts_s;
u8 ts_valid;
struct cndm_tx_info *tx_info; struct cndm_tx_info *tx_info;
struct cndm_rx_info *rx_info; struct cndm_rx_info *rx_info;
@@ -140,7 +146,9 @@ struct cndm_desc {
struct cndm_cpl { struct cndm_cpl {
__u8 rsvd[4]; __u8 rsvd[4];
__le32 len; __le32 len;
__u8 rsvd2[7]; __le32 ts_ns;
__le16 ts_fns;
__u8 ts_s;
__u8 phase; __u8 phase;
}; };
@@ -162,6 +170,11 @@ extern const struct file_operations cndm_fops;
// cndm_ethtool.c // cndm_ethtool.c
extern const struct ethtool_ops cndm_ethtool_ops; 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 // cndm_tx.c
int cndm_free_tx_buf(struct cndm_priv *priv); int cndm_free_tx_buf(struct cndm_priv *priv);
int cndm_poll_tx_cq(struct napi_struct *napi, int budget); int cndm_poll_tx_cq(struct napi_struct *napi, int budget);

View File

@@ -11,6 +11,7 @@ Authors:
#include "cndm.h" #include "cndm.h"
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/version.h>
static void cndm_get_drvinfo(struct net_device *ndev, static void cndm_get_drvinfo(struct net_device *ndev,
struct ethtool_drvinfo *drvinfo) 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)); 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 = { const struct ethtool_ops cndm_ethtool_ops = {
.get_drvinfo = cndm_get_drvinfo, .get_drvinfo = cndm_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_ts_info = cndm_get_ts_info,
}; };

View File

@@ -63,6 +63,8 @@ static int cndm_common_probe(struct cndm_dev *cdev)
if (cdev->port_count > ARRAY_SIZE(cdev->ndev)) if (cdev->port_count > ARRAY_SIZE(cdev->ndev))
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++) { for (k = 0; k < cdev->port_count; k++) {
struct net_device *ndev; struct net_device *ndev;
@@ -113,6 +115,8 @@ static void cndm_common_remove(struct cndm_dev *cdev)
} }
} }
cndm_unregister_phc(cdev);
devlink_unregister(devlink); devlink_unregister(devlink);
} }

View File

@@ -52,6 +52,67 @@ static int cndm_close(struct net_device *ndev)
return 0; 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) static int cndm_set_mac(struct net_device *ndev, void *addr)
{ {
struct sockaddr *saddr = addr; struct sockaddr *saddr = addr;
@@ -70,12 +131,29 @@ static int cndm_set_mac(struct net_device *ndev, void *addr)
return 0; 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 = { static const struct net_device_ops cndm_netdev_ops = {
.ndo_open = cndm_open, .ndo_open = cndm_open,
.ndo_stop = cndm_close, .ndo_stop = cndm_close,
.ndo_start_xmit = cndm_start_xmit, .ndo_start_xmit = cndm_start_xmit,
.ndo_validate_addr = eth_validate_addr, .ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = cndm_set_mac, .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) 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); 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->netdev_ops = &cndm_netdev_ops;
ndev->ethtool_ops = &cndm_ethtool_ops; ndev->ethtool_ops = &cndm_ethtool_ops;

View File

@@ -0,0 +1,192 @@
// SPDX-License-Identifier: GPL
/*
Copyright (c) 2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#include "cndm.h"
#include <linux/version.h>
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");
}
}

View File

@@ -143,15 +143,20 @@ static int cndm_process_rx_cq(struct net_device *ndev, int napi_budget)
if (len < ETH_HLEN) { if (len < ETH_HLEN) {
netdev_warn(priv->ndev, "Dropping short frame (len %d)", len); netdev_warn(priv->ndev, "Dropping short frame (len %d)", len);
__free_pages(page, 0);
goto rx_drop; goto rx_drop;
} }
skb = napi_get_frags(&priv->rx_napi); skb = napi_get_frags(&priv->rx_napi);
if (!skb) { if (!skb) {
netdev_err(priv->ndev, "Failed to allocate skb %d", index); 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_fill_page_desc(skb, 0, page, 0, len);
skb_shinfo(skb)->nr_frags = 1; skb_shinfo(skb)->nr_frags = 1;

View File

@@ -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) static int cndm_process_tx_cq(struct net_device *ndev, int napi_budget)
{ {
struct cndm_priv *priv = netdev_priv(ndev); struct cndm_priv *priv = netdev_priv(ndev);
struct cndm_tx_info *tx_info;
struct cndm_cpl *cpl; struct cndm_cpl *cpl;
struct skb_shared_hwtstamps hwts;
int done = 0; int done = 0;
u32 cq_cons_ptr; u32 cq_cons_ptr;
@@ -64,6 +66,14 @@ static int cndm_process_tx_cq(struct net_device *ndev, int napi_budget)
dma_rmb(); dma_rmb();
index = cons_ptr & priv->txq_mask; 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); 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) int cndm_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{ {
struct skb_shared_info *shinfo = skb_shinfo(skb);
struct cndm_priv *priv = netdev_priv(ndev); struct cndm_priv *priv = netdev_priv(ndev);
struct device *dev = priv->dev; struct device *dev = priv->dev;
u32 index; 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_desc = (struct cndm_desc *)(priv->txq_region + index*16);
tx_info = &priv->tx_info[index]; 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); len = skb_headlen(skb);
dma_addr = dma_map_single(dev, skb->data, len, DMA_TO_DEVICE); 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++; priv->txq_prod++;
skb_tx_timestamp(skb);
if (priv->txq_prod - priv->txq_cons >= 128) { if (priv->txq_prod - priv->txq_cons >= 128) {
netdev_dbg(ndev, "TX ring full"); netdev_dbg(ndev, "TX ring full");
netif_tx_stop_queue(priv->tx_queue); netif_tx_stop_queue(priv->tx_queue);

View File

@@ -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_async_fifo.f
../lib/taxi/src/axis/rtl/taxi_axis_arb_mux.f ../lib/taxi/src/axis/rtl/taxi_axis_arb_mux.f
../lib/taxi/src/axis/rtl/taxi_axis_demux.sv ../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_phc_axil.f
../lib/taxi/src/ptp/rtl/taxi_ptp_td_leaf.sv ../lib/taxi/src/ptp/rtl/taxi_ptp_td_rel2tod.sv

View File

@@ -16,10 +16,11 @@ Authors:
* Corundum-micro core logic * Corundum-micro core logic
*/ */
module cndm_micro_core #( module cndm_micro_core #(
parameter PORTS = 2//, parameter PORTS = 2,
// parameter logic PTP_TS_EN = 1'b1, parameter logic PTP_TS_EN = 1'b1,
// parameter PTP_CLK_PER_NS_NUM = 512, parameter logic PTP_TS_FMT_TOD = 1'b0,
// parameter PTP_CLK_PER_NS_DENOM = 165 parameter PTP_CLK_PER_NS_NUM = 512,
parameter PTP_CLK_PER_NS_DENOM = 165
) )
( (
input wire logic clk, input wire logic clk,
@@ -46,19 +47,20 @@ module cndm_micro_core #(
/* /*
* PTP * PTP
*/ */
// input wire logic ptp_clk = 1'b0, input wire logic ptp_clk = 1'b0,
// input wire logic ptp_rst = 1'b0, input wire logic ptp_rst = 1'b0,
// input wire logic ptp_sample_clk = 1'b0, input wire logic ptp_sample_clk = 1'b0,
// output wire logic ptp_td_sdo, input wire logic ptp_td_sdi = 1'b0,
// output wire logic ptp_pps, output wire logic ptp_td_sdo,
// output wire logic ptp_pps_str, output wire logic ptp_pps,
// output wire logic ptp_sync_locked, output wire logic ptp_pps_str,
// output wire logic [63:0] ptp_sync_ts_rel, output wire logic ptp_sync_locked,
// output wire logic ptp_sync_ts_rel_step, output wire logic [63:0] ptp_sync_ts_rel,
// output wire logic [95:0] ptp_sync_ts_tod, output wire logic ptp_sync_ts_rel_step,
// output wire logic ptp_sync_ts_tod_step, output wire logic [95:0] ptp_sync_ts_tod,
// output wire logic ptp_sync_pps, output wire logic ptp_sync_ts_tod_step,
// output wire logic ptp_sync_pps_str, output wire logic ptp_sync_pps,
output wire logic ptp_sync_pps_str,
/* /*
* Ethernet * 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_SEG_BE_W = dma_ram_wr.SEG_BE_W;
localparam RAM_SEL_W = dma_ram_wr.SEL_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 #( taxi_axil_if #(
.DATA_W(s_axil_wr.DATA_W), .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}) case ({s_axil_ctrl[0].araddr[15:2], 2'b00})
16'h0100: s_axil_rdata_reg <= PORTS; // port count 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 16'h0108: s_axil_rdata_reg <= 32'h00010000; // port stride
default: begin end default: begin end
endcase endcase
@@ -199,47 +199,54 @@ always_ff @(posedge clk) begin
end end
end end
// if (PTP_TS_EN) begin : ptp if (PTP_TS_EN) begin : ptp
// cndm_micro_ptp #( taxi_ptp_td_phc_axil #(
// .PTP_CLK_PER_NS_NUM(PTP_CLK_PER_NS_NUM), .PTP_CLK_PER_NS_NUM(PTP_CLK_PER_NS_NUM),
// .PTP_CLK_PER_NS_DENOM(PTP_CLK_PER_NS_DENOM) .PTP_CLK_PER_NS_DENOM(PTP_CLK_PER_NS_DENOM)
// ) )
// ptp_inst ( ptp_inst (
// .clk(clk), .clk(clk),
// .rst(rst), .rst(rst),
// /* /*
// * Control register interface * Control register interface
// */ */
// .s_axil_wr(s_axil_ctrl[1]), .s_axil_wr(s_axil_ctrl[1]),
// .s_axil_rd(s_axil_ctrl[1]), .s_axil_rd(s_axil_ctrl[1]),
// /* /*
// * PTP * PTP
// */ */
// .ptp_clk(ptp_clk), .ptp_clk(ptp_clk),
// .ptp_rst(ptp_rst), .ptp_rst(ptp_rst),
// .ptp_sample_clk(ptp_sample_clk), .ptp_sample_clk(ptp_sample_clk),
// .ptp_td_sdo(ptp_td_sdo), .ptp_td_sdo(ptp_td_sdo),
// .ptp_pps(ptp_pps), .ptp_pps(ptp_pps),
// .ptp_pps_str(ptp_pps_str), .ptp_pps_str(ptp_pps_str),
// .ptp_sync_locked(ptp_sync_locked), .ptp_sync_locked(ptp_sync_locked),
// .ptp_sync_ts_rel(ptp_sync_ts_rel), .ptp_sync_ts_rel(ptp_sync_ts_rel),
// .ptp_sync_ts_rel_step(ptp_sync_ts_rel_step), .ptp_sync_ts_rel_step(ptp_sync_ts_rel_step),
// .ptp_sync_ts_tod(ptp_sync_ts_tod), .ptp_sync_ts_tod(ptp_sync_ts_tod),
// .ptp_sync_ts_tod_step(ptp_sync_ts_tod_step), .ptp_sync_ts_tod_step(ptp_sync_ts_tod_step),
// .ptp_sync_pps(ptp_sync_pps), .ptp_sync_pps(ptp_sync_pps),
// .ptp_sync_pps_str(ptp_sync_pps_str) .ptp_sync_pps_str(ptp_sync_pps_str)
// ); );
// end else begin : ptp end else begin : ptp
// assign ptp_td_sdo = 1'b0; assign ptp_td_sdo = 1'b0;
// assign ptp_pps = 1'b0; assign ptp_pps = 1'b0;
// assign ptp_pps_str = 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 #( taxi_dma_desc_if #(
.SRC_ADDR_W(dma_rd_desc_req.SRC_ADDR_W), .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 for (genvar p = 0; p < PORTS; p = p + 1) begin : port
cndm_micro_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 ( port_inst (
.clk(clk), .clk(clk),
@@ -349,6 +357,13 @@ for (genvar p = 0; p < PORTS; p = p + 1) begin : port
.irq(irq[p]), .irq(irq[p]),
/*
* PTP
*/
.ptp_clk(ptp_clk),
.ptp_rst(ptp_rst),
.ptp_td_sdi(ptp_td_sdo),
/* /*
* Ethernet * Ethernet
*/ */

View File

@@ -23,6 +23,10 @@ module cndm_micro_pcie_us #(
// device family // device family
parameter string FAMILY = "virtexuplus", parameter string FAMILY = "virtexuplus",
parameter PORTS = 2, 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 RQ_SEQ_NUM_W = 6,
parameter BAR0_APERTURE = 24 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_tph_st_tag,
output wire [7:0] cfg_interrupt_msi_function_number, 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 * Ethernet
*/ */
@@ -455,7 +477,11 @@ msi_inst (
); );
cndm_micro_core #( 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 ( core_inst (
.clk(pcie_clk), .clk(pcie_clk),
@@ -479,6 +505,24 @@ core_inst (
.irq(irq), .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 * Ethernet
*/ */

View File

@@ -16,7 +16,8 @@ Authors:
* Corundum-micro port module * Corundum-micro port module
*/ */
module cndm_micro_port #( 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, input wire logic clk,
@@ -40,6 +41,13 @@ module cndm_micro_port #(
output wire logic irq, 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 * Ethernet
*/ */
@@ -454,7 +462,7 @@ taxi_axis_if #(
.KEEP_EN(mac_axis_tx_cpl.KEEP_EN), .KEEP_EN(mac_axis_tx_cpl.KEEP_EN),
.KEEP_W(mac_axis_tx_cpl.KEEP_W), .KEEP_W(mac_axis_tx_cpl.KEEP_W),
.USER_EN(1), .USER_EN(1),
.USER_W(1) .USER_W(mac_axis_tx_cpl.USER_W)
) )
mac_tx_cpl_int(); mac_tx_cpl_int();
@@ -507,12 +515,20 @@ tx_cpl_fifo (
); );
cndm_micro_tx #( 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 ( tx_inst (
.clk(clk), .clk(clk),
.rst(rst), .rst(rst),
/*
* PTP
*/
.ptp_clk(ptp_clk),
.ptp_rst(ptp_rst),
.ptp_td_sdi(ptp_td_sdi),
/* /*
* DMA * DMA
*/ */
@@ -530,7 +546,7 @@ tx_inst (
taxi_axis_if #( taxi_axis_if #(
.DATA_W(mac_axis_rx.DATA_W), .DATA_W(mac_axis_rx.DATA_W),
.USER_EN(1), .USER_EN(1),
.USER_W(1) .USER_W(mac_axis_rx.USER_W)
) mac_rx_int(); ) mac_rx_int();
taxi_axis_async_fifo #( taxi_axis_async_fifo #(
@@ -582,12 +598,20 @@ rx_fifo (
); );
cndm_micro_rx #( 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 ( rx_inst (
.clk(clk), .clk(clk),
.rst(rst), .rst(rst),
/*
* PTP
*/
.ptp_clk(ptp_clk),
.ptp_rst(ptp_rst),
.ptp_td_sdi(ptp_td_sdi),
/* /*
* DMA * DMA
*/ */

View File

@@ -16,12 +16,20 @@ Authors:
* Corundum-micro receive datapath * Corundum-micro receive datapath
*/ */
module cndm_micro_rx #( 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 clk,
input wire logic rst, 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 * DMA
*/ */
@@ -50,7 +58,7 @@ taxi_dma_desc_if #(
.ID_EN(0), .ID_EN(0),
.DEST_EN(0), .DEST_EN(0),
.USER_EN(1), .USER_EN(1),
.USER_W(1) .USER_W(rx_data.USER_W)
) dma_desc(); ) dma_desc();
localparam [2:0] localparam [2:0]
@@ -65,6 +73,84 @@ logic desc_req_reg = 1'b0;
assign desc_req = desc_req_reg; 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 always_ff @(posedge clk) begin
desc_req_reg <= 1'b0; desc_req_reg <= 1'b0;
@@ -104,6 +190,12 @@ always_ff @(posedge clk) begin
axis_cpl.tlast <= 1'b1; axis_cpl.tlast <= 1'b1;
axis_cpl.tvalid <= axis_cpl.tvalid && !axis_cpl.tready; 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) case (state_reg)
STATE_IDLE: begin STATE_IDLE: begin
dma_desc.req_valid <= 1'b1; dma_desc.req_valid <= 1'b1;

View File

@@ -16,12 +16,20 @@ Authors:
* Corundum-micro transmit datapath * Corundum-micro transmit datapath
*/ */
module cndm_micro_tx #( 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 clk,
input wire logic rst, 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 * DMA
*/ */
@@ -66,6 +74,68 @@ logic desc_req_reg = 1'b0;
assign desc_req = desc_req_reg; 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 always_ff @(posedge clk) begin
desc_req_reg <= 1'b0; desc_req_reg <= 1'b0;
@@ -118,6 +188,8 @@ always_ff @(posedge clk) begin
dma_desc.req_src_addr <= '0; dma_desc.req_src_addr <= '0;
dma_desc.req_len <= axis_desc.tdata[47:32]; 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.tvalid && axis_desc.tready) begin
if (axis_desc.tuser) begin if (axis_desc.tuser) begin
// failed to read desc // failed to read desc
@@ -135,7 +207,10 @@ always_ff @(posedge clk) begin
end end
end end
STATE_TX_DATA: begin 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; axis_cpl.tvalid <= 1'b1;
state_reg <= STATE_IDLE; state_reg <= STATE_IDLE;
end end

View File

@@ -39,6 +39,10 @@ export PARAM_SIM := "1'b1"
export PARAM_VENDOR := "\"XILINX\"" export PARAM_VENDOR := "\"XILINX\""
export PARAM_FAMILY := "\"virtexuplus\"" export PARAM_FAMILY := "\"virtexuplus\""
export PARAM_PORTS := 2 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_MAC_DATA_W := 32
export PARAM_AXIS_PCIE_DATA_W := 256 export PARAM_AXIS_PCIE_DATA_W := 256
export PARAM_BAR0_APERTURE := 24 export PARAM_BAR0_APERTURE := 24

View File

@@ -271,6 +271,10 @@ class TB:
self.dev.functions[0].configure_bar(0, 2**int(dut.uut.axil_ctrl_bar.ADDR_W)) 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 # Ethernet
self.port_mac = [] self.port_mac = []
@@ -291,6 +295,9 @@ class TB:
rx_clk=dut.mac_rx_clk[k], rx_clk=dut.mac_rx_clk[k],
rx_rst=dut.mac_rx_rst[k], rx_rst=dut.mac_rx_rst[k],
rx_bus=AxiStreamBus.from_entity(dut.mac_axis_rx[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 ifg=12, speed=eth_speed
) )
self.port_mac.append(mac) self.port_mac.append(mac)
@@ -347,6 +354,8 @@ class TB:
async def init(self): async def init(self):
self.dut.ptp_rst.setimmediatevalue(0)
for mac in self.port_mac: for mac in self.port_mac:
mac.rx.reset.setimmediatevalue(0) mac.rx.reset.setimmediatevalue(0)
mac.tx.reset.setimmediatevalue(0) mac.tx.reset.setimmediatevalue(0)
@@ -357,6 +366,8 @@ class TB:
for k in range(10): for k in range(10):
await RisingEdge(self.dut.pcie_clk) await RisingEdge(self.dut.pcie_clk)
self.dut.ptp_rst.value = 1
for mac in self.port_mac: for mac in self.port_mac:
mac.rx.reset.value = 1 mac.rx.reset.value = 1
mac.tx.reset.value = 1 mac.tx.reset.value = 1
@@ -364,6 +375,8 @@ class TB:
for k in range(10): for k in range(10):
await RisingEdge(self.dut.pcie_clk) await RisingEdge(self.dut.pcie_clk)
self.dut.ptp_rst.value = 0
for mac in self.port_mac: for mac in self.port_mac:
mac.rx.reset.value = 0 mac.rx.reset.value = 0
mac.tx.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['VENDOR'] = "\"XILINX\""
parameters['FAMILY'] = "\"virtexuplus\"" parameters['FAMILY'] = "\"virtexuplus\""
parameters['PORTS'] = 2 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['MAC_DATA_W'] = mac_data_w
parameters['AXIS_PCIE_DATA_W'] = 256 parameters['AXIS_PCIE_DATA_W'] = 256
parameters['BAR0_APERTURE'] = 24 parameters['BAR0_APERTURE'] = 24

View File

@@ -22,6 +22,10 @@ module test_cndm_micro_pcie_us #
parameter string VENDOR = "XILINX", parameter string VENDOR = "XILINX",
parameter string FAMILY = "virtexuplus", parameter string FAMILY = "virtexuplus",
parameter PORTS = 2, 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 MAC_DATA_W = 32,
parameter AXIS_PCIE_DATA_W = 256, parameter AXIS_PCIE_DATA_W = 256,
parameter AXIS_PCIE_RC_USER_W = AXIS_PCIE_DATA_W < 512 ? 75 : 161, 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 AXIS_PCIE_KEEP_W = (AXIS_PCIE_DATA_W/32);
localparam RQ_SEQ_NUM_W = AXIS_PCIE_RQ_USER_W == 60 ? 4 : 6; 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_tph_st_tag;
logic [7:0] cfg_interrupt_msi_function_number; 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_clk[PORTS];
logic mac_tx_rst[PORTS]; logic mac_tx_rst[PORTS];
@@ -136,7 +156,7 @@ logic mac_rx_clk[PORTS];
logic mac_rx_rst[PORTS]; logic mac_rx_rst[PORTS];
taxi_axis_if #( taxi_axis_if #(
.DATA_W(96), .DATA_W(PTP_TS_W),
.KEEP_W(1), .KEEP_W(1),
.ID_W(8) .ID_W(8)
) mac_axis_tx_cpl[PORTS](); ) mac_axis_tx_cpl[PORTS]();
@@ -145,7 +165,7 @@ taxi_axis_if #(
.DATA_W(MAC_DATA_W), .DATA_W(MAC_DATA_W),
.ID_W(8), .ID_W(8),
.USER_EN(1), .USER_EN(1),
.USER_W(1) .USER_W(PTP_TS_W+1)
) mac_axis_rx[PORTS](); ) mac_axis_rx[PORTS]();
cndm_micro_pcie_us #( cndm_micro_pcie_us #(
@@ -153,6 +173,9 @@ cndm_micro_pcie_us #(
.VENDOR(VENDOR), .VENDOR(VENDOR),
.FAMILY(FAMILY), .FAMILY(FAMILY),
.PORTS(PORTS), .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), .RQ_SEQ_NUM_W(RQ_SEQ_NUM_W),
.BAR0_APERTURE(BAR0_APERTURE) .BAR0_APERTURE(BAR0_APERTURE)
) )
@@ -210,6 +233,23 @@ uut (
.cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag), .cfg_interrupt_msi_tph_st_tag(cfg_interrupt_msi_tph_st_tag),
.cfg_interrupt_msi_function_number(cfg_interrupt_msi_function_number), .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+ * Ethernet: SFP+
*/ */