Эх сурвалжийг харах

first commit

Signed-off-by: sakumisu <1203593632@qq.com>
sakumisu 4 сар өмнө
commit
3540ad7bda
62 өөрчлөгдсөн 13386 нэмэгдсэн , 0 устгасан
  1. 47 0
      .gitattributes
  2. 23 0
      .gitignore
  3. 35 0
      .readthedocs.yaml
  4. 39 0
      CMakeLists.txt
  5. 201 0
      LICENSE
  6. 63 0
      README.md
  7. 63 0
      README_zh.md
  8. 28 0
      SConscript
  9. 5 0
      VERSION
  10. 78 0
      cherryec_config_template.h
  11. 47 0
      demo/hpmicro/CMakeLists.txt
  12. 152 0
      demo/hpmicro/inc/FreeRTOSConfig.h
  13. 136 0
      demo/hpmicro/inc/cia402_def.h
  14. 89 0
      demo/hpmicro/inc/csh_config.h
  15. 78 0
      demo/hpmicro/inc/ec_config.h
  16. 19 0
      demo/hpmicro/inc/shell.h
  17. 166 0
      demo/hpmicro/main.c
  18. 1 0
      docs/.gitignore
  19. 20 0
      docs/Makefile
  20. BIN
      docs/assets/ethercat.png
  21. BIN
      docs/assets/ethercat2.png
  22. BIN
      docs/assets/ethercat3.png
  23. BIN
      docs/assets/ethercat4.png
  24. BIN
      docs/assets/ethercat5.png
  25. 35 0
      docs/make.bat
  26. 9 0
      docs/requirements.txt
  27. 37 0
      docs/source/conf.py
  28. 32 0
      docs/source/ethercat.rst
  29. 14 0
      docs/source/index.rst
  30. 14 0
      include/ec_cmd.h
  31. 25 0
      include/ec_coe.h
  32. 17 0
      include/ec_common.h
  33. 91 0
      include/ec_datagram.h
  34. 507 0
      include/ec_def.h
  35. 26 0
      include/ec_errno.h
  36. 468 0
      include/ec_list.h
  37. 171 0
      include/ec_log.h
  38. 13 0
      include/ec_mailbox.h
  39. 130 0
      include/ec_master.h
  40. 49 0
      include/ec_netdev.h
  41. 71 0
      include/ec_osal.h
  42. 30 0
      include/ec_perf.h
  43. 25 0
      include/ec_port.h
  44. 40 0
      include/ec_sii.h
  45. 111 0
      include/ec_slave.h
  46. 160 0
      include/ec_util.h
  47. 12 0
      include/ec_version.h
  48. 4552 0
      include/esc_register.h
  49. 208 0
      osal/ec_osal_freertos.c
  50. 178 0
      osal/ec_osal_rtthread.c
  51. 267 0
      osal/ec_osal_threadx.c
  52. 437 0
      port/netdev_hpm.c
  53. 788 0
      src/ec_cmd.c
  54. 524 0
      src/ec_coe.c
  55. 349 0
      src/ec_common.c
  56. 195 0
      src/ec_datagram.c
  57. 91 0
      src/ec_mailbox.c
  58. 783 0
      src/ec_master.c
  59. 143 0
      src/ec_netdev.c
  60. 78 0
      src/ec_perf.c
  61. 162 0
      src/ec_sii.c
  62. 1254 0
      src/ec_slave.c

+ 47 - 0
.gitattributes

@@ -0,0 +1,47 @@
+*.c linguist-language=C
+*.C linguist-language=C
+*.h linguist-language=C
+*.H linguist-language=C
+
+* text=auto
+
+*.S text
+*.asm text
+*.c text
+*.cc text
+*.cpp text
+*.cxx text
+*.h text
+*.htm text
+*.html text
+*.in text
+*.ld text
+*.m4 text
+*.mak text
+*.mk text
+*.py text
+*.rb text
+*.s text
+*.sct text
+*.sh text
+*.txt text
+*.xml text
+SConscript text
+Makefile text
+AUTHORS text
+COPYING text
+
+*.LZO -text
+*.Opt -text
+*.Uv2 -text
+*.ewp -text
+*.eww -text
+*.vcproj -text
+*.bat -text
+*.dos -text
+*.icf -text
+*.inf -text
+*.ini -text
+*.sct -text
+*.xsd -text
+Jamfile -text

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+.vscode
+build
+**/Drivers/**
+**/MDK-ARM/DebugConfig/**
+**/MDK-ARM/RTE/**
+**/obj/**
+**/RET/**
+**/Listings/**
+**/Objects/**
+*.map
+*.o
+*.d
+*.htm
+*.dep
+*.lnp
+*.iex
+*.lst
+*.axf
+*.crf
+*.hex
+*.Bak
+*.uvguix.*
+*.scvd

+ 35 - 0
.readthedocs.yaml

@@ -0,0 +1,35 @@
+# Read the Docs configuration file for Sphinx projects
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Set the OS, Python version and other tools you might need
+build:
+  os: ubuntu-22.04
+  tools:
+    python: "3.11"
+    # You can also specify other tool versions:
+    # nodejs: "20"
+    # rust: "1.70"
+    # golang: "1.20"
+
+# Build documentation in the "docs/" directory with Sphinx
+sphinx:
+  configuration: docs/source/conf.py
+  # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
+  # builder: "dirhtml"
+  # Fail on all warnings to avoid broken references
+  # fail_on_warning: true
+
+# Optionally build your docs in additional formats such as PDF and ePub
+# formats:
+#    - pdf
+#    - epub
+
+# Optional but recommended, declare the Python requirements required
+# to build your documentation
+# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
+python:
+   install:
+   - requirements: docs/requirements.txt

+ 39 - 0
CMakeLists.txt

@@ -0,0 +1,39 @@
+if(CONFIG_CHERRYECAT)
+
+    list(APPEND cherryec_incs
+        ${CMAKE_CURRENT_LIST_DIR}/include
+    )
+
+    list(APPEND cherryec_srcs
+        ${CMAKE_CURRENT_LIST_DIR}/src/ec_coe.c
+        ${CMAKE_CURRENT_LIST_DIR}/src/ec_common.c
+        ${CMAKE_CURRENT_LIST_DIR}/src/ec_datagram.c
+        ${CMAKE_CURRENT_LIST_DIR}/src/ec_mailbox.c
+        ${CMAKE_CURRENT_LIST_DIR}/src/ec_master.c
+        ${CMAKE_CURRENT_LIST_DIR}/src/ec_netdev.c
+        ${CMAKE_CURRENT_LIST_DIR}/src/ec_perf.c
+        ${CMAKE_CURRENT_LIST_DIR}/src/ec_sii.c
+        ${CMAKE_CURRENT_LIST_DIR}/src/ec_slave.c
+    )
+
+    if(CONFIG_CHERRYECAT_CMD)
+        list(APPEND cherryec_srcs ${CMAKE_CURRENT_LIST_DIR}/src/ec_cmd.c)
+    endif()
+
+    if(DEFINED CONFIG_CHERRYECAT_OSAL)
+        if("${CONFIG_CHERRYECAT_OSAL}" STREQUAL "freertos")
+            list(APPEND cherryec_srcs ${CMAKE_CURRENT_LIST_DIR}/osal/ec_osal_freertos.c)
+        elseif("${CONFIG_CHERRYECAT_OSAL}" STREQUAL "rtthread")
+            list(APPEND cherryec_srcs ${CMAKE_CURRENT_LIST_DIR}/osal/ec_osal_rtthread.c)
+        elseif("${CONFIG_CHERRYECAT_OSAL}" STREQUAL "threadx")
+            list(APPEND cherryec_srcs ${CMAKE_CURRENT_LIST_DIR}/osal/ec_osal_threadx.c)
+        endif()
+    endif()
+
+    if(HPM_SDK_BASE)
+        list(APPEND cherryec_srcs port/netdev_hpm.c)
+        sdk_inc(${cherryec_incs})
+        sdk_src(${cherryec_srcs})
+    endif()
+
+endif()

+ 201 - 0
LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 63 - 0
README.md

@@ -0,0 +1,63 @@
+**English | [简体中文](README_zh.md)**
+
+<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">CherryECAT</h1>
+<p align="center">
+	<a href="https://github.com/cherry-embedded/CherryECAT/releases"><img src="https://img.shields.io/github/release/cherry-embedded/CherryECAT.svg"></a>
+	<a href="https://github.com/cherry-embedded/CherryECAT/blob/master/LICENSE"><img src="https://img.shields.io/github/license/cherry-embedded/CherryECAT.svg?style=flat-square"></a>
+</p>
+
+CherryECAT is a tiny and beautiful, high real-time and low-jitter EtherCAT master stack, specially designed for MCUs running with RTOS.
+
+## Feature
+
+- ~ 4K ram, ~32K flash(24K + 8K shell cmd + debug log)
+- Asynchronous queue-based transfer (one transfer can carry multiple datagrams)
+- Support hot-plugging
+	- Automatic scanning bus
+	- Automatic updating slave information when the topology changes
+- Support automatic monitoring slave status
+- Support distributed clocks
+- Support CANopen over EtherCAT(COE)
+- Support File over EtherCAT(FOE)
+- Support Ethernet over EtherCAT(EOE)
+- Support Slave SII access
+- Support Slave register access
+- Support multi master
+- Support backup redundancy
+- Support ethercat cmd in shell, ref to IGH
+
+## Hardware limitations
+
+- **Master**
+	- CPU (cache > 16K, memcpy speed > 100MB/s)
+	- ENET must support descriptor dma and iperf with lwip > 90 Mbps
+	- Code must run in ram
+	- Must support High-Precision Timer (jitter < 1us)
+	- Must support High-Precision timestamp (like ARM DWT)
+	- Must support long long print
+
+- **Slave**
+	- Must support COE
+	- Must support PDO assign
+	- Must support sdo complete access
+	- SII must have sync manager information
+
+## Shell cmd
+
+![ethercat](docs/assets/ethercat.png)
+![ethercat](docs/assets/ethercat2.png)
+![ethercat](docs/assets/ethercat3.png)
+![ethercat](docs/assets/ethercat4.png)
+![ethercat](docs/assets/ethercat5.png)
+
+## Support Boards
+
+- HPM6750EVK2/HPM6800EVK/**HPM5E00EVK**(hybrid internal)
+
+## Contact
+
+QQ group: 563650597
+
+## License
+
+FOE,EOE and Backup redundancy features are available for commercial charge; other are free to use

+ 63 - 0
README_zh.md

@@ -0,0 +1,63 @@
+**[English](README.md) | 简体中文**
+
+<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">CherryECAT</h1>
+<p align="center">
+	<a href="https://github.com/cherry-embedded/CherryECAT/releases"><img src="https://img.shields.io/github/release/cherry-embedded/CherryECAT.svg"></a>
+	<a href="https://github.com/cherry-embedded/CherryECAT/blob/master/LICENSE"><img src="https://img.shields.io/github/license/cherry-embedded/CherryECAT.svg?style=flat-square"></a>
+</p>
+
+CherryECAT 是一个小而美的、高实时性、低抖动的 EtherCAT 主机协议栈,专为跑在 RTOS 下的 MCU 设计。
+
+## 特性
+
+- ~ 4K ram,~32K flash(24K + 8K shell cmd + debug log)
+- 异步队列式传输(一次传输可以携带多个 datagram)
+- 支持热插拔
+	- 自动扫描总线
+	- 拓扑结构发生变化时自动更新 Slave 信息
+- 支持自动监控 Slave 状态
+- 支持分布式时钟
+- 支持 CANopen over EtherCAT (COE)
+- 支持 File over EtherCAT(FOE)
+- 支持 Ethernet over EtherCAT(EOE)
+- 支持 Slave SII 读写
+- 支持 Slave 寄存器读写
+- 支持多主站
+- 支持备份冗余
+- 支持 ethercat 命令行交互,参考 IgH
+
+## 硬件限制
+
+- 主站
+	- CPU (cache > 16K, memcpy speed > 100MB/s)
+	- 以太网必须支持 descriptor dma 并且 iperf with lwip > 90 Mbps
+	- 代码必须跑在 ram 上,如果不使用 DC 同步则忽视
+	- 必须支持高精度定时器(抖动小于 1us)
+	- 必须支持高精度时间戳 (比如 ARM DWT)
+	- 必须支持 64 位打印
+
+- 从站
+	- 必须支持 COE
+	- 必须支持 PDO assign
+	- 必须支持 sdo complete access
+	- SII 必须携带 sync manager 信息
+
+## Shell 命令
+
+![ethercat](docs/assets/ethercat.png)
+![ethercat](docs/assets/ethercat2.png)
+![ethercat](docs/assets/ethercat3.png)
+![ethercat](docs/assets/ethercat4.png)
+![ethercat](docs/assets/ethercat5.png)
+
+## 支持的开发板
+
+- HPM6750EVK2/HPM6800EVK/**HPM5E00EVK**(hybrid internal)
+
+## 联系
+
+QQ group: 563650597
+
+## License
+
+FOE,EOE,备份冗余功能为商用收费,其余功能免费商用

+ 28 - 0
SConscript

@@ -0,0 +1,28 @@
+from building import *
+
+cwd = GetCurrentDir()
+path = [cwd + '/include']
+src = []
+
+LIBS    = []
+LIBPATH = []
+CPPDEFINES = []
+
+src += Glob('src/ec_coe.c')
+src += Glob('src/ec_common.c')
+src += Glob('src/ec_datagram.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/osal/ec_osal_rtthread.c')
+
+if GetDepend(['PKG_CHERRYECAT_CMD']):
+    src += Glob('src/ec_cmd.c')
+
+group = DefineGroup('CherryECAT', src, depend = ['PKG_USING_CHERRYECAT'], LIBS = LIBS, LIBPATH=LIBPATH, CPPPATH = path, CPPDEFINES = CPPDEFINES)
+
+Return('group')
+

+ 5 - 0
VERSION

@@ -0,0 +1,5 @@
+VERSION_MAJOR = 0
+VERSION_MINOR = 1
+PATCHLEVEL = 0
+VERSION_TWEAK = 0
+EXTRAVERSION = 0

+ 78 - 0
cherryec_config_template.h

@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_CONFIG_H
+#define EC_CONFIG_H
+
+#define CONFIG_EC_PRINTF(...) printf(__VA_ARGS__)
+
+#ifndef CONFIG_EC_DBG_LEVEL
+#define CONFIG_EC_DBG_LEVEL EC_DBG_INFO
+#endif
+
+#ifndef CONFIG_EC_SLAVE_DBG_LEVEL
+#define CONFIG_EC_SLAVE_DBG_LEVEL EC_DBG_INFO
+#endif
+
+/* Enable print with color */
+#define CONFIG_EC_PRINTF_COLOR_ENABLE
+
+#define EC_FAST_CODE_SECTION
+
+#ifndef CONFIG_EC_MAX_NETDEVS
+#define CONFIG_EC_MAX_NETDEVS 1
+#endif
+
+#ifndef CONFIG_EC_NONPERIOD_PRIO
+#define CONFIG_EC_NONPERIOD_PRIO 0
+#endif
+
+#ifndef CONFIG_EC_NONPERIOD_STACKSIZE
+#define CONFIG_EC_NONPERIOD_STACKSIZE 2048
+#endif
+
+#ifndef CONFIG_EC_NONPERIOD_INTERVAL_MS
+#define CONFIG_EC_NONPERIOD_INTERVAL_MS 10
+#endif
+
+#ifndef CONFIG_EC_NONPERIOD_WAITERS
+#define CONFIG_EC_NONPERIOD_WAITERS 20
+#endif
+
+#ifndef CONFIG_EC_IDLE_PRIO
+#define CONFIG_EC_IDLE_PRIO 1
+#endif
+
+#ifndef CONFIG_EC_IDLE_STACKSIZE
+#define CONFIG_EC_IDLE_STACKSIZE 2048
+#endif
+
+#ifndef CONFIG_EC_IDLE_INTERVAL_MS
+#define CONFIG_EC_IDLE_INTERVAL_MS 10
+#endif
+
+#ifndef CONFIG_EC_PER_SM_MAX_PDOS
+#define CONFIG_EC_PER_SM_MAX_PDOS 8
+#endif
+
+#ifndef CONFIG_EC_PER_PDO_MAX_PDO_ENTRIES
+#define CONFIG_EC_PER_PDO_MAX_PDO_ENTRIES 8
+#endif
+
+#define CONFIG_EC_PERF_ENABLE
+
+#ifndef CONFIG_EC_MAX_PDO_BUFSIZE
+#define CONFIG_EC_MAX_PDO_BUFSIZE 2048
+#endif
+
+#ifndef CONFIG_EC_MAX_ENET_TXBUF_COUNT
+#define CONFIG_EC_MAX_ENET_TXBUF_COUNT 10
+#endif
+
+#ifndef CONFIG_EC_MAX_ENET_RXBUF_COUNT
+#define CONFIG_EC_MAX_ENET_RXBUF_COUNT 10
+#endif
+
+#endif

+ 47 - 0
demo/hpmicro/CMakeLists.txt

@@ -0,0 +1,47 @@
+# Copyright (c) 2021 HPMicro
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.13)
+
+set(HPM_SDK_LD_NO_NANO_SPECS 1)
+
+set(CONFIG_FREERTOS 1)
+# set(CONFIG_CHERRYRB 1)
+set(CONFIG_CHERRYSH 1)
+set(CONFIG_CHERRYSH_INTERFACE "uart")
+
+set(CONFIG_ENET_PHY 1)
+set(APP_USE_ENET_PORT_COUNT 1)
+#set(APP_USE_ENET_ITF_RGMII 1)
+#set(APP_USE_ENET_ITF_RMII 1)
+#set(APP_USE_ENET_PHY_DP83867 1)
+#set(APP_USE_ENET_PHY_RTL8211 1)
+#set(APP_USE_ENET_PHY_DP83848 1)
+set(APP_USE_ENET_PHY_RTL8201 1)
+
+set(CONFIG_CHERRYECAT 1)
+set(CONFIG_CHERRYECAT_CMD 1)
+set(CONFIG_CHERRYECAT_OSAL "freertos")
+
+if(NOT (HPM_BUILD_TYPE STREQUAL "ram"))
+message(FATAL_ERROR "Only support ram build for demo")
+endif()
+
+find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})
+
+project(cherryec)
+
+sdk_compile_definitions(-D__freertos_irq_stack_top=_stack)
+sdk_compile_definitions(-DCONFIG_FREERTOS=1)
+sdk_compile_definitions(-DUSE_NONVECTOR_MODE=1)
+sdk_compile_definitions(-DDISABLE_IRQ_PREEMPTIVE=1)
+
+sdk_compile_options("-O2")
+
+sdk_inc(.)
+sdk_inc(inc)
+sdk_app_src(main.c)
+
+add_subdirectory(../.. cherryec)
+generate_ses_project()
+

+ 152 - 0
demo/hpmicro/inc/FreeRTOSConfig.h

@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2024 HPMicro
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ */
+
+#include "board.h"
+
+#if (portasmHAS_MTIME == 0)
+#define configMTIME_BASE_ADDRESS                (0)
+#define configMTIMECMP_BASE_ADDRESS             (0)
+#else
+#define configMTIME_BASE_ADDRESS                (HPM_MCHTMR_BASE)
+#define configMTIMECMP_BASE_ADDRESS             (HPM_MCHTMR_BASE + 8UL)
+#endif
+
+#define configUSE_PREEMPTION                    1
+#define configCPU_CLOCK_HZ                      ((uint32_t) 24000000)
+#define configTICK_RATE_HZ                      ((TickType_t) 1000)
+#define configMAX_PRIORITIES                    (32)
+#define configMINIMAL_STACK_SIZE                (256)
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 0
+#define configUSE_APPLICATION_TASK_TAG          0
+#define configGENERATE_RUN_TIME_STATS           0
+
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configUSE_MUTEXES                       1
+
+/* Memory allocation definitions. */
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        1
+#define configTOTAL_HEAP_SIZE                   ((size_t) (64 * 1024))
+
+/* Hook function definitions. */
+#define configUSE_IDLE_HOOK                     0
+#define configUSE_TICK_HOOK                     0
+#define configCHECK_FOR_STACK_OVERFLOW          0
+#define configUSE_MALLOC_FAILED_HOOK            0
+#define configUSE_DAEMON_TASK_STARTUP_HOOK      0
+
+/* Run time and task stats gathering definitions. */
+#define configGENERATE_RUN_TIME_STATS           0
+#define configUSE_TRACE_FACILITY                1
+#define configUSE_STATS_FORMATTING_FUNCTIONS    0
+
+/* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */
+#define INCLUDE_vTaskPrioritySet                1
+#define INCLUDE_uxTaskPriorityGet               1
+#define INCLUDE_vTaskDelete                     1
+#define INCLUDE_vTaskCleanUpResources           1
+#define INCLUDE_vTaskSuspend                    1
+#define INCLUDE_vTaskDelayUntil                 1
+#define INCLUDE_vTaskDelay                      1
+#define INCLUDE_xTaskGetCurrentTaskHandle       1
+#define INCLUDE_xTimerPendFunctionCall          1
+#define INCLUDE_eTaskGetState                   1
+#define INCLUDE_xTaskAbortDelay                 1
+#define INCLUDE_xTaskGetHandle                  1
+#define INCLUDE_xSemaphoreGetMutexHolder        1
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                   0
+#define configMAX_CO_ROUTINE_PRIORITIES         2
+
+/* Software timer definitions. */
+#define configUSE_TIMERS                        1
+#define configTIMER_TASK_PRIORITY               (configMAX_PRIORITIES - 1)
+#define configTIMER_QUEUE_LENGTH                4
+#define configTIMER_TASK_STACK_DEPTH            (configMINIMAL_STACK_SIZE)
+
+/* Task priorities.*/
+#ifndef uartPRIMARY_PRIORITY
+    #define uartPRIMARY_PRIORITY                (configMAX_PRIORITIES - 3)
+#endif
+
+/* Normal assert() semantics without relying on the provision of an assert.h header file. */
+#define configASSERT(x) if ((x) == 0) { taskDISABLE_INTERRUPTS(); __asm volatile("ebreak"); for (;;); }
+
+/*
+ * The size of the global output buffer that is available for use when there
+ * are multiple command interpreters running at once (for example, one on a UART
+ * and one on TCP/IP).  This is done to prevent an output buffer being defined by
+ * each implementation - which would waste RAM.  In this case, there is only one
+ * command interpreter running.
+ */
+
+/*
+ * The buffer into which output generated by FreeRTOS+CLI is placed.  This must
+ * be at least big enough to contain the output of the task-stats command, as the
+ * example implementation does not include buffer overlow checking.
+ */
+#define configCOMMAND_INT_MAX_OUTPUT_SIZE        2096
+#define configINCLUDE_QUERY_HEAP_COMMAND         1
+
+/* This file is included from assembler files - make sure C code is not included in assembler files. */
+#ifndef __ASSEMBLER__
+    void vAssertCalled(const char *pcFile, unsigned long ulLine);
+    void vConfigureTickInterrupt(void);
+    void vClearTickInterrupt(void);
+    void vPreSleepProcessing(unsigned long uxExpectedIdleTime);
+    void vPostSleepProcessing(unsigned long uxExpectedIdleTime);
+#endif /* __ASSEMBLER__ */
+
+/****** Hardware/compiler specific settings. *******/
+/*
+ * The application must provide a function that configures a peripheral to
+ * create the FreeRTOS tick interrupt, then define configSETUP_TICK_INTERRUPT()
+ * in FreeRTOSConfig.h to call the function.
+ */
+#define configSETUP_TICK_INTERRUPT() vConfigureTickInterrupt()
+#define configCLEAR_TICK_INTERRUPT() vClearTickInterrupt()
+
+/*
+ * The configPRE_SLEEP_PROCESSING() and configPOST_SLEEP_PROCESSING() macros
+ * allow the application writer to add additional code before and after the MCU is
+ * placed into the low power state respectively.  The empty implementations
+ * provided in this demo can be extended to save even more power.
+ */
+#define configPRE_SLEEP_PROCESSING(uxExpectedIdleTime) vPreSleepProcessing(uxExpectedIdleTime);
+#define configPOST_SLEEP_PROCESSING(uxExpectedIdleTime) vPostSleepProcessing(uxExpectedIdleTime);
+
+
+/* Compiler specifics. */
+#define fabs(x) __builtin_fabs(x)
+
+/* Enable Hardware Stack Protection and Recording mechanism. */
+#define configHSP_ENABLE                       0
+
+/* Record the highest address of stack. */
+#if (configHSP_ENABLE == 1 && configRECORD_STACK_HIGH_ADDRESS != 1)
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#endif
+
+#endif /* FREERTOS_CONFIG_H */

+ 136 - 0
demo/hpmicro/inc/cia402_def.h

@@ -0,0 +1,136 @@
+#ifndef CIA402_DEF_H
+#define CIA402_DEF_H
+
+/**
+STRUCT_PACKED_START: Is defined before the typedef struct construct to pack the generic structures if necessary */
+#ifndef STRUCT_PACKED_START
+#define STRUCT_PACKED_START                       ATTR_PACKED
+#endif
+
+/**
+STRUCT_PACKED_END: Is defined after the typedef struct {} construct to pack the generic structures if necessary */
+#ifndef STRUCT_PACKED_END
+#define STRUCT_PACKED_END
+#endif
+
+/*---------------------------------------------
+-    ControlWord Commands (IEC61800_184e)
+-----------------------------------------------*/
+#define CONTROLWORD_COMMAND_SHUTDOWN                 0x0006 /**< \brief Shutdown command*/
+#define CONTROLWORD_COMMAND_SWITCHON                 0x0007 /**< \brief Switch on command*/
+#define CONTROLWORD_COMMAND_SWITCHON_ENABLEOPERATION 0x000F /**< \brief Switch on & Enable command*/
+#define CONTROLWORD_COMMAND_DISABLEVOLTAGE           0x0000 /**< \brief Disable voltage command*/
+#define CONTROLWORD_COMMAND_QUICKSTOP                0x0002 /**< \brief Quickstop command*/
+#define CONTROLWORD_COMMAND_DISABLEOPERATION         0x0007 /**< \brief Disable operation command*/
+#define CONTROLWORD_COMMAND_ENABLEOPERATION          0x000F /**< \brief Enable operation command*/
+#define CONTROLWORD_COMMAND_FAULTRESET               0x0080 /**< \brief Fault reset command*/
+
+/*---------------------------------------------
+-    StatusWord Masks and Flags
+-----------------------------------------------*/
+#define STATUSWORD_STATE_MASK                        0x006F /**< \brief State mask*/
+#define STATUSWORD_VOLTAGE_ENABLED                   0x0010 /**< \brief Indicate high voltage enabled*/
+#define STATUSWORD_WARNING                           0x0080 /**< \brief Warning active*/
+#define STATUSWORD_MANUFACTORSPECIFIC                0x0100 /**< \brief Manufacturer specific*/
+#define STATUSWORD_INTERNAL_LIMIT                    0x0800 /**< \brief Internal limit*/
+#define STATUSWORD_REMOTE                            0x0200 /**< \brief Set if the control word is processed*/
+#define STATUSWORD_TARGET_REACHED                    0x0400 /**< \brief Target reached*/
+#define STATUSWORD_INTERNALLIMITACTIVE               0x0800 /**< \brief Internal limit active*/
+#define STATUSWORD_DRIVE_FOLLOWS_COMMAND             0x1000 /**< \brief Drive follows command (used in cyclic synchronous modes)*/
+
+/*---------------------------------------------
+-    StatusWord
+-----------------------------------------------*/
+#define STATUSWORD_STATE_NOTREADYTOSWITCHON          0x0000 /**< \brief Not ready to switch on*/
+#define STATUSWORD_STATE_SWITCHEDONDISABLED          0x0040 /**< \brief Switched on but disabled*/
+#define STATUSWORD_STATE_READYTOSWITCHON             0x0021 /**< \brief Ready to switch on*/
+#define STATUSWORD_STATE_SWITCHEDON                  0x0023 /**< \brief Switched on*/
+#define STATUSWORD_STATE_OPERATIONENABLED            0x0027 /**< \brief Operation enabled*/
+#define STATUSWORD_STATE_QUICKSTOPACTIVE             0x0007 /**< \brief Quickstop active*/
+#define STATUSWORD_STATE_FAULTREACTIONACTIVE         0x000F /**< \brief Fault reaction active*/
+#define STATUSWORD_STATE_FAULT                       0x0008 /**< \brief Fault state*/
+
+/*---------------------------------------------
+-    CiA402 Modes of Operation (object 0x6060) (IEC61800_184e)
+-----------------------------------------------*/
+// -128 to -1 Manufacturer-specific operation modes
+#define NO_MODE                                      0 /**< \brief No mode*/
+#define PROFILE_POSITION_MODE                        1 /**< \brief Position Profile mode*/
+#define VELOCITY_MODE                                2 /**< \brief Velocity mode*/
+#define PROFILE_VELOCITY_MOCE                        3 /**< \brief Velocity Profile mode*/
+#define PROFILE_TORQUE_MODE                          4 /**< \brief Torque Profile mode*/
+//5 reserved
+#define HOMING_MODE                                  6  /**< \brief Homing mode*/
+#define INTERPOLATION_POSITION_MODE                  7  /**< \brief Interpolation Position mode*/
+#define CYCLIC_SYNC_POSITION_MODE                    8  /**< \brief Cyclic Synchronous Position mode*/
+#define CYCLIC_SYNC_VELOCITY_MODE                    9  /**< \brief Cyclic Synchronous Velocity mode*/
+#define CYCLIC_SYNC_TORQUE_MODE                      10 /**< \brief Cyclic Synchronous Torque mode*/
+//+11 to +127 reserved
+
+/**
+ * \addtogroup PDO Process Data Objects
+ * @{
+ */
+/** \brief Data structure to handle the process data transmitted via 0x1A00 (csp/csv TxPDO)*/
+typedef struct STRUCT_PACKED_START
+{
+    uint16_t ObjStatusWord; /**< \brief Status word (0x6041)*/
+    int32_t ObjPositionActualValue; /**< \brief Actual position (0x6064)*/
+    int32_t ObjVelocityActualValue; /**< \brief Actual velocity (0x606C)*/
+    int16_t ObjModesOfOperationDisplay; /**< \brief Current mode of operation (0x6061)*/
+}STRUCT_PACKED_END
+TCiA402PDO1A00;
+
+
+/** \brief Data structure to handle the process data transmitted via 0x1A01 (csp TxPDO)*/
+typedef struct STRUCT_PACKED_START
+{
+    uint16_t ObjStatusWord; /**< \brief Status word (0x6041)*/
+    int32_t ObjPositionActualValue; /**< \brief Actual position (0x6064)*/
+    uint16_t Padding16Bit; /**< \brief 16bit padding*/
+}STRUCT_PACKED_END
+TCiA402PDO1A01;
+
+
+/** \brief Data structure to handle the process data transmitted via 0x1A02 (csv TxPDO)*/
+typedef struct STRUCT_PACKED_START
+{
+    uint16_t ObjStatusWord; /**< \brief Status word (0x6041)*/
+    int32_t ObjPositionActualValue; /**< \brief Actual position (0x6064)*/
+    uint16_t Padding16Bit; /**< \brief 16bit padding*/
+}STRUCT_PACKED_END
+TCiA402PDO1A02;
+
+
+/** \brief Data structure to handle the process data transmitted via 0x1600 (csp/csv RxPDO)*/
+typedef struct STRUCT_PACKED_START
+{
+    uint16_t ObjControlWord; /**< \brief Control word (0x6040)*/
+    int32_t ObjTargetPosition; /**< \brief Target position (0x607A)*/
+    int32_t ObjTargetVelocity; /**< \brief Target velocity (0x60FF)*/
+    int16_t ObjModesOfOperation; /**< \brief Mode of operation (0x6060)*/
+}STRUCT_PACKED_END
+TCiA402PDO1600;
+
+
+/** \brief Data structure to handle the process data transmitted via 0x1601 (csp RxPDO)*/
+typedef struct STRUCT_PACKED_START
+{
+    uint16_t ObjControlWord; /**< \brief Control word (0x6040)*/
+    int32_t ObjTargetPosition; /**< \brief Target position (0x607A)*/
+    uint16_t Padding16Bit; /**< \brief 16bit padding*/
+}STRUCT_PACKED_END
+TCiA402PDO1601;
+
+
+/** \brief Data structure to handle the process data transmitted via 0x1602 (csv RxPDO)*/
+typedef struct STRUCT_PACKED_START
+{
+    uint16_t ObjControlWord; /**< \brief Control word (0x6040)*/
+    int32_t ObjTargetVelocity; /**< \brief Target velocity (0x60FF)*/
+    uint16_t Padding16Bit; /**< \brief 16bit padding*/
+}STRUCT_PACKED_END
+TCiA402PDO1602;
+/** @}*/
+
+#endif

+ 89 - 0
demo/hpmicro/inc/csh_config.h

@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2022, Egahp
+ * Copyright (c) 2024, HPMicro
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef CSH_CONFIG_H
+#define CSH_CONFIG_H
+
+/*!< argument check */
+#define CONFIG_CSH_DEBUG 0
+
+/*!< default row */
+#define CONFIG_CSH_DFTROW 25
+
+/*!< default column */
+#define CONFIG_CSH_DFTCOL 80
+
+/*!< history support <+550byte> */
+#define CONFIG_CSH_HISTORY 1
+
+/*!< completion support <+1100byte> */
+#define CONFIG_CSH_COMPLETION 1
+
+/*!< max completion item list count (use stack 4 x count byte) */
+#define CONFIG_CSH_MAX_COMPLETION 40
+
+/*!< prompt edit support <+1000byte>  */
+#define CONFIG_CSH_PROMPTEDIT 1
+
+/*!< prompt segment count */
+#define CONFIG_CSH_PROMPTSEG 7
+
+/*!< xterm support */
+#define CONFIG_CSH_XTERM 0
+
+/*!< newline */
+#define CONFIG_CSH_NEWLINE "\r\n"
+
+/*!< tab space count */
+#define CONFIG_CSH_SPACE 4
+
+/*!< independent ctrl map */
+#define CONFIG_CSH_CTRLMAP 0
+
+/*!< independent alt map */
+#define CONFIG_CSH_ALTMAP 0
+
+/*!< refresh prompt */
+#define CONFIG_CSH_REFRESH_PROMPT 1
+
+/*!< no waiting for sget */
+#define CONFIG_CSH_NOBLOCK 1
+
+/*!< help information */
+#define CONFIG_CSH_HELP ""
+
+/*!< path length 0:const path, <=255:variable path */
+#define CONFIG_CSH_MAXLEN_PATH 128
+
+/*!< path segment count */
+#define CONFIG_CSH_MAXSEG_PATH 16
+
+/*!< user count */
+#define CONFIG_CSH_MAX_USER 1
+
+/*!< max argument count */
+#define CONFIG_CSH_MAX_ARG 8
+
+/*!< linebuffer static or on stack */
+#define CONFIG_CSH_LNBUFF_STATIC 1
+
+/*!< linebuffer size (valid only if lnbuff on stack) */
+#define CONFIG_CSH_LNBUFF_SIZE 256
+
+/*!< multi-thread mode */
+#define CONFIG_CSH_MULTI_THREAD 1
+
+/*!< independent signal handler (for multi instances) */
+#define CONFIG_CSH_SIGNAL_HANDLER 0
+
+/*!< Ctrl+c/d/q/s/z/\ F1-F12 UE <+120byte> */
+#define CONFIG_CSH_USER_CALLBACK 1
+
+/*!< enable macro export symbol table */
+#define CONFIG_CSH_SYMTAB 1
+
+#endif

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

@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_CONFIG_H
+#define EC_CONFIG_H
+
+#define CONFIG_EC_PRINTF(...) printf(__VA_ARGS__)
+
+#ifndef CONFIG_EC_DBG_LEVEL
+#define CONFIG_EC_DBG_LEVEL EC_DBG_INFO
+#endif
+
+#ifndef CONFIG_EC_SLAVE_DBG_LEVEL
+#define CONFIG_EC_SLAVE_DBG_LEVEL EC_DBG_INFO
+#endif
+
+/* Enable print with color */
+#define CONFIG_EC_PRINTF_COLOR_ENABLE
+
+#define EC_FAST_CODE_SECTION __attribute__((section(".fast")))
+
+#ifndef CONFIG_EC_MAX_NETDEVS
+#define CONFIG_EC_MAX_NETDEVS 1
+#endif
+
+#ifndef CONFIG_EC_NONPERIOD_PRIO
+#define CONFIG_EC_NONPERIOD_PRIO 0
+#endif
+
+#ifndef CONFIG_EC_NONPERIOD_STACKSIZE
+#define CONFIG_EC_NONPERIOD_STACKSIZE 2048
+#endif
+
+#ifndef CONFIG_EC_NONPERIOD_INTERVAL_MS
+#define CONFIG_EC_NONPERIOD_INTERVAL_MS 10
+#endif
+
+#ifndef CONFIG_EC_NONPERIOD_WAITERS
+#define CONFIG_EC_NONPERIOD_WAITERS 20
+#endif
+
+#ifndef CONFIG_EC_IDLE_PRIO
+#define CONFIG_EC_IDLE_PRIO 1
+#endif
+
+#ifndef CONFIG_EC_IDLE_STACKSIZE
+#define CONFIG_EC_IDLE_STACKSIZE 2048
+#endif
+
+#ifndef CONFIG_EC_IDLE_INTERVAL_MS
+#define CONFIG_EC_IDLE_INTERVAL_MS 10
+#endif
+
+#ifndef CONFIG_EC_PER_SM_MAX_PDOS
+#define CONFIG_EC_PER_SM_MAX_PDOS 8
+#endif
+
+#ifndef CONFIG_EC_PER_PDO_MAX_PDO_ENTRIES
+#define CONFIG_EC_PER_PDO_MAX_PDO_ENTRIES 8
+#endif
+
+#define CONFIG_EC_PERF_ENABLE
+
+#ifndef CONFIG_EC_MAX_PDO_BUFSIZE
+#define CONFIG_EC_MAX_PDO_BUFSIZE 2048
+#endif
+
+#ifndef CONFIG_EC_MAX_ENET_TXBUF_COUNT
+#define CONFIG_EC_MAX_ENET_TXBUF_COUNT 10
+#endif
+
+#ifndef CONFIG_EC_MAX_ENET_RXBUF_COUNT
+#define CONFIG_EC_MAX_ENET_RXBUF_COUNT 10
+#endif
+
+#endif

+ 19 - 0
demo/hpmicro/inc/shell.h

@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2022, Egahp
+ * Copyright (c) 2024, HPMicro
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef SHELL_H
+#define SHELL_H
+
+#include "hpm_uart_drv.h"
+#include "csh.h"
+
+extern int shell_init(UART_Type *uart, bool need_login);
+extern void shell_uart_isr(void);
+extern void shell_lock(void);
+extern void shell_unlock(void);
+
+#endif

+ 166 - 0
demo/hpmicro/main.c

@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2024 HPMicro
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/* FreeRTOS kernel includes. */
+#include "FreeRTOS.h"
+#include "task.h"
+
+/*  HPM example includes. */
+#include <stdio.h>
+#include "board.h"
+#include "hpm_clock_drv.h"
+#include "hpm_uart_drv.h"
+#include "shell.h"
+#include "hpm_gptmr_drv.h"
+#include "cia402_def.h"
+#include "ec_master.h"
+
+SDK_DECLARE_EXT_ISR_M(BOARD_CONSOLE_UART_IRQ, shell_uart_isr)
+
+#define task_start_PRIORITY (configMAX_PRIORITIES - 2U)
+
+#define MOTOR_MODE_CSV_CSP  0
+#define MOTOR_MODE_CSP      1
+#define MOTOR_MODE_CSV      2
+
+volatile uint8_t motor_mode = MOTOR_MODE_CSV;
+
+ec_master_t g_ec_master;
+
+static void task_start(void *param);
+
+int main(void)
+{
+    board_init();
+
+    if (pdPASS != xTaskCreate(task_start, "task_start", 1024U, NULL, task_start_PRIORITY, NULL)) {
+        printf("Task start creation failed!\r\n");
+        while (1) {
+        };
+    }
+
+    vTaskStartScheduler();
+    printf("Unexpected scheduler exit!\r\n");
+    while (1) {
+    };
+
+    return 0;
+}
+
+static void task_start(void *param)
+{
+    (void)param;
+
+    printf("Try to initialize the uart\r\n"
+           "  if you are using the console uart as the shell uart\r\n"
+           "  failure to initialize may result in no log\r\n");
+
+    uart_config_t shell_uart_config = { 0 };
+    uart_default_config(BOARD_CONSOLE_UART_BASE, &shell_uart_config);
+    shell_uart_config.src_freq_in_hz = clock_get_frequency(BOARD_CONSOLE_UART_CLK_NAME);
+    shell_uart_config.baudrate = 115200;
+
+    if (status_success != uart_init(BOARD_CONSOLE_UART_BASE, &shell_uart_config)) {
+        /* uart failed to be initialized */
+        printf("Failed to initialize uart\r\n");
+        while (1) {
+        };
+    }
+
+    printf("Initialize shell uart successfully\r\n");
+
+    /* default password is : 12345678 */
+    /* shell_init() must be called in-task */
+    if (0 != shell_init(BOARD_CONSOLE_UART_BASE, false)) {
+        /* shell failed to be initialized */
+        printf("Failed to initialize shell\r\n");
+        while (1) {
+        };
+    }
+
+    printf("Initialize shell successfully\r\n");
+
+    /* irq must be enabled after shell_init() */
+    uart_enable_irq(BOARD_CONSOLE_UART_BASE, uart_intr_rx_data_avail_or_timeout);
+    intc_m_enable_irq_with_priority(BOARD_CONSOLE_UART_IRQ, 1);
+
+    printf("Enable shell uart interrupt\r\n");
+
+    ec_master_cmd_init(&g_ec_master);
+    ec_master_init(&g_ec_master, 0);
+
+    printf("Exit start task\r\n");
+
+    vTaskDelete(NULL);
+}
+
+CSH_CMD_EXPORT(ethercat, );
+
+static ec_pdo_entry_info_t coe402_1602[] = {
+    { 0x6040, 0x00, 0x10 },
+    { 0x60ff, 0x00, 0x20 },
+    { 0x0000, 0x00, 0x10 },
+};
+
+static ec_pdo_entry_info_t coe402_1a02[] = {
+    { 0x6041, 0x00, 0x10 },
+    { 0x6064, 0x00, 0x20 },
+    { 0x0000, 0x00, 0x10 },
+};
+
+static ec_pdo_info_t cia402_rxpdos[] = {
+    { 0x1602, 3, &coe402_1602[0] },
+};
+
+static ec_pdo_info_t cia402_txpdos[] = {
+    { 0x1a02, 3, &coe402_1a02[0] },
+};
+
+static ec_sync_info_t cia402_syncs[] = {
+    { 2, EC_DIR_OUTPUT, 1, cia402_rxpdos },
+    { 3, EC_DIR_INPUT, 1, cia402_txpdos },
+};
+
+int ec_start(int argc, const char **argv)
+{
+    static ec_slave_config_t slave_config;
+
+    if (g_ec_master.slave_count == 0) {
+        printf("No slave found, please check the connection\r\n");
+        return -1;
+    }
+
+    if (argc < 2) {
+        printf("Please input: ec_start <cyclic time in us>\r\n");
+        return -1;
+    }
+
+    slave_config.dc_assign_activate = 0x300;
+
+    slave_config.dc_sync[0].cycle_time = atoi(argv[1]) * 1000;
+    slave_config.dc_sync[0].shift_time = 1000000;
+    slave_config.dc_sync[1].cycle_time = 0;
+    slave_config.dc_sync[1].shift_time = 0;
+
+    slave_config.sync = cia402_syncs;
+    slave_config.sync_count = sizeof(cia402_syncs) / sizeof(ec_sync_info_t);
+
+    for (uint32_t i = 0; i < g_ec_master.slave_count; i++) {
+        g_ec_master.slaves[i].config = &slave_config;
+    }
+
+    ec_master_start(&g_ec_master, atoi(argv[1]));
+    return 0;
+}
+CSH_CMD_EXPORT(ec_start, );
+
+int ec_stop(int argc, const char **argv)
+{
+    ec_master_stop(&g_ec_master);
+    return 0;
+}
+CSH_CMD_EXPORT(ec_stop, );

+ 1 - 0
docs/.gitignore

@@ -0,0 +1 @@
+*build

+ 20 - 0
docs/Makefile

@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = source
+BUILDDIR      = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

BIN
docs/assets/ethercat.png


BIN
docs/assets/ethercat2.png


BIN
docs/assets/ethercat3.png


BIN
docs/assets/ethercat4.png


BIN
docs/assets/ethercat5.png


+ 35 - 0
docs/make.bat

@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=source
+set BUILDDIR=build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+	echo.
+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+	echo.installed, then set the SPHINXBUILD environment variable to point
+	echo.to the full path of the 'sphinx-build' executable. Alternatively you
+	echo.may add the Sphinx directory to PATH.
+	echo.
+	echo.If you don't have Sphinx installed, grab it from
+	echo.http://sphinx-doc.org/
+	exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd

+ 9 - 0
docs/requirements.txt

@@ -0,0 +1,9 @@
+# markdown suport
+recommonmark
+# markdown table suport
+sphinx-markdown-tables
+
+# theme default rtd
+
+# crate-docs-theme
+sphinx-rtd-theme

+ 37 - 0
docs/source/conf.py

@@ -0,0 +1,37 @@
+# Configuration file for the Sphinx documentation builder.
+
+# -- Project information
+
+project = 'CherryEC'
+copyright = '2025 ~ 2026, sakumisu'
+author = 'sakumisu'
+
+release = '0.1.0'
+version = '0.1.0'
+
+# -- General configuration
+
+extensions = [
+    'sphinx.ext.duration',
+    'sphinx.ext.doctest',
+    'sphinx.ext.autodoc',
+    'sphinx.ext.autosummary',
+    'sphinx.ext.intersphinx',
+    'recommonmark',
+    'sphinx_markdown_tables'
+]
+
+intersphinx_mapping = {
+#    'python': ('https://docs.python.org/3/', None),
+#    'sphinx': ('https://www.sphinx-doc.org/en/master/', None),
+}
+intersphinx_disabled_domains = ['std']
+
+templates_path = ['_templates']
+
+# -- Options for HTML output
+
+html_theme = 'sphinx_rtd_theme'
+
+# -- Options for EPUB output
+epub_show_urls = 'footnote'

+ 32 - 0
docs/source/ethercat.rst

@@ -0,0 +1,32 @@
+EtherCAT 概念
+===========================
+
+EtherCAT 官方文档汇总
+-----------------------------
+
+EtherCAT 数据格式
+--------------------
+
+EtherCAT 寻址模式
+--------------------
+
+EtherCAT 状态机
+--------------------
+
+EtherCAT 同步
+--------------------
+
+EtherCAT SII EEPROM
+-----------------------
+
+EtherCAT SM & FMMU
+-----------------------
+
+EtherCAT SDO & PDO
+-----------------------
+
+EtherCAT COE
+-----------------------
+
+EtherCAT FOE
+-----------------------

+ 14 - 0
docs/source/index.rst

@@ -0,0 +1,14 @@
+.. CherryEC 使用指南 documentation master file, created by
+   sphinx-quickstart on Thu Nov 21 10:50:33 2019.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+CherryEC 使用指南
+======================================================
+
+CherryEC 是一个小而美的、高实时性、低抖动 EtherCAT 主机协议栈,专为跑在 RTOS 下的 MCU 设计。
+
+.. toctree::
+   :maxdepth: 1
+
+   ethercat

+ 14 - 0
include/ec_cmd.h

@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_CMD_H
+#define EC_CMD_H
+
+typedef struct ec_master ec_master_t;
+
+void ec_master_cmd_init(ec_master_t *master);
+int ethercat(int argc, const char **argv);
+
+#endif

+ 25 - 0
include/ec_coe.h

@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_COE_H
+#define EC_COE_H
+
+int ec_coe_download(ec_slave_t *slave,
+                    ec_datagram_t *datagram,
+                    uint16_t index,
+                    uint8_t subindex,
+                    const void *buf,
+                    uint32_t size,
+                    bool complete_access);
+
+int ec_coe_upload(ec_slave_t *slave,
+                  ec_datagram_t *datagram,
+                  uint16_t index,
+                  uint8_t subindex,
+                  const void *buf,
+                  uint32_t maxsize,
+                  uint32_t *size,
+                  bool complete_access);
+#endif

+ 17 - 0
include/ec_common.h

@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_COMMON_H
+#define EC_COMMON_H
+
+void *ec_memcpy(void *s1, const void *s2, size_t n);
+void ec_memset(void *s, int c, size_t n);
+const char *ec_state_string(uint8_t states, uint8_t multi);
+const char *ec_mbox_protocol_string(uint8_t prot);
+const char *ec_alstatus_string(uint16_t errorcode);
+const char *ec_mbox_error_string(uint16_t errorcode);
+const char *ec_sdo_abort_string(uint32_t errorcode);
+
+#endif

+ 91 - 0
include/ec_datagram.h

@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_DATAGRAM_H
+#define EC_DATAGRAM_H
+
+/** EtherCAT datagram type.
+ */
+typedef enum {
+    EC_DATAGRAM_NONE = 0x00, /**< Dummy. */
+    EC_DATAGRAM_APRD = 0x01, /**< Auto Increment Physical Read. */
+    EC_DATAGRAM_APWR = 0x02, /**< Auto Increment Physical Write. */
+    EC_DATAGRAM_APRW = 0x03, /**< Auto Increment Physical ReadWrite. */
+    EC_DATAGRAM_FPRD = 0x04, /**< Configured Address Physical Read. */
+    EC_DATAGRAM_FPWR = 0x05, /**< Configured Address Physical Write. */
+    EC_DATAGRAM_FPRW = 0x06, /**< Configured Address Physical ReadWrite. */
+    EC_DATAGRAM_BRD = 0x07,  /**< Broadcast Read. */
+    EC_DATAGRAM_BWR = 0x08,  /**< Broadcast Write. */
+    EC_DATAGRAM_BRW = 0x09,  /**< Broadcast ReadWrite. */
+    EC_DATAGRAM_LRD = 0x0A,  /**< Logical Read. */
+    EC_DATAGRAM_LWR = 0x0B,  /**< Logical Write. */
+    EC_DATAGRAM_LRW = 0x0C,  /**< Logical ReadWrite. */
+    EC_DATAGRAM_ARMW = 0x0D, /**< Auto Increment Physical Read Multiple
+                               Write.  */
+    EC_DATAGRAM_FRMW = 0x0E, /**< Configured Address Physical Read Multiple
+                               Write. */
+} ec_datagram_type_t;
+
+/** EtherCAT datagram state.
+ */
+typedef enum {
+    EC_DATAGRAM_INIT,      /**< Initial state of a new datagram. */
+    EC_DATAGRAM_QUEUED,    /**< Queued for sending. */
+    EC_DATAGRAM_SENT,      /**< Sent (still in the queue). */
+    EC_DATAGRAM_RECEIVED,  /**< Received (dequeued). */
+    EC_DATAGRAM_TIMED_OUT, /**< Timed out (dequeued). */
+    EC_DATAGRAM_ERROR      /**< Error while sending/receiving (dequeued). */
+} ec_datagram_state_t;
+
+/** EtherCAT datagram.
+ */
+typedef struct {
+    ec_dlist_t queue;
+    ec_dlist_t ext_queue;
+    ec_dlist_t sent;
+    ec_netdev_index_t netdev_idx;     /**< Netdev via which the datagram shall be / was sent. */
+    ec_datagram_type_t type;          /**< Datagram type (APRD, BWR, etc.). */
+    bool static_alloc;                /**< True, if \a data is statically allocated. */
+    uint8_t address[EC_ADDR_LEN];     /**< Recipient address. */
+    uint8_t *data;                    /**< Datagram payload. */
+    size_t mem_size;                  /**< Datagram \a data memory size. */
+    size_t data_size;                 /**< Size of the data in \a data. */
+    uint8_t index;                    /**< Index (set by master). */
+    uint16_t working_counter;         /**< Working counter. */
+    ec_datagram_state_t state;        /**< State. */
+    uint64_t jiffies_sent;            /**< Jiffies, when the datagram was sent. */
+    uint64_t jiffies_received;        /**< Jiffies, when the datagram was received. */
+    char name[EC_DATAGRAM_NAME_SIZE]; /**< Description of the datagram. */
+    bool waiter;                      /**< True, if someone is waiting for the datagram. */
+    ec_osal_sem_t wait;               /**< Semaphore for waiting. */
+} ec_datagram_t;
+
+void ec_datagram_init(ec_datagram_t *datagram, size_t mem_size);
+void ec_datagram_init_static(ec_datagram_t *datagram, uint8_t *data, size_t mem_size);
+void ec_datagram_clear(ec_datagram_t *datagram);
+void ec_datagram_unqueue(ec_datagram_t *datagram);
+void ec_datagram_zero(ec_datagram_t *datagram);
+void ec_datagram_fill(ec_datagram_t *datagram,
+                      ec_datagram_type_t type,
+                      uint16_t adp,
+                      uint16_t ado,
+                      uint16_t size);
+void ec_datagram_aprd(ec_datagram_t *datagram, uint16_t autoinc_address, uint16_t mem_address, size_t data_size);
+void ec_datagram_apwr(ec_datagram_t *datagram, uint16_t autoinc_address, uint16_t mem_address, size_t data_size);
+void ec_datagram_aprw(ec_datagram_t *datagram, uint16_t autoinc_address, uint16_t mem_address, size_t data_size);
+void ec_datagram_armw(ec_datagram_t *datagram, uint16_t autoinc_address, uint16_t mem_address, size_t data_size);
+void ec_datagram_fprd(ec_datagram_t *datagram, uint16_t configured_address, uint16_t mem_address, size_t data_size);
+void ec_datagram_fpwr(ec_datagram_t *datagram, uint16_t configured_address, uint16_t mem_address, size_t data_size);
+void ec_datagram_fprw(ec_datagram_t *datagram, uint16_t configured_address, uint16_t mem_address, size_t data_size);
+void ec_datagram_frmw(ec_datagram_t *datagram, uint16_t configured_address, uint16_t mem_address, size_t data_size);
+void ec_datagram_brd(ec_datagram_t *datagram, uint16_t mem_address, size_t data_size);
+void ec_datagram_bwr(ec_datagram_t *datagram, uint16_t mem_address, size_t data_size);
+void ec_datagram_brw(ec_datagram_t *datagram, uint16_t mem_address, size_t data_size);
+void ec_datagram_lrd(ec_datagram_t *datagram, uint32_t offset, size_t data_size);
+void ec_datagram_lwr(ec_datagram_t *datagram, uint32_t offset, size_t data_size);
+void ec_datagram_lrw(ec_datagram_t *datagram, uint32_t offset, size_t data_size);
+const char *ec_datagram_type_string(const ec_datagram_t *datagram);
+
+#endif

+ 507 - 0
include/ec_def.h

@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_DEF_H
+#define EC_DEF_H
+
+/*
+ *	IEEE 802.3 Ethernet magic constants.  The frame sizes omit the preamble
+ *	and FCS/CRC (frame check sequence).
+ */
+#define ETH_ALEN                     6    /* Octets in one ethernet addr	 */
+#define ETH_TLEN                     2    /* Octets in ethernet type field */
+#define ETH_HLEN                     14   /* Total octets in header.	 */
+#define ETH_ZLEN                     60   /* Min. octets in frame sans FCS */
+#define ETH_DATA_LEN                 1500 /* Max. octets in payload	 */
+#define ETH_FRAME_LEN                1514 /* Max. octets in frame sans FCS */
+#define ETH_FCS_LEN                  4    /* Octets in the FCS		 */
+
+#define ETH_MIN_MTU                  68      /* Min IPv4 MTU per RFC791	*/
+#define ETH_MAX_MTU                  0xFFFFU /* 65535, same as IP_MAX_MTU	*/
+
+/** Datagram timeout in microseconds. */
+#define EC_IO_TIMEOUT                500
+
+/** Time to send a byte in nanoseconds.
+ *
+ * t_ns = 1 / (100 MBit/s / 8 bit/byte) = 80 ns/byte
+ */
+#define EC_BYTE_TRANSMISSION_TIME_NS 80
+
+/** Minimum size of a buffer used with ec_state_string(). */
+#define EC_STATE_STRING_SIZE         32
+
+/** Maximum SII size in words, to avoid infinite reading. */
+#define EC_MAX_SII_SIZE              4096
+
+/** Number of statistic rate intervals to maintain. */
+#define EC_RATE_COUNT                3
+
+/*****************************************************************************
+ * EtherCAT protocol
+ ****************************************************************************/
+
+/** Size of an EtherCAT frame header. */
+#define EC_FRAME_HEADER_SIZE         2
+
+/** Size of an EtherCAT datagram header. */
+#define EC_DATAGRAM_HEADER_SIZE      10
+
+/** Size of an EtherCAT datagram workcounter. */
+#define EC_DATAGRAM_WC_SIZE          2
+
+/** Size of the EtherCAT address field. */
+#define EC_ADDR_LEN                  4
+
+/** Resulting maximum data size of a single datagram in a frame. */
+#define EC_MAX_DATA_SIZE             (ETH_DATA_LEN - EC_FRAME_HEADER_SIZE - EC_DATAGRAM_HEADER_SIZE - EC_DATAGRAM_WC_SIZE)
+
+/** Mailbox header size.  */
+#define EC_MBOX_HEADER_SIZE          6
+
+/** Word offset of first SII category. */
+#define EC_FIRST_SII_CATEGORY_OFFSET 0x40
+
+/** Size of a sync manager configuration page. */
+#define EC_SYNC_PAGE_SIZE            8
+
+/** Maximum number of FMMUs per slave. */
+#define EC_MAX_FMMUS                 16
+
+/** Size of an FMMU configuration page. */
+#define EC_FMMU_PAGE_SIZE            16
+
+/** Number of DC sync signals. */
+#define EC_SYNC_SIGNAL_COUNT         2
+
+/** Size of the datagram decription string.
+ *
+ * This is also used as the maximum lenth of EoE device names.
+ **/
+#define EC_DATAGRAM_NAME_SIZE        20
+
+/** Maximum hostname size.
+ *
+ * Used inside the EoE set IP parameter request.
+ */
+#define EC_MAX_HOSTNAME_SIZE         32
+
+/** Maximum number of sync managers per slave.
+ */
+#define EC_MAX_SYNC_MANAGERS         16
+
+/** Maximum string length.
+ *
+ * Used in ec_slave_info_t.
+ */
+#define EC_MAX_STRING_LENGTH         64
+
+/** Maximum number of slave ports. */
+#define EC_MAX_PORTS                 4
+
+/** Slave state mask.
+ *
+ * Apply this mask to a slave state byte to get the slave state without
+ * the error flag.
+ */
+#define EC_SLAVE_STATE_MASK          0x0F
+
+/** State of an EtherCAT slave.
+ */
+typedef enum {
+    EC_SLAVE_STATE_UNKNOWN = 0x00,
+    /**< unknown state */
+    EC_SLAVE_STATE_INIT = 0x01,
+    /**< INIT state (no mailbox communication, no IO) */
+    EC_SLAVE_STATE_PREOP = 0x02,
+    /**< PREOP state (mailbox communication, no IO) */
+    EC_SLAVE_STATE_BOOT = 0x03,
+    /**< Bootstrap state (mailbox communication, firmware update) */
+    EC_SLAVE_STATE_SAFEOP = 0x04,
+    /**< SAFEOP (mailbox communication and input update) */
+    EC_SLAVE_STATE_OP = 0x08,
+    /**< OP (mailbox communication and input/output update) */
+    EC_SLAVE_STATE_ACK_ERR = 0x10
+    /**< Acknowledge/Error bit (no actual state) */
+} ec_slave_state_t;
+
+/** Slave information interface CANopen over EtherCAT details flags.
+ */
+typedef struct {
+    uint8_t enable_sdo                 : 1; /**< Enable SDO access. */
+    uint8_t enable_sdo_info            : 1; /**< SDO information service available. */
+    uint8_t enable_pdo_assign          : 1; /**< PDO mapping configurable. */
+    uint8_t enable_pdo_configuration   : 1; /**< PDO configuration possible. */
+    uint8_t enable_upload_at_startup   : 1; /**< ?. */
+    uint8_t enable_sdo_complete_access : 1; /**< Complete access possible. */
+    uint8_t                            : 2; /**< Reserved bits. */
+} ec_sii_coe_details_t;
+
+/** Slave information interface general flags.
+ */
+typedef struct {
+    uint8_t enable_safeop  : 1; /**< ?. */
+    uint8_t enable_not_lrw : 1; /**< Slave does not support LRW. */
+    uint8_t                : 6; /**< Reserved bits. */
+} ec_sii_general_flags_t;
+
+/** EtherCAT slave distributed clocks range.
+ */
+typedef enum {
+    EC_DC_32, /**< 32 bit. */
+    EC_DC_64  /*< 64 bit for system time, system time offset and
+               port 0 receive time. */
+} ec_slave_dc_range_t;
+
+/** EtherCAT slave sync signal configuration.
+ */
+typedef struct {
+    uint32_t cycle_time; /**< Cycle time [ns]. */
+    int32_t shift_time;  /**< Shift time [ns]. */
+} ec_sync_signal_t;
+
+/** Master netdev.
+ */
+typedef enum {
+    EC_NETDEV_MAIN,  /**< Main netdev. */
+    EC_NETDEV_BACKUP /**< Backup netdev */
+} ec_netdev_index_t;
+
+typedef struct ec_alstatus {
+    uint16_t alstatus;
+    uint16_t unused;
+    uint16_t alstatuscode;
+} ec_alstatus_t;
+
+/* AL Status Codes */
+#define EC_ALSTATUSCODE_NOERROR                    0x0000 /**< No error*/
+#define EC_ALSTATUSCODE_UNSPECIFIEDERROR           0x0001 /**< Unspecified error*/
+#define EC_ALSTATUSCODE_NOMEMORY                   0x0002 /**< No Memory*/
+#define EC_ALSTATUSCODE_INVALID_REVISION           0x0004 /**< Output/Input mapping is not valid for this hardware or software revision (0x1018:03)*/
+#define EC_ALSTATUSCODE_FW_SII_NOT_MATCH           0x0006 /**< Firmware and EEPROM do not match. Slave needs BOOT-INIT transition*/
+#define EC_ALSTATUSCODE_FW_UPDATE_FAILED           0x0007 /**< Firmware update not successful. Old firmware still running*/
+#define EC_ALSTATUSCODE_INVALIDALCONTROL           0x0011 /**< Invalid requested state change*/
+#define EC_ALSTATUSCODE_UNKNOWNALCONTROL           0x0012 /**< Unknown requested state*/
+#define EC_ALSTATUSCODE_BOOTNOTSUPP                0x0013 /**< Bootstrap not supported*/
+#define EC_ALSTATUSCODE_NOVALIDFIRMWARE            0x0014 /**< No valid firmware*/
+#define EC_ALSTATUSCODE_INVALIDMBXCFGINBOOT        0x0015 /**< Invalid mailbox configuration (BOOT state)*/
+#define EC_ALSTATUSCODE_INVALIDMBXCFGINPREOP       0x0016 /**< Invalid mailbox configuration (PreOP state)*/
+#define EC_ALSTATUSCODE_INVALIDSMCFG               0x0017 /**< Invalid sync manager configuration*/
+#define EC_ALSTATUSCODE_NOVALIDINPUTS              0x0018 /**< No valid inputs available*/
+#define EC_ALSTATUSCODE_NOVALIDOUTPUTS             0x0019 /**< No valid outputs*/
+#define EC_ALSTATUSCODE_SYNCERROR                  0x001A /**< Synchronization error*/
+#define EC_ALSTATUSCODE_SMWATCHDOG                 0x001B /**< Sync manager watchdog*/
+#define EC_ALSTATUSCODE_SYNCTYPESNOTCOMPATIBLE     0x001C /**< Invalid Sync Manager Types*/
+#define EC_ALSTATUSCODE_INVALIDSMOUTCFG            0x001D /**< Invalid Output Configuration*/
+#define EC_ALSTATUSCODE_INVALIDSMINCFG             0x001E /**< Invalid Input Configuration*/
+#define EC_ALSTATUSCODE_INVALIDWDCFG               0x001F /**< Invalid Watchdog Configuration*/
+#define EC_ALSTATUSCODE_WAITFORCOLDSTART           0x0020 /**< Slave needs cold start*/
+#define EC_ALSTATUSCODE_WAITFORINIT                0x0021 /**< Slave needs INIT*/
+#define EC_ALSTATUSCODE_WAITFORPREOP               0x0022 /**< Slave needs PREOP*/
+#define EC_ALSTATUSCODE_WAITFORSAFEOP              0x0023 /**< Slave needs SAFEOP*/
+#define EC_ALSTATUSCODE_INVALIDINPUTMAPPING        0x0024 /**< Invalid Input Mapping*/
+#define EC_ALSTATUSCODE_INVALIDOUTPUTMAPPING       0x0025 /**< Invalid Output Mapping*/
+#define EC_ALSTATUSCODE_INCONSISTENTSETTINGS       0x0026 /**< Inconsistent Settings*/
+#define EC_ALSTATUSCODE_FREERUNNOTSUPPORTED        0x0027 /**< FreeRun not supported*/
+#define EC_ALSTATUSCODE_SYNCHRONNOTSUPPORTED       0x0028 /**< SyncMode not supported*/
+#define EC_ALSTATUSCODE_FREERUNNEEDS3BUFFERMODE    0x0029 /**< FreeRun needs 3Buffer Mode*/
+#define EC_ALSTATUSCODE_BACKGROUNDWATCHDOG         0x002A /**< Background Watchdog*/
+#define EC_ALSTATUSCODE_NOVALIDINPUTSANDOUTPUTS    0x002B /**< No Valid Inputs and Outputs*/
+#define EC_ALSTATUSCODE_FATALSYNCERROR             0x002C /**< Fatal Sync Error*/
+#define EC_ALSTATUSCODE_NOSYNCERROR                0x002D /**< No Sync Error*/
+#define EC_ALSTATUSCODE_CYCLETIMETOOSMALL          0x002E /**< EtherCAT cycle time smaller Minimum Cycle Time supported by slave*/
+#define EC_ALSTATUSCODE_DCINVALIDSYNCCFG           0x0030 /**< Invalid DC SYNCH Configuration*/
+#define EC_ALSTATUSCODE_DCINVALIDLATCHCFG          0x0031 /**< Invalid DC Latch Configuration*/
+#define EC_ALSTATUSCODE_DCPLLSYNCERROR             0x0032 /**< PLL Error*/
+#define EC_ALSTATUSCODE_DCSYNCIOERROR              0x0033 /**< DC Sync IO Error*/
+#define EC_ALSTATUSCODE_DCSYNCMISSEDERROR          0x0034 /**< DC Sync Timeout Error*/
+#define EC_ALSTATUSCODE_DCINVALIDSYNCCYCLETIME     0x0035 /**< DC Invalid Sync Cycle Time*/
+#define EC_ALSTATUSCODE_DCSYNC0CYCLETIME           0x0036 /**< DC Sync0 Cycle Time*/
+#define EC_ALSTATUSCODE_DCSYNC1CYCLETIME           0x0037 /**< DC Sync1 Cycle Time*/
+#define EC_ALSTATUSCODE_MBX_AOE                    0x0041 /**< MBX_AOE*/
+#define EC_ALSTATUSCODE_MBX_EOE                    0x0042 /**< MBX_EOE*/
+#define EC_ALSTATUSCODE_MBX_COE                    0x0043 /**< MBX_COE*/
+#define EC_ALSTATUSCODE_MBX_FOE                    0x0044 /**< MBX_FOE*/
+#define EC_ALSTATUSCODE_MBX_SOE                    0x0045 /**< MBX_SOE*/
+#define EC_ALSTATUSCODE_MBX_VOE                    0x004F /**< MBX_VOE*/
+#define EC_ALSTATUSCODE_EE_NOACCESS                0x0050 /**< EEPROM no access*/
+#define EC_ALSTATUSCODE_EE_ERROR                   0x0051 /**< EEPROM Error*/
+#define EC_ALSTATUSCODE_EXT_HARDWARE_NOT_READY     0x0052 /**< External hardware not ready. This AL Status Code should be used if the EtherCAT-Slave refused the state transition due to an external connection to another device or signal is missing*/
+#define EC_ALSTATUSCODE_DEVICE_IDENT_VALUE_UPDATED 0x0061 /**< In legacy identification mode (dip switch mapped to register 0x12) this error is returned if the EEPROM ID value does not match to dipswitch value*/
+#define EC_ALSTATUSCODE_MODULE_ID_LIST_NOT_MATCH   0x0070 /**< Detected Module Ident List (0xF030) and Configured Module Ident List (0xF050) does not match*/
+#define EC_ALSTATUSCODE_SUPPLY_VOLTAGE_TOO_LOW     0x0080 /**< The slave supply voltage is too low*/
+#define EC_ALSTATUSCODE_SUPPLY_VOLTAGE_TOO_HIGH    0x0081 /**< The slave supply voltage is too high*/
+#define EC_ALSTATUSCODE_TEMPERATURE_TOO_LOW        0x0082 /**< The slave temperature is too low*/
+#define EC_ALSTATUSCODE_TEMPERATURE_TOO_HIGH       0x0083 /**< The slave temperature is too high*/
+
+#define EC_SII_ADDRESS_MANUF                       (0x0008)
+#define EC_SII_ADDRESS_PRODUCTCODE                 (0x000a)
+#define EC_SII_ADDRESS_REVISION                    (0x000c)
+#define EC_SII_ADDRESS_SN                          (0x000E)
+#define EC_SII_ADDRESS_BOOTRXMBX                   (0x0014)
+#define EC_SII_ADDRESS_BOOTTXMBX                   (0x0016)
+#define EC_SII_ADDRESS_MBXSIZE                     (0x0019)
+#define EC_SII_ADDRESS_TXMBXADR                    (0x001a)
+#define EC_SII_ADDRESS_RXMBXADR                    (0x0018)
+#define EC_SII_ADDRESS_MBXPROTO                    (0x001c)
+#define EC_SII_ADDRESS_ADDITIONAL_INFO             (0x0040)
+
+#define EC_SII_TYPE_NOP                            0x0000
+#define EC_SII_TYPE_STRINGS                        0x000A
+#define EC_SII_TYPE_DATATYPES                      0x0014
+#define EC_SII_TYPE_GENERAL                        0x001E
+#define EC_SII_TYPE_FMMU                           0x0028
+#define EC_SII_TYPE_SM                             0x0029
+#define EC_SII_TYPE_FMMUX                          0x002A
+#define EC_SII_TYPE_SYNCUNIT                       0x002B
+#define EC_SII_TYPE_TXPDO                          0x0032
+#define EC_SII_TYPE_RXPDO                          0x0033
+#define EC_SII_TYPE_DC                             0x003C
+#define EC_SII_TYPE_END                            0xFFFF
+
+#define EC_SII_FMMU_NONE                           0x0000
+#define EC_SII_FMMU_READ                           0x0001
+#define EC_SII_FMMU_WRITE                          0x0002
+#define EC_SII_FMMU_SM_STATUS                      0x0003
+
+#define EC_SII_SM_UNKNOWN                          0x0000
+#define EC_SII_SM_MBX_OUT                          0x0001
+#define EC_SII_SM_MBX_IN                           0x0002
+#define EC_SII_SM_PROCESS_DATA_OUTPUT              0x0003
+#define EC_SII_SM_PROCESS_DATA_INPUT               0x0004
+
+typedef struct __PACKED ec_sii_base {
+    uint16_t pdi_control;
+    uint16_t pdi_config;
+    uint16_t sync_impulselen;
+    uint16_t pdi_config2;
+    uint16_t aliasaddr; /**< Configured station alias. */
+    uint8_t reserved[4];
+    uint16_t checksum;
+    uint32_t vendor_id;       /**< Vendor ID. */
+    uint32_t product_code;    /**< Vendor-specific product code. */
+    uint32_t revision_number; /**< Revision number. */
+    uint32_t serial_number;   /**< Serial number. */
+    uint8_t reserved2[8];
+    uint16_t boot_rx_mailbox_offset; /**< Bootstrap receive mailbox address. */
+    uint16_t boot_rx_mailbox_size;   /**< Bootstrap receive mailbox size. */
+    uint16_t boot_tx_mailbox_offset; /**< Bootstrap transmit mailbox address. */
+    uint16_t boot_tx_mailbox_size;   /**< Bootstrap transmit mailbox size. */
+    uint16_t std_rx_mailbox_offset;  /**< Standard receive mailbox address. */
+    uint16_t std_rx_mailbox_size;    /**< Standard receive mailbox size. */
+    uint16_t std_tx_mailbox_offset;  /**< Standard transmit mailbox address. */
+    uint16_t std_tx_mailbox_size;    /**< Standard transmit mailbox size. */
+    uint16_t mailbox_protocols;      /**< Supported mailbox protocols. */
+    uint8_t reserved3[66];
+    uint16_t size;
+    uint16_t version;
+} ec_sii_base_t;
+
+typedef struct __PACKED ec_sii_general {
+    uint8_t groupidx;
+    uint8_t imgidx;
+    uint8_t orderidx;
+    uint8_t nameidx;
+    uint8_t reserved1;
+    ec_sii_coe_details_t coe_details;
+    uint8_t foe_details;
+    uint8_t eoe_details;
+    uint8_t soe_channels;
+    uint8_t ds402_channels;
+    uint8_t sysmanclass;
+    ec_sii_general_flags_t flags;
+    int16_t current_on_ebus;
+    uint8_t reserved2;
+    uint8_t reserved3;
+    uint16_t phy_port;
+    uint16_t phy_memaddress;
+    uint8_t pad[12];
+} ec_sii_general_t;
+
+typedef struct __PACKED ec_sii_sm {
+    uint16_t physical_start_address;
+    uint16_t length;
+    uint8_t control;
+    uint8_t status;
+    uint8_t active;
+    uint8_t type;
+} ec_sii_sm_t;
+
+typedef struct __PACKED ec_sii_pdo_entry {
+    uint16_t index;
+    uint8_t subindex;
+    uint8_t nameidx;
+    uint8_t data_type;
+    uint8_t bitlen;
+    uint16_t flags;
+} ec_sii_pdo_entry_t;
+
+typedef struct __PACKED ec_sii_pdo_mapping {
+    uint16_t index; /* txpdo: 1a00~1bff, rxpdo: 1600~17ff */
+    uint8_t nentry;
+    uint8_t sm_idx;
+    uint8_t synchronization;
+    uint8_t nameidx;
+    uint16_t flags;
+    ec_sii_pdo_entry_t entry[];
+} ec_sii_pdo_mapping_t;
+
+typedef struct __PACKED ec_sm_reg {
+    uint16_t physical_start_address;
+    uint16_t length;
+    uint8_t control;
+    uint8_t status;
+    uint8_t active;
+    uint8_t pdi_control;
+} ec_sm_reg_t;
+
+typedef struct __PACKED ec_fmmu_reg {
+    uint32_t logical_start_address;
+    uint16_t length;
+    uint8_t logical_start_bit;
+    uint8_t logical_stop_bit;
+    uint16_t physical_start_address;
+    uint8_t physical_start_bit;
+    uint8_t type;
+    uint8_t active;
+    uint8_t reserved[3];
+} ec_fmmu_reg_t;
+
+/**
+ * \brief SmAssignObjects SyncManager Assignment Objects
+ * SyncManager 2 : 0x1C12<br>
+ * SyncManager 3 : 0x1C13<br>
+ */
+typedef struct __PACKED ec_pdo_assign_t {
+    uint16_t count; /**< PDO mapping count. */
+    uint16_t entry[CONFIG_EC_PER_SM_MAX_PDOS];
+} ec_pdo_assign_t;
+
+typedef struct __PACKED ec_pdo_mapping_t {
+    uint16_t count; /**< PDO entry count. */
+    uint32_t entry[CONFIG_EC_PER_PDO_MAX_PDO_ENTRIES];
+} ec_pdo_mapping_t;
+
+typedef struct __PACKED ec_mailbox_header {
+    uint16_t length;
+    uint16_t address;
+    uint8_t channel  : 6;
+    uint8_t priority : 2;
+    uint8_t type     : 4;
+    uint8_t counter  : 3;
+    uint8_t reserved : 1;
+} ec_mailbox_header_t;
+
+/** Size of the mailbox header.
+ */
+#define EC_MBOX_HEADER_SIZE 6
+
+#define EC_MBXPROT_AOE      0x0001
+#define EC_MBXPROT_EOE      0x0002
+#define EC_MBXPROT_COE      0x0004
+#define EC_MBXPROT_FOE      0x0008
+#define EC_MBXPROT_SOE      0x0010
+#define EC_MBXPROT_VOE      0x0020
+
+/** Mailbox types.
+ *
+ * These are used in the 'Type' field of the mailbox header.
+ */
+enum {
+    EC_MBOX_TYPE_EOE = 0x02,
+    EC_MBOX_TYPE_COE = 0x03,
+    EC_MBOX_TYPE_FOE = 0x04,
+    EC_MBOX_TYPE_SOE = 0x05,
+    EC_MBOX_TYPE_VOE = 0x0f,
+};
+
+#define EC_SM_INDEX_MBX_WRITE           0x0000
+#define EC_SM_INDEX_MBX_READ            0x0001
+#define EC_SM_INDEX_PROCESS_DATA_OUTPUT 0x0002
+#define EC_SM_INDEX_PROCESS_DATA_INPUT  0x0003
+
+typedef struct __PACKED ec_coe_header {
+    uint16_t number   : 9;
+    uint16_t reserved : 3;
+    uint16_t service  : 4;
+} ec_coe_header_t;
+
+typedef struct __PACKED ec_sdo_header_common {
+    uint8_t size_indicator  : 1;
+    uint8_t transfertype    : 1; // expedited transfer
+    uint8_t data_set_size   : 2;
+    uint8_t complete_access : 1;
+    uint8_t command         : 3;
+} ec_sdo_header_common_t;
+
+typedef struct __PACKED ec_sdo_header_segment {
+    uint8_t more_follows : 1;
+    uint8_t segdata_size : 3;
+    uint8_t toggle       : 1;
+    uint8_t command      : 3;
+} ec_sdo_header_segment_t;
+
+typedef struct __PACKED ec_sdo_header {
+    union {
+        uint8_t byte;
+        ec_sdo_header_common_t common;
+        ec_sdo_header_segment_t segment;
+    };
+    uint16_t index;
+    uint8_t subindex;
+} ec_sdo_header_t;
+
+#define EC_COE_SERVICE_EMERGENCY            0x01
+#define EC_COE_SERVICE_SDO_REQUEST          0x02
+#define EC_COE_SERVICE_SDO_RESPONSE         0x03
+#define EC_COE_SERVICE_TXPDO                0x04
+#define EC_COE_SERVICE_RXPDO                0x05
+#define EC_COE_SERVICE_TXPDO_REMOTE_REQUSET 0x06
+#define EC_COE_SERVICE_RXPDO_REMOTE_REQUEST 0x07
+#define EC_COE_SERVICE_SDOINFO              0x08
+
+#define EC_COE_REQUEST_SEGMENT_DOWNLOAD     0x00
+#define EC_COE_REQUEST_DOWNLOAD             0x01
+#define EC_COE_REQUEST_UPLOAD               0x02
+#define EC_COE_REQUEST_SEGMENT_UPLOAD       0x03
+#define EC_COE_REQUEST_ABORT                0x04
+
+#define EC_COE_RESPONSE_SEGMENT_UPLOAD      0x00
+#define EC_COE_RESPONSE_SEGMENT_DOWNLOAD    0x01
+#define EC_COE_RESPONSE_UPLOAD              0x02
+#define EC_COE_RESPONSE_DOWNLOAD            0x03
+
+typedef enum {
+    EC_DIR_OUTPUT, /**< Values written by the master. */
+    EC_DIR_INPUT,  /**< Values read by the master. */
+} ec_direction_t;
+
+typedef enum {
+    EC_WD_DEFAULT, /**< Use the default setting of the sync manager. */
+    EC_WD_ENABLE,  /**< Enable the watchdog. */
+    EC_WD_DISABLE, /**< Disable the watchdog. */
+} ec_watchdog_mode_t;
+
+typedef struct {
+    uint16_t index;     /**< PDO entry index. */
+    uint8_t subindex;   /**< PDO entry subindex. */
+    uint8_t bit_length; /**< Size of the PDO entry in bit. */
+} ec_pdo_entry_info_t;
+
+typedef struct {
+    uint16_t index; /**< PDO index. */
+    uint32_t n_entries;
+    ec_pdo_entry_info_t const *entries;
+} ec_pdo_info_t;
+
+typedef struct {
+    uint8_t index; /**< Sync manager index. */
+    ec_direction_t dir;
+    uint32_t n_pdos;
+    ec_pdo_info_t const *pdos;
+    ec_watchdog_mode_t watchdog_mode;
+} ec_sync_info_t;
+
+#endif

+ 26 - 0
include/ec_errno.h

@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_ERRNO_H
+#define EC_ERRNO_H
+
+#define EC_ERR_OK          0  /**< No error */
+#define EC_ERR_NOMEM       1  /**< Out of memory */
+#define EC_ERR_INVAL       2  /**< Invalid argument */
+#define EC_ERR_TIMEOUT     3  /**< Timeout */
+#define EC_ERR_IO          4  /**< I/O error */
+#define EC_ERR_WC          5  /**< working counter error */
+#define EC_ERR_ALERR       6  /**< AL status error */
+#define EC_ERR_SII         7  /**< SII error */
+#define EC_ERR_MBOX        8  /**< mailbox error */
+#define EC_ERR_COE_TYPE    9  /**< COE type error */
+#define EC_ERR_COE_SIZE    10  /**< COE size error */
+#define EC_ERR_COE_REQUEST 11 /**< COE request & index & subindex error */
+#define EC_ERR_COE_TOGGLE  12 /**< COE toggle error */
+#define EC_ERR_COE_ABORT   13 /**< COE abort error */
+
+#define EC_ERR_UNKNOWN     255
+
+#endif

+ 468 - 0
include/ec_list.h

@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_LIST_H
+#define EC_LIST_H
+
+#include <string.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * ec_container_of - return the member address of ptr, if the type of ptr is the
+ * struct type.
+ */
+#define ec_container_of(ptr, type, member) \
+    ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
+
+/**
+ * Single List structure
+ */
+struct ec_slist_node {
+    struct ec_slist_node *next; /**< point to next node. */
+};
+typedef struct ec_slist_node ec_slist_t; /**< Type for single list. */
+
+/**
+ * @brief initialize a single list
+ *
+ * @param l the single list to be initialized
+ */
+static inline void ec_slist_init(ec_slist_t *l)
+{
+    l->next = NULL;
+}
+
+static inline void ec_slist_add_head(ec_slist_t *l, ec_slist_t *n)
+{
+    n->next = l->next;
+    l->next = n;
+}
+
+static inline void ec_slist_add_tail(ec_slist_t *l, ec_slist_t *n)
+{
+    ec_slist_t *tmp = l;
+
+    while (tmp->next) {
+        tmp = tmp->next;
+    }
+
+    /* append the node to the tail */
+    tmp->next = n;
+    n->next = NULL;
+}
+
+static inline void ec_slist_insert(ec_slist_t *l, ec_slist_t *next, ec_slist_t *n)
+{
+    if (!next) {
+        ec_slist_add_tail(next, l);
+        return;
+    }
+
+    while (l->next) {
+        if (l->next == next) {
+            l->next = n;
+            n->next = next;
+        }
+
+        l = l->next;
+    }
+}
+
+static inline ec_slist_t *ec_slist_remove(ec_slist_t *l, ec_slist_t *n)
+{
+    ec_slist_t *tmp = l;
+    /* remove slist head */
+    while (tmp->next && tmp->next != n) {
+        tmp = tmp->next;
+    }
+
+    /* remove node */
+    if (tmp->next != (ec_slist_t *)0) {
+        tmp->next = tmp->next->next;
+    }
+
+    return l;
+}
+
+static inline unsigned int ec_slist_len(const ec_slist_t *l)
+{
+    unsigned int len = 0;
+    const ec_slist_t *list = l->next;
+
+    while (list != NULL) {
+        list = list->next;
+        len++;
+    }
+
+    return len;
+}
+
+static inline unsigned int ec_slist_contains(ec_slist_t *l, ec_slist_t *n)
+{
+    while (l->next) {
+        if (l->next == n) {
+            return 0;
+        }
+
+        l = l->next;
+    }
+
+    return 1;
+}
+
+static inline ec_slist_t *ec_slist_head(ec_slist_t *l)
+{
+    return l->next;
+}
+
+static inline ec_slist_t *ec_slist_tail(ec_slist_t *l)
+{
+    while (l->next) {
+        l = l->next;
+    }
+
+    return l;
+}
+
+static inline ec_slist_t *ec_slist_next(ec_slist_t *n)
+{
+    return n->next;
+}
+
+static inline int ec_slist_isempty(ec_slist_t *l)
+{
+    return l->next == NULL;
+}
+
+/**
+ * @brief initialize a slist object
+ */
+#define EC_SLIST_OBJESCT_INIT(object) \
+    {                                 \
+        NULL                          \
+    }
+
+/**
+ * @brief initialize a slist object
+ */
+#define EC_SLIST_DEFINE(slist) \
+    ec_slist_t slist = { NULL }
+
+/**
+ * @brief get the struct for this single list node
+ * @param node the entry point
+ * @param type the type of structure
+ * @param member the name of list in structure
+ */
+#define ec_slist_entry(node, type, member) \
+    ec_container_of(node, type, member)
+
+/**
+ * ec_slist_first_entry - get the first element from a slist
+ * @ptr:    the slist head to take the element from.
+ * @type:   the type of the struct this is embedded in.
+ * @member: the name of the slist_struct within the struct.
+ *
+ * Note, that slist is expected to be not empty.
+ */
+#define ec_slist_first_entry(ptr, type, member) \
+    ec_slist_entry((ptr)->next, type, member)
+
+/**
+ * ec_slist_tail_entry - get the tail element from a slist
+ * @ptr:    the slist head to take the element from.
+ * @type:   the type of the struct this is embedded in.
+ * @member: the name of the slist_struct within the struct.
+ *
+ * Note, that slist is expected to be not empty.
+ */
+#define ec_slist_tail_entry(ptr, type, member) \
+    ec_slist_entry(ec_slist_tail(ptr), type, member)
+
+/**
+ * ec_slist_first_entry_or_null - get the first element from a slist
+ * @ptr:    the slist head to take the element from.
+ * @type:   the type of the struct this is embedded in.
+ * @member: the name of the slist_struct within the struct.
+ *
+ * Note, that slist is expected to be not empty.
+ */
+#define ec_slist_first_entry_or_null(ptr, type, member) \
+    (ec_slist_isempty(ptr) ? NULL : ec_slist_first_entry(ptr, type, member))
+
+/**
+ * ec_slist_for_each - iterate over a single list
+ * @pos:    the ec_slist_t * to use as a loop cursor.
+ * @head:   the head for your single list.
+ */
+#define ec_slist_for_each(pos, head) \
+    for (pos = (head)->next; pos != NULL; pos = pos->next)
+
+#define ec_slist_for_each_safe(pos, next, head)    \
+    for (pos = (head)->next, next = pos->next; pos; \
+         pos = next, next = pos->next)
+
+/**
+ * ec_slist_for_each_entry  -   iterate over single list of given type
+ * @pos:    the type * to use as a loop cursor.
+ * @head:   the head for your single list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define ec_slist_for_each_entry(pos, head, member)                 \
+    for (pos = ec_slist_entry((head)->next, typeof(*pos), member); \
+         &pos->member != (NULL);                                    \
+         pos = ec_slist_entry(pos->member.next, typeof(*pos), member))
+
+#define ec_slist_for_each_entry_safe(pos, n, head, member)          \
+    for (pos = ec_slist_entry((head)->next, typeof(*pos), member),  \
+        n = ec_slist_entry(pos->member.next, typeof(*pos), member); \
+         &pos->member != (NULL);                                     \
+         pos = n, n = ec_slist_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * Double List structure
+ */
+struct ec_dlist_node {
+    struct ec_dlist_node *next; /**< point to next node. */
+    struct ec_dlist_node *prev; /**< point to prev node. */
+};
+typedef struct ec_dlist_node ec_dlist_t; /**< Type for lists. */
+
+/**
+ * @brief initialize a list
+ *
+ * @param l list to be initialized
+ */
+static inline void ec_dlist_init(ec_dlist_t *l)
+{
+    l->next = l->prev = l;
+}
+
+/**
+ * @brief insert a node after a list
+ *
+ * @param l list to insert it
+ * @param n new node to be inserted
+ */
+static inline void ec_dlist_add_head(ec_dlist_t *l, ec_dlist_t *n)
+{
+    l->next->prev = n;
+    n->next = l->next;
+
+    l->next = n;
+    n->prev = l;
+}
+
+/**
+ * @brief insert a node before a list
+ *
+ * @param n new node to be inserted
+ * @param l list to insert it
+ */
+static inline void ec_dlist_add_tail(ec_dlist_t *l, ec_dlist_t *n)
+{
+    l->prev->next = n;
+    n->prev = l->prev;
+
+    l->prev = n;
+    n->next = l;
+}
+
+/**
+ * @brief remove node from list.
+ * @param n the node to remove from the list.
+ */
+static inline void ec_dlist_remove(ec_dlist_t *n)
+{
+    n->next->prev = n->prev;
+    n->prev->next = n->next;
+
+    n->next = n->prev = n;
+}
+
+/**
+ * @brief move node from list.
+ * @param n the node to remove from the list.
+ */
+static inline void ec_dlist_move_head(ec_dlist_t *l, ec_dlist_t *n)
+{
+    ec_dlist_remove(n);
+    ec_dlist_add_head(l, n);
+}
+
+/**
+ * @brief move node from list.
+ * @param n the node to remove from the list.
+ */
+static inline void ec_dlist_move_tail(ec_dlist_t *l, ec_dlist_t *n)
+{
+    ec_dlist_remove(n);
+    ec_dlist_add_tail(l, n);
+}
+
+/**
+ * @brief tests whether a list is empty
+ * @param l the list to test.
+ */
+static inline int ec_dlist_isempty(const ec_dlist_t *l)
+{
+    return l->next == l;
+}
+
+/**
+ * @brief get the list length
+ * @param l the list to get.
+ */
+static inline unsigned int ec_dlist_len(const ec_dlist_t *l)
+{
+    unsigned int len = 0;
+    const ec_dlist_t *p = l;
+
+    while (p->next != l) {
+        p = p->next;
+        len++;
+    }
+
+    return len;
+}
+
+/**
+ * @brief remove and init list
+ * @param n the list to get.
+ */
+static inline void ec_dlist_del_init(ec_dlist_t *n)
+{
+    ec_dlist_remove(n);
+}
+
+/**
+ * @brief initialize a dlist object
+ */
+#define EC_DLIST_OBJESCT_INIT(object) \
+    {                                 \
+        &(object), &(object)          \
+    }
+/**
+ * @brief initialize a dlist object
+ */
+#define EC_DLIST_DEFINE(list) \
+    ec_dlist_t list = { &(list), &(list) }
+
+/**
+ * @brief get the struct for this entry
+ * @param node the entry point
+ * @param type the type of structure
+ * @param member the name of list in structure
+ */
+#define ec_dlist_entry(node, type, member) \
+    ec_container_of(node, type, member)
+
+/**
+ * dlist_first_entry - get the first element from a list
+ * @ptr:    the list head to take the element from.
+ * @type:   the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define ec_dlist_first_entry(ptr, type, member) \
+    ec_dlist_entry((ptr)->next, type, member)
+/**
+ * dlist_first_entry_or_null - get the first element from a list
+ * @ptr:    the list head to take the element from.
+ * @type:   the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define ec_dlist_first_entry_or_null(ptr, type, member) \
+    (ec_dlist_isempty(ptr) ? NULL : ec_dlist_first_entry(ptr, type, member))
+
+/**
+ * ec_dlist_for_each - iterate over a list
+ * @pos:    the ec_dlist_t * to use as a loop cursor.
+ * @head:   the head for your list.
+ */
+#define ec_dlist_for_each(pos, head) \
+    for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * ec_dlist_for_each_prev - iterate over a list
+ * @pos:    the dlist_t * to use as a loop cursor.
+ * @head:   the head for your list.
+ */
+#define ec_dlist_for_each_prev(pos, head) \
+    for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * ec_dlist_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos:    the dlist_t * to use as a loop cursor.
+ * @n:      another dlist_t * to use as temporary storage
+ * @head:   the head for your list.
+ */
+#define ec_dlist_for_each_safe(pos, n, head)              \
+    for (pos = (head)->next, n = pos->next; pos != (head); \
+         pos = n, n = pos->next)
+
+#define ec_dlist_for_each_prev_safe(pos, n, head)         \
+    for (pos = (head)->prev, n = pos->prev; pos != (head); \
+         pos = n, n = pos->prev)
+/**
+ * ec_dlist_for_each_entry  -   iterate over list of given type
+ * @pos:    the type * to use as a loop cursor.
+ * @head:   the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define ec_dlist_for_each_entry(pos, head, member)                 \
+    for (pos = ec_dlist_entry((head)->next, typeof(*pos), member); \
+         &pos->member != (head);                                    \
+         pos = ec_dlist_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * ec_dlist_for_each_entry_reverse  -   iterate over list of given type
+ * @pos:    the type * to use as a loop cursor.
+ * @head:   the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define ec_dlist_for_each_entry_reverse(pos, head, member)         \
+    for (pos = ec_dlist_entry((head)->prev, typeof(*pos), member); \
+         &pos->member != (head);                                    \
+         pos = ec_dlist_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * ec_dlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:    the type * to use as a loop cursor.
+ * @n:      another type * to use as temporary storage
+ * @head:   the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define ec_dlist_for_each_entry_safe(pos, n, head, member)          \
+    for (pos = ec_dlist_entry((head)->next, typeof(*pos), member),  \
+        n = ec_dlist_entry(pos->member.next, typeof(*pos), member); \
+         &pos->member != (head);                                     \
+         pos = n, n = ec_dlist_entry(n->member.next, typeof(*n), member))
+
+/**
+ * ec_dlist_for_each_entry_safe_reverse - iterate over list of given type safe against removal of list entry
+ * @pos:    the type * to use as a loop cursor.
+ * @n:      another type * to use as temporary storage
+ * @head:   the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define ec_dlist_for_each_entry_safe_reverse(pos, n, head, member)  \
+    for (pos = ec_dlist_entry((head)->prev, typeof(*pos), field),   \
+        n = ec_dlist_entry(pos->member.prev, typeof(*pos), member); \
+         &pos->member != (head);                                     \
+         pos = n, n = ec_dlist_entry(pos->member.prev, typeof(*pos), member))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EC_LIST_H */

+ 171 - 0
include/ec_log.h

@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_LOG_H
+#define EC_LOG_H
+
+#include <stdio.h>
+
+/* DEBUG level */
+#define EC_DBG_ERROR   0
+#define EC_DBG_WARNING 1
+#define EC_DBG_INFO    2
+#define EC_DBG_LOG     3
+
+#ifndef EC_DBG_TAG
+#define EC_DBG_TAG "EC"
+#endif
+/*
+ * The color for terminal (foreground)
+ * BLACK    30
+ * RED      31
+ * GREEN    32
+ * YELLOW   33
+ * BLUE     34
+ * PURPLE   35
+ * CYAN     36
+ * WHITE    37
+ */
+
+#define ec_master_dbg_log_line(lvl, color_n, fmt, ...)          \
+    do {                                                        \
+        CONFIG_EC_PRINTF("\033[" #color_n "m[" lvl "/ec_master" \
+                         "] ");                                 \
+        CONFIG_EC_PRINTF(fmt, ##__VA_ARGS__);                   \
+        CONFIG_EC_PRINTF("\033[0m");                            \
+    } while (0)
+
+#define ec_slave_dbg_log_line(lvl, color_n, fmt, ...)          \
+    do {                                                       \
+        CONFIG_EC_PRINTF("\033[" #color_n "m[" lvl "/ec_slave" \
+                         "] ");                                \
+        CONFIG_EC_PRINTF(fmt, ##__VA_ARGS__);                  \
+        CONFIG_EC_PRINTF("\033[0m");                           \
+    } while (0)
+
+#if (CONFIG_EC_DBG_LEVEL >= EC_DBG_LOG)
+#define EC_LOG_DBG(fmt, ...) ec_master_dbg_log_line("D", 0, fmt, ##__VA_ARGS__)
+#else
+#define EC_LOG_DBG(...) \
+    {                   \
+    }
+#endif
+
+#if (CONFIG_EC_DBG_LEVEL >= EC_DBG_INFO)
+#define EC_LOG_INFO(fmt, ...) ec_master_dbg_log_line("I", 32, fmt, ##__VA_ARGS__)
+#else
+#define EC_LOG_INFO(...) \
+    {                    \
+    }
+#endif
+
+#if (CONFIG_EC_DBG_LEVEL >= EC_DBG_WARNING)
+#define EC_LOG_WRN(fmt, ...) ec_master_dbg_log_line("W", 33, fmt, ##__VA_ARGS__)
+#else
+#define EC_LOG_WRN(...) \
+    {                   \
+    }
+#endif
+
+#if (CONFIG_EC_DBG_LEVEL >= EC_DBG_ERROR)
+#define EC_LOG_ERR(fmt, ...) ec_master_dbg_log_line("E", 31, fmt, ##__VA_ARGS__)
+#else
+#define EC_LOG_ERR(...) \
+    {                   \
+    }
+#endif
+
+#if (CONFIG_EC_SLAVE_DBG_LEVEL >= EC_DBG_LOG)
+#define EC_SLAVE_LOG_DBG(fmt, ...) ec_slave_dbg_log_line("D", 0, fmt, ##__VA_ARGS__)
+#else
+#define EC_SLAVE_LOG_DBG(...) \
+    {                         \
+    }
+#endif
+
+#if (CONFIG_EC_SLAVE_DBG_LEVEL >= EC_DBG_INFO)
+#define EC_SLAVE_LOG_INFO(fmt, ...) ec_slave_dbg_log_line("I", 32, fmt, ##__VA_ARGS__)
+#else
+#define EC_SLAVE_LOG_INFO(...) \
+    {                          \
+    }
+#endif
+
+#if (CONFIG_EC_SLAVE_DBG_LEVEL >= EC_DBG_WARNING)
+#define EC_SLAVE_LOG_WRN(fmt, ...) ec_slave_dbg_log_line("W", 33, fmt, ##__VA_ARGS__)
+#else
+#define EC_SLAVE_LOG_WRN(...) \
+    {                         \
+    }
+#endif
+
+#if (CONFIG_EC_SLAVE_DBG_LEVEL >= EC_DBG_ERROR)
+#define EC_SLAVE_LOG_ERR(fmt, ...) ec_slave_dbg_log_line("E", 31, fmt, ##__VA_ARGS__)
+#else
+#define EC_SLAVE_LOG_ERR(...) \
+    {                         \
+    }
+#endif
+
+#define EC_LOG_RAW(...) CONFIG_EC_PRINTF(__VA_ARGS__)
+
+#ifndef CONFIG_EC_ASSERT_DISABLE
+#define EC_ASSERT(f)                                                            \
+    do {                                                                        \
+        if (!(f)) {                                                             \
+            EC_LOG_ERR("ASSERT FAIL [%s] @ %s:%d\r\n", #f, __FILE__, __LINE__); \
+            while (1) {                                                         \
+            }                                                                   \
+        }                                                                       \
+    } while (false)
+
+#define EC_ASSERT_MSG(f, fmt, ...)                                              \
+    do {                                                                        \
+        if (!(f)) {                                                             \
+            EC_LOG_ERR("ASSERT FAIL [%s] @ %s:%d\r\n", #f, __FILE__, __LINE__); \
+            EC_LOG_ERR(fmt "\r\n", ##__VA_ARGS__);                              \
+            while (1) {                                                         \
+            }                                                                   \
+        }                                                                       \
+    } while (false)
+#else
+#define EC_ASSERT(f) \
+    {                \
+    }
+#define EC_ASSERT_MSG(f, fmt, ...) \
+    {                              \
+    }
+#endif
+
+#define ___is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
+static inline void ec_hexdump(const void *ptr, uint32_t buflen)
+{
+    unsigned char *buf = (unsigned char *)ptr;
+    unsigned int i, j;
+
+    (void)buf;
+
+    for (i = 0; i < buflen; i += 16) {
+        CONFIG_EC_PRINTF("%08x:", i);
+
+        for (j = 0; j < 16; j++)
+            if (i + j < buflen) {
+                if ((j % 8) == 0) {
+                    CONFIG_EC_PRINTF("  ");
+                }
+
+                CONFIG_EC_PRINTF("%02X ", buf[i + j]);
+            } else
+                CONFIG_EC_PRINTF("   ");
+        CONFIG_EC_PRINTF(" ");
+
+        for (j = 0; j < 16; j++)
+            if (i + j < buflen)
+                CONFIG_EC_PRINTF("%c", ___is_print(buf[i + j]) ? buf[i + j] : '.');
+        CONFIG_EC_PRINTF("\n");
+    }
+}
+
+#endif /* EC_LOG_H */

+ 13 - 0
include/ec_mailbox.h

@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_MAILBOX_H
+#define EC_MAILBOX_H
+
+uint8_t *ec_mailbox_fill_send(ec_slave_t *slave, ec_datagram_t *datagram, uint8_t type, uint16_t size);
+int ec_mailbox_send(ec_slave_t *slave, ec_datagram_t *datagram);
+int ec_mailbox_receive(ec_slave_t *slave, ec_datagram_t *datagram, uint8_t *type, uint32_t *size);
+
+#endif

+ 130 - 0
include/ec_master.h

@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_MASTER_H
+#define EC_MASTER_H
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "ec_config.h"
+#include "ec_util.h"
+#include "ec_list.h"
+#include "ec_errno.h"
+#include "ec_log.h"
+#include "esc_register.h"
+#include "ec_def.h"
+#include "ec_osal.h"
+#include "ec_port.h"
+#include "ec_datagram.h"
+#include "ec_common.h"
+#include "ec_sii.h"
+#include "ec_slave.h"
+#include "ec_mailbox.h"
+#include "ec_coe.h"
+#include "ec_cmd.h"
+#include "ec_perf.h"
+#include "ec_version.h"
+
+#define jiffies ec_htimer_get_time_us()
+
+/** Netdev statistics.
+ */
+typedef struct {
+    uint64_t tx_count;                     /**< Number of frames sent. */
+    uint64_t last_tx_count;                /**< Number of frames sent of last statistics cycle. */
+    uint64_t rx_count;                     /**< Number of frames received. */
+    uint64_t last_rx_count;                /**< Number of frames received of last statistics cycle. */
+    uint64_t tx_bytes;                     /**< Number of bytes sent. */
+    uint64_t last_tx_bytes;                /**< Number of bytes sent of last statistics cycle. */
+    uint64_t rx_bytes;                     /**< Number of bytes received. */
+    uint64_t last_rx_bytes;                /**< Number of bytes received of last statistics cycle. */
+    uint64_t last_loss;                    /**< Tx/Rx difference of last statistics cycle. */
+    int32_t tx_frame_rates[EC_RATE_COUNT]; /**< Transmit rates in frames/s for different statistics cycle periods.*/
+    int32_t rx_frame_rates[EC_RATE_COUNT]; /**< Receive rates in frames/s for different statistics cycle periods.*/
+    int32_t tx_byte_rates[EC_RATE_COUNT];  /**< Transmit rates in byte/s for different statistics cycle periods. */
+    int32_t rx_byte_rates[EC_RATE_COUNT];  /**< Receive rates in byte/s for different statistics cycle periods. */
+    int32_t loss_rates[EC_RATE_COUNT];     /**< Frame loss rates for different statistics cycle periods. */
+    uint64_t last_jiffies;                 /**< Jiffies of last statistic cycle. */
+} ec_netdev_stats_t;
+
+/** Cyclic statistics.
+ */
+typedef struct {
+    unsigned int timeouts;        /**< datagram timeouts */
+    unsigned int corrupted;       /**< corrupted frames */
+    unsigned int unmatched;       /**< unmatched datagrams (received, but not queued any longer) */
+    unsigned long output_jiffies; /**< time of last output */
+} ec_stats_t;
+
+typedef enum {
+    EC_ORPHANED, /**< Orphaned phase. The master has no Ethernet device attached. */
+    EC_IDLE,     /**< Idle phase. An Ethernet device is attached, but the master is not in use, yet. */
+    EC_OPERATION /**< Operation phase. The master was requested by a realtime application. */
+} ec_master_phase_t;
+
+typedef struct {
+    ec_dlist_t queue;
+    ec_datagram_t datagrams[CONFIG_EC_MAX_NETDEVS];
+#if CONFIG_EC_MAX_NETDEVS > 1
+    uint8_t *send_buffer;
+#endif
+    uint32_t expected_working_counter;
+} ec_cyclic_datagram_t;
+
+typedef struct ec_master {
+    uint8_t index;
+    ec_netdev_t *netdev[CONFIG_EC_MAX_NETDEVS];
+    bool link_state[CONFIG_EC_MAX_NETDEVS];
+    uint32_t slaves_responding[CONFIG_EC_MAX_NETDEVS];
+    ec_slave_state_t slaves_state[CONFIG_EC_MAX_NETDEVS];
+    ec_netdev_stats_t netdev_stats;
+    ec_stats_t stats;
+    ec_master_phase_t phase;
+    bool active;        /**< Master is started. */
+    bool config_change; /**< Configuration changed and needs to be applied. */
+    bool scan_done;     /**< Slave scan is done. */
+
+    ec_datagram_t main_datagram; /**< Main datagram for slave scan & state change & config & sii */
+
+    ec_dlist_t datagram_queue;        /**< Queue of pending datagrams(internal use)*/
+    ec_dlist_t ext_datagram_queue;    /**< Queue of pending datagrams(external use)*/
+    ec_dlist_t cyclic_datagram_queue; /**< Queue of cyclic datagrams(internal use)*/
+    uint8_t datagram_index;
+
+    ec_slave_t *dc_ref_clock;           /**< DC reference clock slave. */
+    ec_datagram_t dc_ref_sync_datagram; /**< Datagram used for synchronizing the reference clock to the master clock. */
+    ec_datagram_t dc_all_sync_datagram; /**< Datagram used for synchronizing all slaves to the master clock. */
+
+    ec_slave_t *slaves;
+    uint32_t slave_count;
+
+#ifdef CONFIG_EC_PERF_ENABLE
+    ec_perf_t perf;
+#endif
+
+    ec_osal_mutex_t scan_lock;
+    ec_osal_thread_t idle_thread;
+    ec_osal_thread_t nonperiod_thread;
+    ec_osal_sem_t nonperiod_sem;
+    struct ec_osal_timer *linkdetect_timer;
+
+    uint8_t pdo_buffer[CONFIG_EC_MAX_NETDEVS][CONFIG_EC_MAX_PDO_BUFSIZE];
+    uint32_t actual_pdo_size;
+    uint32_t expected_working_counter;
+    uint32_t actual_working_counter;
+} ec_master_t;
+
+int ec_master_init(ec_master_t *master, uint8_t master_index);
+void ec_master_deinit(ec_master_t *master);
+int ec_master_start(ec_master_t *master, uint32_t period_us);
+int ec_master_stop(ec_master_t *master);
+int ec_master_queue_ext_datagram(ec_master_t *master, ec_datagram_t *datagram, bool wakep_poll, bool waiter);
+uint8_t *ec_master_get_slave_domain(ec_master_t *master, uint32_t slave_index);
+uint32_t ec_master_get_slave_domain_size(ec_master_t *master, uint32_t slave_index);
+
+#endif

+ 49 - 0
include/ec_netdev.h

@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_NETDEV_H
+#define EC_NETDEV_H
+
+typedef struct ec_master ec_master_t;
+
+typedef struct ec_netdev {
+    ec_master_t *master;
+    uint8_t index;
+    char name[20];
+    uint8_t mac_addr[6];
+    bool link_state;
+    uint8_t tx_frame_index;
+    unsigned long jiffies_poll;
+
+    // Frame statistics
+    uint64_t tx_count;                     /**< Number of frames sent. */
+    uint64_t last_tx_count;                /**< Number of frames sent of last statistics cycle.*/
+    uint64_t rx_count;                     /**< Number of frames received. */
+    uint64_t last_rx_count;                /**< Number of frames received of last statistics cycle.*/
+    uint64_t tx_bytes;                     /**< Number of bytes sent. */
+    uint64_t last_tx_bytes;                /**< Number of bytes sent of last statistics cycle.*/
+    uint64_t rx_bytes;                     /**< Number of bytes received. */
+    uint64_t last_rx_bytes;                /**< Number of bytes received of last statistics cycle.*/
+    uint64_t tx_errors;                    /**< Number of transmit errors. */
+    int32_t tx_frame_rates[EC_RATE_COUNT]; /**< Transmit rates in frames/s for different statistics cycle periods.*/
+    int32_t rx_frame_rates[EC_RATE_COUNT]; /**< Receive rates in frames/s for different statistics cycle periods.*/
+    int32_t tx_byte_rates[EC_RATE_COUNT];  /**< Transmit rates in byte/s for different statistics cycle periods.*/
+    int32_t rx_byte_rates[EC_RATE_COUNT];  /**< Receive rates in byte/s for different statistics cycle periods.*/
+
+} ec_netdev_t;
+
+void ec_netdev_clear_stats(ec_netdev_t *netdev);
+void ec_netdev_update_stats(ec_netdev_t *netdev);
+
+ec_netdev_t *ec_netdev_init(uint8_t netdev_index);
+void ec_netdev_enable_irq(ec_netdev_t *netdev, bool enable);
+bool ec_netdev_get_link_state(ec_netdev_t *netdev);
+uint8_t *ec_netdev_get_txbuf(ec_netdev_t *netdev);
+int ec_netdev_send(ec_netdev_t *netdev, uint32_t size);
+void ec_netdev_receive(ec_netdev_t *netdev, uint8_t *frame, uint32_t size);
+void ec_netdev_poll(ec_netdev_t *netdev);
+void ec_netdev_trigger_poll(ec_netdev_t *netdev);
+
+#endif

+ 71 - 0
include/ec_osal.h

@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_OSAL_H
+#define EC_OSAL_H
+
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#ifdef __INCLUDE_NUTTX_CONFIG_H
+#define CONFIG_EC_OSAL_THREAD_SET_ARGV int argc, char **argv
+#define CONFIG_EC_OSAL_THREAD_GET_ARGV ((uintptr_t)strtoul(argv[1], NULL, 16))
+#elif defined(__ZEPHYR__)
+#define CONFIG_EC_OSAL_THREAD_SET_ARGV void *p1, void *p2, void *p3
+#define CONFIG_EC_OSAL_THREAD_GET_ARGV ((uintptr_t)p1)
+#else
+#define CONFIG_EC_OSAL_THREAD_SET_ARGV void *argument
+#define CONFIG_EC_OSAL_THREAD_GET_ARGV ((uintptr_t)argument)
+#endif
+
+#define EC_OSAL_WAITING_FOREVER (0xFFFFFFFFU)
+
+typedef void *ec_osal_thread_t;
+typedef void *ec_osal_sem_t;
+typedef void *ec_osal_mutex_t;
+typedef void (*ec_thread_entry_t)(CONFIG_EC_OSAL_THREAD_SET_ARGV);
+typedef void (*ec_timer_handler_t)(void *argument);
+struct ec_osal_timer {
+    ec_timer_handler_t handler;
+    void *argument;
+    bool is_period;
+    uint32_t timeout_ms;
+    void *timer;
+};
+
+/*
+ * Task with smaller priority value indicates higher task priority
+*/
+ec_osal_thread_t ec_osal_thread_create(const char *name, uint32_t stack_size, uint32_t prio, ec_thread_entry_t entry, void *args);
+void ec_osal_thread_delete(ec_osal_thread_t thread);
+void ec_osal_thread_suspend(ec_osal_thread_t thread);
+void ec_osal_thread_resume(ec_osal_thread_t thread);
+
+ec_osal_sem_t ec_osal_sem_create(uint32_t max_count, uint32_t initial_count);
+void ec_osal_sem_delete(ec_osal_sem_t sem);
+int ec_osal_sem_take(ec_osal_sem_t sem, uint32_t timeout);
+int ec_osal_sem_give(ec_osal_sem_t sem);
+void ec_osal_sem_reset(ec_osal_sem_t sem);
+
+ec_osal_mutex_t ec_osal_mutex_create(void);
+void ec_osal_mutex_delete(ec_osal_mutex_t mutex);
+int ec_osal_mutex_take(ec_osal_mutex_t mutex);
+int ec_osal_mutex_give(ec_osal_mutex_t mutex);
+
+struct ec_osal_timer *ec_osal_timer_create(const char *name, uint32_t timeout_ms, ec_timer_handler_t handler, void *argument, bool is_period);
+void ec_osal_timer_delete(struct ec_osal_timer *timer);
+void ec_osal_timer_start(struct ec_osal_timer *timer);
+void ec_osal_timer_stop(struct ec_osal_timer *timer);
+
+size_t ec_osal_enter_critical_section(void);
+void ec_osal_leave_critical_section(size_t flag);
+
+void ec_osal_msleep(uint32_t delay);
+
+void *ec_osal_malloc(size_t size);
+void ec_osal_free(void *ptr);
+
+#endif /* EC_OSAL_H */

+ 30 - 0
include/ec_perf.h

@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_PERF_H
+#define EC_PERF_H
+
+typedef struct {
+    bool enable;    // Enable performance measurement
+    uint64_t count; // Current measurement count
+
+    uint64_t min_interval;    // Minimum interval
+    uint64_t max_interval;    // Maximum interval
+    int64_t min_jitter;       // Minimum jitter
+    int64_t max_jitter;       // Maximum jitter
+    uint64_t total_interval;  // Total interval time
+    int64_t total_jitter;     // Total jitter (for average calculation)
+
+    uint32_t ignore_count;      // Number of ignored measurements
+    uint64_t last_timestamp;    // Last interrupt timestamp
+    uint64_t expected_interval; // Expected interrupt interval
+} ec_perf_t;
+
+void ec_perf_init(ec_perf_t *perf, uint64_t expected_interval_us);
+void ec_perf_polling(ec_perf_t *perf);
+bool ec_perf_is_complete(ec_perf_t *perf);
+void ec_perf_print_statistics(ec_perf_t *perf);
+
+#endif

+ 25 - 0
include/ec_port.h

@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_PORT_H
+#define EC_PORT_H
+
+#include "ec_netdev.h"
+
+typedef void (*ec_htimer_cb)(void *arg);
+
+ec_netdev_t *ec_netdev_low_level_init(uint8_t netdev_index);
+void ec_netdev_low_level_enable_irq(ec_netdev_t *netdev, bool enable);
+bool ec_netdev_low_level_get_link_state(ec_netdev_t *netdev);
+uint8_t *ec_netdev_low_level_get_txbuf(ec_netdev_t *netdev);
+int ec_netdev_low_level_output(ec_netdev_t *netdev, uint32_t size);
+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);
+
+#endif

+ 40 - 0
include/ec_sii.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_SII_H
+#define EC_SII_H
+
+typedef struct ec_slave ec_slave_t;
+
+typedef struct ec_sii {
+    // Non-category data
+    uint16_t aliasaddr;              /**< Configured station alias. */
+    uint32_t vendor_id;              /**< Vendor ID. */
+    uint32_t product_code;           /**< Vendor-specific product code. */
+    uint32_t revision_number;        /**< Revision number. */
+    uint32_t serial_number;          /**< Serial number. */
+    uint16_t boot_rx_mailbox_offset; /**< Bootstrap receive mailbox address. */
+    uint16_t boot_rx_mailbox_size;   /**< Bootstrap receive mailbox size. */
+    uint16_t boot_tx_mailbox_offset; /**< Bootstrap transmit mailbox address. */
+    uint16_t boot_tx_mailbox_size;   /**< Bootstrap transmit mailbox size. */
+    uint16_t std_rx_mailbox_offset;  /**< Standard receive mailbox address. */
+    uint16_t std_rx_mailbox_size;    /**< Standard receive mailbox size. */
+    uint16_t std_tx_mailbox_offset;  /**< Standard transmit mailbox address. */
+    uint16_t std_tx_mailbox_size;    /**< Standard transmit mailbox size. */
+    uint16_t mailbox_protocols;      /**< Supported mailbox protocols. */
+
+    // General
+    ec_sii_general_t general;
+    bool has_general;
+
+    // Strings
+    char **strings;        /**< Strings in SII categories. */
+    uint32_t string_count; /**< Number of SII strings. */
+} ec_sii_t;
+
+int ec_sii_read(ec_slave_t *slave, ec_datagram_t *datagram, uint16_t woffset, uint32_t *buf, uint32_t len);
+int ec_sii_write(ec_slave_t *slave, ec_datagram_t *datagram, uint16_t woffset, const uint16_t *buf, uint32_t len);
+
+#endif

+ 111 - 0
include/ec_slave.h

@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_SALVE_H
+#define EC_SALVE_H
+
+typedef struct ec_master ec_master_t;
+typedef struct ec_slave ec_slave_t;
+
+typedef struct
+{
+    ec_direction_t dir;
+    uint32_t logical_start_address;
+    uint32_t data_size;
+} ec_fmmu_info_t;
+
+typedef struct {
+    uint16_t physical_start_address;
+    uint16_t length;
+    uint8_t control;
+    uint8_t enable;
+    ec_pdo_assign_t pdo_assign;
+    ec_pdo_mapping_t pdo_mapping[CONFIG_EC_PER_SM_MAX_PDOS];
+    ec_fmmu_info_t fmmu;
+    bool fmmu_enable;
+} ec_sm_info_t;
+
+typedef struct {
+    ec_sync_info_t *sync;                           /**< Sync manager configuration. */
+    uint8_t sync_count;                             /**< Number of sync managers. */
+    uint16_t dc_assign_activate;                    /**< dc assign control */
+    ec_sync_signal_t dc_sync[EC_SYNC_SIGNAL_COUNT]; /**< DC sync signals. */
+} ec_slave_config_t;
+
+/** EtherCAT slave port descriptor.
+ */
+typedef enum {
+    EC_PORT_NOT_IMPLEMENTED, /**< Port is not implemented. */
+    EC_PORT_NOT_CONFIGURED,  /**< Port is not configured. */
+    EC_PORT_EBUS,            /**< Port is an E-Bus. */
+    EC_PORT_MII              /**< Port is a MII. */
+} ec_slave_port_desc_t;
+
+/** EtherCAT slave port information.
+ */
+typedef struct {
+    uint8_t link_up;         /**< Link detected. */
+    uint8_t loop_closed;     /**< Loop closed. */
+    uint8_t signal_detected; /**< Detected signal on RX port. */
+} ec_slave_port_link_t;
+
+typedef struct {
+    ec_slave_port_desc_t desc; /**< Port descriptors. */
+    ec_slave_port_link_t link; /**< Port link status. */
+    ec_slave_t *next_slave;    /**< Connected slaves. */
+    uint32_t receive_time;     /**< Port receive times for delay measurement. */
+    uint32_t delay_to_next_dc; /**< Delay to next slave with DC support behind this port [ns]. */
+} ec_slave_port_t;
+
+typedef struct ec_slave {
+    uint32_t index;               /**< Index of the slave in the master slave array. */
+    ec_master_t *master;          /**< Master owning the slave. */
+    ec_netdev_index_t netdev_idx; /**< Index of device the slave responds on. */
+
+    uint16_t autoinc_address; /**< Auto-increment address. */
+    uint16_t station_address; /**< Configured station address. */
+    uint16_t effective_alias; /**< Effective alias address. */
+
+    ec_slave_port_t ports[EC_MAX_PORTS]; /**< Port information. */
+
+    ec_slave_state_t requested_state; /**< Requested application state. */
+    ec_slave_state_t current_state;   /**< Current application state. */
+    uint32_t alstatus_code;           /**< Error code in AL Status register. */
+    bool force_update;                /**< Force update of the slave. */
+
+    uint16_t configured_rx_mailbox_offset; /**< Configured receive mailbox offset. */
+    uint16_t configured_rx_mailbox_size;   /**< Configured receive mailbox size.*/
+    uint16_t configured_tx_mailbox_offset; /**< Configured send mailbox offset. */
+    uint16_t configured_tx_mailbox_size;   /**< Configured send mailbox size. */
+
+    uint8_t base_type;                 /**< Slave type. */
+    uint8_t base_revision;             /**< Revision. */
+    uint16_t base_build;               /**< Build number. */
+    uint8_t base_fmmu_count;           /**< Number of supported FMMUs. */
+    uint8_t base_sync_count;           /**< Number of supported sync managers. */
+    uint8_t base_fmmu_bit_operation;   /**< FMMU bit operation is supported. */
+    uint8_t base_dc_supported;         /**< Distributed clocks are supported. */
+    ec_slave_dc_range_t base_dc_range; /**< DC range. */
+    uint8_t has_dc_system_time;        /**< The slave supports the DC system time register. Otherwise it can only be used for delay measurement. */
+    uint32_t transmission_delay;       /**< DC system time transmission delay (offset from reference clock). */
+
+    uint32_t logical_start_address;
+    uint32_t data_size;
+    uint32_t expected_working_counter;
+
+    uint16_t *sii_image; /**< Complete SII image. */
+    size_t sii_nwords;   /**< Size of the SII contents in words. */
+
+    ec_sii_t sii; /**< Extracted SII data. */
+
+    ec_sm_info_t sm_info[EC_MAX_SYNC_MANAGERS];
+    uint8_t sm_count; /**< Number of sync managers. */
+
+    ec_slave_config_t *config; /**< Slave custom configuration. */
+} ec_slave_t;
+
+void ec_slaves_scanning(ec_master_t *master);
+
+#endif

+ 160 - 0
include/ec_util.h

@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_UTIL_H
+#define EC_UTIL_H
+
+#if defined(__CC_ARM)
+#ifndef __USED
+#define __USED __attribute__((used))
+#endif
+#ifndef __WEAK
+#define __WEAK __attribute__((weak))
+#endif
+#ifndef __PACKED
+#define __PACKED __attribute__((packed))
+#endif
+#ifndef __PACKED_STRUCT
+#define __PACKED_STRUCT __packed struct
+#endif
+#ifndef __PACKED_UNION
+#define __PACKED_UNION __packed union
+#endif
+#ifndef __ALIGNED
+#define __ALIGNED(x) __attribute__((aligned(x)))
+#endif
+#elif defined(__GNUC__)
+#ifndef __USED
+#define __USED __attribute__((used))
+#endif
+#ifndef __WEAK
+#define __WEAK __attribute__((weak))
+#endif
+#ifndef __PACKED
+#define __PACKED __attribute__((packed, aligned(1)))
+#endif
+#ifndef __PACKED_STRUCT
+#define __PACKED_STRUCT struct __attribute__((packed, aligned(1)))
+#endif
+#ifndef __PACKED_UNION
+#define __PACKED_UNION union __attribute__((packed, aligned(1)))
+#endif
+#ifndef __ALIGNED
+#define __ALIGNED(x) __attribute__((aligned(x)))
+#endif
+#elif defined(__ICCARM__) || defined(__ICCRX__) || defined(__ICCRISCV__)
+#if (__VER__ >= 8000000)
+#define __ICCARM_V8 1
+#else
+#define __ICCARM_V8 0
+#endif
+
+#ifndef __USED
+#if defined(__ICCARM_V8) || defined(__ICCRISCV__)
+#define __USED __attribute__((used))
+#else
+#define __USED __root
+#endif
+#endif
+
+#ifndef __WEAK
+#if defined(__ICCARM_V8) || defined(__ICCRISCV__)
+#define __WEAK __attribute__((weak))
+#else
+#define __WEAK _Pragma("__weak")
+#endif
+#endif
+
+#ifndef __PACKED
+#if defined(__ICCARM_V8) || defined(__ICCRISCV__)
+#define __PACKED __attribute__((packed, aligned(1)))
+#else
+/* Needs IAR language extensions */
+#define __PACKED __packed
+#endif
+#endif
+
+#ifndef __PACKED_STRUCT
+#if defined(__ICCARM_V8) || defined(__ICCRISCV__)
+#define __PACKED_STRUCT struct __attribute__((packed, aligned(1)))
+#else
+/* Needs IAR language extensions */
+#define __PACKED_STRUCT __packed struct
+#endif
+#endif
+
+#ifndef __PACKED_UNION
+#if defined(__ICCARM_V8) || defined(__ICCRISCV__)
+#define __PACKED_UNION union __attribute__((packed, aligned(1)))
+#else
+/* Needs IAR language extensions */
+#define __PACKED_UNION __packed union
+#endif
+#endif
+
+#ifndef __ALIGNED
+#if defined(__ICCARM_V8) || defined(__ICCRISCV__)
+#define __ALIGNED(x) __attribute__((aligned(x)))
+#elif (__VER__ >= 7080000)
+/* Needs IAR language extensions */
+#define __ALIGNED(x) __attribute__((aligned(x)))
+#else
+#warning No compiler specific solution for __ALIGNED.__ALIGNED is ignored.
+#define __ALIGNED(x)
+#endif
+#endif
+
+#endif
+
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define EC_ALIGN_UP(size, align) (((size) + (align)-1) & ~((align)-1))
+
+#define EC_WRITE_U8(DATA, VAL)                   \
+    do {                                         \
+        *((uint8_t *)(DATA)) = ((uint8_t)(VAL)); \
+    } while (0)
+
+#define EC_WRITE_U16(DATA, VAL)                    \
+    do {                                           \
+        *((uint16_t *)(DATA)) = ((uint16_t)(VAL)); \
+    } while (0)
+
+#define EC_WRITE_U32(DATA, VAL)                    \
+    do {                                           \
+        *((uint32_t *)(DATA)) = ((uint32_t)(VAL)); \
+    } while (0)
+
+#define EC_WRITE_U64(DATA, VAL)                    \
+    do {                                           \
+        *((uint64_t *)(DATA)) = ((uint64_t)(VAL)); \
+    } while (0)
+
+#define EC_READ_U8(DATA) \
+    ((uint8_t) * ((uint8_t *)(DATA)))
+
+#define EC_READ_U16(DATA) \
+    ((uint16_t) * ((uint16_t *)(DATA)))
+
+#define EC_READ_U32(DATA) \
+    ((uint32_t) * ((uint32_t *)(DATA)))
+
+#define EC_READ_U64(DATA) \
+    ((uint64_t) * ((uint64_t *)(DATA)))
+
+#define ec_htons(A) ((((uint16_t)(A)&0xff00) >> 8) | \
+                     (((uint16_t)(A)&0x00ff) << 8))
+#define ec_htonl(A) ((((uint32_t)(A)&0xff000000) >> 24) | \
+                     (((uint32_t)(A)&0x00ff0000) >> 8) |  \
+                     (((uint32_t)(A)&0x0000ff00) << 8) |  \
+                     (((uint32_t)(A)&0x000000ff) << 24))
+
+#endif

+ 12 - 0
include/ec_version.h

@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef EC_VERSION_H
+#define EC_VERSION_H
+
+#define CHERRYECAT_VERSION     0x000100
+#define CHERRYECAT_VERSION_STR "v0.1.0"
+
+#endif

+ 4552 - 0
include/esc_register.h

@@ -0,0 +1,4552 @@
+/*
+ * Copyright (c) 2021-2025 HPMicro
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+
+#ifndef ESC_REGISTER_H
+#define ESC_REGISTER_H
+
+#define __R volatile const /* Define "read-only" permission */
+#define __RW volatile      /* Define "read-write" permission */
+#define __W volatile       /* Define "write-only" permission */
+
+typedef struct {
+    __R  uint8_t  TYPE;                        /* 0x0: Type of EtherCAT controller */
+    __R  uint8_t  REVISION;                    /* 0x1: Revision of EtherCAT controller */
+    __R  uint16_t BUILD;                       /* 0x2: Build of EtherCAT controller */
+    __R  uint8_t  FMMU_NUM;                    /* 0x4: FMMU supported */
+    __R  uint8_t  SYNCM_NUM;                   /* 0x5: SyncManagers supported */
+    __R  uint8_t  RAM_SIZE;                    /* 0x6: RAM Size */
+    __R  uint8_t  PORT_DESC;                   /* 0x7: Port Descriptor */
+    __R  uint16_t FEATURE;                     /* 0x8: ESC Feature supported */
+    __R  uint8_t  RESERVED0[6];                /* 0xA - 0xF: Reserved */
+    __R  uint16_t STATION_ADDR;                /* 0x10: Configured Station Address */
+    __RW uint16_t STATION_ALS;                 /* 0x12: Configured Station Alias */
+    __R  uint8_t  RESERVED1[12];               /* 0x14 - 0x1F: Reserved */
+    __R  uint8_t  REG_WEN;                     /* 0x20: Register Write Enable */
+    __R  uint8_t  REG_WP;                      /* 0x21: Register Write Protection */
+    __R  uint8_t  RESERVED2[14];               /* 0x22 - 0x2F: Reserved */
+    __R  uint8_t  ESC_WEN;                     /* 0x30: ESC Write Enable */
+    __R  uint8_t  ESC_WP;                      /* 0x31: ESC Write Protection */
+    __R  uint8_t  RESERVED3[14];               /* 0x32 - 0x3F: Reserved */
+    __R  uint8_t  ESC_RST_ECAT;                /* 0x40: ESC Reset ECAT */
+    __RW uint8_t  ESC_RST_PDI;                 /* 0x41: ESC Reset PDI */
+    __R  uint8_t  RESERVED4[190];              /* 0x42 - 0xFF: Reserved */
+    __R  uint32_t ESC_DL_CTRL;                 /* 0x100: ESC DL Control */
+    __R  uint8_t  RESERVED5[4];                /* 0x104 - 0x107: Reserved */
+    __R  uint16_t PHYSICAL_RW_OFFSET;          /* 0x108: Physical Read/Write Offset */
+    __R  uint8_t  RESERVED6[6];                /* 0x10A - 0x10F: Reserved */
+    __R  uint16_t ESC_DL_STAT;                 /* 0x110: ESC DL Status */
+    __R  uint8_t  RESERVED7[14];               /* 0x112 - 0x11F: Reserved */
+    __RW uint16_t AL_CTRL;                     /* 0x120: AL Control */
+    __R  uint8_t  RESERVED8[14];               /* 0x122 - 0x12F: Reserved */
+    __RW uint16_t AL_STAT;                     /* 0x130: AL Status */
+    __R  uint8_t  RESERVED9[2];                /* 0x132 - 0x133: Reserved */
+    __RW uint16_t AL_STAT_CODE;                /* 0x134: AL Status Code */
+    __R  uint8_t  RESERVED10[2];               /* 0x136 - 0x137: Reserved */
+    __RW uint8_t  RUN_LED_OVRD;                /* 0x138: RUN LED Override */
+    __RW uint8_t  ERR_LED_OVRD;                /* 0x139: ERR LED Override */
+    __R  uint8_t  RESERVED11[6];               /* 0x13A - 0x13F: Reserved */
+    __R  uint8_t  PDI_CTRL;                    /* 0x140: PDI Control */
+    __R  uint8_t  ESC_CFG;                     /* 0x141: ESC Configuration */
+    __R  uint8_t  RESERVED12[12];              /* 0x142 - 0x14D: Reserved */
+    __R  uint16_t PDI_INFO;                    /* 0x14E: PDI Information */
+    __R  uint8_t  PDI_CFG;                     /* 0x150: PDI Configuration */
+    __R  uint8_t  PDI_SL_CFG;                  /* 0x151: PDI Sync/Latch[1:0] Configuration */
+    __RW uint16_t PDI_EXT_CFG;                 /* 0x152: PDI Extended Configuration */
+    __R  uint8_t  RESERVED13[172];             /* 0x154 - 0x1FF: Reserved */
+    __R  uint16_t ECAT_EVT_MSK;                /* 0x200: ECAT Event Mask */
+    __R  uint8_t  RESERVED14[2];               /* 0x202 - 0x203: Reserved */
+    __RW uint32_t PDI_AL_EVT_MSK;              /* 0x204: PDI AL Event Mask */
+    __R  uint8_t  RESERVED15[8];               /* 0x208 - 0x20F: Reserved */
+    __R  uint16_t ECAT_EVT_REQ;                /* 0x210: ECAT Event Request */
+    __R  uint8_t  RESERVED16[14];              /* 0x212 - 0x21F: Reserved */
+    __R  uint32_t AL_EVT_REQ;                  /* 0x220: AL Event Request */
+    __R  uint8_t  RESERVED17[220];             /* 0x224 - 0x2FF: Reserved */
+    __R  uint16_t RX_ERR_CNT[4];               /* 0x300 - 0x306: RX Error Counter */
+    __R  uint8_t  FWD_RX_ERR_CNT[4];           /* 0x308 - 0x30B: Forwarded RX Error Counter */
+    __R  uint8_t  ECAT_PU_ERR_CNT;             /* 0x30C: ECAT Processing Unit Error Counter */
+    __R  uint8_t  PDI_ERR_CNT;                 /* 0x30D: PDI Error Counter */
+    __R  uint8_t  RESERVED18[2];               /* 0x30E - 0x30F: Reserved */
+    __R  uint8_t  LOST_LINK_CNT[4];            /* 0x310 - 0x313: Lost Link Counter */
+    __R  uint8_t  RESERVED19[236];             /* 0x314 - 0x3FF: Reserved */
+    __R  uint16_t WDG_DIV;                     /* 0x400: Watchdog Divider */
+    __R  uint8_t  RESERVED20[14];              /* 0x402 - 0x40F: Reserved */
+    __R  uint16_t WDG_TIME_PDI;                /* 0x410: Watchdog Time PDI */
+    __R  uint8_t  RESERVED21[14];              /* 0x412 - 0x41F: Reserved */
+    __R  uint16_t WDG_TIME_PDAT;               /* 0x420: Watchdog Time Process Data */
+    __R  uint8_t  RESERVED22[30];              /* 0x422 - 0x43F: Reserved */
+    __RW uint16_t WDG_STAT_PDAT;               /* 0x440: Watchdog Status Process Data */
+    __R  uint8_t  WDG_CNT_PDAT;                /* 0x442: Watchdog Counter Process Data */
+    __R  uint8_t  WDG_CNT_PDI;                 /* 0x443: Watchdog Counter PDI */
+    __R  uint8_t  RESERVED23[188];             /* 0x444 - 0x4FF: Reserved */
+    __R  uint8_t  EEPROM_CFG;                  /* 0x500: EEPROM Configuration */
+    __RW uint8_t  EEPROM_PDI_ACC_STAT;         /* 0x501: EEPROM PDI Access State */
+    __RW uint16_t EEPROM_CTRL_STAT;            /* 0x502: EEPROM Control/Status */
+    __RW uint32_t EEPROM_ADDR;                 /* 0x504: EEPROM Address */
+    __RW uint64_t EEPROM_DATA;                 /* 0x508: EEPROM Data */
+    __RW uint16_t MII_MNG_CS;                  /* 0x510: MII Management Control/Status */
+    __RW uint8_t  PHY_ADDR;                    /* 0x512: PHY Address */
+    __RW uint8_t  PHY_REG_ADDR;                /* 0x513: PHY Register Address */
+    __RW uint16_t PHY_DATA;                    /* 0x514: PHY Data */
+    __R  uint8_t  MIIM_ECAT_ACC_STAT;          /* 0x516: MII Management ECAT Access State */
+    __RW uint8_t  MIIM_PDI_ACC_STAT;           /* 0x517: MII Management PDI Access State */
+    __RW uint8_t  PHY_STAT[4];                 /* 0x518 - 0x51B: PHY Port */
+    __R  uint8_t  RESERVED24[228];             /* 0x51C - 0x5FF: Reserved */
+    struct {
+        __R  uint32_t LOGIC_START_ADDR;        /* 0x600: Logical Start Address */
+        __R  uint16_t LENGTH;                  /* 0x604: Length */
+        __R  uint8_t  LOGIC_START_BIT;         /* 0x606: Logical Start Bit */
+        __R  uint8_t  LOGIC_STOP_BIT;          /* 0x607: Logical Stop Bit */
+        __R  uint16_t PHYSICAL_START_ADDR;     /* 0x608: Physical Start Address */
+        __R  uint8_t  PHYSICAL_START_BIT;      /* 0x60A: Physical Start Bit */
+        __R  uint8_t  TYPE;                    /* 0x60B: Type */
+        __R  uint8_t  ACTIVATE;                /* 0x60C: Activate */
+        __R  uint8_t  RESERVED0[3];            /* 0x60D - 0x60F: Reserved */
+    } FMMU[8];
+    __R  uint8_t  RESERVED25[384];             /* 0x680 - 0x7FF: Reserved */
+    struct {
+        __R  uint16_t PHYSICAL_START_ADDR;     /* 0x800: Physical Start Address */
+        __R  uint16_t LENGTH;                  /* 0x802: Length */
+        __R  uint8_t  CONTROL;                 /* 0x804: Control */
+        __R  uint8_t  STATUS;                  /* 0x805: Status */
+        __RW uint8_t  ACTIVATE;                /* 0x806: Activate */
+        __RW uint8_t  PDI_CTRL;                /* 0x807: PDI Control */
+    } SYNCM[8];
+    __R  uint8_t  RESERVED26[192];             /* 0x840 - 0x8FF: Reserved */
+    __R  uint32_t RCV_TIME[4];                 /* 0x900 - 0x90C: Receive Time */
+    __RW uint64_t SYS_TIME;                    /* 0x910: System Time */
+    __R  uint64_t RCVT_ECAT_PU;                /* 0x918: Receive Time ECAT Processing Unit */
+    __RW uint64_t SYS_TIME_OFFSET;             /* 0x920: System Time Offset */
+    __RW uint32_t SYS_TIME_DELAY;              /* 0x928: System Time Delay */
+    __R  uint32_t SYS_TIME_DIFF;               /* 0x92C: System Time Difference */
+    __RW uint16_t SPD_CNT_START;               /* 0x930: Speed Counter Start */
+    __R  uint16_t SPD_CNT_DIFF;                /* 0x932: Speed Counter Diff */
+    __RW uint8_t  SYS_TIME_DIFF_FD;            /* 0x934: System Time Difference Filter Depth */
+    __RW uint8_t  SPD_CNT_FD;                  /* 0x935: Speed Counter Filter Depth */
+    __R  uint8_t  RCV_TIME_LM;                 /* 0x936: Receive Time Latch Mode */
+    __R  uint8_t  RESERVED27[73];              /* 0x937 - 0x97F: Reserved */
+    __R  uint8_t  CYC_UNIT_CTRL;               /* 0x980: Cyclic Unit Control */
+    __RW uint8_t  SYNCO_ACT;                   /* 0x981: SYNC Out Unit Activation */
+    __R  uint16_t PULSE_LEN;                   /* 0x982: Pulse Length of SyncSignals */
+    __R  uint8_t  ACT_STAT;                    /* 0x984: Activation Status */
+    __R  uint8_t  RESERVED28[9];               /* 0x985 - 0x98D: Reserved */
+    __RW uint8_t  SYNC0_STAT;                  /* 0x98E: SYNC0 Status */
+    __RW uint8_t  SYNC1_STAT;                  /* 0x98F: SYNC1 Status */
+    __RW uint64_t START_TIME_CO;               /* 0x990: Start Time Cyclic Operation */
+    __R  uint64_t NXT_SYNC1_PULSE;             /* 0x998: Next SYNC1 Pulse */
+    __RW uint32_t SYNC0_CYC_TIME;              /* 0x9A0: SYNC0 Cycle Time */
+    __RW uint32_t SYNC1_CYC_TIME;              /* 0x9A4: SYNC1 Cycle Time */
+    __RW uint8_t  LATCH0_CTRL;                 /* 0x9A8: Latch0 Control */
+    __RW uint8_t  LATCH1_CTRL;                 /* 0x9A9: Latch1 Control */
+    __R  uint8_t  RESERVED29[4];               /* 0x9AA - 0x9AD: Reserved */
+    __R  uint8_t  LATCH0_STAT;                 /* 0x9AE: Latch0 Status */
+    __R  uint8_t  LATCH1_STAT;                 /* 0x9AF: Latch1 Status */
+    __RW uint64_t LATCH0_TIME_PE;              /* 0x9B0: Latch0 Time Positive Edge */
+    __RW uint64_t LATCH0_TIME_NE;              /* 0x9B8: Latch0 Time Negative Edge */
+    __RW uint64_t LATCH1_TIME_PE;              /* 0x9C0: Latch1 Time Positive Edge */
+    __RW uint64_t LATCH1_TIME_NE;              /* 0x9C8: Latch1 Time Negative Edge */
+    __R  uint8_t  RESERVED30[32];              /* 0x9D0 - 0x9EF: Reserved */
+    __R  uint32_t ECAT_BUF_CET;                /* 0x9F0: EtherCAT Buffer Change Event Time */
+    __R  uint8_t  RESERVED31[4];               /* 0x9F4 - 0x9F7: Reserved */
+    __R  uint32_t PDI_BUF_SET;                 /* 0x9F8: PDI Buffer Start Event Time */
+    __R  uint32_t PDI_BUF_CET;                 /* 0x9FC: PDI Buffer Change Event Time */
+    __R  uint8_t  RESERVED32[1024];            /* 0xA00 - 0xDFF: Reserved */
+    __R  uint64_t PID;                         /* 0xE00: Product ID */
+    __R  uint64_t VID;                         /* 0xE08: Vendor ID */
+    __R  uint8_t  RESERVED33[240];             /* 0xE10 - 0xEFF: Reserved */
+    __R  uint32_t DIO_OUT_DATA;                /* 0xF00: Digital I/O Output Data */
+    __R  uint8_t  RESERVED34[12];              /* 0xF04 - 0xF0F: Reserved */
+    __RW uint64_t GPO;                         /* 0xF10: General Purpose Outputs */
+    __R  uint64_t GPI;                         /* 0xF18: General Purpose Inputs */
+    __R  uint8_t  RESERVED35[96];              /* 0xF20 - 0xF7F: Reserved */
+    __RW uint8_t  USER_RAM_BYTE0;              /* 0xF80: User Ram Byte 0 */
+    __RW uint8_t  USER_RAM_BYTE1;              /* 0xF81: User Ram Byte 1 */
+    __RW uint8_t  USER_RAM_BYTE2;              /* 0xF82: User Ram Byte 2 */
+    __RW uint8_t  USER_RAM_BYTE3;              /* 0xF83: User Ram Byte 3 */
+    __RW uint8_t  USER_RAM_BYTE4;              /* 0xF84: User Ram Byte 4 */
+    __RW uint8_t  USER_RAM_BYTE5;              /* 0xF85: User Ram Byte 5 */
+    __RW uint8_t  USER_RAM_BYTE6;              /* 0xF86: User Ram Byte 6 */
+    __RW uint8_t  USER_RAM_BYTE7;              /* 0xF87: User Ram Byte 7 */
+    __RW uint8_t  USER_RAM_BYTE8;              /* 0xF88: User Ram Byte 8 */
+    __RW uint8_t  USER_RAM_BYTE9;              /* 0xF89: User Ram Byte 9 */
+    __RW uint8_t  USER_RAM_BYTE10;             /* 0xF8A: User Ram Byte 10 */
+    __RW uint8_t  USER_RAM_BYTE11;             /* 0xF8B: User Ram Byte 11 */
+    __R  uint8_t  RESERVED36[2];               /* 0xF8C - 0xF8D: Reserved */
+    __RW uint8_t  USER_RAM_BYTE14;             /* 0xF8E: User Ram Byte 14 */
+    __RW uint8_t  USER_RAM_BYTE15;             /* 0xF8F: User Ram Byte 15 */
+    __R  uint8_t  RESERVED37[3];               /* 0xF90 - 0xF92: Reserved */
+    __RW uint8_t  USER_RAM_BYTE19;             /* 0xF93: User Ram Byte 19 */
+    __R  uint8_t  RESERVED38[108];             /* 0xF94 - 0xFFF: Reserved */
+    __RW uint32_t PDRAM;                       /* 0x1000: Process Data Ram */
+    __R  uint8_t  RESERVED39[61436];           /* 0x1004 - 0xFFFF: Reserved */
+    __RW uint32_t PDRAM_ALS;                   /* 0x10000: Process Data Ram Alias */
+    __R  uint8_t  RESERVED40[61436];           /* 0x10004 - 0x1EFFF: Reserved */
+    __RW uint32_t GPR_CFG0;                    /* 0x1F000: General Purpose Configure 0 */
+    __RW uint32_t GPR_CFG1;                    /* 0x1F004: General Purpose Configure 1 */
+    __RW uint32_t GPR_CFG2;                    /* 0x1F008: General Purpose Configure 2 */
+    __R  uint8_t  RESERVED41[4];               /* 0x1F00C - 0x1F00F: Reserved */
+    __RW uint32_t PHY_CFG0;                    /* 0x1F010: PHY Configure 0 */
+    __RW uint32_t PHY_CFG1;                    /* 0x1F014: PHY Configure 1 */
+    __R  uint8_t  RESERVED42[8];               /* 0x1F018 - 0x1F01F: Reserved */
+    __RW uint32_t GPIO_CTRL;                   /* 0x1F020: GPIO Output Enable */
+    __R  uint8_t  RESERVED43[12];              /* 0x1F024 - 0x1F02F: Reserved */
+    __RW uint32_t GPI_OVERRIDE0;               /* 0x1F030: GPI low word Override value */
+    __RW uint32_t GPI_OVERRIDE1;               /* 0x1F034: GPI high word Override value */
+    __R  uint32_t GPO_REG0;                    /* 0x1F038: GPO low word read value */
+    __R  uint32_t GPO_REG1;                    /* 0x1F03C: GPO high word read value */
+    __R  uint32_t GPI_REG0;                    /* 0x1F040: GPI low word read value */
+    __R  uint32_t GPI_REG1;                    /* 0x1F044: GPI high word read value */
+    __R  uint8_t  RESERVED44[24];              /* 0x1F048 - 0x1F05F: Reserved */
+    __R  uint32_t GPR_STATUS;                  /* 0x1F060: global status register */
+    __R  uint8_t  RESERVED45[28];              /* 0x1F064 - 0x1F07F: Reserved */
+    __RW uint32_t IO_CFG[9];                   /* 0x1F080 - 0x1F0A0: CTR IO Configure */
+} ESC_t;
+
+#define ESCREG_BASE (0x00000000UL) /* Base address of ESC peripheral */
+#define ESCREG      ((ESC_t *) ESCREG_BASE) /* Pointer to ESC peripheral */
+#define ESCREG_OF(n) ((size_t)&(n)) /* offset of ESC peripheral */
+
+/* Bitfield definition for register: TYPE */
+/*
+ * TYPE (RO)
+ *
+ * Controller type
+ */
+#define ESC_TYPE_TYPE_MASK (0xFFU)
+#define ESC_TYPE_TYPE_SHIFT (0U)
+#define ESC_TYPE_TYPE_GET(x) (((uint8_t)(x) & ESC_TYPE_TYPE_MASK) >> ESC_TYPE_TYPE_SHIFT)
+
+/* Bitfield definition for register: REVISION */
+/*
+ * X (RO)
+ *
+ * major version X
+ */
+#define ESC_REVISION_X_MASK (0xFFU)
+#define ESC_REVISION_X_SHIFT (0U)
+#define ESC_REVISION_X_GET(x) (((uint8_t)(x) & ESC_REVISION_X_MASK) >> ESC_REVISION_X_SHIFT)
+
+/* Bitfield definition for register: BUILD */
+/*
+ * BUILD (RO)
+ *
+ */
+#define ESC_BUILD_BUILD_MASK (0xFF00U)
+#define ESC_BUILD_BUILD_SHIFT (8U)
+#define ESC_BUILD_BUILD_GET(x) (((uint16_t)(x) & ESC_BUILD_BUILD_MASK) >> ESC_BUILD_BUILD_SHIFT)
+
+/*
+ * Y (RO)
+ *
+ * minor version Y
+ */
+#define ESC_BUILD_Y_MASK (0xF0U)
+#define ESC_BUILD_Y_SHIFT (4U)
+#define ESC_BUILD_Y_GET(x) (((uint16_t)(x) & ESC_BUILD_Y_MASK) >> ESC_BUILD_Y_SHIFT)
+
+/*
+ * Z (RO)
+ *
+ * maintenance version Z
+ */
+#define ESC_BUILD_Z_MASK (0xFU)
+#define ESC_BUILD_Z_SHIFT (0U)
+#define ESC_BUILD_Z_GET(x) (((uint16_t)(x) & ESC_BUILD_Z_MASK) >> ESC_BUILD_Z_SHIFT)
+
+/* Bitfield definition for register: FMMU_NUM */
+/*
+ * NUM (RO)
+ *
+ * Number of supported FMMU channels (or entities)
+ */
+#define ESC_FMMU_NUM_NUM_MASK (0xFFU)
+#define ESC_FMMU_NUM_NUM_SHIFT (0U)
+#define ESC_FMMU_NUM_NUM_GET(x) (((uint8_t)(x) & ESC_FMMU_NUM_NUM_MASK) >> ESC_FMMU_NUM_NUM_SHIFT)
+
+/* Bitfield definition for register: SYNCM_NUM */
+/*
+ * NUM (RO)
+ *
+ * Number of supported SyncManager channels (or entities)
+ */
+#define ESC_SYNCM_NUM_NUM_MASK (0xFFU)
+#define ESC_SYNCM_NUM_NUM_SHIFT (0U)
+#define ESC_SYNCM_NUM_NUM_GET(x) (((uint8_t)(x) & ESC_SYNCM_NUM_NUM_MASK) >> ESC_SYNCM_NUM_NUM_SHIFT)
+
+/* Bitfield definition for register: RAM_SIZE */
+/*
+ * SIZE (RO)
+ *
+ * Process Data RAM size supported in KByte
+ */
+#define ESC_RAM_SIZE_SIZE_MASK (0xFFU)
+#define ESC_RAM_SIZE_SIZE_SHIFT (0U)
+#define ESC_RAM_SIZE_SIZE_GET(x) (((uint8_t)(x) & ESC_RAM_SIZE_SIZE_MASK) >> ESC_RAM_SIZE_SIZE_SHIFT)
+
+/* Bitfield definition for register: PORT_DESC */
+/*
+ * PORT3 (RO)
+ *
+ * Port configuration:
+ * 00:Not implemented
+ * 01:Not configured (SII EEPROM)
+ * 10:EBUS
+ * 11:MII/RMII/RGMII
+ */
+#define ESC_PORT_DESC_PORT3_MASK (0xC0U)
+#define ESC_PORT_DESC_PORT3_SHIFT (6U)
+#define ESC_PORT_DESC_PORT3_GET(x) (((uint8_t)(x) & ESC_PORT_DESC_PORT3_MASK) >> ESC_PORT_DESC_PORT3_SHIFT)
+
+/*
+ * PORT2 (RO)
+ *
+ * Port configuration:
+ * 00:Not implemented
+ * 01:Not configured (SII EEPROM)
+ * 10:EBUS
+ * 11:MII/RMII/RGMII
+ */
+#define ESC_PORT_DESC_PORT2_MASK (0x30U)
+#define ESC_PORT_DESC_PORT2_SHIFT (4U)
+#define ESC_PORT_DESC_PORT2_GET(x) (((uint8_t)(x) & ESC_PORT_DESC_PORT2_MASK) >> ESC_PORT_DESC_PORT2_SHIFT)
+
+/*
+ * PORT1 (RO)
+ *
+ * Port configuration:
+ * 00:Not implemented
+ * 01:Not configured (SII EEPROM)
+ * 10:EBUS
+ * 11:MII/RMII/RGMII
+ */
+#define ESC_PORT_DESC_PORT1_MASK (0xCU)
+#define ESC_PORT_DESC_PORT1_SHIFT (2U)
+#define ESC_PORT_DESC_PORT1_GET(x) (((uint8_t)(x) & ESC_PORT_DESC_PORT1_MASK) >> ESC_PORT_DESC_PORT1_SHIFT)
+
+/*
+ * PORT0 (RO)
+ *
+ * Port configuration:
+ * 00:Not implemented
+ * 01:Not configured (SII EEPROM)
+ * 10:EBUS
+ * 11:MII/RMII/RGMII
+ */
+#define ESC_PORT_DESC_PORT0_MASK (0x3U)
+#define ESC_PORT_DESC_PORT0_SHIFT (0U)
+#define ESC_PORT_DESC_PORT0_GET(x) (((uint8_t)(x) & ESC_PORT_DESC_PORT0_MASK) >> ESC_PORT_DESC_PORT0_SHIFT)
+
+/* Bitfield definition for register: FEATURE */
+/*
+ * FFSC (RO)
+ *
+ * Fixed FMMU/SyncManager configuration:
+ * 0:Variable configuration
+ * 1:Fixed configuration (refer to documentation of supporting ESCs)
+ */
+#define ESC_FEATURE_FFSC_MASK (0x800U)
+#define ESC_FEATURE_FFSC_SHIFT (11U)
+#define ESC_FEATURE_FFSC_GET(x) (((uint16_t)(x) & ESC_FEATURE_FFSC_MASK) >> ESC_FEATURE_FFSC_SHIFT)
+
+/*
+ * RWC (RO)
+ *
+ * EtherCAT read/write command support(BRW,APRW,FPRW):
+ * 0:Supported
+ * 1:Not supported
+ */
+#define ESC_FEATURE_RWC_MASK (0x400U)
+#define ESC_FEATURE_RWC_SHIFT (10U)
+#define ESC_FEATURE_RWC_GET(x) (((uint16_t)(x) & ESC_FEATURE_RWC_MASK) >> ESC_FEATURE_RWC_SHIFT)
+
+/*
+ * LRW (RO)
+ *
+ * EtherCAT LRW command support:
+ * 0:Supported
+ * 1:Not supported
+ */
+#define ESC_FEATURE_LRW_MASK (0x200U)
+#define ESC_FEATURE_LRW_SHIFT (9U)
+#define ESC_FEATURE_LRW_GET(x) (((uint16_t)(x) & ESC_FEATURE_LRW_MASK) >> ESC_FEATURE_LRW_SHIFT)
+
+/*
+ * EDSA (RO)
+ *
+ * Enhanced DC SYNC Activation:
+ * 0:Not available
+ * 1:Available
+ * Note:This feature refers to registers 0x981[7:3] and 0x0984
+ */
+#define ESC_FEATURE_EDSA_MASK (0x100U)
+#define ESC_FEATURE_EDSA_SHIFT (8U)
+#define ESC_FEATURE_EDSA_GET(x) (((uint16_t)(x) & ESC_FEATURE_EDSA_MASK) >> ESC_FEATURE_EDSA_SHIFT)
+
+/*
+ * SHFE (RO)
+ *
+ * Seperate Handling of FCS Errors:
+ * 0:Not supported
+ * 1:Supported, frames with wrong FCS and additional nibble will be counted separately in Forwarded RX Error Counter
+ */
+#define ESC_FEATURE_SHFE_MASK (0x80U)
+#define ESC_FEATURE_SHFE_SHIFT (7U)
+#define ESC_FEATURE_SHFE_GET(x) (((uint16_t)(x) & ESC_FEATURE_SHFE_MASK) >> ESC_FEATURE_SHFE_SHIFT)
+
+/*
+ * ELDM (RO)
+ *
+ * Enhanced Link Detection MII:
+ * 0:Not available
+ * 1:Available
+ */
+#define ESC_FEATURE_ELDM_MASK (0x40U)
+#define ESC_FEATURE_ELDM_SHIFT (6U)
+#define ESC_FEATURE_ELDM_GET(x) (((uint16_t)(x) & ESC_FEATURE_ELDM_MASK) >> ESC_FEATURE_ELDM_SHIFT)
+
+/*
+ * DCW (RO)
+ *
+ * Distributed Clocks width:
+ * 0:32 bit
+ * 1:64 bit
+ */
+#define ESC_FEATURE_DCW_MASK (0x8U)
+#define ESC_FEATURE_DCW_SHIFT (3U)
+#define ESC_FEATURE_DCW_GET(x) (((uint16_t)(x) & ESC_FEATURE_DCW_MASK) >> ESC_FEATURE_DCW_SHIFT)
+
+/*
+ * DC (RO)
+ *
+ * Distributed Clocks:
+ * 0:Not available
+ * 1:Available
+ */
+#define ESC_FEATURE_DC_MASK (0x4U)
+#define ESC_FEATURE_DC_SHIFT (2U)
+#define ESC_FEATURE_DC_GET(x) (((uint16_t)(x) & ESC_FEATURE_DC_MASK) >> ESC_FEATURE_DC_SHIFT)
+
+/*
+ * FMMU (RO)
+ *
+ * FMMU Operation:
+ * 0:Bit oriented
+ * 1:Byte oriented
+ */
+#define ESC_FEATURE_FMMU_MASK (0x1U)
+#define ESC_FEATURE_FMMU_SHIFT (0U)
+#define ESC_FEATURE_FMMU_GET(x) (((uint16_t)(x) & ESC_FEATURE_FMMU_MASK) >> ESC_FEATURE_FMMU_SHIFT)
+
+/* Bitfield definition for register: STATION_ADDR */
+/*
+ * ADDR (RO)
+ *
+ * Address used for node addressing
+ * (FPRD/FPWR/FPRW/FRMW commands)
+ */
+#define ESC_STATION_ADDR_ADDR_MASK (0xFFFFU)
+#define ESC_STATION_ADDR_ADDR_SHIFT (0U)
+#define ESC_STATION_ADDR_ADDR_GET(x) (((uint16_t)(x) & ESC_STATION_ADDR_ADDR_MASK) >> ESC_STATION_ADDR_ADDR_SHIFT)
+
+/* Bitfield definition for register: STATION_ALS */
+/*
+ * ADDR (RW)
+ *
+ * Alias Address used for node addressing
+ * (FPRD/FPWR/FPRW/FRMW commands).
+ * The use of this alias is activated by Register
+ * DL Control Bit 0x0100[24].
+ * NOTE:EEPROM value is only transferred into this
+ * register at first EEPROM load after power-on or
+ * reset.
+ * ESC20 exception:EEPROM value is transferred
+ * into this register after each EEPROM reload
+ * command.
+ */
+#define ESC_STATION_ALS_ADDR_MASK (0xFFFFU)
+#define ESC_STATION_ALS_ADDR_SHIFT (0U)
+#define ESC_STATION_ALS_ADDR_SET(x) (((uint16_t)(x) << ESC_STATION_ALS_ADDR_SHIFT) & ESC_STATION_ALS_ADDR_MASK)
+#define ESC_STATION_ALS_ADDR_GET(x) (((uint16_t)(x) & ESC_STATION_ALS_ADDR_MASK) >> ESC_STATION_ALS_ADDR_SHIFT)
+
+/* Bitfield definition for register: REG_WEN */
+/*
+ * EN (RO)
+ *
+ * If register write protection is enabled, this
+ * register has to be written in the same
+ * Ethernet frame (value does not matter)
+ * before other writes to this station are allowed.
+ * This bit is self-clearing at the beginning of the
+ * next frame (SOF), or if Register Write
+ * Protection is disabled.
+ */
+#define ESC_REG_WEN_EN_MASK (0x1U)
+#define ESC_REG_WEN_EN_SHIFT (0U)
+#define ESC_REG_WEN_EN_GET(x) (((uint8_t)(x) & ESC_REG_WEN_EN_MASK) >> ESC_REG_WEN_EN_SHIFT)
+
+/* Bitfield definition for register: REG_WP */
+/*
+ * WP (RO)
+ *
+ * Register write protection:
+ * 0:Protection disabled
+ * 1:Protection enabled
+ * Registers 0x0000:0x0F7F are write-protected,
+ * except for 0x0020 and 0x0030
+ */
+#define ESC_REG_WP_WP_MASK (0x1U)
+#define ESC_REG_WP_WP_SHIFT (0U)
+#define ESC_REG_WP_WP_GET(x) (((uint8_t)(x) & ESC_REG_WP_WP_MASK) >> ESC_REG_WP_WP_SHIFT)
+
+/* Bitfield definition for register: ESC_WEN */
+/*
+ * EN (RO)
+ *
+ * If ESC write protection is enabled, this
+ * register has to be written in the same
+ * Ethernet frame (value does not matter)
+ * before other writes to this station are allowed.
+ * This bit is self-clearing at the beginning of the
+ * next frame (SOF), or if ESC Write Protection
+ * is disabled.
+ */
+#define ESC_ESC_WEN_EN_MASK (0x1U)
+#define ESC_ESC_WEN_EN_SHIFT (0U)
+#define ESC_ESC_WEN_EN_GET(x) (((uint8_t)(x) & ESC_ESC_WEN_EN_MASK) >> ESC_ESC_WEN_EN_SHIFT)
+
+/* Bitfield definition for register: ESC_WP */
+/*
+ * WP (RO)
+ *
+ * Write protect:
+ * 0:Protection disabled
+ * 1:Protection enabled
+ * All areas are write-protected, except for 0x0030.
+ */
+#define ESC_ESC_WP_WP_MASK (0x1U)
+#define ESC_ESC_WP_WP_SHIFT (0U)
+#define ESC_ESC_WP_WP_GET(x) (((uint8_t)(x) & ESC_ESC_WP_WP_MASK) >> ESC_ESC_WP_WP_SHIFT)
+
+/* Bitfield definition for register: ESC_RST_ECAT */
+/*
+ * PR (RO)
+ *
+ * Progress of the reset procedure:
+ * 00:initial/reset state
+ * 01:after writing 0x52 ('R'), when previous
+ * state was 00
+ * 10:after writing 0x45 ('E'), when previous
+ * state was 01
+ * 11:after writing 0x53 ('S'), when previous
+ * state was 10.
+ * This value must not be observed
+ * because the ESC enters reset when this
+ * state is reached, resulting in state 00
+ */
+#define ESC_ESC_RST_ECAT_PR_MASK (0x3U)
+#define ESC_ESC_RST_ECAT_PR_SHIFT (0U)
+#define ESC_ESC_RST_ECAT_PR_GET(x) (((uint8_t)(x) & ESC_ESC_RST_ECAT_PR_MASK) >> ESC_ESC_RST_ECAT_PR_SHIFT)
+
+/* Bitfield definition for register: ESC_RST_PDI */
+/*
+ * RST (RW)
+ *
+ * A reset is asserted after writing the reset
+ * sequence 0x52 ('R'), 0x45 ('E') and 0x53 ('S')
+ * in this register with 3 consecutive commands.
+ * Any other command which does not continue
+ * the sequence by writing the next expected
+ * value will cancel the reset procedure
+ */
+#define ESC_ESC_RST_PDI_RST_MASK (0xFFU)
+#define ESC_ESC_RST_PDI_RST_SHIFT (0U)
+#define ESC_ESC_RST_PDI_RST_SET(x) (((uint8_t)(x) << ESC_ESC_RST_PDI_RST_SHIFT) & ESC_ESC_RST_PDI_RST_MASK)
+#define ESC_ESC_RST_PDI_RST_GET(x) (((uint8_t)(x) & ESC_ESC_RST_PDI_RST_MASK) >> ESC_ESC_RST_PDI_RST_SHIFT)
+
+/* Bitfield definition for register: ESC_DL_CTRL */
+/*
+ * SA (RO)
+ *
+ * Station alias:
+ * 0:Ignore Station Alias
+ * 1:Alias can be used for all configured
+ * address comm
+ */
+#define ESC_ESC_DL_CTRL_SA_MASK (0x1000000UL)
+#define ESC_ESC_DL_CTRL_SA_SHIFT (24U)
+#define ESC_ESC_DL_CTRL_SA_GET(x) (((uint32_t)(x) & ESC_ESC_DL_CTRL_SA_MASK) >> ESC_ESC_DL_CTRL_SA_SHIFT)
+
+/*
+ * RFS (RO)
+ *
+ * RX FIFO Size (ESC delays start of
+ * forwarding until FIFO is at least half full).
+ * RX FIFO Size/RX delay reduction** :
+ * Value:EBUS:MII:
+ * 0:-50 ns -40 ns (-80 ns***)
+ * 1:-40 ns -40 ns (-80 ns***)
+ * 2:-30 ns -40 ns
+ * 3:-20 ns -40 ns
+ * 4:-10 ns no change
+ * 5:no change no change
+ * 6:no change no change
+ * 7:default default
+ * NOTE:EEPROM value is only taken over at first
+ * EEPROM load after power-on or reset
+ */
+#define ESC_ESC_DL_CTRL_RFS_MASK (0x70000UL)
+#define ESC_ESC_DL_CTRL_RFS_SHIFT (16U)
+#define ESC_ESC_DL_CTRL_RFS_GET(x) (((uint32_t)(x) & ESC_ESC_DL_CTRL_RFS_MASK) >> ESC_ESC_DL_CTRL_RFS_SHIFT)
+
+/*
+ * LP3 (RO)
+ *
+ * Loop Port 3:
+ * 00:Auto
+ * 01:Auto Close
+ * 10:Open
+ * 11:Closed
+ */
+#define ESC_ESC_DL_CTRL_LP3_MASK (0xC000U)
+#define ESC_ESC_DL_CTRL_LP3_SHIFT (14U)
+#define ESC_ESC_DL_CTRL_LP3_GET(x) (((uint32_t)(x) & ESC_ESC_DL_CTRL_LP3_MASK) >> ESC_ESC_DL_CTRL_LP3_SHIFT)
+
+/*
+ * LP2 (RO)
+ *
+ * Loop Port 2:
+ * 00:Auto
+ * 01:Auto Close
+ * 10:Open
+ * 11:Closed
+ */
+#define ESC_ESC_DL_CTRL_LP2_MASK (0x3000U)
+#define ESC_ESC_DL_CTRL_LP2_SHIFT (12U)
+#define ESC_ESC_DL_CTRL_LP2_GET(x) (((uint32_t)(x) & ESC_ESC_DL_CTRL_LP2_MASK) >> ESC_ESC_DL_CTRL_LP2_SHIFT)
+
+/*
+ * LP1 (RO)
+ *
+ * Loop Port 1:
+ * 00:Auto
+ * 01:Auto Close
+ * 10:Open
+ * 11:Closed
+ */
+#define ESC_ESC_DL_CTRL_LP1_MASK (0xC00U)
+#define ESC_ESC_DL_CTRL_LP1_SHIFT (10U)
+#define ESC_ESC_DL_CTRL_LP1_GET(x) (((uint32_t)(x) & ESC_ESC_DL_CTRL_LP1_MASK) >> ESC_ESC_DL_CTRL_LP1_SHIFT)
+
+/*
+ * LP0 (RO)
+ *
+ * Loop Port 0:
+ * 00:Auto
+ * 01:Auto Close
+ * 10:Open
+ * 11:Closed
+ * NOTE:
+ * Loop open means sending/receiving over this port
+ * is enabled, loop closed means sending/receiving
+ * is disabled and frames are forwarded to the next
+ * open port internally.
+ * Auto:loop closed at link down, opened at link up
+ * Auto Close:loop closed at link down, opened with
+ * writing 01 again after link up (or receiving a valid
+ * Ethernet frame at the closed port)
+ * Open:loop open regardless of link state
+ * Closed:loop closed regardless of link state
+ */
+#define ESC_ESC_DL_CTRL_LP0_MASK (0x300U)
+#define ESC_ESC_DL_CTRL_LP0_SHIFT (8U)
+#define ESC_ESC_DL_CTRL_LP0_GET(x) (((uint32_t)(x) & ESC_ESC_DL_CTRL_LP0_MASK) >> ESC_ESC_DL_CTRL_LP0_SHIFT)
+
+/*
+ * TU (RO)
+ *
+ * Temporary use of settings in
+ * 0x0100:0x0103[8:15]:
+ * 0:permanent use
+ * 1:use for about 1 second, then revert to
+ * previous settings
+ */
+#define ESC_ESC_DL_CTRL_TU_MASK (0x2U)
+#define ESC_ESC_DL_CTRL_TU_SHIFT (1U)
+#define ESC_ESC_DL_CTRL_TU_GET(x) (((uint32_t)(x) & ESC_ESC_DL_CTRL_TU_MASK) >> ESC_ESC_DL_CTRL_TU_SHIFT)
+
+/*
+ * FR (RO)
+ *
+ * Forwarding rule:
+ * 0:Forward non-EtherCAT frames:
+ * EtherCAT frames are processed,
+ * non-EtherCAT frames are forwarded
+ * without processing or modification.
+ * The source MAC address is not
+ * changed for any frame.
+ * 1:Destroy non-EtherCAT frames:
+ * EtherCAT frames are processed, non-EtherCAT frames are destroyed.
+ * The source MAC address is changed by
+ * the Processing Unit for every frame
+ * (SOURCE_MAC[1] is set
+ */
+#define ESC_ESC_DL_CTRL_FR_MASK (0x1U)
+#define ESC_ESC_DL_CTRL_FR_SHIFT (0U)
+#define ESC_ESC_DL_CTRL_FR_GET(x) (((uint32_t)(x) & ESC_ESC_DL_CTRL_FR_MASK) >> ESC_ESC_DL_CTRL_FR_SHIFT)
+
+/* Bitfield definition for register: PHYSICAL_RW_OFFSET */
+/*
+ * OFFSET (RO)
+ *
+ * This register is used for ReadWrite
+ * commands in Device Addressing mode
+ * (FPRW, APRW, BRW).
+ * The internal read address is directly taken
+ * from the offset address field of the EtherCAT
+ * datagram header, while the internal write
+ * address is calculated by adding the Physical
+ * Read/Write Offset value to the offset address
+ * field.
+ * Internal read address = ADR,
+ * internal write address = ADR + R/W-Offset
+ */
+#define ESC_PHYSICAL_RW_OFFSET_OFFSET_MASK (0xFFFFU)
+#define ESC_PHYSICAL_RW_OFFSET_OFFSET_SHIFT (0U)
+#define ESC_PHYSICAL_RW_OFFSET_OFFSET_GET(x) (((uint16_t)(x) & ESC_PHYSICAL_RW_OFFSET_OFFSET_MASK) >> ESC_PHYSICAL_RW_OFFSET_OFFSET_SHIFT)
+
+/* Bitfield definition for register: ESC_DL_STAT */
+/*
+ * CP3 (RO)
+ *
+ * Communication on Port 3:
+ * 0:No stable communication
+ * 1:Communication established
+ */
+#define ESC_ESC_DL_STAT_CP3_MASK (0x8000U)
+#define ESC_ESC_DL_STAT_CP3_SHIFT (15U)
+#define ESC_ESC_DL_STAT_CP3_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_CP3_MASK) >> ESC_ESC_DL_STAT_CP3_SHIFT)
+
+/*
+ * LP3 (RO)
+ *
+ * Loop Port 3:
+ * 0:Open
+ * 1:Closed
+ */
+#define ESC_ESC_DL_STAT_LP3_MASK (0x4000U)
+#define ESC_ESC_DL_STAT_LP3_SHIFT (14U)
+#define ESC_ESC_DL_STAT_LP3_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_LP3_MASK) >> ESC_ESC_DL_STAT_LP3_SHIFT)
+
+/*
+ * CP2 (RO)
+ *
+ * Communication on Port 2:
+ * 0:No stable communication
+ * 1:Communication established
+ */
+#define ESC_ESC_DL_STAT_CP2_MASK (0x2000U)
+#define ESC_ESC_DL_STAT_CP2_SHIFT (13U)
+#define ESC_ESC_DL_STAT_CP2_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_CP2_MASK) >> ESC_ESC_DL_STAT_CP2_SHIFT)
+
+/*
+ * LP2 (RO)
+ *
+ * Loop Port 2:
+ * 0:Open
+ * 1:Closed
+ */
+#define ESC_ESC_DL_STAT_LP2_MASK (0x1000U)
+#define ESC_ESC_DL_STAT_LP2_SHIFT (12U)
+#define ESC_ESC_DL_STAT_LP2_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_LP2_MASK) >> ESC_ESC_DL_STAT_LP2_SHIFT)
+
+/*
+ * CP1 (RO)
+ *
+ * Communication on Port 1:
+ * 0:No stable communication
+ * 1:Communication established
+ */
+#define ESC_ESC_DL_STAT_CP1_MASK (0x800U)
+#define ESC_ESC_DL_STAT_CP1_SHIFT (11U)
+#define ESC_ESC_DL_STAT_CP1_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_CP1_MASK) >> ESC_ESC_DL_STAT_CP1_SHIFT)
+
+/*
+ * LP1 (RO)
+ *
+ * Loop Port 1:
+ * 0:Open
+ * 1:Closed
+ */
+#define ESC_ESC_DL_STAT_LP1_MASK (0x400U)
+#define ESC_ESC_DL_STAT_LP1_SHIFT (10U)
+#define ESC_ESC_DL_STAT_LP1_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_LP1_MASK) >> ESC_ESC_DL_STAT_LP1_SHIFT)
+
+/*
+ * CP0 (RO)
+ *
+ * Communication on Port 0:
+ * 0:No stable communication
+ * 1:Communication established
+ */
+#define ESC_ESC_DL_STAT_CP0_MASK (0x200U)
+#define ESC_ESC_DL_STAT_CP0_SHIFT (9U)
+#define ESC_ESC_DL_STAT_CP0_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_CP0_MASK) >> ESC_ESC_DL_STAT_CP0_SHIFT)
+
+/*
+ * LP0 (RO)
+ *
+ * Loop Port 0:
+ * 0:Open
+ * 1:Closed
+ */
+#define ESC_ESC_DL_STAT_LP0_MASK (0x100U)
+#define ESC_ESC_DL_STAT_LP0_SHIFT (8U)
+#define ESC_ESC_DL_STAT_LP0_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_LP0_MASK) >> ESC_ESC_DL_STAT_LP0_SHIFT)
+
+/*
+ * PLP3 (RO)
+ *
+ * Physical link on Port 3:
+ * 0:No link
+ * 1:Link detected
+ */
+#define ESC_ESC_DL_STAT_PLP3_MASK (0x80U)
+#define ESC_ESC_DL_STAT_PLP3_SHIFT (7U)
+#define ESC_ESC_DL_STAT_PLP3_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_PLP3_MASK) >> ESC_ESC_DL_STAT_PLP3_SHIFT)
+
+/*
+ * PLP2 (RO)
+ *
+ * Physical link on Port 2:
+ * 0:No link
+ * 1:Link detected
+ */
+#define ESC_ESC_DL_STAT_PLP2_MASK (0x40U)
+#define ESC_ESC_DL_STAT_PLP2_SHIFT (6U)
+#define ESC_ESC_DL_STAT_PLP2_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_PLP2_MASK) >> ESC_ESC_DL_STAT_PLP2_SHIFT)
+
+/*
+ * PLP1 (RO)
+ *
+ * Physical link on Port 1:
+ * 0:No link
+ * 1:Link detected
+ */
+#define ESC_ESC_DL_STAT_PLP1_MASK (0x20U)
+#define ESC_ESC_DL_STAT_PLP1_SHIFT (5U)
+#define ESC_ESC_DL_STAT_PLP1_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_PLP1_MASK) >> ESC_ESC_DL_STAT_PLP1_SHIFT)
+
+/*
+ * PLP0 (RO)
+ *
+ * Physical link on Port 0:
+ * 0:No link
+ * 1:Link detected
+ */
+#define ESC_ESC_DL_STAT_PLP0_MASK (0x10U)
+#define ESC_ESC_DL_STAT_PLP0_SHIFT (4U)
+#define ESC_ESC_DL_STAT_PLP0_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_PLP0_MASK) >> ESC_ESC_DL_STAT_PLP0_SHIFT)
+
+/*
+ * ELD (RO)
+ *
+ * Enhanced Link detection:
+ * 0:Deactivated for all ports
+ * 1:Activated for at least one port
+ * NOTE:EEPROM value is only transferred into this
+ * register at first EEPROM load after power-on or
+ * reset
+ */
+#define ESC_ESC_DL_STAT_ELD_MASK (0x4U)
+#define ESC_ESC_DL_STAT_ELD_SHIFT (2U)
+#define ESC_ESC_DL_STAT_ELD_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_ELD_MASK) >> ESC_ESC_DL_STAT_ELD_SHIFT)
+
+/*
+ * WDS (RO)
+ *
+ * PDI Watchdog Status:
+ * 0:Watchdog expired
+ * 1:Watchdog reloaded
+ */
+#define ESC_ESC_DL_STAT_WDS_MASK (0x2U)
+#define ESC_ESC_DL_STAT_WDS_SHIFT (1U)
+#define ESC_ESC_DL_STAT_WDS_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_WDS_MASK) >> ESC_ESC_DL_STAT_WDS_SHIFT)
+
+/*
+ * EPLC (RO)
+ *
+ * PDI operational/EEPROM loaded correctly:
+ * 0:EEPROM not loaded, PDI not
+ * operational (no access to Process Data
+ * RAM)
+ * 1:EEPROM loaded correctly, PDI
+ * operational (access to Process Data
+ * RAM)
+ */
+#define ESC_ESC_DL_STAT_EPLC_MASK (0x1U)
+#define ESC_ESC_DL_STAT_EPLC_SHIFT (0U)
+#define ESC_ESC_DL_STAT_EPLC_GET(x) (((uint16_t)(x) & ESC_ESC_DL_STAT_EPLC_MASK) >> ESC_ESC_DL_STAT_EPLC_SHIFT)
+
+/* Bitfield definition for register: AL_CTRL */
+/*
+ * DI (RW)
+ *
+ * Device Identification:
+ * 0:No request
+ * 1:Device Identification request
+ */
+#define ESC_AL_CTRL_DI_MASK (0x20U)
+#define ESC_AL_CTRL_DI_SHIFT (5U)
+#define ESC_AL_CTRL_DI_SET(x) (((uint16_t)(x) << ESC_AL_CTRL_DI_SHIFT) & ESC_AL_CTRL_DI_MASK)
+#define ESC_AL_CTRL_DI_GET(x) (((uint16_t)(x) & ESC_AL_CTRL_DI_MASK) >> ESC_AL_CTRL_DI_SHIFT)
+
+/*
+ * EIA (RW)
+ *
+ * Error Ind Ack:
+ * 0:No Ack of Error Ind in AL status register
+ * 1:Ack of Error Ind in AL status register
+ */
+#define ESC_AL_CTRL_EIA_MASK (0x10U)
+#define ESC_AL_CTRL_EIA_SHIFT (4U)
+#define ESC_AL_CTRL_EIA_SET(x) (((uint16_t)(x) << ESC_AL_CTRL_EIA_SHIFT) & ESC_AL_CTRL_EIA_MASK)
+#define ESC_AL_CTRL_EIA_GET(x) (((uint16_t)(x) & ESC_AL_CTRL_EIA_MASK) >> ESC_AL_CTRL_EIA_SHIFT)
+
+/*
+ * IST (RW)
+ *
+ * Initiate State Transition of the Device State
+ * Machine:
+ * 1:Request Init State
+ * 3:Request Bootstrap State
+ * 2:Request Pre-Operational State
+ * 4:Request Safe-Operational State
+ * 8:Request Operational State
+ */
+#define ESC_AL_CTRL_IST_MASK (0xFU)
+#define ESC_AL_CTRL_IST_SHIFT (0U)
+#define ESC_AL_CTRL_IST_SET(x) (((uint16_t)(x) << ESC_AL_CTRL_IST_SHIFT) & ESC_AL_CTRL_IST_MASK)
+#define ESC_AL_CTRL_IST_GET(x) (((uint16_t)(x) & ESC_AL_CTRL_IST_MASK) >> ESC_AL_CTRL_IST_SHIFT)
+
+/* Bitfield definition for register: AL_STAT */
+/*
+ * DI (RW)
+ *
+ * Device Identification:
+ * 0:Device Identification not valid
+ * 1:Device Identification loaded
+ */
+#define ESC_AL_STAT_DI_MASK (0x20U)
+#define ESC_AL_STAT_DI_SHIFT (5U)
+#define ESC_AL_STAT_DI_SET(x) (((uint16_t)(x) << ESC_AL_STAT_DI_SHIFT) & ESC_AL_STAT_DI_MASK)
+#define ESC_AL_STAT_DI_GET(x) (((uint16_t)(x) & ESC_AL_STAT_DI_MASK) >> ESC_AL_STAT_DI_SHIFT)
+
+/*
+ * EI (RW)
+ *
+ * Error Ind:
+ * 0:Device is in State as requested or Flag
+ * cleared by command
+ * 1:Device has not entered requested State
+ * or changed State as result of a local
+ * action
+ */
+#define ESC_AL_STAT_EI_MASK (0x10U)
+#define ESC_AL_STAT_EI_SHIFT (4U)
+#define ESC_AL_STAT_EI_SET(x) (((uint16_t)(x) << ESC_AL_STAT_EI_SHIFT) & ESC_AL_STAT_EI_MASK)
+#define ESC_AL_STAT_EI_GET(x) (((uint16_t)(x) & ESC_AL_STAT_EI_MASK) >> ESC_AL_STAT_EI_SHIFT)
+
+/*
+ * AS (RW)
+ *
+ * Actual State of the Device State Machine:
+ * 1:Init State
+ * 3:Bootstrap State
+ * 2:Pre-Operational State
+ * 4:Safe-Operational State
+ * 8:Operational State
+ */
+#define ESC_AL_STAT_AS_MASK (0xFU)
+#define ESC_AL_STAT_AS_SHIFT (0U)
+#define ESC_AL_STAT_AS_SET(x) (((uint16_t)(x) << ESC_AL_STAT_AS_SHIFT) & ESC_AL_STAT_AS_MASK)
+#define ESC_AL_STAT_AS_GET(x) (((uint16_t)(x) & ESC_AL_STAT_AS_MASK) >> ESC_AL_STAT_AS_SHIFT)
+
+/* Bitfield definition for register: AL_STAT_CODE */
+/*
+ * CODE (RW)
+ *
+ * AL Status Code
+ */
+#define ESC_AL_STAT_CODE_CODE_MASK (0xFFFFU)
+#define ESC_AL_STAT_CODE_CODE_SHIFT (0U)
+#define ESC_AL_STAT_CODE_CODE_SET(x) (((uint16_t)(x) << ESC_AL_STAT_CODE_CODE_SHIFT) & ESC_AL_STAT_CODE_CODE_MASK)
+#define ESC_AL_STAT_CODE_CODE_GET(x) (((uint16_t)(x) & ESC_AL_STAT_CODE_CODE_MASK) >> ESC_AL_STAT_CODE_CODE_SHIFT)
+
+/* Bitfield definition for register: RUN_LED_OVRD */
+/*
+ * EN_OVRD (RW)
+ *
+ * Enable Override:
+ * 0:Override disabled
+ * 1:Override enabled
+ */
+#define ESC_RUN_LED_OVRD_EN_OVRD_MASK (0x10U)
+#define ESC_RUN_LED_OVRD_EN_OVRD_SHIFT (4U)
+#define ESC_RUN_LED_OVRD_EN_OVRD_SET(x) (((uint8_t)(x) << ESC_RUN_LED_OVRD_EN_OVRD_SHIFT) & ESC_RUN_LED_OVRD_EN_OVRD_MASK)
+#define ESC_RUN_LED_OVRD_EN_OVRD_GET(x) (((uint8_t)(x) & ESC_RUN_LED_OVRD_EN_OVRD_MASK) >> ESC_RUN_LED_OVRD_EN_OVRD_SHIFT)
+
+/*
+ * LED_CODE (RW)
+ *
+ * LED code:
+ * 0x0:Off
+ * 0x1:Flash 1x
+ * 0x2-0xC:Flash 2x – 12x
+ * 0xD:Blinking
+ * 0xE:Flickering
+ * 0xF:On
+ */
+#define ESC_RUN_LED_OVRD_LED_CODE_MASK (0xFU)
+#define ESC_RUN_LED_OVRD_LED_CODE_SHIFT (0U)
+#define ESC_RUN_LED_OVRD_LED_CODE_SET(x) (((uint8_t)(x) << ESC_RUN_LED_OVRD_LED_CODE_SHIFT) & ESC_RUN_LED_OVRD_LED_CODE_MASK)
+#define ESC_RUN_LED_OVRD_LED_CODE_GET(x) (((uint8_t)(x) & ESC_RUN_LED_OVRD_LED_CODE_MASK) >> ESC_RUN_LED_OVRD_LED_CODE_SHIFT)
+
+/* Bitfield definition for register: ERR_LED_OVRD */
+/*
+ * EN_OVRD (RW)
+ *
+ * Enable Override:
+ * 0:Override disabled
+ * 1:Override enabled
+ */
+#define ESC_ERR_LED_OVRD_EN_OVRD_MASK (0x10U)
+#define ESC_ERR_LED_OVRD_EN_OVRD_SHIFT (4U)
+#define ESC_ERR_LED_OVRD_EN_OVRD_SET(x) (((uint8_t)(x) << ESC_ERR_LED_OVRD_EN_OVRD_SHIFT) & ESC_ERR_LED_OVRD_EN_OVRD_MASK)
+#define ESC_ERR_LED_OVRD_EN_OVRD_GET(x) (((uint8_t)(x) & ESC_ERR_LED_OVRD_EN_OVRD_MASK) >> ESC_ERR_LED_OVRD_EN_OVRD_SHIFT)
+
+/*
+ * LED_CODE (RW)
+ *
+ * LED code:
+ * 0x0:Off
+ * 0x1-0xC:Flash 1x – 12x
+ * 0xD:Blinking
+ * 0xE:Flickering
+ * 0xF:On
+ */
+#define ESC_ERR_LED_OVRD_LED_CODE_MASK (0xFU)
+#define ESC_ERR_LED_OVRD_LED_CODE_SHIFT (0U)
+#define ESC_ERR_LED_OVRD_LED_CODE_SET(x) (((uint8_t)(x) << ESC_ERR_LED_OVRD_LED_CODE_SHIFT) & ESC_ERR_LED_OVRD_LED_CODE_MASK)
+#define ESC_ERR_LED_OVRD_LED_CODE_GET(x) (((uint8_t)(x) & ESC_ERR_LED_OVRD_LED_CODE_MASK) >> ESC_ERR_LED_OVRD_LED_CODE_SHIFT)
+
+/* Bitfield definition for register: PDI_CTRL */
+/*
+ * PDI (RO)
+ *
+ * Process data interface:
+ * 0x00:Interface deactivated (no PDI)
+ * 0x01:4 Digital Input
+ * 0x02:4 Digital Output
+ * 0x03:2 Digital Input and 2 Digital Output
+ * 0x04:Digital I/O
+ * 0x05:SPI Slave
+ * 0x06:Oversampling I/O
+ * 0x07:EtherCAT Bridge (port 3)
+ * 0x08:16 Bit asynchronous Microcontroller
+ * interface
+ * 0x09:8 Bit asynchronous Microcontroller
+ * interface
+ * 0x0A:16 Bit synchronous Microcontroller
+ * interface
+ * 0x0B:8 Bit synchronous Microcontroller
+ * interface
+ * 0x10:32 Digital Input and 0 Digital Output
+ * 0x11:24 Digital Input and 8 Digital Output
+ * 0x12:16 Digital Input and 16 Digital Output
+ * 0x13:8 Digital Input and 24 Digital Output
+ * 0x14:0 Digital Input and 32 Digital Output
+ * 0x80:On-chip bus
+ * Others:Reserved
+ */
+#define ESC_PDI_CTRL_PDI_MASK (0xFFU)
+#define ESC_PDI_CTRL_PDI_SHIFT (0U)
+#define ESC_PDI_CTRL_PDI_GET(x) (((uint8_t)(x) & ESC_PDI_CTRL_PDI_MASK) >> ESC_PDI_CTRL_PDI_SHIFT)
+
+/* Bitfield definition for register: ESC_CFG */
+/*
+ * ELP3 (RO)
+ *
+ * Enhanced Link port 3:
+ * 0:disabled (if bit 1=0)
+ * 1:enabled
+ */
+#define ESC_ESC_CFG_ELP3_MASK (0x80U)
+#define ESC_ESC_CFG_ELP3_SHIFT (7U)
+#define ESC_ESC_CFG_ELP3_GET(x) (((uint8_t)(x) & ESC_ESC_CFG_ELP3_MASK) >> ESC_ESC_CFG_ELP3_SHIFT)
+
+/*
+ * ELP2 (RO)
+ *
+ * Enhanced Link port 2:
+ * 0:disabled (if bit 1=0)
+ * 1:enabled
+ */
+#define ESC_ESC_CFG_ELP2_MASK (0x40U)
+#define ESC_ESC_CFG_ELP2_SHIFT (6U)
+#define ESC_ESC_CFG_ELP2_GET(x) (((uint8_t)(x) & ESC_ESC_CFG_ELP2_MASK) >> ESC_ESC_CFG_ELP2_SHIFT)
+
+/*
+ * ELP1 (RO)
+ *
+ * Enhanced Link port 1:
+ * 0:disabled (if bit 1=0)
+ * 1:enabled
+ */
+#define ESC_ESC_CFG_ELP1_MASK (0x20U)
+#define ESC_ESC_CFG_ELP1_SHIFT (5U)
+#define ESC_ESC_CFG_ELP1_GET(x) (((uint8_t)(x) & ESC_ESC_CFG_ELP1_MASK) >> ESC_ESC_CFG_ELP1_SHIFT)
+
+/*
+ * ELP0 (RO)
+ *
+ * Enhanced Link port 0:
+ * 0:disabled (if bit 1=0)
+ * 1:enabled
+ */
+#define ESC_ESC_CFG_ELP0_MASK (0x10U)
+#define ESC_ESC_CFG_ELP0_SHIFT (4U)
+#define ESC_ESC_CFG_ELP0_GET(x) (((uint8_t)(x) & ESC_ESC_CFG_ELP0_MASK) >> ESC_ESC_CFG_ELP0_SHIFT)
+
+/*
+ * CDLIU (RO)
+ *
+ * Distributed Clocks Latch In Unit:
+ * 0:disabled (power saving)
+ * 1:enabled
+ */
+#define ESC_ESC_CFG_CDLIU_MASK (0x8U)
+#define ESC_ESC_CFG_CDLIU_SHIFT (3U)
+#define ESC_ESC_CFG_CDLIU_GET(x) (((uint8_t)(x) & ESC_ESC_CFG_CDLIU_MASK) >> ESC_ESC_CFG_CDLIU_SHIFT)
+
+/*
+ * DCSOU (RO)
+ *
+ * Distributed Clocks SYNC Out Unit:
+ * 0:disabled (power saving)
+ * 1:enabled
+ */
+#define ESC_ESC_CFG_DCSOU_MASK (0x4U)
+#define ESC_ESC_CFG_DCSOU_SHIFT (2U)
+#define ESC_ESC_CFG_DCSOU_GET(x) (((uint8_t)(x) & ESC_ESC_CFG_DCSOU_MASK) >> ESC_ESC_CFG_DCSOU_SHIFT)
+
+/*
+ * ELDAP (RO)
+ *
+ * Enhanced Link detection all ports:
+ * 0:disabled (if bits [7:4]=0)
+ * 1:enabled at all ports (overrides bits [7:4])
+ */
+#define ESC_ESC_CFG_ELDAP_MASK (0x2U)
+#define ESC_ESC_CFG_ELDAP_SHIFT (1U)
+#define ESC_ESC_CFG_ELDAP_GET(x) (((uint8_t)(x) & ESC_ESC_CFG_ELDAP_MASK) >> ESC_ESC_CFG_ELDAP_SHIFT)
+
+/*
+ * DEV_EMU (RO)
+ *
+ * Device emulation (control of AL status):
+ * 0:AL status register has to be set by PDI
+ * 1:AL status register will be set to value
+ * written to AL control register
+ */
+#define ESC_ESC_CFG_DEV_EMU_MASK (0x1U)
+#define ESC_ESC_CFG_DEV_EMU_SHIFT (0U)
+#define ESC_ESC_CFG_DEV_EMU_GET(x) (((uint8_t)(x) & ESC_ESC_CFG_DEV_EMU_MASK) >> ESC_ESC_CFG_DEV_EMU_SHIFT)
+
+/* Bitfield definition for register: PDI_INFO */
+/*
+ * PDICN (RO)
+ *
+ * PDI configuration invalid:
+ * 0:PDI configuration ok
+ * 1:PDI configuration invalid
+ */
+#define ESC_PDI_INFO_PDICN_MASK (0x8U)
+#define ESC_PDI_INFO_PDICN_SHIFT (3U)
+#define ESC_PDI_INFO_PDICN_GET(x) (((uint16_t)(x) & ESC_PDI_INFO_PDICN_MASK) >> ESC_PDI_INFO_PDICN_SHIFT)
+
+/*
+ * PDIA (RO)
+ *
+ * PDI active:
+ * 0:PDI not active
+ * 1:PDI active
+ */
+#define ESC_PDI_INFO_PDIA_MASK (0x4U)
+#define ESC_PDI_INFO_PDIA_SHIFT (2U)
+#define ESC_PDI_INFO_PDIA_GET(x) (((uint16_t)(x) & ESC_PDI_INFO_PDIA_MASK) >> ESC_PDI_INFO_PDIA_SHIFT)
+
+/*
+ * ECLFE (RO)
+ *
+ * ESC configuration area loaded from
+ * EEPROM:
+ * 0:not loaded
+ * 1:loaded
+ */
+#define ESC_PDI_INFO_ECLFE_MASK (0x2U)
+#define ESC_PDI_INFO_ECLFE_SHIFT (1U)
+#define ESC_PDI_INFO_ECLFE_GET(x) (((uint16_t)(x) & ESC_PDI_INFO_ECLFE_MASK) >> ESC_PDI_INFO_ECLFE_SHIFT)
+
+/*
+ * PFABW (RO)
+ *
+ * DI function acknowledge by write:
+ * 0:Disabled
+ * 1:Enabled
+ */
+#define ESC_PDI_INFO_PFABW_MASK (0x1U)
+#define ESC_PDI_INFO_PFABW_SHIFT (0U)
+#define ESC_PDI_INFO_PFABW_GET(x) (((uint16_t)(x) & ESC_PDI_INFO_PFABW_MASK) >> ESC_PDI_INFO_PFABW_SHIFT)
+
+/* Bitfield definition for register: PDI_CFG */
+/*
+ * BUS (RO)
+ *
+ * On-chip bus:
+ * 000:Intel® Avalon®
+ * 001:AXI®
+ * 010:Xilinx® PLB v4.6
+ * 100:Xilinx OPB
+ * others:reserved
+ */
+#define ESC_PDI_CFG_BUS_MASK (0xE0U)
+#define ESC_PDI_CFG_BUS_SHIFT (5U)
+#define ESC_PDI_CFG_BUS_GET(x) (((uint8_t)(x) & ESC_PDI_CFG_BUS_MASK) >> ESC_PDI_CFG_BUS_SHIFT)
+
+/*
+ * CLK (RO)
+ *
+ * On-chip bus clock:
+ * 0:asynchronous
+ * 1-31:synchronous multiplication factor
+ * (N * 25 MHz)
+ */
+#define ESC_PDI_CFG_CLK_MASK (0x1FU)
+#define ESC_PDI_CFG_CLK_SHIFT (0U)
+#define ESC_PDI_CFG_CLK_GET(x) (((uint8_t)(x) & ESC_PDI_CFG_CLK_MASK) >> ESC_PDI_CFG_CLK_SHIFT)
+
+/* Bitfield definition for register: PDI_SL_CFG */
+/*
+ * SYNC1_MAER (RO)
+ *
+ * SYNC1 mapped to AL Event Request
+ * register 0x0220[3]:
+ * 0:Disabled
+ * 1:Enabled
+ */
+#define ESC_PDI_SL_CFG_SYNC1_MAER_MASK (0x80U)
+#define ESC_PDI_SL_CFG_SYNC1_MAER_SHIFT (7U)
+#define ESC_PDI_SL_CFG_SYNC1_MAER_GET(x) (((uint8_t)(x) & ESC_PDI_SL_CFG_SYNC1_MAER_MASK) >> ESC_PDI_SL_CFG_SYNC1_MAER_SHIFT)
+
+/*
+ * SYNC1_CFG (RO)
+ *
+ * SYNC1/LATCH1 configuration*:
+ * 0:LATCH1 input
+ * 1:SYNC1 output
+ */
+#define ESC_PDI_SL_CFG_SYNC1_CFG_MASK (0x40U)
+#define ESC_PDI_SL_CFG_SYNC1_CFG_SHIFT (6U)
+#define ESC_PDI_SL_CFG_SYNC1_CFG_GET(x) (((uint8_t)(x) & ESC_PDI_SL_CFG_SYNC1_CFG_MASK) >> ESC_PDI_SL_CFG_SYNC1_CFG_SHIFT)
+
+/*
+ * SYNC1_ODP (RO)
+ *
+ * SYNC1 output driver/polarity:
+ * 00:Push-Pull active low
+ * 01:Open Drain (active low)
+ * 10:Push-Pull active high
+ * 11:Open Source (active high)
+ */
+#define ESC_PDI_SL_CFG_SYNC1_ODP_MASK (0x30U)
+#define ESC_PDI_SL_CFG_SYNC1_ODP_SHIFT (4U)
+#define ESC_PDI_SL_CFG_SYNC1_ODP_GET(x) (((uint8_t)(x) & ESC_PDI_SL_CFG_SYNC1_ODP_MASK) >> ESC_PDI_SL_CFG_SYNC1_ODP_SHIFT)
+
+/*
+ * SYNC0_MAER (RO)
+ *
+ * SYNC0 mapped to AL Event Request
+ * register 0x0220[2]:
+ * 0:Disabled
+ * 1:Enabled
+ */
+#define ESC_PDI_SL_CFG_SYNC0_MAER_MASK (0x8U)
+#define ESC_PDI_SL_CFG_SYNC0_MAER_SHIFT (3U)
+#define ESC_PDI_SL_CFG_SYNC0_MAER_GET(x) (((uint8_t)(x) & ESC_PDI_SL_CFG_SYNC0_MAER_MASK) >> ESC_PDI_SL_CFG_SYNC0_MAER_SHIFT)
+
+/*
+ * SYNC0_CFG (RO)
+ *
+ * SYNC0/LATCH0 configuration*:
+ * 0:LATCH0 Input
+ * 1:SYNC0 Output
+ */
+#define ESC_PDI_SL_CFG_SYNC0_CFG_MASK (0x4U)
+#define ESC_PDI_SL_CFG_SYNC0_CFG_SHIFT (2U)
+#define ESC_PDI_SL_CFG_SYNC0_CFG_GET(x) (((uint8_t)(x) & ESC_PDI_SL_CFG_SYNC0_CFG_MASK) >> ESC_PDI_SL_CFG_SYNC0_CFG_SHIFT)
+
+/*
+ * SYNC0_ODP (RO)
+ *
+ * SYNC0 output driver/polarity:
+ * 00:Push-Pull active low
+ * 01:Open Drain (active low)
+ * 10:Push-Pull active high
+ * 11:Open Source (active high)
+ */
+#define ESC_PDI_SL_CFG_SYNC0_ODP_MASK (0x3U)
+#define ESC_PDI_SL_CFG_SYNC0_ODP_SHIFT (0U)
+#define ESC_PDI_SL_CFG_SYNC0_ODP_GET(x) (((uint8_t)(x) & ESC_PDI_SL_CFG_SYNC0_ODP_MASK) >> ESC_PDI_SL_CFG_SYNC0_ODP_SHIFT)
+
+/* Bitfield definition for register: PDI_EXT_CFG */
+/*
+ * OCBST (RW)
+ *
+ * On-chip bus sub-type for AXI:
+ * 000:AXI3
+ * 001:AXI4
+ * 010:AXI4 LITE
+ * others:reserved
+ */
+#define ESC_PDI_EXT_CFG_OCBST_MASK (0x700U)
+#define ESC_PDI_EXT_CFG_OCBST_SHIFT (8U)
+#define ESC_PDI_EXT_CFG_OCBST_SET(x) (((uint16_t)(x) << ESC_PDI_EXT_CFG_OCBST_SHIFT) & ESC_PDI_EXT_CFG_OCBST_MASK)
+#define ESC_PDI_EXT_CFG_OCBST_GET(x) (((uint16_t)(x) & ESC_PDI_EXT_CFG_OCBST_MASK) >> ESC_PDI_EXT_CFG_OCBST_SHIFT)
+
+/*
+ * RPS (RO)
+ *
+ * Read prefetch size (in cycles of PDI width):
+ * 0:4 cycles
+ * 1:1 cycle (typical)
+ * 2:2 cycles
+ * 3:Reserved
+ */
+#define ESC_PDI_EXT_CFG_RPS_MASK (0x3U)
+#define ESC_PDI_EXT_CFG_RPS_SHIFT (0U)
+#define ESC_PDI_EXT_CFG_RPS_GET(x) (((uint16_t)(x) & ESC_PDI_EXT_CFG_RPS_MASK) >> ESC_PDI_EXT_CFG_RPS_SHIFT)
+
+/* Bitfield definition for register: ECAT_EVT_MSK */
+/*
+ * MASK (RO)
+ *
+ * ECAT Event masking of the ECAT Event
+ * Request Events for mapping into ECAT event
+ * field of EtherCAT frames:
+ * 0:Corresponding ECAT Event Request
+ * register bit is not mapped
+ * 1:Corresponding ECAT Event Request
+ * register bit is mapped
+ */
+#define ESC_ECAT_EVT_MSK_MASK_MASK (0xFFFFU)
+#define ESC_ECAT_EVT_MSK_MASK_SHIFT (0U)
+#define ESC_ECAT_EVT_MSK_MASK_GET(x) (((uint16_t)(x) & ESC_ECAT_EVT_MSK_MASK_MASK) >> ESC_ECAT_EVT_MSK_MASK_SHIFT)
+
+/* Bitfield definition for register: PDI_AL_EVT_MSK */
+/*
+ * MASK (RW)
+ *
+ * AL Event masking of the AL Event Request
+ * register Events for mapping to PDI IRQ
+ * signal:
+ * 0:Corresponding AL Event Request
+ * register bit is not mapped
+ * 1:Corresponding AL Event Request
+ * register bit is mapped
+ */
+#define ESC_PDI_AL_EVT_MSK_MASK_MASK (0xFFFFFFFFUL)
+#define ESC_PDI_AL_EVT_MSK_MASK_SHIFT (0U)
+#define ESC_PDI_AL_EVT_MSK_MASK_SET(x) (((uint32_t)(x) << ESC_PDI_AL_EVT_MSK_MASK_SHIFT) & ESC_PDI_AL_EVT_MSK_MASK_MASK)
+#define ESC_PDI_AL_EVT_MSK_MASK_GET(x) (((uint32_t)(x) & ESC_PDI_AL_EVT_MSK_MASK_MASK) >> ESC_PDI_AL_EVT_MSK_MASK_SHIFT)
+
+/* Bitfield definition for register: ECAT_EVT_REQ */
+/*
+ * MV (RO)
+ *
+ * Mirrors values of each SyncManager Status:
+ * 0:No Sync Channel 0 event
+ * 1:Sync Channel 0 event pending
+ * 0:No Sync Channel 1 event
+ * 1:Sync Channel 1 event pending
+ * …
+ * 0:No Sync Channel 7 event
+ * 1:Sync Channel 7 event pending
+ */
+#define ESC_ECAT_EVT_REQ_MV_MASK (0xFF0U)
+#define ESC_ECAT_EVT_REQ_MV_SHIFT (4U)
+#define ESC_ECAT_EVT_REQ_MV_GET(x) (((uint16_t)(x) & ESC_ECAT_EVT_REQ_MV_MASK) >> ESC_ECAT_EVT_REQ_MV_SHIFT)
+
+/*
+ * ALS_EVT (RO)
+ *
+ * AL Status event:
+ * 0:No change in AL Status
+ * 1:AL Status change
+ * (Bit is cleared by reading out AL Status
+ * 0x0130:0x0131 from ECAT)
+ */
+#define ESC_ECAT_EVT_REQ_ALS_EVT_MASK (0x8U)
+#define ESC_ECAT_EVT_REQ_ALS_EVT_SHIFT (3U)
+#define ESC_ECAT_EVT_REQ_ALS_EVT_GET(x) (((uint16_t)(x) & ESC_ECAT_EVT_REQ_ALS_EVT_MASK) >> ESC_ECAT_EVT_REQ_ALS_EVT_SHIFT)
+
+/*
+ * DLS_EVT (RO)
+ *
+ * DL Status event:
+ * 0:No change in DL Status
+ * 1:DL Status change
+ * (Bit is cleared by reading out DL Status
+ * 0x0110:0x0111 from ECAT)
+ */
+#define ESC_ECAT_EVT_REQ_DLS_EVT_MASK (0x4U)
+#define ESC_ECAT_EVT_REQ_DLS_EVT_SHIFT (2U)
+#define ESC_ECAT_EVT_REQ_DLS_EVT_GET(x) (((uint16_t)(x) & ESC_ECAT_EVT_REQ_DLS_EVT_MASK) >> ESC_ECAT_EVT_REQ_DLS_EVT_SHIFT)
+
+/*
+ * DCL_EVT (RO)
+ *
+ * DC Latch event:
+ * 0:No change on DC Latch Inputs
+ * 1:At least one change on DC Latch Inputs
+ * (Bit is cleared by reading DC Latch event
+ * times from ECAT for ECAT-controlled Latch
+ * Units, so that Latch 0/1 Status
+ * 0x09AE:0x09AF indicates no event)
+ */
+#define ESC_ECAT_EVT_REQ_DCL_EVT_MASK (0x1U)
+#define ESC_ECAT_EVT_REQ_DCL_EVT_SHIFT (0U)
+#define ESC_ECAT_EVT_REQ_DCL_EVT_GET(x) (((uint16_t)(x) & ESC_ECAT_EVT_REQ_DCL_EVT_MASK) >> ESC_ECAT_EVT_REQ_DCL_EVT_SHIFT)
+
+/* Bitfield definition for register: AL_EVT_REQ */
+/*
+ * SM_INT (RO)
+ *
+ * SyncManager interrupts (SyncManager
+ * register offset 0x5, bit [0] or [1]):
+ * 0:No SyncManager 0 interrupt
+ * 1:SyncManager 0 interrupt pending
+ * 0:No SyncManager 1 interrupt
+ * 1:SyncManager 1 interrupt pending
+ * …
+ * 0:No SyncManager 15 interrupt
+ * 1:SyncManager 15 interrupt pending
+ */
+#define ESC_AL_EVT_REQ_SM_INT_MASK (0xFFFF00UL)
+#define ESC_AL_EVT_REQ_SM_INT_SHIFT (8U)
+#define ESC_AL_EVT_REQ_SM_INT_GET(x) (((uint32_t)(x) & ESC_AL_EVT_REQ_SM_INT_MASK) >> ESC_AL_EVT_REQ_SM_INT_SHIFT)
+
+/*
+ * WDG_PD (RO)
+ *
+ * Watchdog Process Data:
+ * 0:Has not expired
+ * 1:Has expired
+ * (Bit is cleared by reading Watchdog Status
+ * Process Data 0x0440 from PDI)
+ */
+#define ESC_AL_EVT_REQ_WDG_PD_MASK (0x40U)
+#define ESC_AL_EVT_REQ_WDG_PD_SHIFT (6U)
+#define ESC_AL_EVT_REQ_WDG_PD_GET(x) (((uint32_t)(x) & ESC_AL_EVT_REQ_WDG_PD_MASK) >> ESC_AL_EVT_REQ_WDG_PD_SHIFT)
+
+/*
+ * EE_EMU (RO)
+ *
+ * EEPROM Emulation:
+ * 0:No command pending
+ * 1:EEPROM command pending
+ * (Bit is cleared by acknowledging the
+ * command in EEPROM Control/Status
+ * register 0x0502:0x0503[10:8] from PDI)
+ */
+#define ESC_AL_EVT_REQ_EE_EMU_MASK (0x20U)
+#define ESC_AL_EVT_REQ_EE_EMU_SHIFT (5U)
+#define ESC_AL_EVT_REQ_EE_EMU_GET(x) (((uint32_t)(x) & ESC_AL_EVT_REQ_EE_EMU_MASK) >> ESC_AL_EVT_REQ_EE_EMU_SHIFT)
+
+/*
+ * SM_ACT (RO)
+ *
+ * SyncManager activation register
+ * (SyncManager register offset 0x6) changed:
+ * 0:No change in any SyncManager
+ * 1:At least one SyncManager changed
+ * (Bit is cleared by reading SyncManager
+ * Activation registers 0x0806 etc. from PDI)
+ */
+#define ESC_AL_EVT_REQ_SM_ACT_MASK (0x10U)
+#define ESC_AL_EVT_REQ_SM_ACT_SHIFT (4U)
+#define ESC_AL_EVT_REQ_SM_ACT_GET(x) (((uint32_t)(x) & ESC_AL_EVT_REQ_SM_ACT_MASK) >> ESC_AL_EVT_REQ_SM_ACT_SHIFT)
+
+/*
+ * ST_DC_SYNC1 (RO)
+ *
+ * State of DC SYNC1 (if register
+ * 0x0151[7]=1):
+ * (Bit is cleared by reading of SYNC1 status
+ * 0x098F from PDI, use only in Acknowledge
+ * mode)
+ */
+#define ESC_AL_EVT_REQ_ST_DC_SYNC1_MASK (0x8U)
+#define ESC_AL_EVT_REQ_ST_DC_SYNC1_SHIFT (3U)
+#define ESC_AL_EVT_REQ_ST_DC_SYNC1_GET(x) (((uint32_t)(x) & ESC_AL_EVT_REQ_ST_DC_SYNC1_MASK) >> ESC_AL_EVT_REQ_ST_DC_SYNC1_SHIFT)
+
+/*
+ * ST_DC_SYNC0 (RO)
+ *
+ * State of DC SYNC0 (if register
+ * 0x0151[3]=1):
+ * (Bit is cleared by reading SYNC0 status
+ * 0x098E from PDI, use only in Acknowledge
+ * mode)
+ */
+#define ESC_AL_EVT_REQ_ST_DC_SYNC0_MASK (0x4U)
+#define ESC_AL_EVT_REQ_ST_DC_SYNC0_SHIFT (2U)
+#define ESC_AL_EVT_REQ_ST_DC_SYNC0_GET(x) (((uint32_t)(x) & ESC_AL_EVT_REQ_ST_DC_SYNC0_MASK) >> ESC_AL_EVT_REQ_ST_DC_SYNC0_SHIFT)
+
+/*
+ * DCL_EVT (RO)
+ *
+ * DC Latch event:
+ * 0:No change on DC Latch Inputs
+ * 1:At least one change on DC Latch Inputs
+ * (Bit is cleared by reading DC Latch event
+ * times from PDI, so that Latch 0/1 Status
+ * 0x09AE:0x09AF indicates no event. Available
+ * if Latch Unit is PDI-controlled)
+ */
+#define ESC_AL_EVT_REQ_DCL_EVT_MASK (0x2U)
+#define ESC_AL_EVT_REQ_DCL_EVT_SHIFT (1U)
+#define ESC_AL_EVT_REQ_DCL_EVT_GET(x) (((uint32_t)(x) & ESC_AL_EVT_REQ_DCL_EVT_MASK) >> ESC_AL_EVT_REQ_DCL_EVT_SHIFT)
+
+/*
+ * ALC_EVT (RO)
+ *
+ * AL Control event:
+ * 0:No AL Control Register change
+ * 1:AL Control Register has been written3
+ * (Bit is cleared by reading AL Control register
+ * 0x0120:0x0121 from PDI)
+ */
+#define ESC_AL_EVT_REQ_ALC_EVT_MASK (0x1U)
+#define ESC_AL_EVT_REQ_ALC_EVT_SHIFT (0U)
+#define ESC_AL_EVT_REQ_ALC_EVT_GET(x) (((uint32_t)(x) & ESC_AL_EVT_REQ_ALC_EVT_MASK) >> ESC_AL_EVT_REQ_ALC_EVT_SHIFT)
+
+/* Bitfield definition for register array: RX_ERR_CNT */
+/*
+ * RX_ERR (RO)
+ *
+ * RX Error counter of Port y (counting is
+ * stopped when 0xFF is reached).
+ */
+#define ESC_RX_ERR_CNT_RX_ERR_MASK (0xFF00U)
+#define ESC_RX_ERR_CNT_RX_ERR_SHIFT (8U)
+#define ESC_RX_ERR_CNT_RX_ERR_GET(x) (((uint16_t)(x) & ESC_RX_ERR_CNT_RX_ERR_MASK) >> ESC_RX_ERR_CNT_RX_ERR_SHIFT)
+
+/*
+ * IVD_FRM (RO)
+ *
+ * Invalid frame counter of Port y (counting is
+ * stopped when 0xFF is reached).
+ */
+#define ESC_RX_ERR_CNT_IVD_FRM_MASK (0xFFU)
+#define ESC_RX_ERR_CNT_IVD_FRM_SHIFT (0U)
+#define ESC_RX_ERR_CNT_IVD_FRM_GET(x) (((uint16_t)(x) & ESC_RX_ERR_CNT_IVD_FRM_MASK) >> ESC_RX_ERR_CNT_IVD_FRM_SHIFT)
+
+/* Bitfield definition for register array: FWD_RX_ERR_CNT */
+/*
+ * ERR_CNT (RO)
+ *
+ * Forwarded error counter of Port y (counting is
+ * stopped when 0xFF is reached).
+ */
+#define ESC_FWD_RX_ERR_CNT_ERR_CNT_MASK (0xFFU)
+#define ESC_FWD_RX_ERR_CNT_ERR_CNT_SHIFT (0U)
+#define ESC_FWD_RX_ERR_CNT_ERR_CNT_GET(x) (((uint8_t)(x) & ESC_FWD_RX_ERR_CNT_ERR_CNT_MASK) >> ESC_FWD_RX_ERR_CNT_ERR_CNT_SHIFT)
+
+/* Bitfield definition for register: ECAT_PU_ERR_CNT */
+/*
+ * CNT (RO)
+ *
+ * ECAT Processing Unit error counter
+ * (counting is stopped when 0xFF is reached).
+ * Counts errors of frames passing the
+ * Processing Unit.
+ */
+#define ESC_ECAT_PU_ERR_CNT_CNT_MASK (0xFFU)
+#define ESC_ECAT_PU_ERR_CNT_CNT_SHIFT (0U)
+#define ESC_ECAT_PU_ERR_CNT_CNT_GET(x) (((uint8_t)(x) & ESC_ECAT_PU_ERR_CNT_CNT_MASK) >> ESC_ECAT_PU_ERR_CNT_CNT_SHIFT)
+
+/* Bitfield definition for register: PDI_ERR_CNT */
+/*
+ * CNT (RO)
+ *
+ * PDI Error counter (counting is stopped when
+ * 0xFF is reached). Counts if a PDI access has
+ * an interface error.
+ */
+#define ESC_PDI_ERR_CNT_CNT_MASK (0xFFU)
+#define ESC_PDI_ERR_CNT_CNT_SHIFT (0U)
+#define ESC_PDI_ERR_CNT_CNT_GET(x) (((uint8_t)(x) & ESC_PDI_ERR_CNT_CNT_MASK) >> ESC_PDI_ERR_CNT_CNT_SHIFT)
+
+/* Bitfield definition for register array: LOST_LINK_CNT */
+/*
+ * CNT (RO)
+ *
+ * Lost Link counter of Port y (counting is
+ * stopped when 0xff is reached). Counts only if
+ * port is open and loop is Auto.
+ */
+#define ESC_LOST_LINK_CNT_CNT_MASK (0xFFU)
+#define ESC_LOST_LINK_CNT_CNT_SHIFT (0U)
+#define ESC_LOST_LINK_CNT_CNT_GET(x) (((uint8_t)(x) & ESC_LOST_LINK_CNT_CNT_MASK) >> ESC_LOST_LINK_CNT_CNT_SHIFT)
+
+/* Bitfield definition for register: WDG_DIV */
+/*
+ * DIV (RO)
+ *
+ * Watchdog divider:Number of 25 MHz tics
+ * (minus 2) that represent the basic watchdog
+ * increment. (Default value is 100µs = 2498)
+ */
+#define ESC_WDG_DIV_DIV_MASK (0xFFFFU)
+#define ESC_WDG_DIV_DIV_SHIFT (0U)
+#define ESC_WDG_DIV_DIV_GET(x) (((uint16_t)(x) & ESC_WDG_DIV_DIV_MASK) >> ESC_WDG_DIV_DIV_SHIFT)
+
+/* Bitfield definition for register: WDG_TIME_PDI */
+/*
+ * TIME (RO)
+ *
+ * Watchdog Time PDI:number of basic
+ * watchdog increments
+ * (Default value with Watchdog divider 100µs
+ * means 100ms Watchdog)
+ */
+#define ESC_WDG_TIME_PDI_TIME_MASK (0xFFFFU)
+#define ESC_WDG_TIME_PDI_TIME_SHIFT (0U)
+#define ESC_WDG_TIME_PDI_TIME_GET(x) (((uint16_t)(x) & ESC_WDG_TIME_PDI_TIME_MASK) >> ESC_WDG_TIME_PDI_TIME_SHIFT)
+
+/* Bitfield definition for register: WDG_TIME_PDAT */
+/*
+ * TIME (RO)
+ *
+ * Watchdog Time Process Data:number of
+ * basic watchdog increments
+ * (Default value with Watchdog divider 100µs
+ * means 100ms Watchdog)
+ */
+#define ESC_WDG_TIME_PDAT_TIME_MASK (0xFFFFU)
+#define ESC_WDG_TIME_PDAT_TIME_SHIFT (0U)
+#define ESC_WDG_TIME_PDAT_TIME_GET(x) (((uint16_t)(x) & ESC_WDG_TIME_PDAT_TIME_MASK) >> ESC_WDG_TIME_PDAT_TIME_SHIFT)
+
+/* Bitfield definition for register: WDG_STAT_PDAT */
+/*
+ * ST (RW)
+ *
+ * Watchdog Status of Process Data (triggered
+ * by SyncManagers)
+ * 0:Watchdog Process Data expired
+ * 1:Watchdog Process Data is active or
+ * disabled
+ */
+#define ESC_WDG_STAT_PDAT_ST_MASK (0x1U)
+#define ESC_WDG_STAT_PDAT_ST_SHIFT (0U)
+#define ESC_WDG_STAT_PDAT_ST_SET(x) (((uint16_t)(x) << ESC_WDG_STAT_PDAT_ST_SHIFT) & ESC_WDG_STAT_PDAT_ST_MASK)
+#define ESC_WDG_STAT_PDAT_ST_GET(x) (((uint16_t)(x) & ESC_WDG_STAT_PDAT_ST_MASK) >> ESC_WDG_STAT_PDAT_ST_SHIFT)
+
+/* Bitfield definition for register: WDG_CNT_PDAT */
+/*
+ * CNT (RO)
+ *
+ * Watchdog Counter Process Data (counting is
+ * stopped when 0xFF is reached). Counts if
+ * Process Data Watchdog expires.
+ */
+#define ESC_WDG_CNT_PDAT_CNT_MASK (0xFFU)
+#define ESC_WDG_CNT_PDAT_CNT_SHIFT (0U)
+#define ESC_WDG_CNT_PDAT_CNT_GET(x) (((uint8_t)(x) & ESC_WDG_CNT_PDAT_CNT_MASK) >> ESC_WDG_CNT_PDAT_CNT_SHIFT)
+
+/* Bitfield definition for register: WDG_CNT_PDI */
+/*
+ * CNT (RO)
+ *
+ * Watchdog PDI counter (counting is stopped
+ * when 0xFF is reached). Counts if PDI
+ * Watchdog expires.
+ */
+#define ESC_WDG_CNT_PDI_CNT_MASK (0xFFU)
+#define ESC_WDG_CNT_PDI_CNT_SHIFT (0U)
+#define ESC_WDG_CNT_PDI_CNT_GET(x) (((uint8_t)(x) & ESC_WDG_CNT_PDI_CNT_MASK) >> ESC_WDG_CNT_PDI_CNT_SHIFT)
+
+/* Bitfield definition for register: EEPROM_CFG */
+/*
+ * FORCE_ECAT (RO)
+ *
+ * Force ECAT access:
+ * 0:Do not change Bit 0x0501[0]
+ * 1:Reset Bit 0x0501[0] to 0
+ */
+#define ESC_EEPROM_CFG_FORCE_ECAT_MASK (0x2U)
+#define ESC_EEPROM_CFG_FORCE_ECAT_SHIFT (1U)
+#define ESC_EEPROM_CFG_FORCE_ECAT_GET(x) (((uint8_t)(x) & ESC_EEPROM_CFG_FORCE_ECAT_MASK) >> ESC_EEPROM_CFG_FORCE_ECAT_SHIFT)
+
+/*
+ * PDI (RO)
+ *
+ * EEPROM control is offered to PDI:
+ * 0:no
+ * 1:yes (PDI has EEPROM control)
+ */
+#define ESC_EEPROM_CFG_PDI_MASK (0x1U)
+#define ESC_EEPROM_CFG_PDI_SHIFT (0U)
+#define ESC_EEPROM_CFG_PDI_GET(x) (((uint8_t)(x) & ESC_EEPROM_CFG_PDI_MASK) >> ESC_EEPROM_CFG_PDI_SHIFT)
+
+/* Bitfield definition for register: EEPROM_PDI_ACC_STAT */
+/*
+ * ACCESS (RW)
+ *
+ * Access to EEPROM:
+ * 0:PDI releases EEPROM access
+ * 1:PDI takes EEPROM access (PDI has
+ * EEPROM control)
+ */
+#define ESC_EEPROM_PDI_ACC_STAT_ACCESS_MASK (0x1U)
+#define ESC_EEPROM_PDI_ACC_STAT_ACCESS_SHIFT (0U)
+#define ESC_EEPROM_PDI_ACC_STAT_ACCESS_SET(x) (((uint8_t)(x) << ESC_EEPROM_PDI_ACC_STAT_ACCESS_SHIFT) & ESC_EEPROM_PDI_ACC_STAT_ACCESS_MASK)
+#define ESC_EEPROM_PDI_ACC_STAT_ACCESS_GET(x) (((uint8_t)(x) & ESC_EEPROM_PDI_ACC_STAT_ACCESS_MASK) >> ESC_EEPROM_PDI_ACC_STAT_ACCESS_SHIFT)
+
+/* Bitfield definition for register: EEPROM_CTRL_STAT */
+/*
+ * BUSY (RO)
+ *
+ * Busy:
+ * 0:EEPROM Interface is idle
+ * 1:EEPROM Interface is busy
+ */
+#define ESC_EEPROM_CTRL_STAT_BUSY_MASK (0x8000U)
+#define ESC_EEPROM_CTRL_STAT_BUSY_SHIFT (15U)
+#define ESC_EEPROM_CTRL_STAT_BUSY_GET(x) (((uint16_t)(x) & ESC_EEPROM_CTRL_STAT_BUSY_MASK) >> ESC_EEPROM_CTRL_STAT_BUSY_SHIFT)
+
+/*
+ * ERR_WEN (RO)
+ *
+ * Error Write Enable*3
+ * :
+ * 0:No error
+ * 1:Write Command without Write enable
+ */
+#define ESC_EEPROM_CTRL_STAT_ERR_WEN_MASK (0x4000U)
+#define ESC_EEPROM_CTRL_STAT_ERR_WEN_SHIFT (14U)
+#define ESC_EEPROM_CTRL_STAT_ERR_WEN_GET(x) (((uint16_t)(x) & ESC_EEPROM_CTRL_STAT_ERR_WEN_MASK) >> ESC_EEPROM_CTRL_STAT_ERR_WEN_SHIFT)
+
+/*
+ * ERR_ACK_CMD (RW)
+ *
+ * Error Acknowledge/Command*3
+ * :
+ * 0:No error
+ * 1:Missing EEPROM acknowledge or invalid
+ * command
+ * EEPROM emulation only:PDI writes 1 if a temporary
+ * failure has occurred.
+ */
+#define ESC_EEPROM_CTRL_STAT_ERR_ACK_CMD_MASK (0x2000U)
+#define ESC_EEPROM_CTRL_STAT_ERR_ACK_CMD_SHIFT (13U)
+#define ESC_EEPROM_CTRL_STAT_ERR_ACK_CMD_SET(x) (((uint16_t)(x) << ESC_EEPROM_CTRL_STAT_ERR_ACK_CMD_SHIFT) & ESC_EEPROM_CTRL_STAT_ERR_ACK_CMD_MASK)
+#define ESC_EEPROM_CTRL_STAT_ERR_ACK_CMD_GET(x) (((uint16_t)(x) & ESC_EEPROM_CTRL_STAT_ERR_ACK_CMD_MASK) >> ESC_EEPROM_CTRL_STAT_ERR_ACK_CMD_SHIFT)
+
+/*
+ * EE_LDS (RO)
+ *
+ * EEPROM loading status:
+ * 0:EEPROM loaded, device information ok
+ * 1:EEPROM not loaded, device information not
+ * available (EEPROM loading in progress or
+ * finished with a failure)
+ */
+#define ESC_EEPROM_CTRL_STAT_EE_LDS_MASK (0x1000U)
+#define ESC_EEPROM_CTRL_STAT_EE_LDS_SHIFT (12U)
+#define ESC_EEPROM_CTRL_STAT_EE_LDS_GET(x) (((uint16_t)(x) & ESC_EEPROM_CTRL_STAT_EE_LDS_MASK) >> ESC_EEPROM_CTRL_STAT_EE_LDS_SHIFT)
+
+/*
+ * CKSM_ERR (RW)
+ *
+ * Checksum Error in ESC Configuration Area:
+ * 0:Checksum ok
+ * 1:Checksum error
+ * EEPROM emulation for IP Core only:PDI writes 1 if a
+ * CRC failure has occurred for a reload command.
+ */
+#define ESC_EEPROM_CTRL_STAT_CKSM_ERR_MASK (0x800U)
+#define ESC_EEPROM_CTRL_STAT_CKSM_ERR_SHIFT (11U)
+#define ESC_EEPROM_CTRL_STAT_CKSM_ERR_SET(x) (((uint16_t)(x) << ESC_EEPROM_CTRL_STAT_CKSM_ERR_SHIFT) & ESC_EEPROM_CTRL_STAT_CKSM_ERR_MASK)
+#define ESC_EEPROM_CTRL_STAT_CKSM_ERR_GET(x) (((uint16_t)(x) & ESC_EEPROM_CTRL_STAT_CKSM_ERR_MASK) >> ESC_EEPROM_CTRL_STAT_CKSM_ERR_SHIFT)
+
+/*
+ * CMD (RW)
+ *
+ * Command register*2:
+ * Write:Initiate command.
+ * Read:Currently executed command
+ * Commands:
+ * 000:No command/EEPROM idle (clear error bits)
+ * 001:Read
+ * 010:Write
+ * 100:Reload
+ * Others:Reserved/invalid commands (do not issue)
+ * EEPROM emulation only:after execution, PDI writes
+ * command value to indicate operation is ready.
+ */
+#define ESC_EEPROM_CTRL_STAT_CMD_MASK (0x700U)
+#define ESC_EEPROM_CTRL_STAT_CMD_SHIFT (8U)
+#define ESC_EEPROM_CTRL_STAT_CMD_SET(x) (((uint16_t)(x) << ESC_EEPROM_CTRL_STAT_CMD_SHIFT) & ESC_EEPROM_CTRL_STAT_CMD_MASK)
+#define ESC_EEPROM_CTRL_STAT_CMD_GET(x) (((uint16_t)(x) & ESC_EEPROM_CTRL_STAT_CMD_MASK) >> ESC_EEPROM_CTRL_STAT_CMD_SHIFT)
+
+/*
+ * EE_ALGM (RO)
+ *
+ * Selected EEPROM Algorithm:
+ * 0:1 address byte (1Kbit – 16Kbit EEPROMs)
+ * 1:2 address bytes (32Kbit – 4 Mbit EEPROMs)
+ */
+#define ESC_EEPROM_CTRL_STAT_EE_ALGM_MASK (0x80U)
+#define ESC_EEPROM_CTRL_STAT_EE_ALGM_SHIFT (7U)
+#define ESC_EEPROM_CTRL_STAT_EE_ALGM_GET(x) (((uint16_t)(x) & ESC_EEPROM_CTRL_STAT_EE_ALGM_MASK) >> ESC_EEPROM_CTRL_STAT_EE_ALGM_SHIFT)
+
+/*
+ * NUM_RD_BYTE (RO)
+ *
+ * Supported number of EEPROM read bytes:
+ * 0:4 Bytes
+ * 1:8 Bytes
+ */
+#define ESC_EEPROM_CTRL_STAT_NUM_RD_BYTE_MASK (0x40U)
+#define ESC_EEPROM_CTRL_STAT_NUM_RD_BYTE_SHIFT (6U)
+#define ESC_EEPROM_CTRL_STAT_NUM_RD_BYTE_GET(x) (((uint16_t)(x) & ESC_EEPROM_CTRL_STAT_NUM_RD_BYTE_MASK) >> ESC_EEPROM_CTRL_STAT_NUM_RD_BYTE_SHIFT)
+
+/*
+ * EE_EMU (RO)
+ *
+ * EPROM emulation:
+ * 0:Normal operation (I²C interface used)
+ * 1:PDI emulates EEPROM (I²C not used)
+ */
+#define ESC_EEPROM_CTRL_STAT_EE_EMU_MASK (0x20U)
+#define ESC_EEPROM_CTRL_STAT_EE_EMU_SHIFT (5U)
+#define ESC_EEPROM_CTRL_STAT_EE_EMU_GET(x) (((uint16_t)(x) & ESC_EEPROM_CTRL_STAT_EE_EMU_MASK) >> ESC_EEPROM_CTRL_STAT_EE_EMU_SHIFT)
+
+/*
+ * ECAT_WEN (RO)
+ *
+ * ECAT write enable*2
+ * :
+ * 0:Write requests are disabled
+ * 1:Write requests are enabled
+ * This bit is always 1 if PDI has EEPROM control.
+ */
+#define ESC_EEPROM_CTRL_STAT_ECAT_WEN_MASK (0x1U)
+#define ESC_EEPROM_CTRL_STAT_ECAT_WEN_SHIFT (0U)
+#define ESC_EEPROM_CTRL_STAT_ECAT_WEN_GET(x) (((uint16_t)(x) & ESC_EEPROM_CTRL_STAT_ECAT_WEN_MASK) >> ESC_EEPROM_CTRL_STAT_ECAT_WEN_SHIFT)
+
+/* Bitfield definition for register: EEPROM_ADDR */
+/*
+ * ADDR (RW)
+ *
+ * EEPROM Address
+ * 0:First word (= 16 bit)
+ * 1:Second word
+ * …
+ * Actually used EEPROM Address bits:
+ * 9-0: EEPROM size up to 16 Kbit
+ * 17-0: EEPROM size 32 Kbit – 4 Mbit
+ * 31-0: EEPROM Emulation
+ */
+#define ESC_EEPROM_ADDR_ADDR_MASK (0xFFFFFFFFUL)
+#define ESC_EEPROM_ADDR_ADDR_SHIFT (0U)
+#define ESC_EEPROM_ADDR_ADDR_SET(x) (((uint32_t)(x) << ESC_EEPROM_ADDR_ADDR_SHIFT) & ESC_EEPROM_ADDR_ADDR_MASK)
+#define ESC_EEPROM_ADDR_ADDR_GET(x) (((uint32_t)(x) & ESC_EEPROM_ADDR_ADDR_MASK) >> ESC_EEPROM_ADDR_ADDR_SHIFT)
+
+/* Bitfield definition for register: EEPROM_DATA */
+/*
+ * HI (RW)
+ *
+ * EEPROM Read data (data read from
+ * EEPROM, higher bytes)
+ */
+#define ESC_EEPROM_DATA_HI_MASK (0xFFFFFFFFFFFF0000ULL)
+#define ESC_EEPROM_DATA_HI_SHIFT (16U)
+#define ESC_EEPROM_DATA_HI_SET(x) (((uint64_t)(x) << ESC_EEPROM_DATA_HI_SHIFT) & ESC_EEPROM_DATA_HI_MASK)
+#define ESC_EEPROM_DATA_HI_GET(x) (((uint64_t)(x) & ESC_EEPROM_DATA_HI_MASK) >> ESC_EEPROM_DATA_HI_SHIFT)
+
+/*
+ * LO (RW)
+ *
+ * EEPROM Write data (data to be written to
+ * EEPROM) or
+ * EEPROM Read data (data read from
+ * EEPROM, lower bytes)
+ */
+#define ESC_EEPROM_DATA_LO_MASK (0xFFFFU)
+#define ESC_EEPROM_DATA_LO_SHIFT (0U)
+#define ESC_EEPROM_DATA_LO_SET(x) (((uint64_t)(x) << ESC_EEPROM_DATA_LO_SHIFT) & ESC_EEPROM_DATA_LO_MASK)
+#define ESC_EEPROM_DATA_LO_GET(x) (((uint64_t)(x) & ESC_EEPROM_DATA_LO_MASK) >> ESC_EEPROM_DATA_LO_SHIFT)
+
+/* Bitfield definition for register: MII_MNG_CS */
+/*
+ * BUSY (RO)
+ *
+ * Busy:
+ * 0:MII Management Interface is idle
+ * 1:MII Management Interface is busy
+ */
+#define ESC_MII_MNG_CS_BUSY_MASK (0x8000U)
+#define ESC_MII_MNG_CS_BUSY_SHIFT (15U)
+#define ESC_MII_MNG_CS_BUSY_GET(x) (((uint16_t)(x) & ESC_MII_MNG_CS_BUSY_MASK) >> ESC_MII_MNG_CS_BUSY_SHIFT)
+
+/*
+ * CMD_ERR (RO)
+ *
+ * Command error:
+ * 0:Last Command was successful
+ * 1:Invalid command or write command
+ * without Write Enable
+ * Cleared by executing a valid command or by
+ * writing “00” to Command register bits [9:8].
+ */
+#define ESC_MII_MNG_CS_CMD_ERR_MASK (0x4000U)
+#define ESC_MII_MNG_CS_CMD_ERR_SHIFT (14U)
+#define ESC_MII_MNG_CS_CMD_ERR_GET(x) (((uint16_t)(x) & ESC_MII_MNG_CS_CMD_ERR_MASK) >> ESC_MII_MNG_CS_CMD_ERR_SHIFT)
+
+/*
+ * RD_ERR (RO)
+ *
+ * Read error:
+ * 0:No read error
+ * 1:Read error occurred (PHY or register
+ * not available)
+ * Cleared by writing to register 0x0511
+ */
+#define ESC_MII_MNG_CS_RD_ERR_MASK (0x2000U)
+#define ESC_MII_MNG_CS_RD_ERR_SHIFT (13U)
+#define ESC_MII_MNG_CS_RD_ERR_GET(x) (((uint16_t)(x) & ESC_MII_MNG_CS_RD_ERR_MASK) >> ESC_MII_MNG_CS_RD_ERR_SHIFT)
+
+/*
+ * CMD (RW)
+ *
+ * Command register*:
+ * Write:Initiate command.
+ * Read:Currently executed command
+ * 00:No command/MI idle (clear error bits)
+ * 01:Read
+ * 10:Write
+ * Others:Reserved/invalid command (do not
+ * issue)
+ */
+#define ESC_MII_MNG_CS_CMD_MASK (0x300U)
+#define ESC_MII_MNG_CS_CMD_SHIFT (8U)
+#define ESC_MII_MNG_CS_CMD_SET(x) (((uint16_t)(x) << ESC_MII_MNG_CS_CMD_SHIFT) & ESC_MII_MNG_CS_CMD_MASK)
+#define ESC_MII_MNG_CS_CMD_GET(x) (((uint16_t)(x) & ESC_MII_MNG_CS_CMD_MASK) >> ESC_MII_MNG_CS_CMD_SHIFT)
+
+/*
+ * PHY_ADDR (RO)
+ *
+ * PHY address of port 0
+ * (this is equal to the PHY address offset, if the
+ * PHY addresses are consecutive)
+ * IP Core since V3.0.0/3.00c:
+ * Translation 0x0512[7]=0:
+ * Register 0x0510[7:3] shows PHY address of
+ * port 0
+ * Translation 0x0512[7]=1:
+ * Register 0x0510[7:3] shows the PHY address
+ * which will be used for port 0-3 as requested
+ * by 0x0512[4:0] (valid values 0-3)
+ */
+#define ESC_MII_MNG_CS_PHY_ADDR_MASK (0xF8U)
+#define ESC_MII_MNG_CS_PHY_ADDR_SHIFT (3U)
+#define ESC_MII_MNG_CS_PHY_ADDR_GET(x) (((uint16_t)(x) & ESC_MII_MNG_CS_PHY_ADDR_MASK) >> ESC_MII_MNG_CS_PHY_ADDR_SHIFT)
+
+/*
+ * LINK_DC (RO)
+ *
+ * MI link detection and configuration:
+ * 0:Disabled for all ports
+ * 1:Enabled for at least one MII port, refer
+ * to PHY Port Status (0x0518 ff.) for
+ * details
+ */
+#define ESC_MII_MNG_CS_LINK_DC_MASK (0x4U)
+#define ESC_MII_MNG_CS_LINK_DC_SHIFT (2U)
+#define ESC_MII_MNG_CS_LINK_DC_GET(x) (((uint16_t)(x) & ESC_MII_MNG_CS_LINK_DC_MASK) >> ESC_MII_MNG_CS_LINK_DC_SHIFT)
+
+/*
+ * PDI (RO)
+ *
+ * Management Interface can be controlled by
+ * PDI (registers 0x0516-0x0517):
+ * 0:Only ECAT control
+ * 1:PDI control possible
+ */
+#define ESC_MII_MNG_CS_PDI_MASK (0x2U)
+#define ESC_MII_MNG_CS_PDI_SHIFT (1U)
+#define ESC_MII_MNG_CS_PDI_GET(x) (((uint16_t)(x) & ESC_MII_MNG_CS_PDI_MASK) >> ESC_MII_MNG_CS_PDI_SHIFT)
+
+/*
+ * WEN (RO)
+ *
+ * Write enable*:
+ * 0:Write disabled
+ * 1:Write enabled
+ * This bit is always 1 if PDI has MI control.
+ * ET1100-0000/-0001 exception:
+ * Bit is not always 1 if PDI has MI control, and
+ * bit is writable by PDI.
+ */
+#define ESC_MII_MNG_CS_WEN_MASK (0x1U)
+#define ESC_MII_MNG_CS_WEN_SHIFT (0U)
+#define ESC_MII_MNG_CS_WEN_GET(x) (((uint16_t)(x) & ESC_MII_MNG_CS_WEN_MASK) >> ESC_MII_MNG_CS_WEN_SHIFT)
+
+/* Bitfield definition for register: PHY_ADDR */
+/*
+ * SHOW (RW)
+ *
+ * Target PHY Address translation:
+ * 0:Enabled
+ * 1:Disabled
+ * Refer to 0x0512[4:0] and 0x0510[7:3] for
+ * details.
+ */
+#define ESC_PHY_ADDR_SHOW_MASK (0x80U)
+#define ESC_PHY_ADDR_SHOW_SHIFT (7U)
+#define ESC_PHY_ADDR_SHOW_SET(x) (((uint8_t)(x) << ESC_PHY_ADDR_SHOW_SHIFT) & ESC_PHY_ADDR_SHOW_MASK)
+#define ESC_PHY_ADDR_SHOW_GET(x) (((uint8_t)(x) & ESC_PHY_ADDR_SHOW_MASK) >> ESC_PHY_ADDR_SHOW_SHIFT)
+
+/*
+ * ADDR (RW)
+ *
+ * Target PHY Address
+ * Translation 0x0512[7]=0:
+ * 0-3:Target PHY Addresses 0-3 are used
+ * to access the PHYs at port 0-3, when
+ * the PHY addresses are properly
+ * configured
+ * 4-31:The configured PHY address of port 0
+ * (PHY address offset) is added to the
+ * Target PHY Address values 4-31
+ * when accessing a PHY
+ * Translation 0x0512[7]=1:
+ * 0-31:Target PHY Addresses is used when
+ * accessing a PHY without translation
+ */
+#define ESC_PHY_ADDR_ADDR_MASK (0x1FU)
+#define ESC_PHY_ADDR_ADDR_SHIFT (0U)
+#define ESC_PHY_ADDR_ADDR_SET(x) (((uint8_t)(x) << ESC_PHY_ADDR_ADDR_SHIFT) & ESC_PHY_ADDR_ADDR_MASK)
+#define ESC_PHY_ADDR_ADDR_GET(x) (((uint8_t)(x) & ESC_PHY_ADDR_ADDR_MASK) >> ESC_PHY_ADDR_ADDR_SHIFT)
+
+/* Bitfield definition for register: PHY_REG_ADDR */
+/*
+ * ADDR (RW)
+ *
+ * Address of PHY Register that shall be
+ * read/written
+ */
+#define ESC_PHY_REG_ADDR_ADDR_MASK (0x1FU)
+#define ESC_PHY_REG_ADDR_ADDR_SHIFT (0U)
+#define ESC_PHY_REG_ADDR_ADDR_SET(x) (((uint8_t)(x) << ESC_PHY_REG_ADDR_ADDR_SHIFT) & ESC_PHY_REG_ADDR_ADDR_MASK)
+#define ESC_PHY_REG_ADDR_ADDR_GET(x) (((uint8_t)(x) & ESC_PHY_REG_ADDR_ADDR_MASK) >> ESC_PHY_REG_ADDR_ADDR_SHIFT)
+
+/* Bitfield definition for register: PHY_DATA */
+/*
+ * DATA (RW)
+ *
+ * PHY Read/Write Data
+ */
+#define ESC_PHY_DATA_DATA_MASK (0xFFFFU)
+#define ESC_PHY_DATA_DATA_SHIFT (0U)
+#define ESC_PHY_DATA_DATA_SET(x) (((uint16_t)(x) << ESC_PHY_DATA_DATA_SHIFT) & ESC_PHY_DATA_DATA_MASK)
+#define ESC_PHY_DATA_DATA_GET(x) (((uint16_t)(x) & ESC_PHY_DATA_DATA_MASK) >> ESC_PHY_DATA_DATA_SHIFT)
+
+/* Bitfield definition for register: MIIM_ECAT_ACC_STAT */
+/*
+ * ACC (RO)
+ *
+ * Access to MII management:
+ * 0:ECAT enables PDI takeover of MII
+ * management interface
+ * 1:ECAT claims exclusive access to MII
+ * management interface
+ */
+#define ESC_MIIM_ECAT_ACC_STAT_ACC_MASK (0x1U)
+#define ESC_MIIM_ECAT_ACC_STAT_ACC_SHIFT (0U)
+#define ESC_MIIM_ECAT_ACC_STAT_ACC_GET(x) (((uint8_t)(x) & ESC_MIIM_ECAT_ACC_STAT_ACC_MASK) >> ESC_MIIM_ECAT_ACC_STAT_ACC_SHIFT)
+
+/* Bitfield definition for register: MIIM_PDI_ACC_STAT */
+/*
+ * FORCE (RO)
+ *
+ * Force PDI Access State:
+ * 0:Do not change Bit 0x0517[0]
+ * 1:Reset Bit 0x0517[0] to 0
+ */
+#define ESC_MIIM_PDI_ACC_STAT_FORCE_MASK (0x2U)
+#define ESC_MIIM_PDI_ACC_STAT_FORCE_SHIFT (1U)
+#define ESC_MIIM_PDI_ACC_STAT_FORCE_GET(x) (((uint8_t)(x) & ESC_MIIM_PDI_ACC_STAT_FORCE_MASK) >> ESC_MIIM_PDI_ACC_STAT_FORCE_SHIFT)
+
+/*
+ * ACC (RW)
+ *
+ * Access to MII management:
+ * 0:ECAT has access to MII management
+ * 1:PDI has access to MII management
+ */
+#define ESC_MIIM_PDI_ACC_STAT_ACC_MASK (0x1U)
+#define ESC_MIIM_PDI_ACC_STAT_ACC_SHIFT (0U)
+#define ESC_MIIM_PDI_ACC_STAT_ACC_SET(x) (((uint8_t)(x) << ESC_MIIM_PDI_ACC_STAT_ACC_SHIFT) & ESC_MIIM_PDI_ACC_STAT_ACC_MASK)
+#define ESC_MIIM_PDI_ACC_STAT_ACC_GET(x) (((uint8_t)(x) & ESC_MIIM_PDI_ACC_STAT_ACC_MASK) >> ESC_MIIM_PDI_ACC_STAT_ACC_SHIFT)
+
+/* Bitfield definition for register array: PHY_STAT */
+/*
+ * PCU (RW)
+ *
+ * PHY configuration updated:
+ * 0:No update
+ * 1:PHY configuration was updated
+ * Cleared by writing any value to at least one
+ * of the PHY Port y Status registers.
+ */
+#define ESC_PHY_STAT_PCU_MASK (0x20U)
+#define ESC_PHY_STAT_PCU_SHIFT (5U)
+#define ESC_PHY_STAT_PCU_SET(x) (((uint8_t)(x) << ESC_PHY_STAT_PCU_SHIFT) & ESC_PHY_STAT_PCU_MASK)
+#define ESC_PHY_STAT_PCU_GET(x) (((uint8_t)(x) & ESC_PHY_STAT_PCU_MASK) >> ESC_PHY_STAT_PCU_SHIFT)
+
+/*
+ * LPE (RO)
+ *
+ * Link partner error:
+ * 0:No error detected
+ * 1:Link partner error
+ */
+#define ESC_PHY_STAT_LPE_MASK (0x10U)
+#define ESC_PHY_STAT_LPE_SHIFT (4U)
+#define ESC_PHY_STAT_LPE_GET(x) (((uint8_t)(x) & ESC_PHY_STAT_LPE_MASK) >> ESC_PHY_STAT_LPE_SHIFT)
+
+/*
+ * RE (RW)
+ *
+ * Read error:
+ * 0:No read error occurred
+ * 1:A read error has occurred
+ * Cleared by writing any value to at least one
+ * of the PHY Port y Status registers.
+ */
+#define ESC_PHY_STAT_RE_MASK (0x8U)
+#define ESC_PHY_STAT_RE_SHIFT (3U)
+#define ESC_PHY_STAT_RE_SET(x) (((uint8_t)(x) << ESC_PHY_STAT_RE_SHIFT) & ESC_PHY_STAT_RE_MASK)
+#define ESC_PHY_STAT_RE_GET(x) (((uint8_t)(x) & ESC_PHY_STAT_RE_MASK) >> ESC_PHY_STAT_RE_SHIFT)
+
+/*
+ * LSE (RO)
+ *
+ * Link status error:
+ * 0:No error
+ * 1:Link error, link inhibited
+ */
+#define ESC_PHY_STAT_LSE_MASK (0x4U)
+#define ESC_PHY_STAT_LSE_SHIFT (2U)
+#define ESC_PHY_STAT_LSE_GET(x) (((uint8_t)(x) & ESC_PHY_STAT_LSE_MASK) >> ESC_PHY_STAT_LSE_SHIFT)
+
+/*
+ * LS (RO)
+ *
+ * Link status (100 Mbit/s, Full Duplex, Auto
+ * negotiation):
+ * 0:No link
+ * 1:Link detected
+ */
+#define ESC_PHY_STAT_LS_MASK (0x2U)
+#define ESC_PHY_STAT_LS_SHIFT (1U)
+#define ESC_PHY_STAT_LS_GET(x) (((uint8_t)(x) & ESC_PHY_STAT_LS_MASK) >> ESC_PHY_STAT_LS_SHIFT)
+
+/*
+ * PLS (RO)
+ *
+ * Physical link status (PHY status register 1.2):
+ * 0:No physical link
+ * 1:Physical link detected
+ */
+#define ESC_PHY_STAT_PLS_MASK (0x1U)
+#define ESC_PHY_STAT_PLS_SHIFT (0U)
+#define ESC_PHY_STAT_PLS_GET(x) (((uint8_t)(x) & ESC_PHY_STAT_PLS_MASK) >> ESC_PHY_STAT_PLS_SHIFT)
+
+/* Bitfield definition for register of struct array FMMU: LOGIC_START_ADDR */
+/*
+ * ADDR (RO)
+ *
+ * Logical start address within the EtherCAT
+ * Address Space.
+ */
+#define ESC_FMMU_LOGIC_START_ADDR_ADDR_MASK (0xFFFFFFFFUL)
+#define ESC_FMMU_LOGIC_START_ADDR_ADDR_SHIFT (0U)
+#define ESC_FMMU_LOGIC_START_ADDR_ADDR_GET(x) (((uint32_t)(x) & ESC_FMMU_LOGIC_START_ADDR_ADDR_MASK) >> ESC_FMMU_LOGIC_START_ADDR_ADDR_SHIFT)
+
+/* Bitfield definition for register of struct array FMMU: LENGTH */
+/*
+ * OFFSET (RO)
+ *
+ * Offset from the first logical FMMU byte to the
+ * last FMMU byte + 1 (e.g., if two bytes are
+ * used, then this parameter shall contain 2)
+ */
+#define ESC_FMMU_LENGTH_OFFSET_MASK (0xFFFFU)
+#define ESC_FMMU_LENGTH_OFFSET_SHIFT (0U)
+#define ESC_FMMU_LENGTH_OFFSET_GET(x) (((uint16_t)(x) & ESC_FMMU_LENGTH_OFFSET_MASK) >> ESC_FMMU_LENGTH_OFFSET_SHIFT)
+
+/* Bitfield definition for register of struct array FMMU: LOGIC_START_BIT */
+/*
+ * START (RO)
+ *
+ * Logical starting bit that shall be mapped (bits
+ * are counted from least significant bit 0 to
+ * most significant bit 7)
+ */
+#define ESC_FMMU_LOGIC_START_BIT_START_MASK (0x7U)
+#define ESC_FMMU_LOGIC_START_BIT_START_SHIFT (0U)
+#define ESC_FMMU_LOGIC_START_BIT_START_GET(x) (((uint8_t)(x) & ESC_FMMU_LOGIC_START_BIT_START_MASK) >> ESC_FMMU_LOGIC_START_BIT_START_SHIFT)
+
+/* Bitfield definition for register of struct array FMMU: LOGIC_STOP_BIT */
+/*
+ * STOP (RO)
+ *
+ * Last logical bit that shall be mapped (bits are
+ * counted from least significant bit 0 to most
+ * significant bit 7)
+ */
+#define ESC_FMMU_LOGIC_STOP_BIT_STOP_MASK (0x7U)
+#define ESC_FMMU_LOGIC_STOP_BIT_STOP_SHIFT (0U)
+#define ESC_FMMU_LOGIC_STOP_BIT_STOP_GET(x) (((uint8_t)(x) & ESC_FMMU_LOGIC_STOP_BIT_STOP_MASK) >> ESC_FMMU_LOGIC_STOP_BIT_STOP_SHIFT)
+
+/* Bitfield definition for register of struct array FMMU: PHYSICAL_START_ADDR */
+/*
+ * ADDR (RO)
+ *
+ * Physical Start Address (mapped to logical
+ * Start address)
+ */
+#define ESC_FMMU_PHYSICAL_START_ADDR_ADDR_MASK (0xFFFFU)
+#define ESC_FMMU_PHYSICAL_START_ADDR_ADDR_SHIFT (0U)
+#define ESC_FMMU_PHYSICAL_START_ADDR_ADDR_GET(x) (((uint16_t)(x) & ESC_FMMU_PHYSICAL_START_ADDR_ADDR_MASK) >> ESC_FMMU_PHYSICAL_START_ADDR_ADDR_SHIFT)
+
+/* Bitfield definition for register of struct array FMMU: PHYSICAL_START_BIT */
+/*
+ * START (RO)
+ *
+ * Physical starting bit as target of logical start
+ * bit mapping (bits are counted from least
+ * significant bit 0 to most significant bit 7)
+ */
+#define ESC_FMMU_PHYSICAL_START_BIT_START_MASK (0x7U)
+#define ESC_FMMU_PHYSICAL_START_BIT_START_SHIFT (0U)
+#define ESC_FMMU_PHYSICAL_START_BIT_START_GET(x) (((uint8_t)(x) & ESC_FMMU_PHYSICAL_START_BIT_START_MASK) >> ESC_FMMU_PHYSICAL_START_BIT_START_SHIFT)
+
+/* Bitfield definition for register of struct array FMMU: TYPE */
+/*
+ * MAP_WR (RO)
+ *
+ * 0:Ignore mapping for write accesses
+ * 1:Use mapping for write accesses
+ */
+#define ESC_FMMU_TYPE_MAP_WR_MASK (0x2U)
+#define ESC_FMMU_TYPE_MAP_WR_SHIFT (1U)
+#define ESC_FMMU_TYPE_MAP_WR_GET(x) (((uint8_t)(x) & ESC_FMMU_TYPE_MAP_WR_MASK) >> ESC_FMMU_TYPE_MAP_WR_SHIFT)
+
+/*
+ * MAP_RD (RO)
+ *
+ * 0:Ignore mapping for read accesses
+ * 1:Use mapping for read accesses
+ */
+#define ESC_FMMU_TYPE_MAP_RD_MASK (0x1U)
+#define ESC_FMMU_TYPE_MAP_RD_SHIFT (0U)
+#define ESC_FMMU_TYPE_MAP_RD_GET(x) (((uint8_t)(x) & ESC_FMMU_TYPE_MAP_RD_MASK) >> ESC_FMMU_TYPE_MAP_RD_SHIFT)
+
+/* Bitfield definition for register of struct array FMMU: ACTIVATE */
+/*
+ * ACT (RO)
+ *
+ * 0:FMMU deactivated
+ * 1:FMMU activated. FMMU checks
+ * logically addressed blocks to be
+ * mapped according to configured
+ * mapping
+ */
+#define ESC_FMMU_ACTIVATE_ACT_MASK (0x1U)
+#define ESC_FMMU_ACTIVATE_ACT_SHIFT (0U)
+#define ESC_FMMU_ACTIVATE_ACT_GET(x) (((uint8_t)(x) & ESC_FMMU_ACTIVATE_ACT_MASK) >> ESC_FMMU_ACTIVATE_ACT_SHIFT)
+
+/* Bitfield definition for register of struct array SYNCM: PHYSICAL_START_ADDR */
+/*
+ * ADDR (RO)
+ *
+ * First byte that will be handled by
+ * SyncManager
+ */
+#define ESC_SYNCM_PHYSICAL_START_ADDR_ADDR_MASK (0xFFFFU)
+#define ESC_SYNCM_PHYSICAL_START_ADDR_ADDR_SHIFT (0U)
+#define ESC_SYNCM_PHYSICAL_START_ADDR_ADDR_GET(x) (((uint16_t)(x) & ESC_SYNCM_PHYSICAL_START_ADDR_ADDR_MASK) >> ESC_SYNCM_PHYSICAL_START_ADDR_ADDR_SHIFT)
+
+/* Bitfield definition for register of struct array SYNCM: LENGTH */
+/*
+ * LEN (RO)
+ *
+ * Number of bytes assigned to SyncManager
+ * (shall be greater than 1, otherwise
+ * SyncManager is not activated. If set to 1, only
+ * Watchdog Trigger is generated if configured)
+ */
+#define ESC_SYNCM_LENGTH_LEN_MASK (0xFFFFU)
+#define ESC_SYNCM_LENGTH_LEN_SHIFT (0U)
+#define ESC_SYNCM_LENGTH_LEN_GET(x) (((uint16_t)(x) & ESC_SYNCM_LENGTH_LEN_MASK) >> ESC_SYNCM_LENGTH_LEN_SHIFT)
+
+/* Bitfield definition for register of struct array SYNCM: CONTROL */
+/*
+ * WDG_TRG_EN (RO)
+ *
+ * Watchdog Trigger Enable:
+ * 0:Disabled
+ * 1:Enabled
+ */
+#define ESC_SYNCM_CONTROL_WDG_TRG_EN_MASK (0x40U)
+#define ESC_SYNCM_CONTROL_WDG_TRG_EN_SHIFT (6U)
+#define ESC_SYNCM_CONTROL_WDG_TRG_EN_GET(x) (((uint8_t)(x) & ESC_SYNCM_CONTROL_WDG_TRG_EN_MASK) >> ESC_SYNCM_CONTROL_WDG_TRG_EN_SHIFT)
+
+/*
+ * INT_AL (RO)
+ *
+ * Interrupt in AL Event Request Register:
+ * 0:Disabled
+ * 1:Enabled
+ */
+#define ESC_SYNCM_CONTROL_INT_AL_MASK (0x20U)
+#define ESC_SYNCM_CONTROL_INT_AL_SHIFT (5U)
+#define ESC_SYNCM_CONTROL_INT_AL_GET(x) (((uint8_t)(x) & ESC_SYNCM_CONTROL_INT_AL_MASK) >> ESC_SYNCM_CONTROL_INT_AL_SHIFT)
+
+/*
+ * INT_ECAT (RO)
+ *
+ * Interrupt in ECAT Event Request Register:
+ * 0:Disabled
+ * 1:Enabled
+ */
+#define ESC_SYNCM_CONTROL_INT_ECAT_MASK (0x10U)
+#define ESC_SYNCM_CONTROL_INT_ECAT_SHIFT (4U)
+#define ESC_SYNCM_CONTROL_INT_ECAT_GET(x) (((uint8_t)(x) & ESC_SYNCM_CONTROL_INT_ECAT_MASK) >> ESC_SYNCM_CONTROL_INT_ECAT_SHIFT)
+
+/*
+ * DIR (RO)
+ *
+ * Direction:
+ * 00:Read:ECAT read access, PDI write
+ * access.
+ * 01:Write:ECAT write access, PDI read
+ * access.
+ * 10:Reserved
+ * 11:Reserved
+ */
+#define ESC_SYNCM_CONTROL_DIR_MASK (0xCU)
+#define ESC_SYNCM_CONTROL_DIR_SHIFT (2U)
+#define ESC_SYNCM_CONTROL_DIR_GET(x) (((uint8_t)(x) & ESC_SYNCM_CONTROL_DIR_MASK) >> ESC_SYNCM_CONTROL_DIR_SHIFT)
+
+/*
+ * OP_MODE (RO)
+ *
+ * Operation Mode:
+ * 00:Buffered (3 buffer mode)
+ * 01:Reserved
+ * 10:Mailbox (Single buffer mode)
+ * 11:Reserved
+ */
+#define ESC_SYNCM_CONTROL_OP_MODE_MASK (0x3U)
+#define ESC_SYNCM_CONTROL_OP_MODE_SHIFT (0U)
+#define ESC_SYNCM_CONTROL_OP_MODE_GET(x) (((uint8_t)(x) & ESC_SYNCM_CONTROL_OP_MODE_MASK) >> ESC_SYNCM_CONTROL_OP_MODE_SHIFT)
+
+/* Bitfield definition for register of struct array SYNCM: STATUS */
+/*
+ * WB_INUSE (RO)
+ *
+ * Write buffer in use (opened)
+ */
+#define ESC_SYNCM_STATUS_WB_INUSE_MASK (0x80U)
+#define ESC_SYNCM_STATUS_WB_INUSE_SHIFT (7U)
+#define ESC_SYNCM_STATUS_WB_INUSE_GET(x) (((uint8_t)(x) & ESC_SYNCM_STATUS_WB_INUSE_MASK) >> ESC_SYNCM_STATUS_WB_INUSE_SHIFT)
+
+/*
+ * RB_INUSE (RO)
+ *
+ * Read buffer in use (opened)
+ */
+#define ESC_SYNCM_STATUS_RB_INUSE_MASK (0x40U)
+#define ESC_SYNCM_STATUS_RB_INUSE_SHIFT (6U)
+#define ESC_SYNCM_STATUS_RB_INUSE_GET(x) (((uint8_t)(x) & ESC_SYNCM_STATUS_RB_INUSE_MASK) >> ESC_SYNCM_STATUS_RB_INUSE_SHIFT)
+
+/*
+ * BUF_MODE (RO)
+ *
+ * Buffered mode:buffer status (last written
+ * buffer):
+ * 00:1
+ * st buffer
+ * 01:2
+ * nd buffer
+ * 10:3
+ * rd buffer
+ * 11:(no buffer written)
+ * Mailbox mode:reserved
+ */
+#define ESC_SYNCM_STATUS_BUF_MODE_MASK (0x30U)
+#define ESC_SYNCM_STATUS_BUF_MODE_SHIFT (4U)
+#define ESC_SYNCM_STATUS_BUF_MODE_GET(x) (((uint8_t)(x) & ESC_SYNCM_STATUS_BUF_MODE_MASK) >> ESC_SYNCM_STATUS_BUF_MODE_SHIFT)
+
+/*
+ * MBX_MODE (RO)
+ *
+ * Mailbox mode:mailbox status:
+ * 0:Mailbox empty
+ * 1:Mailbox full
+ * Buffered mode:reserved
+ */
+#define ESC_SYNCM_STATUS_MBX_MODE_MASK (0x8U)
+#define ESC_SYNCM_STATUS_MBX_MODE_SHIFT (3U)
+#define ESC_SYNCM_STATUS_MBX_MODE_GET(x) (((uint8_t)(x) & ESC_SYNCM_STATUS_MBX_MODE_MASK) >> ESC_SYNCM_STATUS_MBX_MODE_SHIFT)
+
+/*
+ * INT_RD (RO)
+ *
+ * Interrupt Read:
+ * 1:Interrupt after buffer was completely and
+ * successfully read
+ * 0:Interrupt cleared after first byte of buffer
+ * was written
+ * NOTE:This interrupt is signalled to the writing
+ * side if enabled in the SM Control register
+ */
+#define ESC_SYNCM_STATUS_INT_RD_MASK (0x2U)
+#define ESC_SYNCM_STATUS_INT_RD_SHIFT (1U)
+#define ESC_SYNCM_STATUS_INT_RD_GET(x) (((uint8_t)(x) & ESC_SYNCM_STATUS_INT_RD_MASK) >> ESC_SYNCM_STATUS_INT_RD_SHIFT)
+
+/*
+ * INT_WR (RO)
+ *
+ * Interrupt Write:
+ * 1:Interrupt after buffer was completely and
+ * successfully written
+ * 0:Interrupt cleared after first byte of buffer
+ * was read
+ * NOTE:This interrupt is signalled to the reading
+ * side if enabled in the SM Control register
+ */
+#define ESC_SYNCM_STATUS_INT_WR_MASK (0x1U)
+#define ESC_SYNCM_STATUS_INT_WR_SHIFT (0U)
+#define ESC_SYNCM_STATUS_INT_WR_GET(x) (((uint8_t)(x) & ESC_SYNCM_STATUS_INT_WR_MASK) >> ESC_SYNCM_STATUS_INT_WR_SHIFT)
+
+/* Bitfield definition for register of struct array SYNCM: ACTIVATE */
+/*
+ * LATCH_PDI (RO)
+ *
+ * Latch Event PDI:
+ * 0:No
+ * 1:Generate Latch events when PDI issues
+ * a buffer exchange or when PDI
+ * accesses buffer start address
+ */
+#define ESC_SYNCM_ACTIVATE_LATCH_PDI_MASK (0x80U)
+#define ESC_SYNCM_ACTIVATE_LATCH_PDI_SHIFT (7U)
+#define ESC_SYNCM_ACTIVATE_LATCH_PDI_GET(x) (((uint8_t)(x) & ESC_SYNCM_ACTIVATE_LATCH_PDI_MASK) >> ESC_SYNCM_ACTIVATE_LATCH_PDI_SHIFT)
+
+/*
+ * LATCH_ECAT (RO)
+ *
+ * Latch Event ECAT:
+ * 0:No
+ * 1:Generate Latch event when EtherCAT
+ * master issues a buffer exchange
+ */
+#define ESC_SYNCM_ACTIVATE_LATCH_ECAT_MASK (0x40U)
+#define ESC_SYNCM_ACTIVATE_LATCH_ECAT_SHIFT (6U)
+#define ESC_SYNCM_ACTIVATE_LATCH_ECAT_GET(x) (((uint8_t)(x) & ESC_SYNCM_ACTIVATE_LATCH_ECAT_MASK) >> ESC_SYNCM_ACTIVATE_LATCH_ECAT_SHIFT)
+
+/*
+ * REPEAT (RO)
+ *
+ * Repeat Request:
+ * A toggle of Repeat Request means that a
+ * mailbox retry is needed (primarily used in
+ * conjunction with ECAT Read Mailbox)
+ */
+#define ESC_SYNCM_ACTIVATE_REPEAT_MASK (0x2U)
+#define ESC_SYNCM_ACTIVATE_REPEAT_SHIFT (1U)
+#define ESC_SYNCM_ACTIVATE_REPEAT_GET(x) (((uint8_t)(x) & ESC_SYNCM_ACTIVATE_REPEAT_MASK) >> ESC_SYNCM_ACTIVATE_REPEAT_SHIFT)
+
+/*
+ * EN (RW)
+ *
+ * SyncManager Enable/Disable:
+ * 0:Disable:Access to Memory without
+ * SyncManager control
+ * 1:Enable:SyncManager is active and
+ * controls Memory area set in
+ * configuration
+ */
+#define ESC_SYNCM_ACTIVATE_EN_MASK (0x1U)
+#define ESC_SYNCM_ACTIVATE_EN_SHIFT (0U)
+#define ESC_SYNCM_ACTIVATE_EN_SET(x) (((uint8_t)(x) << ESC_SYNCM_ACTIVATE_EN_SHIFT) & ESC_SYNCM_ACTIVATE_EN_MASK)
+#define ESC_SYNCM_ACTIVATE_EN_GET(x) (((uint8_t)(x) & ESC_SYNCM_ACTIVATE_EN_MASK) >> ESC_SYNCM_ACTIVATE_EN_SHIFT)
+
+/* Bitfield definition for register of struct array SYNCM: PDI_CTRL */
+/*
+ * REPEAT_ACK (RW)
+ *
+ * Repeat Ack:
+ * If this is set to the same value as that set by
+ * Repeat Request, the PDI acknowledges the
+ * execution of a previous set Repeat request.
+ */
+#define ESC_SYNCM_PDI_CTRL_REPEAT_ACK_MASK (0x2U)
+#define ESC_SYNCM_PDI_CTRL_REPEAT_ACK_SHIFT (1U)
+#define ESC_SYNCM_PDI_CTRL_REPEAT_ACK_SET(x) (((uint8_t)(x) << ESC_SYNCM_PDI_CTRL_REPEAT_ACK_SHIFT) & ESC_SYNCM_PDI_CTRL_REPEAT_ACK_MASK)
+#define ESC_SYNCM_PDI_CTRL_REPEAT_ACK_GET(x) (((uint8_t)(x) & ESC_SYNCM_PDI_CTRL_REPEAT_ACK_MASK) >> ESC_SYNCM_PDI_CTRL_REPEAT_ACK_SHIFT)
+
+/*
+ * DEACT (RW)
+ *
+ * Deactivate SyncManager:
+ * Read:
+ * 0:Normal operation, SyncManager
+ * activated.
+ * 1:SyncManager deactivated and reset.
+ * SyncManager locks access to Memory
+ * area.
+ * Write:
+ * 0:Activate SyncManager
+ * 1:Request SyncManager deactivation
+ * NOTE:Writing 1 is delayed until the end of the
+ * frame, which is currently processed.
+ */
+#define ESC_SYNCM_PDI_CTRL_DEACT_MASK (0x1U)
+#define ESC_SYNCM_PDI_CTRL_DEACT_SHIFT (0U)
+#define ESC_SYNCM_PDI_CTRL_DEACT_SET(x) (((uint8_t)(x) << ESC_SYNCM_PDI_CTRL_DEACT_SHIFT) & ESC_SYNCM_PDI_CTRL_DEACT_MASK)
+#define ESC_SYNCM_PDI_CTRL_DEACT_GET(x) (((uint8_t)(x) & ESC_SYNCM_PDI_CTRL_DEACT_MASK) >> ESC_SYNCM_PDI_CTRL_DEACT_SHIFT)
+
+/* Bitfield definition for register array: RCV_TIME */
+/*
+ * LT (RO)
+ *
+ * Local time at the beginning of the last receive
+ * frame containing a write access to register
+ * 0x0900.
+ */
+#define ESC_RCV_TIME_LT_MASK (0xFFFFFF00UL)
+#define ESC_RCV_TIME_LT_SHIFT (8U)
+#define ESC_RCV_TIME_LT_GET(x) (((uint32_t)(x) & ESC_RCV_TIME_LT_MASK) >> ESC_RCV_TIME_LT_SHIFT)
+
+/*
+ * REQ (RO)
+ *
+ * Write:
+ * A write access to register 0x0900 with
+ * BWR or FPWR latches the local time at
+ * the beginning of the receive frame (start
+ * first bit of preamble) at each port.
+ * Write (ESC20, ET1200 exception):
+ * A write access latches the local time at
+ * the beginning of the receive frame at
+ * port 0. It enables the time stamping at
+ * the other ports.
+ * Read:
+ * Local time at the beginning of the last
+ * receive frame containing a write access
+ * to this register.
+ * NOTE:FPWR requires an address match for
+ * accessing this register like any FPWR command.
+ * All write commands with address match will
+ * increment the working counter (e.g., APWR), but
+ * they will not trigger receive time latching.
+ */
+#define ESC_RCV_TIME_REQ_MASK (0xFFU)
+#define ESC_RCV_TIME_REQ_SHIFT (0U)
+#define ESC_RCV_TIME_REQ_GET(x) (((uint32_t)(x) & ESC_RCV_TIME_REQ_MASK) >> ESC_RCV_TIME_REQ_SHIFT)
+
+/* Bitfield definition for register: SYS_TIME */
+/*
+ * ST (RW)
+ *
+ */
+#define ESC_SYS_TIME_ST_MASK (0xFFFFFFFFFFFFFFFFULL)
+#define ESC_SYS_TIME_ST_SHIFT (0U)
+#define ESC_SYS_TIME_ST_SET(x) (((uint64_t)(x) << ESC_SYS_TIME_ST_SHIFT) & ESC_SYS_TIME_ST_MASK)
+#define ESC_SYS_TIME_ST_GET(x) (((uint64_t)(x) & ESC_SYS_TIME_ST_MASK) >> ESC_SYS_TIME_ST_SHIFT)
+
+/* Bitfield definition for register: RCVT_ECAT_PU */
+/*
+ * LT (RO)
+ *
+ * Local time at the beginning of a frame (start
+ * first bit of preamble) received at the ECAT
+ * Processing Unit containing a write access to
+ * register 0x0900
+ * NOTE:E.g., if port 0 is open, this register reflects
+ * the Receive Time Port 0 as a 64 Bit value.
+ * Any valid EtherCAT write access to register
+ * 0x0900 triggers latching, not only BWR/FPWR
+ * commands as with register 0x0900.
+ */
+#define ESC_RCVT_ECAT_PU_LT_MASK (0xFFFFFFFFFFFFFFFFULL)
+#define ESC_RCVT_ECAT_PU_LT_SHIFT (0U)
+#define ESC_RCVT_ECAT_PU_LT_GET(x) (((uint64_t)(x) & ESC_RCVT_ECAT_PU_LT_MASK) >> ESC_RCVT_ECAT_PU_LT_SHIFT)
+
+/* Bitfield definition for register: SYS_TIME_OFFSET */
+/*
+ * OFFSET (RW)
+ *
+ * Difference between local time and System
+ * Time. Offset is added to the local time.
+ */
+#define ESC_SYS_TIME_OFFSET_OFFSET_MASK (0xFFFFFFFFFFFFFFFFULL)
+#define ESC_SYS_TIME_OFFSET_OFFSET_SHIFT (0U)
+#define ESC_SYS_TIME_OFFSET_OFFSET_SET(x) (((uint64_t)(x) << ESC_SYS_TIME_OFFSET_OFFSET_SHIFT) & ESC_SYS_TIME_OFFSET_OFFSET_MASK)
+#define ESC_SYS_TIME_OFFSET_OFFSET_GET(x) (((uint64_t)(x) & ESC_SYS_TIME_OFFSET_OFFSET_MASK) >> ESC_SYS_TIME_OFFSET_OFFSET_SHIFT)
+
+/* Bitfield definition for register: SYS_TIME_DELAY */
+/*
+ * DLY (RW)
+ *
+ * Delay between Reference Clock and the
+ * ESC
+ */
+#define ESC_SYS_TIME_DELAY_DLY_MASK (0xFFFFFFFFUL)
+#define ESC_SYS_TIME_DELAY_DLY_SHIFT (0U)
+#define ESC_SYS_TIME_DELAY_DLY_SET(x) (((uint32_t)(x) << ESC_SYS_TIME_DELAY_DLY_SHIFT) & ESC_SYS_TIME_DELAY_DLY_MASK)
+#define ESC_SYS_TIME_DELAY_DLY_GET(x) (((uint32_t)(x) & ESC_SYS_TIME_DELAY_DLY_MASK) >> ESC_SYS_TIME_DELAY_DLY_SHIFT)
+
+/* Bitfield definition for register: SYS_TIME_DIFF */
+/*
+ * DIFF (RO)
+ *
+ * 0:Local copy of System Time less than
+ * received System Time
+ * 1:Local copy of System Time greater than
+ * or equal to received System Time
+ */
+#define ESC_SYS_TIME_DIFF_DIFF_MASK (0x80000000UL)
+#define ESC_SYS_TIME_DIFF_DIFF_SHIFT (31U)
+#define ESC_SYS_TIME_DIFF_DIFF_GET(x) (((uint32_t)(x) & ESC_SYS_TIME_DIFF_DIFF_MASK) >> ESC_SYS_TIME_DIFF_DIFF_SHIFT)
+
+/*
+ * NUM (RO)
+ *
+ * Mean difference between local copy of
+ * System Time and received System Time
+ * values
+ * Difference = Received System Time –
+ * local copy of System Time
+ */
+#define ESC_SYS_TIME_DIFF_NUM_MASK (0x7FFFFFFFUL)
+#define ESC_SYS_TIME_DIFF_NUM_SHIFT (0U)
+#define ESC_SYS_TIME_DIFF_NUM_GET(x) (((uint32_t)(x) & ESC_SYS_TIME_DIFF_NUM_MASK) >> ESC_SYS_TIME_DIFF_NUM_SHIFT)
+
+/* Bitfield definition for register: SPD_CNT_START */
+/*
+ * BW (RW)
+ *
+ * Bandwidth for adjustment of local copy of
+ * System Time (larger values → smaller
+ * bandwidth and smoother adjustment)
+ * A write access resets System Time
+ * Difference (0x092C:0x092F) and Speed
+ * Counter Diff (0x0932:0x0933).
+ * Valid values:0x0080 to 0x3FFF
+ */
+#define ESC_SPD_CNT_START_BW_MASK (0x7FFFU)
+#define ESC_SPD_CNT_START_BW_SHIFT (0U)
+#define ESC_SPD_CNT_START_BW_SET(x) (((uint16_t)(x) << ESC_SPD_CNT_START_BW_SHIFT) & ESC_SPD_CNT_START_BW_MASK)
+#define ESC_SPD_CNT_START_BW_GET(x) (((uint16_t)(x) & ESC_SPD_CNT_START_BW_MASK) >> ESC_SPD_CNT_START_BW_SHIFT)
+
+/* Bitfield definition for register: SPD_CNT_DIFF */
+/*
+ * DIFF (RO)
+ *
+ * Representation of the deviation between
+ * local clock period and Reference Clock's
+ * clock period (representation:two's
+ * complement)
+ * Range:±(Speed Counter Start – 0x7F)
+ */
+#define ESC_SPD_CNT_DIFF_DIFF_MASK (0xFFFFU)
+#define ESC_SPD_CNT_DIFF_DIFF_SHIFT (0U)
+#define ESC_SPD_CNT_DIFF_DIFF_GET(x) (((uint16_t)(x) & ESC_SPD_CNT_DIFF_DIFF_MASK) >> ESC_SPD_CNT_DIFF_DIFF_SHIFT)
+
+/* Bitfield definition for register: SYS_TIME_DIFF_FD */
+/*
+ * DEPTH (RW)
+ *
+ * Filter depth for averaging the received
+ * System Time deviation
+ * IP Core since V2.2.0/V2.02a:
+ * A write access resets System Time
+ * Difference (0x092C:0x092F)
+ */
+#define ESC_SYS_TIME_DIFF_FD_DEPTH_MASK (0xFU)
+#define ESC_SYS_TIME_DIFF_FD_DEPTH_SHIFT (0U)
+#define ESC_SYS_TIME_DIFF_FD_DEPTH_SET(x) (((uint8_t)(x) << ESC_SYS_TIME_DIFF_FD_DEPTH_SHIFT) & ESC_SYS_TIME_DIFF_FD_DEPTH_MASK)
+#define ESC_SYS_TIME_DIFF_FD_DEPTH_GET(x) (((uint8_t)(x) & ESC_SYS_TIME_DIFF_FD_DEPTH_MASK) >> ESC_SYS_TIME_DIFF_FD_DEPTH_SHIFT)
+
+/* Bitfield definition for register: SPD_CNT_FD */
+/*
+ * DEPTH (RW)
+ *
+ * Filter depth for averaging the clock period
+ * deviation
+ * IP Core since V2.2.0/V2.02a:
+ * A write access resets the internal speed
+ * counter filter
+ */
+#define ESC_SPD_CNT_FD_DEPTH_MASK (0xFU)
+#define ESC_SPD_CNT_FD_DEPTH_SHIFT (0U)
+#define ESC_SPD_CNT_FD_DEPTH_SET(x) (((uint8_t)(x) << ESC_SPD_CNT_FD_DEPTH_SHIFT) & ESC_SPD_CNT_FD_DEPTH_MASK)
+#define ESC_SPD_CNT_FD_DEPTH_GET(x) (((uint8_t)(x) & ESC_SPD_CNT_FD_DEPTH_MASK) >> ESC_SPD_CNT_FD_DEPTH_SHIFT)
+
+/* Bitfield definition for register: RCV_TIME_LM */
+/*
+ * LATCH_MODE (RO)
+ *
+ * Receive Time Latch Mode:
+ * 0:Forwarding mode (used if frames are
+ * entering the ESC at port 0 first):
+ * Receive time stamps of ports 1-3 are
+ * enabled after the write access to
+ * 0x0900, so the following frame at ports
+ * 1-3 will be time stamped (this is typically
+ * the write frame to 0x0900 coming back
+ * from the network behind the ESC).
+ * 1:Reverse mode (used if frames are
+ * entering ESC at port 1-3 first):
+ * Receive time stamps of ports 1-3 are
+ * immediately taken over from the internal
+ * hidden time stamp registers, so the
+ * previous frame entering the ESC at
+ * ports 1-3 will be time stamped when the
+ * write frame to 0x0900 enters port 0 (the
+ * previous frame at ports 1-3 is typically
+ * the write frame to 0x0900 coming from
+ * the master, which will enable time
+ * stamp
+ */
+#define ESC_RCV_TIME_LM_LATCH_MODE_MASK (0x1U)
+#define ESC_RCV_TIME_LM_LATCH_MODE_SHIFT (0U)
+#define ESC_RCV_TIME_LM_LATCH_MODE_GET(x) (((uint8_t)(x) & ESC_RCV_TIME_LM_LATCH_MODE_MASK) >> ESC_RCV_TIME_LM_LATCH_MODE_SHIFT)
+
+/* Bitfield definition for register: CYC_UNIT_CTRL */
+/*
+ * LATCHI1 (RO)
+ *
+ * Latch In unit 1:
+ * 0:ECAT-controlled
+ * 1:PDI-controlled
+ * NOTE:Latch interrupt is routed to ECAT/PDI
+ * depending on this setting
+ */
+#define ESC_CYC_UNIT_CTRL_LATCHI1_MASK (0x20U)
+#define ESC_CYC_UNIT_CTRL_LATCHI1_SHIFT (5U)
+#define ESC_CYC_UNIT_CTRL_LATCHI1_GET(x) (((uint8_t)(x) & ESC_CYC_UNIT_CTRL_LATCHI1_MASK) >> ESC_CYC_UNIT_CTRL_LATCHI1_SHIFT)
+
+/*
+ * LATCHI0 (RO)
+ *
+ * Latch In unit 0:
+ * 0:ECAT-controlled
+ * 1:PDI-controlled
+ * NOTE:Latch interrupt is routed to ECAT/PDI
+ * depending on this setting.
+ * Always 1 (PDI-controlled) if System Time is PDIcontrolled.
+ */
+#define ESC_CYC_UNIT_CTRL_LATCHI0_MASK (0x10U)
+#define ESC_CYC_UNIT_CTRL_LATCHI0_SHIFT (4U)
+#define ESC_CYC_UNIT_CTRL_LATCHI0_GET(x) (((uint8_t)(x) & ESC_CYC_UNIT_CTRL_LATCHI0_MASK) >> ESC_CYC_UNIT_CTRL_LATCHI0_SHIFT)
+
+/*
+ * SYNCO (RO)
+ *
+ * Cyclic Unit and SYNC0 out unit control:
+ * 0:ECAT-controlled
+ * 1:PDI-controlled
+ */
+#define ESC_CYC_UNIT_CTRL_SYNCO_MASK (0x1U)
+#define ESC_CYC_UNIT_CTRL_SYNCO_SHIFT (0U)
+#define ESC_CYC_UNIT_CTRL_SYNCO_GET(x) (((uint8_t)(x) & ESC_CYC_UNIT_CTRL_SYNCO_MASK) >> ESC_CYC_UNIT_CTRL_SYNCO_SHIFT)
+
+/* Bitfield definition for register: SYNCO_ACT */
+/*
+ * SSDP (RW)
+ *
+ * SyncSignal debug pulse (Vasily bit):
+ * 0:Deactivated
+ * 1:Immediately generate one ping only on
+ * SYNC0-1 according to 0x0981[2:1 for
+ * debugging
+ * This bit is self-clearing, always read 0.
+ * All pulses are generated at the same time,
+ * the cycle time is ignored. The configured
+ * pulse length is used.
+ */
+#define ESC_SYNCO_ACT_SSDP_MASK (0x80U)
+#define ESC_SYNCO_ACT_SSDP_SHIFT (7U)
+#define ESC_SYNCO_ACT_SSDP_SET(x) (((uint8_t)(x) << ESC_SYNCO_ACT_SSDP_SHIFT) & ESC_SYNCO_ACT_SSDP_MASK)
+#define ESC_SYNCO_ACT_SSDP_GET(x) (((uint8_t)(x) & ESC_SYNCO_ACT_SSDP_MASK) >> ESC_SYNCO_ACT_SSDP_SHIFT)
+
+/*
+ * NFC (RW)
+ *
+ * Near future configuration (approx.):
+ * 0:½ DC width future (231 ns or 263 ns)
+ * 1:~2.1 sec. future (231 ns)
+ */
+#define ESC_SYNCO_ACT_NFC_MASK (0x40U)
+#define ESC_SYNCO_ACT_NFC_SHIFT (6U)
+#define ESC_SYNCO_ACT_NFC_SET(x) (((uint8_t)(x) << ESC_SYNCO_ACT_NFC_SHIFT) & ESC_SYNCO_ACT_NFC_MASK)
+#define ESC_SYNCO_ACT_NFC_GET(x) (((uint8_t)(x) & ESC_SYNCO_ACT_NFC_MASK) >> ESC_SYNCO_ACT_NFC_SHIFT)
+
+/*
+ * STPC (RW)
+ *
+ * Start Time plausibility check:
+ * 0:Disabled. SyncSignal generation if Start
+ * Time is reached.
+ * 1:Immediate SyncSignal generation if
+ * Start Time is outside near future (see
+ * 0x0981[6])
+ */
+#define ESC_SYNCO_ACT_STPC_MASK (0x20U)
+#define ESC_SYNCO_ACT_STPC_SHIFT (5U)
+#define ESC_SYNCO_ACT_STPC_SET(x) (((uint8_t)(x) << ESC_SYNCO_ACT_STPC_SHIFT) & ESC_SYNCO_ACT_STPC_MASK)
+#define ESC_SYNCO_ACT_STPC_GET(x) (((uint8_t)(x) & ESC_SYNCO_ACT_STPC_MASK) >> ESC_SYNCO_ACT_STPC_SHIFT)
+
+/*
+ * EXT (RW)
+ *
+ * Extension of Start Time Cyclic Operation
+ * (0x0990:0x0993):
+ * 0:No extension
+ * 1:Extend 32 bit written Start Time to 64 bit
+ */
+#define ESC_SYNCO_ACT_EXT_MASK (0x10U)
+#define ESC_SYNCO_ACT_EXT_SHIFT (4U)
+#define ESC_SYNCO_ACT_EXT_SET(x) (((uint8_t)(x) << ESC_SYNCO_ACT_EXT_SHIFT) & ESC_SYNCO_ACT_EXT_MASK)
+#define ESC_SYNCO_ACT_EXT_GET(x) (((uint8_t)(x) & ESC_SYNCO_ACT_EXT_MASK) >> ESC_SYNCO_ACT_EXT_SHIFT)
+
+/*
+ * AC (RW)
+ *
+ * Auto-activation by writing Start Time Cyclic
+ * Operation (0x0990:0x0997):
+ * 0:Disabled
+ * 1:Auto-activation enabled. 0x0981[0] is
+ * set automatically after Start Time is
+ * written.
+ */
+#define ESC_SYNCO_ACT_AC_MASK (0x8U)
+#define ESC_SYNCO_ACT_AC_SHIFT (3U)
+#define ESC_SYNCO_ACT_AC_SET(x) (((uint8_t)(x) << ESC_SYNCO_ACT_AC_SHIFT) & ESC_SYNCO_ACT_AC_MASK)
+#define ESC_SYNCO_ACT_AC_GET(x) (((uint8_t)(x) & ESC_SYNCO_ACT_AC_MASK) >> ESC_SYNCO_ACT_AC_SHIFT)
+
+/*
+ * SYNC1_GEN (RW)
+ *
+ * SYNC1 generation:
+ * 0:Deactivated
+ * 1:SYNC1 pulse is generated
+ */
+#define ESC_SYNCO_ACT_SYNC1_GEN_MASK (0x4U)
+#define ESC_SYNCO_ACT_SYNC1_GEN_SHIFT (2U)
+#define ESC_SYNCO_ACT_SYNC1_GEN_SET(x) (((uint8_t)(x) << ESC_SYNCO_ACT_SYNC1_GEN_SHIFT) & ESC_SYNCO_ACT_SYNC1_GEN_MASK)
+#define ESC_SYNCO_ACT_SYNC1_GEN_GET(x) (((uint8_t)(x) & ESC_SYNCO_ACT_SYNC1_GEN_MASK) >> ESC_SYNCO_ACT_SYNC1_GEN_SHIFT)
+
+/*
+ * SYNC0_GEN (RW)
+ *
+ * SYNC0 generation:
+ * 0:Deactivated
+ * 1:SYNC0 pulse is generated
+ */
+#define ESC_SYNCO_ACT_SYNC0_GEN_MASK (0x2U)
+#define ESC_SYNCO_ACT_SYNC0_GEN_SHIFT (1U)
+#define ESC_SYNCO_ACT_SYNC0_GEN_SET(x) (((uint8_t)(x) << ESC_SYNCO_ACT_SYNC0_GEN_SHIFT) & ESC_SYNCO_ACT_SYNC0_GEN_MASK)
+#define ESC_SYNCO_ACT_SYNC0_GEN_GET(x) (((uint8_t)(x) & ESC_SYNCO_ACT_SYNC0_GEN_MASK) >> ESC_SYNCO_ACT_SYNC0_GEN_SHIFT)
+
+/*
+ * SOUA (RW)
+ *
+ * Sync Out Unit activation:
+ * 0:Deactivated
+ * 1:Activated
+ */
+#define ESC_SYNCO_ACT_SOUA_MASK (0x1U)
+#define ESC_SYNCO_ACT_SOUA_SHIFT (0U)
+#define ESC_SYNCO_ACT_SOUA_SET(x) (((uint8_t)(x) << ESC_SYNCO_ACT_SOUA_SHIFT) & ESC_SYNCO_ACT_SOUA_MASK)
+#define ESC_SYNCO_ACT_SOUA_GET(x) (((uint8_t)(x) & ESC_SYNCO_ACT_SOUA_MASK) >> ESC_SYNCO_ACT_SOUA_SHIFT)
+
+/* Bitfield definition for register: PULSE_LEN */
+/*
+ * LEN (RO)
+ *
+ * Pulse length of SyncSignals (in Units of
+ * 10ns)
+ * 0:Acknowledge mode:SyncSignal will be
+ * cleared by reading SYNC[1:0] Status
+ * register
+ */
+#define ESC_PULSE_LEN_LEN_MASK (0xFFFFU)
+#define ESC_PULSE_LEN_LEN_SHIFT (0U)
+#define ESC_PULSE_LEN_LEN_GET(x) (((uint16_t)(x) & ESC_PULSE_LEN_LEN_MASK) >> ESC_PULSE_LEN_LEN_SHIFT)
+
+/* Bitfield definition for register: ACT_STAT */
+/*
+ * CHK_RSLT (RO)
+ *
+ * Start Time Cyclic Operation (0x0990:0x0997)
+ * plausibility check result when Sync Out Unit
+ * was activated:
+ * 0:Start Time was within near future
+ * 1:Start Time was out of near future
+ * (0x0981[6])
+ */
+#define ESC_ACT_STAT_CHK_RSLT_MASK (0x4U)
+#define ESC_ACT_STAT_CHK_RSLT_SHIFT (2U)
+#define ESC_ACT_STAT_CHK_RSLT_GET(x) (((uint8_t)(x) & ESC_ACT_STAT_CHK_RSLT_MASK) >> ESC_ACT_STAT_CHK_RSLT_SHIFT)
+
+/*
+ * SYNC1 (RO)
+ *
+ * SYNC1 activation state:
+ * 0:First SYNC1 pulse is not pending
+ * 1:First SYNC1 pulse is pending
+ */
+#define ESC_ACT_STAT_SYNC1_MASK (0x2U)
+#define ESC_ACT_STAT_SYNC1_SHIFT (1U)
+#define ESC_ACT_STAT_SYNC1_GET(x) (((uint8_t)(x) & ESC_ACT_STAT_SYNC1_MASK) >> ESC_ACT_STAT_SYNC1_SHIFT)
+
+/*
+ * SYNC0 (RO)
+ *
+ * SYNC0 activation state:
+ * 0:First SYNC0 pulse is not pending
+ * 1:First SYNC0 pulse is pending
+ */
+#define ESC_ACT_STAT_SYNC0_MASK (0x1U)
+#define ESC_ACT_STAT_SYNC0_SHIFT (0U)
+#define ESC_ACT_STAT_SYNC0_GET(x) (((uint8_t)(x) & ESC_ACT_STAT_SYNC0_MASK) >> ESC_ACT_STAT_SYNC0_SHIFT)
+
+/* Bitfield definition for register: SYNC0_STAT */
+/*
+ * ACK (RW)
+ *
+ * SYNC0 state for Acknowledge mode.
+ * SYNC0 in Acknowledge mode is cleared by
+ * reading this register from PDI, use only in
+ * Acknowledge mode
+ */
+#define ESC_SYNC0_STAT_ACK_MASK (0x1U)
+#define ESC_SYNC0_STAT_ACK_SHIFT (0U)
+#define ESC_SYNC0_STAT_ACK_SET(x) (((uint8_t)(x) << ESC_SYNC0_STAT_ACK_SHIFT) & ESC_SYNC0_STAT_ACK_MASK)
+#define ESC_SYNC0_STAT_ACK_GET(x) (((uint8_t)(x) & ESC_SYNC0_STAT_ACK_MASK) >> ESC_SYNC0_STAT_ACK_SHIFT)
+
+/* Bitfield definition for register: SYNC1_STAT */
+/*
+ * ACK (RW)
+ *
+ * SYNC1 state for Acknowledge mode.
+ * SYNC1 in Acknowledge mode is cleared by
+ * reading this register from PDI, use only in
+ * Acknowledge mode
+ */
+#define ESC_SYNC1_STAT_ACK_MASK (0x1U)
+#define ESC_SYNC1_STAT_ACK_SHIFT (0U)
+#define ESC_SYNC1_STAT_ACK_SET(x) (((uint8_t)(x) << ESC_SYNC1_STAT_ACK_SHIFT) & ESC_SYNC1_STAT_ACK_MASK)
+#define ESC_SYNC1_STAT_ACK_GET(x) (((uint8_t)(x) & ESC_SYNC1_STAT_ACK_MASK) >> ESC_SYNC1_STAT_ACK_SHIFT)
+
+/* Bitfield definition for register: START_TIME_CO */
+/*
+ * ST (RW)
+ *
+ * Write:Start time (System time) of cyclic
+ * operation in ns
+ * Read:System time of next SYNC0 pulse in
+ * ns
+ */
+#define ESC_START_TIME_CO_ST_MASK (0xFFFFFFFFFFFFFFFFULL)
+#define ESC_START_TIME_CO_ST_SHIFT (0U)
+#define ESC_START_TIME_CO_ST_SET(x) (((uint64_t)(x) << ESC_START_TIME_CO_ST_SHIFT) & ESC_START_TIME_CO_ST_MASK)
+#define ESC_START_TIME_CO_ST_GET(x) (((uint64_t)(x) & ESC_START_TIME_CO_ST_MASK) >> ESC_START_TIME_CO_ST_SHIFT)
+
+/* Bitfield definition for register: NXT_SYNC1_PULSE */
+/*
+ * TIME (RO)
+ *
+ * System time of next SYNC1 pulse in ns
+ */
+#define ESC_NXT_SYNC1_PULSE_TIME_MASK (0xFFFFFFFFFFFFFFFFULL)
+#define ESC_NXT_SYNC1_PULSE_TIME_SHIFT (0U)
+#define ESC_NXT_SYNC1_PULSE_TIME_GET(x) (((uint64_t)(x) & ESC_NXT_SYNC1_PULSE_TIME_MASK) >> ESC_NXT_SYNC1_PULSE_TIME_SHIFT)
+
+/* Bitfield definition for register: SYNC0_CYC_TIME */
+/*
+ * CYC (RW)
+ *
+ * Time between two consecutive SYNC0
+ * pulses in ns.
+ * 0:Single shot mode, generate only one
+ * SYNC0 pulse.
+ */
+#define ESC_SYNC0_CYC_TIME_CYC_MASK (0xFFFFFFFFUL)
+#define ESC_SYNC0_CYC_TIME_CYC_SHIFT (0U)
+#define ESC_SYNC0_CYC_TIME_CYC_SET(x) (((uint32_t)(x) << ESC_SYNC0_CYC_TIME_CYC_SHIFT) & ESC_SYNC0_CYC_TIME_CYC_MASK)
+#define ESC_SYNC0_CYC_TIME_CYC_GET(x) (((uint32_t)(x) & ESC_SYNC0_CYC_TIME_CYC_MASK) >> ESC_SYNC0_CYC_TIME_CYC_SHIFT)
+
+/* Bitfield definition for register: SYNC1_CYC_TIME */
+/*
+ * CYC (RW)
+ *
+ * Time between SYNC0 pulse and SYNC1
+ * pulse in ns
+ */
+#define ESC_SYNC1_CYC_TIME_CYC_MASK (0xFFFFFFFFUL)
+#define ESC_SYNC1_CYC_TIME_CYC_SHIFT (0U)
+#define ESC_SYNC1_CYC_TIME_CYC_SET(x) (((uint32_t)(x) << ESC_SYNC1_CYC_TIME_CYC_SHIFT) & ESC_SYNC1_CYC_TIME_CYC_MASK)
+#define ESC_SYNC1_CYC_TIME_CYC_GET(x) (((uint32_t)(x) & ESC_SYNC1_CYC_TIME_CYC_MASK) >> ESC_SYNC1_CYC_TIME_CYC_SHIFT)
+
+/* Bitfield definition for register: LATCH0_CTRL */
+/*
+ * NEG_EDGE (RW)
+ *
+ * Latch0 negative edge:
+ * 0:Continuous Latch active
+ * 1:Single event (only first event active)
+ */
+#define ESC_LATCH0_CTRL_NEG_EDGE_MASK (0x2U)
+#define ESC_LATCH0_CTRL_NEG_EDGE_SHIFT (1U)
+#define ESC_LATCH0_CTRL_NEG_EDGE_SET(x) (((uint8_t)(x) << ESC_LATCH0_CTRL_NEG_EDGE_SHIFT) & ESC_LATCH0_CTRL_NEG_EDGE_MASK)
+#define ESC_LATCH0_CTRL_NEG_EDGE_GET(x) (((uint8_t)(x) & ESC_LATCH0_CTRL_NEG_EDGE_MASK) >> ESC_LATCH0_CTRL_NEG_EDGE_SHIFT)
+
+/*
+ * POS_EDGE (RW)
+ *
+ * Latch0 positive edge:
+ * 0:Continuous Latch active
+ * 1:Single event (only first event active)
+ */
+#define ESC_LATCH0_CTRL_POS_EDGE_MASK (0x1U)
+#define ESC_LATCH0_CTRL_POS_EDGE_SHIFT (0U)
+#define ESC_LATCH0_CTRL_POS_EDGE_SET(x) (((uint8_t)(x) << ESC_LATCH0_CTRL_POS_EDGE_SHIFT) & ESC_LATCH0_CTRL_POS_EDGE_MASK)
+#define ESC_LATCH0_CTRL_POS_EDGE_GET(x) (((uint8_t)(x) & ESC_LATCH0_CTRL_POS_EDGE_MASK) >> ESC_LATCH0_CTRL_POS_EDGE_SHIFT)
+
+/* Bitfield definition for register: LATCH1_CTRL */
+/*
+ * NEG_EDGE (RW)
+ *
+ * Latch1 negative edge:
+ * 0:Continuous Latch active
+ * 1:Single event (only first event active)
+ */
+#define ESC_LATCH1_CTRL_NEG_EDGE_MASK (0x2U)
+#define ESC_LATCH1_CTRL_NEG_EDGE_SHIFT (1U)
+#define ESC_LATCH1_CTRL_NEG_EDGE_SET(x) (((uint8_t)(x) << ESC_LATCH1_CTRL_NEG_EDGE_SHIFT) & ESC_LATCH1_CTRL_NEG_EDGE_MASK)
+#define ESC_LATCH1_CTRL_NEG_EDGE_GET(x) (((uint8_t)(x) & ESC_LATCH1_CTRL_NEG_EDGE_MASK) >> ESC_LATCH1_CTRL_NEG_EDGE_SHIFT)
+
+/*
+ * POS_EDGE (RW)
+ *
+ * Latch1 positive edge:
+ * 0:Continuous Latch active
+ * 1:Single event (only first event active)
+ */
+#define ESC_LATCH1_CTRL_POS_EDGE_MASK (0x1U)
+#define ESC_LATCH1_CTRL_POS_EDGE_SHIFT (0U)
+#define ESC_LATCH1_CTRL_POS_EDGE_SET(x) (((uint8_t)(x) << ESC_LATCH1_CTRL_POS_EDGE_SHIFT) & ESC_LATCH1_CTRL_POS_EDGE_MASK)
+#define ESC_LATCH1_CTRL_POS_EDGE_GET(x) (((uint8_t)(x) & ESC_LATCH1_CTRL_POS_EDGE_MASK) >> ESC_LATCH1_CTRL_POS_EDGE_SHIFT)
+
+/* Bitfield definition for register: LATCH0_STAT */
+/*
+ * PIN_STAT (RO)
+ *
+ * Latch0 pin state
+ */
+#define ESC_LATCH0_STAT_PIN_STAT_MASK (0x4U)
+#define ESC_LATCH0_STAT_PIN_STAT_SHIFT (2U)
+#define ESC_LATCH0_STAT_PIN_STAT_GET(x) (((uint8_t)(x) & ESC_LATCH0_STAT_PIN_STAT_MASK) >> ESC_LATCH0_STAT_PIN_STAT_SHIFT)
+
+/*
+ * NEG_EDGE (RO)
+ *
+ * Event Latch0 negative edge.
+ * 0:Negative edge not detected or
+ * continuous mode
+ * 1:Negative edge detected in single event
+ * mode only.
+ * Flag cleared by reading out Latch0 Time
+ * Negative Edge.
+ */
+#define ESC_LATCH0_STAT_NEG_EDGE_MASK (0x2U)
+#define ESC_LATCH0_STAT_NEG_EDGE_SHIFT (1U)
+#define ESC_LATCH0_STAT_NEG_EDGE_GET(x) (((uint8_t)(x) & ESC_LATCH0_STAT_NEG_EDGE_MASK) >> ESC_LATCH0_STAT_NEG_EDGE_SHIFT)
+
+/*
+ * POS_EDGE (RO)
+ *
+ * Event Latch0 positive edge.
+ * 0:Positive edge not detected or
+ * continuous mode
+ * 1:Positive edge detected in single event
+ * mode only.
+ * Flag cleared by reading out Latch0 Time
+ * Positive Edge.
+ */
+#define ESC_LATCH0_STAT_POS_EDGE_MASK (0x1U)
+#define ESC_LATCH0_STAT_POS_EDGE_SHIFT (0U)
+#define ESC_LATCH0_STAT_POS_EDGE_GET(x) (((uint8_t)(x) & ESC_LATCH0_STAT_POS_EDGE_MASK) >> ESC_LATCH0_STAT_POS_EDGE_SHIFT)
+
+/* Bitfield definition for register: LATCH1_STAT */
+/*
+ * PIN_STAT (RO)
+ *
+ * Latch1 pin state
+ */
+#define ESC_LATCH1_STAT_PIN_STAT_MASK (0x4U)
+#define ESC_LATCH1_STAT_PIN_STAT_SHIFT (2U)
+#define ESC_LATCH1_STAT_PIN_STAT_GET(x) (((uint8_t)(x) & ESC_LATCH1_STAT_PIN_STAT_MASK) >> ESC_LATCH1_STAT_PIN_STAT_SHIFT)
+
+/*
+ * NEG_EDGE (RO)
+ *
+ * Event Latch1 negative edge.
+ * 0:Negative edge not detected or
+ * continuous mode
+ * 1:Negative edge detected in single event
+ * mode only.
+ * Flag cleared by reading out Latch1 Time
+ * Negative Edge.
+ */
+#define ESC_LATCH1_STAT_NEG_EDGE_MASK (0x2U)
+#define ESC_LATCH1_STAT_NEG_EDGE_SHIFT (1U)
+#define ESC_LATCH1_STAT_NEG_EDGE_GET(x) (((uint8_t)(x) & ESC_LATCH1_STAT_NEG_EDGE_MASK) >> ESC_LATCH1_STAT_NEG_EDGE_SHIFT)
+
+/*
+ * POS_EDGE (RO)
+ *
+ * Event Latch1 positive edge.
+ * 0:Positive edge not detected or
+ * continuous mode
+ * 1:Positive edge detected in single event
+ * mode only.
+ * Flag cleared by reading out Latch1 Time
+ * Positive Edge.
+ */
+#define ESC_LATCH1_STAT_POS_EDGE_MASK (0x1U)
+#define ESC_LATCH1_STAT_POS_EDGE_SHIFT (0U)
+#define ESC_LATCH1_STAT_POS_EDGE_GET(x) (((uint8_t)(x) & ESC_LATCH1_STAT_POS_EDGE_MASK) >> ESC_LATCH1_STAT_POS_EDGE_SHIFT)
+
+/* Bitfield definition for register: LATCH0_TIME_PE */
+/*
+ * TIME (RW)
+ *
+ * System time at the positive edge of the
+ * Latch0 signal.
+ */
+#define ESC_LATCH0_TIME_PE_TIME_MASK (0xFFFFFFFFFFFFFFFFULL)
+#define ESC_LATCH0_TIME_PE_TIME_SHIFT (0U)
+#define ESC_LATCH0_TIME_PE_TIME_SET(x) (((uint64_t)(x) << ESC_LATCH0_TIME_PE_TIME_SHIFT) & ESC_LATCH0_TIME_PE_TIME_MASK)
+#define ESC_LATCH0_TIME_PE_TIME_GET(x) (((uint64_t)(x) & ESC_LATCH0_TIME_PE_TIME_MASK) >> ESC_LATCH0_TIME_PE_TIME_SHIFT)
+
+/* Bitfield definition for register: LATCH0_TIME_NE */
+/*
+ * TIME (RW)
+ *
+ * System time at the negative edge of the
+ * Latch0 signal.
+ */
+#define ESC_LATCH0_TIME_NE_TIME_MASK (0xFFFFFFFFFFFFFFFFULL)
+#define ESC_LATCH0_TIME_NE_TIME_SHIFT (0U)
+#define ESC_LATCH0_TIME_NE_TIME_SET(x) (((uint64_t)(x) << ESC_LATCH0_TIME_NE_TIME_SHIFT) & ESC_LATCH0_TIME_NE_TIME_MASK)
+#define ESC_LATCH0_TIME_NE_TIME_GET(x) (((uint64_t)(x) & ESC_LATCH0_TIME_NE_TIME_MASK) >> ESC_LATCH0_TIME_NE_TIME_SHIFT)
+
+/* Bitfield definition for register: LATCH1_TIME_PE */
+/*
+ * TIME (RW)
+ *
+ * System time at the positive edge of the
+ * Latch1 signal.
+ */
+#define ESC_LATCH1_TIME_PE_TIME_MASK (0xFFFFFFFFFFFFFFFFULL)
+#define ESC_LATCH1_TIME_PE_TIME_SHIFT (0U)
+#define ESC_LATCH1_TIME_PE_TIME_SET(x) (((uint64_t)(x) << ESC_LATCH1_TIME_PE_TIME_SHIFT) & ESC_LATCH1_TIME_PE_TIME_MASK)
+#define ESC_LATCH1_TIME_PE_TIME_GET(x) (((uint64_t)(x) & ESC_LATCH1_TIME_PE_TIME_MASK) >> ESC_LATCH1_TIME_PE_TIME_SHIFT)
+
+/* Bitfield definition for register: LATCH1_TIME_NE */
+/*
+ * TIME (RW)
+ *
+ * System time at the negative edge of the
+ * Latch1 signal.
+ */
+#define ESC_LATCH1_TIME_NE_TIME_MASK (0xFFFFFFFFFFFFFFFFULL)
+#define ESC_LATCH1_TIME_NE_TIME_SHIFT (0U)
+#define ESC_LATCH1_TIME_NE_TIME_SET(x) (((uint64_t)(x) << ESC_LATCH1_TIME_NE_TIME_SHIFT) & ESC_LATCH1_TIME_NE_TIME_MASK)
+#define ESC_LATCH1_TIME_NE_TIME_GET(x) (((uint64_t)(x) & ESC_LATCH1_TIME_NE_TIME_MASK) >> ESC_LATCH1_TIME_NE_TIME_SHIFT)
+
+/* Bitfield definition for register: ECAT_BUF_CET */
+/*
+ * TIME (RO)
+ *
+ * Local time at the beginning of the frame
+ * which causes at least one SyncManager to
+ * assert an ECAT event
+ */
+#define ESC_ECAT_BUF_CET_TIME_MASK (0xFFFFFFFFUL)
+#define ESC_ECAT_BUF_CET_TIME_SHIFT (0U)
+#define ESC_ECAT_BUF_CET_TIME_GET(x) (((uint32_t)(x) & ESC_ECAT_BUF_CET_TIME_MASK) >> ESC_ECAT_BUF_CET_TIME_SHIFT)
+
+/* Bitfield definition for register: PDI_BUF_SET */
+/*
+ * TIME (RO)
+ *
+ * Local time when at least one SyncManager
+ * asserts a PDI buffer start event
+ */
+#define ESC_PDI_BUF_SET_TIME_MASK (0xFFFFFFFFUL)
+#define ESC_PDI_BUF_SET_TIME_SHIFT (0U)
+#define ESC_PDI_BUF_SET_TIME_GET(x) (((uint32_t)(x) & ESC_PDI_BUF_SET_TIME_MASK) >> ESC_PDI_BUF_SET_TIME_SHIFT)
+
+/* Bitfield definition for register: PDI_BUF_CET */
+/*
+ * TIME (RO)
+ *
+ * Local time when at least one SyncManager
+ * asserts a PDI buffer change event
+ */
+#define ESC_PDI_BUF_CET_TIME_MASK (0xFFFFFFFFUL)
+#define ESC_PDI_BUF_CET_TIME_SHIFT (0U)
+#define ESC_PDI_BUF_CET_TIME_GET(x) (((uint32_t)(x) & ESC_PDI_BUF_CET_TIME_MASK) >> ESC_PDI_BUF_CET_TIME_SHIFT)
+
+/* Bitfield definition for register: PID */
+/*
+ * PID (RO)
+ *
+ * Product ID
+ */
+#define ESC_PID_PID_MASK (0xFFFFFFFFFFFFFFFFULL)
+#define ESC_PID_PID_SHIFT (0U)
+#define ESC_PID_PID_GET(x) (((uint64_t)(x) & ESC_PID_PID_MASK) >> ESC_PID_PID_SHIFT)
+
+/* Bitfield definition for register: VID */
+/*
+ * VID (RO)
+ *
+ * Vendor ID:
+ * 23-0: Company
+ * 31-24: Department
+ * NOTE:Test Vendor IDs have [31:28]=0xE
+ */
+#define ESC_VID_VID_MASK (0xFFFFFFFFFFFFFFFFULL)
+#define ESC_VID_VID_SHIFT (0U)
+#define ESC_VID_VID_GET(x) (((uint64_t)(x) & ESC_VID_VID_MASK) >> ESC_VID_VID_SHIFT)
+
+/* Bitfield definition for register: DIO_OUT_DATA */
+/*
+ * OD (RO)
+ *
+ * Output Data
+ */
+#define ESC_DIO_OUT_DATA_OD_MASK (0xFFFFFFFFUL)
+#define ESC_DIO_OUT_DATA_OD_SHIFT (0U)
+#define ESC_DIO_OUT_DATA_OD_GET(x) (((uint32_t)(x) & ESC_DIO_OUT_DATA_OD_MASK) >> ESC_DIO_OUT_DATA_OD_SHIFT)
+
+/* Bitfield definition for register: GPO */
+/*
+ * GPOD (RW)
+ *
+ * General Purpose Output Data
+ */
+#define ESC_GPO_GPOD_MASK (0xFFFFFFFFFFFFFFFFULL)
+#define ESC_GPO_GPOD_SHIFT (0U)
+#define ESC_GPO_GPOD_SET(x) (((uint64_t)(x) << ESC_GPO_GPOD_SHIFT) & ESC_GPO_GPOD_MASK)
+#define ESC_GPO_GPOD_GET(x) (((uint64_t)(x) & ESC_GPO_GPOD_MASK) >> ESC_GPO_GPOD_SHIFT)
+
+/* Bitfield definition for register: GPI */
+/*
+ * GPID (RO)
+ *
+ * General Purpose Input Data
+ */
+#define ESC_GPI_GPID_MASK (0xFFFFFFFFFFFFFFFFULL)
+#define ESC_GPI_GPID_SHIFT (0U)
+#define ESC_GPI_GPID_GET(x) (((uint64_t)(x) & ESC_GPI_GPID_MASK) >> ESC_GPI_GPID_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE0 */
+/*
+ * EXTF (RW)
+ *
+ * Number of extended feature bits
+ */
+#define ESC_USER_RAM_BYTE0_EXTF_MASK (0xFFU)
+#define ESC_USER_RAM_BYTE0_EXTF_SHIFT (0U)
+#define ESC_USER_RAM_BYTE0_EXTF_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE0_EXTF_SHIFT) & ESC_USER_RAM_BYTE0_EXTF_MASK)
+#define ESC_USER_RAM_BYTE0_EXTF_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE0_EXTF_MASK) >> ESC_USER_RAM_BYTE0_EXTF_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE1 */
+/*
+ * PRWO (RW)
+ *
+ * Physical Read/Write Offset (0x0108:0x0109)
+ */
+#define ESC_USER_RAM_BYTE1_PRWO_MASK (0x80U)
+#define ESC_USER_RAM_BYTE1_PRWO_SHIFT (7U)
+#define ESC_USER_RAM_BYTE1_PRWO_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE1_PRWO_SHIFT) & ESC_USER_RAM_BYTE1_PRWO_MASK)
+#define ESC_USER_RAM_BYTE1_PRWO_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE1_PRWO_MASK) >> ESC_USER_RAM_BYTE1_PRWO_SHIFT)
+
+/*
+ * AEMW (RW)
+ *
+ * AL Event Mask writable (0x0204:0x0207)
+ */
+#define ESC_USER_RAM_BYTE1_AEMW_MASK (0x40U)
+#define ESC_USER_RAM_BYTE1_AEMW_SHIFT (6U)
+#define ESC_USER_RAM_BYTE1_AEMW_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE1_AEMW_SHIFT) & ESC_USER_RAM_BYTE1_AEMW_MASK)
+#define ESC_USER_RAM_BYTE1_AEMW_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE1_AEMW_MASK) >> ESC_USER_RAM_BYTE1_AEMW_SHIFT)
+
+/*
+ * GPO (RW)
+ *
+ * General Purpose Outputs (0x0F10:0x0F17)
+ */
+#define ESC_USER_RAM_BYTE1_GPO_MASK (0x20U)
+#define ESC_USER_RAM_BYTE1_GPO_SHIFT (5U)
+#define ESC_USER_RAM_BYTE1_GPO_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE1_GPO_SHIFT) & ESC_USER_RAM_BYTE1_GPO_MASK)
+#define ESC_USER_RAM_BYTE1_GPO_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE1_GPO_MASK) >> ESC_USER_RAM_BYTE1_GPO_SHIFT)
+
+/*
+ * GPI (RW)
+ *
+ * General Purpose Inputs (0x0F18:0x0F1F)
+ */
+#define ESC_USER_RAM_BYTE1_GPI_MASK (0x10U)
+#define ESC_USER_RAM_BYTE1_GPI_SHIFT (4U)
+#define ESC_USER_RAM_BYTE1_GPI_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE1_GPI_SHIFT) & ESC_USER_RAM_BYTE1_GPI_MASK)
+#define ESC_USER_RAM_BYTE1_GPI_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE1_GPI_MASK) >> ESC_USER_RAM_BYTE1_GPI_SHIFT)
+
+/*
+ * CSA (RW)
+ *
+ * Configured Station Alias (0x0012:0x0013)
+ */
+#define ESC_USER_RAM_BYTE1_CSA_MASK (0x8U)
+#define ESC_USER_RAM_BYTE1_CSA_SHIFT (3U)
+#define ESC_USER_RAM_BYTE1_CSA_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE1_CSA_SHIFT) & ESC_USER_RAM_BYTE1_CSA_MASK)
+#define ESC_USER_RAM_BYTE1_CSA_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE1_CSA_MASK) >> ESC_USER_RAM_BYTE1_CSA_SHIFT)
+
+/*
+ * EIM (RW)
+ *
+ * ECAT Interrupt Mask (0x0200:0x0201)
+ */
+#define ESC_USER_RAM_BYTE1_EIM_MASK (0x4U)
+#define ESC_USER_RAM_BYTE1_EIM_SHIFT (2U)
+#define ESC_USER_RAM_BYTE1_EIM_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE1_EIM_SHIFT) & ESC_USER_RAM_BYTE1_EIM_MASK)
+#define ESC_USER_RAM_BYTE1_EIM_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE1_EIM_MASK) >> ESC_USER_RAM_BYTE1_EIM_SHIFT)
+
+/*
+ * ALSCR (RW)
+ *
+ * AL Status Code Register (0x0134:0x0135)
+ */
+#define ESC_USER_RAM_BYTE1_ALSCR_MASK (0x2U)
+#define ESC_USER_RAM_BYTE1_ALSCR_SHIFT (1U)
+#define ESC_USER_RAM_BYTE1_ALSCR_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE1_ALSCR_SHIFT) & ESC_USER_RAM_BYTE1_ALSCR_MASK)
+#define ESC_USER_RAM_BYTE1_ALSCR_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE1_ALSCR_MASK) >> ESC_USER_RAM_BYTE1_ALSCR_SHIFT)
+
+/*
+ * EDLCR (RW)
+ *
+ * Extended DL Control Register (0x0102:0x0103)
+ */
+#define ESC_USER_RAM_BYTE1_EDLCR_MASK (0x1U)
+#define ESC_USER_RAM_BYTE1_EDLCR_SHIFT (0U)
+#define ESC_USER_RAM_BYTE1_EDLCR_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE1_EDLCR_SHIFT) & ESC_USER_RAM_BYTE1_EDLCR_MASK)
+#define ESC_USER_RAM_BYTE1_EDLCR_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE1_EDLCR_MASK) >> ESC_USER_RAM_BYTE1_EDLCR_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE2 */
+/*
+ * ESCFG (RW)
+ *
+ * EEPROM Size configurable (0x0502[7]):
+ * 0:EEPROM Size fixed to sizes up to 16 Kbit
+ * 1:EEPROM Size configurable
+ */
+#define ESC_USER_RAM_BYTE2_ESCFG_MASK (0x80U)
+#define ESC_USER_RAM_BYTE2_ESCFG_SHIFT (7U)
+#define ESC_USER_RAM_BYTE2_ESCFG_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE2_ESCFG_SHIFT) & ESC_USER_RAM_BYTE2_ESCFG_MASK)
+#define ESC_USER_RAM_BYTE2_ESCFG_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE2_ESCFG_MASK) >> ESC_USER_RAM_BYTE2_ESCFG_SHIFT)
+
+/*
+ * EPUPEC (RW)
+ *
+ * ECAT Processing Unit/PDI Error Counter
+ * (0x030C:0x030D)
+ */
+#define ESC_USER_RAM_BYTE2_EPUPEC_MASK (0x40U)
+#define ESC_USER_RAM_BYTE2_EPUPEC_SHIFT (6U)
+#define ESC_USER_RAM_BYTE2_EPUPEC_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE2_EPUPEC_SHIFT) & ESC_USER_RAM_BYTE2_EPUPEC_MASK)
+#define ESC_USER_RAM_BYTE2_EPUPEC_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE2_EPUPEC_MASK) >> ESC_USER_RAM_BYTE2_EPUPEC_SHIFT)
+
+/*
+ * DCSMET (RW)
+ *
+ * DC SyncManager Event Times (0x09F0:0x09FF)
+ */
+#define ESC_USER_RAM_BYTE2_DCSMET_MASK (0x20U)
+#define ESC_USER_RAM_BYTE2_DCSMET_SHIFT (5U)
+#define ESC_USER_RAM_BYTE2_DCSMET_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE2_DCSMET_SHIFT) & ESC_USER_RAM_BYTE2_DCSMET_MASK)
+#define ESC_USER_RAM_BYTE2_DCSMET_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE2_DCSMET_MASK) >> ESC_USER_RAM_BYTE2_DCSMET_SHIFT)
+
+/*
+ * RESET (RW)
+ *
+ * Reset (0x0040:0x0041)
+ */
+#define ESC_USER_RAM_BYTE2_RESET_MASK (0x8U)
+#define ESC_USER_RAM_BYTE2_RESET_SHIFT (3U)
+#define ESC_USER_RAM_BYTE2_RESET_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE2_RESET_SHIFT) & ESC_USER_RAM_BYTE2_RESET_MASK)
+#define ESC_USER_RAM_BYTE2_RESET_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE2_RESET_MASK) >> ESC_USER_RAM_BYTE2_RESET_SHIFT)
+
+/*
+ * WP (RW)
+ *
+ * Write Protection (0x0020:0x0031)
+ */
+#define ESC_USER_RAM_BYTE2_WP_MASK (0x4U)
+#define ESC_USER_RAM_BYTE2_WP_SHIFT (2U)
+#define ESC_USER_RAM_BYTE2_WP_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE2_WP_SHIFT) & ESC_USER_RAM_BYTE2_WP_MASK)
+#define ESC_USER_RAM_BYTE2_WP_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE2_WP_MASK) >> ESC_USER_RAM_BYTE2_WP_SHIFT)
+
+/*
+ * WDGCNT (RW)
+ *
+ * Watchdog counters (0x0442:0x0443)
+ */
+#define ESC_USER_RAM_BYTE2_WDGCNT_MASK (0x2U)
+#define ESC_USER_RAM_BYTE2_WDGCNT_SHIFT (1U)
+#define ESC_USER_RAM_BYTE2_WDGCNT_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE2_WDGCNT_SHIFT) & ESC_USER_RAM_BYTE2_WDGCNT_MASK)
+#define ESC_USER_RAM_BYTE2_WDGCNT_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE2_WDGCNT_MASK) >> ESC_USER_RAM_BYTE2_WDGCNT_SHIFT)
+
+/*
+ * WDW (RW)
+ *
+ * Watchdog divider writable (0x0400:0x0401) and
+ * Watchdog PDI (0x0410:0x0411)
+ */
+#define ESC_USER_RAM_BYTE2_WDW_MASK (0x1U)
+#define ESC_USER_RAM_BYTE2_WDW_SHIFT (0U)
+#define ESC_USER_RAM_BYTE2_WDW_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE2_WDW_SHIFT) & ESC_USER_RAM_BYTE2_WDW_MASK)
+#define ESC_USER_RAM_BYTE2_WDW_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE2_WDW_MASK) >> ESC_USER_RAM_BYTE2_WDW_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE3 */
+/*
+ * RLED (RW)
+ *
+ * Run LED (DEV_STATE LED)
+ */
+#define ESC_USER_RAM_BYTE3_RLED_MASK (0x80U)
+#define ESC_USER_RAM_BYTE3_RLED_SHIFT (7U)
+#define ESC_USER_RAM_BYTE3_RLED_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE3_RLED_SHIFT) & ESC_USER_RAM_BYTE3_RLED_MASK)
+#define ESC_USER_RAM_BYTE3_RLED_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE3_RLED_MASK) >> ESC_USER_RAM_BYTE3_RLED_SHIFT)
+
+/*
+ * ELDE (RW)
+ *
+ * Enhanced Link Detection EBUS
+ */
+#define ESC_USER_RAM_BYTE3_ELDE_MASK (0x40U)
+#define ESC_USER_RAM_BYTE3_ELDE_SHIFT (6U)
+#define ESC_USER_RAM_BYTE3_ELDE_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE3_ELDE_SHIFT) & ESC_USER_RAM_BYTE3_ELDE_MASK)
+#define ESC_USER_RAM_BYTE3_ELDE_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE3_ELDE_MASK) >> ESC_USER_RAM_BYTE3_ELDE_SHIFT)
+
+/*
+ * ELDM (RW)
+ *
+ * Enhanced Link Detection MII
+ */
+#define ESC_USER_RAM_BYTE3_ELDM_MASK (0x20U)
+#define ESC_USER_RAM_BYTE3_ELDM_SHIFT (5U)
+#define ESC_USER_RAM_BYTE3_ELDM_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE3_ELDM_SHIFT) & ESC_USER_RAM_BYTE3_ELDM_MASK)
+#define ESC_USER_RAM_BYTE3_ELDM_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE3_ELDM_MASK) >> ESC_USER_RAM_BYTE3_ELDM_SHIFT)
+
+/*
+ * MMI (RW)
+ *
+ * MII Management Interface (0x0510:0x0515)
+ */
+#define ESC_USER_RAM_BYTE3_MMI_MASK (0x10U)
+#define ESC_USER_RAM_BYTE3_MMI_SHIFT (4U)
+#define ESC_USER_RAM_BYTE3_MMI_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE3_MMI_SHIFT) & ESC_USER_RAM_BYTE3_MMI_MASK)
+#define ESC_USER_RAM_BYTE3_MMI_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE3_MMI_MASK) >> ESC_USER_RAM_BYTE3_MMI_SHIFT)
+
+/*
+ * LLC (RW)
+ *
+ * Lost Link Counter (0x0310:0x0313)
+ */
+#define ESC_USER_RAM_BYTE3_LLC_MASK (0x8U)
+#define ESC_USER_RAM_BYTE3_LLC_SHIFT (3U)
+#define ESC_USER_RAM_BYTE3_LLC_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE3_LLC_SHIFT) & ESC_USER_RAM_BYTE3_LLC_MASK)
+#define ESC_USER_RAM_BYTE3_LLC_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE3_LLC_MASK) >> ESC_USER_RAM_BYTE3_LLC_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE4 */
+/*
+ * LDCM (RW)
+ *
+ * Link detection and configuration by MI
+ */
+#define ESC_USER_RAM_BYTE4_LDCM_MASK (0x80U)
+#define ESC_USER_RAM_BYTE4_LDCM_SHIFT (7U)
+#define ESC_USER_RAM_BYTE4_LDCM_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE4_LDCM_SHIFT) & ESC_USER_RAM_BYTE4_LDCM_MASK)
+#define ESC_USER_RAM_BYTE4_LDCM_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE4_LDCM_MASK) >> ESC_USER_RAM_BYTE4_LDCM_SHIFT)
+
+/*
+ * DTLC (RW)
+ *
+ * DC Time loop control assigned to PDI
+ */
+#define ESC_USER_RAM_BYTE4_DTLC_MASK (0x40U)
+#define ESC_USER_RAM_BYTE4_DTLC_SHIFT (6U)
+#define ESC_USER_RAM_BYTE4_DTLC_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE4_DTLC_SHIFT) & ESC_USER_RAM_BYTE4_DTLC_MASK)
+#define ESC_USER_RAM_BYTE4_DTLC_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE4_DTLC_MASK) >> ESC_USER_RAM_BYTE4_DTLC_SHIFT)
+
+/*
+ * DSOU (RW)
+ *
+ * DC Sync Out Unit
+ */
+#define ESC_USER_RAM_BYTE4_DSOU_MASK (0x20U)
+#define ESC_USER_RAM_BYTE4_DSOU_SHIFT (5U)
+#define ESC_USER_RAM_BYTE4_DSOU_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE4_DSOU_SHIFT) & ESC_USER_RAM_BYTE4_DSOU_MASK)
+#define ESC_USER_RAM_BYTE4_DSOU_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE4_DSOU_MASK) >> ESC_USER_RAM_BYTE4_DSOU_SHIFT)
+
+/*
+ * DLIU (RW)
+ *
+ * DC Latch In Unit
+ */
+#define ESC_USER_RAM_BYTE4_DLIU_MASK (0x8U)
+#define ESC_USER_RAM_BYTE4_DLIU_SHIFT (3U)
+#define ESC_USER_RAM_BYTE4_DLIU_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE4_DLIU_SHIFT) & ESC_USER_RAM_BYTE4_DLIU_MASK)
+#define ESC_USER_RAM_BYTE4_DLIU_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE4_DLIU_MASK) >> ESC_USER_RAM_BYTE4_DLIU_SHIFT)
+
+/*
+ * LALED (RW)
+ *
+ * Link/Activity LED
+ */
+#define ESC_USER_RAM_BYTE4_LALED_MASK (0x1U)
+#define ESC_USER_RAM_BYTE4_LALED_SHIFT (0U)
+#define ESC_USER_RAM_BYTE4_LALED_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE4_LALED_SHIFT) & ESC_USER_RAM_BYTE4_LALED_MASK)
+#define ESC_USER_RAM_BYTE4_LALED_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE4_LALED_MASK) >> ESC_USER_RAM_BYTE4_LALED_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE5 */
+/*
+ * DDIOR (RW)
+ *
+ * Disable Digital I/O register (0x0F00:0x0F03)
+ */
+#define ESC_USER_RAM_BYTE5_DDIOR_MASK (0x20U)
+#define ESC_USER_RAM_BYTE5_DDIOR_SHIFT (5U)
+#define ESC_USER_RAM_BYTE5_DDIOR_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE5_DDIOR_SHIFT) & ESC_USER_RAM_BYTE5_DDIOR_MASK)
+#define ESC_USER_RAM_BYTE5_DDIOR_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE5_DDIOR_MASK) >> ESC_USER_RAM_BYTE5_DDIOR_SHIFT)
+
+/*
+ * EEU (RW)
+ *
+ * EEPROM emulation by µController
+ */
+#define ESC_USER_RAM_BYTE5_EEU_MASK (0x4U)
+#define ESC_USER_RAM_BYTE5_EEU_SHIFT (2U)
+#define ESC_USER_RAM_BYTE5_EEU_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE5_EEU_SHIFT) & ESC_USER_RAM_BYTE5_EEU_MASK)
+#define ESC_USER_RAM_BYTE5_EEU_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE5_EEU_MASK) >> ESC_USER_RAM_BYTE5_EEU_SHIFT)
+
+/*
+ * ATS (RW)
+ *
+ * Automatic TX shift
+ */
+#define ESC_USER_RAM_BYTE5_ATS_MASK (0x2U)
+#define ESC_USER_RAM_BYTE5_ATS_SHIFT (1U)
+#define ESC_USER_RAM_BYTE5_ATS_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE5_ATS_SHIFT) & ESC_USER_RAM_BYTE5_ATS_MASK)
+#define ESC_USER_RAM_BYTE5_ATS_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE5_ATS_MASK) >> ESC_USER_RAM_BYTE5_ATS_SHIFT)
+
+/*
+ * MCPP (RW)
+ *
+ * MI control by PDI possible
+ */
+#define ESC_USER_RAM_BYTE5_MCPP_MASK (0x1U)
+#define ESC_USER_RAM_BYTE5_MCPP_SHIFT (0U)
+#define ESC_USER_RAM_BYTE5_MCPP_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE5_MCPP_SHIFT) & ESC_USER_RAM_BYTE5_MCPP_MASK)
+#define ESC_USER_RAM_BYTE5_MCPP_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE5_MCPP_MASK) >> ESC_USER_RAM_BYTE5_MCPP_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE6 */
+/*
+ * RELEDOR (RW)
+ *
+ * RUN/ERR LED Override (0x0138:0x0139)
+ */
+#define ESC_USER_RAM_BYTE6_RELEDOR_MASK (0x4U)
+#define ESC_USER_RAM_BYTE6_RELEDOR_SHIFT (2U)
+#define ESC_USER_RAM_BYTE6_RELEDOR_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE6_RELEDOR_SHIFT) & ESC_USER_RAM_BYTE6_RELEDOR_MASK)
+#define ESC_USER_RAM_BYTE6_RELEDOR_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE6_RELEDOR_MASK) >> ESC_USER_RAM_BYTE6_RELEDOR_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE7 */
+/*
+ * DCST (RW)
+ *
+ * DC System Time (0x0910:0x0936)
+ */
+#define ESC_USER_RAM_BYTE7_DCST_MASK (0x80U)
+#define ESC_USER_RAM_BYTE7_DCST_SHIFT (7U)
+#define ESC_USER_RAM_BYTE7_DCST_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE7_DCST_SHIFT) & ESC_USER_RAM_BYTE7_DCST_MASK)
+#define ESC_USER_RAM_BYTE7_DCST_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE7_DCST_MASK) >> ESC_USER_RAM_BYTE7_DCST_SHIFT)
+
+/*
+ * DCRT (RW)
+ *
+ * DC Receive Times (0x0900:0x090F)
+ */
+#define ESC_USER_RAM_BYTE7_DCRT_MASK (0x40U)
+#define ESC_USER_RAM_BYTE7_DCRT_SHIFT (6U)
+#define ESC_USER_RAM_BYTE7_DCRT_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE7_DCRT_SHIFT) & ESC_USER_RAM_BYTE7_DCRT_MASK)
+#define ESC_USER_RAM_BYTE7_DCRT_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE7_DCRT_MASK) >> ESC_USER_RAM_BYTE7_DCRT_SHIFT)
+
+/*
+ * DCS1D (RW)
+ *
+ * DC Sync1 disable
+ */
+#define ESC_USER_RAM_BYTE7_DCS1D_MASK (0x8U)
+#define ESC_USER_RAM_BYTE7_DCS1D_SHIFT (3U)
+#define ESC_USER_RAM_BYTE7_DCS1D_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE7_DCS1D_SHIFT) & ESC_USER_RAM_BYTE7_DCS1D_MASK)
+#define ESC_USER_RAM_BYTE7_DCS1D_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE7_DCS1D_MASK) >> ESC_USER_RAM_BYTE7_DCS1D_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE8 */
+/*
+ * PPDI (RW)
+ *
+ * PLB PDI
+ */
+#define ESC_USER_RAM_BYTE8_PPDI_MASK (0x20U)
+#define ESC_USER_RAM_BYTE8_PPDI_SHIFT (5U)
+#define ESC_USER_RAM_BYTE8_PPDI_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE8_PPDI_SHIFT) & ESC_USER_RAM_BYTE8_PPDI_MASK)
+#define ESC_USER_RAM_BYTE8_PPDI_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE8_PPDI_MASK) >> ESC_USER_RAM_BYTE8_PPDI_SHIFT)
+
+/*
+ * OPDI (RW)
+ *
+ * OPB PDI
+ */
+#define ESC_USER_RAM_BYTE8_OPDI_MASK (0x10U)
+#define ESC_USER_RAM_BYTE8_OPDI_SHIFT (4U)
+#define ESC_USER_RAM_BYTE8_OPDI_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE8_OPDI_SHIFT) & ESC_USER_RAM_BYTE8_OPDI_MASK)
+#define ESC_USER_RAM_BYTE8_OPDI_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE8_OPDI_MASK) >> ESC_USER_RAM_BYTE8_OPDI_SHIFT)
+
+/*
+ * APDI (RW)
+ *
+ * Avalon PDI
+ */
+#define ESC_USER_RAM_BYTE8_APDI_MASK (0x8U)
+#define ESC_USER_RAM_BYTE8_APDI_SHIFT (3U)
+#define ESC_USER_RAM_BYTE8_APDI_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE8_APDI_SHIFT) & ESC_USER_RAM_BYTE8_APDI_MASK)
+#define ESC_USER_RAM_BYTE8_APDI_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE8_APDI_MASK) >> ESC_USER_RAM_BYTE8_APDI_SHIFT)
+
+/*
+ * PDICEC (RW)
+ *
+ * PDI clears error counter
+ */
+#define ESC_USER_RAM_BYTE8_PDICEC_MASK (0x4U)
+#define ESC_USER_RAM_BYTE8_PDICEC_SHIFT (2U)
+#define ESC_USER_RAM_BYTE8_PDICEC_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE8_PDICEC_SHIFT) & ESC_USER_RAM_BYTE8_PDICEC_MASK)
+#define ESC_USER_RAM_BYTE8_PDICEC_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE8_PDICEC_MASK) >> ESC_USER_RAM_BYTE8_PDICEC_SHIFT)
+
+/*
+ * DC64 (RW)
+ *
+ * DC 64 bit
+ */
+#define ESC_USER_RAM_BYTE8_DC64_MASK (0x1U)
+#define ESC_USER_RAM_BYTE8_DC64_SHIFT (0U)
+#define ESC_USER_RAM_BYTE8_DC64_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE8_DC64_SHIFT) & ESC_USER_RAM_BYTE8_DC64_MASK)
+#define ESC_USER_RAM_BYTE8_DC64_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE8_DC64_MASK) >> ESC_USER_RAM_BYTE8_DC64_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE9 */
+/*
+ * DR (RW)
+ *
+ * Direct RESET
+ */
+#define ESC_USER_RAM_BYTE9_DR_MASK (0x80U)
+#define ESC_USER_RAM_BYTE9_DR_SHIFT (7U)
+#define ESC_USER_RAM_BYTE9_DR_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE9_DR_SHIFT) & ESC_USER_RAM_BYTE9_DR_MASK)
+#define ESC_USER_RAM_BYTE9_DR_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE9_DR_MASK) >> ESC_USER_RAM_BYTE9_DR_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE10 */
+/*
+ * PDIIR (RW)
+ *
+ * PDI Information register (0x014E:0x014F)
+ */
+#define ESC_USER_RAM_BYTE10_PDIIR_MASK (0x80U)
+#define ESC_USER_RAM_BYTE10_PDIIR_SHIFT (7U)
+#define ESC_USER_RAM_BYTE10_PDIIR_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE10_PDIIR_SHIFT) & ESC_USER_RAM_BYTE10_PDIIR_MASK)
+#define ESC_USER_RAM_BYTE10_PDIIR_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE10_PDIIR_MASK) >> ESC_USER_RAM_BYTE10_PDIIR_SHIFT)
+
+/*
+ * PDIFA (RW)
+ *
+ * PDI function acknowledge by PDI write
+ */
+#define ESC_USER_RAM_BYTE10_PDIFA_MASK (0x40U)
+#define ESC_USER_RAM_BYTE10_PDIFA_SHIFT (6U)
+#define ESC_USER_RAM_BYTE10_PDIFA_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE10_PDIFA_SHIFT) & ESC_USER_RAM_BYTE10_PDIFA_MASK)
+#define ESC_USER_RAM_BYTE10_PDIFA_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE10_PDIFA_MASK) >> ESC_USER_RAM_BYTE10_PDIFA_SHIFT)
+
+/*
+ * APDI (RW)
+ *
+ * AXI PDI
+ */
+#define ESC_USER_RAM_BYTE10_APDI_MASK (0x8U)
+#define ESC_USER_RAM_BYTE10_APDI_SHIFT (3U)
+#define ESC_USER_RAM_BYTE10_APDI_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE10_APDI_SHIFT) & ESC_USER_RAM_BYTE10_APDI_MASK)
+#define ESC_USER_RAM_BYTE10_APDI_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE10_APDI_MASK) >> ESC_USER_RAM_BYTE10_APDI_SHIFT)
+
+/*
+ * DCL1D (RW)
+ *
+ * DC Latch1 disable
+ */
+#define ESC_USER_RAM_BYTE10_DCL1D_MASK (0x4U)
+#define ESC_USER_RAM_BYTE10_DCL1D_SHIFT (2U)
+#define ESC_USER_RAM_BYTE10_DCL1D_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE10_DCL1D_SHIFT) & ESC_USER_RAM_BYTE10_DCL1D_MASK)
+#define ESC_USER_RAM_BYTE10_DCL1D_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE10_DCL1D_MASK) >> ESC_USER_RAM_BYTE10_DCL1D_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE11 */
+/*
+ * LEDTST (RW)
+ *
+ * LED test
+ */
+#define ESC_USER_RAM_BYTE11_LEDTST_MASK (0x8U)
+#define ESC_USER_RAM_BYTE11_LEDTST_SHIFT (3U)
+#define ESC_USER_RAM_BYTE11_LEDTST_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE11_LEDTST_SHIFT) & ESC_USER_RAM_BYTE11_LEDTST_MASK)
+#define ESC_USER_RAM_BYTE11_LEDTST_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE11_LEDTST_MASK) >> ESC_USER_RAM_BYTE11_LEDTST_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE14 */
+/*
+ * DIOBS (RW)
+ *
+ * Digital I/O PDI byte size
+ */
+#define ESC_USER_RAM_BYTE14_DIOBS_MASK (0xC0U)
+#define ESC_USER_RAM_BYTE14_DIOBS_SHIFT (6U)
+#define ESC_USER_RAM_BYTE14_DIOBS_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE14_DIOBS_SHIFT) & ESC_USER_RAM_BYTE14_DIOBS_MASK)
+#define ESC_USER_RAM_BYTE14_DIOBS_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE14_DIOBS_MASK) >> ESC_USER_RAM_BYTE14_DIOBS_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE15 */
+/*
+ * AUCPDI (RW)
+ *
+ * Asynchronous µC PDI
+ */
+#define ESC_USER_RAM_BYTE15_AUCPDI_MASK (0x10U)
+#define ESC_USER_RAM_BYTE15_AUCPDI_SHIFT (4U)
+#define ESC_USER_RAM_BYTE15_AUCPDI_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE15_AUCPDI_SHIFT) & ESC_USER_RAM_BYTE15_AUCPDI_MASK)
+#define ESC_USER_RAM_BYTE15_AUCPDI_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE15_AUCPDI_MASK) >> ESC_USER_RAM_BYTE15_AUCPDI_SHIFT)
+
+/*
+ * SSPDI (RW)
+ *
+ * SPI Slave PDI
+ */
+#define ESC_USER_RAM_BYTE15_SSPDI_MASK (0x8U)
+#define ESC_USER_RAM_BYTE15_SSPDI_SHIFT (3U)
+#define ESC_USER_RAM_BYTE15_SSPDI_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE15_SSPDI_SHIFT) & ESC_USER_RAM_BYTE15_SSPDI_MASK)
+#define ESC_USER_RAM_BYTE15_SSPDI_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE15_SSPDI_MASK) >> ESC_USER_RAM_BYTE15_SSPDI_SHIFT)
+
+/*
+ * DIOPDI (RW)
+ *
+ * Digital I/O PDI
+ */
+#define ESC_USER_RAM_BYTE15_DIOPDI_MASK (0x4U)
+#define ESC_USER_RAM_BYTE15_DIOPDI_SHIFT (2U)
+#define ESC_USER_RAM_BYTE15_DIOPDI_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE15_DIOPDI_SHIFT) & ESC_USER_RAM_BYTE15_DIOPDI_MASK)
+#define ESC_USER_RAM_BYTE15_DIOPDI_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE15_DIOPDI_MASK) >> ESC_USER_RAM_BYTE15_DIOPDI_SHIFT)
+
+/* Bitfield definition for register: USER_RAM_BYTE19 */
+/*
+ * SCP (RW)
+ *
+ * Security CPLD protection
+ */
+#define ESC_USER_RAM_BYTE19_SCP_MASK (0x40U)
+#define ESC_USER_RAM_BYTE19_SCP_SHIFT (6U)
+#define ESC_USER_RAM_BYTE19_SCP_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE19_SCP_SHIFT) & ESC_USER_RAM_BYTE19_SCP_MASK)
+#define ESC_USER_RAM_BYTE19_SCP_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE19_SCP_MASK) >> ESC_USER_RAM_BYTE19_SCP_SHIFT)
+
+/*
+ * RMII (RW)
+ *
+ * RMII
+ */
+#define ESC_USER_RAM_BYTE19_RMII_MASK (0x20U)
+#define ESC_USER_RAM_BYTE19_RMII_SHIFT (5U)
+#define ESC_USER_RAM_BYTE19_RMII_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE19_RMII_SHIFT) & ESC_USER_RAM_BYTE19_RMII_MASK)
+#define ESC_USER_RAM_BYTE19_RMII_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE19_RMII_MASK) >> ESC_USER_RAM_BYTE19_RMII_SHIFT)
+
+/*
+ * URGP (RW)
+ *
+ * Use RGMII GTX_CLK phase shifted clock input
+ */
+#define ESC_USER_RAM_BYTE19_URGP_MASK (0x10U)
+#define ESC_USER_RAM_BYTE19_URGP_SHIFT (4U)
+#define ESC_USER_RAM_BYTE19_URGP_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE19_URGP_SHIFT) & ESC_USER_RAM_BYTE19_URGP_MASK)
+#define ESC_USER_RAM_BYTE19_URGP_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE19_URGP_MASK) >> ESC_USER_RAM_BYTE19_URGP_SHIFT)
+
+/*
+ * CIA (RW)
+ *
+ * CLK_PDI_EXT is asynchronous
+ */
+#define ESC_USER_RAM_BYTE19_CIA_MASK (0x4U)
+#define ESC_USER_RAM_BYTE19_CIA_SHIFT (2U)
+#define ESC_USER_RAM_BYTE19_CIA_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE19_CIA_SHIFT) & ESC_USER_RAM_BYTE19_CIA_MASK)
+#define ESC_USER_RAM_BYTE19_CIA_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE19_CIA_MASK) >> ESC_USER_RAM_BYTE19_CIA_SHIFT)
+
+/*
+ * IPARO (RW)
+ *
+ * Individual PHY address read out (0x0510[7:3])
+ */
+#define ESC_USER_RAM_BYTE19_IPARO_MASK (0x2U)
+#define ESC_USER_RAM_BYTE19_IPARO_SHIFT (1U)
+#define ESC_USER_RAM_BYTE19_IPARO_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE19_IPARO_SHIFT) & ESC_USER_RAM_BYTE19_IPARO_MASK)
+#define ESC_USER_RAM_BYTE19_IPARO_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE19_IPARO_MASK) >> ESC_USER_RAM_BYTE19_IPARO_SHIFT)
+
+/*
+ * RGMII (RW)
+ *
+ * RGMII
+ */
+#define ESC_USER_RAM_BYTE19_RGMII_MASK (0x1U)
+#define ESC_USER_RAM_BYTE19_RGMII_SHIFT (0U)
+#define ESC_USER_RAM_BYTE19_RGMII_SET(x) (((uint8_t)(x) << ESC_USER_RAM_BYTE19_RGMII_SHIFT) & ESC_USER_RAM_BYTE19_RGMII_MASK)
+#define ESC_USER_RAM_BYTE19_RGMII_GET(x) (((uint8_t)(x) & ESC_USER_RAM_BYTE19_RGMII_MASK) >> ESC_USER_RAM_BYTE19_RGMII_SHIFT)
+
+/* Bitfield definition for register: PDRAM */
+/*
+ * DATA (RW)
+ *
+ * Input Data
+ */
+#define ESC_PDRAM_DATA_MASK (0xFFFFFFFFUL)
+#define ESC_PDRAM_DATA_SHIFT (0U)
+#define ESC_PDRAM_DATA_SET(x) (((uint32_t)(x) << ESC_PDRAM_DATA_SHIFT) & ESC_PDRAM_DATA_MASK)
+#define ESC_PDRAM_DATA_GET(x) (((uint32_t)(x) & ESC_PDRAM_DATA_MASK) >> ESC_PDRAM_DATA_SHIFT)
+
+/* Bitfield definition for register: PDRAM_ALS */
+/*
+ * DATA (RW)
+ *
+ */
+#define ESC_PDRAM_ALS_DATA_MASK (0xFFFFFFFFUL)
+#define ESC_PDRAM_ALS_DATA_SHIFT (0U)
+#define ESC_PDRAM_ALS_DATA_SET(x) (((uint32_t)(x) << ESC_PDRAM_ALS_DATA_SHIFT) & ESC_PDRAM_ALS_DATA_MASK)
+#define ESC_PDRAM_ALS_DATA_GET(x) (((uint32_t)(x) & ESC_PDRAM_ALS_DATA_MASK) >> ESC_PDRAM_ALS_DATA_SHIFT)
+
+/* Bitfield definition for register: GPR_CFG0 */
+/*
+ * CLK100_EN (RW)
+ *
+ */
+#define ESC_GPR_CFG0_CLK100_EN_MASK (0x2000U)
+#define ESC_GPR_CFG0_CLK100_EN_SHIFT (13U)
+#define ESC_GPR_CFG0_CLK100_EN_SET(x) (((uint32_t)(x) << ESC_GPR_CFG0_CLK100_EN_SHIFT) & ESC_GPR_CFG0_CLK100_EN_MASK)
+#define ESC_GPR_CFG0_CLK100_EN_GET(x) (((uint32_t)(x) & ESC_GPR_CFG0_CLK100_EN_MASK) >> ESC_GPR_CFG0_CLK100_EN_SHIFT)
+
+/*
+ * EEPROM_EMU (RW)
+ *
+ * 1 is EEPROM emulation mode (default)
+ */
+#define ESC_GPR_CFG0_EEPROM_EMU_MASK (0x1000U)
+#define ESC_GPR_CFG0_EEPROM_EMU_SHIFT (12U)
+#define ESC_GPR_CFG0_EEPROM_EMU_SET(x) (((uint32_t)(x) << ESC_GPR_CFG0_EEPROM_EMU_SHIFT) & ESC_GPR_CFG0_EEPROM_EMU_MASK)
+#define ESC_GPR_CFG0_EEPROM_EMU_GET(x) (((uint32_t)(x) & ESC_GPR_CFG0_EEPROM_EMU_MASK) >> ESC_GPR_CFG0_EEPROM_EMU_SHIFT)
+
+/*
+ * I2C_SCLK_EN (RW)
+ *
+ */
+#define ESC_GPR_CFG0_I2C_SCLK_EN_MASK (0x8U)
+#define ESC_GPR_CFG0_I2C_SCLK_EN_SHIFT (3U)
+#define ESC_GPR_CFG0_I2C_SCLK_EN_SET(x) (((uint32_t)(x) << ESC_GPR_CFG0_I2C_SCLK_EN_SHIFT) & ESC_GPR_CFG0_I2C_SCLK_EN_MASK)
+#define ESC_GPR_CFG0_I2C_SCLK_EN_GET(x) (((uint32_t)(x) & ESC_GPR_CFG0_I2C_SCLK_EN_MASK) >> ESC_GPR_CFG0_I2C_SCLK_EN_SHIFT)
+
+/*
+ * PROM_SIZE (RW)
+ *
+ * Sets EEPROM size:
+ * 0:up to 16 kbit EEPROM
+ * 1:32 kbit-4Mbit EEPROM
+ */
+#define ESC_GPR_CFG0_PROM_SIZE_MASK (0x1U)
+#define ESC_GPR_CFG0_PROM_SIZE_SHIFT (0U)
+#define ESC_GPR_CFG0_PROM_SIZE_SET(x) (((uint32_t)(x) << ESC_GPR_CFG0_PROM_SIZE_SHIFT) & ESC_GPR_CFG0_PROM_SIZE_MASK)
+#define ESC_GPR_CFG0_PROM_SIZE_GET(x) (((uint32_t)(x) & ESC_GPR_CFG0_PROM_SIZE_MASK) >> ESC_GPR_CFG0_PROM_SIZE_SHIFT)
+
+/* Bitfield definition for register: GPR_CFG1 */
+/*
+ * SYNC1_IRQ_EN (RW)
+ *
+ */
+#define ESC_GPR_CFG1_SYNC1_IRQ_EN_MASK (0x80000000UL)
+#define ESC_GPR_CFG1_SYNC1_IRQ_EN_SHIFT (31U)
+#define ESC_GPR_CFG1_SYNC1_IRQ_EN_SET(x) (((uint32_t)(x) << ESC_GPR_CFG1_SYNC1_IRQ_EN_SHIFT) & ESC_GPR_CFG1_SYNC1_IRQ_EN_MASK)
+#define ESC_GPR_CFG1_SYNC1_IRQ_EN_GET(x) (((uint32_t)(x) & ESC_GPR_CFG1_SYNC1_IRQ_EN_MASK) >> ESC_GPR_CFG1_SYNC1_IRQ_EN_SHIFT)
+
+/*
+ * SYNC0_IRQ_EN (RW)
+ *
+ */
+#define ESC_GPR_CFG1_SYNC0_IRQ_EN_MASK (0x40000000UL)
+#define ESC_GPR_CFG1_SYNC0_IRQ_EN_SHIFT (30U)
+#define ESC_GPR_CFG1_SYNC0_IRQ_EN_SET(x) (((uint32_t)(x) << ESC_GPR_CFG1_SYNC0_IRQ_EN_SHIFT) & ESC_GPR_CFG1_SYNC0_IRQ_EN_MASK)
+#define ESC_GPR_CFG1_SYNC0_IRQ_EN_GET(x) (((uint32_t)(x) & ESC_GPR_CFG1_SYNC0_IRQ_EN_MASK) >> ESC_GPR_CFG1_SYNC0_IRQ_EN_SHIFT)
+
+/*
+ * RSTO_IRQ_EN (RW)
+ *
+ */
+#define ESC_GPR_CFG1_RSTO_IRQ_EN_MASK (0x20000000UL)
+#define ESC_GPR_CFG1_RSTO_IRQ_EN_SHIFT (29U)
+#define ESC_GPR_CFG1_RSTO_IRQ_EN_SET(x) (((uint32_t)(x) << ESC_GPR_CFG1_RSTO_IRQ_EN_SHIFT) & ESC_GPR_CFG1_RSTO_IRQ_EN_MASK)
+#define ESC_GPR_CFG1_RSTO_IRQ_EN_GET(x) (((uint32_t)(x) & ESC_GPR_CFG1_RSTO_IRQ_EN_MASK) >> ESC_GPR_CFG1_RSTO_IRQ_EN_SHIFT)
+
+/*
+ * SYNC1_DMA_EN (RW)
+ *
+ */
+#define ESC_GPR_CFG1_SYNC1_DMA_EN_MASK (0x2000U)
+#define ESC_GPR_CFG1_SYNC1_DMA_EN_SHIFT (13U)
+#define ESC_GPR_CFG1_SYNC1_DMA_EN_SET(x) (((uint32_t)(x) << ESC_GPR_CFG1_SYNC1_DMA_EN_SHIFT) & ESC_GPR_CFG1_SYNC1_DMA_EN_MASK)
+#define ESC_GPR_CFG1_SYNC1_DMA_EN_GET(x) (((uint32_t)(x) & ESC_GPR_CFG1_SYNC1_DMA_EN_MASK) >> ESC_GPR_CFG1_SYNC1_DMA_EN_SHIFT)
+
+/*
+ * SYNC0_DMA_EN (RW)
+ *
+ */
+#define ESC_GPR_CFG1_SYNC0_DMA_EN_MASK (0x1000U)
+#define ESC_GPR_CFG1_SYNC0_DMA_EN_SHIFT (12U)
+#define ESC_GPR_CFG1_SYNC0_DMA_EN_SET(x) (((uint32_t)(x) << ESC_GPR_CFG1_SYNC0_DMA_EN_SHIFT) & ESC_GPR_CFG1_SYNC0_DMA_EN_MASK)
+#define ESC_GPR_CFG1_SYNC0_DMA_EN_GET(x) (((uint32_t)(x) & ESC_GPR_CFG1_SYNC0_DMA_EN_MASK) >> ESC_GPR_CFG1_SYNC0_DMA_EN_SHIFT)
+
+/*
+ * LATCH1_FROM_IO (RW)
+ *
+ * 0:from NTM
+ */
+#define ESC_GPR_CFG1_LATCH1_FROM_IO_MASK (0x200U)
+#define ESC_GPR_CFG1_LATCH1_FROM_IO_SHIFT (9U)
+#define ESC_GPR_CFG1_LATCH1_FROM_IO_SET(x) (((uint32_t)(x) << ESC_GPR_CFG1_LATCH1_FROM_IO_SHIFT) & ESC_GPR_CFG1_LATCH1_FROM_IO_MASK)
+#define ESC_GPR_CFG1_LATCH1_FROM_IO_GET(x) (((uint32_t)(x) & ESC_GPR_CFG1_LATCH1_FROM_IO_MASK) >> ESC_GPR_CFG1_LATCH1_FROM_IO_SHIFT)
+
+/*
+ * LATCH0_FROM_IO (RW)
+ *
+ * 0:from TRIGGER_MUX
+ */
+#define ESC_GPR_CFG1_LATCH0_FROM_IO_MASK (0x100U)
+#define ESC_GPR_CFG1_LATCH0_FROM_IO_SHIFT (8U)
+#define ESC_GPR_CFG1_LATCH0_FROM_IO_SET(x) (((uint32_t)(x) << ESC_GPR_CFG1_LATCH0_FROM_IO_SHIFT) & ESC_GPR_CFG1_LATCH0_FROM_IO_MASK)
+#define ESC_GPR_CFG1_LATCH0_FROM_IO_GET(x) (((uint32_t)(x) & ESC_GPR_CFG1_LATCH0_FROM_IO_MASK) >> ESC_GPR_CFG1_LATCH0_FROM_IO_SHIFT)
+
+/*
+ * RSTO_OVRD (RW)
+ *
+ */
+#define ESC_GPR_CFG1_RSTO_OVRD_MASK (0x80U)
+#define ESC_GPR_CFG1_RSTO_OVRD_SHIFT (7U)
+#define ESC_GPR_CFG1_RSTO_OVRD_SET(x) (((uint32_t)(x) << ESC_GPR_CFG1_RSTO_OVRD_SHIFT) & ESC_GPR_CFG1_RSTO_OVRD_MASK)
+#define ESC_GPR_CFG1_RSTO_OVRD_GET(x) (((uint32_t)(x) & ESC_GPR_CFG1_RSTO_OVRD_MASK) >> ESC_GPR_CFG1_RSTO_OVRD_SHIFT)
+
+/*
+ * RSTO_OVRD_ENJ (RW)
+ *
+ */
+#define ESC_GPR_CFG1_RSTO_OVRD_ENJ_MASK (0x40U)
+#define ESC_GPR_CFG1_RSTO_OVRD_ENJ_SHIFT (6U)
+#define ESC_GPR_CFG1_RSTO_OVRD_ENJ_SET(x) (((uint32_t)(x) << ESC_GPR_CFG1_RSTO_OVRD_ENJ_SHIFT) & ESC_GPR_CFG1_RSTO_OVRD_ENJ_MASK)
+#define ESC_GPR_CFG1_RSTO_OVRD_ENJ_GET(x) (((uint32_t)(x) & ESC_GPR_CFG1_RSTO_OVRD_ENJ_MASK) >> ESC_GPR_CFG1_RSTO_OVRD_ENJ_SHIFT)
+
+/* Bitfield definition for register: GPR_CFG2 */
+/*
+ * NMII_LINK2_FROM_IO (RW)
+ *
+ */
+#define ESC_GPR_CFG2_NMII_LINK2_FROM_IO_MASK (0x20000000UL)
+#define ESC_GPR_CFG2_NMII_LINK2_FROM_IO_SHIFT (29U)
+#define ESC_GPR_CFG2_NMII_LINK2_FROM_IO_SET(x) (((uint32_t)(x) << ESC_GPR_CFG2_NMII_LINK2_FROM_IO_SHIFT) & ESC_GPR_CFG2_NMII_LINK2_FROM_IO_MASK)
+#define ESC_GPR_CFG2_NMII_LINK2_FROM_IO_GET(x) (((uint32_t)(x) & ESC_GPR_CFG2_NMII_LINK2_FROM_IO_MASK) >> ESC_GPR_CFG2_NMII_LINK2_FROM_IO_SHIFT)
+
+/*
+ * NMII_LINK2_GPR (RW)
+ *
+ */
+#define ESC_GPR_CFG2_NMII_LINK2_GPR_MASK (0x10000000UL)
+#define ESC_GPR_CFG2_NMII_LINK2_GPR_SHIFT (28U)
+#define ESC_GPR_CFG2_NMII_LINK2_GPR_SET(x) (((uint32_t)(x) << ESC_GPR_CFG2_NMII_LINK2_GPR_SHIFT) & ESC_GPR_CFG2_NMII_LINK2_GPR_MASK)
+#define ESC_GPR_CFG2_NMII_LINK2_GPR_GET(x) (((uint32_t)(x) & ESC_GPR_CFG2_NMII_LINK2_GPR_MASK) >> ESC_GPR_CFG2_NMII_LINK2_GPR_SHIFT)
+
+/*
+ * NMII_LINK1_FROM_IO (RW)
+ *
+ */
+#define ESC_GPR_CFG2_NMII_LINK1_FROM_IO_MASK (0x2000000UL)
+#define ESC_GPR_CFG2_NMII_LINK1_FROM_IO_SHIFT (25U)
+#define ESC_GPR_CFG2_NMII_LINK1_FROM_IO_SET(x) (((uint32_t)(x) << ESC_GPR_CFG2_NMII_LINK1_FROM_IO_SHIFT) & ESC_GPR_CFG2_NMII_LINK1_FROM_IO_MASK)
+#define ESC_GPR_CFG2_NMII_LINK1_FROM_IO_GET(x) (((uint32_t)(x) & ESC_GPR_CFG2_NMII_LINK1_FROM_IO_MASK) >> ESC_GPR_CFG2_NMII_LINK1_FROM_IO_SHIFT)
+
+/*
+ * NMII_LINK1_GPR (RW)
+ *
+ */
+#define ESC_GPR_CFG2_NMII_LINK1_GPR_MASK (0x1000000UL)
+#define ESC_GPR_CFG2_NMII_LINK1_GPR_SHIFT (24U)
+#define ESC_GPR_CFG2_NMII_LINK1_GPR_SET(x) (((uint32_t)(x) << ESC_GPR_CFG2_NMII_LINK1_GPR_SHIFT) & ESC_GPR_CFG2_NMII_LINK1_GPR_MASK)
+#define ESC_GPR_CFG2_NMII_LINK1_GPR_GET(x) (((uint32_t)(x) & ESC_GPR_CFG2_NMII_LINK1_GPR_MASK) >> ESC_GPR_CFG2_NMII_LINK1_GPR_SHIFT)
+
+/*
+ * NMII_LINK0_FROM_IO (RW)
+ *
+ */
+#define ESC_GPR_CFG2_NMII_LINK0_FROM_IO_MASK (0x200000UL)
+#define ESC_GPR_CFG2_NMII_LINK0_FROM_IO_SHIFT (21U)
+#define ESC_GPR_CFG2_NMII_LINK0_FROM_IO_SET(x) (((uint32_t)(x) << ESC_GPR_CFG2_NMII_LINK0_FROM_IO_SHIFT) & ESC_GPR_CFG2_NMII_LINK0_FROM_IO_MASK)
+#define ESC_GPR_CFG2_NMII_LINK0_FROM_IO_GET(x) (((uint32_t)(x) & ESC_GPR_CFG2_NMII_LINK0_FROM_IO_MASK) >> ESC_GPR_CFG2_NMII_LINK0_FROM_IO_SHIFT)
+
+/*
+ * NMII_LINK0_GPR (RW)
+ *
+ */
+#define ESC_GPR_CFG2_NMII_LINK0_GPR_MASK (0x100000UL)
+#define ESC_GPR_CFG2_NMII_LINK0_GPR_SHIFT (20U)
+#define ESC_GPR_CFG2_NMII_LINK0_GPR_SET(x) (((uint32_t)(x) << ESC_GPR_CFG2_NMII_LINK0_GPR_SHIFT) & ESC_GPR_CFG2_NMII_LINK0_GPR_MASK)
+#define ESC_GPR_CFG2_NMII_LINK0_GPR_GET(x) (((uint32_t)(x) & ESC_GPR_CFG2_NMII_LINK0_GPR_MASK) >> ESC_GPR_CFG2_NMII_LINK0_GPR_SHIFT)
+
+/* Bitfield definition for register: PHY_CFG0 */
+/*
+ * MAC_SPEED (RW)
+ *
+ * 1:100M
+ */
+#define ESC_PHY_CFG0_MAC_SPEED_MASK (0x40000000UL)
+#define ESC_PHY_CFG0_MAC_SPEED_SHIFT (30U)
+#define ESC_PHY_CFG0_MAC_SPEED_SET(x) (((uint32_t)(x) << ESC_PHY_CFG0_MAC_SPEED_SHIFT) & ESC_PHY_CFG0_MAC_SPEED_MASK)
+#define ESC_PHY_CFG0_MAC_SPEED_GET(x) (((uint32_t)(x) & ESC_PHY_CFG0_MAC_SPEED_MASK) >> ESC_PHY_CFG0_MAC_SPEED_SHIFT)
+
+/*
+ * PHY_OFFSET_VAL (RW)
+ *
+ */
+#define ESC_PHY_CFG0_PHY_OFFSET_VAL_MASK (0x1F000000UL)
+#define ESC_PHY_CFG0_PHY_OFFSET_VAL_SHIFT (24U)
+#define ESC_PHY_CFG0_PHY_OFFSET_VAL_SET(x) (((uint32_t)(x) << ESC_PHY_CFG0_PHY_OFFSET_VAL_SHIFT) & ESC_PHY_CFG0_PHY_OFFSET_VAL_MASK)
+#define ESC_PHY_CFG0_PHY_OFFSET_VAL_GET(x) (((uint32_t)(x) & ESC_PHY_CFG0_PHY_OFFSET_VAL_MASK) >> ESC_PHY_CFG0_PHY_OFFSET_VAL_SHIFT)
+
+/*
+ * PORT2_RMII_EN (RW)
+ *
+ */
+#define ESC_PHY_CFG0_PORT2_RMII_EN_MASK (0x800000UL)
+#define ESC_PHY_CFG0_PORT2_RMII_EN_SHIFT (23U)
+#define ESC_PHY_CFG0_PORT2_RMII_EN_SET(x) (((uint32_t)(x) << ESC_PHY_CFG0_PORT2_RMII_EN_SHIFT) & ESC_PHY_CFG0_PORT2_RMII_EN_MASK)
+#define ESC_PHY_CFG0_PORT2_RMII_EN_GET(x) (((uint32_t)(x) & ESC_PHY_CFG0_PORT2_RMII_EN_MASK) >> ESC_PHY_CFG0_PORT2_RMII_EN_SHIFT)
+
+/*
+ * PORT1_RMII_EN (RW)
+ *
+ */
+#define ESC_PHY_CFG0_PORT1_RMII_EN_MASK (0x8000U)
+#define ESC_PHY_CFG0_PORT1_RMII_EN_SHIFT (15U)
+#define ESC_PHY_CFG0_PORT1_RMII_EN_SET(x) (((uint32_t)(x) << ESC_PHY_CFG0_PORT1_RMII_EN_SHIFT) & ESC_PHY_CFG0_PORT1_RMII_EN_MASK)
+#define ESC_PHY_CFG0_PORT1_RMII_EN_GET(x) (((uint32_t)(x) & ESC_PHY_CFG0_PORT1_RMII_EN_MASK) >> ESC_PHY_CFG0_PORT1_RMII_EN_SHIFT)
+
+/*
+ * PORT0_RMII_EN (RW)
+ *
+ */
+#define ESC_PHY_CFG0_PORT0_RMII_EN_MASK (0x80U)
+#define ESC_PHY_CFG0_PORT0_RMII_EN_SHIFT (7U)
+#define ESC_PHY_CFG0_PORT0_RMII_EN_SET(x) (((uint32_t)(x) << ESC_PHY_CFG0_PORT0_RMII_EN_SHIFT) & ESC_PHY_CFG0_PORT0_RMII_EN_MASK)
+#define ESC_PHY_CFG0_PORT0_RMII_EN_GET(x) (((uint32_t)(x) & ESC_PHY_CFG0_PORT0_RMII_EN_MASK) >> ESC_PHY_CFG0_PORT0_RMII_EN_SHIFT)
+
+/* Bitfield definition for register: PHY_CFG1 */
+/*
+ * RMII_REFCLK_SEL (RW)
+ *
+ * 0:use RXCK as 50M refclk. 1:use TXCK as 50M refclk
+ */
+#define ESC_PHY_CFG1_RMII_REFCLK_SEL_MASK (0x700U)
+#define ESC_PHY_CFG1_RMII_REFCLK_SEL_SHIFT (8U)
+#define ESC_PHY_CFG1_RMII_REFCLK_SEL_SET(x) (((uint32_t)(x) << ESC_PHY_CFG1_RMII_REFCLK_SEL_SHIFT) & ESC_PHY_CFG1_RMII_REFCLK_SEL_MASK)
+#define ESC_PHY_CFG1_RMII_REFCLK_SEL_GET(x) (((uint32_t)(x) & ESC_PHY_CFG1_RMII_REFCLK_SEL_MASK) >> ESC_PHY_CFG1_RMII_REFCLK_SEL_SHIFT)
+
+/*
+ * REFCK_25M_INV (RW)
+ *
+ */
+#define ESC_PHY_CFG1_REFCK_25M_INV_MASK (0x80U)
+#define ESC_PHY_CFG1_REFCK_25M_INV_SHIFT (7U)
+#define ESC_PHY_CFG1_REFCK_25M_INV_SET(x) (((uint32_t)(x) << ESC_PHY_CFG1_REFCK_25M_INV_SHIFT) & ESC_PHY_CFG1_REFCK_25M_INV_MASK)
+#define ESC_PHY_CFG1_REFCK_25M_INV_GET(x) (((uint32_t)(x) & ESC_PHY_CFG1_REFCK_25M_INV_MASK) >> ESC_PHY_CFG1_REFCK_25M_INV_SHIFT)
+
+/*
+ * RMII_P2_RXCK_REFCLK_OE (RW)
+ *
+ */
+#define ESC_PHY_CFG1_RMII_P2_RXCK_REFCLK_OE_MASK (0x40U)
+#define ESC_PHY_CFG1_RMII_P2_RXCK_REFCLK_OE_SHIFT (6U)
+#define ESC_PHY_CFG1_RMII_P2_RXCK_REFCLK_OE_SET(x) (((uint32_t)(x) << ESC_PHY_CFG1_RMII_P2_RXCK_REFCLK_OE_SHIFT) & ESC_PHY_CFG1_RMII_P2_RXCK_REFCLK_OE_MASK)
+#define ESC_PHY_CFG1_RMII_P2_RXCK_REFCLK_OE_GET(x) (((uint32_t)(x) & ESC_PHY_CFG1_RMII_P2_RXCK_REFCLK_OE_MASK) >> ESC_PHY_CFG1_RMII_P2_RXCK_REFCLK_OE_SHIFT)
+
+/*
+ * RMII_P1_RXCK_REFCLK_OE (RW)
+ *
+ */
+#define ESC_PHY_CFG1_RMII_P1_RXCK_REFCLK_OE_MASK (0x20U)
+#define ESC_PHY_CFG1_RMII_P1_RXCK_REFCLK_OE_SHIFT (5U)
+#define ESC_PHY_CFG1_RMII_P1_RXCK_REFCLK_OE_SET(x) (((uint32_t)(x) << ESC_PHY_CFG1_RMII_P1_RXCK_REFCLK_OE_SHIFT) & ESC_PHY_CFG1_RMII_P1_RXCK_REFCLK_OE_MASK)
+#define ESC_PHY_CFG1_RMII_P1_RXCK_REFCLK_OE_GET(x) (((uint32_t)(x) & ESC_PHY_CFG1_RMII_P1_RXCK_REFCLK_OE_MASK) >> ESC_PHY_CFG1_RMII_P1_RXCK_REFCLK_OE_SHIFT)
+
+/*
+ * RMII_P0_RXCK_REFCLK_OE (RW)
+ *
+ */
+#define ESC_PHY_CFG1_RMII_P0_RXCK_REFCLK_OE_MASK (0x10U)
+#define ESC_PHY_CFG1_RMII_P0_RXCK_REFCLK_OE_SHIFT (4U)
+#define ESC_PHY_CFG1_RMII_P0_RXCK_REFCLK_OE_SET(x) (((uint32_t)(x) << ESC_PHY_CFG1_RMII_P0_RXCK_REFCLK_OE_SHIFT) & ESC_PHY_CFG1_RMII_P0_RXCK_REFCLK_OE_MASK)
+#define ESC_PHY_CFG1_RMII_P0_RXCK_REFCLK_OE_GET(x) (((uint32_t)(x) & ESC_PHY_CFG1_RMII_P0_RXCK_REFCLK_OE_MASK) >> ESC_PHY_CFG1_RMII_P0_RXCK_REFCLK_OE_SHIFT)
+
+/*
+ * REFCK_25M_OE (RW)
+ *
+ */
+#define ESC_PHY_CFG1_REFCK_25M_OE_MASK (0x8U)
+#define ESC_PHY_CFG1_REFCK_25M_OE_SHIFT (3U)
+#define ESC_PHY_CFG1_REFCK_25M_OE_SET(x) (((uint32_t)(x) << ESC_PHY_CFG1_REFCK_25M_OE_SHIFT) & ESC_PHY_CFG1_REFCK_25M_OE_MASK)
+#define ESC_PHY_CFG1_REFCK_25M_OE_GET(x) (((uint32_t)(x) & ESC_PHY_CFG1_REFCK_25M_OE_MASK) >> ESC_PHY_CFG1_REFCK_25M_OE_SHIFT)
+
+/*
+ * RMII_P2_TXCK_REFCLK_OE (RW)
+ *
+ */
+#define ESC_PHY_CFG1_RMII_P2_TXCK_REFCLK_OE_MASK (0x4U)
+#define ESC_PHY_CFG1_RMII_P2_TXCK_REFCLK_OE_SHIFT (2U)
+#define ESC_PHY_CFG1_RMII_P2_TXCK_REFCLK_OE_SET(x) (((uint32_t)(x) << ESC_PHY_CFG1_RMII_P2_TXCK_REFCLK_OE_SHIFT) & ESC_PHY_CFG1_RMII_P2_TXCK_REFCLK_OE_MASK)
+#define ESC_PHY_CFG1_RMII_P2_TXCK_REFCLK_OE_GET(x) (((uint32_t)(x) & ESC_PHY_CFG1_RMII_P2_TXCK_REFCLK_OE_MASK) >> ESC_PHY_CFG1_RMII_P2_TXCK_REFCLK_OE_SHIFT)
+
+/*
+ * RMII_P1_TXCK_REFCLK_OE (RW)
+ *
+ */
+#define ESC_PHY_CFG1_RMII_P1_TXCK_REFCLK_OE_MASK (0x2U)
+#define ESC_PHY_CFG1_RMII_P1_TXCK_REFCLK_OE_SHIFT (1U)
+#define ESC_PHY_CFG1_RMII_P1_TXCK_REFCLK_OE_SET(x) (((uint32_t)(x) << ESC_PHY_CFG1_RMII_P1_TXCK_REFCLK_OE_SHIFT) & ESC_PHY_CFG1_RMII_P1_TXCK_REFCLK_OE_MASK)
+#define ESC_PHY_CFG1_RMII_P1_TXCK_REFCLK_OE_GET(x) (((uint32_t)(x) & ESC_PHY_CFG1_RMII_P1_TXCK_REFCLK_OE_MASK) >> ESC_PHY_CFG1_RMII_P1_TXCK_REFCLK_OE_SHIFT)
+
+/*
+ * RMII_P0_TXCK_REFCLK_OE (RW)
+ *
+ */
+#define ESC_PHY_CFG1_RMII_P0_TXCK_REFCLK_OE_MASK (0x1U)
+#define ESC_PHY_CFG1_RMII_P0_TXCK_REFCLK_OE_SHIFT (0U)
+#define ESC_PHY_CFG1_RMII_P0_TXCK_REFCLK_OE_SET(x) (((uint32_t)(x) << ESC_PHY_CFG1_RMII_P0_TXCK_REFCLK_OE_SHIFT) & ESC_PHY_CFG1_RMII_P0_TXCK_REFCLK_OE_MASK)
+#define ESC_PHY_CFG1_RMII_P0_TXCK_REFCLK_OE_GET(x) (((uint32_t)(x) & ESC_PHY_CFG1_RMII_P0_TXCK_REFCLK_OE_MASK) >> ESC_PHY_CFG1_RMII_P0_TXCK_REFCLK_OE_SHIFT)
+
+/* Bitfield definition for register: GPIO_CTRL */
+/*
+ * SW_LATCH_GPI (WO)
+ *
+ * if gpi_trig_sel is set to 4'b1001, setting this bit will latch GPI to gpi_reg0/1
+ */
+#define ESC_GPIO_CTRL_SW_LATCH_GPI_MASK (0x80000000UL)
+#define ESC_GPIO_CTRL_SW_LATCH_GPI_SHIFT (31U)
+#define ESC_GPIO_CTRL_SW_LATCH_GPI_SET(x) (((uint32_t)(x) << ESC_GPIO_CTRL_SW_LATCH_GPI_SHIFT) & ESC_GPIO_CTRL_SW_LATCH_GPI_MASK)
+#define ESC_GPIO_CTRL_SW_LATCH_GPI_GET(x) (((uint32_t)(x) & ESC_GPIO_CTRL_SW_LATCH_GPI_MASK) >> ESC_GPIO_CTRL_SW_LATCH_GPI_SHIFT)
+
+/*
+ * SW_LATCH_GPO (WO)
+ *
+ * if gpo_trig_sel is set to 4'b1001, setting this bit will latch GPO to gpo_reg0/1
+ */
+#define ESC_GPIO_CTRL_SW_LATCH_GPO_MASK (0x40000000UL)
+#define ESC_GPIO_CTRL_SW_LATCH_GPO_SHIFT (30U)
+#define ESC_GPIO_CTRL_SW_LATCH_GPO_SET(x) (((uint32_t)(x) << ESC_GPIO_CTRL_SW_LATCH_GPO_SHIFT) & ESC_GPIO_CTRL_SW_LATCH_GPO_MASK)
+#define ESC_GPIO_CTRL_SW_LATCH_GPO_GET(x) (((uint32_t)(x) & ESC_GPIO_CTRL_SW_LATCH_GPO_MASK) >> ESC_GPIO_CTRL_SW_LATCH_GPO_SHIFT)
+
+/*
+ * GPI_OVERRIDE_EN (RW)
+ *
+ * set this bit will use GPI from the software register gpi_override0/1
+ * clr to use GPI from pad directly
+ */
+#define ESC_GPIO_CTRL_GPI_OVERRIDE_EN_MASK (0x2000U)
+#define ESC_GPIO_CTRL_GPI_OVERRIDE_EN_SHIFT (13U)
+#define ESC_GPIO_CTRL_GPI_OVERRIDE_EN_SET(x) (((uint32_t)(x) << ESC_GPIO_CTRL_GPI_OVERRIDE_EN_SHIFT) & ESC_GPIO_CTRL_GPI_OVERRIDE_EN_MASK)
+#define ESC_GPIO_CTRL_GPI_OVERRIDE_EN_GET(x) (((uint32_t)(x) & ESC_GPIO_CTRL_GPI_OVERRIDE_EN_MASK) >> ESC_GPIO_CTRL_GPI_OVERRIDE_EN_SHIFT)
+
+/*
+ * GPI_TRIG_EN (RW)
+ *
+ * use gpi_trig_sel can select the trigger event to latch GPI signal(from reg or pad)
+ * set to use triggered signal;
+ * clr to use signals direclty(from reg or pad)
+ * assign pdi_gpi = gpi_trig_en ? gpi_reg :
+ *                (gpi_override_en ? gpi_override :pad_di_ecat_gpi);
+ */
+#define ESC_GPIO_CTRL_GPI_TRIG_EN_MASK (0x1000U)
+#define ESC_GPIO_CTRL_GPI_TRIG_EN_SHIFT (12U)
+#define ESC_GPIO_CTRL_GPI_TRIG_EN_SET(x) (((uint32_t)(x) << ESC_GPIO_CTRL_GPI_TRIG_EN_SHIFT) & ESC_GPIO_CTRL_GPI_TRIG_EN_MASK)
+#define ESC_GPIO_CTRL_GPI_TRIG_EN_GET(x) (((uint32_t)(x) & ESC_GPIO_CTRL_GPI_TRIG_EN_MASK) >> ESC_GPIO_CTRL_GPI_TRIG_EN_SHIFT)
+
+/*
+ * GPI_TRIG_SEL (RW)
+ *
+ * select the trigger signal to latch GPI.
+ * 0000: SOF;  0001: EOF;  0010: pos of  SYNC0;  0011: pos of SYNC1;
+ * 0100: pos of LATCH0;   0101: pos of LATCH1;   0110: neg of LATCH0;   0111: neg of LATCH1
+ * 1000: wdog trigger;   1001: sw set gpio_ctrl[31];
+ * others no trigger
+ */
+#define ESC_GPIO_CTRL_GPI_TRIG_SEL_MASK (0xF00U)
+#define ESC_GPIO_CTRL_GPI_TRIG_SEL_SHIFT (8U)
+#define ESC_GPIO_CTRL_GPI_TRIG_SEL_SET(x) (((uint32_t)(x) << ESC_GPIO_CTRL_GPI_TRIG_SEL_SHIFT) & ESC_GPIO_CTRL_GPI_TRIG_SEL_MASK)
+#define ESC_GPIO_CTRL_GPI_TRIG_SEL_GET(x) (((uint32_t)(x) & ESC_GPIO_CTRL_GPI_TRIG_SEL_MASK) >> ESC_GPIO_CTRL_GPI_TRIG_SEL_SHIFT)
+
+/*
+ * GPO_TRIG_EN (RW)
+ *
+ * use gpo_trig_sel can select the trigger event to latch GPO signal(from core)
+ * set to use triggered signal;
+ * clr to use GPO signals direclty(from reg or pad)
+ */
+#define ESC_GPIO_CTRL_GPO_TRIG_EN_MASK (0x10U)
+#define ESC_GPIO_CTRL_GPO_TRIG_EN_SHIFT (4U)
+#define ESC_GPIO_CTRL_GPO_TRIG_EN_SET(x) (((uint32_t)(x) << ESC_GPIO_CTRL_GPO_TRIG_EN_SHIFT) & ESC_GPIO_CTRL_GPO_TRIG_EN_MASK)
+#define ESC_GPIO_CTRL_GPO_TRIG_EN_GET(x) (((uint32_t)(x) & ESC_GPIO_CTRL_GPO_TRIG_EN_MASK) >> ESC_GPIO_CTRL_GPO_TRIG_EN_SHIFT)
+
+/*
+ * GPO_TRIG_SEL (RW)
+ *
+ * select the trigger signal to latch GPO.
+ * 0000: SOF;  0001: EOF;  0010: pos of  SYNC0;  0011: pos of SYNC1;
+ * 0100: pos of LATCH0;   0101: pos of LATCH1;   0110: neg of LATCH0;   0111: neg of LATCH1
+ * 1000: wdog trigger;   1001: sw set gpio_ctrl[30];
+ * others no trigger
+ */
+#define ESC_GPIO_CTRL_GPO_TRIG_SEL_MASK (0xFU)
+#define ESC_GPIO_CTRL_GPO_TRIG_SEL_SHIFT (0U)
+#define ESC_GPIO_CTRL_GPO_TRIG_SEL_SET(x) (((uint32_t)(x) << ESC_GPIO_CTRL_GPO_TRIG_SEL_SHIFT) & ESC_GPIO_CTRL_GPO_TRIG_SEL_MASK)
+#define ESC_GPIO_CTRL_GPO_TRIG_SEL_GET(x) (((uint32_t)(x) & ESC_GPIO_CTRL_GPO_TRIG_SEL_MASK) >> ESC_GPIO_CTRL_GPO_TRIG_SEL_SHIFT)
+
+/* Bitfield definition for register: GPI_OVERRIDE0 */
+/*
+ * GPR_OVERRIDE_LOW (RW)
+ *
+ */
+#define ESC_GPI_OVERRIDE0_GPR_OVERRIDE_LOW_MASK (0xFFFFFFFFUL)
+#define ESC_GPI_OVERRIDE0_GPR_OVERRIDE_LOW_SHIFT (0U)
+#define ESC_GPI_OVERRIDE0_GPR_OVERRIDE_LOW_SET(x) (((uint32_t)(x) << ESC_GPI_OVERRIDE0_GPR_OVERRIDE_LOW_SHIFT) & ESC_GPI_OVERRIDE0_GPR_OVERRIDE_LOW_MASK)
+#define ESC_GPI_OVERRIDE0_GPR_OVERRIDE_LOW_GET(x) (((uint32_t)(x) & ESC_GPI_OVERRIDE0_GPR_OVERRIDE_LOW_MASK) >> ESC_GPI_OVERRIDE0_GPR_OVERRIDE_LOW_SHIFT)
+
+/* Bitfield definition for register: GPI_OVERRIDE1 */
+/*
+ * GPR_OVERRIDE_HIGH (RW)
+ *
+ */
+#define ESC_GPI_OVERRIDE1_GPR_OVERRIDE_HIGH_MASK (0xFFFFFFFFUL)
+#define ESC_GPI_OVERRIDE1_GPR_OVERRIDE_HIGH_SHIFT (0U)
+#define ESC_GPI_OVERRIDE1_GPR_OVERRIDE_HIGH_SET(x) (((uint32_t)(x) << ESC_GPI_OVERRIDE1_GPR_OVERRIDE_HIGH_SHIFT) & ESC_GPI_OVERRIDE1_GPR_OVERRIDE_HIGH_MASK)
+#define ESC_GPI_OVERRIDE1_GPR_OVERRIDE_HIGH_GET(x) (((uint32_t)(x) & ESC_GPI_OVERRIDE1_GPR_OVERRIDE_HIGH_MASK) >> ESC_GPI_OVERRIDE1_GPR_OVERRIDE_HIGH_SHIFT)
+
+/* Bitfield definition for register: GPO_REG0 */
+/*
+ * VALUE (RO)
+ *
+ */
+#define ESC_GPO_REG0_VALUE_MASK (0xFFFFFFFFUL)
+#define ESC_GPO_REG0_VALUE_SHIFT (0U)
+#define ESC_GPO_REG0_VALUE_GET(x) (((uint32_t)(x) & ESC_GPO_REG0_VALUE_MASK) >> ESC_GPO_REG0_VALUE_SHIFT)
+
+/* Bitfield definition for register: GPO_REG1 */
+/*
+ * VALUE (RO)
+ *
+ */
+#define ESC_GPO_REG1_VALUE_MASK (0xFFFFFFFFUL)
+#define ESC_GPO_REG1_VALUE_SHIFT (0U)
+#define ESC_GPO_REG1_VALUE_GET(x) (((uint32_t)(x) & ESC_GPO_REG1_VALUE_MASK) >> ESC_GPO_REG1_VALUE_SHIFT)
+
+/* Bitfield definition for register: GPI_REG0 */
+/*
+ * VALUE (RO)
+ *
+ */
+#define ESC_GPI_REG0_VALUE_MASK (0xFFFFFFFFUL)
+#define ESC_GPI_REG0_VALUE_SHIFT (0U)
+#define ESC_GPI_REG0_VALUE_GET(x) (((uint32_t)(x) & ESC_GPI_REG0_VALUE_MASK) >> ESC_GPI_REG0_VALUE_SHIFT)
+
+/* Bitfield definition for register: GPI_REG1 */
+/*
+ * VALUE (RO)
+ *
+ */
+#define ESC_GPI_REG1_VALUE_MASK (0xFFFFFFFFUL)
+#define ESC_GPI_REG1_VALUE_SHIFT (0U)
+#define ESC_GPI_REG1_VALUE_GET(x) (((uint32_t)(x) & ESC_GPI_REG1_VALUE_MASK) >> ESC_GPI_REG1_VALUE_SHIFT)
+
+/* Bitfield definition for register: GPR_STATUS */
+/*
+ * NLINK2_PADSEL (RO)
+ *
+ */
+#define ESC_GPR_STATUS_NLINK2_PADSEL_MASK (0xF0000000UL)
+#define ESC_GPR_STATUS_NLINK2_PADSEL_SHIFT (28U)
+#define ESC_GPR_STATUS_NLINK2_PADSEL_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_NLINK2_PADSEL_MASK) >> ESC_GPR_STATUS_NLINK2_PADSEL_SHIFT)
+
+/*
+ * NLINK1_PADSEL (RO)
+ *
+ */
+#define ESC_GPR_STATUS_NLINK1_PADSEL_MASK (0xF000000UL)
+#define ESC_GPR_STATUS_NLINK1_PADSEL_SHIFT (24U)
+#define ESC_GPR_STATUS_NLINK1_PADSEL_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_NLINK1_PADSEL_MASK) >> ESC_GPR_STATUS_NLINK1_PADSEL_SHIFT)
+
+/*
+ * NLINK0_PADSEL (RO)
+ *
+ */
+#define ESC_GPR_STATUS_NLINK0_PADSEL_MASK (0xF00000UL)
+#define ESC_GPR_STATUS_NLINK0_PADSEL_SHIFT (20U)
+#define ESC_GPR_STATUS_NLINK0_PADSEL_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_NLINK0_PADSEL_MASK) >> ESC_GPR_STATUS_NLINK0_PADSEL_SHIFT)
+
+/*
+ * PDI_SOF (RO)
+ *
+ */
+#define ESC_GPR_STATUS_PDI_SOF_MASK (0x80000UL)
+#define ESC_GPR_STATUS_PDI_SOF_SHIFT (19U)
+#define ESC_GPR_STATUS_PDI_SOF_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_PDI_SOF_MASK) >> ESC_GPR_STATUS_PDI_SOF_SHIFT)
+
+/*
+ * PDI_EOF (RO)
+ *
+ */
+#define ESC_GPR_STATUS_PDI_EOF_MASK (0x40000UL)
+#define ESC_GPR_STATUS_PDI_EOF_SHIFT (18U)
+#define ESC_GPR_STATUS_PDI_EOF_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_PDI_EOF_MASK) >> ESC_GPR_STATUS_PDI_EOF_SHIFT)
+
+/*
+ * PDI_WD_TRIGGER (RO)
+ *
+ */
+#define ESC_GPR_STATUS_PDI_WD_TRIGGER_MASK (0x20000UL)
+#define ESC_GPR_STATUS_PDI_WD_TRIGGER_SHIFT (17U)
+#define ESC_GPR_STATUS_PDI_WD_TRIGGER_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_PDI_WD_TRIGGER_MASK) >> ESC_GPR_STATUS_PDI_WD_TRIGGER_SHIFT)
+
+/*
+ * PDI_WD_STATE (RO)
+ *
+ */
+#define ESC_GPR_STATUS_PDI_WD_STATE_MASK (0x10000UL)
+#define ESC_GPR_STATUS_PDI_WD_STATE_SHIFT (16U)
+#define ESC_GPR_STATUS_PDI_WD_STATE_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_PDI_WD_STATE_MASK) >> ESC_GPR_STATUS_PDI_WD_STATE_SHIFT)
+
+/*
+ * SYNC_OUT1 (RO)
+ *
+ */
+#define ESC_GPR_STATUS_SYNC_OUT1_MASK (0x200U)
+#define ESC_GPR_STATUS_SYNC_OUT1_SHIFT (9U)
+#define ESC_GPR_STATUS_SYNC_OUT1_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_SYNC_OUT1_MASK) >> ESC_GPR_STATUS_SYNC_OUT1_SHIFT)
+
+/*
+ * SYNC_OUT0 (RO)
+ *
+ */
+#define ESC_GPR_STATUS_SYNC_OUT0_MASK (0x100U)
+#define ESC_GPR_STATUS_SYNC_OUT0_SHIFT (8U)
+#define ESC_GPR_STATUS_SYNC_OUT0_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_SYNC_OUT0_MASK) >> ESC_GPR_STATUS_SYNC_OUT0_SHIFT)
+
+/*
+ * LED_STATE_RUN (RO)
+ *
+ */
+#define ESC_GPR_STATUS_LED_STATE_RUN_MASK (0x40U)
+#define ESC_GPR_STATUS_LED_STATE_RUN_SHIFT (6U)
+#define ESC_GPR_STATUS_LED_STATE_RUN_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_LED_STATE_RUN_MASK) >> ESC_GPR_STATUS_LED_STATE_RUN_SHIFT)
+
+/*
+ * LED_ERR (RO)
+ *
+ */
+#define ESC_GPR_STATUS_LED_ERR_MASK (0x20U)
+#define ESC_GPR_STATUS_LED_ERR_SHIFT (5U)
+#define ESC_GPR_STATUS_LED_ERR_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_LED_ERR_MASK) >> ESC_GPR_STATUS_LED_ERR_SHIFT)
+
+/*
+ * LED_RUN (RO)
+ *
+ */
+#define ESC_GPR_STATUS_LED_RUN_MASK (0x10U)
+#define ESC_GPR_STATUS_LED_RUN_SHIFT (4U)
+#define ESC_GPR_STATUS_LED_RUN_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_LED_RUN_MASK) >> ESC_GPR_STATUS_LED_RUN_SHIFT)
+
+/*
+ * DEV_STATE (RO)
+ *
+ */
+#define ESC_GPR_STATUS_DEV_STATE_MASK (0x8U)
+#define ESC_GPR_STATUS_DEV_STATE_SHIFT (3U)
+#define ESC_GPR_STATUS_DEV_STATE_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_DEV_STATE_MASK) >> ESC_GPR_STATUS_DEV_STATE_SHIFT)
+
+/*
+ * LINK_ACT (RO)
+ *
+ */
+#define ESC_GPR_STATUS_LINK_ACT_MASK (0x7U)
+#define ESC_GPR_STATUS_LINK_ACT_SHIFT (0U)
+#define ESC_GPR_STATUS_LINK_ACT_GET(x) (((uint32_t)(x) & ESC_GPR_STATUS_LINK_ACT_MASK) >> ESC_GPR_STATUS_LINK_ACT_SHIFT)
+
+/* Bitfield definition for register array: IO_CFG */
+/*
+ * INVERT (RW)
+ *
+ * 1:invert the IO
+ */
+#define ESC_IO_CFG_INVERT_MASK (0x10U)
+#define ESC_IO_CFG_INVERT_SHIFT (4U)
+#define ESC_IO_CFG_INVERT_SET(x) (((uint32_t)(x) << ESC_IO_CFG_INVERT_SHIFT) & ESC_IO_CFG_INVERT_MASK)
+#define ESC_IO_CFG_INVERT_GET(x) (((uint32_t)(x) & ESC_IO_CFG_INVERT_MASK) >> ESC_IO_CFG_INVERT_SHIFT)
+
+/*
+ * FUNC_ALT (RW)
+ *
+ * IO usage:
+ * 0:NMII_LINK0
+ * 1:NMII_LINK1
+ * 2:NMII_LINK2
+ * 3:LINK_ACT0
+ * 4:LINK_ACT1
+ * 5:LINK_ACT2
+ * 6:LED_RUN
+ * 7:LED_ERR
+ * 8:RESET_OUT
+ */
+#define ESC_IO_CFG_FUNC_ALT_MASK (0xFU)
+#define ESC_IO_CFG_FUNC_ALT_SHIFT (0U)
+#define ESC_IO_CFG_FUNC_ALT_SET(x) (((uint32_t)(x) << ESC_IO_CFG_FUNC_ALT_SHIFT) & ESC_IO_CFG_FUNC_ALT_MASK)
+#define ESC_IO_CFG_FUNC_ALT_GET(x) (((uint32_t)(x) & ESC_IO_CFG_FUNC_ALT_MASK) >> ESC_IO_CFG_FUNC_ALT_SHIFT)
+
+
+
+/* RX_ERR_CNT register group index macro definition */
+#define ESC_RX_ERR_CNT_PORT0 (0UL)
+#define ESC_RX_ERR_CNT_PORT1 (1UL)
+#define ESC_RX_ERR_CNT_PORT2 (2UL)
+#define ESC_RX_ERR_CNT_PORT3 (3UL)
+
+/* FWD_RX_ERR_CNT register group index macro definition */
+#define ESC_FWD_RX_ERR_CNT_PORT0 (0UL)
+#define ESC_FWD_RX_ERR_CNT_PORT1 (1UL)
+#define ESC_FWD_RX_ERR_CNT_PORT2 (2UL)
+#define ESC_FWD_RX_ERR_CNT_PORT3 (3UL)
+
+/* LOST_LINK_CNT register group index macro definition */
+#define ESC_LOST_LINK_CNT_PORT0 (0UL)
+#define ESC_LOST_LINK_CNT_PORT1 (1UL)
+#define ESC_LOST_LINK_CNT_PORT2 (2UL)
+#define ESC_LOST_LINK_CNT_PORT3 (3UL)
+
+/* PHY_STAT register group index macro definition */
+#define ESC_PHY_STAT_PORT0 (0UL)
+#define ESC_PHY_STAT_PORT1 (1UL)
+#define ESC_PHY_STAT_PORT2 (2UL)
+#define ESC_PHY_STAT_PORT3 (3UL)
+
+/* FMMU register group index macro definition */
+#define ESC_FMMU_0 (0UL)
+#define ESC_FMMU_1 (1UL)
+#define ESC_FMMU_2 (2UL)
+#define ESC_FMMU_3 (3UL)
+#define ESC_FMMU_4 (4UL)
+#define ESC_FMMU_5 (5UL)
+#define ESC_FMMU_6 (6UL)
+#define ESC_FMMU_7 (7UL)
+
+/* SYNCM register group index macro definition */
+#define ESC_SYNCM_0 (0UL)
+#define ESC_SYNCM_1 (1UL)
+#define ESC_SYNCM_2 (2UL)
+#define ESC_SYNCM_3 (3UL)
+#define ESC_SYNCM_4 (4UL)
+#define ESC_SYNCM_5 (5UL)
+#define ESC_SYNCM_6 (6UL)
+#define ESC_SYNCM_7 (7UL)
+
+/* RCV_TIME register group index macro definition */
+#define ESC_RCV_TIME_PORT0 (0UL)
+#define ESC_RCV_TIME_PORT1 (1UL)
+#define ESC_RCV_TIME_PORT2 (2UL)
+#define ESC_RCV_TIME_PORT3 (3UL)
+
+/* IO_CFG register group index macro definition */
+#define ESC_IO_CFG_CTR0 (0UL)
+#define ESC_IO_CFG_CTR1 (1UL)
+#define ESC_IO_CFG_CTR2 (2UL)
+#define ESC_IO_CFG_CTR3 (3UL)
+#define ESC_IO_CFG_CTR4 (4UL)
+#define ESC_IO_CFG_CTR5 (5UL)
+#define ESC_IO_CFG_CTR6 (6UL)
+#define ESC_IO_CFG_CTR7 (7UL)
+#define ESC_IO_CFG_CTR8 (8UL)
+
+
+#endif /* HPM_ESC_H */

+ 208 - 0
osal/ec_osal_freertos.c

@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+#include <FreeRTOS.h>
+#include "semphr.h"
+#include "event_groups.h"
+
+ec_osal_thread_t ec_osal_thread_create(const char *name, uint32_t stack_size, uint32_t prio, ec_thread_entry_t entry, void *args)
+{
+    TaskHandle_t htask = NULL;
+    stack_size /= sizeof(StackType_t);
+    xTaskCreate(entry, name, stack_size, args, configMAX_PRIORITIES - 1 - prio, &htask);
+    if (htask == NULL) {
+        EC_LOG_ERR("Create thread %s failed\r\n", name);
+        while (1) {
+        }
+    }
+    return (ec_osal_thread_t)htask;
+}
+
+void ec_osal_thread_delete(ec_osal_thread_t thread)
+{
+    vTaskDelete(thread);
+}
+
+void ec_osal_thread_suspend(ec_osal_thread_t thread)
+{
+    vTaskSuspend((TaskHandle_t)thread);
+}
+
+void ec_osal_thread_resume(ec_osal_thread_t thread)
+{
+    vTaskResume((TaskHandle_t)thread);
+}
+
+ec_osal_sem_t ec_osal_sem_create(uint32_t max_count, uint32_t initial_count)
+{
+    ec_osal_sem_t sem = (ec_osal_sem_t)xSemaphoreCreateCounting(max_count, initial_count);
+    if (sem == NULL) {
+        EC_LOG_ERR("Create semaphore failed\r\n");
+        while (1) {
+        }
+    }
+    return sem;
+}
+
+void ec_osal_sem_delete(ec_osal_sem_t sem)
+{
+    vSemaphoreDelete((SemaphoreHandle_t)sem);
+}
+
+int ec_osal_sem_take(ec_osal_sem_t sem, uint32_t timeout)
+{
+    if (timeout == EC_OSAL_WAITING_FOREVER) {
+        return (xSemaphoreTake((SemaphoreHandle_t)sem, portMAX_DELAY) == pdPASS) ? 0 : -EC_ERR_TIMEOUT;
+    } else {
+        return (xSemaphoreTake((SemaphoreHandle_t)sem, pdMS_TO_TICKS(timeout)) == pdPASS) ? 0 : -EC_ERR_TIMEOUT;
+    }
+}
+
+int ec_osal_sem_give(ec_osal_sem_t sem)
+{
+    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+    int ret;
+
+    if (xPortIsInsideInterrupt()) {
+        ret = xSemaphoreGiveFromISR((SemaphoreHandle_t)sem, &xHigherPriorityTaskWoken);
+        if (ret == pdPASS) {
+            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+        }
+    } else {
+        ret = xSemaphoreGive((SemaphoreHandle_t)sem);
+    }
+
+    return (ret == pdPASS) ? 0 : -EC_ERR_TIMEOUT;
+}
+
+void ec_osal_sem_reset(ec_osal_sem_t sem)
+{
+    xQueueReset((QueueHandle_t)sem);
+}
+
+ec_osal_mutex_t ec_osal_mutex_create(void)
+{
+    ec_osal_mutex_t mutex = (ec_osal_mutex_t)xSemaphoreCreateMutex();
+    if (mutex == NULL) {
+        EC_LOG_ERR("Create mutex failed\r\n");
+        while (1) {
+        }
+    }
+    return mutex;
+}
+
+void ec_osal_mutex_delete(ec_osal_mutex_t mutex)
+{
+    vSemaphoreDelete((SemaphoreHandle_t)mutex);
+}
+
+int ec_osal_mutex_take(ec_osal_mutex_t mutex)
+{
+    return (xSemaphoreTake((SemaphoreHandle_t)mutex, portMAX_DELAY) == pdPASS) ? 0 : -EC_ERR_TIMEOUT;
+}
+
+int ec_osal_mutex_give(ec_osal_mutex_t mutex)
+{
+    return (xSemaphoreGive((SemaphoreHandle_t)mutex) == pdPASS) ? 0 : -EC_ERR_TIMEOUT;
+}
+
+static void __ec_timeout(TimerHandle_t *handle)
+{
+    struct ec_osal_timer *timer = (struct ec_osal_timer *)pvTimerGetTimerID((TimerHandle_t)handle);
+
+    timer->handler(timer->argument);
+}
+
+struct ec_osal_timer *ec_osal_timer_create(const char *name, uint32_t timeout_ms, ec_timer_handler_t handler, void *argument, bool is_period)
+{
+    struct ec_osal_timer *timer;
+    (void)name;
+
+    timer = pvPortMalloc(sizeof(struct ec_osal_timer));
+
+    if (timer == NULL) {
+        EC_LOG_ERR("Create ec_osal_timer failed\r\n");
+        while (1) {
+        }
+    }
+    memset(timer, 0, sizeof(struct ec_osal_timer));
+
+    timer->handler = handler;
+    timer->argument = argument;
+
+    timer->timer = (void *)xTimerCreate("ec_tim", pdMS_TO_TICKS(timeout_ms), is_period, timer, (TimerCallbackFunction_t)__ec_timeout);
+    if (timer->timer == NULL) {
+        EC_LOG_ERR("Create timer failed\r\n");
+        while (1) {
+        }
+    }
+    return timer;
+}
+
+void ec_osal_timer_delete(struct ec_osal_timer *timer)
+{
+    xTimerStop(timer->timer, 0);
+    xTimerDelete(timer->timer, 0);
+    vPortFree(timer);
+}
+
+void ec_osal_timer_start(struct ec_osal_timer *timer)
+{
+    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+    int ret;
+
+    if (xPortIsInsideInterrupt()) {
+        ret = xTimerStartFromISR(timer->timer, &xHigherPriorityTaskWoken);
+        if (ret == pdPASS) {
+            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+        }
+    } else {
+        xTimerStart(timer->timer, 0);
+    }
+}
+
+void ec_osal_timer_stop(struct ec_osal_timer *timer)
+{
+    xTimerStop(timer->timer, 0);
+}
+
+size_t ec_osal_enter_critical_section(void)
+{
+    size_t ret;
+
+    if (xPortIsInsideInterrupt()) {
+        ret = taskENTER_CRITICAL_FROM_ISR();
+    } else {
+        taskENTER_CRITICAL();
+        ret = 1;
+    }
+
+    return ret;
+}
+
+void ec_osal_leave_critical_section(size_t flag)
+{
+    if (xPortIsInsideInterrupt()) {
+        taskEXIT_CRITICAL_FROM_ISR(flag);
+    } else {
+        taskEXIT_CRITICAL();
+    }
+}
+
+void ec_osal_msleep(uint32_t delay)
+{
+    vTaskDelay(pdMS_TO_TICKS(delay));
+}
+
+void *ec_osal_malloc(size_t size)
+{
+    return pvPortMalloc(size);
+}
+
+void ec_osal_free(void *ptr)
+{
+    vPortFree(ptr);
+}

+ 178 - 0
osal/ec_osal_rtthread.c

@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+#include <rtthread.h>
+#include <rthw.h>
+
+ec_osal_thread_t ec_osal_thread_create(const char *name, uint32_t stack_size, uint32_t prio, ec_thread_entry_t entry, void *args)
+{
+    rt_thread_t htask;
+    htask = rt_thread_create(name, entry, args, stack_size, prio, 10);
+    if (htask == NULL) {
+        EC_LOG_ERR("Create thread %s failed\r\n", name);
+        while (1) {
+        }
+    }
+    rt_thread_startup(htask);
+    return (ec_osal_thread_t)htask;
+}
+
+void ec_osal_thread_delete(ec_osal_thread_t thread)
+{
+    if (thread == NULL) {
+        rt_thread_t self = rt_thread_self();
+        rt_thread_control(self, RT_THREAD_CTRL_CLOSE, RT_NULL);
+        return;
+    }
+
+    rt_thread_delete(thread);
+}
+
+void ec_osal_thread_suspend(ec_osal_thread_t thread)
+{
+    rt_thread_suspend((rt_thread_t)thread);
+}
+
+void ec_osal_thread_resume(ec_osal_thread_t thread)
+{
+    rt_thread_resume((rt_thread_t)thread);
+}
+
+ec_osal_sem_t ec_osal_sem_create(uint32_t initial_count)
+{
+    ec_osal_sem_t sem = (ec_osal_sem_t)rt_sem_create("ec_sem", initial_count, RT_IPC_FLAG_FIFO);
+    if (sem == NULL) {
+        EC_LOG_ERR("Create semaphore failed\r\n");
+        while (1) {
+        }
+    }
+    return sem;
+}
+
+void ec_osal_sem_delete(ec_osal_sem_t sem)
+{
+    rt_sem_delete((rt_sem_t)sem);
+}
+
+int ec_osal_sem_take(ec_osal_sem_t sem, uint32_t timeout)
+{
+    int ret = 0;
+    rt_err_t result = RT_EOK;
+
+    if (timeout == EC_OSAL_WAITING_FOREVER) {
+        result = rt_sem_take((rt_sem_t)sem, RT_WAITING_FOREVER);
+    } else {
+        result = rt_sem_take((rt_sem_t)sem, rt_tick_from_millisecond(timeout));
+    }
+    if (result == -RT_ETIMEOUT) {
+        ret = -EC_ERR_TIMEOUT;
+    } else if (result == -RT_ERROR) {
+        ret = -EC_ERR_INVAL;
+    } else {
+        ret = 0;
+    }
+
+    return (int)ret;
+}
+
+int ec_osal_sem_give(ec_osal_sem_t sem)
+{
+    return (int)rt_sem_release((rt_sem_t)sem);
+}
+
+void ec_osal_sem_reset(ec_osal_sem_t sem)
+{
+    rt_sem_control((rt_sem_t)sem, RT_IPC_CMD_RESET, (void *)0);
+}
+
+ec_osal_mutex_t ec_osal_mutex_create(void)
+{
+    ec_osal_mutex_t mutex = (ec_osal_mutex_t)rt_mutex_create("ec_mutex", RT_IPC_FLAG_FIFO);
+    if (mutex == NULL) {
+        EC_LOG_ERR("Create mutex failed\r\n");
+        while (1) {
+        }
+    }
+    return mutex;
+}
+
+void ec_osal_mutex_delete(ec_osal_mutex_t mutex)
+{
+    rt_mutex_delete((rt_mutex_t)mutex);
+}
+
+int ec_osal_mutex_take(ec_osal_mutex_t mutex)
+{
+    return (int)rt_mutex_take((rt_mutex_t)mutex, RT_WAITING_FOREVER);
+}
+
+int ec_osal_mutex_give(ec_osal_mutex_t mutex)
+{
+    return (int)rt_mutex_release((rt_mutex_t)mutex);
+}
+
+struct ec_osal_timer *ec_osal_timer_create(const char *name, uint32_t timeout_ms, ec_timer_handler_t handler, void *argument, bool is_period)
+{
+    struct ec_osal_timer *timer;
+
+    timer = rt_malloc(sizeof(struct ec_osal_timer));
+    if (timer == NULL) {
+        EC_LOG_ERR("Create ec_osal_timer failed\r\n");
+        while (1) {
+        }
+    }
+    memset(timer, 0, sizeof(struct ec_osal_timer));
+
+    timer->timer = (void *)rt_timer_create(name, handler, argument, timeout_ms, is_period ? (RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER) : (RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER));
+    if (timer->timer == NULL) {
+        EC_LOG_ERR("Create timer failed\r\n");
+        while (1) {
+        }
+    }
+    return timer;
+}
+
+void ec_osal_timer_delete(struct ec_osal_timer *timer)
+{
+    rt_timer_stop(timer->timer);
+    rt_timer_delete(timer->timer);
+    rt_free(timer);
+}
+
+void ec_osal_timer_start(struct ec_osal_timer *timer)
+{
+    rt_timer_start(timer->timer);
+}
+
+void ec_osal_timer_stop(struct ec_osal_timer *timer)
+{
+    rt_timer_stop(timer->timer);
+}
+
+size_t ec_osal_enter_critical_section(void)
+{
+    return rt_hw_interrupt_disable();
+}
+
+void ec_osal_leave_critical_section(size_t flag)
+{
+    rt_hw_interrupt_enable(flag);
+}
+
+void ec_osal_msleep(uint32_t delay)
+{
+    rt_thread_mdelay(delay);
+}
+
+void *ec_osal_malloc(size_t size)
+{
+    return rt_malloc(size);
+}
+
+void ec_osal_free(void *ptr)
+{
+    rt_free(ptr);
+}

+ 267 - 0
osal/ec_osal_threadx.c

@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+#include "tx_api.h"
+
+/* create bytepool in tx_application_define
+ *
+ * tx_byte_pool_create(&ec_byte_pool, "ec byte pool", memory_area, 65536);
+ */
+
+extern TX_BYTE_POOL ec_byte_pool;
+
+ec_osal_thread_t ec_osal_thread_create(const char *name, uint32_t stack_size, uint32_t prio, ec_thread_entry_t entry, void *args)
+{
+    CHAR *pointer = TX_NULL;
+    TX_THREAD *thread_ptr = TX_NULL;
+
+    tx_byte_allocate(&ec_byte_pool, (VOID **)&thread_ptr, sizeof(TX_THREAD), TX_NO_WAIT);
+
+    if (thread_ptr == TX_NULL) {
+        EC_LOG_ERR("Create thread %s failed\r\n", name);
+        while (1) {
+        }
+    }
+
+    tx_byte_allocate(&ec_byte_pool, (VOID **)&pointer, stack_size, TX_NO_WAIT);
+    if (pointer == TX_NULL) {
+        EC_LOG_ERR("Create thread %s failed\r\n", name);
+        while (1) {
+        }
+    }
+
+    tx_thread_create(thread_ptr, (CHAR *)name, (VOID(*)(ULONG))entry, (uintptr_t)args,
+                     pointer, stack_size,
+                     prio, prio, TX_NO_TIME_SLICE, TX_AUTO_START);
+
+    return (ec_osal_thread_t)thread_ptr;
+}
+
+void ec_osal_thread_delete(ec_osal_thread_t thread)
+{
+    TX_THREAD *thread_ptr = NULL;
+
+    if (thread == NULL) {
+        /* Call the tx_thread_identify to get the control block pointer of the
+        currently executing thread. */
+        thread_ptr = tx_thread_identify();
+
+        /* Check if the current running thread pointer is not NULL */
+        if (thread_ptr != NULL) {
+            /* Call the tx_thread_terminate to terminates the specified application
+            thread regardless of whether the thread is suspended or not. A thread
+            may call this service to terminate itself. */
+            tx_thread_terminate(thread_ptr);
+            tx_byte_release(thread_ptr->tx_thread_stack_start);
+            tx_byte_release(thread_ptr);
+        }
+        return;
+    }
+
+    tx_thread_terminate(thread);
+    tx_byte_release(thread_ptr->tx_thread_stack_start);
+    tx_byte_release(thread);
+}
+
+void ec_osal_thread_suspend(ec_osal_thread_t thread)
+{
+    tx_thread_suspend((TX_THREAD *)thread);
+}
+
+void ec_osal_thread_resume(ec_osal_thread_t thread)
+{
+    tx_thread_resume((TX_THREAD *)thread);
+}
+
+ec_osal_sem_t ec_osal_sem_create(uint32_t initial_count)
+{
+    TX_SEMAPHORE *sem_ptr = TX_NULL;
+
+    tx_byte_allocate(&ec_byte_pool, (VOID **)&sem_ptr, sizeof(TX_SEMAPHORE), TX_NO_WAIT);
+
+    if (sem_ptr == TX_NULL) {
+        EC_LOG_ERR("Create semaphore failed\r\n");
+        while (1) {
+        }
+    }
+
+    tx_semaphore_create(sem_ptr, "ec_sem", initial_count);
+    return (ec_osal_sem_t)sem_ptr;
+}
+
+void ec_osal_sem_delete(ec_osal_sem_t sem)
+{
+    tx_semaphore_delete((TX_SEMAPHORE *)sem);
+    tx_byte_release(sem);
+}
+
+int ec_osal_sem_take(ec_osal_sem_t sem, uint32_t timeout)
+{
+    int ret = 0;
+
+    ret = tx_semaphore_get((TX_SEMAPHORE *)sem, timeout);
+    if (ret == TX_SUCCESS) {
+        ret = 0;
+    } else if ((ret == TX_WAIT_ABORTED) || (ret == TX_NO_INSTANCE)) {
+        ret = -EC_ERR_TIMEOUT;
+    } else {
+        ret = -EC_ERR_INVAL;
+    }
+
+    return (int)ret;
+}
+
+int ec_osal_sem_give(ec_osal_sem_t sem)
+{
+    return (int)tx_semaphore_put((TX_SEMAPHORE *)sem);
+}
+
+void ec_osal_sem_reset(ec_osal_sem_t sem)
+{
+    tx_semaphore_get((TX_SEMAPHORE *)sem, 0);
+}
+
+ec_osal_mutex_t ec_osal_mutex_create(void)
+{
+    TX_MUTEX *mutex_ptr = TX_NULL;
+
+    tx_byte_allocate(&ec_byte_pool, (VOID **)&mutex_ptr, sizeof(TX_MUTEX), TX_NO_WAIT);
+
+    if (mutex_ptr == TX_NULL) {
+        EC_LOG_ERR("Create mutex failed\r\n");
+        while (1) {
+        }
+    }
+
+    tx_mutex_create(mutex_ptr, "ec_mutx", TX_INHERIT);
+    return (ec_osal_mutex_t)mutex_ptr;
+}
+
+void ec_osal_mutex_delete(ec_osal_mutex_t mutex)
+{
+    tx_mutex_delete((TX_MUTEX *)mutex);
+    tx_byte_release(mutex);
+}
+
+int ec_osal_mutex_take(ec_osal_mutex_t mutex)
+{
+    int ret = 0;
+
+    ret = tx_mutex_get((TX_MUTEX *)mutex, TX_WAIT_FOREVER);
+    if (ret == TX_SUCCESS) {
+        ret = 0;
+    } else if ((ret == TX_WAIT_ABORTED) || (ret == TX_NO_INSTANCE)) {
+        ret = -EC_ERR_TIMEOUT;
+    } else {
+        ret = -EC_ERR_INVAL;
+    }
+
+    return (int)ret;
+}
+
+int ec_osal_mutex_give(ec_osal_mutex_t mutex)
+{
+    return (int)(tx_mutex_put((TX_MUTEX *)mutex) == TX_SUCCESS) ? 0 : -EC_ERR_INVAL;
+}
+
+struct ec_osal_timer *ec_osal_timer_create(const char *name, uint32_t timeout_ms, ec_timer_handler_t handler, void *argument, bool is_period)
+{
+    TX_TIMER *timer_ptr = TX_NULL;
+    struct ec_osal_timer *timer;
+
+    tx_byte_allocate(&ec_byte_pool, (VOID **)&timer, sizeof(struct ec_osal_timer), TX_NO_WAIT);
+
+    if (timer == TX_NULL) {
+        EC_LOG_ERR("Create ec_osal_timer failed\r\n");
+        while (1) {
+        }
+    }
+    memset(timer, 0, sizeof(struct ec_osal_timer));
+
+    tx_byte_allocate(&ec_byte_pool, (VOID **)&timer_ptr, sizeof(TX_TIMER), TX_NO_WAIT);
+
+    if (timer_ptr == TX_NULL) {
+        EC_LOG_ERR("Create TX_TIMER failed\r\n");
+        while (1) {
+        }
+    }
+
+    timer->timer = timer_ptr;
+    timer->timeout_ms = timeout_ms;
+    timer->is_period = is_period;
+    if (tx_timer_create(timer_ptr, (CHAR *)name, (void (*)(ULONG))handler, (uintptr_t)argument, 1, is_period ? 1 : 0,
+                        TX_NO_ACTIVATE) != TX_SUCCESS) {
+        return NULL;
+    }
+    return timer;
+}
+
+void ec_osal_timer_delete(struct ec_osal_timer *timer)
+{
+    tx_timer_deactivate((TX_TIMER *)timer->timer);
+    tx_timer_delete((TX_TIMER *)timer->timer);
+    tx_byte_release(timer->timer);
+    tx_byte_release(timer);
+}
+
+void ec_osal_timer_start(struct ec_osal_timer *timer)
+{
+    if (tx_timer_change((TX_TIMER *)timer->timer, timer->timeout_ms, timer->is_period ? timer->timeout_ms : 0) == TX_SUCCESS) {
+        /* Call the tx_timer_activate to activates the specified application
+             timer. The expiration routines of timers that expire at the same
+             time are executed in the order they were activated. */
+        if (tx_timer_activate((TX_TIMER *)timer->timer) == TX_SUCCESS) {
+            /* Return osOK for success */
+        } else {
+            /* Return osErrorResource in case of error */
+        }
+    } else {
+    }
+}
+
+void ec_osal_timer_stop(struct ec_osal_timer *timer)
+{
+    tx_timer_deactivate((TX_TIMER *)timer->timer);
+}
+
+size_t ec_osal_enter_critical_section(void)
+{
+    TX_INTERRUPT_SAVE_AREA
+
+    TX_DISABLE
+
+    return interrupt_save;
+}
+
+void ec_osal_leave_critical_section(size_t flag)
+{
+    int interrupt_save;
+
+    interrupt_save = flag;
+    TX_RESTORE
+}
+
+void ec_osal_msleep(uint32_t delay)
+{
+#if TX_TIMER_TICKS_PER_SECOND != 1000
+#error "TX_TIMER_TICKS_PER_SECOND must be 1000"
+#endif
+    tx_thread_sleep(delay);
+}
+
+void *ec_osal_malloc(size_t size)
+{
+    CHAR *pointer = TX_NULL;
+
+    tx_byte_allocate(&ec_byte_pool, (VOID **)&pointer, size, TX_WAIT_FOREVER);
+
+    return pointer;
+}
+
+void ec_osal_free(void *ptr)
+{
+    tx_byte_release(ptr);
+}

+ 437 - 0
port/netdev_hpm.c

@@ -0,0 +1,437 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "hpm_clock_drv.h"
+#include "hpm_enet_drv.h"
+#include "hpm_enet_phy_common.h"
+#include "hpm_otp_drv.h"
+#include "hpm_l1c_drv.h"
+#include "board.h"
+#include "ec_master.h"
+
+#if defined(RGMII) && RGMII
+#define ENET_INF_TYPE enet_inf_rgmii
+#define ENET          BOARD_ENET_RGMII
+#else
+#define ENET_INF_TYPE enet_inf_rmii
+#define ENET          BOARD_ENET_RMII
+#endif
+
+#define __ENABLE_ENET_RECEIVE_INTERRUPT 1
+
+#define MAC_ADDR0          0x00
+#define MAC_ADDR1          0x80
+#define MAC_ADDR2          0xE1
+#define MAC_ADDR3          0x00
+#define MAC_ADDR4          0x00
+#define MAC_ADDR5          0x00
+
+#define ENET_TX_BUFF_COUNT CONFIG_EC_MAX_ENET_TXBUF_COUNT
+#define ENET_RX_BUFF_COUNT CONFIG_EC_MAX_ENET_RXBUF_COUNT
+#define ENET_RX_BUFF_SIZE  ENET_MAX_FRAME_SIZE
+#define ENET_TX_BUFF_SIZE  ENET_MAX_FRAME_SIZE
+
+ATTR_PLACE_AT_NONCACHEABLE_WITH_ALIGNMENT(ENET_SOC_DESC_ADDR_ALIGNMENT)
+__RW enet_rx_desc_t dma_rx_desc_tab[ENET_RX_BUFF_COUNT]; /* Ethernet Rx DMA Descriptor */
+
+ATTR_PLACE_AT_NONCACHEABLE_WITH_ALIGNMENT(ENET_SOC_DESC_ADDR_ALIGNMENT)
+__RW enet_tx_desc_t dma_tx_desc_tab[ENET_TX_BUFF_COUNT]; /* Ethernet Tx DMA Descriptor */
+
+ATTR_PLACE_AT_FAST_RAM_BSS_WITH_ALIGNMENT(ENET_SOC_BUFF_ADDR_ALIGNMENT)
+__RW uint8_t rx_buff[ENET_RX_BUFF_COUNT][ENET_RX_BUFF_SIZE]; /* Ethernet Receive Buffer */
+
+ATTR_PLACE_AT_FAST_RAM_BSS_WITH_ALIGNMENT(ENET_SOC_BUFF_ADDR_ALIGNMENT)
+__RW uint8_t tx_buff[ENET_TX_BUFF_COUNT][ENET_TX_BUFF_SIZE]; /* Ethernet Transmit Buffer */
+
+enet_desc_t desc;
+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;
+
+    uint32_t uuid[(ENET_MAC + (ENET_MAC - 1)) / sizeof(uint32_t)];
+
+    for (int i = 0; i < ARRAY_SIZE(uuid); i++) {
+        uuid[i] = otp_read_from_shadow(OTP_SOC_UUID_IDX + i);
+        if (uuid[i] != 0xFFFFFFFFUL && uuid[i] != 0) {
+            invalid = false;
+        }
+    }
+
+    if (invalid == true) {
+        ec_memcpy(mac, &uuid, ENET_MAC);
+    } else {
+        mac[0] = MAC_ADDR0;
+        mac[1] = MAC_ADDR1;
+        mac[2] = MAC_ADDR2;
+        mac[3] = MAC_ADDR3;
+        mac[4] = MAC_ADDR4;
+        mac[5] = MAC_ADDR5;
+    }
+}
+
+hpm_stat_t enet_init(ENET_Type *ptr)
+{
+    enet_int_config_t int_config = { .int_enable = 0, .int_mask = 0 };
+    enet_mac_config_t enet_config;
+    enet_tx_control_config_t enet_tx_control_config;
+
+#if defined(RGMII) && RGMII
+#if defined(__USE_DP83867) && __USE_DP83867
+    dp83867_config_t phy_config;
+#else
+    rtl8211_config_t phy_config;
+#endif
+#else
+#if defined(__USE_DP83848) && __USE_DP83848
+    dp83848_config_t phy_config;
+#else
+    rtl8201_config_t phy_config;
+#endif
+#endif
+
+    /* Initialize td, rd and the corresponding buffers */
+    memset((uint8_t *)dma_tx_desc_tab, 0x00, sizeof(dma_tx_desc_tab));
+    memset((uint8_t *)dma_rx_desc_tab, 0x00, sizeof(dma_rx_desc_tab));
+    memset((uint8_t *)rx_buff, 0x00, sizeof(rx_buff));
+    memset((uint8_t *)tx_buff, 0x00, sizeof(tx_buff));
+
+    desc.tx_desc_list_head = (enet_tx_desc_t *)core_local_mem_to_sys_address(BOARD_RUNNING_CORE, (uint32_t)dma_tx_desc_tab);
+    desc.rx_desc_list_head = (enet_rx_desc_t *)core_local_mem_to_sys_address(BOARD_RUNNING_CORE, (uint32_t)dma_rx_desc_tab);
+
+    desc.tx_buff_cfg.buffer = core_local_mem_to_sys_address(BOARD_RUNNING_CORE, (uint32_t)tx_buff);
+    desc.tx_buff_cfg.count = ENET_TX_BUFF_COUNT;
+    desc.tx_buff_cfg.size = ENET_TX_BUFF_SIZE;
+
+    desc.rx_buff_cfg.buffer = core_local_mem_to_sys_address(BOARD_RUNNING_CORE, (uint32_t)rx_buff);
+    desc.rx_buff_cfg.count = ENET_RX_BUFF_COUNT;
+    desc.rx_buff_cfg.size = ENET_RX_BUFF_SIZE;
+
+    /*Get a default control config for tx descriptor */
+    enet_get_default_tx_control_config(ENET, &enet_tx_control_config);
+
+    /* Set the control config for tx descriptor */
+    ec_memcpy(&desc.tx_control_config, &enet_tx_control_config, sizeof(enet_tx_control_config_t));
+
+    /* Get MAC address */
+    enet_get_mac_address(mac);
+
+    /* Set MAC0 address */
+    enet_config.mac_addr_high[0] = mac[5] << 8 | mac[4];
+    enet_config.mac_addr_low[0] = mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0];
+    enet_config.valid_max_count = 1;
+
+    /* Set DMA PBL */
+    enet_config.dma_pbl = board_get_enet_dma_pbl(ENET);
+
+    /* Set SARC */
+    enet_config.sarc = enet_sarc_replace_mac0;
+
+#if defined(__ENABLE_ENET_RECEIVE_INTERRUPT) && __ENABLE_ENET_RECEIVE_INTERRUPT
+    /* Get the default interrupt config */
+    enet_get_default_interrupt_config(ENET, &int_config);
+#endif
+
+    /* Initialize enet controller */
+    if (enet_controller_init(ptr, ENET_INF_TYPE, &desc, &enet_config, &int_config) != status_success) {
+        return status_fail;
+    }
+
+#if defined(__ENABLE_ENET_RECEIVE_INTERRUPT) && __ENABLE_ENET_RECEIVE_INTERRUPT
+    /* Disable LPI interrupt */
+    enet_disable_lpi_interrupt(ENET);
+#endif
+
+/* Initialize phy */
+#if defined(RGMII) && RGMII
+#if defined(__USE_DP83867) && __USE_DP83867
+    dp83867_reset(ptr);
+#if defined(__DISABLE_AUTO_NEGO) && __DISABLE_AUTO_NEGO
+    dp83867_set_mdi_crossover_mode(ENET, enet_phy_mdi_crossover_manual_mdix);
+#endif
+    dp83867_basic_mode_default_config(ptr, &phy_config);
+    if (dp83867_basic_mode_init(ptr, &phy_config) == true) {
+#else
+    rtl8211_reset(ptr);
+    rtl8211_basic_mode_default_config(ptr, &phy_config);
+    if (rtl8211_basic_mode_init(ptr, &phy_config) == true) {
+#endif
+#else
+#if defined(__USE_DP83848) && __USE_DP83848
+    dp83848_reset(ptr);
+    dp83848_basic_mode_default_config(ptr, &phy_config);
+    if (dp83848_basic_mode_init(ptr, &phy_config) == true) {
+#else
+    rtl8201_reset(ptr);
+    rtl8201_basic_mode_default_config(ptr, &phy_config);
+    if (rtl8201_basic_mode_init(ptr, &phy_config) == true) {
+#endif
+#endif
+        printf("Enet phy init passed !\n");
+        return status_success;
+    } else {
+        printf("Enet phy init failed !\n");
+        return status_fail;
+    }
+}
+
+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);
+
+    /* Reset an enet PHY */
+    board_reset_enet_phy(ENET);
+#if defined(RGMII) && RGMII
+    /* Set RGMII clock delay */
+    board_init_enet_rgmii_clock_delay(ENET);
+#else
+    /* Set RMII reference clock */
+    board_init_enet_rmii_reference_clock(ENET, BOARD_ENET_RMII_INT_REF_CLK);
+    printf("Reference Clock: %s\n", BOARD_ENET_RMII_INT_REF_CLK ? "Internal Clock" : "External Clock");
+#endif
+
+    /* Initialize MAC and DMA */
+    if (enet_init(ENET) == 0) {
+    } else {
+        printf("Enet initialization fails !!!\n");
+        while (1) {
+        }
+    }
+
+    ec_memcpy(g_netdev.mac_addr, mac, ENET_MAC);
+
+    for (uint32_t i = 0; i < ENET_TX_BUFF_COUNT; i++) {
+        for (uint8_t j = 0; j < 6; j++) { // dst MAC
+            EC_WRITE_U8(&tx_buff[i][j], 0xFF);
+        }
+        for (uint8_t j = 0; j < 6; j++) { // src MAC
+            EC_WRITE_U8(&tx_buff[i][6 + j], mac[j]);
+        }
+        EC_WRITE_U16(&tx_buff[i][12], ec_htons(0x88a4));
+    }
+
+    return &g_netdev;
+}
+
+void ec_netdev_low_level_enable_irq(ec_netdev_t *netdev, bool enable)
+{
+    if (enable) {
+        /* Enable Enet IRQ */
+        board_enable_enet_irq(ENET);
+    } else {
+        /* Disable Enet IRQ */
+        board_disable_enet_irq(ENET);
+    }
+}
+
+bool ec_netdev_low_level_get_link_state(ec_netdev_t *netdev)
+{
+    static enet_phy_status_t last_status;
+    enet_phy_status_t status = { 0 };
+
+    enet_line_speed_t line_speed[] = { enet_line_speed_10mbps, enet_line_speed_100mbps, enet_line_speed_1000mbps };
+
+#if defined(RGMII) && RGMII
+#if defined(__USE_DP83867) && __USE_DP83867
+    dp83867_get_phy_status(ENET, &status);
+#else
+    rtl8211_get_phy_status(ENET, &status);
+#endif
+#else
+#if defined(__USE_DP83848) && __USE_DP83848
+    dp83848_get_phy_status(ENET, &status);
+#else
+    rtl8201_get_phy_status(ENET, &status);
+#endif
+#endif
+
+    if (memcmp(&last_status, &status, sizeof(enet_phy_status_t)) != 0) {
+        ec_memcpy(&last_status, &status, sizeof(enet_phy_status_t));
+        if (status.enet_phy_link) {
+            enet_set_line_speed(ENET, line_speed[status.enet_phy_speed]);
+            enet_set_duplex_mode(ENET, status.enet_phy_duplex);
+        } else {
+        }
+    }
+
+    return status.enet_phy_link;
+}
+
+EC_FAST_CODE_SECTION uint8_t *ec_netdev_low_level_get_txbuf(ec_netdev_t *netdev)
+{
+    return (uint8_t *)tx_buff[netdev->tx_frame_index];
+}
+
+EC_FAST_CODE_SECTION int ec_netdev_low_level_output(ec_netdev_t *netdev, uint32_t size)
+{
+    __IO enet_tx_desc_t *dma_tx_desc;
+
+    dma_tx_desc = desc.tx_desc_list_cur;
+    if (dma_tx_desc->tdes0_bm.own != 0) {
+        return -1;
+    }
+
+    netdev->tx_frame_index++;
+    netdev->tx_frame_index %= ENET_TX_BUFF_COUNT;
+
+    /* Prepare transmit descriptors to give to DMA*/
+    enet_prepare_transmission_descriptors(ENET, &desc.tx_desc_list_cur, size + 4, desc.tx_buff_cfg.size);
+
+    return 0;
+}
+
+EC_FAST_CODE_SECTION int ec_netdev_low_level_input(ec_netdev_t *netdev)
+{
+    uint32_t len;
+    uint8_t *buffer;
+    enet_frame_t frame = { 0, 0, 0 };
+    enet_rx_desc_t *dma_rx_desc;
+    uint32_t i = 0;
+    int ret = 0;
+
+    /* Check and get a received frame */
+    if (enet_check_received_frame(&desc.rx_desc_list_cur, &desc.rx_frame_info) == 1) {
+        frame = enet_get_received_frame(&desc.rx_desc_list_cur, &desc.rx_frame_info);
+    }
+
+    /* Obtain the size of the packet and put it into the "len" variable. */
+    len = frame.length;
+    buffer = (uint8_t *)sys_address_to_core_local_mem(BOARD_RUNNING_CORE, (uint32_t)frame.buffer);
+
+    if (len > 0) {
+        ec_netdev_receive(netdev, buffer, len);
+        /* Release descriptors to DMA */
+        dma_rx_desc = frame.rx_desc;
+
+        /* Set Own bit in Rx descriptors: gives the buffers back to DMA */
+        for (i = 0; i < desc.rx_frame_info.seg_count; i++) {
+            dma_rx_desc->rdes0_bm.own = 1;
+            dma_rx_desc = (enet_rx_desc_t *)(dma_rx_desc->rdes3_bm.next_desc);
+        }
+
+        /* Clear Segment_Count */
+        desc.rx_frame_info.seg_count = 0;
+    } else {
+        ret = -1;
+    }
+
+    /* Resume Rx Process */
+    enet_rx_resume(ENET);
+    return ret;
+}
+
+#if defined(__ENABLE_ENET_RECEIVE_INTERRUPT) && __ENABLE_ENET_RECEIVE_INTERRUPT
+void isr_enet(ENET_Type *ptr)
+{
+    uint32_t status;
+    uint32_t rxgbfrmis;
+    uint32_t intr_status;
+
+    status = ptr->DMA_STATUS;
+    rxgbfrmis = ptr->MMC_INTR_RX;
+    intr_status = ptr->INTR_STATUS;
+
+    if (ENET_DMA_STATUS_GLPII_GET(status)) {
+        /* read LPI_CSR to clear interrupt status */
+        ptr->LPI_CSR;
+    }
+
+    if (ENET_INTR_STATUS_RGSMIIIS_GET(intr_status)) {
+        /* read XMII_CSR to clear interrupt status */
+        ptr->XMII_CSR;
+    }
+
+    if (ENET_DMA_STATUS_RI_GET(status)) {
+        ptr->DMA_STATUS |= ENET_DMA_STATUS_RI_MASK;
+        ec_netdev_trigger_poll(&g_netdev);
+    }
+
+    if (ENET_MMC_INTR_RX_RXCTRLFIS_GET(rxgbfrmis)) {
+        ptr->RXFRAMECOUNT_GB;
+    }
+}
+
+#ifdef HPM_ENET0_BASE
+void isr_enet0(void)
+{
+    isr_enet(ENET);
+}
+SDK_DECLARE_EXT_ISR_M(IRQn_ENET0, isr_enet0)
+#endif
+
+#ifdef HPM_ENET1_BASE
+void isr_enet1(void)
+{
+    isr_enet(ENET);
+}
+SDK_DECLARE_EXT_ISR_M(IRQn_ENET1, isr_enet1)
+#endif
+
+#endif
+
+#include "hpm_gptmr_drv.h"
+
+#define EC_HTIMER          (HPM_GPTMR2)
+#define EC_HTIMER_CH       0
+#define EC_HTIMER_IRQ      IRQn_GPTMR2
+#define EC_HTIMER_CLK_NAME (clock_gptmr2)
+
+ec_htimer_cb g_ec_htimer_cb = NULL;
+void *g_ec_htimer_arg = NULL;
+
+void ec_htimer_isr(void)
+{
+    if (gptmr_check_status(EC_HTIMER, GPTMR_CH_RLD_STAT_MASK(EC_HTIMER_CH))) {
+        gptmr_clear_status(EC_HTIMER, GPTMR_CH_RLD_STAT_MASK(EC_HTIMER_CH));
+        g_ec_htimer_cb(g_ec_htimer_arg);
+    }
+}
+SDK_DECLARE_EXT_ISR_M(EC_HTIMER_IRQ, ec_htimer_isr);
+
+void ec_htimer_start(uint32_t us, ec_htimer_cb cb, void *arg)
+{
+    uint32_t gptmr_freq;
+    gptmr_channel_config_t config;
+
+    g_ec_htimer_cb = cb;
+    g_ec_htimer_arg = arg;
+
+    gptmr_channel_get_default_config(EC_HTIMER, &config);
+
+    clock_add_to_group(EC_HTIMER_CLK_NAME, 0);
+    gptmr_freq = clock_get_frequency(EC_HTIMER_CLK_NAME);
+
+    config.reload = gptmr_freq / 1000000 * us;
+    gptmr_stop_counter(EC_HTIMER, EC_HTIMER_CH);
+    gptmr_channel_config(EC_HTIMER, EC_HTIMER_CH, &config, false);
+    gptmr_enable_irq(EC_HTIMER, GPTMR_CH_RLD_IRQ_MASK(EC_HTIMER_CH));
+    intc_m_enable_irq_with_priority(EC_HTIMER_IRQ, 10);
+    gptmr_start_counter(EC_HTIMER, EC_HTIMER_CH);
+}
+
+void ec_htimer_stop(void)
+{
+    gptmr_stop_counter(EC_HTIMER, EC_HTIMER_CH);
+    gptmr_disable_irq(EC_HTIMER, GPTMR_CH_RLD_IRQ_MASK(EC_HTIMER_CH));
+    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)
+{
+    return hpm_csr_get_core_mcycle() / g_clock_time_div;
+}

+ 788 - 0
src/ec_cmd.c

@@ -0,0 +1,788 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+
+#define EC_IOCTL_STRING_SIZE 64
+
+typedef struct {
+    uint32_t slave_count;
+    uint8_t phase;
+    uint8_t active;
+    struct ec_ioctl_device {
+        uint8_t mac_addr[6];
+        uint8_t attached;
+        uint8_t link_state;
+        uint64_t tx_count;
+        uint64_t rx_count;
+        uint64_t tx_bytes;
+        uint64_t rx_bytes;
+        uint64_t tx_errors;
+        int32_t tx_frame_rates[EC_RATE_COUNT];
+        int32_t rx_frame_rates[EC_RATE_COUNT];
+        int32_t tx_byte_rates[EC_RATE_COUNT];
+        int32_t rx_byte_rates[EC_RATE_COUNT];
+    } netdevs[CONFIG_EC_MAX_NETDEVS];
+    uint32_t num_netdevs;
+    uint64_t tx_count;
+    uint64_t rx_count;
+    uint64_t tx_bytes;
+    uint64_t rx_bytes;
+    int32_t tx_frame_rates[EC_RATE_COUNT];
+    int32_t rx_frame_rates[EC_RATE_COUNT];
+    int32_t tx_byte_rates[EC_RATE_COUNT];
+    int32_t rx_byte_rates[EC_RATE_COUNT];
+    int32_t loss_rates[EC_RATE_COUNT];
+    uint64_t app_time;
+    uint64_t dc_ref_time;
+    uint16_t ref_clock;
+} ec_cmd_master_info_t;
+
+typedef struct {
+    uint32_t netdev_idx;
+    uint32_t vendor_id;
+    uint32_t product_code;
+    uint32_t revision_number;
+    uint32_t serial_number;
+    uint16_t alias;
+    uint16_t boot_rx_mailbox_offset;
+    uint16_t boot_rx_mailbox_size;
+    uint16_t boot_tx_mailbox_offset;
+    uint16_t boot_tx_mailbox_size;
+    uint16_t std_rx_mailbox_offset;
+    uint16_t std_rx_mailbox_size;
+    uint16_t std_tx_mailbox_offset;
+    uint16_t std_tx_mailbox_size;
+    uint16_t mailbox_protocols;
+    bool has_general;
+    ec_sii_coe_details_t coe_details;
+    ec_sii_general_flags_t general_flags;
+    int16_t current_on_ebus;
+    struct {
+        ec_slave_port_desc_t desc;
+        ec_slave_port_link_t link;
+        uint32_t receive_time;
+        uint16_t next_slave;
+        uint32_t delay_to_next_dc;
+    } ports[EC_MAX_PORTS];
+    uint8_t base_fmmu_bit_operation;
+    uint8_t base_dc_supported;
+    ec_slave_dc_range_t base_dc_range;
+    uint8_t has_dc_system_time;
+    uint32_t transmission_delay;
+    uint8_t current_state;
+    uint8_t error_flag;
+    uint8_t sync_count;
+    uint16_t sdo_count;
+    uint32_t sii_nwords;
+    char group[EC_IOCTL_STRING_SIZE];
+    char image[EC_IOCTL_STRING_SIZE];
+    char order[EC_IOCTL_STRING_SIZE];
+    char name[EC_IOCTL_STRING_SIZE];
+} ec_cmd_slave_info_t;
+
+static ec_master_t *global_cmd_master = NULL;
+
+void ec_master_cmd_init(ec_master_t *master)
+{
+    global_cmd_master = master;
+}
+
+static void ec_master_cmd_show_help(void)
+{
+    EC_LOG_RAW("CherryECAT " CHERRYECAT_VERSION_STR " Command Line Tool\n");
+    EC_LOG_RAW("Usage: ethercat <command> [options]\n\n");
+    EC_LOG_RAW("Commands:\n");
+    EC_LOG_RAW("  master                  Show master information\n");
+    EC_LOG_RAW("  slaves                  Show slaves overview\n");
+    EC_LOG_RAW("  slaves -v               Show detailed information for all slaves\n");
+    EC_LOG_RAW("  slaves -p <idx>         Show information for slave <idx>\n");
+    EC_LOG_RAW("  slaves -p <idx> -v      Show detailed information for slave <idx>\n");
+    EC_LOG_RAW("  pdos                    Show PDOs for all slaves\n");
+    EC_LOG_RAW("  pdos -p <idx>           Show PDOs for slave <idx>\n");
+    EC_LOG_RAW("  states <state>          Request state for all slaves (hex)\n");
+    EC_LOG_RAW("  states -p <idx> <state> Request state for slave <idx> (hex)\n");
+    EC_LOG_RAW("  coe_read -p [idx] [index] [subindex] Read SDO via CoE\n");
+    EC_LOG_RAW("  coe_write -p [idx] [index] [subindex] [value] Write SDO via CoE\n");
+    EC_LOG_RAW("  sii_read -p [idx]       Read SII\n");
+    EC_LOG_RAW("  wc                      Show master working counter\n");
+#ifdef CONFIG_EC_PERF_ENABLE
+    EC_LOG_RAW("  perf -s <time>          Start performance test\n");
+    EC_LOG_RAW("  perf -v                 Show performance statistics\n");
+#endif
+    EC_LOG_RAW("  help                    Show this help\n\n");
+}
+
+static const char *ec_slave_state_string(uint8_t state)
+{
+    switch (state) {
+        case 0x01:
+            return "INIT";
+        case 0x02:
+            return "PREOP";
+        case 0x03:
+            return "BOOT";
+        case 0x04:
+            return "SAFEOP";
+        case 0x08:
+            return "OP";
+        default:
+            return "UNKNOWN";
+    }
+}
+
+// 添加端口类型字符串转换函数
+static const char *ec_port_desc_string(uint8_t desc)
+{
+    switch (desc) {
+        case 0:
+            return "N/A";
+        case 1:
+            return "N/C";
+        case 2:
+            return "EBUS";
+        case 3:
+            return "MII";
+        default:
+            return "???";
+    }
+}
+
+static void ec_master_get_master_info(ec_master_t *master, ec_cmd_master_info_t *info)
+{
+    unsigned int dev_idx;
+    int j;
+
+    info->slave_count = master->slave_count;
+    info->phase = (uint8_t)master->phase;
+
+    for (dev_idx = EC_NETDEV_MAIN; dev_idx < CONFIG_EC_MAX_NETDEVS; dev_idx++) {
+        ec_netdev_t *device = master->netdev[dev_idx];
+
+        ec_memcpy(info->netdevs[dev_idx].mac_addr, device->mac_addr, ETH_ALEN);
+
+        info->netdevs[dev_idx].attached = master->netdev[dev_idx] ? 1 : 0;
+        info->netdevs[dev_idx].link_state = device->link_state ? 1 : 0;
+        info->netdevs[dev_idx].tx_count = device->tx_count;
+        info->netdevs[dev_idx].rx_count = device->rx_count;
+        info->netdevs[dev_idx].tx_bytes = device->tx_bytes;
+        info->netdevs[dev_idx].rx_bytes = device->rx_bytes;
+        info->netdevs[dev_idx].tx_errors = device->tx_errors;
+        for (j = 0; j < EC_RATE_COUNT; j++) {
+            info->netdevs[dev_idx].tx_frame_rates[j] =
+                device->tx_frame_rates[j];
+            info->netdevs[dev_idx].rx_frame_rates[j] =
+                device->rx_frame_rates[j];
+            info->netdevs[dev_idx].tx_byte_rates[j] =
+                device->tx_byte_rates[j];
+            info->netdevs[dev_idx].rx_byte_rates[j] =
+                device->rx_byte_rates[j];
+        }
+    }
+    info->num_netdevs = CONFIG_EC_MAX_NETDEVS;
+
+    info->tx_count = master->netdev_stats.tx_count;
+    info->rx_count = master->netdev_stats.rx_count;
+    info->tx_bytes = master->netdev_stats.tx_bytes;
+    info->rx_bytes = master->netdev_stats.rx_bytes;
+    for (j = 0; j < EC_RATE_COUNT; j++) {
+        info->tx_frame_rates[j] =
+            master->netdev_stats.tx_frame_rates[j];
+        info->rx_frame_rates[j] =
+            master->netdev_stats.rx_frame_rates[j];
+        info->tx_byte_rates[j] =
+            master->netdev_stats.tx_byte_rates[j];
+        info->rx_byte_rates[j] =
+            master->netdev_stats.rx_byte_rates[j];
+        info->loss_rates[j] =
+            master->netdev_stats.loss_rates[j];
+    }
+
+    // info->app_time = master->app_time;
+    // info->dc_ref_time = master->dc_ref_time;
+    // info->ref_clock = master->dc_ref_clock ? master->dc_ref_clock->autoinc_address : 0xffff;
+}
+
+static void ec_master_get_slave_info(ec_slave_t *slave, ec_cmd_slave_info_t *info)
+{
+    int i;
+
+    info->netdev_idx = slave->netdev_idx;
+    info->vendor_id = slave->sii.vendor_id;
+    info->product_code = slave->sii.product_code;
+    info->revision_number = slave->sii.revision_number;
+    info->serial_number = slave->sii.serial_number;
+    info->alias = slave->effective_alias;
+    info->boot_rx_mailbox_offset = slave->sii.boot_rx_mailbox_offset;
+    info->boot_rx_mailbox_size = slave->sii.boot_rx_mailbox_size;
+    info->boot_tx_mailbox_offset = slave->sii.boot_tx_mailbox_offset;
+    info->boot_tx_mailbox_size = slave->sii.boot_tx_mailbox_size;
+    info->std_rx_mailbox_offset = slave->sii.std_rx_mailbox_offset;
+    info->std_rx_mailbox_size = slave->sii.std_rx_mailbox_size;
+    info->std_tx_mailbox_offset = slave->sii.std_tx_mailbox_offset;
+    info->std_tx_mailbox_size = slave->sii.std_tx_mailbox_size;
+    info->mailbox_protocols = slave->sii.mailbox_protocols;
+    info->has_general = slave->sii.has_general;
+    info->coe_details = slave->sii.general.coe_details;
+    info->general_flags = slave->sii.general.flags;
+    info->current_on_ebus = slave->sii.general.current_on_ebus;
+    for (i = 0; i < EC_MAX_PORTS; i++) {
+        info->ports[i].desc = slave->ports[i].desc;
+        info->ports[i].link.link_up = slave->ports[i].link.link_up;
+        info->ports[i].link.loop_closed = slave->ports[i].link.loop_closed;
+        info->ports[i].link.signal_detected =
+            slave->ports[i].link.signal_detected;
+        info->ports[i].receive_time = slave->ports[i].receive_time;
+        if (slave->ports[i].next_slave) {
+            info->ports[i].next_slave =
+                slave->ports[i].next_slave->autoinc_address;
+        } else {
+            info->ports[i].next_slave = 0xffff;
+        }
+        info->ports[i].delay_to_next_dc = slave->ports[i].delay_to_next_dc;
+    }
+    info->base_fmmu_bit_operation = slave->base_fmmu_bit_operation;
+    info->base_dc_supported = slave->base_dc_supported;
+    info->base_dc_range = slave->base_dc_range;
+    info->has_dc_system_time = slave->has_dc_system_time;
+    info->transmission_delay = slave->transmission_delay;
+    info->current_state = slave->current_state;
+}
+
+void ec_master_cmd_master(ec_master_t *master)
+{
+    unsigned int dev_idx, j;
+    uint64_t lost;
+    double perc;
+    int colwidth = 8;
+    ec_cmd_master_info_t data;
+
+    ec_osal_mutex_take(master->scan_lock);
+    ec_master_get_master_info(master, &data);
+    ec_osal_mutex_give(master->scan_lock);
+
+    EC_LOG_RAW("Master%d\n", master->index);
+    EC_LOG_RAW("  Phase: ");
+    switch (data.phase) {
+        case 0:
+            EC_LOG_RAW("Waiting for device(s)...");
+            break;
+        case 1:
+            EC_LOG_RAW("Idle");
+            break;
+        case 2:
+            EC_LOG_RAW("Operation");
+            break;
+        default:
+            EC_LOG_RAW("???");
+    }
+
+    EC_LOG_RAW("\n");
+    EC_LOG_RAW("  Slaves: %u\n", data.slave_count);
+    EC_LOG_RAW("  Ethernet net devices:\n");
+
+    for (dev_idx = EC_NETDEV_MAIN; dev_idx < CONFIG_EC_MAX_NETDEVS; dev_idx++) {
+        EC_LOG_RAW("    %s: ", dev_idx == EC_NETDEV_MAIN ? "Main" : "Backup");
+        EC_LOG_RAW("%02x:%02x:%02x:%02x:%02x:%02x\n",
+                   data.netdevs[dev_idx].mac_addr[0],
+                   data.netdevs[dev_idx].mac_addr[1],
+                   data.netdevs[dev_idx].mac_addr[2],
+                   data.netdevs[dev_idx].mac_addr[3],
+                   data.netdevs[dev_idx].mac_addr[4],
+                   data.netdevs[dev_idx].mac_addr[5]);
+        EC_LOG_RAW("      Link: %s\n", data.netdevs[dev_idx].link_state ? "UP" : "DOWN");
+        EC_LOG_RAW("      Tx frames:   %llu\n", data.netdevs[dev_idx].tx_count);
+        EC_LOG_RAW("      Tx bytes:    %llu\n", data.netdevs[dev_idx].tx_bytes);
+        EC_LOG_RAW("      Rx frames:   %llu\n", data.netdevs[dev_idx].rx_count);
+        EC_LOG_RAW("      Rx bytes:    %llu\n", data.netdevs[dev_idx].rx_bytes);
+        EC_LOG_RAW("      Tx errors:   %llu\n", data.netdevs[dev_idx].tx_errors);
+
+        EC_LOG_RAW("      Tx frame rate [1/s]: ");
+        for (j = 0; j < EC_RATE_COUNT; j++) {
+            EC_LOG_RAW("%*.*f", colwidth, 0, data.netdevs[dev_idx].tx_frame_rates[j] / 1000.0);
+            if (j < EC_RATE_COUNT - 1)
+                EC_LOG_RAW(" ");
+        }
+        EC_LOG_RAW("\n      Tx rate [KByte/s]:   ");
+        for (j = 0; j < EC_RATE_COUNT; j++) {
+            EC_LOG_RAW("%*.*f", colwidth, 1, data.netdevs[dev_idx].tx_byte_rates[j] / 1024.0);
+            if (j < EC_RATE_COUNT - 1)
+                EC_LOG_RAW(" ");
+        }
+        EC_LOG_RAW("\n      Rx frame rate [1/s]: ");
+        for (j = 0; j < EC_RATE_COUNT; j++) {
+            EC_LOG_RAW("%*.*f", colwidth, 0, data.netdevs[dev_idx].rx_frame_rates[j] / 1000.0);
+            if (j < EC_RATE_COUNT - 1)
+                EC_LOG_RAW(" ");
+        }
+        EC_LOG_RAW("\n      Rx rate [KByte/s]:   ");
+        for (j = 0; j < EC_RATE_COUNT; j++) {
+            EC_LOG_RAW("%*.*f", colwidth, 1, data.netdevs[dev_idx].rx_byte_rates[j] / 1024.0);
+            if (j < EC_RATE_COUNT - 1)
+                EC_LOG_RAW(" ");
+        }
+        EC_LOG_RAW("\n");
+    }
+
+    lost = data.tx_count - data.rx_count;
+    if (lost == 1)
+        lost = 0;
+    EC_LOG_RAW("    Common:\n");
+    EC_LOG_RAW("      Tx frames:   %llu\n", data.tx_count);
+    EC_LOG_RAW("      Tx bytes:    %llu\n", data.tx_bytes);
+    EC_LOG_RAW("      Rx frames:   %llu\n", data.rx_count);
+    EC_LOG_RAW("      Rx bytes:    %llu\n", data.rx_bytes);
+    EC_LOG_RAW("      Lost frames: %llu\n", lost);
+
+    EC_LOG_RAW("      Tx frame rate [1/s]: ");
+    for (j = 0; j < EC_RATE_COUNT; j++) {
+        EC_LOG_RAW("%*.*f", colwidth, 0, data.tx_frame_rates[j] / 1000.0);
+        if (j < EC_RATE_COUNT - 1)
+            EC_LOG_RAW(" ");
+    }
+    EC_LOG_RAW("\n      Tx rate [KByte/s]:   ");
+    for (j = 0; j < EC_RATE_COUNT; j++) {
+        EC_LOG_RAW("%*.*f", colwidth, 1, data.tx_byte_rates[j] / 1024.0);
+        if (j < EC_RATE_COUNT - 1)
+            EC_LOG_RAW(" ");
+    }
+    EC_LOG_RAW("\n      Rx frame rate [1/s]: ");
+    for (j = 0; j < EC_RATE_COUNT; j++) {
+        EC_LOG_RAW("%*.*f", colwidth, 0, data.rx_frame_rates[j] / 1000.0);
+        if (j < EC_RATE_COUNT - 1)
+            EC_LOG_RAW(" ");
+    }
+    EC_LOG_RAW("\n      Rx rate [KByte/s]:   ");
+    for (j = 0; j < EC_RATE_COUNT; j++) {
+        EC_LOG_RAW("%*.*f", colwidth, 1, data.rx_byte_rates[j] / 1024.0);
+        if (j < EC_RATE_COUNT - 1)
+            EC_LOG_RAW(" ");
+    }
+    EC_LOG_RAW("\n      Loss rate [1/s]:     ");
+    for (j = 0; j < EC_RATE_COUNT; j++) {
+        EC_LOG_RAW("%*.*f", colwidth, 0, data.loss_rates[j] / 1000.0);
+        if (j < EC_RATE_COUNT - 1)
+            EC_LOG_RAW(" ");
+    }
+    EC_LOG_RAW("\n      Frame loss [%%]:      ");
+    for (j = 0; j < EC_RATE_COUNT; j++) {
+        perc = 0.0;
+        if (data.tx_frame_rates[j])
+            perc = 100.0 * data.loss_rates[j] / data.tx_frame_rates[j];
+        EC_LOG_RAW("%*.*f", colwidth, 1, perc);
+        if (j < EC_RATE_COUNT - 1)
+            EC_LOG_RAW(" ");
+    }
+    EC_LOG_RAW("\n");
+}
+
+static void ec_cmd_show_slave_detail(ec_master_t *master, uint32_t slave_idx)
+{
+    unsigned int port_idx;
+    ec_slave_t *slave;
+    ec_cmd_slave_info_t data;
+
+    if (slave_idx >= master->slave_count) {
+        EC_LOG_RAW("No slaves found\n");
+        return;
+    }
+
+    slave = &master->slaves[slave_idx];
+
+    ec_osal_mutex_take(master->scan_lock);
+    ec_master_get_slave_info(slave, &data);
+    ec_osal_mutex_give(master->scan_lock);
+
+    EC_LOG_RAW("=== Master %d, Slave %d ===\n", master->index, slave_idx);
+
+    if (data.alias != 0) {
+        EC_LOG_RAW("Alias: %d\n", data.alias != 0);
+    }
+
+    EC_LOG_RAW("Device: %s\n", master->netdev[slave->netdev_idx]->name);
+    EC_LOG_RAW("State: %s\n", ec_slave_state_string(data.current_state));
+
+    EC_LOG_RAW("Identity:\n");
+    EC_LOG_RAW("  Vendor Id:       0x%08x\n", data.vendor_id);
+    EC_LOG_RAW("  Product code:    0x%08x\n", data.product_code);
+    EC_LOG_RAW("  Revision number: 0x%08x\n", data.revision_number);
+    EC_LOG_RAW("  Serial number:   0x%08x\n", data.serial_number);
+
+    EC_LOG_RAW("DL information:\n");
+    EC_LOG_RAW("  FMMU bit operation: %s\n", (data.base_fmmu_bit_operation ? "yes" : "no"));
+    EC_LOG_RAW("  Distributed clocks: ");
+
+    if (data.base_dc_supported) {
+        if (data.has_dc_system_time) {
+            EC_LOG_RAW("yes, ");
+            if (data.base_dc_range) {
+                EC_LOG_RAW("64 bit\n");
+            } else {
+                EC_LOG_RAW("32 bit\n");
+            }
+            EC_LOG_RAW("  DC system time transmission delay: %d ns\n",
+                       data.transmission_delay);
+        } else {
+            EC_LOG_RAW("yes, delay measurement only\n");
+        }
+    } else {
+        EC_LOG_RAW("no\n");
+    }
+
+    EC_LOG_RAW("Port  Type  Link  Loop    Signal  NextSlave");
+    if (data.base_dc_supported) {
+        EC_LOG_RAW("  RxTime [ns]  Diff [ns]   NextDc [ns]");
+    }
+    EC_LOG_RAW("\n");
+
+    for (port_idx = 0; port_idx < EC_MAX_PORTS; port_idx++) {
+        EC_LOG_RAW("   %d  %-4s  %-4s  %-6s  %-6s  ",
+                   port_idx,
+                   ec_port_desc_string(data.ports[port_idx].desc),
+                   (data.ports[port_idx].link.link_up ? "up" : "down"),
+                   (data.ports[port_idx].link.loop_closed ? "closed" : "open"),
+                   (data.ports[port_idx].link.signal_detected ? "yes" : "no"));
+
+        if (data.ports[port_idx].next_slave != 0xffff) {
+            EC_LOG_RAW("%-9d", data.ports[port_idx].next_slave);
+        } else {
+            EC_LOG_RAW("%-9s", "-");
+        }
+
+        if (data.base_dc_supported) {
+            if (!data.ports[port_idx].link.loop_closed) {
+                EC_LOG_RAW("  %11u  %10d  %10d",
+                           data.ports[port_idx].receive_time,
+                           data.ports[port_idx].receive_time - data.ports[0].receive_time,
+                           data.ports[port_idx].delay_to_next_dc);
+            } else {
+                EC_LOG_RAW("  %11s  %10s  %10s", "-", "-", "-");
+            }
+        }
+
+        EC_LOG_RAW("\n");
+    }
+
+    if (data.mailbox_protocols) {
+        EC_LOG_RAW("Mailboxes:\n");
+        EC_LOG_RAW("  Bootstrap RX: 0x%04x/%d, TX: 0x%04x/%d\n",
+                   data.boot_rx_mailbox_offset,
+                   data.boot_rx_mailbox_size,
+                   data.boot_tx_mailbox_offset,
+                   data.boot_tx_mailbox_size);
+        EC_LOG_RAW("  Standard  RX: 0x%04x/%d, TX: 0x%04x/%d\n",
+                   data.std_rx_mailbox_offset,
+                   data.std_rx_mailbox_size,
+                   data.std_tx_mailbox_offset,
+                   data.std_tx_mailbox_size);
+
+        EC_LOG_RAW("  Supported protocols: %s\n", ec_mbox_protocol_string(data.mailbox_protocols));
+    }
+
+    if (data.has_general) {
+        EC_LOG_RAW("General:\n");
+        // EC_LOG_RAW("  Group: %s\n", data.sii.group ? data.sii.group : "");
+        // EC_LOG_RAW("  Image name: %s\n", data.sii.image ? data.sii.image : "");
+        // EC_LOG_RAW("  Order number: %s\n", data.sii.order ? data.sii.order : "");
+        // EC_LOG_RAW("  Device name: %s\n", data.sii.name ? data.sii.name : "");
+
+        if (data.mailbox_protocols & EC_MBXPROT_COE) {
+            EC_LOG_RAW("  CoE details:\n");
+            EC_LOG_RAW("    Enable SDO: %s\n",
+                       (data.coe_details.enable_sdo ? "yes" : "no"));
+            EC_LOG_RAW("    Enable SDO Info: %s\n",
+                       (data.coe_details.enable_sdo_info ? "yes" : "no"));
+            EC_LOG_RAW("    Enable PDO Assign: %s\n",
+                       (data.coe_details.enable_pdo_assign ? "yes" : "no"));
+            EC_LOG_RAW("    Enable PDO Configuration: %s\n",
+                       (data.coe_details.enable_pdo_configuration ? "yes" : "no"));
+            EC_LOG_RAW("    Enable Upload at startup: %s\n",
+                       (data.coe_details.enable_upload_at_startup ? "yes" : "no"));
+            EC_LOG_RAW("    Enable SDO complete access: %s\n",
+                       (data.coe_details.enable_sdo_complete_access ? "yes" : "no"));
+        }
+
+        EC_LOG_RAW("  Flags:\n");
+        EC_LOG_RAW("    Enable SafeOp: %s\n",
+                   (data.general_flags.enable_safeop ? "yes" : "no"));
+        EC_LOG_RAW("    Enable notLRW: %s\n",
+                   (data.general_flags.enable_not_lrw ? "yes" : "no"));
+        EC_LOG_RAW("  Current consumption: %d mA\n", data.current_on_ebus);
+    }
+
+    EC_LOG_RAW("\n");
+}
+
+static void ec_cmd_show_slave_simple(ec_master_t *master, uint32_t slave_idx)
+{
+    ec_slave_t *slave;
+    ec_cmd_slave_info_t data;
+
+    if (slave_idx >= master->slave_count) {
+        EC_LOG_RAW("No slaves found\n");
+        return;
+    }
+
+    slave = &master->slaves[slave_idx];
+
+    ec_osal_mutex_take(master->scan_lock);
+    ec_master_get_slave_info(slave, &data);
+    ec_osal_mutex_give(master->scan_lock);
+
+    EC_LOG_RAW("%-3u  %u:%04x   %-13s %s\n",
+               master->index,
+               slave_idx,
+               slave->autoinc_address,
+               ec_state_string(slave->current_state, 0),
+               ec_alstatus_string(slave->alstatus_code));
+}
+
+static void ec_cmd_show_slave_pdos(ec_master_t *master, uint32_t slave_idx)
+{
+    if (slave_idx >= master->slave_count) {
+        EC_LOG_RAW("No slaves found\n");
+        return;
+    }
+
+    EC_LOG_RAW("=== Master %d, Slave %d ===\n", master->index, slave_idx);
+
+    for (uint8_t i = 0; i < master->slaves[slave_idx].sm_count; i++) {
+        EC_LOG_RAW("SM%u: physaddr 0x%04x, length 0x%04x, control 0x%02x, enable %d\r\n",
+                   i,
+                   master->slaves[slave_idx].sm_info[i].physical_start_address,
+                   master->slaves[slave_idx].sm_info[i].length,
+                   master->slaves[slave_idx].sm_info[i].control,
+                   master->slaves[slave_idx].sm_info[i].enable);
+
+        // if sm is process data output, print rxpdo info
+        if (i == EC_SM_INDEX_PROCESS_DATA_OUTPUT) {
+            for (uint16_t j = 0; j < master->slaves[slave_idx].sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_assign.count; j++) {
+                EC_LOG_RAW("\tRxPDO 0x%04x\r\n", master->slaves[slave_idx].sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_assign.entry[j]);
+
+                for (uint16_t k = 0; k < master->slaves[slave_idx].sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_mapping[j].count; k++) {
+                    EC_LOG_RAW("\t\tPDO entry 0x%04x:0x%02x, bitlen 0x%02x\r\n",
+                               master->slaves[slave_idx].sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_mapping[j].entry[k] >> 16 & 0xffff,
+                               master->slaves[slave_idx].sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_mapping[j].entry[k] >> 8 & 0xff,
+                               master->slaves[slave_idx].sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_mapping[j].entry[k] >> 0 & 0xff);
+                }
+            }
+        }
+
+        // if sm is process data input, print txpdo info
+        if (i == EC_SM_INDEX_PROCESS_DATA_INPUT) {
+            for (uint16_t j = 0; j < master->slaves[slave_idx].sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_assign.count; j++) {
+                EC_LOG_RAW("\tTxPDO 0x%04x\r\n", master->slaves[slave_idx].sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_assign.entry[j]);
+
+                for (uint16_t k = 0; k < master->slaves[slave_idx].sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_mapping[j].count; k++) {
+                    EC_LOG_RAW("\t\tPDO entry 0x%04x:0x%02x, bitlen 0x%02x\r\n",
+                               master->slaves[slave_idx].sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_mapping[j].entry[k] >> 16 & 0xffff,
+                               master->slaves[slave_idx].sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_mapping[j].entry[k] >> 8 & 0xff,
+                               master->slaves[slave_idx].sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_mapping[j].entry[k] >> 0 & 0xff);
+                }
+            }
+        }
+    }
+}
+
+static void ec_cmd_slave_state_request(ec_master_t *master, uint32_t slave_idx, ec_slave_state_t state)
+{
+    if (slave_idx >= master->slave_count) {
+        EC_LOG_RAW("No slaves found\n");
+        return;
+    }
+
+    ec_slave_t *slave = &master->slaves[slave_idx];
+    slave->requested_state = state;
+}
+
+int ethercat(int argc, const char **argv)
+{
+    if (global_cmd_master == NULL) {
+        EC_LOG_RAW("No master configured\n");
+        return -1;
+    }
+
+    if (argc < 2) {
+        ec_master_cmd_show_help();
+        return -1;
+    }
+
+    if (strcmp(argv[1], "help") == 0) {
+        ec_master_cmd_show_help();
+        return 0;
+    } else if (strcmp(argv[1], "master") == 0) {
+        ec_master_cmd_master(global_cmd_master);
+        return 0;
+#ifdef CONFIG_EC_PERF_ENABLE
+    } else if (strcmp(argv[1], "perf") == 0) {
+        if (argc >= 4 && strcmp(argv[2], "-s") == 0) {
+            ec_perf_init(&global_cmd_master->perf, atoi(argv[3]));
+            return 0;
+        } else if (argc >= 3 && strcmp(argv[2], "-v") == 0) {
+            ec_perf_print_statistics(&global_cmd_master->perf);
+            return 0;
+        }
+#endif
+    } else if (strcmp(argv[1], "slaves") == 0) {
+        // ethercat slaves
+        if (argc == 2) {
+            for (uint32_t i = 0; i < global_cmd_master->slave_count; i++) {
+                ec_cmd_show_slave_simple(global_cmd_master, i);
+            }
+            return 0;
+        } else if (argc >= 4 && strcmp(argv[2], "-p") == 0) {
+            // ethercat slaves -p [x] [-v]
+            int show_detail = 0;
+            if (argc == 5) {
+                if (argc == 5 && strcmp(argv[4], "-v") == 0) {
+                    show_detail = 1;
+                }
+            }
+
+            if (show_detail) {
+                ec_cmd_show_slave_detail(global_cmd_master, atoi(argv[3]));
+            } else {
+                ec_cmd_show_slave_simple(global_cmd_master, atoi(argv[3]));
+            }
+
+            return 0;
+        } else if (argc == 3 && strcmp(argv[2], "-v") == 0) {
+            // ethercat slaves -v
+            for (uint32_t i = 0; i < global_cmd_master->slave_count; i++) {
+                ec_cmd_show_slave_detail(global_cmd_master, i);
+            }
+
+            return 0;
+        } else {
+        }
+    } else if (strcmp(argv[1], "pdos") == 0) {
+        // ethercat pdos
+        if (argc == 2) {
+            for (uint32_t i = 0; i < global_cmd_master->slave_count; i++) {
+                ec_cmd_show_slave_pdos(global_cmd_master, i);
+            }
+            return 0;
+        } else if (argc == 4 && strcmp(argv[2], "-p") == 0) {
+            // ethercat pdos -p [x]
+            ec_cmd_show_slave_pdos(global_cmd_master, atoi(argv[3]));
+            return 0;
+        } else if (argc == 3 && strcmp(argv[2], "-v") == 0) {
+            // ethercat slaves -v
+            for (uint32_t i = 0; i < global_cmd_master->slave_count; i++) {
+                ec_cmd_show_slave_detail(global_cmd_master, i);
+            }
+
+            return 0;
+        } else {
+        }
+    } else if (argc >= 3 && strcmp(argv[1], "states") == 0) {
+        // ethercat states
+        if (argc == 3) {
+            // ethercat states [state]
+            for (uint32_t i = 0; i < global_cmd_master->slave_count; i++) {
+                ec_cmd_slave_state_request(global_cmd_master, i, strtol(argv[4], NULL, 16));
+            }
+            return 0;
+        } else if (argc == 5 && strcmp(argv[2], "-p") == 0) {
+            // ethercat states -p [x] [state]
+            ec_cmd_slave_state_request(global_cmd_master, atoi(argv[3]), strtol(argv[4], NULL, 16));
+            return 0;
+        } else {
+        }
+    } else if (argc >= 2 && strcmp(argv[1], "wc") == 0) {
+        // ethercat wc
+        if (argc == 2) {
+            // ethercat wc
+            EC_LOG_RAW("Master %d working counter(expect/actual): %u/%u\n",
+                       global_cmd_master->index,
+                       global_cmd_master->expected_working_counter,
+                       global_cmd_master->actual_working_counter);
+            return 0;
+        } else {
+        }
+    } else if (argc >= 5 && strcmp(argv[1], "coe_read") == 0) {
+        // ethercat coe_read -p [slave_idx] [index] [subindex]
+        static ec_datagram_t datagram;
+        static uint8_t output_buffer[512];
+        uint32_t actual_size;
+        int ret;
+
+        uint32_t slave_idx = atoi(argv[3]);
+        if (slave_idx >= global_cmd_master->slave_count) {
+            EC_LOG_RAW("No slaves found\n");
+            return -1;
+        }
+        ec_datagram_init(&datagram, 512);
+        ret = ec_coe_upload(&global_cmd_master->slaves[slave_idx],
+                            &datagram,
+                            strtol(argv[4], NULL, 16),
+                            argc >= 6 ? strtol(argv[5], NULL, 16) : 0x00,
+                            output_buffer,
+                            sizeof(output_buffer),
+                            &actual_size,
+                            argc >= 6 ? false : true);
+        if (ret < 0) {
+            EC_SLAVE_LOG_ERR("Slave %u coe_read failed: %d\n", slave_idx, ret);
+        } else {
+            ec_hexdump(output_buffer, actual_size);
+        }
+        ec_datagram_clear(&datagram);
+
+        return 0;
+    } else if (argc >= 6 && strcmp(argv[1], "coe_write") == 0) {
+        // ethercat coe_write -p [slave_idx] [index] [subindex] [u32data]
+        static ec_datagram_t datagram;
+        uint32_t u32data;
+        uint32_t size;
+        int ret;
+
+        uint32_t slave_idx = atoi(argv[3]);
+        if (slave_idx >= global_cmd_master->slave_count) {
+            EC_LOG_RAW("No slaves found\n");
+            return -1;
+        }
+        u32data = strtol(argv[6], NULL, 16);
+
+        if (u32data < 0xff)
+            size = 1;
+        else if (u32data < 0xffff)
+            size = 2;
+        else
+            size = 4;
+
+        ec_datagram_init(&datagram, 512);
+        ret = ec_coe_download(&global_cmd_master->slaves[slave_idx],
+                              &datagram,
+                              strtol(argv[4], NULL, 16),
+                              strtol(argv[5], NULL, 16),
+                              &u32data,
+                              size,
+                              false);
+        if (ret < 0) {
+            EC_SLAVE_LOG_ERR("Slave %u coe_write failed: %d\n", slave_idx, ret);
+        } else {
+            EC_LOG_RAW("Slave %u coe write success\n", slave_idx);
+        }
+        ec_datagram_clear(&datagram);
+        return 0;
+    } else if (argc >= 4 && strcmp(argv[1], "sii_read") == 0) {
+        // ethercat sii_read -p [slave_idx]
+        uint32_t slave_idx = atoi(argv[3]);
+        if (slave_idx >= global_cmd_master->slave_count) {
+            EC_LOG_RAW("No slaves found\n");
+            return -1;
+        }
+
+        ec_hexdump(global_cmd_master->slaves[slave_idx].sii_image,
+                   global_cmd_master->slaves[slave_idx].sii_nwords * 2);
+
+        return 0;
+    } else {
+    }
+
+    EC_LOG_RAW("Invalid command: %s\n", argv[1]);
+    ec_master_cmd_show_help();
+    return -1;
+}

+ 524 - 0
src/ec_coe.c

@@ -0,0 +1,524 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+
+typedef struct __PACKED {
+    ec_coe_header_t coe_header;
+    ec_sdo_header_common_t sdo_header;
+    uint16_t index;
+    uint8_t subindex;
+    uint8_t data[4];
+} ec_coe_download_common_header_t;
+
+typedef struct __PACKED {
+    ec_coe_header_t coe_header;
+    ec_sdo_header_segment_t sdo_header;
+} ec_coe_download_segment_header_t;
+
+typedef ec_coe_download_common_header_t ec_coe_upload_common_header_t;
+typedef ec_coe_download_segment_header_t ec_coe_upload_segment_header_t;
+
+/** CoE download request header size.
+ */
+#define EC_COE_DOWN_REQ_HEADER_SIZE     (sizeof(ec_coe_download_common_header_t))
+
+/** CoE upload request header size.
+ */
+#define EC_COE_UP_REQ_HEADER_SIZE       (sizeof(ec_coe_upload_common_header_t))
+
+/** CoE download segment request header size.
+ */
+#define EC_COE_DOWN_SEG_REQ_HEADER_SIZE (sizeof(ec_coe_download_segment_header_t))
+
+/** CoE upload segment request header size.
+ */
+#define EC_COE_UP_SEG_REQ_HEADER_SIZE   (sizeof(ec_coe_upload_segment_header_t))
+
+/** Minimum size of download segment.
+ */
+#define EC_COE_DOWN_SEG_MIN_DATA_SIZE   7
+
+static int ec_coe_download_expedited(ec_slave_t *slave,
+                                     ec_datagram_t *datagram,
+                                     uint16_t index,
+                                     uint8_t subindex,
+                                     const void *buf,
+                                     uint32_t size,
+                                     bool complete_access)
+{
+    uint8_t *data;
+    uint8_t mbox_proto;
+    uint32_t recv_size;
+    ec_coe_download_common_header_t *download_common;
+    int ret;
+
+    data = ec_mailbox_fill_send(slave, datagram, EC_MBOX_TYPE_COE, EC_COE_DOWN_REQ_HEADER_SIZE);
+
+    download_common = (ec_coe_download_common_header_t *)data;
+    download_common->coe_header.number = 0;
+    download_common->coe_header.reserved = 0;
+    download_common->coe_header.service = EC_COE_SERVICE_SDO_REQUEST;
+    download_common->sdo_header.size_indicator = 1;
+    download_common->sdo_header.transfertype = 1; // expedited
+    download_common->sdo_header.data_set_size = 4 - size;
+    download_common->sdo_header.complete_access = complete_access ? 1 : 0;
+    download_common->sdo_header.command = EC_COE_REQUEST_DOWNLOAD;
+
+    download_common->index = index;
+    download_common->subindex = complete_access ? 0x00 : subindex;
+
+    ec_memcpy(download_common->data, buf, size);
+    memset(download_common->data + size, 0x00, 4 - size);
+    ret = ec_mailbox_send(slave, datagram);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = ec_mailbox_receive(slave, datagram, &mbox_proto, &recv_size);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (mbox_proto != EC_MBOX_TYPE_COE) {
+        return -EC_ERR_COE_TYPE;
+    }
+
+    if (recv_size < 6) {
+        return -EC_ERR_COE_SIZE;
+    }
+
+    data = datagram->data + EC_MBOX_HEADER_SIZE;
+
+    if (EC_READ_U16(data) >> 12 == EC_COE_SERVICE_SDO_REQUEST &&
+        EC_READ_U8(data + 2) >> 5 == EC_COE_REQUEST_ABORT) {
+        EC_SLAVE_LOG_ERR("Slave %u SDO abort code: 0x%08x (%s)\n", slave->index, EC_READ_U32(data + 6), ec_sdo_abort_string(EC_READ_U32(data + 6)));
+        return -EC_ERR_COE_ABORT;
+    }
+
+    if (EC_READ_U16(data) >> 12 != EC_COE_SERVICE_SDO_RESPONSE ||
+        EC_READ_U8(data + 2) >> 5 != EC_COE_RESPONSE_DOWNLOAD ||
+        EC_READ_U16(data + 3) != index ||
+        EC_READ_U8(data + 5) != subindex) {
+        return -EC_ERR_COE_REQUEST;
+    }
+
+    return 0;
+}
+
+static int ec_coe_download_common(ec_slave_t *slave,
+                                  ec_datagram_t *datagram,
+                                  uint16_t index,
+                                  uint8_t subindex,
+                                  const void *buf,
+                                  uint32_t size,
+                                  bool complete_access)
+{
+    uint8_t *data;
+    uint8_t mbox_proto;
+    uint32_t data_size, recv_size, max_data_size;
+    ec_coe_download_common_header_t *download_common;
+    int ret;
+
+    max_data_size = slave->configured_rx_mailbox_size - EC_MBOX_HEADER_SIZE - EC_COE_DOWN_REQ_HEADER_SIZE;
+
+    if (size > max_data_size) {
+        data_size = EC_COE_DOWN_REQ_HEADER_SIZE + max_data_size;
+    } else {
+        data_size = EC_COE_DOWN_REQ_HEADER_SIZE + size;
+    }
+
+    data = ec_mailbox_fill_send(slave, datagram, EC_MBOX_TYPE_COE, data_size);
+
+    download_common = (ec_coe_download_common_header_t *)data;
+    download_common->coe_header.number = 0;
+    download_common->coe_header.reserved = 0;
+    download_common->coe_header.service = EC_COE_SERVICE_SDO_REQUEST;
+    download_common->sdo_header.size_indicator = 1;
+    download_common->sdo_header.transfertype = 0; // normal
+    download_common->sdo_header.data_set_size = 0;
+    download_common->sdo_header.complete_access = complete_access ? 1 : 0;
+    download_common->sdo_header.command = EC_COE_REQUEST_DOWNLOAD;
+
+    download_common->index = index;
+    download_common->subindex = complete_access ? 0x00 : subindex;
+
+    ec_memcpy(download_common->data, &size, 4);
+    ec_memcpy(data + EC_COE_DOWN_REQ_HEADER_SIZE, buf, data_size - EC_COE_DOWN_REQ_HEADER_SIZE);
+    ret = ec_mailbox_send(slave, datagram);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = ec_mailbox_receive(slave, datagram, &mbox_proto, &recv_size);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (mbox_proto != EC_MBOX_TYPE_COE) {
+        return -EC_ERR_COE_TYPE;
+    }
+
+    if (recv_size < 6) {
+        return -EC_ERR_COE_SIZE;
+    }
+
+    data = datagram->data + EC_MBOX_HEADER_SIZE;
+
+    if (EC_READ_U16(data) >> 12 == EC_COE_SERVICE_SDO_REQUEST &&
+        EC_READ_U8(data + 2) >> 5 == EC_COE_REQUEST_ABORT) {
+        EC_SLAVE_LOG_ERR("Slave %u SDO abort code: 0x%08x (%s)\n", slave->index, EC_READ_U32(data + 6), ec_sdo_abort_string(EC_READ_U32(data + 6)));
+        return -EC_ERR_COE_ABORT;
+    }
+
+    if (EC_READ_U16(data) >> 12 != EC_COE_SERVICE_SDO_RESPONSE ||
+        EC_READ_U8(data + 2) >> 5 != EC_COE_RESPONSE_DOWNLOAD ||
+        EC_READ_U16(data + 3) != index ||
+        EC_READ_U8(data + 5) != subindex) {
+        return -EC_ERR_COE_REQUEST;
+    }
+
+    return 0;
+}
+
+static int ec_coe_download_segment(ec_slave_t *slave,
+                                   ec_datagram_t *datagram,
+                                   const void *seg_data,
+                                   uint16_t size,
+                                   bool toggle,
+                                   bool last)
+{
+    uint8_t *data;
+    uint8_t mbox_proto;
+    uint32_t data_size, recv_size;
+    uint32_t seg_size;
+    ec_coe_download_segment_header_t *download_seg;
+    int ret;
+
+    if (size > EC_COE_DOWN_SEG_MIN_DATA_SIZE) {
+        seg_size = 0;
+        data_size = EC_COE_DOWN_SEG_REQ_HEADER_SIZE + size;
+    } else {
+        seg_size = EC_COE_DOWN_SEG_MIN_DATA_SIZE - size;
+        data_size = EC_COE_DOWN_SEG_REQ_HEADER_SIZE + EC_COE_DOWN_SEG_MIN_DATA_SIZE;
+    }
+
+    data = ec_mailbox_fill_send(slave, datagram, EC_MBOX_TYPE_COE, data_size);
+
+    download_seg = (ec_coe_download_segment_header_t *)data;
+    download_seg->coe_header.number = 0;
+    download_seg->coe_header.reserved = 0;
+    download_seg->coe_header.service = EC_COE_SERVICE_SDO_REQUEST;
+
+    download_seg->sdo_header.more_follows = last ? 1 : 0;
+    download_seg->sdo_header.toggle = toggle ? 1 : 0;
+    download_seg->sdo_header.command = EC_COE_REQUEST_SEGMENT_DOWNLOAD;
+    download_seg->sdo_header.segdata_size = seg_size;
+
+    ec_memcpy(data + EC_COE_DOWN_SEG_REQ_HEADER_SIZE, seg_data, size);
+    if (size < EC_COE_DOWN_SEG_MIN_DATA_SIZE) {
+        memset(data + EC_COE_DOWN_SEG_REQ_HEADER_SIZE + size, 0x00, EC_COE_DOWN_SEG_MIN_DATA_SIZE - size);
+    }
+
+    ret = ec_mailbox_send(slave, datagram);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = ec_mailbox_receive(slave, datagram, &mbox_proto, &recv_size);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (mbox_proto != EC_MBOX_TYPE_COE) {
+        return -EC_ERR_COE_TYPE;
+    }
+
+    if (recv_size < 6) {
+        return -EC_ERR_COE_SIZE;
+    }
+
+    data = datagram->data + EC_MBOX_HEADER_SIZE;
+
+    if (EC_READ_U16(data) >> 12 == EC_COE_SERVICE_SDO_REQUEST &&
+        EC_READ_U8(data + 2) >> 5 == EC_COE_REQUEST_ABORT) {
+        EC_SLAVE_LOG_ERR("Slave %u SDO abort code: 0x%08x (%s)\n", slave->index, EC_READ_U32(data + 6), ec_sdo_abort_string(EC_READ_U32(data + 6)));
+        return -EC_ERR_COE_ABORT;
+    }
+
+    if (EC_READ_U16(data) >> 12 != EC_COE_SERVICE_SDO_RESPONSE ||
+        EC_READ_U8(data + 2) >> 5 != EC_COE_RESPONSE_SEGMENT_DOWNLOAD) {
+        return -EC_ERR_COE_REQUEST;
+    }
+
+    if (((EC_READ_U8(data + 2) >> 4) & 0x01) != toggle) {
+        return -EC_ERR_COE_TOGGLE;
+    }
+
+    return 0;
+}
+
+int ec_coe_download(ec_slave_t *slave,
+                    ec_datagram_t *datagram,
+                    uint16_t index,
+                    uint8_t subindex,
+                    const void *buf,
+                    uint32_t size,
+                    bool complete_access)
+{
+    uint8_t *ptr;
+    uint32_t seg_size, max_data_size;
+    bool toggle;
+    bool last;
+    int ret;
+
+    ptr = (uint8_t *)buf;
+
+    if (size <= 4) {
+        return ec_coe_download_expedited(slave, datagram, index, subindex, ptr, size, complete_access);
+    } else {
+        max_data_size = slave->configured_rx_mailbox_size - EC_MBOX_HEADER_SIZE - EC_COE_DOWN_REQ_HEADER_SIZE;
+        if (size <= max_data_size) {
+            return ec_coe_download_common(slave, datagram, index, subindex, ptr, size, complete_access);
+        } else {
+            ret = ec_coe_download_common(slave, datagram, index, subindex, ptr, size, complete_access);
+            if (ret < 0) {
+                return ret;
+            }
+
+            size -= max_data_size;
+            ptr += max_data_size;
+            toggle = false;
+            last = false;
+            max_data_size += 7;
+
+            while (1) {
+                seg_size = MIN(size, max_data_size);
+                if (seg_size < max_data_size) {
+                    last = true;
+                }
+                ret = ec_coe_download_segment(slave, datagram, ptr, seg_size, toggle, last);
+                if (ret < 0) {
+                    return ret;
+                }
+                ptr += seg_size;
+                size -= seg_size;
+                toggle ^= 1;
+                if (size == 0) {
+                    return 0;
+                }
+            }
+        }
+    }
+}
+
+int ec_coe_upload(ec_slave_t *slave,
+                  ec_datagram_t *datagram,
+                  uint16_t index,
+                  uint8_t subindex,
+                  const void *buf,
+                  uint32_t maxsize,
+                  uint32_t *size,
+                  bool complete_access)
+{
+    uint8_t *data;
+    uint8_t *ptr;
+    uint8_t mbox_proto;
+    uint32_t recv_size;
+    uint16_t rec_index;
+    uint8_t rec_subindex;
+    uint32_t data_size, total_size, offset;
+    bool expedited, size_specified;
+    ec_coe_upload_common_header_t *upload_common;
+    ec_coe_upload_segment_header_t *upload_seg;
+    bool toggle;
+    bool last;
+    int ret;
+
+    ptr = (uint8_t *)buf;
+
+    data = ec_mailbox_fill_send(slave, datagram, EC_MBOX_TYPE_COE, EC_COE_UP_REQ_HEADER_SIZE);
+
+    upload_common = (ec_coe_upload_common_header_t *)data;
+    upload_common->coe_header.number = 0;
+    upload_common->coe_header.reserved = 0;
+    upload_common->coe_header.service = EC_COE_SERVICE_SDO_REQUEST;
+    upload_common->sdo_header.size_indicator = 0;
+    upload_common->sdo_header.transfertype = 0;
+    upload_common->sdo_header.data_set_size = 0;
+    upload_common->sdo_header.complete_access = complete_access ? 1 : 0;
+    upload_common->sdo_header.command = EC_COE_REQUEST_UPLOAD;
+
+    upload_common->index = index;
+    upload_common->subindex = complete_access ? 0x00 : subindex;
+
+    memset(upload_common->data, 0x00, 4);
+    ret = ec_mailbox_send(slave, datagram);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = ec_mailbox_receive(slave, datagram, &mbox_proto, &recv_size);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (mbox_proto != EC_MBOX_TYPE_COE) {
+        return -EC_ERR_COE_TYPE;
+    }
+
+    if (recv_size < 6) {
+        return -EC_ERR_COE_SIZE;
+    }
+
+    data = datagram->data + EC_MBOX_HEADER_SIZE;
+
+    if (EC_READ_U16(data) >> 12 == EC_COE_SERVICE_SDO_REQUEST &&
+        EC_READ_U8(data + 2) >> 5 == EC_COE_REQUEST_ABORT) {
+        EC_SLAVE_LOG_ERR("Slave %u SDO abort code: 0x%08x (%s)\n", slave->index, EC_READ_U32(data + 6), ec_sdo_abort_string(EC_READ_U32(data + 6)));
+        return -EC_ERR_COE_ABORT;
+    }
+
+    if (EC_READ_U16(data) >> 12 != EC_COE_SERVICE_SDO_RESPONSE ||
+        EC_READ_U8(data + 2) >> 5 != EC_COE_RESPONSE_UPLOAD) {
+        return -EC_ERR_COE_REQUEST;
+    }
+
+    rec_index = EC_READ_U16(data + 3);
+    rec_subindex = EC_READ_U8(data + 5);
+
+    if (rec_index != index || rec_subindex != (complete_access ? 0x00 : subindex)) {
+        return -EC_ERR_COE_REQUEST;
+    }
+
+    expedited = EC_READ_U8(data + 2) & 0x02;
+
+    if (expedited) {
+        size_specified = EC_READ_U8(data + 2) & 0x01;
+        if (size_specified) {
+            total_size = 4 - ((EC_READ_U8(data + 2) & 0x0C) >> 2);
+        } else {
+            total_size = 4;
+        }
+
+        if (recv_size < (total_size + 6)) {
+            return -EC_ERR_COE_SIZE;
+        }
+
+        ec_memcpy(ptr, data + 6, total_size);
+
+        if (maxsize < total_size) {
+            return -EC_ERR_COE_SIZE;
+        }
+
+        if (size) {
+            *size = total_size;
+        }
+        return 0;
+    } else { // normal or segment
+        if (recv_size < EC_COE_UP_REQ_HEADER_SIZE) {
+            return -EC_ERR_COE_SIZE;
+        }
+
+        data_size = recv_size - EC_COE_UP_REQ_HEADER_SIZE;
+        total_size = EC_READ_U32(data + 6);
+        offset = 0;
+
+        ec_memcpy(ptr, data + EC_COE_UP_REQ_HEADER_SIZE, data_size);
+
+        ptr += data_size;
+        offset += data_size;
+
+        toggle = false;
+
+        if (data_size < total_size) {
+            while (1) {
+                data = ec_mailbox_fill_send(slave, datagram, EC_MBOX_TYPE_COE, EC_COE_UP_REQ_HEADER_SIZE);
+
+                upload_seg = (ec_coe_upload_segment_header_t *)data;
+                upload_seg->coe_header.number = 0;
+                upload_seg->coe_header.reserved = 0;
+                upload_seg->coe_header.service = EC_COE_SERVICE_SDO_REQUEST;
+                upload_seg->sdo_header.more_follows = 0;
+                upload_seg->sdo_header.toggle = toggle ? 1 : 0;
+                upload_seg->sdo_header.command = EC_COE_REQUEST_SEGMENT_UPLOAD;
+                upload_seg->sdo_header.segdata_size = 0;
+                memset(data + EC_COE_DOWN_SEG_REQ_HEADER_SIZE, 0x00, 7);
+                ret = ec_mailbox_send(slave, datagram);
+                if (ret < 0) {
+                    return ret;
+                }
+
+                ret = ec_mailbox_receive(slave, datagram, &mbox_proto, &recv_size);
+                if (ret < 0) {
+                    return ret;
+                }
+
+                if (mbox_proto != EC_MBOX_TYPE_COE) {
+                    return -EC_ERR_COE_TYPE;
+                }
+
+                if (recv_size < EC_COE_UP_REQ_HEADER_SIZE) {
+                    return -EC_ERR_COE_SIZE;
+                }
+
+                data = datagram->data + EC_MBOX_HEADER_SIZE;
+
+                if (EC_READ_U16(data) >> 12 == EC_COE_SERVICE_SDO_REQUEST &&
+                    EC_READ_U8(data + 2) >> 5 == EC_COE_REQUEST_ABORT) {
+                    EC_SLAVE_LOG_ERR("Slave %u SDO abort code: 0x%08x (%s)\n", slave->index, EC_READ_U32(data + 6), ec_sdo_abort_string(EC_READ_U32(data + 6)));
+                    return -EC_ERR_COE_ABORT;
+                }
+
+                if (EC_READ_U16(data) >> 12 != EC_COE_SERVICE_SDO_RESPONSE ||
+                    EC_READ_U8(data + 2) >> 5 != EC_COE_RESPONSE_SEGMENT_UPLOAD) {
+                    return -EC_ERR_COE_REQUEST;
+                }
+
+                data_size = recv_size - EC_COE_UP_SEG_REQ_HEADER_SIZE;
+
+                if (recv_size == EC_COE_UP_REQ_HEADER_SIZE) {
+                    uint8_t seg_size = (EC_READ_U8(data + 2) & 0xE) >> 1;
+                    data_size -= seg_size;
+                }
+
+                if ((offset + data_size) > total_size) {
+                    return -EC_ERR_COE_SIZE;
+                }
+
+                ec_memcpy(ptr, data + EC_COE_UP_SEG_REQ_HEADER_SIZE, data_size);
+                ptr += data_size;
+                offset += data_size;
+                toggle ^= 1;
+
+                last = EC_READ_U8(data + 2) & 0x01;
+
+                if (last) {
+                    if (offset != total_size) {
+                        return -EC_ERR_COE_SIZE;
+                    }
+
+                    if (maxsize < total_size) {
+                        return -EC_ERR_COE_SIZE;
+                    }
+
+                    if (size) {
+                        *size = total_size;
+                    }
+                    return 0;
+                }
+            }
+        } else {
+            if (maxsize < total_size) {
+                return -EC_ERR_COE_SIZE;
+            }
+
+            if (size) {
+                *size = total_size;
+            }
+            return 0;
+        }
+    }
+}

+ 349 - 0
src/ec_common.c

@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+
+#define ALIGN_UP_DWORD(x) ((uint32_t)(uintptr_t)(x) & (sizeof(uint32_t) - 1))
+
+static inline void dword2array(char *addr, uint32_t w)
+{
+    addr[0] = w;
+    addr[1] = w >> 8;
+    addr[2] = w >> 16;
+    addr[3] = w >> 24;
+}
+
+EC_FAST_CODE_SECTION void *ec_memcpy(void *s1, const void *s2, size_t n)
+{
+    char *b1 = (char *)s1;
+    const char *b2 = (const char *)s2;
+    uint32_t *w1;
+    const uint32_t *w2;
+
+    if (ALIGN_UP_DWORD(b1) == ALIGN_UP_DWORD(b2)) {
+        while (ALIGN_UP_DWORD(b1) != 0 && n > 0) {
+            *b1++ = *b2++;
+            --n;
+        }
+
+        w1 = (uint32_t *)b1;
+        w2 = (const uint32_t *)b2;
+
+        while (n >= 4 * sizeof(uint32_t)) {
+            *w1++ = *w2++;
+            *w1++ = *w2++;
+            *w1++ = *w2++;
+            *w1++ = *w2++;
+            n -= 4 * sizeof(uint32_t);
+        }
+
+        while (n >= sizeof(uint32_t)) {
+            *w1++ = *w2++;
+            n -= sizeof(uint32_t);
+        }
+
+        b1 = (char *)w1;
+        b2 = (const char *)w2;
+
+        while (n--) {
+            *b1++ = *b2++;
+        }
+    } else {
+        while (n > 0 && ALIGN_UP_DWORD(b2) != 0) {
+            *b1++ = *b2++;
+            --n;
+        }
+
+        w2 = (const uint32_t *)b2;
+
+        while (n >= 4 * sizeof(uint32_t)) {
+            dword2array(b1, *w2++);
+            b1 += sizeof(uint32_t);
+            dword2array(b1, *w2++);
+            b1 += sizeof(uint32_t);
+            dword2array(b1, *w2++);
+            b1 += sizeof(uint32_t);
+            dword2array(b1, *w2++);
+            b1 += sizeof(uint32_t);
+            n -= 4 * sizeof(uint32_t);
+        }
+
+        while (n >= sizeof(uint32_t)) {
+            dword2array(b1, *w2++);
+            b1 += sizeof(uint32_t);
+            n -= sizeof(uint32_t);
+        }
+
+        b2 = (const char *)w2;
+
+        while (n--) {
+            *b1++ = *b2++;
+        }
+    }
+    return s1;
+}
+
+EC_FAST_CODE_SECTION void ec_memset(void *s, int c, size_t n)
+{
+    char *b = (char *)s;
+    uint32_t *w;
+
+    while (ALIGN_UP_DWORD(b) != 0 && n > 0) {
+        *b++ = (char)c;
+        --n;
+    }
+
+    w = (uint32_t *)b;
+    c = (c & 0xff) | ((c & 0xff) << 8) | ((c & 0xff) << 16) | ((c & 0xff) << 24);
+
+    while (n >= 4 * sizeof(uint32_t)) {
+        *w++ = c;
+        *w++ = c;
+        *w++ = c;
+        *w++ = c;
+        n -= 4 * sizeof(uint32_t);
+    }
+
+    while (n >= sizeof(uint32_t)) {
+        *w++ = c;
+        n -= sizeof(uint32_t);
+    }
+
+    b = (char *)w;
+
+    while (n--) {
+        *b++ = (char)c;
+    }
+}
+
+typedef struct {
+    uint32_t code;
+    const char *message;
+} ec_code_msg_t;
+
+const char *ec_state_string(uint8_t states, uint8_t multi)
+{
+    off_t off = 0;
+    unsigned int first = 1;
+    static char buffer[64];
+
+    memset(buffer, 0, sizeof(buffer));
+
+    if (!states) {
+        off += sprintf(buffer + off, "(unknown)");
+        return buffer;
+    }
+
+    if (multi) { // multiple slaves
+        if (states & EC_SLAVE_STATE_INIT) {
+            off += sprintf(buffer + off, "INIT");
+            first = 0;
+        }
+        if (states & EC_SLAVE_STATE_PREOP) {
+            if (!first)
+                off += sprintf(buffer + off, ", ");
+            off += sprintf(buffer + off, "PREOP");
+            first = 0;
+        }
+        if (states & EC_SLAVE_STATE_SAFEOP) {
+            if (!first)
+                off += sprintf(buffer + off, ", ");
+            off += sprintf(buffer + off, "SAFEOP");
+            first = 0;
+        }
+        if (states & EC_SLAVE_STATE_OP) {
+            if (!first)
+                off += sprintf(buffer + off, ", ");
+            off += sprintf(buffer + off, "OP");
+        }
+    } else { // single slave
+        if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_INIT) {
+            off += sprintf(buffer + off, "INIT");
+        } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_PREOP) {
+            off += sprintf(buffer + off, "PREOP");
+        } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_BOOT) {
+            off += sprintf(buffer + off, "BOOT");
+        } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_SAFEOP) {
+            off += sprintf(buffer + off, "SAFEOP");
+        } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_OP) {
+            off += sprintf(buffer + off, "OP");
+        } else {
+            off += sprintf(buffer + off, "(invalid)");
+        }
+        first = 0;
+    }
+
+    if (states & EC_SLAVE_STATE_ACK_ERR) {
+        if (!first)
+            off += sprintf(buffer + off, " + ");
+        off += sprintf(buffer + off, "ERROR");
+    }
+
+    return buffer;
+}
+
+const ec_code_msg_t ec_mbox_protocol_messages[] = {
+    { EC_MBXPROT_AOE, "AOE" },
+    { EC_MBXPROT_EOE, "EOE" },
+    { EC_MBXPROT_COE, "COE" },
+    { EC_MBXPROT_FOE, "FOE" },
+    { EC_MBXPROT_SOE, "SOE" },
+    { EC_MBXPROT_VOE, "VOE" },
+    { 0, "" }
+};
+
+const char *ec_mbox_protocol_string(uint8_t prot)
+{
+    static char buffer[64];
+
+    memset(buffer, 0, sizeof(buffer));
+
+    int first = 1;
+    for (uint32_t i = 0; ec_mbox_protocol_messages[i].code != 0; i++) {
+        if (prot & ec_mbox_protocol_messages[i].code) {
+            if (!first) {
+                strcat(buffer, "|");
+            }
+            strcat(buffer, ec_mbox_protocol_messages[i].message);
+            first = 0;
+        }
+    }
+    if (first) {
+        return "";
+    }
+    return buffer;
+}
+
+const ec_code_msg_t al_status_messages[] = {
+    { 0x0000, "No error" },
+    { 0x0001, "Unspecified error" },
+    { 0x0002, "No Memory" },
+    { 0x0011, "Invalid requested state change" },
+    { 0x0012, "Unknown requested state" },
+    { 0x0013, "Bootstrap not supported" },
+    { 0x0014, "No valid firmware" },
+    { 0x0015, "Invalid mailbox configuration" },
+    { 0x0016, "Invalid mailbox configuration" },
+    { 0x0017, "Invalid sync manager configuration" },
+    { 0x0018, "No valid inputs available" },
+    { 0x0019, "No valid outputs" },
+    { 0x001A, "Synchronization error" },
+    { 0x001B, "Sync manager watchdog" },
+    { 0x001C, "Invalid sync manager types" },
+    { 0x001D, "Invalid output configuration" },
+    { 0x001E, "Invalid input configuration" },
+    { 0x001F, "Invalid watchdog configuration" },
+    { 0x0020, "Slave needs cold start" },
+    { 0x0021, "Slave needs INIT" },
+    { 0x0022, "Slave needs PREOP" },
+    { 0x0023, "Slave needs SAFEOP" },
+    { 0x0024, "Invalid Input Mapping" },
+    { 0x0025, "Invalid Output Mapping" },
+    { 0x0026, "Inconsistent Settings" },
+    { 0x0027, "Freerun not supported" },
+    { 0x0028, "Synchronization not supported" },
+    { 0x0029, "Freerun needs 3 Buffer Mode" },
+    { 0x002A, "Background Watchdog" },
+    { 0x002B, "No Valid Inputs and Outputs" },
+    { 0x002C, "Fatal Sync Error" },
+    { 0x002D, "No Sync Error" },
+    { 0x0030, "Invalid DC SYNCH configuration" },
+    { 0x0031, "Invalid DC latch configuration" },
+    { 0x0032, "PLL error" },
+    { 0x0033, "DC Sync IO Error" },
+    { 0x0034, "DC Sync Timeout Error" },
+    { 0x0035, "DC Invalid Sync Cycle Time" },
+    { 0x0036, "DC Sync0 Cycle Time" },
+    { 0x0037, "DC Sync1 Cycle Time" },
+    { 0x0041, "MBX_AOE" },
+    { 0x0042, "MBX_EOE" },
+    { 0x0043, "MBX_COE" },
+    { 0x0044, "MBX_FOE" },
+    { 0x0045, "MBX_SOE" },
+    { 0x004F, "MBX_VOE" },
+    { 0x0050, "EEPROM No Access" },
+    { 0x0051, "EEPROM Error" },
+    { 0x0060, "Slave Restarted Locally" },
+    { 0xffff }
+};
+
+const char *ec_alstatus_string(uint16_t errorcode)
+{
+    for (uint32_t i = 0; al_status_messages[i].code != 0xffff; i++) {
+        if (al_status_messages[i].code == errorcode) {
+            return al_status_messages[i].message;
+        }
+    }
+    return "Unknown errorcode";
+}
+
+const ec_code_msg_t mbox_error_messages[] = {
+    { 0x00000001, "MBXERR_SYNTAX" },
+    { 0x00000002, "MBXERR_UNSUPPORTEDPROTOCOL" },
+    { 0x00000003, "MBXERR_INVAILDCHANNEL" },
+    { 0x00000004, "MBXERR_SERVICENOTSUPPORTED" },
+    { 0x00000005, "MBXERR_INVALIDHEADER" },
+    { 0x00000006, "MBXERR_SIZETOOSHORT" },
+    { 0x00000007, "MBXERR_NOMOREMEMORY" },
+    { 0x00000008, "MBXERR_INVALIDSIZE" },
+    { 0xffffffff }
+};
+
+const char *ec_mbox_error_string(uint16_t errorcode)
+{
+    for (uint32_t i = 0; mbox_error_messages[i].code != 0xffffffff; i++) {
+        if (mbox_error_messages[i].code == errorcode) {
+            return mbox_error_messages[i].message;
+        }
+    }
+    return "Unknown errorcode";
+}
+
+const ec_code_msg_t sdo_abort_messages[] = {
+    { 0x05030000, "Toggle bit not changed" },
+    { 0x05040000, "SDO protocol timeout" },
+    { 0x05040001, "Client/Server command specifier not valid or unknown" },
+    { 0x05040005, "Out of memory" },
+    { 0x06010000, "Unsupported access to an object" },
+    { 0x06010001, "Attempt to read a write-only object" },
+    { 0x06010002, "Attempt to write a read-only object" },
+    { 0x06020000, "This object does not exist in the object directory" },
+    { 0x06040041, "The object cannot be mapped into the PDO" },
+    { 0x06040042, "The number and length of the objects to be mapped would"
+                  " exceed the PDO length" },
+    { 0x06040043, "General parameter incompatibility reason" },
+    { 0x06040047, "Gerneral internal incompatibility in device" },
+    { 0x06060000, "Access failure due to a hardware error" },
+    { 0x06070010, "Data type does not match, length of service parameter does"
+                  " not match" },
+    { 0x06070012, "Data type does not match, length of service parameter too"
+                  " high" },
+    { 0x06070013, "Data type does not match, length of service parameter too"
+                  " low" },
+    { 0x06090011, "Subindex does not exist" },
+    { 0x06090030, "Value range of parameter exceeded" },
+    { 0x06090031, "Value of parameter written too high" },
+    { 0x06090032, "Value of parameter written too low" },
+    { 0x06090036, "Maximum value is less than minimum value" },
+    { 0x08000000, "General error" },
+    { 0x08000020, "Data cannot be transferred or stored to the application" },
+    { 0x08000021, "Data cannot be transferred or stored to the application"
+                  " because of local control" },
+    { 0x08000022, "Data cannot be transferred or stored to the application"
+                  " because of the present device state" },
+    { 0x08000023, "Object dictionary dynamic generation fails or no object"
+                  " dictionary is present" },
+    {}
+};
+
+const char *ec_sdo_abort_string(uint32_t errorcode)
+{
+    for (uint32_t i = 0; sdo_abort_messages[i].code != 0; i++) {
+        if (sdo_abort_messages[i].code == errorcode) {
+            return sdo_abort_messages[i].message;
+        }
+    }
+    return "Unknown errorcode";
+}

+ 195 - 0
src/ec_datagram.c

@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+
+static const char *type_strings[] = {
+    "?",
+    "APRD",
+    "APWR",
+    "APRW",
+    "FPRD",
+    "FPWR",
+    "FPRW",
+    "BRD",
+    "BWR",
+    "BRW",
+    "LRD",
+    "LWR",
+    "LRW",
+    "ARMW",
+    "FRMW"
+};
+
+void ec_datagram_init(ec_datagram_t *datagram, size_t mem_size)
+{
+    uint8_t *data = NULL;
+
+    data = ec_osal_malloc(mem_size);
+    if (!data) {
+        EC_LOG_ERR("Failed to allocate memory for datagram data\n");
+        return;
+    }
+
+    ec_dlist_init(&datagram->queue);
+    datagram->netdev_idx = EC_NETDEV_MAIN;
+    datagram->type = EC_DATAGRAM_NONE;
+    memset(datagram->address, 0x00, EC_ADDR_LEN);
+    datagram->data = data;
+    datagram->mem_size = mem_size;
+    datagram->data_size = 0;
+    datagram->index = 0x00;
+    datagram->working_counter = 0x0000;
+    datagram->state = EC_DATAGRAM_INIT;
+    datagram->jiffies_sent = 0;
+    datagram->jiffies_received = 0;
+    memset(datagram->name, 0x00, EC_DATAGRAM_NAME_SIZE);
+
+    datagram->waiter = 0;
+    datagram->wait = ec_osal_sem_create(1, 0);
+    if (!datagram->wait) {
+        EC_LOG_ERR("Failed to create semaphore for datagram\n");
+        ec_osal_free(data);
+        datagram->data = NULL;
+        return;
+    }
+}
+
+void ec_datagram_init_static(ec_datagram_t *datagram, uint8_t *data, size_t mem_size)
+{
+    ec_dlist_init(&datagram->queue);
+    datagram->netdev_idx = EC_NETDEV_MAIN;
+    datagram->type = EC_DATAGRAM_NONE;
+    datagram->static_alloc = true;
+    memset(datagram->address, 0x00, EC_ADDR_LEN);
+    datagram->data = data;
+    datagram->mem_size = mem_size;
+    datagram->data_size = 0;
+    datagram->index = 0x00;
+    datagram->working_counter = 0x0000;
+    datagram->state = EC_DATAGRAM_INIT;
+    datagram->jiffies_sent = 0;
+    datagram->jiffies_received = 0;
+    memset(datagram->name, 0x00, EC_DATAGRAM_NAME_SIZE);
+}
+
+void ec_datagram_clear(ec_datagram_t *datagram)
+{
+    ec_datagram_unqueue(datagram);
+
+    if (datagram->data && !datagram->static_alloc) {
+        ec_osal_free(datagram->data);
+        datagram->data = NULL;
+
+        if (datagram->wait) {
+            ec_osal_sem_delete(datagram->wait);
+            datagram->wait = NULL;
+        }
+    }
+}
+
+void ec_datagram_unqueue(ec_datagram_t *datagram)
+{
+    if (!ec_dlist_isempty(&datagram->queue)) {
+        ec_dlist_del_init(&datagram->queue);
+    }
+}
+
+EC_FAST_CODE_SECTION void ec_datagram_zero(ec_datagram_t *datagram)
+{
+    memset(datagram->data, 0x00, datagram->data_size);
+}
+
+void ec_datagram_fill(ec_datagram_t *datagram,
+                      ec_datagram_type_t type,
+                      uint16_t adp,
+                      uint16_t ado,
+                      uint16_t size)
+{
+    datagram->index = 0;
+    datagram->working_counter = 0;
+    datagram->state = EC_DATAGRAM_INIT;
+
+    datagram->type = type;
+    EC_WRITE_U16(datagram->address, adp);
+    EC_WRITE_U16(datagram->address + 2, ado);
+    datagram->data_size = size;
+    datagram->waiter = 0;
+}
+
+void ec_datagram_aprd(ec_datagram_t *datagram, uint16_t autoinc_address, uint16_t mem_address, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_APRD, autoinc_address, mem_address, data_size);
+}
+
+void ec_datagram_apwr(ec_datagram_t *datagram, uint16_t autoinc_address, uint16_t mem_address, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_APWR, autoinc_address, mem_address, data_size);
+}
+
+void ec_datagram_aprw(ec_datagram_t *datagram, uint16_t autoinc_address, uint16_t mem_address, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_APRW, autoinc_address, mem_address, data_size);
+}
+
+void ec_datagram_armw(ec_datagram_t *datagram, uint16_t autoinc_address, uint16_t mem_address, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_ARMW, autoinc_address, mem_address, data_size);
+}
+
+void ec_datagram_fprd(ec_datagram_t *datagram, uint16_t configured_address, uint16_t mem_address, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_FPRD, configured_address, mem_address, data_size);
+}
+
+void ec_datagram_fpwr(ec_datagram_t *datagram, uint16_t configured_address, uint16_t mem_address, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_FPWR, configured_address, mem_address, data_size);
+}
+
+void ec_datagram_fprw(ec_datagram_t *datagram, uint16_t configured_address, uint16_t mem_address, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_FPRW, configured_address, mem_address, data_size);
+}
+
+void ec_datagram_frmw(ec_datagram_t *datagram, uint16_t configured_address, uint16_t mem_address, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_FRMW, configured_address, mem_address, data_size);
+}
+
+void ec_datagram_brd(ec_datagram_t *datagram, uint16_t mem_address, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_BRD, 0x0000, mem_address, data_size);
+}
+
+void ec_datagram_bwr(ec_datagram_t *datagram, uint16_t mem_address, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_BWR, 0x0000, mem_address, data_size);
+}
+
+void ec_datagram_brw(ec_datagram_t *datagram, uint16_t mem_address, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_BRW, 0x0000, mem_address, data_size);
+}
+
+void ec_datagram_lrd(ec_datagram_t *datagram, uint32_t offset, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_LRD, offset & 0xffff, offset >> 16, data_size);
+}
+
+void ec_datagram_lwr(ec_datagram_t *datagram, uint32_t offset, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_LWR, offset & 0xffff, offset >> 16, data_size);
+}
+
+void ec_datagram_lrw(ec_datagram_t *datagram, uint32_t offset, size_t data_size)
+{
+    ec_datagram_fill(datagram, EC_DATAGRAM_LRW, offset & 0xffff, offset >> 16, data_size);
+}
+
+const char *ec_datagram_type_string(const ec_datagram_t *datagram)
+{
+    return type_strings[datagram->type];
+}

+ 91 - 0
src/ec_mailbox.c

@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+
+#define EC_MBOX_READ_TIMEOUT (100 * 1000)
+
+uint8_t *ec_mailbox_fill_send(ec_slave_t *slave, ec_datagram_t *datagram, uint8_t type, uint16_t size)
+{
+    EC_ASSERT_MSG((EC_MBOX_HEADER_SIZE + size) <= slave->configured_rx_mailbox_size, "RX Mailbox size overflow");
+
+    EC_WRITE_U16(datagram->data, size);                       // mailbox service data length
+    EC_WRITE_U16(datagram->data + 2, slave->station_address); // station address
+    EC_WRITE_U8(datagram->data + 4, 0x00);                    // channel & priority
+    EC_WRITE_U8(datagram->data + 5, type);                    // protocol type
+
+    return (datagram->data + EC_MBOX_HEADER_SIZE);
+}
+
+int ec_mailbox_send(ec_slave_t *slave, ec_datagram_t *datagram)
+{
+    ec_datagram_fpwr(datagram, slave->station_address, slave->configured_rx_mailbox_offset, slave->configured_rx_mailbox_size);
+    datagram->netdev_idx = slave->netdev_idx;
+    return ec_master_queue_ext_datagram(slave->master, datagram, true, true);
+}
+
+int ec_mailbox_read_status(ec_slave_t *slave, ec_datagram_t *datagram)
+{
+    uint32_t start_time;
+    int ret;
+
+    start_time = jiffies;
+
+check_again:
+    ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->SYNCM[EC_SM_INDEX_MBX_READ]), 8);
+    ec_datagram_zero(datagram);
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(slave->master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (!(EC_READ_U8(datagram->data + 5) & ESC_SYNCM_STATUS_MBX_MODE_MASK)) {
+        if ((jiffies - start_time) > EC_MBOX_READ_TIMEOUT) {
+            return -EC_ERR_TIMEOUT;
+        }
+        goto check_again;
+    }
+
+    return 0;
+}
+
+int ec_mailbox_receive(ec_slave_t *slave, ec_datagram_t *datagram, uint8_t *type, uint32_t *size)
+{
+    uint16_t code;
+    uint32_t tmp_size;
+    uint8_t tmp_type;
+    int ret;
+
+    ret = ec_mailbox_read_status(slave, datagram);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ec_datagram_fprd(datagram, slave->station_address, slave->configured_tx_mailbox_offset, slave->configured_tx_mailbox_size);
+    ec_datagram_zero(datagram);
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(slave->master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    tmp_size = EC_READ_U16(datagram->data);
+    tmp_type = EC_READ_U8(datagram->data + 5) & 0x0F;
+
+    EC_ASSERT_MSG(tmp_size <= slave->configured_tx_mailbox_size, "TX Mailbox size overflow");
+
+    if (tmp_type == 0x00) {
+        code = EC_READ_U16(datagram->data + 8);
+
+        EC_SLAVE_LOG_ERR("Slave %u mailbox errorcode: 0x%04x (%s)\n", slave->index, code, ec_mbox_error_string(code));
+        return -EC_ERR_MBOX;
+    }
+
+    *type = tmp_type;
+    *size = tmp_size;
+
+    return 0;
+}

+ 783 - 0
src/ec_master.c

@@ -0,0 +1,783 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+
+#define EC_DATAGRAM_TIMEOUT_MS (50 * 1000)
+
+static void ec_master_cyclic_process(void *arg);
+
+/** List of intervals for statistics [s].
+ */
+const unsigned int rate_intervals[] = {
+    1, 10, 60
+};
+
+EC_FAST_CODE_SECTION static void ec_master_update_netdev_stats(ec_master_t *master)
+{
+    ec_netdev_stats_t *s = &master->netdev_stats;
+    int32_t tx_frame_rate, rx_frame_rate, tx_byte_rate, rx_byte_rate, loss_rate;
+    uint64_t loss;
+    unsigned int i, netdev_idx;
+
+    // frame statistics
+    if ((jiffies - s->last_jiffies) < 1000000) {
+        return;
+    }
+
+    tx_frame_rate = (s->tx_count - s->last_tx_count) * 1000;
+    rx_frame_rate = (s->rx_count - s->last_rx_count) * 1000;
+    tx_byte_rate = s->tx_bytes - s->last_tx_bytes;
+    rx_byte_rate = s->rx_bytes - s->last_rx_bytes;
+    loss = s->tx_count - s->rx_count;
+    loss_rate = (loss - s->last_loss) * 1000;
+
+    /* Low-pass filter:
+     *      Y_n = y_(n - 1) + T / tau * (x - y_(n - 1))   | T = 1
+     *   -> Y_n += (x - y_(n - 1)) / tau
+     */
+    for (i = 0; i < EC_RATE_COUNT; i++) {
+        int32_t n = rate_intervals[i];
+        s->tx_frame_rates[i] += (tx_frame_rate - s->tx_frame_rates[i]) / n;
+        s->rx_frame_rates[i] += (rx_frame_rate - s->rx_frame_rates[i]) / n;
+        s->tx_byte_rates[i] += (tx_byte_rate - s->tx_byte_rates[i]) / n;
+        s->rx_byte_rates[i] += (rx_byte_rate - s->rx_byte_rates[i]) / n;
+        s->loss_rates[i] += (loss_rate - s->loss_rates[i]) / n;
+    }
+
+    s->last_tx_count = s->tx_count;
+    s->last_rx_count = s->rx_count;
+    s->last_tx_bytes = s->tx_bytes;
+    s->last_rx_bytes = s->rx_bytes;
+    s->last_loss = loss;
+
+    for (netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS;
+         netdev_idx++) {
+        ec_netdev_update_stats(master->netdev[netdev_idx]);
+    }
+
+    s->last_jiffies = jiffies;
+}
+
+EC_FAST_CODE_SECTION static void ec_master_clear_netdev_stats(ec_master_t *master)
+{
+    unsigned int i;
+
+    // zero frame statistics
+    master->netdev_stats.tx_count = 0;
+    master->netdev_stats.last_tx_count = 0;
+    master->netdev_stats.rx_count = 0;
+    master->netdev_stats.last_rx_count = 0;
+    master->netdev_stats.tx_bytes = 0;
+    master->netdev_stats.last_tx_bytes = 0;
+    master->netdev_stats.rx_bytes = 0;
+    master->netdev_stats.last_rx_bytes = 0;
+    master->netdev_stats.last_loss = 0;
+
+    for (i = 0; i < EC_RATE_COUNT; i++) {
+        master->netdev_stats.tx_frame_rates[i] = 0;
+        master->netdev_stats.rx_frame_rates[i] = 0;
+        master->netdev_stats.tx_byte_rates[i] = 0;
+        master->netdev_stats.rx_byte_rates[i] = 0;
+        master->netdev_stats.loss_rates[i] = 0;
+    }
+
+    master->netdev_stats.last_jiffies = 0;
+}
+
+EC_FAST_CODE_SECTION static void ec_master_queue_datagram(ec_master_t *master, ec_datagram_t *datagram)
+{
+    ec_datagram_t *queued_datagram;
+
+    ec_dlist_for_each_entry(queued_datagram, &master->datagram_queue, queue)
+    {
+        if (queued_datagram == datagram) {
+            datagram->state = EC_DATAGRAM_QUEUED;
+            return;
+        }
+    }
+
+    ec_dlist_add_tail(&master->datagram_queue, &datagram->queue);
+    datagram->state = EC_DATAGRAM_QUEUED;
+}
+
+EC_FAST_CODE_SECTION static void ec_master_unqueue_datagram(ec_master_t *master, ec_datagram_t *datagram)
+{
+    ec_dlist_del_init(&datagram->queue);
+
+    if (datagram->waiter) {
+        datagram->waiter = false;
+        ec_osal_sem_give(datagram->wait);
+    }
+}
+
+EC_FAST_CODE_SECTION static void ec_master_send_datagrams(ec_master_t *master, ec_netdev_index_t netdev_idx)
+{
+    ec_datagram_t *datagram, *next;
+    size_t datagram_size;
+    uint8_t *frame_data, *cur_data = NULL;
+    void *follows_word;
+#ifdef CONFIG_EC_HAVE_CYCLES
+    uint64_t cycles_start, cycles_end;
+#endif
+    uint64_t jiffies_sent;
+    unsigned int frame_count, more_datagrams_waiting;
+    ec_dlist_t sent_datagrams;
+
+#ifdef CONFIG_EC_HAVE_CYCLES
+    cycles_start = get_cycles();
+#endif
+    frame_count = 0;
+    ec_dlist_init(&sent_datagrams);
+
+    do {
+        frame_data = NULL;
+        follows_word = NULL;
+        more_datagrams_waiting = 0;
+
+        // fill current frame with datagrams
+        ec_dlist_for_each_entry(datagram, &master->datagram_queue, queue)
+        {
+            if (datagram->state != EC_DATAGRAM_QUEUED ||
+                datagram->netdev_idx != netdev_idx) {
+                continue;
+            }
+
+            if (!frame_data) {
+                // fetch pointer to transmit socket buffer
+                frame_data =
+                    ec_netdev_get_txbuf(master->netdev[netdev_idx]);
+                cur_data = frame_data + EC_FRAME_HEADER_SIZE;
+            }
+
+            // does the current datagram fit in the frame?
+            datagram_size = EC_DATAGRAM_HEADER_SIZE + datagram->data_size + EC_DATAGRAM_WC_SIZE;
+            if (cur_data - frame_data + datagram_size > ETH_DATA_LEN) {
+                more_datagrams_waiting = 1;
+                break;
+            }
+
+            ec_dlist_add_tail(&sent_datagrams, &datagram->sent);
+            datagram->index = master->datagram_index++;
+
+            EC_LOG_DBG("Adding datagram 0x%02X\n", datagram->index);
+
+            // set "datagram following" flag in previous datagram
+            if (follows_word) {
+                EC_WRITE_U16(follows_word,
+                             EC_READ_U16(follows_word) | 0x8000);
+            }
+
+            // EtherCAT datagram header
+            EC_WRITE_U8(cur_data, datagram->type);
+            EC_WRITE_U8(cur_data + 1, datagram->index);
+            ec_memcpy(cur_data + 2, datagram->address, EC_ADDR_LEN);
+            EC_WRITE_U16(cur_data + 6, datagram->data_size & 0x7FF);
+            EC_WRITE_U16(cur_data + 8, 0x0000); // IRQ
+            follows_word = cur_data + 6;
+            cur_data += EC_DATAGRAM_HEADER_SIZE;
+
+            // EtherCAT datagram data
+            ec_memcpy(cur_data, datagram->data, datagram->data_size);
+            cur_data += datagram->data_size;
+
+            // EtherCAT datagram footer
+            EC_WRITE_U16(cur_data, 0x0000); // reset working counter
+            cur_data += EC_DATAGRAM_WC_SIZE;
+        }
+
+        if (ec_dlist_isempty(&sent_datagrams)) {
+            EC_LOG_DBG("nothing to send.\n");
+            break;
+        }
+
+        // EtherCAT frame header
+        EC_WRITE_U16(frame_data, ((cur_data - frame_data - EC_FRAME_HEADER_SIZE) & 0x7FF) | 0x1000);
+
+        // pad frame
+        while (cur_data - frame_data < ETH_ZLEN - ETH_HLEN)
+            EC_WRITE_U8(cur_data++, 0x00);
+
+        EC_LOG_DBG("frame size: %u\n", cur_data - frame_data);
+
+        // send frame
+        if (ec_netdev_send(master->netdev[netdev_idx], cur_data - frame_data) < 0) {
+            EC_LOG_ERR("ec_netdev_send() failed.\n");
+        }
+
+        jiffies_sent = jiffies;
+
+        // set datagram states and sending timestamps
+        ec_dlist_for_each_entry_safe(datagram, next, &sent_datagrams, sent)
+        {
+            datagram->state = EC_DATAGRAM_SENT;
+            datagram->jiffies_sent = jiffies_sent;
+            ec_dlist_del_init(&datagram->sent); // empty list of sent datagrams
+        }
+
+        frame_count++;
+    } while (more_datagrams_waiting);
+
+#ifdef CONFIG_EC_HAVE_CYCLES
+    cycles_end = jiffies;
+    EC_LOG_DBG("Sent %u frames in %uus.\n", frame_count, (unsigned int)(cycles_end - cycles_start));
+#endif
+}
+
+EC_FAST_CODE_SECTION void ec_master_receive_datagrams(ec_master_t *master,
+                                                      ec_netdev_index_t netdev_idx,
+                                                      const uint8_t *frame_data,
+                                                      size_t size)
+{
+    size_t frame_size, data_size;
+    uint8_t datagram_type, datagram_index;
+    unsigned int cmd_follows, matched;
+    const uint8_t *cur_data;
+    ec_datagram_t *datagram;
+
+    if (size < EC_FRAME_HEADER_SIZE) {
+        EC_LOG_ERR("Corrupted frame received on %s (size %u < %u byte)\n",
+                   master->netdev[netdev_idx]->name, size, EC_FRAME_HEADER_SIZE);
+        master->stats.corrupted++;
+        return;
+    }
+
+    cur_data = frame_data;
+
+    // check length of entire frame
+    frame_size = EC_READ_U16(cur_data) & 0x07FF;
+    cur_data += EC_FRAME_HEADER_SIZE;
+
+    if (frame_size > size) {
+        EC_LOG_ERR("Corrupted frame received on %s (invalid frame size %u > received size %u)\n",
+                   master->netdev[netdev_idx]->name, frame_size, size);
+        master->stats.corrupted++;
+        return;
+    }
+
+    cmd_follows = 1;
+    while (cmd_follows) {
+        // process datagram header
+        datagram_type = EC_READ_U8(cur_data);
+        datagram_index = EC_READ_U8(cur_data + 1);
+        data_size = EC_READ_U16(cur_data + 6) & 0x07FF;
+        cmd_follows = EC_READ_U16(cur_data + 6) & 0x8000;
+        cur_data += EC_DATAGRAM_HEADER_SIZE;
+
+        if ((cur_data - frame_data + data_size + EC_DATAGRAM_WC_SIZE) > size) {
+            EC_LOG_ERR("Corrupted frame received on %s (invalid data size %u)\n",
+                       master->netdev[netdev_idx]->name, data_size);
+            master->stats.corrupted++;
+            return;
+        }
+
+        // search for matching datagram in the queue
+        matched = 0;
+        ec_dlist_for_each_entry(datagram, &master->datagram_queue, queue)
+        {
+            if ((datagram->index == datagram_index) &&
+                (datagram->state == EC_DATAGRAM_SENT) &&
+                (datagram->type == datagram_type) &&
+                (datagram->data_size == data_size)) {
+                matched = 1;
+                break;
+            }
+        }
+
+        // no matching datagram was found
+        if (!matched) {
+            EC_LOG_DBG("No matching datagram found for index 0x%02X, type 0x%02X, size %u on %s\n",
+                       datagram_index, datagram_type, data_size,
+                       master->netdev[netdev_idx]->name);
+            master->stats.unmatched++;
+
+            cur_data += data_size + EC_DATAGRAM_WC_SIZE;
+            continue;
+        }
+
+        if (datagram->type != EC_DATAGRAM_APWR &&
+            datagram->type != EC_DATAGRAM_FPWR &&
+            datagram->type != EC_DATAGRAM_BWR &&
+            datagram->type != EC_DATAGRAM_LWR) {
+            // copy received data into the datagram memory,
+            // if something has been read
+            ec_memcpy(datagram->data, cur_data, data_size);
+        }
+        cur_data += data_size;
+
+        // set the datagram's working counter
+        datagram->working_counter = EC_READ_U16(cur_data);
+        cur_data += EC_DATAGRAM_WC_SIZE;
+
+        // dequeue the received datagram
+        datagram->state = EC_DATAGRAM_RECEIVED;
+
+        datagram->jiffies_received = master->netdev[EC_NETDEV_MAIN]->jiffies_poll;
+        ec_master_unqueue_datagram(master, datagram);
+    }
+}
+
+EC_FAST_CODE_SECTION static void ec_master_send(ec_master_t *master)
+{
+    ec_datagram_t *datagram, *n;
+    ec_netdev_index_t netdev_idx;
+    uintptr_t flags;
+
+    flags = ec_osal_enter_critical_section();
+    ec_dlist_for_each_entry_safe(datagram, n, &master->ext_datagram_queue, ext_queue)
+    {
+        ec_dlist_del_init(&datagram->ext_queue);
+        ec_master_queue_datagram(master, datagram);
+    }
+    ec_osal_leave_critical_section(flags);
+
+    for (netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS; netdev_idx++) {
+        if (!master->netdev[netdev_idx]->link_state) {
+            // link is down, no datagram can be sent
+            ec_dlist_for_each_entry_safe(datagram, n, &master->datagram_queue, queue)
+            {
+                if (datagram->netdev_idx == netdev_idx) {
+                    datagram->state = EC_DATAGRAM_ERROR;
+                    ec_master_unqueue_datagram(master, datagram);
+                }
+            }
+
+            if (!master->netdev[netdev_idx]) {
+                continue;
+            }
+
+            // clear frame statistics
+            ec_netdev_clear_stats(master->netdev[netdev_idx]);
+            continue;
+        }
+
+        // send frames
+        ec_master_send_datagrams(master, netdev_idx);
+    }
+}
+
+EC_FAST_CODE_SECTION static void ec_master_receive(ec_master_t *master)
+{
+    ec_netdev_index_t netdev_idx;
+    ec_datagram_t *datagram, *next;
+
+    for (netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS; netdev_idx++) {
+        ec_netdev_poll(master->netdev[netdev_idx]);
+    }
+    ec_master_update_netdev_stats(master);
+
+    // dequeue all datagrams that timed out
+    ec_dlist_for_each_entry_safe(datagram, next, &master->datagram_queue, queue)
+    {
+        if (datagram->state != EC_DATAGRAM_SENT)
+            continue;
+
+        if ((jiffies - datagram->jiffies_sent) > EC_DATAGRAM_TIMEOUT_MS) {
+            datagram->state = EC_DATAGRAM_TIMED_OUT;
+            ec_master_unqueue_datagram(master, datagram);
+            master->stats.timeouts++;
+        }
+    }
+}
+
+static void ec_netdev_linkpoll_timer(void *argument)
+{
+    ec_master_t *master = (ec_master_t *)argument;
+    unsigned int netdev_idx;
+
+    for (netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS; netdev_idx++) {
+        master->netdev[netdev_idx]->link_state = ec_netdev_get_link_state(master->netdev[netdev_idx]);
+    }
+}
+
+static void ec_master_nonperiod_thread(void *argument)
+{
+    ec_master_t *master = (ec_master_t *)argument;
+
+    while (1) {
+        ec_osal_sem_take(master->nonperiod_sem, CONFIG_EC_NONPERIOD_INTERVAL_MS);
+        ec_master_receive(master);
+        ec_master_send(master);
+    }
+}
+
+static void ec_master_idle_thread(void *argument)
+{
+    ec_master_t *master = (ec_master_t *)argument;
+
+    while (1) {
+        ec_osal_mutex_take(master->scan_lock);
+        ec_slaves_scanning(master);
+        ec_osal_mutex_give(master->scan_lock);
+        ec_osal_msleep(CONFIG_EC_IDLE_INTERVAL_MS);
+    }
+}
+
+static int ec_master_enter_idle(ec_master_t *master)
+{
+    unsigned int netdev_idx;
+    uintptr_t flags;
+
+    flags = ec_osal_enter_critical_section();
+
+    master->phase = EC_IDLE;
+
+    for (netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS; netdev_idx++) {
+        ec_netdev_low_level_enable_irq(master->netdev[netdev_idx], true);
+    }
+    ec_osal_leave_critical_section(flags);
+
+    ec_osal_thread_resume(master->nonperiod_thread);
+
+    return 0;
+}
+
+static void ec_master_exit_idle(ec_master_t *master)
+{
+    unsigned int netdev_idx;
+    uintptr_t flags;
+
+    ec_osal_thread_suspend(master->nonperiod_thread);
+
+    flags = ec_osal_enter_critical_section();
+
+    for (netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS; netdev_idx++) {
+        ec_netdev_low_level_enable_irq(master->netdev[netdev_idx], false);
+    }
+
+    ec_osal_leave_critical_section(flags);
+}
+
+int ec_master_init(ec_master_t *master, uint8_t master_index)
+{
+    unsigned int netdev_idx;
+
+    memset(master, 0, sizeof(ec_master_t));
+    master->index = master_index;
+    master->datagram_index = 1; // start with index 1
+
+    ec_dlist_init(&master->datagram_queue);
+    ec_dlist_init(&master->ext_datagram_queue);
+    ec_dlist_init(&master->cyclic_datagram_queue);
+
+    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]) {
+            return -1;
+        }
+        master->netdev[netdev_idx]->master = master;
+    }
+
+    ec_master_clear_netdev_stats(master);
+
+    ec_datagram_init(&master->main_datagram, EC_MAX_DATA_SIZE);
+    ec_datagram_init(&master->dc_ref_sync_datagram, 8);
+    ec_datagram_init(&master->dc_all_sync_datagram, 8);
+
+    master->scan_lock = ec_osal_mutex_create();
+    if (!master->scan_lock) {
+        return -1;
+    }
+
+    master->nonperiod_sem = ec_osal_sem_create(CONFIG_EC_NONPERIOD_WAITERS, 0);
+    if (!master->nonperiod_sem) {
+        return -1;
+    }
+
+    master->nonperiod_thread = ec_osal_thread_create("ec_nonperiod", CONFIG_EC_NONPERIOD_STACKSIZE, CONFIG_EC_NONPERIOD_PRIO, ec_master_nonperiod_thread, master);
+    if (!master->nonperiod_thread) {
+        return -1;
+    }
+
+    master->idle_thread = ec_osal_thread_create("ec_idle", CONFIG_EC_IDLE_STACKSIZE, CONFIG_EC_IDLE_PRIO, ec_master_idle_thread, master);
+    if (!master->idle_thread) {
+        return -1;
+    }
+
+    master->linkdetect_timer = ec_osal_timer_create("ec_linkdetect", 1000, ec_netdev_linkpoll_timer, master, true);
+    if (!master->linkdetect_timer) {
+        return -1;
+    }
+
+    ec_osal_timer_start(master->linkdetect_timer);
+
+    ec_master_enter_idle(master);
+
+    return 0;
+}
+
+void ec_master_deinit(ec_master_t *master)
+{
+}
+
+int ec_master_start(ec_master_t *master, uint32_t period_us)
+{
+    ec_slave_t *slave;
+    uint32_t bitlen;
+    bool used[2] = { false, false };
+    unsigned int netdev_idx;
+    uint8_t sm_idx;
+
+    if (master->active) {
+        return 0;
+    }
+
+    while (!master->scan_done) {
+        ec_osal_msleep(1);
+    }
+
+    ec_osal_mutex_take(master->scan_lock);
+
+    master->actual_working_counter = 0;
+    master->expected_working_counter = 0;
+    master->actual_pdo_size = 0;
+    master->phase = EC_OPERATION;
+
+    for (uint32_t slave_idx = 0; slave_idx < master->slave_count; slave_idx++) {
+        slave = &master->slaves[slave_idx];
+
+        EC_ASSERT_MSG(slave->config != NULL, "Slave %u has no configuration\n", slave_idx);
+
+        slave->logical_start_address = master->actual_pdo_size;
+        slave->data_size = 0;
+        for (uint8_t i = 0; i < slave->config->sync_count; i++) {
+            bitlen = 0;
+
+            sm_idx = slave->config->sync[i].index;
+            EC_ASSERT_MSG(sm_idx < slave->sm_count, "Slave %u: Invalid sync manager index %u\n",
+                          slave_idx, sm_idx);
+
+            slave->sm_info[sm_idx].pdo_assign.count = slave->config->sync[i].n_pdos;
+
+            for (uint32_t j = 0; j < slave->config->sync[i].n_pdos; j++) {
+                slave->sm_info[sm_idx].pdo_assign.entry[j] = slave->config->sync[i].pdos[j].index;
+
+                slave->sm_info[sm_idx].pdo_mapping[j].count = slave->config->sync[i].pdos[j].n_entries;
+
+                for (uint32_t k = 0; k < slave->config->sync[i].pdos[j].n_entries; k++) {
+                    uint32_t entry = (slave->config->sync[i].pdos[j].entries[k].index << 16) |
+                                     (slave->config->sync[i].pdos[j].entries[k].subindex & 0xFF) << 8 |
+                                     (slave->config->sync[i].pdos[j].entries[k].bit_length & 0xFF);
+                    slave->sm_info[sm_idx].pdo_mapping[j].entry[k] = entry;
+
+                    bitlen += slave->config->sync[i].pdos[j].entries[k].bit_length;
+                }
+            }
+
+            // update SM
+            slave->sm_info[sm_idx].length = (bitlen + 7) / 8;
+            slave->sm_info[sm_idx].enable = true;
+
+            // update FMMU
+            slave->sm_info[sm_idx].fmmu.data_size = (bitlen + 7) / 8;
+            slave->sm_info[sm_idx].fmmu.logical_start_address = master->actual_pdo_size;
+            slave->sm_info[sm_idx].fmmu.dir = slave->config->sync[i].dir;
+            slave->sm_info[sm_idx].fmmu_enable = true;
+            master->actual_pdo_size += (bitlen + 7) / 8;
+            slave->data_size += (bitlen + 7) / 8;
+
+            if (slave->config->sync[i].dir == EC_DIR_INPUT) {
+                used[EC_DIR_INPUT] = true;
+            }
+            if (slave->config->sync[i].dir == EC_DIR_OUTPUT) {
+                used[EC_DIR_OUTPUT] = true;
+            }
+        }
+
+        ec_cyclic_datagram_t *cyclic_datagram;
+
+        cyclic_datagram = (ec_cyclic_datagram_t *)ec_osal_malloc(sizeof(ec_cyclic_datagram_t));
+        if (!cyclic_datagram) {
+            return -1;
+        }
+        memset(cyclic_datagram, 0, sizeof(ec_cyclic_datagram_t));
+
+        for (netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS; netdev_idx++) {
+            ec_datagram_init_static(&cyclic_datagram->datagrams[netdev_idx], &master->pdo_buffer[netdev_idx][slave->logical_start_address], slave->data_size);
+            cyclic_datagram->datagrams[netdev_idx].netdev_idx = netdev_idx;
+
+            if (used[EC_DIR_INPUT] && used[EC_DIR_INPUT]) {
+                ec_datagram_lrw(&cyclic_datagram->datagrams[netdev_idx], slave->logical_start_address, slave->data_size);
+                cyclic_datagram->expected_working_counter = 3;
+            } else if (used[EC_DIR_INPUT]) {
+                ec_datagram_lrd(&cyclic_datagram->datagrams[netdev_idx], slave->logical_start_address, slave->data_size);
+                cyclic_datagram->expected_working_counter = 1;
+            } else if (used[EC_DIR_OUTPUT]) {
+                ec_datagram_lwr(&cyclic_datagram->datagrams[netdev_idx], slave->logical_start_address, slave->data_size);
+                cyclic_datagram->expected_working_counter = 1;
+            }
+            ec_datagram_zero(&cyclic_datagram->datagrams[netdev_idx]);
+        }
+        slave->expected_working_counter = cyclic_datagram->expected_working_counter;
+        master->expected_working_counter += slave->expected_working_counter;
+
+        EC_SLAVE_LOG_INFO("Slave %u: Logical address 0x%08x, %u byte, expected working counter %u\n",
+                          slave->index,
+                          slave->logical_start_address, slave->data_size,
+                          slave->expected_working_counter);
+
+        ec_dlist_add_tail(&master->cyclic_datagram_queue, &cyclic_datagram->queue);
+    }
+
+    ec_master_exit_idle(master);
+
+    ec_htimer_start(period_us, ec_master_cyclic_process, master);
+
+    for (uint32_t i = 0; i < master->slave_count; i++) {
+        master->slaves[i].requested_state = EC_SLAVE_STATE_OP;
+        master->slaves[i].alstatus_code = 0;
+    }
+    master->config_change = true;
+    master->active = true;
+
+    ec_osal_mutex_give(master->scan_lock);
+    return 0;
+}
+
+int ec_master_stop(ec_master_t *master)
+{
+    ec_cyclic_datagram_t *cyclic_datagram, *n;
+    unsigned int netdev_idx;
+
+    if (!master->active) {
+        return 0;
+    }
+
+    ec_osal_mutex_take(master->scan_lock);
+    ec_htimer_stop();
+
+    for (uint32_t i = 0; i < master->slave_count; i++) {
+        master->slaves[i].requested_state = EC_SLAVE_STATE_PREOP;
+        master->slaves[i].alstatus_code = 0;
+        master->slaves[i].force_update = true;
+    }
+
+    ec_dlist_for_each_entry_safe(cyclic_datagram, n, &master->cyclic_datagram_queue, queue)
+    {
+        for (netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS; netdev_idx++) {
+            ec_datagram_clear(&cyclic_datagram->datagrams[netdev_idx]);
+        }
+        ec_dlist_del_init(&cyclic_datagram->queue);
+        ec_osal_free(cyclic_datagram);
+    }
+
+    master->active = false;
+
+    ec_master_enter_idle(master);
+
+    ec_osal_mutex_give(master->scan_lock);
+
+    return 0;
+}
+
+int ec_master_queue_ext_datagram(ec_master_t *master, ec_datagram_t *datagram, bool wakep_poll, bool waiter)
+{
+    uintptr_t flags;
+    int ret;
+
+    flags = ec_osal_enter_critical_section();
+    ec_dlist_add_tail(&master->ext_datagram_queue, &datagram->ext_queue);
+    datagram->waiter = waiter;
+
+    if (wakep_poll && master->nonperiod_sem) {
+        ec_osal_sem_give(master->nonperiod_sem);
+    }
+    ec_osal_leave_critical_section(flags);
+
+    if (waiter) {
+        ret = ec_osal_sem_take(datagram->wait, EC_OSAL_WAITING_FOREVER);
+        if (ret < 0) {
+            return ret;
+        }
+
+        if (datagram->state == EC_DATAGRAM_RECEIVED) {
+            if (datagram->working_counter == 0) {
+                return -EC_ERR_WC;
+            }
+            return 0;
+        } else if (datagram->state == EC_DATAGRAM_TIMED_OUT) {
+            return -EC_ERR_TIMEOUT;
+        } else if (datagram->state == EC_DATAGRAM_ERROR) {
+            return -EC_ERR_IO;
+        } else {
+            return -EC_ERR_UNKNOWN;
+        }
+    }
+
+    return 0;
+}
+
+uint8_t *ec_master_get_slave_domain(ec_master_t *master, uint32_t slave_index)
+{
+    ec_slave_t *slave;
+
+    if (slave_index >= master->slave_count) {
+        return NULL;
+    }
+
+    slave = &master->slaves[slave_index];
+    if (slave->data_size == 0) {
+        return NULL;
+    }
+
+    return &master->pdo_buffer[EC_NETDEV_MAIN][slave->logical_start_address];
+}
+
+uint32_t ec_master_get_slave_domain_size(ec_master_t *master, uint32_t slave_index)
+{
+    ec_slave_t *slave;
+
+    if (slave_index >= master->slave_count) {
+        return 0;
+    }
+
+    slave = &master->slaves[slave_index];
+
+    return slave->data_size;
+}
+
+EC_FAST_CODE_SECTION static void ec_master_cyclic_process(void *arg)
+{
+    ec_master_t *master = (ec_master_t *)arg;
+    ec_cyclic_datagram_t *cyclic_datagram, *n;
+    unsigned int netdev_idx;
+
+    if (master->phase != EC_OPERATION) {
+        return;
+    }
+
+#ifdef CONFIG_EC_PERF_ENABLE
+    ec_perf_polling(&master->perf);
+#endif
+    ec_master_receive(master);
+
+    master->actual_working_counter = 0;
+    ec_dlist_for_each_entry_safe(cyclic_datagram, n, &master->cyclic_datagram_queue, queue)
+    {
+        for (netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS; netdev_idx++) {
+            if (cyclic_datagram->datagrams[netdev_idx].state == EC_DATAGRAM_RECEIVED) {
+                master->actual_working_counter += cyclic_datagram->datagrams[netdev_idx].working_counter;
+            }
+        }
+    }
+
+    if (master->dc_ref_clock) {
+        EC_WRITE_U32(master->dc_ref_sync_datagram.data, ec_htimer_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_master_queue_datagram(master, &master->dc_ref_sync_datagram);
+
+        ec_datagram_zero(&master->dc_all_sync_datagram);
+        ec_master_queue_datagram(master, &master->dc_all_sync_datagram);
+    }
+
+    ec_dlist_for_each_entry_safe(cyclic_datagram, n, &master->cyclic_datagram_queue, queue)
+    {
+        ec_master_queue_datagram(master, &cyclic_datagram->datagrams[EC_NETDEV_MAIN]);
+    }
+
+    ec_master_send(master);
+}

+ 143 - 0
src/ec_netdev.c

@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+
+extern void ec_master_receive_datagrams(ec_master_t *master,
+                                        ec_netdev_index_t netdev_index,
+                                        const uint8_t *frame_data,
+                                        size_t size);
+
+/** List of intervals for statistics [s].
+ */
+const unsigned int netdev_rate_intervals[] = {
+    1, 10, 60
+};
+
+EC_FAST_CODE_SECTION void ec_netdev_clear_stats(ec_netdev_t *netdev)
+{
+    unsigned int i;
+
+    // zero frame statistics
+    netdev->tx_count = 0;
+    netdev->last_tx_count = 0;
+    netdev->rx_count = 0;
+    netdev->last_rx_count = 0;
+    netdev->tx_bytes = 0;
+    netdev->last_tx_bytes = 0;
+    netdev->rx_bytes = 0;
+    netdev->last_rx_bytes = 0;
+    netdev->tx_errors = 0;
+
+    for (i = 0; i < EC_RATE_COUNT; i++) {
+        netdev->tx_frame_rates[i] = 0;
+        netdev->rx_frame_rates[i] = 0;
+        netdev->tx_byte_rates[i] = 0;
+        netdev->rx_byte_rates[i] = 0;
+    }
+}
+
+EC_FAST_CODE_SECTION void ec_netdev_update_stats(ec_netdev_t *netdev)
+{
+    unsigned int i;
+
+    int32_t tx_frame_rate = (netdev->tx_count - netdev->last_tx_count) * 1000;
+    int32_t rx_frame_rate = (netdev->rx_count - netdev->last_rx_count) * 1000;
+    int32_t tx_byte_rate = (netdev->tx_bytes - netdev->last_tx_bytes);
+    int32_t rx_byte_rate = (netdev->rx_bytes - netdev->last_rx_bytes);
+
+    /* Low-pass filter:
+     *      Y_n = y_(n - 1) + T / tau * (x - y_(n - 1))   | T = 1
+     *   -> Y_n += (x - y_(n - 1)) / tau
+     */
+    for (i = 0; i < EC_RATE_COUNT; i++) {
+        int32_t n = netdev_rate_intervals[i];
+        netdev->tx_frame_rates[i] +=
+            (tx_frame_rate - netdev->tx_frame_rates[i]) / n;
+        netdev->rx_frame_rates[i] +=
+            (rx_frame_rate - netdev->rx_frame_rates[i]) / n;
+        netdev->tx_byte_rates[i] +=
+            (tx_byte_rate - netdev->tx_byte_rates[i]) / n;
+        netdev->rx_byte_rates[i] +=
+            (rx_byte_rate - netdev->rx_byte_rates[i]) / n;
+    }
+
+    netdev->last_tx_count = netdev->tx_count;
+    netdev->last_rx_count = netdev->rx_count;
+    netdev->last_tx_bytes = netdev->tx_bytes;
+    netdev->last_rx_bytes = netdev->rx_bytes;
+}
+
+ec_netdev_t *ec_netdev_init(uint8_t netdev_index)
+{
+    ec_netdev_t *netdev;
+
+    netdev = ec_netdev_low_level_init(netdev_index);
+    if (netdev) {
+        ec_netdev_clear_stats(netdev);
+        netdev->index = netdev_index;
+        netdev->tx_frame_index = 0;
+        netdev->link_state = false;
+
+        snprintf(netdev->name, sizeof(netdev->name), "ec-netdev%d(%s)", netdev_index, netdev_index == 0 ? "main" : "backup");
+    }
+    return netdev;
+}
+
+void ec_netdev_enable_irq(ec_netdev_t *netdev, bool enable)
+{
+    ec_netdev_low_level_enable_irq(netdev, enable);
+}
+
+bool ec_netdev_get_link_state(ec_netdev_t *netdev)
+{
+    return ec_netdev_low_level_get_link_state(netdev);
+}
+
+EC_FAST_CODE_SECTION uint8_t *ec_netdev_get_txbuf(ec_netdev_t *netdev)
+{
+    return (ec_netdev_low_level_get_txbuf(netdev) + ETH_HLEN);
+}
+
+EC_FAST_CODE_SECTION int ec_netdev_send(ec_netdev_t *netdev, uint32_t size)
+{
+    if (ec_netdev_low_level_output(netdev, size + ETH_HLEN) == 0) {
+        netdev->tx_count++;
+        netdev->master->netdev_stats.tx_count++;
+        netdev->tx_bytes += ETH_HLEN + size;
+        netdev->master->netdev_stats.tx_bytes += ETH_HLEN + size;
+
+        return 0;
+    } else {
+        netdev->tx_errors++;
+        return -1;
+    }
+}
+
+EC_FAST_CODE_SECTION void ec_netdev_receive(ec_netdev_t *netdev, uint8_t *frame, uint32_t size)
+{
+    const uint8_t *ec_data = frame + ETH_HLEN;
+    uint32_t ec_size = size - ETH_HLEN;
+
+    netdev->rx_count++;
+    netdev->master->netdev_stats.rx_count++;
+    netdev->rx_bytes += size;
+    netdev->master->netdev_stats.rx_bytes += size;
+
+    ec_master_receive_datagrams(netdev->master, netdev->index, ec_data, ec_size);
+}
+
+EC_FAST_CODE_SECTION void ec_netdev_poll(ec_netdev_t *netdev)
+{
+    while (ec_netdev_low_level_input(netdev) == 0) {
+    }
+}
+
+void ec_netdev_trigger_poll(ec_netdev_t *netdev)
+{
+    if (netdev->master->nonperiod_sem) {
+        ec_osal_sem_give(netdev->master->nonperiod_sem);
+    }
+}

+ 78 - 0
src/ec_perf.c

@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+
+void ec_perf_init(ec_perf_t *perf, uint64_t expected_interval_us)
+{
+    memset(perf, 0, sizeof(ec_perf_t));
+
+    perf->enable = true;
+    perf->min_interval = UINT64_MAX;
+    perf->max_interval = 0;
+    perf->min_jitter = INT64_MAX;
+    perf->max_jitter = INT64_MIN;
+    perf->expected_interval = expected_interval_us;
+    perf->ignore_count = 5;
+
+    EC_LOG_RAW("Perf initialized\n");
+    EC_LOG_RAW("Expected interval: %llu us\n", expected_interval_us);
+}
+
+EC_FAST_CODE_SECTION void ec_perf_polling(ec_perf_t *perf)
+{
+    uint64_t current_timestamp = ec_htimer_get_time_us();
+
+    if (!perf->enable) {
+        return;
+    }
+
+    if (perf->ignore_count > 0) {
+        perf->ignore_count--;
+        perf->last_timestamp = current_timestamp;
+        return;
+    }
+
+    uint64_t interval = current_timestamp - perf->last_timestamp;
+    int64_t jitter = (int64_t)interval - (int64_t)perf->expected_interval;
+
+    if (interval < perf->min_interval)
+        perf->min_interval = interval;
+    if (interval > perf->max_interval)
+        perf->max_interval = interval;
+    if (jitter < perf->min_jitter)
+        perf->min_jitter = jitter;
+    if (jitter > perf->max_jitter)
+        perf->max_jitter = jitter;
+
+    perf->total_interval += interval;
+    perf->total_jitter += jitter;
+
+    perf->count++;
+    perf->last_timestamp = current_timestamp;
+}
+
+void ec_perf_print_statistics(ec_perf_t *perf)
+{
+    if (perf->count == 0)
+        return;
+
+    double avg_interval = (double)perf->total_interval / perf->count;
+    double avg_jitter = (double)perf->total_jitter / perf->count;
+
+    EC_LOG_RAW("\n========= Perf Statistics =========\n");
+    EC_LOG_RAW("Measurements: %lld\n", perf->count);
+    EC_LOG_RAW("\nInterval Statistics (us):\n");
+    EC_LOG_RAW("  Average:    %.2f\n", avg_interval);
+    EC_LOG_RAW("  Minimum:    %llu\n", perf->min_interval);
+    EC_LOG_RAW("  Maximum:    %llu\n", perf->max_interval);
+
+    EC_LOG_RAW("\nJitter Statistics (us):\n");
+    EC_LOG_RAW("  Average:        %.2f\n", avg_jitter);
+    EC_LOG_RAW("  Minimum:        %lld\n", perf->min_jitter);
+    EC_LOG_RAW("  Maximum:        %lld\n", perf->max_jitter);
+
+    EC_LOG_RAW("===================================\n");
+}

+ 162 - 0
src/ec_sii.c

@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+
+#define SII_TIMEOUT_MS (200 * 1000)
+
+static int ec_sii_assign_master(ec_slave_t *slave, ec_datagram_t *datagram)
+{
+    // assign SII to ECAT
+    ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->EEPROM_CFG), 1);
+    EC_WRITE_U8(datagram->data, 0x00);
+    datagram->netdev_idx = slave->netdev_idx;
+    return ec_master_queue_ext_datagram(slave->master, datagram, true, true);
+}
+
+static int esc_sii_assign_pdi(ec_slave_t *slave, ec_datagram_t *datagram)
+{
+    // assign SII to PDI
+    ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->EEPROM_CFG), 1);
+    EC_WRITE_U8(datagram->data, 0x01);
+    datagram->netdev_idx = slave->netdev_idx;
+    return ec_master_queue_ext_datagram(slave->master, datagram, true, true);
+}
+
+static int ec_sii_read_dword(ec_slave_t *slave, ec_datagram_t *datagram, uint16_t woffset, uint32_t *value)
+{
+    uint32_t start_time;
+    int ret;
+
+    ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->EEPROM_CTRL_STAT), 4);
+    EC_WRITE_U8(datagram->data, 0x80);         // two address bytes
+    EC_WRITE_U8(datagram->data + 1, 0x01);     // read command
+    EC_WRITE_U16(datagram->data + 2, woffset); // word offset
+
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(slave->master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    start_time = jiffies;
+sii_check:
+    // read with 4 bytes
+    ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->EEPROM_CTRL_STAT), 10);
+    ec_datagram_zero(datagram);
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(slave->master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (EC_READ_U16(datagram->data) & ESC_EEPROM_CTRL_STAT_ERR_ACK_CMD_SHIFT) {
+        return -EC_ERR_SII;
+    }
+
+    if (EC_READ_U16(datagram->data) & ESC_EEPROM_CTRL_STAT_BUSY_MASK) {
+        if ((jiffies - start_time) > SII_TIMEOUT_MS) {
+            return -EC_ERR_TIMEOUT;
+        }
+        goto sii_check;
+    }
+
+    ec_memcpy(value, datagram->data + 6, 4);
+
+    return 0;
+}
+
+static int ec_sii_write_word(ec_slave_t *slave, ec_datagram_t *datagram, uint16_t woffset, uint16_t value)
+{
+    uint32_t start_time;
+    int ret;
+
+    // write with 2 bytes
+    ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->EEPROM_CTRL_STAT), 8);
+    EC_WRITE_U8(datagram->data, 0x81);         // two address bytes + enable write access
+    EC_WRITE_U8(datagram->data + 1, 0x02);     // write command
+    EC_WRITE_U16(datagram->data + 2, woffset); // word offset
+    EC_WRITE_U16(datagram->data + 4, 0x00);
+    EC_WRITE_U16(datagram->data + 6, value);
+
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(slave->master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    start_time = jiffies;
+sii_check:
+    ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->EEPROM_CTRL_STAT), 2);
+    ec_datagram_zero(datagram);
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(slave->master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (EC_READ_U16(datagram->data) & ESC_EEPROM_CTRL_STAT_ERR_ACK_CMD_SHIFT) {
+        return -EC_ERR_SII;
+    }
+
+    if (EC_READ_U16(datagram->data) & ESC_EEPROM_CTRL_STAT_BUSY_MASK) {
+        if ((jiffies - start_time) > SII_TIMEOUT_MS) {
+            return -EC_ERR_TIMEOUT;
+        }
+        goto sii_check;
+    }
+
+    if (EC_READ_U16(datagram->data) & ESC_EEPROM_CTRL_STAT_ERR_WEN_MASK) {
+        return -EC_ERR_SII;
+    }
+
+    return 0;
+}
+
+int ec_sii_read(ec_slave_t *slave, ec_datagram_t *datagram, uint16_t woffset, uint32_t *buf, uint32_t len)
+{
+    int ret;
+
+    if (len % 4) {
+        return -EC_ERR_INVAL;
+    }
+
+    ret = ec_sii_assign_master(slave, datagram);
+    if (ret < 0) {
+        return ret;
+    }
+
+    for (uint32_t i = 0; i < (len / 4); i++) {
+        ret = ec_sii_read_dword(slave, datagram, woffset + i * 2, &buf[i]);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    return esc_sii_assign_pdi(slave, datagram);
+}
+
+int esc_sii_write(ec_slave_t *slave, ec_datagram_t *datagram, uint16_t woffset, const uint16_t *buf, uint32_t len)
+{
+    int ret;
+
+    if (len % 2) {
+        return -EC_ERR_INVAL;
+    }
+
+    ret = ec_sii_assign_master(slave, datagram);
+    if (ret < 0) {
+        return ret;
+    }
+
+    for (uint32_t i = 0; i < (len / 2); i++) {
+        ret = ec_sii_write_word(slave, datagram, woffset + i, buf[i]);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    return esc_sii_assign_pdi(slave, datagram);
+}

+ 1254 - 0
src/ec_slave.c

@@ -0,0 +1,1254 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ec_master.h"
+
+/** Maximum clock difference (in ns) before going to SAFEOP.
+ *
+ * Wait for DC time difference to drop under this absolute value before
+ * requesting SAFEOP.
+ */
+#define EC_DC_MAX_SYNC_DIFF_NS 1000
+
+/** Maximum time (in ms) to wait for clock discipline.
+ */
+#define EC_DC_SYNC_WAIT_MS     (5000 * 1000)
+
+/** Time offset (in ns), that is added to cyclic start time.
+ */
+#define EC_DC_START_OFFSET     100000000ULL
+
+static void ec_slave_init(ec_slave_t *slave,
+                          uint32_t slave_index,
+                          ec_master_t *master,
+                          ec_netdev_index_t netdev_idx,
+                          uint16_t autoinc_address,
+                          uint16_t station_address)
+{
+    slave->index = slave_index;
+    slave->master = master;
+    slave->netdev_idx = netdev_idx;
+    slave->autoinc_address = autoinc_address;
+    slave->station_address = station_address;
+
+    slave->requested_state = EC_SLAVE_STATE_PREOP;
+}
+
+static void ec_slave_clear(ec_slave_t *slave)
+{
+    int i;
+
+    if (slave->sii_image) {
+        ec_osal_free(slave->sii_image);
+        slave->sii_image = NULL;
+        slave->sii_nwords = 0;
+    }
+
+    if (slave->sii.strings) {
+        for (i = 0; i < slave->sii.string_count; i++)
+            ec_osal_free(slave->sii.strings[i]);
+        ec_osal_free(slave->sii.strings);
+        slave->sii.strings = NULL;
+    }
+}
+
+static int ec_slave_fetch_sii_strings(ec_slave_t *slave, const uint8_t *data, size_t data_size)
+{
+    int i, ret;
+    uint32_t size;
+    uint32_t offset;
+
+    slave->sii.string_count = data[0];
+
+    if (slave->sii.string_count) {
+        slave->sii.strings = ec_osal_malloc(sizeof(char *) * slave->sii.string_count);
+        if (!slave->sii.strings) {
+            EC_SLAVE_LOG_ERR("Failed to allocate string array memory\n");
+            ret = -EC_ERR_NOMEM;
+            goto errorout1;
+        }
+
+        offset = 1;
+        for (i = 0; i < slave->sii.string_count; i++) {
+            size = data[offset];
+
+            slave->sii.strings[i] = ec_osal_malloc(sizeof(char) * size + 1);
+            if (!slave->sii.strings[i]) {
+                EC_SLAVE_LOG_ERR("Failed to allocate string memory\n");
+                ret = -EC_ERR_NOMEM;
+                goto errorout2;
+            }
+
+            ec_memcpy(slave->sii.strings[i], data + offset + 1, size);
+            slave->sii.strings[i][size] = 0x00;
+            offset += 1 + size;
+        }
+    }
+
+    return 0;
+
+errorout2:
+    for (i = 0; i < slave->sii.string_count; i++) {
+        ec_osal_free(slave->sii.strings[i]);
+    }
+    ec_osal_free(slave->sii.strings);
+    slave->sii.strings = NULL;
+errorout1:
+    slave->sii.string_count = 0;
+    return ret;
+}
+
+/** Get timeout in us.
+ *
+ * For defaults see ETG2000_S_R_V1i0i15 section 5.3.6.2.
+ */
+unsigned int ec_slave_state_change_timeout_us(ec_slave_state_t old_state, ec_slave_state_t requested_state)
+{
+    ec_slave_state_t from = old_state;
+    ec_slave_state_t to = requested_state;
+
+    if (from == EC_SLAVE_STATE_INIT &&
+        (to == EC_SLAVE_STATE_PREOP || to == EC_SLAVE_STATE_BOOT)) {
+        return (3000 * 1000); // PreopTimeout
+    }
+    if ((from == EC_SLAVE_STATE_PREOP && to == EC_SLAVE_STATE_SAFEOP) ||
+        (from == EC_SLAVE_STATE_SAFEOP && to == EC_SLAVE_STATE_OP)) {
+        return (10000 * 1000); // SafeopOpTimeout
+    }
+    if (to == EC_SLAVE_STATE_INIT ||
+        ((from == EC_SLAVE_STATE_OP || from == EC_SLAVE_STATE_SAFEOP) && to == EC_SLAVE_STATE_PREOP)) {
+        return (5000 * 1000); // BackToInitTimeout
+    }
+    if (from == EC_SLAVE_STATE_OP && to == EC_SLAVE_STATE_SAFEOP) {
+        return (200 * 1000); // BackToSafeopTimeout
+    }
+
+    return (10000 * 1000); // default [us]
+}
+
+static uint8_t ec_slave_get_previous_port(const ec_slave_t *slave, uint8_t port_index)
+{
+    static const uint8_t prev_table[EC_MAX_PORTS] = {
+        2, 3, 1, 0
+    };
+
+    do {
+        port_index = prev_table[port_index];
+        if (slave->ports[port_index].next_slave) {
+            return port_index;
+        }
+    } while (port_index);
+
+    return 0;
+}
+
+static uint8_t ec_slave_get_next_port(const ec_slave_t *slave, uint8_t port_index)
+{
+    static const uint8_t next_table[EC_MAX_PORTS] = {
+        3, 2, 0, 1
+    };
+
+    do {
+        port_index = next_table[port_index];
+        if (slave->ports[port_index].next_slave) {
+            return port_index;
+        }
+    } while (port_index);
+
+    return 0;
+}
+
+/** Calculates the sum of round-trip-times of connected ports 1-3.
+ *
+ * \return Round-trip-time in ns.
+ */
+static uint32_t ec_slave_calc_rtt_sum(const ec_slave_t *slave)
+{
+    uint32_t rtt_sum = 0, rtt;
+    uint8_t port_index = ec_slave_get_next_port(slave, 0);
+
+    while (port_index != 0) {
+        uint8_t prev_index =
+            ec_slave_get_previous_port(slave, port_index);
+
+        rtt = slave->ports[port_index].receive_time -
+              slave->ports[prev_index].receive_time;
+        rtt_sum += rtt;
+        port_index = ec_slave_get_next_port(slave, port_index);
+    }
+
+    return rtt_sum;
+}
+
+/** Finds the next slave supporting DC delay measurement.
+ *
+ * \return Next DC slave, or NULL.
+ */
+static ec_slave_t *ec_slave_find_next_dc_slave(ec_slave_t *slave)
+{
+    uint8_t port_index;
+    ec_slave_t *dc_slave = NULL;
+
+    if (slave->base_dc_supported) {
+        dc_slave = slave;
+    } else {
+        port_index = ec_slave_get_next_port(slave, 0);
+
+        while (port_index != 0) {
+            ec_slave_t *next = slave->ports[port_index].next_slave;
+
+            if (next) {
+                dc_slave = ec_slave_find_next_dc_slave(next);
+
+                if (dc_slave) {
+                    break;
+                }
+            }
+            port_index = ec_slave_get_next_port(slave, port_index);
+        }
+    }
+
+    return dc_slave;
+}
+
+void ec_slave_calc_port_delays(ec_slave_t *slave)
+{
+    uint8_t port_index;
+    ec_slave_t *next_slave, *next_dc;
+    uint32_t rtt, next_rtt_sum;
+
+    if (!slave->base_dc_supported)
+        return;
+
+    port_index = ec_slave_get_next_port(slave, 0);
+
+    while (port_index != 0) {
+        next_slave = slave->ports[port_index].next_slave;
+        next_dc = ec_slave_find_next_dc_slave(next_slave);
+
+        if (next_dc) {
+            uint8_t prev_port =
+                ec_slave_get_previous_port(slave, port_index);
+
+            rtt = slave->ports[port_index].receive_time -
+                  slave->ports[prev_port].receive_time;
+            next_rtt_sum = ec_slave_calc_rtt_sum(next_dc);
+
+            slave->ports[port_index].delay_to_next_dc =
+                (rtt - next_rtt_sum) / 2;
+            next_dc->ports[0].delay_to_next_dc =
+                (rtt - next_rtt_sum) / 2;
+        }
+
+        port_index = ec_slave_get_next_port(slave, port_index);
+    }
+}
+
+void ec_slave_calc_transmission_delays(ec_slave_t *slave, uint32_t *delay)
+{
+    unsigned int i;
+    ec_slave_t *next_dc;
+
+    slave->transmission_delay = *delay;
+
+    i = ec_slave_get_next_port(slave, 0);
+
+    while (i != 0) {
+        ec_slave_port_t *port = &slave->ports[i];
+        next_dc = ec_slave_find_next_dc_slave(port->next_slave);
+        if (next_dc) {
+            *delay = *delay + port->delay_to_next_dc;
+
+            ec_slave_calc_transmission_delays(next_dc, delay);
+        }
+
+        i = ec_slave_get_next_port(slave, i);
+    }
+
+    *delay = *delay + slave->ports[0].delay_to_next_dc;
+}
+
+static int ec_slave_state_clear_ack_error(ec_master_t *master, ec_slave_t *slave, ec_slave_state_t requested_state)
+{
+    ec_datagram_t *datagram;
+    int ret;
+    uint64_t start_time;
+    uint32_t status_code;
+
+    datagram = &master->main_datagram;
+
+    start_time = jiffies;
+
+    ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->AL_STAT_CODE), 2);
+    ec_datagram_zero(datagram);
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    status_code = EC_READ_U16(datagram->data);
+
+    slave->alstatus_code = status_code;
+
+    ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->AL_CTRL), 2);
+    EC_WRITE_U16(datagram->data, slave->current_state);
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+repeat_check:
+    ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->AL_STAT), 2);
+    ec_datagram_zero(datagram);
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    slave->current_state = EC_READ_U8(datagram->data);
+
+    if (!(slave->current_state & EC_SLAVE_STATE_ACK_ERR)) {
+        EC_SLAVE_LOG_ERR("Slave %u acked state %s, alstatus code: 0x%04x (%s)\n",
+                         slave->index,
+                         ec_state_string(slave->current_state, 0),
+                         status_code,
+                         ec_alstatus_string(status_code));
+
+        return -EC_ERR_ALERR;
+    } else {
+        if ((jiffies - start_time) > ec_slave_state_change_timeout_us(slave->current_state, requested_state)) {
+            return -EC_ERR_TIMEOUT;
+        }
+        goto repeat_check;
+    }
+}
+
+/* ec_slave_state_change - change slave state
+ *
+ * 1. write AL control register
+ * 2. read AL status register
+ * 3. if state is changed to correct state, return success
+ * 4. if state is not changed and acknowledge bit is set, read AL status code
+ *    and write AL control register to acknowledge error, then repeat 2
+ * 5. if state is changed to other state, repeat step 2
+*/
+static int ec_slave_state_change(ec_master_t *master, ec_slave_t *slave, ec_slave_state_t requested_state)
+{
+    ec_datagram_t *datagram;
+    int ret;
+    uint32_t start_time;
+    ec_slave_state_t old_state;
+
+    datagram = &master->main_datagram;
+
+    old_state = slave->current_state;
+    start_time = jiffies;
+
+    ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->AL_CTRL), 2);
+    EC_WRITE_U16(datagram->data, requested_state);
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+repeat_check:
+    ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->AL_STAT), 2);
+    ec_datagram_zero(datagram);
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    slave->current_state = EC_READ_U8(datagram->data);
+
+    if (slave->current_state == requested_state) {
+        EC_SLAVE_LOG_INFO("Slave %u State changed to %s\n", slave->index, ec_state_string(slave->current_state, 0));
+        return 0;
+    }
+
+    if (slave->current_state != old_state) {
+        if ((slave->current_state & 0x0F) == (old_state & 0x0F)) { // acknowledge bit enable
+            return ec_slave_state_clear_ack_error(master, slave, requested_state);
+        } else {
+            old_state = slave->current_state;
+            if ((jiffies - start_time) > ec_slave_state_change_timeout_us(slave->current_state, requested_state)) {
+                return -EC_ERR_TIMEOUT;
+            }
+            goto repeat_check;
+        }
+    }
+
+    // still in old state
+    if ((jiffies - start_time) > ec_slave_state_change_timeout_us(slave->current_state, requested_state)) {
+        return -EC_ERR_TIMEOUT;
+    }
+    goto repeat_check;
+}
+
+static inline void ec_slave_sm_config(ec_sm_info_t *sm, uint8_t *data)
+{
+    EC_WRITE_U16(data, sm->physical_start_address);
+    EC_WRITE_U16(data + 2, sm->length);
+    EC_WRITE_U8(data + 4, sm->control);
+    EC_WRITE_U8(data + 5, 0x00); // status byte (read only)
+    EC_WRITE_U16(data + 6, sm->enable);
+}
+
+static inline void ec_slave_fmmu_config(ec_sm_info_t *sm, uint8_t *data)
+{
+    EC_WRITE_U32(data, sm->fmmu.logical_start_address);
+    EC_WRITE_U16(data + 4, sm->fmmu.data_size); // size of fmmu
+    EC_WRITE_U8(data + 6, 0x00);                // logical start bit
+    EC_WRITE_U8(data + 7, 0x07);                // logical end bit
+    EC_WRITE_U16(data + 8, sm->physical_start_address);
+    EC_WRITE_U8(data + 10, 0x00); // physical start bit
+    EC_WRITE_U8(data + 11, sm->fmmu.dir == EC_DIR_INPUT ? 0x01 : 0x02);
+    EC_WRITE_U16(data + 12, 0x0001); // enable
+    EC_WRITE_U16(data + 14, 0x0000); // reserved
+}
+
+static int ec_slave_config_dc_systime_and_delay(ec_slave_t *slave)
+{
+    ec_datagram_t *datagram;
+    uint64_t system_time, old_system_time_offset, new_system_time_offset;
+    uint64_t time_diff;
+    int ret;
+
+    datagram = &slave->master->main_datagram;
+
+    if (slave->base_dc_supported && slave->has_dc_system_time) {
+        ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->SYS_TIME), 24);
+        datagram->netdev_idx = slave->netdev_idx;
+        ret = ec_master_queue_ext_datagram(slave->master, datagram, true, true);
+        if (ret < 0) {
+            return ret;
+        }
+        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;
+
+        if (slave->base_dc_range == EC_DC_32) {
+            system_time = (uint32_t)system_time + datagram->jiffies_sent * 1000;
+            old_system_time_offset = (uint32_t)old_system_time_offset;
+        } else {
+            system_time = system_time + datagram->jiffies_sent * 1000;
+        }
+
+        if (time_diff > 1000000) { // 1ms
+            new_system_time_offset = time_diff + old_system_time_offset;
+        } else {
+            new_system_time_offset = old_system_time_offset;
+        }
+
+        // set DC system time offset and transmission delay
+        ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->SYS_TIME_OFFSET), 12);
+        EC_WRITE_U64(datagram->data, new_system_time_offset);
+        EC_WRITE_U32(datagram->data + 8, slave->transmission_delay);
+        datagram->netdev_idx = slave->netdev_idx;
+        ret = ec_master_queue_ext_datagram(slave->master, datagram, true, true);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
+static int ec_slave_config(ec_master_t *master, ec_slave_t *slave)
+{
+    ec_datagram_t *datagram;
+    uint64_t start_time;
+    int ret;
+
+    datagram = &master->main_datagram;
+
+    ret = ec_slave_state_change(master, slave, EC_SLAVE_STATE_INIT);
+    if (ret < 0) {
+        return ret;
+    }
+
+    // clear FMMU configurations
+    ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->FMMU[0]), EC_FMMU_PAGE_SIZE * slave->base_fmmu_count);
+    ec_datagram_zero(datagram);
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    // clear sync manager configurations
+    ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->SYNCM[0]), EC_SYNC_PAGE_SIZE * slave->base_sync_count);
+    ec_datagram_zero(datagram);
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    // Clear the DC assignment
+    ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->CYC_UNIT_CTRL), 2);
+    ec_datagram_zero(datagram);
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    // init state done
+    if (slave->current_state == slave->requested_state) {
+        return 0;
+    }
+
+    // Config mailbox sm
+    ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->SYNCM[0]), EC_SYNC_PAGE_SIZE * 2);
+    ec_datagram_zero(datagram);
+    for (uint8_t i = 0; i < 2; i++) {
+        ec_slave_sm_config(&slave->sm_info[i], datagram->data + EC_SYNC_PAGE_SIZE * i);
+    }
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    slave->configured_rx_mailbox_offset = slave->sm_info[EC_SM_INDEX_MBX_WRITE].physical_start_address;
+    slave->configured_rx_mailbox_size = slave->sm_info[EC_SM_INDEX_MBX_WRITE].length;
+    slave->configured_tx_mailbox_offset = slave->sm_info[EC_SM_INDEX_MBX_READ].physical_start_address;
+    slave->configured_tx_mailbox_size = slave->sm_info[EC_SM_INDEX_MBX_READ].length;
+
+    ret = ec_slave_state_change(master, slave, EC_SLAVE_STATE_PREOP);
+    if (ret < 0) {
+        EC_SLAVE_LOG_ERR("Failed to change state to %s on slave %u\n",
+                         ec_state_string(slave->requested_state, 0), slave->index);
+        return ret;
+    }
+#if 0
+    uint32_t size;
+    // Scanning PDO assignment and mapping
+    ret = ec_coe_upload(slave, datagram, 0x1c12, 0x00, &slave->sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_assign, sizeof(ec_pdo_assign_t), &size, true);
+    if (ret < 0) {
+        EC_SLAVE_LOG_ERR("Failed to read RxPDO assignment on slave %u, err: %d\n", slave->index, ret);
+        return ret;
+    }
+
+    if (slave->sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_assign.count > CONFIG_EC_PER_SM_MAX_PDOS) {
+        EC_SLAVE_LOG_WRN("Slave %u has more RxPDOs (%u) than the master can handle (%u)\n",
+                         slave->index, slave->sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_assign.count, CONFIG_EC_PER_SM_MAX_PDOS);
+        slave->sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_assign.count = CONFIG_EC_PER_SM_MAX_PDOS;
+    }
+
+    ret = ec_coe_upload(slave, datagram, 0x1c13, 0x00, &slave->sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_assign, sizeof(ec_pdo_assign_t), &size, true);
+    if (ret < 0) {
+        EC_SLAVE_LOG_ERR("Failed to read TxPDO assignment on slave %u, err: %d\n", slave->index, ret);
+        return ret;
+    }
+
+    if (slave->sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_assign.count > CONFIG_EC_PER_SM_MAX_PDOS) {
+        EC_SLAVE_LOG_WRN("Slave %u has more TxPDOs (%u) than the master can handle (%u)\n",
+                         slave->index, slave->sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_assign, CONFIG_EC_PER_SM_MAX_PDOS);
+        slave->sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_assign.count = CONFIG_EC_PER_SM_MAX_PDOS;
+    }
+
+    for (uint8_t i = 0; i < slave->sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_assign.count; i++) {
+        ret = ec_coe_upload(slave, datagram, slave->sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_assign.entry[i], 0x00, &slave->sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_mapping, sizeof(ec_pdo_mapping_t) * CONFIG_EC_PER_SM_MAX_PDOS, &size, true);
+        if (ret < 0) {
+            EC_SLAVE_LOG_ERR("Failed to read RxPDO mapping 0x%04x on slave %u, err: %d\n", slave->sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT].pdo_assign.entry[i], slave->index, ret);
+            return ret;
+        }
+    }
+
+    for (uint8_t i = 0; i < slave->sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_assign.count; i++) {
+        ret = ec_coe_upload(slave, datagram, slave->sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_assign.entry[i], 0x00, &slave->sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_mapping, sizeof(ec_pdo_mapping_t) * CONFIG_EC_PER_SM_MAX_PDOS, &size, true);
+        if (ret < 0) {
+            EC_SLAVE_LOG_ERR("Failed to read TxPDO mapping 0x%04x on slave %u, err: %d\n", slave->sm_info[EC_SM_INDEX_PROCESS_DATA_INPUT].pdo_assign.entry[i], slave->index, ret);
+            return ret;
+        }
+    }
+
+    EC_SLAVE_LOG_INFO("Scanning Slave %u PDO assignment and mapping success\n", slave->index);
+#endif
+    // preop state done
+    if (slave->current_state == slave->requested_state) {
+        return 0;
+    }
+
+    // Config process data sm
+    ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->SYNCM[2]), EC_SYNC_PAGE_SIZE * 2);
+    ec_datagram_zero(datagram);
+    for (uint8_t i = 0; i < 2; i++) {
+        ec_slave_sm_config(&slave->sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT + i], datagram->data + EC_SYNC_PAGE_SIZE * i);
+    }
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->FMMU[0]), EC_FMMU_PAGE_SIZE * 2);
+    ec_datagram_zero(datagram);
+    for (uint8_t i = 0; i < 2; i++) {
+        ec_slave_fmmu_config(&slave->sm_info[EC_SM_INDEX_PROCESS_DATA_OUTPUT + i], datagram->data + EC_FMMU_PAGE_SIZE * i);
+    }
+    datagram->netdev_idx = slave->netdev_idx;
+    ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (slave->config && slave->config->dc_assign_activate) {
+        if (!slave->base_dc_supported) {
+            EC_SLAVE_LOG_WRN("Slave %u does not support DC, but DC is activated in master config\n", slave->index);
+        }
+
+        ec_slave_config_dc_systime_and_delay(slave);
+
+        // set DC cycle times
+        ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->SYNC0_CYC_TIME), 8);
+        EC_WRITE_U32(datagram->data, slave->config->dc_sync[0].cycle_time);
+        EC_WRITE_U32(datagram->data + 4, slave->config->dc_sync[1].cycle_time);
+        datagram->netdev_idx = slave->netdev_idx;
+        ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+        if (ret < 0) {
+            return ret;
+        }
+
+        start_time = jiffies;
+    read_check:
+        ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->SYS_TIME_DIFF), 4);
+        ec_datagram_zero(datagram);
+        datagram->netdev_idx = slave->netdev_idx;
+        ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+        if (ret < 0) {
+            return ret;
+        }
+
+        uint32_t time_diff = EC_READ_U32(datagram->data) & 0x7fffffff;
+        if (time_diff > EC_DC_MAX_SYNC_DIFF_NS) {
+            if ((jiffies - start_time) > EC_DC_SYNC_WAIT_MS) {
+                return -EC_ERR_TIMEOUT;
+            }
+            goto read_check;
+        } else {
+            EC_SLAVE_LOG_INFO("Slave %u DC time diff: %u ns\n", slave->index, time_diff);
+        }
+
+        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 +
+                        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);
+
+        EC_WRITE_U64(datagram->data, dc_start_time);
+        datagram->netdev_idx = slave->netdev_idx;
+        ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+        if (ret < 0) {
+            return ret;
+        }
+
+        ec_datagram_fpwr(datagram, slave->station_address, ESCREG_OF(ESCREG->CYC_UNIT_CTRL), 2);
+        EC_WRITE_U16(datagram->data, slave->config->dc_assign_activate);
+        datagram->netdev_idx = slave->netdev_idx;
+        ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    ret = ec_slave_state_change(master, slave, EC_SLAVE_STATE_SAFEOP);
+    if (ret < 0) {
+        EC_SLAVE_LOG_ERR("Failed to change state to %s on slave %u\n",
+                         ec_state_string(slave->requested_state, 0), slave->index);
+        return ret;
+    }
+
+    // safeop state done
+    if (slave->current_state == slave->requested_state) {
+        return 0;
+    }
+
+    ret = ec_slave_state_change(master, slave, EC_SLAVE_STATE_OP);
+    if (ret < 0) {
+        EC_SLAVE_LOG_ERR("Failed to change state to %s on slave %u\n",
+                         ec_state_string(slave->requested_state, 0), slave->index);
+        return ret;
+    }
+
+    return 0;
+}
+
+static void ec_master_clear_slaves(ec_master_t *master)
+{
+    ec_slave_t *slave;
+
+    if (master->slaves) {
+        for (uint32_t slave_index = 0; slave_index < master->slave_count; slave_index++) {
+            slave = master->slaves + slave_index;
+            ec_slave_clear(slave);
+        }
+
+        ec_osal_free(master->slaves);
+        master->slaves = NULL;
+    }
+
+    master->slave_count = 0;
+}
+
+static int ec_master_calc_topology_rec(ec_master_t *master, ec_slave_t *port0_slave, unsigned int *slave_position)
+{
+    ec_slave_t *slave = master->slaves + *slave_position;
+    unsigned int port_index;
+    int ret;
+
+    static const unsigned int next_table[EC_MAX_PORTS] = {
+        3, 2, 0, 1
+    };
+
+    slave->ports[0].next_slave = port0_slave;
+
+    port_index = 3;
+    while (port_index != 0) {
+        if (!slave->ports[port_index].link.loop_closed) {
+            *slave_position = *slave_position + 1;
+            if (*slave_position < master->slave_count) {
+                slave->ports[port_index].next_slave =
+                    master->slaves + *slave_position;
+                ret = ec_master_calc_topology_rec(master,
+                                                  slave, slave_position);
+                if (ret) {
+                    return ret;
+                }
+            } else {
+                return -1;
+            }
+        }
+
+        port_index = next_table[port_index];
+    }
+
+    return 0;
+}
+
+static void ec_master_find_dc_ref_clock(ec_master_t *master)
+{
+    ec_slave_t *slave, *ref = NULL;
+
+    // Use first slave with DC support as reference clock
+    for (slave = master->slaves;
+         slave < master->slaves + master->slave_count;
+         slave++) {
+        if (slave->base_dc_supported && slave->has_dc_system_time) {
+            ref = slave;
+            break;
+        }
+    }
+
+    master->dc_ref_clock = ref;
+
+    if (ref) {
+        EC_LOG_INFO("Using slave %u as DC reference clock\n", ref->index);
+    } else {
+        EC_LOG_INFO("No DC reference clock found\n");
+    }
+
+    ec_datagram_fpwr(&master->dc_ref_sync_datagram,
+                     ref ? ref->station_address : 0xffff, ESCREG_OF(ESCREG->SYS_TIME), ref->base_dc_range == EC_DC_64 ? 8 : 4);
+    ec_datagram_frmw(&master->dc_all_sync_datagram,
+                     ref ? ref->station_address : 0xffff, ESCREG_OF(ESCREG->SYS_TIME), ref->base_dc_range == EC_DC_64 ? 8 : 4);
+}
+
+static void ec_master_calc_topology(ec_master_t *master)
+{
+    unsigned int slave_position = 0;
+
+    if (master->slave_count == 0)
+        return;
+
+    EC_ASSERT_MSG(ec_master_calc_topology_rec(master, NULL, &slave_position) == 0,
+                  "Failed to calculate bus topology\n");
+}
+
+static void ec_master_calc_transmission_delays(ec_master_t *master)
+{
+    ec_slave_t *slave;
+
+    for (slave = master->slaves;
+         slave < master->slaves + master->slave_count;
+         slave++) {
+        ec_slave_calc_port_delays(slave);
+    }
+
+    if (master->dc_ref_clock) {
+        uint32_t delay = 0;
+        ec_slave_calc_transmission_delays(master->dc_ref_clock, &delay);
+    }
+}
+
+static void ec_master_calc_dc(ec_master_t *master)
+{
+    // find DC reference clock
+    ec_master_find_dc_ref_clock(master);
+
+    // calculate bus topology
+    ec_master_calc_topology(master);
+
+    ec_master_calc_transmission_delays(master);
+}
+
+static void ec_master_scan_slaves_state(ec_master_t *master)
+{
+    ec_datagram_t *datagram;
+    ec_slave_t *slave;
+    uint8_t slave_state;
+    int ret;
+
+    datagram = &master->main_datagram;
+
+    for (uint32_t slave_index = 0; slave_index < master->slave_count; slave_index++) {
+        slave = master->slaves + slave_index;
+
+        ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->AL_STAT), 2);
+        ec_datagram_zero(datagram);
+        datagram->netdev_idx = slave->netdev_idx;
+        ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+        if (ret < 0) {
+            continue;
+        }
+
+        slave_state = EC_READ_U8(datagram->data);
+
+        if (slave->current_state != slave_state) {
+            EC_SLAVE_LOG_WRN("Slave %u state changed to %s\n", slave->index, ec_state_string(slave_state, 0));
+            slave->current_state = slave_state;
+        }
+
+        if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) {
+            ret = ec_slave_state_clear_ack_error(master, slave, slave->requested_state);
+            if (ret < 0) {
+                continue;
+            }
+        } else {
+            slave->alstatus_code = 0;
+        }
+
+        if (((slave->requested_state != slave->current_state) && (slave->alstatus_code == 0)) || slave->force_update) {
+            ret = ec_slave_config(master, slave);
+            if (ret < 0) {
+                EC_SLAVE_LOG_ERR("Failed to configure slave %u\n", slave->index);
+                continue;
+            }
+            slave->force_update = false;
+        }
+    }
+}
+
+void ec_slaves_scanning(ec_master_t *master)
+{
+    ec_datagram_t *datagram;
+    ec_slave_t *slave;
+    unsigned int netdev_idx;
+    bool rescan_required = false;
+    unsigned int scan_jiffies;
+    int ret;
+
+    datagram = &master->main_datagram;
+
+    for (netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS; netdev_idx++) {
+        if (!master->link_state[netdev_idx] && master->netdev[netdev_idx]->link_state) {
+            EC_LOG_INFO("Detect link up on %s\n",
+                        master->netdev[netdev_idx]->name);
+        }
+
+        if (master->link_state[netdev_idx] && !master->netdev[netdev_idx]->link_state) {
+            EC_LOG_INFO("Detect link down on %s\n",
+                        master->netdev[netdev_idx]->name);
+
+            ec_master_clear_slaves(master);
+
+            for (uint8_t i = EC_NETDEV_MAIN; i < CONFIG_EC_MAX_NETDEVS; i++) {
+                master->slaves_state[i] = 0x00;
+                master->slaves_responding[i] = 0;
+            }
+            master->scan_done = false;
+        }
+        master->link_state[netdev_idx] = master->netdev[netdev_idx]->link_state;
+    }
+
+    for (netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS; netdev_idx++) {
+        ec_datagram_brd(datagram, ESCREG_OF(ESCREG->AL_STAT), 2);
+        ec_datagram_zero(datagram);
+        datagram->netdev_idx = netdev_idx;
+        ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+        if (ret < 0) {
+            return;
+        }
+
+        if (datagram->working_counter != master->slaves_responding[netdev_idx]) {
+            rescan_required = 1;
+            master->slaves_responding[netdev_idx] = datagram->working_counter;
+            EC_LOG_INFO("%u slaves responding on %s device\n",
+                        master->slaves_responding[netdev_idx],
+                        master->netdev[netdev_idx]->name);
+        }
+
+        if (master->slaves_responding[netdev_idx] > 0) {
+            uint8_t states = EC_READ_U8(datagram->data);
+            if (states != master->slaves_state[netdev_idx]) {
+                // slave states changed
+                master->slaves_state[netdev_idx] = states;
+                EC_LOG_INFO("Slaves state on %s device: %s\n",
+                            master->netdev[netdev_idx]->name, ec_state_string(states, 1));
+            }
+        } else {
+            master->slaves_state[netdev_idx] = 0;
+        }
+    }
+
+    if (rescan_required) {
+        uint32_t count = 0, slave_index, autoinc_address;
+
+        rescan_required = 0;
+
+        master->scan_done = false;
+        EC_LOG_INFO("Rescanning bus...\n");
+
+        ec_master_clear_slaves(master);
+
+        scan_jiffies = jiffies;
+
+        for (uint8_t i = EC_NETDEV_MAIN; i < CONFIG_EC_MAX_NETDEVS; i++) {
+            count += master->slaves_responding[i];
+        }
+
+        if (!count) {
+            return;
+        }
+
+        master->slaves = ec_osal_malloc(sizeof(ec_slave_t) * count);
+        if (!master->slaves) {
+            EC_LOG_ERR("Failed to allocate memory for slaves\n");
+            return;
+        }
+
+        master->slave_count = count;
+        memset(master->slaves, 0, sizeof(ec_slave_t) * count);
+
+        slave_index = 0;
+        for (uint8_t netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS; netdev_idx++) {
+            autoinc_address = 0;
+            for (uint32_t j = 0; j < master->slaves_responding[netdev_idx]; j++) {
+                slave = master->slaves + slave_index;
+
+                ec_slave_init(slave, slave_index, master, netdev_idx, (int16_t)autoinc_address * (-1), slave_index + 1001);
+
+                slave_index++;
+                autoinc_address++;
+            }
+        }
+
+        for (uint8_t netdev_idx = EC_NETDEV_MAIN; netdev_idx < CONFIG_EC_MAX_NETDEVS; netdev_idx++) {
+            if (master->slaves_responding[netdev_idx] == 0) {
+                continue;
+            }
+            // Clear station address
+            ec_datagram_bwr(datagram, ESCREG_OF(ESCREG->STATION_ADDR), 2);
+            ec_datagram_zero(datagram);
+            datagram->netdev_idx = netdev_idx;
+            ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+            if (ret < 0) {
+                EC_LOG_ERR("Failed to clear station address on %s link\n", master->netdev[netdev_idx]->name);
+                return;
+            }
+
+            // Clear recevice time for dc measure delays
+            ec_datagram_bwr(datagram, ESCREG_OF(ESCREG->RCV_TIME[0]), 4);
+            ec_datagram_zero(datagram);
+            datagram->netdev_idx = netdev_idx;
+            ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+            if (ret < 0) {
+                EC_LOG_ERR("Failed to clear receive time on %s link\n", master->netdev[netdev_idx]->name);
+                return;
+            }
+        }
+
+        for (uint32_t slave_index = 0; slave_index < master->slave_count; slave_index++) {
+            slave = master->slaves + slave_index;
+
+            EC_SLAVE_LOG_INFO("Scanning slave %u on %s\n", slave->index, master->netdev[slave->netdev_idx]->name);
+
+            // Set station address
+            ec_datagram_apwr(datagram, slave->autoinc_address, ESCREG_OF(ESCREG->STATION_ADDR), 2);
+            EC_WRITE_U16(datagram->data, slave->station_address);
+            datagram->netdev_idx = slave->netdev_idx;
+            ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+            if (ret < 0) {
+                EC_LOG_ERR("Failed to set station address on slave %u\n", slave->index);
+                return;
+            }
+
+            // Read AL state
+            ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->AL_STAT), 2);
+            ec_datagram_zero(datagram);
+            datagram->netdev_idx = slave->netdev_idx;
+            ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+            if (ret < 0) {
+                EC_LOG_ERR("Failed to read AL status on slave %u\n", slave->index);
+                return;
+            }
+
+            // Read base information
+            ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->TYPE), 12);
+            ec_datagram_zero(datagram);
+            datagram->netdev_idx = slave->netdev_idx;
+            ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+            if (ret < 0) {
+                EC_SLAVE_LOG_ERR("Failed to read base on slave %u\n", slave->index);
+                return;
+            }
+
+            slave->base_type = EC_READ_U8(datagram->data);
+            slave->base_revision = EC_READ_U8(datagram->data + 1);
+            slave->base_build = EC_READ_U16(datagram->data + 2);
+
+            slave->base_fmmu_count = EC_READ_U8(datagram->data + 4);
+            if (slave->base_fmmu_count > EC_MAX_FMMUS) {
+                EC_SLAVE_LOG_WRN("Slave has more FMMUs (%u) than the master can handle (%u)\n",
+                                 slave->base_fmmu_count, EC_MAX_FMMUS);
+                slave->base_fmmu_count = EC_MAX_FMMUS;
+            }
+
+            slave->base_sync_count = EC_READ_U8(datagram->data + 5);
+            if (slave->base_sync_count > EC_MAX_SYNC_MANAGERS) {
+                EC_SLAVE_LOG_WRN("Slave provides more sync managers (%u) than the master can handle (%u)\n",
+                                 slave->base_sync_count, EC_MAX_SYNC_MANAGERS);
+                slave->base_sync_count = EC_MAX_SYNC_MANAGERS;
+            }
+
+            uint8_t data = EC_READ_U8(datagram->data + 7);
+            for (uint8_t i = 0; i < EC_MAX_PORTS; i++) {
+                slave->ports[i].desc = (data >> (2 * i)) & 0x03;
+            }
+
+            data = EC_READ_U8(datagram->data + 8);
+            slave->base_fmmu_bit_operation = data & 0x01;
+            slave->base_dc_supported = (data >> 2) & 0x01;
+            slave->base_dc_range = ((data >> 3) & 0x01) ? EC_DC_64 : EC_DC_32;
+
+            if (slave->base_dc_supported) {
+                // Read DC capabilities
+                ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->SYS_TIME),
+                                 slave->base_dc_range == EC_DC_64 ? 8 : 4);
+                ec_datagram_zero(datagram);
+                datagram->netdev_idx = slave->netdev_idx;
+                ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+                if (ret < 0) {
+                    EC_SLAVE_LOG_ERR("Failed to read DC capabilities on slave %u\n", slave->index);
+                    return;
+                }
+
+                if (datagram->working_counter == 1) {
+                    slave->has_dc_system_time = 1;
+                    EC_SLAVE_LOG_DBG("Slave has the System Time register\n");
+                } else {
+                    slave->has_dc_system_time = 0;
+                    EC_SLAVE_LOG_DBG("Slave has no System Time register; delay measurement only\n");
+                }
+
+                // Read DC port receive times
+                ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->RCV_TIME[0]), 16);
+                ec_datagram_zero(datagram);
+                datagram->netdev_idx = slave->netdev_idx;
+                ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+                if (ret < 0) {
+                    EC_SLAVE_LOG_ERR("Failed to read DC receive times on slave %u\n", slave->index);
+                    return;
+                }
+
+                for (uint8_t i = 0; i < EC_MAX_PORTS; i++) {
+                    slave->ports[i].receive_time = EC_READ_U32(datagram->data + 4 * i);
+                }
+            } else {
+            }
+
+            // Read data link status
+            ec_datagram_fprd(datagram, slave->station_address, ESCREG_OF(ESCREG->ESC_DL_STAT), 2);
+            ec_datagram_zero(datagram);
+            datagram->netdev_idx = slave->netdev_idx;
+            ret = ec_master_queue_ext_datagram(master, datagram, true, true);
+            if (ret < 0) {
+                EC_SLAVE_LOG_ERR("Failed to read data link status on slave %u\n", slave->index);
+                return;
+            }
+
+            uint16_t dl_status = EC_READ_U16(datagram->data);
+            for (uint8_t i = 0; i < EC_MAX_PORTS; i++) {
+                slave->ports[i].link.link_up =
+                    dl_status & (1 << (4 + i)) ? 1 : 0;
+                slave->ports[i].link.loop_closed =
+                    dl_status & (1 << (8 + i * 2)) ? 1 : 0;
+                slave->ports[i].link.signal_detected =
+                    dl_status & (1 << (9 + i * 2)) ? 1 : 0;
+            }
+
+            uint16_t sii_offset = EC_FIRST_SII_CATEGORY_OFFSET;
+            uint16_t cat_type, cat_size;
+            uint32_t sii_data;
+            uint16_t *cat_data;
+
+            // Read SII category headers to determine full SII size
+            do {
+                ret = ec_sii_read(slave, datagram, sii_offset, &sii_data, 4);
+                if (ret < 0) {
+                    EC_SLAVE_LOG_ERR("Failed to read SII category header on slave %u\n", slave->index);
+                    return;
+                }
+
+                cat_type = sii_data & 0xFFFF;
+                cat_size = (sii_data >> 16) & 0xFFFF;
+
+                sii_offset += 2 + cat_size;
+                EC_SLAVE_LOG_DBG("Found category type 0x%04x with size 0x%04x, next offset 0x%04x\n",
+                                 cat_type, cat_size * 2, sii_offset);
+            } while (cat_type != 0xFFFF && (sii_offset < EC_MAX_SII_SIZE));
+
+            slave->sii_nwords = EC_ALIGN_UP(sii_offset + 1, 2);
+
+            slave->sii_image = ec_osal_malloc(slave->sii_nwords * 2);
+            if (!slave->sii_image) {
+                EC_LOG_ERR("Failed to allocate memory for SII on slave %u\n", slave->index);
+                return;
+            }
+            memset(slave->sii_image, 0, slave->sii_nwords * 2);
+
+            // Read full SII and parse it
+            ret = ec_sii_read(slave, datagram, 0x0000, (uint32_t *)slave->sii_image, slave->sii_nwords * 2);
+            if (ret < 0) {
+                EC_SLAVE_LOG_ERR("Failed to read SII category header on slave %u\n", slave->index);
+                return;
+            }
+
+            slave->sii.aliasaddr =
+                EC_READ_U16(slave->sii_image + 0x0004);
+            slave->effective_alias = slave->sii.aliasaddr;
+            slave->sii.vendor_id =
+                EC_READ_U32(slave->sii_image + 0x0008);
+            slave->sii.product_code =
+                EC_READ_U32(slave->sii_image + 0x000A);
+            slave->sii.revision_number =
+                EC_READ_U32(slave->sii_image + 0x000C);
+            slave->sii.serial_number =
+                EC_READ_U32(slave->sii_image + 0x000E);
+            slave->sii.boot_rx_mailbox_offset =
+                EC_READ_U16(slave->sii_image + 0x0014);
+            slave->sii.boot_rx_mailbox_size =
+                EC_READ_U16(slave->sii_image + 0x0015);
+            slave->sii.boot_tx_mailbox_offset =
+                EC_READ_U16(slave->sii_image + 0x0016);
+            slave->sii.boot_tx_mailbox_size =
+                EC_READ_U16(slave->sii_image + 0x0017);
+            slave->sii.std_rx_mailbox_offset =
+                EC_READ_U16(slave->sii_image + 0x0018);
+            slave->sii.std_rx_mailbox_size =
+                EC_READ_U16(slave->sii_image + 0x0019);
+            slave->sii.std_tx_mailbox_offset =
+                EC_READ_U16(slave->sii_image + 0x001A);
+            slave->sii.std_tx_mailbox_size =
+                EC_READ_U16(slave->sii_image + 0x001B);
+            slave->sii.mailbox_protocols =
+                EC_READ_U16(slave->sii_image + 0x001C);
+
+            EC_ASSERT_MSG(slave->sii.mailbox_protocols & EC_MBXPROT_COE, "Slave %u must support COE\n", slave->index);
+            EC_SLAVE_LOG_INFO("Slave %u mbxprot support: %s\n", slave->index, ec_mbox_protocol_string(slave->sii.mailbox_protocols));
+
+            cat_data = slave->sii_image + EC_FIRST_SII_CATEGORY_OFFSET;
+
+            while (EC_READ_U16(cat_data) != 0xFFFF) {
+                cat_type = EC_READ_U16(cat_data);     // category type
+                cat_size = EC_READ_U16(cat_data + 1); // category size
+                cat_data += 2;
+
+                EC_SLAVE_LOG_DBG("Parsing category type 0x%04x with size 0x%04x\n",
+                                 cat_type, cat_size * 2);
+
+                switch (cat_type) {
+                    case EC_SII_TYPE_STRINGS:
+                        ret = ec_slave_fetch_sii_strings(slave, (uint8_t *)cat_data, cat_size * 2);
+                        if (ret < 0) {
+                            EC_SLAVE_LOG_ERR("Failed to fetch SII strings on slave %u\n", slave->index);
+                            return;
+                        }
+                        break;
+                    case EC_SII_TYPE_GENERAL:
+                        slave->sii.has_general = true;
+                        ec_memcpy(&slave->sii.general, cat_data, sizeof(ec_sii_general_t));
+                        break;
+                    case EC_SII_TYPE_FMMU:
+                        break;
+                    case EC_SII_TYPE_SM:
+                        slave->sm_count = (cat_size * 2) / sizeof(ec_sii_sm_t);
+
+                        EC_ASSERT_MSG(slave->sm_count >= 4, "Slave %u has less than 4 sync managers\n", slave->index);
+
+                        for (uint8_t i = 0; i < slave->sm_count; i++) {
+                            ec_sii_sm_t *sm = (ec_sii_sm_t *)((uint8_t *)cat_data + i * sizeof(ec_sii_sm_t));
+
+                            slave->sm_info[i].physical_start_address = sm->physical_start_address;
+                            slave->sm_info[i].length = sm->length;
+                            slave->sm_info[i].control = sm->control;
+                            slave->sm_info[i].enable = sm->active;
+                        }
+                        break;
+                    case EC_SII_TYPE_TXPDO:
+                        break;
+                    case EC_SII_TYPE_RXPDO:
+                        break;
+                    case EC_SII_TYPE_DC:
+                        break;
+                    default:
+                        EC_SLAVE_LOG_WRN("Unknown SII category type 0x%04x\n", cat_type);
+                        break;
+                }
+
+                cat_data += cat_size;
+            }
+
+            EC_SLAVE_LOG_INFO("Slave %u parse eeprom success\n", slave->index);
+
+            ret = ec_slave_config(master, slave);
+            if (ret < 0) {
+                EC_SLAVE_LOG_ERR("Failed to configure slave %u\n", slave->index);
+                return;
+            }
+        }
+
+        EC_LOG_INFO("Bus scanning completed in %u ms\n", (unsigned int)((jiffies - scan_jiffies) / 1000));
+        master->scan_done = true;
+
+        ec_master_calc_dc(master);
+    }
+
+    if (master->slave_count) {
+        ec_master_scan_slaves_state(master);
+    }
+}
+
+char *ec_slave_get_sii_string(const ec_slave_t *slave, uint32_t index)
+{
+    if (!index--)
+        return NULL;
+
+    if (index >= slave->sii.string_count) {
+        EC_LOG_ERR("String %u not found\n", index);
+        return NULL;
+    }
+
+    return slave->sii.strings[index];
+}