ソースを参照

feat: add timestamp driver

Signed-off-by: sakumisu <1203593632@qq.com>
sakumisu 3 ヶ月 前
コミット
b258f1be53

+ 1 - 0
CMakeLists.txt

@@ -16,6 +16,7 @@ if(CONFIG_CHERRYECAT)
         ${CMAKE_CURRENT_LIST_DIR}/src/ec_perf.c
         ${CMAKE_CURRENT_LIST_DIR}/src/ec_sii.c
         ${CMAKE_CURRENT_LIST_DIR}/src/ec_slave.c
+        ${CMAKE_CURRENT_LIST_DIR}/src/ec_timestamp.c
     )
 
     if(DEFINED CONFIG_CHERRYECAT_OSAL)

+ 1 - 1
README.md

@@ -37,7 +37,7 @@ CherryECAT is a tiny and beautiful, high real-time and low-jitter EtherCAT maste
 	- ENET must support descriptor dma and iperf with lwip > 90 Mbps
 	- Code must run in ram, ignore if no dc
 	- Must support High-Precision Timer (jitter < 1us)
-	- Must support High-Precision timestamp (like ARM DWT)
+	- Must support High-Precision timestamp (ARM DWT/RISC-V MCYCLE)
 	- Must support long long print
 
 - **Slave**

+ 1 - 1
README_zh.md

@@ -37,7 +37,7 @@ CherryECAT 是一个小而美的、高实时性、低抖动的 EtherCAT 主机
 	- 以太网必须支持 descriptor dma 并且 iperf with lwip > 90 Mbps
 	- 代码必须跑在 ram 上,如果不使用 DC 同步则忽视
 	- 必须支持高精度定时器(抖动小于 1us)
-	- 必须支持高精度时间戳 (比如 ARM DWT)
+	- 必须支持高精度时间戳 (ARM DWT/RISC-V MCYCLE)
 	- 必须支持 64 位打印
 
 - 从站

+ 2 - 0
SConscript

@@ -12,12 +12,14 @@ src += Glob('src/ec_cmd.c')
 src += Glob('src/ec_coe.c')
 src += Glob('src/ec_common.c')
 src += Glob('src/ec_datagram.c')
+src += Glob('src/ec_foe.c')
 src += Glob('src/ec_mailbox.c')
 src += Glob('src/ec_master.c')
 src += Glob('src/ec_netdev.c')
 src += Glob('src/ec_perf.c')
 src += Glob('src/ec_sii.c')
 src += Glob('src/ec_slave.c')
+src += Glob('src/ec_timestamp.c')
 src += Glob('osal/ec_osal_rtthread.c')
 
 group = DefineGroup('CherryECAT', src, depend = ['PKG_USING_CHERRYECAT'], LIBS = LIBS, LIBPATH=LIBPATH, CPPPATH = path, CPPDEFINES = CPPDEFINES)

+ 1 - 0
cherryec_config_template.h

@@ -63,6 +63,7 @@
 
 #define CONFIG_EC_PERF_ENABLE
 #define CONFIG_EC_CMD_ENABLE
+//#define CONFIG_EC_TIMESTAMP_CUSTOM
 
 #ifndef CONFIG_EC_MAX_PDO_BUFSIZE
 #define CONFIG_EC_MAX_PDO_BUFSIZE 2048

+ 1 - 0
demo/hpmicro/inc/ec_config.h

@@ -63,6 +63,7 @@
 
 #define CONFIG_EC_PERF_ENABLE
 #define CONFIG_EC_CMD_ENABLE
+//#define CONFIG_EC_TIMESTAMP_CUSTOM
 
 #ifndef CONFIG_EC_MAX_PDO_BUFSIZE
 #define CONFIG_EC_MAX_PDO_BUFSIZE 2048

+ 1 - 2
include/ec_master.h

@@ -28,11 +28,10 @@
 #include "ec_coe.h"
 #include "ec_cmd.h"
 #include "ec_perf.h"
+#include "ec_timestamp.h"
 #include "ec_version.h"
 #include "ec_foe.h"
 
