├── module_ethercat ├── module_build_info └── src │ ├── ethercat.xc │ ├── pduhandler.h │ ├── ethercat.h │ ├── frame.h │ ├── frameAssembly.S │ ├── frame.xc │ └── pduhandler.xc ├── .gitignore ├── doc ├── summary.rst ├── photo-4-slices.jpg ├── index.rst ├── api.rst ├── Makefile ├── usage.rst ├── design-notes.rst └── threads.svg ├── testbench ├── Makefile └── src │ └── stimulus.c ├── app_ethercat_slice ├── src │ ├── waitFor.S │ └── main.xc └── Makefile ├── app_ethercat_singlecore ├── src │ ├── waitFor.S │ └── main.xc └── Makefile ├── app_ethercat_test ├── Makefile └── src │ ├── waitFor.S │ └── main.xc ├── app_master_harness ├── Makefile └── src │ └── ping.xc ├── visualiseTrace.sh ├── README.rst ├── LICENSE.txt ├── Makefile ├── .project └── .cproject /module_ethercat/module_build_info: -------------------------------------------------------------------------------- 1 | MODULE_XCC_FLAGS = $(XCC_FLAGS) -O3 -g 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | */.build*/* 2 | */bin/* 3 | *.o 4 | *.xe 5 | *.vcd 6 | *.xi 7 | *.i 8 | *.a 9 | -------------------------------------------------------------------------------- /doc/summary.rst: -------------------------------------------------------------------------------- 1 | Ethercat 2 | ======== 3 | 4 | module_ethercat 5 | --------------- 6 | 7 | -------------------------------------------------------------------------------- /doc/photo-4-slices.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcore/sc_ethercat/HEAD/doc/photo-4-slices.jpg -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | Ethercat 2 | -------- 3 | 4 | .. toctree:: 5 | 6 | summary 7 | design-notes 8 | usage 9 | api 10 | -------------------------------------------------------------------------------- /testbench/Makefile: -------------------------------------------------------------------------------- 1 | test: bin/tester 2 | bin/tester "$(TFLAG) ../app_ethercat_test/bin/XK-1/app_ethercat_test.xe" 3 | 4 | bin/tester: src/stimulus.c 5 | if [ ! -d bin ]; then mkdir bin; fi 6 | cc -o bin/tester -std=c99 -m32 -I $(XMOS_TOOL_PATH)/include src/stimulus.c $(XMOS_TOOL_PATH)/lib/libxsidevice.so 7 | -------------------------------------------------------------------------------- /app_ethercat_slice/src/waitFor.S: -------------------------------------------------------------------------------- 1 | .section .dp.bss,"awd",@nobits 2 | 3 | .align 4 4 | .globl destinationsCore1 5 | .globl destinationsCore1_ 6 | .globl destinationsCore0 7 | .globl destinationsCore0_ 8 | destinationsCore0: 9 | destinationsCore0_: 10 | .word 0,0,0 11 | destinationsCore1: 12 | destinationsCore1_: 13 | .word 0,0,0 14 | -------------------------------------------------------------------------------- /app_ethercat_singlecore/src/waitFor.S: -------------------------------------------------------------------------------- 1 | .section .dp.bss,"awd",@nobits 2 | 3 | .align 4 4 | .globl destinationsCore1 5 | .globl destinationsCore1_ 6 | .globl destinationsCore0 7 | .globl destinationsCore0_ 8 | destinationsCore0: 9 | destinationsCore0_: 10 | .word 0,0,0 11 | destinationsCore1: 12 | destinationsCore1_: 13 | .word 0,0,0 14 | -------------------------------------------------------------------------------- /doc/api.rst: -------------------------------------------------------------------------------- 1 | API 2 | === 3 | 4 | Internal 5 | -------- 6 | 7 | This section contains internal documentation - that is the bits that make 8 | up the ethercat controller. 9 | 10 | .. doxygenstruct:: mii_ethercat_ports 11 | 12 | .. doxygenfunction:: ethercat_port_init 13 | .. doxygenfunction:: rxProcessS 14 | .. doxygenfunction:: txProcess 15 | 16 | .. doxygenfunction:: frameProcess 17 | -------------------------------------------------------------------------------- /app_ethercat_test/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = XC-3 2 | APP_NAME = 3 | XCC_FLAGS = -O2 -g 4 | USED_MODULES = module_ethercat module_ethernet_smi 5 | 6 | #============================================================================= 7 | # The following part of the Makefile includes the common build infrastructure 8 | # for compiling XMOS applications. You should not need to edit below here. 9 | 10 | XMOS_MAKE_PATH ?= ../.. 11 | include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common 12 | -------------------------------------------------------------------------------- /app_master_harness/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = XC-2 2 | APP_NAME = 3 | XCC_FLAGS = -g -O2 -fsubword-select 4 | USED_MODULES = module_mii_singlethread module_ethernet_smi 5 | 6 | #============================================================================= 7 | # The following part of the Makefile includes the common build infrastructure 8 | # for compiling XMOS applications. You should not need to edit below here. 9 | 10 | XMOS_MAKE_PATH ?= ../.. 11 | include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common 12 | -------------------------------------------------------------------------------- /app_ethercat_slice/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = xp-skc-l2-single 2 | APP_NAME = 3 | XCC_FLAGS = -O2 -g -DSMI_MDC_BIT=1 -DSMI_MDIO_BIT=0 4 | USED_MODULES = module_ethercat module_ethernet_smi module_slicekit_xn 5 | 6 | #============================================================================= 7 | # The following part of the Makefile includes the common build infrastructure 8 | # for compiling XMOS applications. You should not need to edit below here. 9 | 10 | XMOS_MAKE_PATH ?= ../.. 11 | include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common 12 | -------------------------------------------------------------------------------- /app_ethercat_singlecore/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = xp-skc-l2-single 2 | APP_NAME = 3 | XCC_FLAGS = -O2 -g -DSMI_MDC_BIT=1 -DSMI_MDIO_BIT=0 4 | USED_MODULES = module_ethercat module_ethernet_smi module_slicekit_xn 5 | 6 | #============================================================================= 7 | # The following part of the Makefile includes the common build infrastructure 8 | # for compiling XMOS applications. You should not need to edit below here. 9 | 10 | XMOS_MAKE_PATH ?= ../.. 11 | include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common 12 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | SPHINX_PROJECT_NAME=Ethercat 2 | VERSION=0v1 3 | DOXYGEN_DIRS=../../sc_ethercat/module_ethercat 4 | SOURCE_INCLUDE_DIRS=../../sc_ethercat 5 | XDOC_DIR ?= ../../xdoc 6 | include $(XDOC_DIR)/Makefile.inc 7 | 8 | all: html pdf 9 | @if [ ! -d ../../sc_ethercat_gh_pages ] ; then echo '**** no gh_pages checked out ****'; exit 0; else cp -r _build/html/* ../../sc_ethercat_gh_pages/; cp -r _build/html/.doctrees ../../sc_ethercat_gh_pages/; echo 'HTML files copied to sc_ethercat_gh_pages'; echo 'Now go to sc_ethercat_gh_pages, commit, and push to publish the documentation'; fi 10 | -------------------------------------------------------------------------------- /module_ethercat/src/ethercat.xc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, XMOS Ltd, All rights reserved 2 | // This software is freely distributable under a derivative of the 3 | // University of Illinois/NCSA Open Source License posted in 4 | // LICENSE.txt and at 5 | 6 | #include 7 | #include "ethercat.h" 8 | 9 | void ethercat_port_init(mii_ethercat_ports &m) { 10 | configure_clock_src(m.clk_mii_rx, m.p_mii_rxclk); 11 | configure_clock_src(m.clk_mii_tx, m.p_mii_txclk); 12 | 13 | set_clock_fall_delay(m.clk_mii_tx, 7); // NEEDED? 14 | 15 | configure_in_port_strobed_slave(m.p_mii_rxd, m.p_mii_rxdv, m.clk_mii_rx); 16 | configure_out_port_strobed_master(m.p_mii_txd, m.p_mii_txen, m.clk_mii_tx, 0); 17 | 18 | start_clock(m.clk_mii_rx); 19 | start_clock(m.clk_mii_tx); 20 | } 21 | -------------------------------------------------------------------------------- /doc/usage.rst: -------------------------------------------------------------------------------- 1 | Usage 2 | ----- 3 | 4 | The app_ethercat_test application should be run on any number of XC-3 nodes 5 | (to be replaced with Ethernet-slice when available) conencted to each 6 | other. 7 | 8 | * The left conncetor on the XC-3 is the main connector, the right 9 | connector can be used for chaining. 10 | 11 | * The Ethernet connected to slice 0-0 is the main connector, the other four 12 | can be used for building a chain (or a tree!) 13 | 14 | * An ethercat master needs to be inserted in the chain, connected to the 15 | first Ethercat slave. The master is in app_master_harness and is designed 16 | to run on an XC-2 (to be replaced by an slice too). The master sends an 17 | Ethercat packet every second, and prints it out no return, including the 18 | round-trip-time in 10 ns ticks. 19 | -------------------------------------------------------------------------------- /module_ethercat/src/pduhandler.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, XMOS Ltd, All rights reserved 2 | // This software is freely distributable under a derivative of the 3 | // University of Illinois/NCSA Open Source License posted in 4 | // LICENSE.txt and at 5 | 6 | /** Function that processes all frames. Frames are streamed in over fromRx 7 | * and streamed out over toTx. The data streams are opened with control 8 | * token 3 and terminated with two control tokens. One control token to 9 | * signal the CRC status (15: CRC error, 0: CRC good), and an END token. 10 | * 11 | * \param fromRx stream into the process, comes from rxProcessS() 12 | * 13 | * \param toTx stream into the process, goes to txProcess() 14 | * 15 | * \param destination channel end where to send to. 16 | * 17 | * \param memory array of words that contains the Ethercat memory. 18 | */ 19 | void frameProcess(streaming chanend fromRx, streaming chanend toTx, 20 | int &destination, short memory[]); 21 | -------------------------------------------------------------------------------- /app_ethercat_test/src/waitFor.S: -------------------------------------------------------------------------------- 1 | .globl waitFor 2 | .globl waitFor.nstackwords 3 | .linkset waitFor.nstackwords, 0 4 | .globl waitFor.chanends 5 | .linkset waitFor.chanends, 0 6 | .globl waitFor.timers 7 | .linkset waitFor.timers, 0 8 | 9 | .text 10 | .align 4 11 | waitFor: 12 | ldw r11, r0[0] 13 | bf r11, waitFor 14 | retsp 0 15 | 16 | .globl assignDestination 17 | .globl assignDestination.nstackwords 18 | .linkset assignDestination.nstackwords, 0 19 | .globl assignDestination.chanends 20 | .linkset assignDestination.chanends, 0 21 | .globl assignDestination.timers 22 | .linkset assignDestination.timers, 0 23 | 24 | .text 25 | .align 4 26 | assignDestination: 27 | stw r1, r0[0] 28 | retsp 0 29 | 30 | 31 | .section .dp.bss,"awd",@nobits 32 | .globl destinationIdentifiers 33 | .globl destinationIdentifiers_ 34 | .globl destinations 35 | .globl destinations_ 36 | destinationIdentifiers: 37 | destinationIdentifiers_: 38 | .word 0,0,0,0,0 39 | destinations: 40 | destinations_: 41 | .word 0,0,0,0,0 42 | -------------------------------------------------------------------------------- /visualiseTrace.sh: -------------------------------------------------------------------------------- 1 | make TFLAG=-t test | awk -F@ ' 2 | { 3 | if ($3 > 46900) print $0 4 | } 5 | ' > OUTN 6 | 7 | for i in 0 1 2 8 | do 9 | fgrep "]@$i" < OUTN | sed -e 's/^.*stdcore.0.//' | grep -v -e '-P-.* out' | grep -v -e '-P-.* in' | fgrep -v 'Event caus' | sed 's/[^:]*: //' | awk -F@ ' 10 | BEGIN { 11 | last = 46959; 12 | this = '$i'; 13 | if (this == 1) OUTCNT = -5 14 | if (this == 0) nextt = 2; 15 | if (this == 1) nextt = 3; 16 | if (this == 2) nextt = 1; 17 | } 18 | /^in/{ 19 | last++; 20 | while($2 > last) printf("@%d\n", last++); 21 | print "Q" this "." (++INCNT) " " $0 22 | next; 23 | } 24 | /^out/{ 25 | last++; 26 | while($2 > last) printf("@%d\n", last++); 27 | print "Q" nextt "." (++OUTCNT) " " $0 28 | next; 29 | } 30 | { 31 | last++; 32 | while($2 > last) printf("@%d\n", last++); 33 | print $0 34 | next; 35 | } 36 | END { 37 | while(last < 50100) printf("@%d\n", last++); 38 | } 39 | ' > OUT$i 40 | done 41 | join -t@ -1 2 -2 2 OUT0 OUT2 | join -t@ -1 1 -2 2 - OUT1 | tr '@' ' ' | expand -6,56,106,156 42 | rm OUT0 OUT1 OUT2 OUTN 43 | -------------------------------------------------------------------------------- /module_ethercat/src/ethercat.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, XMOS Ltd, All rights reserved 2 | // This software is freely distributable under a derivative of the 3 | // University of Illinois/NCSA Open Source License posted in 4 | // LICENSE.txt and at 5 | 6 | /** Structure containing all the ports of an MII PHY. 7 | */ 8 | typedef struct mii_ethercat_ports { 9 | clock clk_mii_rx; /**< RX Clock Block **/ 10 | clock clk_mii_tx; /**< TX Clock Block **/ 11 | 12 | port p_mii_rxclk; /**< MII RX clock wire */ 13 | in buffered port:8 p_mii_rxd; /**< MII RX data wire */ 14 | in port p_mii_rxdv; /**< MII RX data valid wire */ 15 | 16 | port p_mii_txclk; /**< MII TX clock wire */ 17 | out buffered port:8 p_mii_txd; /**< MII TX data wire */ 18 | out port p_mii_txen; /**< MII TX enable wire */ 19 | } mii_ethercat_ports; 20 | 21 | /** Function that initialises the ports and clockblocks that control the 22 | * PHY. This needs to be called once for each PHY. 23 | * 24 | * \param m port structure. 25 | */ 26 | void ethercat_port_init(mii_ethercat_ports &m); 27 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Ethercat 2 | ........ 3 | 4 | :Stable release: unreleased 5 | 6 | :Status: idea 7 | 8 | :Maintainer: https://github.com/henkmuller 9 | 10 | :Description: Feasibility study for EtherCAT 11 | 12 | 13 | Key Features 14 | ============ 15 | 16 | * Ethercat test-harness to run on XC-2 17 | 18 | * Ethercat slave to run on XC-3 19 | 20 | * Low latency MII Rx and Tx 21 | 22 | * Rudimentary EtherCAT protocol handler 23 | 24 | * Current latency of 800ns, but memory access has not been implemented yet 25 | (PHY latency adds 400 ns). Return path latency approximately 640 ns 26 | (preamble) + 400 ns (PHY) + 160 ns (processing) 27 | 28 | To Do 29 | ===== 30 | 31 | * Mailbox protocol 32 | * Network variable protocol 33 | * Memory interface. 34 | 35 | Firmware Overview 36 | ================= 37 | 38 | The module and applicatons in this repo are a feasibility study on coding 39 | an EtherCAT slave on an XS1 device. The target is either an L2 or a G4, 40 | depending on whether fast threads are required (> 100 MIPS requires an L2) 41 | and on whether a fast switch is required (the G4 switch is very low 42 | latency). 43 | 44 | Known Issues 45 | ============ 46 | 47 | * app_ethercat_test won't build (and is deprecated) 48 | 49 | Required Repositories 50 | ===================== 51 | 52 | * hw_slicekit_system git\@github.com:xcore/hw_slicekit_system.git (XN file) 53 | * sc_ethernet git\@github.com:xcore/sc_ethernet.git (SMI module) 54 | * xcommon git\@github.com:xcore/xcommon.git (build system) 55 | * xdoc git\@github.com:xcore/xdoc.git (documentation) 56 | 57 | Support 58 | ======= 59 | 60 | 61 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Software License Agreement 2 | 3 | Copyright (c) 2011, XMOS Ltd, All rights reserved. 4 | 5 | The copyright holders hereby grant to any person obtaining a copy of this software (the "Software") and/or its associated 6 | documentation files (the Documentation), the perpetual, irrevocable (except in the case of breach of this license) no-cost, 7 | royalty free, sublicensable rights to use, copy, modify, merge, publish, display, publicly perform, distribute, and/or 8 | sell copies of the Software and the Documentation, together or separately, and to permit persons to whom the Software and/or 9 | Documentation is furnished to do so, subject to the following conditions: 10 | 11 | . Redistributions of the Software in source code must retain the above copyright notice, this list of conditions and the 12 | following disclaimers. 13 | 14 | . Redistributions of the Software in binary form must reproduce the above copyright notice, this list of conditions and 15 | the following disclaimers in the documentation and/or other materials provided with the distribution. 16 | 17 | . Redistributions of the Documentation must retain the above copyright notice, this list of conditions and the following 18 | disclaimers. 19 | 20 | Neither the name of XMOS, nor the names of its contributors may be used to endorse or promote products derived from this 21 | Software or the Documentation without specific prior written permission of the copyright holder. 22 | 23 | THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 24 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 26 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR DOCUMENTATION OR THE USE OF OR OTHER 27 | DEALINGS WITH THE SOFTWARE OR DOCUMENTATION. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This Makefile acts as a composite builder for all the elements 2 | # of this repository. 3 | 4 | # It has target patterns for all, clean and test for sub-directories of the 5 | # form dir.target e.g. calling: 6 | # 7 | # xmake app_uart_demo.all 8 | # 9 | # will execute 'xmake all' in the app_uart_demo sub-directory. 10 | # 11 | # In addition the following targets are defined: 12 | # 13 | # all: 14 | # 15 | # This target will build all the applications listed in the BUILD_SUBDIRS 16 | # variable. 17 | # 18 | # plugins: 19 | # 20 | # This target will build all the plugins listed in the PLUGIN_SUBDIRS 21 | # variable 22 | # 23 | # clean: 24 | # 25 | # This target will clean all the applications listed in the BUILD_SUBDIRS 26 | # variable. 27 | # 28 | # clean_plugins: 29 | # 30 | # This target will clean all the plugins listed in the PLUGIN_SUBDIRS 31 | # variable. 32 | # 33 | # test: 34 | # 35 | # This target will make the test make target in all the directories 36 | # listed in TEST_SUBDIRS. 37 | # 38 | 39 | 40 | # This variable should contain a space separated list of all 41 | # the directories containing buildable applications (usually 42 | # prefixed with the app_ prefix) 43 | BUILD_SUBDIRS = app_ethercat_test 44 | 45 | # This variable should contain a space separated list of all 46 | # the directories containing buildable plugins (usually 47 | # prefixed with the plugin_ prefix) 48 | PLUGIN_SUBDIRS = 49 | 50 | # This variable should contain a space separated list of all 51 | # the directories containing applications with a 'test' make target 52 | TEST_SUBDIRS = testbench 53 | 54 | # Provided that the above variables are set you shouldn't need to modify 55 | # the targets below here. 56 | 57 | %.all: 58 | cd $* && xmake all 59 | 60 | %.clean: 61 | cd $* && xmake clean 62 | 63 | %.test: 64 | cd $* && xmake test 65 | 66 | all: $(foreach x, $(BUILD_SUBDIRS), $x.all) 67 | plugins: $(foreach x, $(PLUGIN_SUBDIRS), $x.all) 68 | clean: $(foreach x, $(BUILD_SUBDIRS), $x.clean) 69 | clean_plugins: $(foreach x, $(PLUGIN_SUBDIRS), $x.clean) 70 | test: $(foreach x, $(TEST_SUBDIRS), $x.test) 71 | -------------------------------------------------------------------------------- /module_ethercat/src/frame.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, XMOS Ltd, All rights reserved 2 | // This software is freely distributable under a derivative of the 3 | // University of Illinois/NCSA Open Source License posted in 4 | // LICENSE.txt and at 5 | 6 | 7 | /** Function that transmits data on an mii port. This function reads bytes 8 | * from an input channel, and outputs them one byte at a time. Data should 9 | * arrive fast enough to prevent a gap in the packet. 10 | * 11 | * The first token is a control token: a zero control token indicates that 12 | * the transmitter should append a CRC; a non-zero control token indicates 13 | * that a valid CRC is included at the end of the message. After the 14 | * initial control token, a stream of data bytes are expected on the 15 | * channel, terminated by a control token. The terminating token is either 16 | * 0 or 0xF depending on whether the CRC on the incoming packet was valid 17 | * or not. If the TX function was asked to add a valid CRC, *and* the 18 | * incoming CRC was deemed invalid, then it will knacker the outgoing CRC 19 | * too. Just to be consistent. Finally a '1' control token is read closing 20 | * the stream. 21 | * 22 | * \param txData port on which data is transmitted, 23 | * must be byte buffered. 24 | * 25 | * \param fromProtocol channel on which data arrives 26 | */ 27 | void txProcess(buffered out port:8 txData, 28 | streaming chanend fromProtocol); 29 | 30 | 31 | 32 | /** Function that receives data on the master port. Only frames arriving on 33 | * this port are interpreted. This function reads byte from the input port 34 | * (guarded by the datavalid port), and outputs them on the channel. The 35 | * data stream is opened with control token 3 (indicating that a valid CRC 36 | * will eb transmitted at the end of the stream) and terminated with two 37 | * control tokens. One control token to signal the CRC status (15: CRC 38 | * error, 0: CRC good), and an END token. 39 | * 40 | * \param dataValid port that signals whether rxData contains 41 | * valid data. 42 | * 43 | * \param rxData port on which data arrives, must be byte buffered 44 | * 45 | * \param toProtocol channel end on which packet is transmitted 46 | * 47 | * \param destinationChannel channelend to use as destination - can be 48 | * modified on the fly 49 | */ 50 | void rxProcessS(in port dataValid, 51 | buffered in port:8 rxData, 52 | streaming chanend toProtocol, 53 | int &destinationChannel); 54 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | sc_ethercat 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.cdt.managedbuilder.core.genmakebuilder 10 | clean,full,incremental, 11 | 12 | 13 | ?name? 14 | 15 | 16 | 17 | org.eclipse.cdt.make.core.append_environment 18 | true 19 | 20 | 21 | org.eclipse.cdt.make.core.autoBuildTarget 22 | all 23 | 24 | 25 | org.eclipse.cdt.make.core.buildArguments 26 | -f Makefile 27 | 28 | 29 | org.eclipse.cdt.make.core.buildCommand 30 | xmake 31 | 32 | 33 | org.eclipse.cdt.make.core.buildLocation 34 | ${workspace_loc:/sc_ethercat} 35 | 36 | 37 | org.eclipse.cdt.make.core.cleanBuildTarget 38 | clean 39 | 40 | 41 | org.eclipse.cdt.make.core.contents 42 | org.eclipse.cdt.make.core.activeConfigSettings 43 | 44 | 45 | org.eclipse.cdt.make.core.enableAutoBuild 46 | false 47 | 48 | 49 | org.eclipse.cdt.make.core.enableCleanBuild 50 | true 51 | 52 | 53 | org.eclipse.cdt.make.core.enableFullBuild 54 | true 55 | 56 | 57 | org.eclipse.cdt.make.core.fullBuildTarget 58 | all 59 | 60 | 61 | org.eclipse.cdt.make.core.stopOnError 62 | true 63 | 64 | 65 | org.eclipse.cdt.make.core.useDefaultBuildCmd 66 | false 67 | 68 | 69 | 70 | 71 | org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder 72 | 73 | 74 | 75 | 76 | 77 | org.eclipse.cdt.managedbuilder.core.ScannerConfigNature 78 | org.eclipse.cdt.managedbuilder.core.managedBuildNature 79 | org.eclipse.cdt.core.cnature 80 | 81 | 82 | -------------------------------------------------------------------------------- /module_ethercat/src/frameAssembly.S: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | .globl rxProcessS 4 | .globl rxProcessS.nstackwords 5 | .linkset rxProcessS.nstackwords, 0 6 | .globl rxProcessS.chanends 7 | .linkset rxProcessS.chanends, 0 8 | .globl rxProcessS.timers 9 | .linkset rxProcessS.timers, 0 10 | 11 | // r0: dataValid 12 | // r1: rxData 13 | // r2: toProtocol 14 | // r3: &destinationChanend 15 | // r4: poly 16 | // r5: CRC 17 | // r6: 0xFFFFFFFF (correct CRC) 18 | // r7: Table with two control tokens 19 | // r11: temp 20 | 21 | .section .cp.const4 ,"acM", @progbits, 4 22 | .align 4 23 | polynomial: 24 | .word 0xEDB88320 25 | initialCRC: 26 | .word 0x9226F562 27 | controlTokenTable: 28 | .byte 0x0F, 0, 0, 0 29 | 30 | .text 31 | .align 4 32 | rxProcessS: 33 | getd r11, res[r2] // Initialise default destination 34 | stw r11, r3[0] // To current destination 35 | 36 | ldw r4, cp[polynomial] // r4 is constant - loaded once. 37 | 38 | ldap r11, dataValidVector // Set up dataValid resource 39 | setv res[r0], r11 // Point vector to entry point 40 | ldc r11, 0 // Set condition data to 0 41 | setd res[r0], r11 // Set condition itself to 42 | setc res[r0], XS1_SETC_COND_EQ // EQ: when pinseq(0) 43 | 44 | ldap r11, rxdVector // Set up data port resource 45 | setv res[r1], r11 // Point vector to entry point 46 | ldc r11, 0xD // Set condition data to 0xD 47 | setd res[r1], r11 // Set condition delayed to inside loop 48 | 49 | ldaw r11, cp[controlTokenTable] // Set r7 to point to array with two control 50 | or r7, r11, r11 51 | mkmsk r6, 32 // tokens, and set r6 to contain correct CRC. 52 | 53 | loop: 54 | clre 55 | ldw r5, cp[initialCRC] // Initialise the CRC to magic pattern 56 | setc res[r1], XS1_SETC_COND_EQ // Set condition to test on pinseq(0xD) 57 | in r11, res[r1] // Wait for the 0xD to arrive. 58 | ldw r11, r3[0] // Load the direction for this packet 59 | setd res[r2], r11 // Set packet to go there. 60 | outct res[r2], 3 // Send packet start 61 | eeu res[r0] // Enable events on both resources 62 | eeu res[r1] // And wait for byte on first one. 63 | 64 | rxdVector: 65 | in r11, res[r1] // Read byte from port 66 | outt res[r2], r11 // Send it to destination (protocol or port) 67 | crc8 r5, r11, r11, r4 // Incorporate into CRC 68 | waiteu // Wait for next byte or datavalid. 69 | 70 | dataValidVector: 71 | in r11, res[r0] // Data valid has disappeared, read it. 72 | eq r5, r5, r6 // Test if CRC is 0xFFFFFFFF, use result 73 | ld8u r5, r7[r5] // to read appropriate control-token 74 | outct res[r2], r5 // And send control token out. 75 | outct res[r2], 1 // And terminate stream. 76 | bu loop 77 | -------------------------------------------------------------------------------- /module_ethercat/src/frame.xc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, XMOS Ltd, All rights reserved 2 | // This software is freely distributable under a derivative of the 3 | // University of Illinois/NCSA Open Source License posted in 4 | // LICENSE.txt and at 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern int rx0, rx1; 12 | 13 | void rx0Process(in port rxActive, buffered in port:8 rxPort, streaming chanend toProtocol) { 14 | const register unsigned poly = 0xEDB88320; 15 | int cnt = 0; 16 | while (1) { 17 | int clockCounter, running = 1, bits; 18 | unsigned crc = 0x9226F562; 19 | rxPort when pinseq(0xD) :> void; 20 | soutct(toProtocol, 3); 21 | while(running) { 22 | char inputWord; 23 | select { 24 | case rxPort :> inputWord: 25 | toProtocol <: inputWord; 26 | crc8shr(crc, inputWord, poly); 27 | break; 28 | case rxActive when pinseq(0) :> void: 29 | soutct(toProtocol, ~crc ? 0xF : 0); 30 | running = 0; 31 | break; 32 | } 33 | } 34 | soutct(toProtocol, 1); 35 | asm("add %0, %1, 1" : "=r"(cnt) : "r" (cnt)); 36 | } 37 | } 38 | 39 | void rxNProcess(in port rxActive, buffered in port:8 rxPort, streaming chanend toProtocol) { 40 | const register unsigned poly = 0xEDB88320; 41 | int cnt = 0; 42 | while (1) { 43 | int clockCounter, running = 1, bits, txChannelEnd; 44 | unsigned crc = 0x9226F562; 45 | rxPort when pinseq(0xD) :> void; 46 | // asm("ldw %0, dp[rx1]" : "=r" (txChannelEnd)); 47 | // asm("setd res[%0],%1" :: "r" (toProtocol), "r" (txChannelEnd)); 48 | soutct(toProtocol, 3); 49 | while(running) { 50 | char inputWord; 51 | select { 52 | case rxPort :> inputWord: 53 | toProtocol <: inputWord; 54 | crc8shr(crc, inputWord, poly); 55 | break; 56 | case rxActive when pinseq(0) :> void: 57 | soutct(toProtocol, ~crc ? 0xF : 0); 58 | running = 0; 59 | break; 60 | } 61 | } 62 | soutct(toProtocol, 1); 63 | asm("add %0, %1, 1" : "=r"(cnt) : "r" (cnt)); 64 | } 65 | } 66 | 67 | void txProcess(buffered out port:8 txPort, streaming chanend fromProtocol) { 68 | register const unsigned poly = 0xEDB88320; 69 | int cnt = 0; 70 | while(1) { 71 | int clockCounter; 72 | unsigned int bytesLeft, outputWord; 73 | unsigned char outputChar; 74 | unsigned crc = 0x9226F562, incomingCrc; 75 | int keepExistingCRC; 76 | keepExistingCRC = sinct(fromProtocol); 77 | txPort <: 0x55; 78 | #pragma loop unroll 79 | for(int i = 0; i < 6; i++) { 80 | txPort <: 0x55; 81 | } 82 | fromProtocol :> outputChar; 83 | crc8shr(crc, outputChar, poly); 84 | txPort <: 0xD5; 85 | while(!stestct(fromProtocol)) { 86 | txPort <: outputChar; 87 | fromProtocol :> outputChar; 88 | crc8shr(crc, outputChar, poly); 89 | } 90 | txPort <: outputChar; 91 | if (keepExistingCRC) { 92 | sinct(fromProtocol); 93 | } else { 94 | crc32(crc, keepExistingCRC, poly); 95 | crc = ~crc; 96 | txPort <: >> crc; 97 | txPort <: >> crc; 98 | incomingCrc = sinct(fromProtocol); 99 | txPort <: >> crc; 100 | crc ^= incomingCrc<<6; 101 | txPort <: >> crc; 102 | } 103 | schkct(fromProtocol, 1); 104 | asm("add %0, %1, 1" : "=r"(cnt) : "r" (cnt)); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /testbench/src/stimulus.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, XMOS Ltd, All rights reserved 2 | // This software is freely distributable under a derivative of the 3 | // University of Illinois/NCSA Open Source License posted in 4 | // LICENSE.txt and at 5 | 6 | #include "xsidevice.h" 7 | #include 8 | #include 9 | 10 | void *xsim = 0; 11 | 12 | unsigned char packet[] = { 13 | 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xB5, 14 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 15 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 16 | 0x88, 0xA4, // Ethercat frame type 17 | 18 | 0x38, 0x10, // 11 bits length, 4 bit protocol (0x1) 19 | 20 | 0x01, // PDU frame 21 | 22 | 0x01, // Command: Auto Increment Physical Read (first PDU, length 28) 23 | 0x5A, // IDX: master index 24 | 0x00, 0x00, // ADP: auto increment address 25 | 0x01, 0x01, // ADO: physical memory address 26 | 0x10, 0x80, // Length: 16 bytes, not circulated, another PDU to follow 27 | 0x00, 0x00, // IRQ 28 | 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // DATA 29 | 0x00, 0x00, // WKC: working counter. 30 | 31 | 0x02, // Command: Auto Increment Physical Write (second PDU, length 28) 32 | 0x57, // IDX: master index 33 | 0xFF, 0x01, // ADP: auto increment address 34 | 0x30, 0x40, // ADO: physical memory address 35 | 0x10, 0x00, // Length: 16 bytes, not circulated, last PDU 36 | 0x00, 0x00, // IRQ 37 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, // DATA 38 | 0x00, 0x00, // WKC: working counter. 39 | 40 | 0x00, 0x01, 0x02, 0x03, // Ethernet CRC 41 | }; 42 | 43 | int main(int argc, char **argv) { 44 | int time = 0, clock = 0, cnt = 0, even = 0, oldready = 0, startTime = 0; 45 | XsiStatus status = xsi_create(&xsim, argv[1]); 46 | assert(status == XSI_STATUS_OK); 47 | while (status != XSI_STATUS_DONE && time < 1000000) { 48 | time++; 49 | if(time % 20 == 3) { 50 | clock = !clock; 51 | xsi_drive_port_pins(xsim, "stdcore[0]", "XS1_PORT_1A", 1, clock); 52 | if (clock == 1) { 53 | if (time > 100000) { 54 | if (cnt < sizeof(packet)) { 55 | if (cnt == 0 && !even) { 56 | startTime = time; 57 | } 58 | xsi_drive_port_pins(xsim, "stdcore[0]", "XS1_PORT_1B", 1, 1); 59 | xsi_drive_port_pins(xsim, "stdcore[0]", "XS1_PORT_4A", 0xF, 60 | even ? packet[cnt] >> 4 : packet[cnt]); 61 | if (even) { 62 | cnt++; 63 | } 64 | even = !even; 65 | } else { 66 | xsi_drive_port_pins(xsim, "stdcore[0]", "XS1_PORT_1B", 1, 0); 67 | } 68 | } 69 | } 70 | } 71 | if(time % 20 == 4) { 72 | xsi_drive_port_pins(xsim, "stdcore[0]", "XS1_PORT_1C", 1, clock); 73 | if (clock == 0) { 74 | unsigned ready; 75 | xsi_sample_port_pins(xsim, "stdcore[0]", "XS1_PORT_1D", 1, &ready); 76 | if (ready) { 77 | unsigned nibble; 78 | if (!oldready) { 79 | printf("%d ns delay <", time - startTime); 80 | oldready = 1; 81 | } 82 | xsi_sample_port_pins(xsim, "stdcore[0]", "XS1_PORT_4B", 0xF, &nibble); 83 | printf("%01x", nibble); 84 | } else { 85 | if (oldready) { 86 | printf(">\n", time); 87 | oldready = 0; 88 | } 89 | } 90 | } 91 | } 92 | if(time % 5 == 0 || time % 3 == 0) { 93 | status = xsi_clock(xsim); 94 | assert(status == XSI_STATUS_OK || status == XSI_STATUS_DONE ); 95 | } 96 | } 97 | status = xsi_terminate(xsim); 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /doc/design-notes.rst: -------------------------------------------------------------------------------- 1 | Ethercat Design Notes 2 | ===================== 3 | 4 | The prototype environment to develop Ethercat is shown below. It comprises 5 | a single L2 motherboard, with 4 ethernet "slices".The bottom left port is 6 | port 0, port 1, 2, and 3 are anticlockwise from there. The XN file included 7 | models that board with all four slices, but missing ports are automatically 8 | skipped. 9 | 10 | .. figure:: photo-4-slices.* 11 | :width: 50% 12 | 13 | Photo of prototype ethercat system with 4 ports. 14 | 15 | Philosophy 16 | ---------- 17 | 18 | Ethercat requires a short latency between incoming packet and outgoing 19 | packet. The key to meeting this latency is to use a different design method 20 | then usual for input and output. 21 | 22 | Traditionally one would use 32 bit buffered ports to transport data. A port 23 | with this level of buffering creates a 31-bit latency between a bit 24 | arriving and a bit being processed; or 310 ns. Switching to 8 bit ports 25 | creates 70ns latency instead. 26 | 27 | A second design criterion is that ports that are switched off should be 28 | bypassed. In our design we are achieving this by rerouting channel ends. 29 | The end of a channel always points to the first active transmit port. This 30 | method enables us to add no latency when skipping ports that are not in 31 | use. 32 | 33 | An Ethercat system with N ports requires the following threads: 34 | 35 | #. Data Rx (N) 36 | #. Data Tx (N). 37 | #. Frame processing thread (1) 38 | #. Topology controller (1) 39 | 40 | Data Rx and Tx 41 | -------------- 42 | 43 | Each port has a data receive and transmit thread. The receive thread reads 44 | data of the MII port and outputs it over a streaming channel. The transmit 45 | thread reads data from a streaming channel and outputs this onto the MII 46 | ports. The protocol over the channel comprises unidirectional messages from 47 | Rx to Tx that are always delivered as a whole: 48 | 49 | #. A control token, either CT(3) that means *original CRC attached* or CT(0) 50 | that means *no CRC in this stream, please compute one*. 51 | 52 | #. A stream of data bytes comprising the Ethernet frame, including the CRC 53 | if it started with CT(3). 54 | 55 | #. A control token, either CT(0) that means *original CRC was valid* or 56 | CT(15) that means *original CRC was not valid* 57 | 58 | #. An END control token. This closes the channel, enabling it to be 59 | redirected if required. 60 | 61 | The RX process will always set the destination of its output channel prior 62 | to transmitting a packet. That means that a whole packet is either 63 | transmitted to the current channel end, or a different one (if the topology 64 | changed). The RX process will always output a 3 token (CRC attached), and 65 | end with 0/15 as appropriate. 66 | 67 | The TX process will store the start token, and compute a CRC on the data on 68 | the fly, but, if it gets to the final token it takes one of three 69 | decisions: 70 | 71 | #. The start token was CT(3): we have just outputted the original CRC, and 72 | hence we discard our CRC and stop transmitting; packet complete. 73 | 74 | #. The start token was CT(0) and the end token was CT(0): compute the final 75 | CRC, and transmit it. 76 | 77 | #. The start token was CT(0) and the end token was not CT(0): compute the 78 | final CRC, xor it with the final token (invalidating the CRC), and 79 | transmit the invalid CRC. This guarantees that an invalid CRC on input 80 | results in an invalid CRC on output. 81 | 82 | 83 | Frame processing 84 | ---------------- 85 | 86 | Frame processing inputs and outputs the packet with the same channel 87 | protocol, but it replaces the initial '3' control token with a '0', and 88 | discards the last 4 btyes of the stream; discarding the CRC, and requesting 89 | the transmitter to compute a new one. This enables the frame processor to 90 | modify the data and implement mailboxes, reads, and writes. 91 | 92 | 93 | Toplogy controller 94 | ------------------ 95 | 96 | 97 | The topology controller inspects, using SMI and INT_N wires, whether the 98 | PHYs are connected to other PHYs. Given the current configuration, it 99 | computes the first connected port for each port, and sets up the 100 | destination for each Rx process. Rx process 0 is always destined to the 101 | frame processor, but the FrameProcessor, Rx1, Rx2, and Rx3, communicate with 102 | whatever Tx is next. Fully populated they would transmit to Tx1, Tx2, Tx3, 103 | and Tx0; but if, say, nothing is plugged into port 2, then they would 104 | communicate with Tx1, Tx3, Null, and Tx0. 105 | 106 | This is arranged through an array in some shared memory - the words contain 107 | destination addresses, are written byt he topology controller, and are read 108 | by the Rx/FrameController threads. 109 | -------------------------------------------------------------------------------- /app_master_harness/src/ping.xc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, XMOS Ltd, All rights reserved 2 | // This software is freely distributable under a derivative of the 3 | // University of Illinois/NCSA Open Source License posted in 4 | // LICENSE.txt and at 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "smi.h" 14 | #include "miiClient.h" 15 | #include "miiDriver.h" 16 | 17 | #define ETH_CORE 2 18 | 19 | on stdcore[ETH_CORE]: port led0 = PORT_LED_2_0; 20 | on stdcore[ETH_CORE]: port led1 = PORT_LED_2_1; 21 | 22 | on stdcore[ETH_CORE]: mii_interface_t mii = 23 | { 24 | XS1_CLKBLK_1, 25 | XS1_CLKBLK_2, 26 | 27 | PORT_ETH_RXCLK, 28 | PORT_ETH_RXER, 29 | PORT_ETH_RXD, 30 | PORT_ETH_RXDV, 31 | 32 | PORT_ETH_TXCLK, 33 | PORT_ETH_TXEN, 34 | PORT_ETH_TXD, 35 | 36 | XS1_PORT_8A, 37 | }; 38 | 39 | #ifdef PORT_ETH_RST_N 40 | on stdcore[ETH_CORE]: out port p_mii_resetn = PORT_ETH_RST_N; 41 | on stdcore[ETH_CORE]: smi_interface_t smi = { 0, PORT_ETH_MDIO, PORT_ETH_MDC }; 42 | #else 43 | on stdcore[ETH_CORE]: smi_interface_t smi = { 0, PORT_ETH_RST_N_MDIO, PORT_ETH_MDC }; 44 | #endif 45 | 46 | on stdcore[ETH_CORE]: clock clk_smi = XS1_CLKBLK_5; 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | unsigned char packet[] = { 55 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 56 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 57 | 0x88, 0xA4, // Ethercat frame type 58 | 59 | 0x38, 0x10, // 11 bits length, 4 bit protocol (0x1) 60 | 61 | 0x01, // PDU frame 62 | 63 | 0x01, // Command: Auto Increment Physical Read (first PDU, length 28) 64 | 0x5A, // IDX: master index 65 | 0x00, 0x00, // ADP: auto increment address 66 | 0x01, 0x01, // ADO: physical memory address 67 | 0x10, 0x80, // Length: 16 bytes, not circulated, another PDU to follow 68 | 0x00, 0x00, // IRQ 69 | 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 70 | 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // DATA 71 | 0x00, 0x00, // WKC: working counter. 72 | 73 | 0x02, // Command: Auto Increment Physical Write (second PDU, length 28) 74 | 0x57, // IDX: master index 75 | 0xFF, 0x01, // ADP: auto increment address 76 | 0x30, 0x40, // ADO: physical memory address 77 | 0x10, 0x00, // Length: 16 bytes, not circulated, last PDU 78 | 0x00, 0x00, // IRQ 79 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 80 | 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, // DATA 81 | 0x00, 0x00, // WKC: working counter. 82 | 83 | 1,2,3,4,5,6,7,8// PADDING FOR TX. 84 | }; 85 | 86 | 87 | static int led0Status = ~0; 88 | static int led1Status = ~0; 89 | 90 | #define NAV 100 91 | 92 | void handlePacket(int a, int nBytes, int rxTime, int txTime) { 93 | static int av[NAV]; 94 | static int avc = 0; 95 | static int avsum; 96 | static int avwrapped = 0; 97 | int diff = rxTime - txTime; 98 | int avg; 99 | led0Status = ~led0Status; 100 | led0 <: led0Status; 101 | avsum -= av[avc]; 102 | av[avc] = diff; 103 | avsum += diff; 104 | avc ++; 105 | if (avc == NAV) { 106 | avc = 0; 107 | avwrapped = 1; 108 | } 109 | avg = avsum * 1000 / (avwrapped?NAV:avc); 110 | printf("txTime %10u, rxTime %10u diff %d ns Av %d.%02d ns \n", txTime, rxTime, diff * 10, avg / 100, avg % 100); 111 | #if 0 112 | for(int i = 0; i < nBytes; i++) { 113 | int v; 114 | asm("ld8u %0, %1[%2]" : "=r" (v) : "r" (a), "r" (i)); 115 | printf(" %02x", v); 116 | if (v != packet[i]) { 117 | printf(" (%02x)", packet[i]); 118 | } else { 119 | printf(" "); 120 | } 121 | if ((i & 0x7) == 0x7) { 122 | printf("\n"); 123 | } 124 | } 125 | printf("\n"); 126 | #endif 127 | } 128 | 129 | void generateEthercat(chanend cIn, chanend cOut, chanend cNotifications) { 130 | int b[3200]; 131 | struct miiData miiData; 132 | timer t; 133 | int timeout; 134 | int transmitTime; 135 | 136 | led0 <: led0Status; 137 | led1 <: led1Status; 138 | 139 | miiBufferInit(miiData, cIn, cNotifications, b, 3200); 140 | miiOutInit(cOut); 141 | 142 | t:> timeout; 143 | timeout += 200000000; 144 | while (1) { 145 | select { 146 | case miiNotified(miiData, cNotifications); 147 | case t when timerafter(timeout) :> void: 148 | timeout += 100000000; 149 | transmitTime = miiOutPacket(cOut, (packet, int[]), 0, sizeof(packet)-8); 150 | miiOutPacketDone(cOut); 151 | led1Status = ~led1Status; 152 | led1 <: led1Status; 153 | break; 154 | } 155 | while(1) { 156 | int nBytes, a, timeStamp; 157 | {a,nBytes,timeStamp} = miiGetInBuffer(miiData); 158 | if (a == 0) { 159 | break; 160 | } 161 | handlePacket(a, nBytes, timeStamp, transmitTime); 162 | miiFreeInBuffer(miiData, a); 163 | } 164 | miiRestartBuffer(miiData); 165 | } 166 | } 167 | 168 | void ethernetLayer(void) { 169 | chan cIn, cOut; 170 | chan notifications; 171 | miiInitialise(smi.p_smi_mdio, mii); 172 | smi_port_init(clk_smi, smi); 173 | eth_phy_config(1, smi); 174 | par { 175 | miiDriver(mii, cIn, cOut); 176 | generateEthercat(cIn, cOut, notifications); 177 | } 178 | } 179 | 180 | int main() { 181 | par { 182 | on stdcore[ETH_CORE]: ethernetLayer(); 183 | } 184 | return 0; 185 | } 186 | -------------------------------------------------------------------------------- /app_ethercat_test/src/main.xc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, XMOS Ltd, All rights reserved 2 | // This software is freely distributable under a derivative of the 3 | // University of Illinois/NCSA Open Source License posted in 4 | // LICENSE.txt and at 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "pduhandler.h" 11 | #include "smi.h" 12 | 13 | #define NUMBER_DYNAMIC_CHANNELS 5 14 | 15 | extern int destinationIdentifiers[NUMBER_DYNAMIC_CHANNELS]; 16 | extern int destinationIdentifiers_[NUMBER_DYNAMIC_CHANNELS]; 17 | extern int destinations[NUMBER_DYNAMIC_CHANNELS]; 18 | extern int destinations_[NUMBER_DYNAMIC_CHANNELS]; 19 | 20 | extern void waitFor(int &x); 21 | 22 | /** This structure is subtly different from the normal mii_interface_t 23 | * in that it is 8 bit buffered rather than 32 bit buffered. No need 24 | * for a facke port either. 25 | */ 26 | typedef struct mii_interface_t { 27 | clock clk_mii_rx; /**< MII RX Clock Block **/ 28 | clock clk_mii_tx; /**< MII TX Clock Block **/ 29 | 30 | in port p_mii_rxclk; /**< MII RX clock wire */ 31 | in port p_mii_rxer; /**< MII RX error wire */ 32 | in buffered port:8 p_mii_rxd; /**< MII RX data wire */ 33 | in port p_mii_rxdv; /**< MII RX data valid wire */ 34 | 35 | in port p_mii_txclk; /**< MII TX clock wire */ 36 | out port p_mii_txen; /**< MII TX enable wire */ 37 | out buffered port:8 p_mii_txd; /**< MII TX data wire */ 38 | } mii_interface_t; 39 | 40 | 41 | on stdcore[2]: mii_interface_t mii0 = { 42 | XS1_CLKBLK_1, XS1_CLKBLK_2, 43 | PORT_ETH_RXCLK_0, PORT_ETH_RXER_0, PORT_ETH_RXD_0, PORT_ETH_RXDV_0, 44 | PORT_ETH_TXCLK_0, PORT_ETH_TXEN_0, PORT_ETH_TXD_0, 45 | }; 46 | 47 | on stdcore[2]: mii_interface_t mii1 = { 48 | XS1_CLKBLK_3, XS1_CLKBLK_4, 49 | PORT_ETH_RXCLK_1, PORT_ETH_RXER_1, PORT_ETH_RXD_1, PORT_ETH_RXDV_1, 50 | PORT_ETH_TXCLK_1, PORT_ETH_TXEN_1, PORT_ETH_TXD_1, 51 | }; 52 | 53 | on stdcore[2]: out port p_mii_resetn = PORT_ETH_RST_N; 54 | on stdcore[2]: smi_interface_t smi0 = { 0x1f, PORT_ETH_MDIO_0, PORT_ETH_MDC_0 }; 55 | on stdcore[2]: smi_interface_t smi1 = { 0x1f, PORT_ETH_MDIO_1, PORT_ETH_MDC_1 }; 56 | 57 | on stdcore[2]: clock clk_smi = XS1_CLKBLK_5; 58 | 59 | void mii_port_init(mii_interface_t &m) { 60 | configure_clock_src(m.clk_mii_rx, m.p_mii_rxclk); 61 | configure_clock_src(m.clk_mii_tx, m.p_mii_txclk); 62 | 63 | set_clock_fall_delay(m.clk_mii_tx, 7); // NEEDED? 64 | 65 | configure_in_port_strobed_slave(m.p_mii_rxd, m.p_mii_rxdv, m.clk_mii_rx); 66 | configure_out_port_strobed_master(m.p_mii_txd, m.p_mii_txen, m.clk_mii_tx, 0); 67 | 68 | start_clock(m.clk_mii_rx); 69 | start_clock(m.clk_mii_tx); 70 | } 71 | 72 | #define RESET_TIMER_DELAY 50000 73 | 74 | void phy_reset(out port p_mii_resetn, timer tmr) { 75 | unsigned int resetTime; 76 | 77 | p_mii_resetn <: 0; 78 | tmr :> resetTime; 79 | resetTime += RESET_TIMER_DELAY; 80 | tmr when timerafter(resetTime) :> void; 81 | 82 | p_mii_resetn <: ~0; 83 | tmr :> resetTime; 84 | resetTime += RESET_TIMER_DELAY; 85 | tmr when timerafter(resetTime) :> void; 86 | } 87 | 88 | void linkConnectionChecker(smi_interface_t &smi0, smi_interface_t &smi1) { 89 | timer t; 90 | int u, w0, w1; 91 | int forwarding; 92 | for(int i = 0; i < 3; i++) { 93 | waitFor(destinationIdentifiers_[i]); 94 | destinations_[i] = destinationIdentifiers_[i]; 95 | } 96 | forwarding = 0; 97 | t :> u; 98 | while(1) { 99 | u += 150000000; 100 | t when timerafter(u) :> void; 101 | w0 = smiCheckLinkState(smi0); 102 | w1 = smiCheckLinkState(smi1); 103 | // printintln(20000 + w0 * 100 + w1); 104 | if (w1) { 105 | if (!forwarding) { 106 | forwarding = 1; 107 | destinations_[0] = destinationIdentifiers[1]; 108 | destinations_[1] = destinationIdentifiers[0]; 109 | printstr("Forwarding enabled\n"); 110 | } 111 | } else { 112 | if (forwarding) { 113 | forwarding = 0; 114 | destinations_[0] = destinationIdentifiers[0]; 115 | destinations_[1] = destinationIdentifiers[0]; 116 | printstr("Forwarding disabled\n"); 117 | } 118 | } 119 | } 120 | } 121 | 122 | 123 | void ethernetCore() { 124 | streaming chan rx0ToProto, protoToTx1, rx1ToTx0; 125 | timer t; 126 | 127 | phy_reset(p_mii_resetn, t); 128 | mii_port_init(mii0); 129 | mii_port_init(mii1); 130 | printstr("Inited\n"); 131 | configure_clock_ref (clk_smi, 10); 132 | configure_out_port_no_ready(smi0.p_smi_mdc, clk_smi, 1); 133 | configure_out_port_no_ready(smi1.p_smi_mdc, clk_smi, 1); 134 | start_clock (clk_smi); 135 | 136 | eth_phy_config(1, smi0); 137 | eth_phy_config(1, smi1); 138 | 139 | printhexln(eth_phy_id(smi0)); 140 | printhexln(eth_phy_id(smi1)); 141 | 142 | par { 143 | #if 0 144 | rx0Process(mii0.p_mii_rxdv, mii0.p_mii_rxd, rx0ToProto); 145 | frameProcess(rx0ToProto, protoToTx1, txFrame); 146 | txProcess(mii1.p_mii_txd, protoToTx1, tx1); 147 | rxNProcess(mii1.p_mii_rxdv, mii1.p_mii_rxd, rx1ToTx0); 148 | txProcess(mii0.p_mii_txd, rx1ToTx0, tx0); 149 | #else 150 | { 151 | waitFor(destinations[2]); 152 | rxProcessS(mii0.p_mii_rxdv, mii0.p_mii_rxd, rx0ToProto, destinations[2]); 153 | } 154 | { 155 | waitFor(destinations[0]); 156 | frameProcess(rx0ToProto, protoToTx1, destinationIdentifiers[2], destinations[0]); 157 | } 158 | txProcess(mii1.p_mii_txd, protoToTx1, destinationIdentifiers[1]); 159 | { 160 | waitFor(destinations[1]); 161 | rxProcessS(mii1.p_mii_rxdv, mii1.p_mii_rxd, rx1ToTx0, destinations[1]); 162 | } 163 | txProcess(mii0.p_mii_txd, rx1ToTx0, destinationIdentifiers[0]); 164 | #endif 165 | linkConnectionChecker(smi0, smi1); 166 | } 167 | } 168 | 169 | int main(void) { 170 | par { 171 | on stdcore[2]: ethernetCore(); 172 | } 173 | return 0; 174 | } 175 | -------------------------------------------------------------------------------- /app_ethercat_slice/src/main.xc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011, XMOS Ltd, All rights reserved 2 | // This software is freely distributable under a derivative of the 3 | // University of Illinois/NCSA Open Source License posted in 4 | // LICENSE.txt and at 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "ethercat.h" 11 | #include "pduhandler.h" 12 | #include "smi.h" 13 | 14 | #define ETHCORE 0 15 | 16 | #define NUMBER_DYNAMIC_CHANNELS 2 17 | 18 | extern int destinationsCore0[NUMBER_DYNAMIC_CHANNELS]; 19 | extern int destinationsCore0_[NUMBER_DYNAMIC_CHANNELS]; 20 | extern int destinationsCore1[NUMBER_DYNAMIC_CHANNELS]; 21 | extern int destinationsCore1_[NUMBER_DYNAMIC_CHANNELS]; 22 | 23 | extern void waitFor(int &x); 24 | 25 | on stdcore[0]: out port disableFlash = PORT_SPI_DISABLE; 26 | 27 | on stdcore[0]: mii_ethercat_ports mii0 = { 28 | XS1_CLKBLK_3, XS1_CLKBLK_4, 29 | PORT_ETH_RXCLK_0, PORT_ETH_RXD_0, PORT_ETH_RXDV_0, 30 | PORT_ETH_TXCLK_0, PORT_ETH_TXD_0, PORT_ETH_TXEN_0, 31 | }; 32 | 33 | on stdcore[0]: mii_ethercat_ports mii1 = { 34 | XS1_CLKBLK_1, XS1_CLKBLK_2, 35 | PORT_ETH_RXCLK_1, PORT_ETH_RXD_1, PORT_ETH_RXDV_1, 36 | PORT_ETH_TXCLK_1, PORT_ETH_TXD_1, PORT_ETH_TXEN_1, 37 | }; 38 | 39 | on stdcore[1]: mii_ethercat_ports mii2 = { 40 | XS1_CLKBLK_3, XS1_CLKBLK_4, 41 | PORT_ETH_RXCLK_2, PORT_ETH_RXD_2, PORT_ETH_RXDV_2, 42 | PORT_ETH_TXCLK_2, PORT_ETH_TXD_2, PORT_ETH_TXEN_2, 43 | }; 44 | 45 | on stdcore[1]: mii_ethercat_ports mii3 = { 46 | XS1_CLKBLK_1, XS1_CLKBLK_2, 47 | PORT_ETH_RXCLK_3, PORT_ETH_RXD_3, PORT_ETH_RXDV_3, 48 | PORT_ETH_TXCLK_3, PORT_ETH_TXD_3, PORT_ETH_TXEN_3, 49 | }; 50 | 51 | on stdcore[0]: smi_interface_t smi0 = { 0x80000000, 52 | PORT_ETH_MDIOFAKE_0, 53 | PORT_ETH_MDIOC_0 }; 54 | on stdcore[0]: smi_interface_t smi1 = { 0, 55 | PORT_ETH_MDIO_1, 56 | PORT_ETH_MDC_1 }; 57 | on stdcore[1]: smi_interface_t smi2 = { 0x80000000, 58 | PORT_ETH_MDIOFAKE_2, 59 | PORT_ETH_MDIOC_2 }; 60 | on stdcore[1]: smi_interface_t smi3 = { 0, 61 | PORT_ETH_MDIO_3, 62 | PORT_ETH_MDC_3 }; 63 | 64 | on stdcore[0]: clock clk_smi0 = XS1_CLKBLK_5; 65 | on stdcore[1]: clock clk_smi2 = XS1_CLKBLK_5; 66 | 67 | int phyPresentAndUp(smi_interface_t &smi) { 68 | int basicStatus = smi_reg(smi, 1, 0, 1); 69 | return (basicStatus & 0x12) == 0 && (basicStatus & 4) == 4; 70 | } 71 | 72 | #define NUMBER_PORTS 4 73 | 74 | void connectionHandler(chanend from0, chanend from1) { 75 | int portIsConnected[NUMBER_PORTS]; 76 | int portIdentifiers[NUMBER_PORTS]; 77 | int destinations[NUMBER_PORTS]; 78 | 79 | for(int i = 0; i < NUMBER_PORTS; i++) { 80 | portIsConnected[i] = 1; 81 | } 82 | 83 | from0 :> portIdentifiers[0]; 84 | from0 :> portIdentifiers[1]; 85 | from1 :> portIdentifiers[2]; 86 | from1 :> portIdentifiers[3]; 87 | 88 | while(1) { 89 | from0 :> portIsConnected[0]; 90 | from0 :> portIsConnected[1]; 91 | from1 :> portIsConnected[2]; 92 | from1 :> portIsConnected[3]; 93 | portIsConnected[0] = 1; // Assume connected. Better be. 94 | 95 | for(int i = 0; i < NUMBER_PORTS; i++) { 96 | if (!portIsConnected[i]) { 97 | destinations[i] = portIdentifiers[i] | 0xff00; // null chan 98 | continue; 99 | } 100 | for(int j = i+1; j < NUMBER_PORTS+1; j++) { 101 | int portNr; 102 | if (j == NUMBER_PORTS) { 103 | portNr = 0; 104 | } else { 105 | portNr = j; 106 | } 107 | if (portIsConnected[portNr]) { 108 | destinations[i] = portIdentifiers[portNr]; 109 | break; 110 | } 111 | } 112 | } 113 | from0 <: destinations[0]; 114 | from0 <: destinations[1]; 115 | from1 <: destinations[2]; 116 | from1 <: destinations[3]; 117 | } 118 | } 119 | 120 | void linkConnectionChecker(smi_interface_t &smi0, smi_interface_t &smi1, 121 | chanend toMaster, 122 | int destinations_[2], 123 | chanend init0, chanend init1) { 124 | timer t; 125 | int u, w0, w1, dest; 126 | 127 | init0 :> dest; 128 | toMaster <: dest; 129 | init1 :> dest; 130 | toMaster <: dest; 131 | 132 | t :> u; 133 | 134 | while(1) { 135 | u += 150000000; 136 | t when timerafter(u) :> void; 137 | w0 = phyPresentAndUp(smi0); 138 | w1 = phyPresentAndUp(smi1); 139 | toMaster <: w0; 140 | toMaster <: w1; 141 | toMaster :> destinations_[0]; 142 | toMaster :> destinations_[1]; 143 | } 144 | } 145 | 146 | void publishDestination(chanend init, streaming chanend dest) { 147 | int i; 148 | asm("add %0, %1, 0" : "=r" (i) : "r" (dest)); 149 | init <: i; 150 | } 151 | 152 | void ethernetCore(mii_ethercat_ports &mii0, mii_ethercat_ports &mii1, 153 | smi_interface_t &smi0, smi_interface_t &smi1, 154 | clock clk_smi, out port ?disableFlash, 155 | chanend toMaster, 156 | int destinations[2], int destinations_[2], 157 | int isProcessingNode) { 158 | streaming chan protoToTx1, rx1ToTx0; 159 | timer tmr; 160 | int resetTime; 161 | chan init0, init1; 162 | int tempDestination; 163 | short memory[100]; 164 | 165 | if (!isnull(disableFlash)) { 166 | disableFlash <: 0; 167 | } 168 | 169 | tmr :> resetTime; 170 | resetTime += 50000000; 171 | tmr when timerafter(resetTime) :> void; 172 | 173 | ethercat_port_init(mii0); 174 | ethercat_port_init(mii1); 175 | configure_clock_ref (clk_smi, 10); 176 | configure_out_port_no_ready(smi0.p_smi_mdc, clk_smi, 1); 177 | configure_out_port_no_ready(smi1.p_smi_mdc, clk_smi, 1< 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "ethercat.h" 11 | #include "pduhandler.h" 12 | #include "smi.h" 13 | 14 | #define ETHCORE 0 15 | 16 | #define NUMBER_DYNAMIC_CHANNELS 2 17 | 18 | extern int destinationsCore0[NUMBER_DYNAMIC_CHANNELS]; 19 | extern int destinationsCore0_[NUMBER_DYNAMIC_CHANNELS]; 20 | extern int destinationsCore1[NUMBER_DYNAMIC_CHANNELS]; 21 | extern int destinationsCore1_[NUMBER_DYNAMIC_CHANNELS]; 22 | 23 | extern void waitFor(int &x); 24 | 25 | on stdcore[0]: out port disableFlash = PORT_SPI_DISABLE; 26 | 27 | on stdcore[1]: mii_ethercat_ports mii0 = { 28 | XS1_CLKBLK_3, XS1_CLKBLK_4, 29 | PORT_ETH_RXCLK_0, PORT_ETH_RXD_0, PORT_ETH_RXDV_0, 30 | PORT_ETH_TXCLK_0, PORT_ETH_TXD_0, PORT_ETH_TXEN_0, 31 | }; 32 | 33 | on stdcore[1]: mii_ethercat_ports mii1 = { 34 | XS1_CLKBLK_1, XS1_CLKBLK_2, 35 | PORT_ETH_RXCLK_1, PORT_ETH_RXD_1, PORT_ETH_RXDV_1, 36 | PORT_ETH_TXCLK_1, PORT_ETH_TXD_1, PORT_ETH_TXEN_1, 37 | }; 38 | 39 | on stdcore[0]: mii_ethercat_ports mii2 = { 40 | XS1_CLKBLK_3, XS1_CLKBLK_4, 41 | PORT_ETH_RXCLK_2, PORT_ETH_RXD_2, PORT_ETH_RXDV_2, 42 | PORT_ETH_TXCLK_2, PORT_ETH_TXD_2, PORT_ETH_TXEN_2, 43 | }; 44 | 45 | on stdcore[0]: mii_ethercat_ports mii3 = { 46 | XS1_CLKBLK_1, XS1_CLKBLK_2, 47 | PORT_ETH_RXCLK_3, PORT_ETH_RXD_3, PORT_ETH_RXDV_3, 48 | PORT_ETH_TXCLK_3, PORT_ETH_TXD_3, PORT_ETH_TXEN_3, 49 | }; 50 | 51 | on stdcore[1]: smi_interface_t smi0 = { 0x80000000, 52 | PORT_ETH_MDIOFAKE_0, 53 | PORT_ETH_MDIOC_0 }; 54 | on stdcore[1]: smi_interface_t smi1 = { 0, 55 | PORT_ETH_MDIO_1, 56 | PORT_ETH_MDC_1 }; 57 | on stdcore[0]: smi_interface_t smi2 = { 0x80000000, 58 | PORT_ETH_MDIOFAKE_2, 59 | PORT_ETH_MDIOC_2 }; 60 | on stdcore[0]: smi_interface_t smi3 = { 0, 61 | PORT_ETH_MDIO_3, 62 | PORT_ETH_MDC_3 }; 63 | 64 | on stdcore[1]: clock clk_smi0 = XS1_CLKBLK_5; 65 | on stdcore[0]: clock clk_smi2 = XS1_CLKBLK_5; 66 | 67 | int phyPresentAndUp(smi_interface_t &smi) { 68 | int basicStatus = smi_reg(smi, 1, 0, 1); 69 | return (basicStatus & 0x12) == 0 && (basicStatus & 4) == 4; 70 | } 71 | 72 | #define NUMBER_PORTS 4 73 | 74 | void connectionHandler(chanend from0, chanend from1) { 75 | int portIsConnected[NUMBER_PORTS]; 76 | int portIdentifiers[NUMBER_PORTS]; 77 | int destinations[NUMBER_PORTS]; 78 | 79 | for(int i = 0; i < NUMBER_PORTS; i++) { 80 | portIsConnected[i] = 1; 81 | } 82 | 83 | from0 :> portIdentifiers[0]; 84 | from0 :> portIdentifiers[1]; 85 | from1 :> portIdentifiers[2]; 86 | from1 :> portIdentifiers[3]; 87 | 88 | while(1) { 89 | from0 :> portIsConnected[0]; 90 | from0 :> portIsConnected[1]; 91 | from1 :> portIsConnected[2]; 92 | from1 :> portIsConnected[3]; 93 | portIsConnected[0] = 1; // Assume connected. Better be. 94 | 95 | for(int i = 0; i < NUMBER_PORTS; i++) { 96 | if (!portIsConnected[i]) { 97 | destinations[i] = portIdentifiers[i] | 0xff00; // null chan 98 | continue; 99 | } 100 | for(int j = i+1; j < NUMBER_PORTS+1; j++) { 101 | int portNr; 102 | if (j == NUMBER_PORTS) { 103 | portNr = 0; 104 | } else { 105 | portNr = j; 106 | } 107 | if (portIsConnected[portNr]) { 108 | destinations[i] = portIdentifiers[portNr]; 109 | break; 110 | } 111 | } 112 | } 113 | from0 <: destinations[0]; 114 | from0 <: destinations[1]; 115 | from1 <: destinations[2]; 116 | from1 <: destinations[3]; 117 | } 118 | } 119 | 120 | void linkConnectionChecker(smi_interface_t &smi0, smi_interface_t &smi1, 121 | chanend toMaster, 122 | int destinations_[2], 123 | chanend init0, chanend init1) { 124 | timer t; 125 | int u, w0, w1, dest; 126 | 127 | init0 :> dest; 128 | toMaster <: dest; 129 | init1 :> dest; 130 | toMaster <: dest; 131 | 132 | t :> u; 133 | 134 | while(1) { 135 | u += 150000000; 136 | t when timerafter(u) :> void; 137 | w0 = phyPresentAndUp(smi0); 138 | w1 = phyPresentAndUp(smi1); 139 | toMaster <: w0; 140 | toMaster <: w1; 141 | toMaster :> destinations_[0]; 142 | toMaster :> destinations_[1]; 143 | } 144 | } 145 | 146 | void publishDestination(chanend init, streaming chanend dest) { 147 | int i; 148 | asm("add %0, %1, 0" : "=r" (i) : "r" (dest)); 149 | init <: i; 150 | } 151 | 152 | 153 | 154 | 155 | void ethernetCore(mii_ethercat_ports &mii0, mii_ethercat_ports &mii1, 156 | smi_interface_t &smi0, smi_interface_t &smi1, 157 | clock clk_smi, out port ?disableFlash, 158 | chanend toMaster, 159 | int destinations[2], int destinations_[2], 160 | int isProcessingNode) { 161 | short memory[4096]; 162 | streaming chan protoToTx1, rx1ToTx0; 163 | timer tmr; 164 | int resetTime; 165 | chan init0, init1; 166 | int tempDestination; 167 | 168 | memory[0x502/2] = 0; 169 | 170 | if (!isnull(disableFlash)) { 171 | disableFlash <: 0; 172 | } 173 | 174 | tmr :> resetTime; 175 | resetTime += 50000000; 176 | tmr when timerafter(resetTime) :> void; 177 | 178 | ethercat_port_init(mii0); 179 | ethercat_port_init(mii1); 180 | configure_clock_ref (clk_smi, 10); 181 | configure_out_port_no_ready(smi0.p_smi_mdc, clk_smi, 1); 182 | configure_out_port_no_ready(smi1.p_smi_mdc, clk_smi, 1< 5 | 6 | #include 7 | #include 8 | #include 9 | #include "pduhandler.h" 10 | 11 | extern int rx0, rx1; 12 | 13 | #define MEM_LENGTH 8192 14 | 15 | 16 | void passthrough(streaming chanend fromRx, streaming chanend toTx) { 17 | unsigned char byte; 18 | while (!stestct(fromRx)) { 19 | fromRx :> byte; toTx <: byte; 20 | } 21 | soutct(toTx, sinct(fromRx)); 22 | soutct(toTx, 1); 23 | schkct(fromRx, 1); 24 | } 25 | 26 | static inline unsigned char passByte(streaming chanend fromRx, streaming chanend toTx) { 27 | unsigned char byte; 28 | fromRx :> byte; toTx <: byte; 29 | return byte; 30 | } 31 | 32 | static inline int pass16(streaming chanend fromRx, streaming chanend toTx) { 33 | unsigned char byteH, byteL; 34 | fromRx :> byteL; toTx <: byteL; // ADO, Physical address 35 | fromRx :> byteH; toTx <: byteH; // ADO 36 | return byteL | (byteH << 8); 37 | } 38 | 39 | static inline int pass32(streaming chanend fromRx, streaming chanend toTx) { 40 | unsigned char byteH, byteL; 41 | int a; 42 | fromRx :> byteL; toTx <: byteL; 43 | fromRx :> byteH; toTx <: byteH; 44 | a = byteL | (byteH << 8); 45 | fromRx :> byteH; toTx <: byteH; 46 | a |= byteH << 16; 47 | fromRx :> byteH; toTx <: byteH; 48 | return a | byteH << 24; 49 | } 50 | 51 | static inline int passADPinc(streaming chanend fromRx, streaming chanend toTx) { 52 | unsigned char byteH, byteL, byteH_, byteL_; 53 | fromRx :> byteL; // Auto Inc address 54 | byteL_ = byteL + 1; 55 | toTx <: byteL_; 56 | fromRx :> byteH; // Auto Inc address 57 | if (byteL_ == 0) { 58 | byteH_ = byteH + 1; 59 | } 60 | toTx <: byteH_; 61 | return (byteL | byteH) == 0; 62 | } 63 | 64 | static inline int passADO(streaming chanend fromRx, streaming chanend toTx) { 65 | return pass16(fromRx, toTx); 66 | } 67 | 68 | static inline int passADR(streaming chanend fromRx, streaming chanend toTx) { 69 | return pass32(fromRx, toTx); 70 | } 71 | 72 | static inline int passADP(streaming chanend fromRx, streaming chanend toTx) { 73 | return pass16(fromRx, toTx); 74 | } 75 | 76 | static inline int passTotalLength(streaming chanend fromRx, streaming chanend toTx) { 77 | return pass16(fromRx, toTx); 78 | } 79 | 80 | static inline int passLEN(streaming chanend fromRx, streaming chanend toTx, int &morePDUs) { 81 | unsigned char byteH, byteL; 82 | fromRx :> byteL; toTx <: byteL; // LEN 83 | fromRx :> byteH; toTx <: byteH; // LEN 84 | morePDUs = byteH >> 7; 85 | return byteL | (byteH & 0x7 << 8); 86 | } 87 | 88 | static inline void passIRQ(streaming chanend fromRx, streaming chanend toTx) { 89 | passByte(fromRx, toTx); 90 | passByte(fromRx, toTx); 91 | } 92 | 93 | static inline void passWKCinc(streaming chanend fromRx, streaming chanend toTx) { 94 | unsigned char byteH, byteL, byteH_, byteL_; 95 | fromRx :> byteL; 96 | byteL_ = byteL + 1; 97 | toTx <: byteL_; 98 | fromRx :> byteH; 99 | if (byteL_ == 0) { 100 | byteH_ = byteH + 1; 101 | } 102 | toTx <: byteH_; 103 | } 104 | 105 | static inline void passWKC(streaming chanend fromRx, streaming chanend toTx) { 106 | passByte(fromRx, toTx); 107 | passByte(fromRx, toTx); 108 | } 109 | 110 | 111 | #pragma unsafe arrays 112 | void frameProcess(streaming chanend fromRx, streaming chanend toTx, int &destination, short memoryWord[]) { 113 | int handled = 0; 114 | unsigned char byte, ot2 = 0; 115 | unsigned total; 116 | int cnt = 0; 117 | int morePDUs, operate, address, length, station; 118 | int defaultDest; 119 | 120 | asm("getd %0,res[%1]" : "=r" (defaultDest) : "r" (toTx)); 121 | destination = defaultDest; 122 | 123 | while (1) { 124 | morePDUs = 1; 125 | schkct(fromRx, 3); 126 | asm("setd res[%0],%1" :: "r" (toTx), "r" (destination)); 127 | soutct(toTx, 0); // Recompute CRC. 128 | #pragma loop unroll 129 | for(int i = 0; i < 12; i++) { 130 | fromRx :> byte; toTx <: byte; 131 | } 132 | fromRx :> byte; toTx <: byte; 133 | if (byte != 0x88) { 134 | // printf("Got <%02x> not 0x88\n", byte); 135 | passthrough(fromRx, toTx); continue; 136 | } 137 | fromRx :> byte; toTx <: byte; 138 | if (byte != 0xA4) { 139 | // printf("Got <%02x> not 0xA4\n", byte); 140 | passthrough(fromRx, toTx); continue; 141 | } 142 | total = passTotalLength(fromRx, toTx); 143 | byte = total >> 12; 144 | total &= 0xfff; 145 | // ot = total; 146 | // fromRx :> byte; toTx <: byte; // frame type 147 | if (total < 44) { 148 | total = 44; 149 | } 150 | switch(byte) { 151 | case 1: // PDU frame 152 | do { 153 | fromRx :> byte; toTx <: byte; // Command 154 | switch(byte) { 155 | case 0x01: // APRD 156 | passByte(fromRx, toTx); // IDX 157 | operate = passADPinc(fromRx, toTx); // ADP 158 | address = passADO(fromRx, toTx); // ADO 159 | length = passLEN(fromRx, toTx, morePDUs); // ADO 160 | passIRQ(fromRx, toTx); 161 | total -= length + 12; 162 | if (operate) { 163 | for(int i = 0; i < length; i++) { 164 | fromRx :> byte; 165 | toTx <: (memoryWord, unsigned char[])[address++]; 166 | } 167 | passWKCinc(fromRx, toTx); 168 | } else { 169 | for(int i = 0; i < length; i++) { 170 | passByte(fromRx, toTx); 171 | } 172 | passWKC(fromRx, toTx); 173 | } 174 | break; 175 | case 0x02: // APWR 176 | passByte(fromRx, toTx); // IDX 177 | operate = passADPinc(fromRx, toTx); // ADP 178 | address = passADO(fromRx, toTx); // ADO 179 | length = passLEN(fromRx, toTx, morePDUs); // ADO 180 | passIRQ(fromRx, toTx); 181 | ot2 = total; 182 | total -= length + 12; 183 | if (operate) { 184 | for(int i = 0; i < length; i++) { 185 | (memoryWord, unsigned char[])[address++] = passByte(fromRx, toTx); 186 | } 187 | passWKCinc(fromRx, toTx); 188 | } else { 189 | for(int i = 0; i < length; i++) { 190 | passByte(fromRx, toTx); 191 | } 192 | passWKC(fromRx, toTx); 193 | } 194 | break; 195 | case 0x04: // FPRD 196 | passByte(fromRx, toTx); // IDX 197 | station = passADP(fromRx, toTx); // ADP 198 | operate = station == memoryWord[0x0010/2]; 199 | address = passADO(fromRx, toTx); // ADO 200 | operate |= station == memoryWord[0x0012/2]; 201 | length = passLEN(fromRx, toTx, morePDUs); 202 | passIRQ(fromRx, toTx); 203 | total -= length + 12; 204 | if (operate) { 205 | for(int i = 0; i < length; i++) { 206 | fromRx :> byte; 207 | toTx <: (memoryWord, unsigned char[])[address++]; 208 | } 209 | passWKCinc(fromRx, toTx); 210 | } else { 211 | printf("Station %04x Word %04x\n", station, memoryWord[0x0010/2]); 212 | for(int i = 0; i < length; i++) { 213 | passByte(fromRx, toTx); 214 | } 215 | passWKC(fromRx, toTx); 216 | } 217 | break; 218 | case 0x05: // FPWR 219 | passByte(fromRx, toTx); // IDX 220 | station = passADP(fromRx, toTx); // ADP 221 | operate = station == memoryWord[0x0010/2]; 222 | address = passADO(fromRx, toTx); // ADO 223 | operate |= station == memoryWord[0x0012/2]; 224 | length = passLEN(fromRx, toTx, morePDUs); 225 | passIRQ(fromRx, toTx); 226 | total -= length + 12; 227 | if (operate) { 228 | for(int i = 0; i < length; i++) { 229 | (memoryWord, unsigned char[])[address++] = passByte(fromRx, toTx); 230 | } 231 | passWKCinc(fromRx, toTx); 232 | } else { 233 | for(int i = 0; i < length; i++) { 234 | passByte(fromRx, toTx); 235 | } 236 | passWKC(fromRx, toTx); 237 | } 238 | break; 239 | case 0x07: // BRD 240 | passByte(fromRx, toTx); // IDX 241 | passADPinc(fromRx, toTx); // ADP 242 | address = passADO(fromRx, toTx); // ADO 243 | length = passLEN(fromRx, toTx, morePDUs); 244 | operate = address+length < MEM_LENGTH; 245 | passIRQ(fromRx, toTx); 246 | total -= length + 12; 247 | if (operate) { 248 | for(int i = 0; i < length; i++) { 249 | fromRx :> byte; 250 | toTx <: (unsigned char) (byte | (memoryWord, unsigned char[])[address++]); 251 | } 252 | passWKCinc(fromRx, toTx); 253 | } else { 254 | for(int i = 0; i < length; i++) { 255 | passByte(fromRx, toTx); 256 | } 257 | passWKC(fromRx, toTx); 258 | } 259 | break; 260 | case 0x08: // BWR 261 | passByte(fromRx, toTx); // IDX 262 | passADPinc(fromRx, toTx); // ADP 263 | address = passADO(fromRx, toTx); // ADO 264 | length = passLEN(fromRx, toTx, morePDUs); // ADO 265 | operate = address+length < MEM_LENGTH; 266 | passIRQ(fromRx, toTx); 267 | total -= length + 12; 268 | if (operate) { 269 | #pragma unsafe arrays 270 | for(int i = 0; i < length; i++) { 271 | (memoryWord, unsigned char[])[address++] = passByte(fromRx, toTx); 272 | } 273 | passWKCinc(fromRx, toTx); 274 | } else { 275 | for(int i = 0; i < length; i++) { 276 | passByte(fromRx, toTx); 277 | } 278 | passWKC(fromRx, toTx); 279 | } 280 | break; 281 | case 0x0A: // LRD 282 | passByte(fromRx, toTx); // IDX 283 | address = passADR(fromRx, toTx); // ADO 284 | length = passLEN(fromRx, toTx, morePDUs); 285 | operate = 1; /* TO BE DECIDED */ 286 | passIRQ(fromRx, toTx); 287 | total -= length + 12; 288 | if (operate) { 289 | for(int i = 0; i < length; i++) { 290 | fromRx :> byte; 291 | toTx <: (memoryWord, unsigned char[])[address++]; 292 | } 293 | passWKCinc(fromRx, toTx); 294 | } else { 295 | for(int i = 0; i < length; i++) { 296 | passByte(fromRx, toTx); 297 | } 298 | passWKC(fromRx, toTx); 299 | } 300 | break; 301 | case 0x0B: // LWR 302 | break; 303 | default: 304 | printf("Did not see a known command but %02x\n", byte); 305 | passthrough(fromRx, toTx); 306 | break; 307 | } 308 | } while (morePDUs); 309 | break; 310 | default: 311 | printf("Did not see a PDU frame but %02x\n", byte); 312 | passthrough(fromRx, toTx); 313 | break; 314 | } 315 | for(int i = 0; i < total; i++) { // transmit any trailer, but not the CRC. 316 | passByte(fromRx, toTx); 317 | } 318 | fromRx :> int _; // gobble up CRC. 319 | soutct(toTx, sinct(fromRx)); 320 | soutct(toTx, 1); 321 | schkct(fromRx, 1); 322 | asm("add %0, %1, 1" : "=r"(cnt) : "r" (cnt)); 323 | asm("add %0, %1, 1" : "=r"(cnt) : "r" (total)); 324 | asm("add %0, %1, 1" : "=r"(cnt) : "r" (address)); 325 | asm("add %0, %1, 1" : "=r"(handled) : "r" (handled)); 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /.cproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 47 | 61 | 62 | 63 | 64 | 78 | 92 | 93 | 94 | 95 | 96 | 114 | 132 | 133 | 134 | 135 | 136 | 150 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | xmake 270 | -f .makefile 271 | all 272 | true 273 | false 274 | true 275 | 276 | 277 | xmake 278 | -f Makefile 279 | app_uart_test.all 280 | true 281 | true 282 | true 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | -------------------------------------------------------------------------------- /doc/threads.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 27 | 32 | 33 | 40 | 45 | 46 | 53 | 58 | 59 | 62 | 66 | 67 | 70 | 74 | 75 | 76 | 94 | 101 | 102 | 104 | 105 | 107 | image/svg+xml 108 | 110 | 111 | 112 | 113 | 114 | 118 | 125 | 132 | PHY 143 | 145 | 155 | RX 166 | 167 | 170 | 179 | TX 190 | 191 | 196 | 201 | 204 | 211 | PHY 222 | 225 | 234 | RX 245 | 246 | 249 | 258 | TX 269 | 270 | 275 | 280 | 281 | 284 | 291 | PHY 302 | 305 | 314 | RX 325 | 326 | 329 | 338 | TX 349 | 350 | 355 | 360 | 361 | 364 | 371 | PHY 382 | 385 | 394 | RX 405 | 406 | 409 | 418 | TX 429 | 430 | 435 | 440 | 441 | 447 | 452 | 458 | 463 | 469 | 475 | 477 | 487 | framehandler 502 | 503 | 508 | 513 | 518 | 523 | 529 | 535 | 538 | 548 | linkdetect 567 | 568 | One TX and RX process per PHYNumber of PHY's flexible (>= 1)dotted channels are used if linnksare not connectedlink detect process reconnectschannel ends 599 | 600 | 601 | --------------------------------------------------------------------------------