-#define jiffies ec_htimer_get_time_us()
-
 /** Netdev statistics.
  */
 typedef struct {

+ 2 - 2
include/ec_port.h

@@ -18,7 +18,7 @@ int ec_netdev_low_level_input(ec_netdev_t *netdev);
 
 void ec_htimer_start(uint32_t us, ec_htimer_cb cb, void *arg);
 void ec_htimer_stop(void);
-uint64_t ec_htimer_get_time_ns(void);
-uint64_t ec_htimer_get_time_us(void);
+
+uint32_t ec_get_cpu_frequency(void);
 
 #endif

+ 15 - 0
include/ec_timestamp.h

@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_TIMESTAMP_H
+#define EC_TIMESTAMP_H
+
+void ec_timestamp_init(void);
+uint64_t ec_timestamp_get_time_ns(void);
+uint64_t ec_timestamp_get_time_us(void);
+
+#define jiffies ec_timestamp_get_time_us()
+
+#endif

+ 2 - 11
port/netdev_hpmicro.c

@@ -50,8 +50,6 @@ uint8_t mac[ENET_MAC];
 
 ec_netdev_t g_netdev;
 
-uint32_t g_clock_time_div;
-
 ATTR_WEAK void enet_get_mac_address(uint8_t *mac)
 {
     bool invalid = true;
@@ -187,8 +185,6 @@ hpm_stat_t enet_init(ENET_Type *ptr)
 
 ec_netdev_t *ec_netdev_low_level_init(uint8_t netdev_index)
 {
-    g_clock_time_div = clock_get_frequency(clock_cpu0) / 1000000UL;
-
     /* Initialize GPIOs */
     board_init_enet_pins(ENET);
 
@@ -419,12 +415,7 @@ void ec_htimer_stop(void)
     intc_m_disable_irq(EC_HTIMER_IRQ);
 }
 
-EC_FAST_CODE_SECTION uint64_t ec_htimer_get_time_ns(void)
-{
-    return (hpm_csr_get_core_mcycle() * 1000) / g_clock_time_div;
-}
-
-EC_FAST_CODE_SECTION uint64_t ec_htimer_get_time_us(void)
+uint32_t ec_get_cpu_frequency(void)
 {
-    return hpm_csr_get_core_mcycle() / g_clock_time_div;
+    return clock_get_frequency(clock_cpu0);
 }

+ 4 - 2
src/ec_master.c

@@ -442,6 +442,8 @@ int ec_master_init(ec_master_t *master, uint8_t master_index)
     ec_dlist_init(&master->ext_datagram_queue);
     ec_dlist_init(&master->cyclic_datagram_queue);
 
+    ec_timestamp_init();
+
     for (netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS; netdev_idx++) {
         master->netdev[netdev_idx] = ec_netdev_init(netdev_idx);
         if (!master->netdev[netdev_idx]) {
@@ -831,9 +833,9 @@ EC_FAST_CODE_SECTION static void ec_master_period_process(void *arg)
     }
 
     if (master->dc_ref_clock) {
-        EC_WRITE_U32(master->dc_ref_sync_datagram.data, ec_htimer_get_time_ns() & 0xffffffff);
+        EC_WRITE_U32(master->dc_ref_sync_datagram.data, ec_timestamp_get_time_ns() & 0xffffffff);
         if (master->dc_ref_clock->base_dc_range == EC_DC_64) {
-            EC_WRITE_U32(master->dc_ref_sync_datagram.data + 4, (uint32_t)(ec_htimer_get_time_ns() >> 32));
+            EC_WRITE_U32(master->dc_ref_sync_datagram.data + 4, (uint32_t)(ec_timestamp_get_time_ns() >> 32));
         }
         ec_master_queue_datagram(master, &master->dc_ref_sync_datagram);
 

+ 1 - 1
src/ec_perf.c

@@ -24,7 +24,7 @@ void ec_perf_init(ec_perf_t *perf, uint64_t expected_interval_us)
 
 EC_FAST_CODE_SECTION void ec_perf_polling(ec_perf_t *perf)
 {
-    uint64_t current_timestamp = ec_htimer_get_time_us();
+    uint64_t current_timestamp = jiffies;
 
     if (!perf->enable) {
         return;

+ 2 - 2
src/ec_slave.c

@@ -436,7 +436,7 @@ static int ec_slave_config_dc_systime_and_delay(ec_slave_t *slave)
         }
         system_time = EC_READ_U64(datagram->data);
         old_system_time_offset = EC_READ_U64(datagram->data + 16);
-        time_diff = ec_htimer_get_time_ns() - system_time;
+        time_diff = ec_timestamp_get_time_ns() - system_time;
 
         if (slave->base_dc_range == EC_DC_32) {
             system_time = (uint32_t)system_time + datagram->jiffies_sent * 1000;
@@ -741,7 +741,7 @@ static int ec_slave_config(ec_master_t *master, ec_slave_t *slave)
         uint64_t dc_start_time;
         uint32_t remainder = EC_DC_START_OFFSET / (slave->config->dc_sync[0].cycle_time + slave->config->dc_sync[1].cycle_time);
 
-        dc_start_time = ec_htimer_get_time_ns() + EC_DC_START_OFFSET +
+        dc_start_time = ec_timestamp_get_time_ns() + EC_DC_START_OFFSET +
                         slave->config->dc_sync[0].cycle_time + slave->config->dc_sync[1].cycle_time - remainder +
                         slave->config->dc_sync[0].shift_time;
         ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->START_TIME_CO), 8);

+ 110 - 0
src/ec_timestamp.c

@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+
+#ifndef CONFIG_EC_TIMESTAMP_CUSTOM
+#ifdef __riscv
+
+#define READ_CSR(csr_num) ({ uint32_t v; __asm volatile("csrr %0, %1" : "=r"(v) : "i"(csr_num)); v; })
+
+#define CSR_MCYCLE  (0xB00)
+#define CSR_MCYCLEH (0xB80)
+
+static inline uint64_t riscv_csr_get_core_mcycle(void)
+{
+    uint64_t result;
+    uint32_t resultl_first = READ_CSR(CSR_MCYCLE);
+    uint32_t resulth = READ_CSR(CSR_MCYCLEH);
+    uint32_t resultl_second = READ_CSR(CSR_MCYCLE);
+    if (resultl_first < resultl_second) {
+        result = ((uint64_t)resulth << 32) | resultl_first; /* if MCYCLE didn't roll over, return the value directly */
+    } else {
+        resulth = READ_CSR(CSR_MCYCLEH);
+        result = ((uint64_t)resulth << 32) | resultl_second; /* if MCYCLE rolled over, need to get the MCYCLEH again */
+    }
+    return result;
+}
+
+static uint32_t g_clock_time_div;
+
+void ec_timestamp_init(void)
+{
+    g_clock_time_div = ec_get_cpu_frequency() / 1000000;
+
+    uint64_t start_cycle = ec_timestamp_get_time_us();
+    ec_osal_msleep(10);
+
+    EC_ASSERT_MSG((ec_timestamp_get_time_us() - start_cycle) >= 9000, "Timestamp timer not running\n");
+}
+
+EC_FAST_CODE_SECTION uint64_t ec_timestamp_get_time_ns(void)
+{
+    return (riscv_csr_get_core_mcycle() * 1000) / g_clock_time_div;
+}
+
+EC_FAST_CODE_SECTION uint64_t ec_timestamp_get_time_us(void)
+{
+    return riscv_csr_get_core_mcycle() / g_clock_time_div;
+}
+#elif defined(__arm__)
+
+#define DWT_CR     (*(volatile uint32_t *)0xE0001000)
+#define DWT_CYCCNT (*(volatile uint32_t *)0xE0001004)
+#define DEM_CR     (*(volatile uint32_t *)0xE000EDFC)
+#define ITM_LAR    (*((volatile unsigned int *)0xE0001FB0))
+
+#define DEM_CR_TRCENA    (1 << 24)
+#define DWT_CR_CYCCNTENA (1 << 0)
+
+static volatile uint32_t g_dwt_high = 0;
+static volatile uint32_t g_dwt_last_low = 0;
+
+static inline uint64_t arm_dwt_get_cycle_count(void)
+{
+    uint32_t current_low = DWT_CYCCNT;
+
+    if (current_low < g_dwt_last_low) {
+        g_dwt_high++;
+    }
+    g_dwt_last_low = current_low;
+
+    return ((uint64_t)g_dwt_high << 32) | current_low;
+}
+
+void ec_timestamp_init(void)
+{
+    g_clock_time_div = ec_get_cpu_frequency() / 1000000;
+
+    g_dwt_high = 0;
+    g_dwt_last_low = 0;
+
+    DEM_CR |= (uint32_t)DEM_CR_TRCENA;
+    DWT_CYCCNT = (uint32_t)0u;
+    g_dwt_last_low = 0;
+
+    DWT_CR |= (uint32_t)DWT_CR_CYCCNTENA;
+
+    uint64_t start_cycle = ec_timestamp_get_time_us();
+    ec_osal_msleep(10);
+
+    EC_ASSERT_MSG((ec_timestamp_get_time_us() - start_cycle) >= 9000, "Timestamp timer not running\n");
+}
+
+EC_FAST_CODE_SECTION uint64_t ec_timestamp_get_time_ns(void)
+{
+    return (arm_dwt_get_cycle_count() * 1000) / g_clock_time_div;
+}
+
+EC_FAST_CODE_SECTION uint64_t ec_timestamp_get_time_us(void)
+{
+    return arm_dwt_get_cycle_count() / g_clock_time_div;
+}
+
+#else
+#error "Unsupported architecture"
+#endif
+
+#endif