From: Sebastien Bourdeauducq Date: Wed, 25 Feb 2015 17:34:11 +0000 (-0700) Subject: move files for misoc integration X-Git-Tag: 24jan2021_ls180~2604^2 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=8015d12692ccdc0463c68577c59d1dbd0cb61a66;p=litex.git move files for misoc integration --- diff --git a/LICENSE b/LICENSE deleted file mode 100644 index cbbfe8be..00000000 --- a/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Unless otherwise noted, LiteEth is copyright (C) 2015 Florent Kermarrec. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -Other authors retain ownership of their contributions. If a submission can -reasonably be considered independently copyrightable, it's yours and we -encourage you to claim it with appropriate copyright notices. This submission -then falls under the "otherwise noted" category. All submissions are strongly -encouraged to use the two-clause BSD license reproduced above. diff --git a/README b/README deleted file mode 100644 index b3308328..00000000 --- a/README +++ /dev/null @@ -1,142 +0,0 @@ - __ _ __ ______ __ - / / (_) /____ / __/ /_/ / - / /__/ / __/ -_) _// __/ _ \ - /____/_/\__/\__/___/\__/_//_/ - - Copyright 2012-2015 / EnjoyDigital - florent@enjoy-digital.fr - - A small footprint and configurable Ethernet core - with UDP/IP hw stack and Etherbone frontend - powered by Migen - -[> Doc ---------- -HTML : www.enjoy-digital.fr/litex/liteeth/ -PDF : www.enjoy-digital.fr/litex/liteeth.pdf - -[> Intro ---------- -LiteEth provides a small footprint and configurable Ethernet core. - -LiteEth is part of LiteX libraries whose aims are to lower entry level of -complex FPGA cores by providing simple, elegant and efficient implementations -ofcomponents used in today's SoC such as Ethernet, SATA, PCIe, SDRAM Controller... - -The core uses simple and specific streaming buses and will provides in the future -adapters to use standardized AXI or Avalon-ST streaming buses. - -Since Python is used to describe the HDL, the core is highly and easily -configurable. - -LiteEth uses technologies developed in partnership with M-Labs Ltd: - - Migen enables generating HDL with Python in an efficient way. - - MiSoC provides the basic blocks to build a powerful and small footprint SoC. - -LiteEth can be used as a Migen/MiSoC library (by simply installing it -with the provided setup.py) or can be integrated with your standard design flow -by generating the verilog rtl that you will use as a standard core. - -[> Features ------------ -- Ethernet MAC with various interfaces and various PHYs (GMII, MII, Loopback) -- Hardware UDP/IP stack with ARP and ICMP - -[> Possible improvements -------------------------- -- add standardized interfaces (AXI, Avalon-ST) -- add DMA interface to MAC -- add RGMII/SGMII PHYs -- ... See below Support and consulting :) - -If you want to support these features, please contact us at florent [AT] -enjoy-digital.fr. You can also contact our partner on the public mailing list -devel [AT] lists.m-labs.hk. - - -[> Getting started ------------------- -1. Install Python3 and your vendor's software - -2. Obtain Migen and install it: - git clone https://github.com/m-labs/migen - cd migen - python3 setup.py install - cd .. - -Note: in case you have issues with Migen, please retry -with our fork at: - https://github.com/enjoy-digital/misoc -until new features are merged. - -3. Obtain LiteScope and install it: - git clone https://github.com/enjoy-digital/litescope - cd litescope - python3 setup.py install - cd .. - -4. Obtain LiteEth - git clone https://github.com/enjoy-digital/liteeth - -5. Build and load UDP loopback design (only for KC705 for now): - python3 make.py -t udp all - -6. Test design (only for KC705 for now): - try to ping 192.168.1.40 - go to ./test directory: - change com port in config.py to your com port - run make test_udp - -7. Build and load Etherbone design (only for KC705 for now): - python3 make.py -t etherbone all - -8. Test design (only for KC705 for now): - try to ping 192.168.1.40 - go to ./test directory run: - run make test_etherbone - -[> Simulations: - Simulations are available in ./liteth/test/: - - mac_core_tb - - mac_wishbone_tb - - arp_tb - - ip_tb - - icmp_tb - - udp_tb - All ethernet layers have their own model tested against real Ethernet dumps (dumps.py) - To run a simulation, move to ./liteeth/test and run: - make simulation_name - -[> Tests : - An UDP loopback example is provided and be controlled with: ./test/test_udp.py - An Etherbone example with Wishbone SRAM is provided and can be controlled with: - ./test/test_etherbone.py - -[> License ------------ -LiteEth is released under the very permissive two-clause BSD license. Under -the terms of this license, you are authorized to use LiteEth for closed-source -proprietary designs. -Even though we do not require you to do so, those things are awesome, so please -do them if possible: - - tell us that you are using LiteEth - - cite LiteEth in publications related to research it has helped - - send us feedback and suggestions for improvements - - send us bug reports when something goes wrong - - send us the modifications and improvements you have done to LiteEth. - -[> Support and consulting --------------------------- -We love open-source hardware and like sharing our designs with others. - -LiteEth is developed and maintained by EnjoyDigital. - -If you would like to know more about LiteEth or if you are already a happy -user and would like to extend it for your needs, EnjoyDigital can provide standard -commercial support as well as consulting services. - -So feel free to contact us, we'd love to work with you! (and eventually shorten -the list of the possible improvements :) - -[> Contact -E-mail: florent [AT] enjoy-digital.fr \ No newline at end of file diff --git a/doc/.gitignore b/doc/.gitignore deleted file mode 100644 index 873d48e7..00000000 --- a/doc/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.diff -*.pyc -*~ -*.bc -*.md - -# Ignore generated files -build/ diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index 512689bd..00000000 --- a/doc/Makefile +++ /dev/null @@ -1,157 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/doc/make.bat b/doc/make.bat deleted file mode 100644 index ef155313..00000000 --- a/doc/make.bat +++ /dev/null @@ -1,220 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source -set I18NSPHINXOPTS=%SPHINXOPTS% source -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. xml to make Docutils-native XML files - echo. pseudoxml to make pseudoxml-XML files for display purposes - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - - -%SPHINXBUILD% 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 -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdf" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf - cd %BUILDDIR%/.. - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdfja" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf-ja - cd %BUILDDIR%/.. - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -if "%1" == "xml" ( - %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The XML files are in %BUILDDIR%/xml. - goto end -) - -if "%1" == "pseudoxml" ( - %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. - goto end -) - -:end diff --git a/doc/source/_static/LiteEth_logo_full.png b/doc/source/_static/LiteEth_logo_full.png deleted file mode 100644 index b8e14b9f..00000000 Binary files a/doc/source/_static/LiteEth_logo_full.png and /dev/null differ diff --git a/doc/source/_static/LiteEth_logo_full.svg b/doc/source/_static/LiteEth_logo_full.svg deleted file mode 100644 index 4ce07aff..00000000 --- a/doc/source/_static/LiteEth_logo_full.svg +++ /dev/null @@ -1,246 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - LITE - ETH - - powered by - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/__init__.py b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/__init__.py deleted file mode 100644 index bfcf0d6c..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Sphinx ReadTheDocs theme. - -From https://github.com/ryan-roemer/sphinx-bootstrap-theme. - -""" -import os - -VERSION = (0, 1, 6) - -__version__ = ".".join(str(v) for v in VERSION) -__version_full__ = __version__ - - -def get_html_theme_path(): - """Return list of HTML theme paths.""" - cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) - return cur_dir diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/breadcrumbs.html b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/breadcrumbs.html deleted file mode 100644 index 605cb9d2..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/breadcrumbs.html +++ /dev/null @@ -1,11 +0,0 @@ -
- - - -
\ No newline at end of file diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/footer.html b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/footer.html deleted file mode 100644 index b1788192..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/footer.html +++ /dev/null @@ -1,100 +0,0 @@ -
- {% if next or prev %} - - {% endif %} - - - - - - - - - - - - {% set footer_links = [ - ('docs/getting_started/bug_reports', 'Report Bug', 'Report Bug'), - ('docs/introducing_liteeth/license', 'Licensing', 'Licensing'), - ('docs/contributing/contributing', 'Contributing', 'Contributing'), - ('docs/introducing_liteeth/release_notes', 'Release notes', 'Release notes'), - ('docs/introducing_liteeth/community', 'Help', 'Contact'), -] -%} - - - - - - - - - - - - - -
diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/layout.html b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/layout.html deleted file mode 100644 index 8cca0aa2..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/layout.html +++ /dev/null @@ -1,208 +0,0 @@ -{# TEMPLATE VAR SETTINGS #} -{%- set url_root = pathto('', 1) %} -{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} -{%- if not embedded and docstitle %} - {%- set titlesuffix = " — "|safe + docstitle|e %} -{%- else %} - {%- set titlesuffix = "" %} -{%- endif %} - - - - - - - - {% block htmltitle %} - {{ title|striptags|e }}{{ titlesuffix }} - {% endblock %} - - {# FAVICON #} - {% if favicon %} - - {% endif %} - - {# CSS #} - - - {# OPENSEARCH #} - {% if not embedded %} - {% if use_opensearch %} - - {% endif %} - - {% endif %} - - {# RTD hosts this file, so just load on non RTD builds #} - {% if not READTHEDOCS %} - - {% endif %} - - {% for cssfile in css_files %} - - {% endfor %} - - {%- block linktags %} - {%- if hasdoc('about') %} - - {%- endif %} - {%- if hasdoc('genindex') %} - - {%- endif %} - {%- if hasdoc('search') %} - - {%- endif %} - {%- if hasdoc('copyright') %} - - {%- endif %} - - {%- if parents %} - - {%- endif %} - {%- if next %} - - {%- endif %} - {%- if prev %} - - {%- endif %} - {%- endblock %} - {%- block extrahead %} {% endblock %} - - {# Keep modernizr in head - http://modernizr.com/docs/#installing #} - - - - - - - -
- -
- - - - {# SIDE NAV, TOGGLES ON MOBILE #} - - -
- - {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #} - - - - - {# PAGE CONTENT #} -
-
- - - - - {% set navigation_bar = [ - ('docs/index', 'Docs', 'Documentation'), - ('docs/getting_started/downloads', 'SDK', 'Downloads'), - ('docs/introducing_liteeth/community', 'Help', 'Community'), - ('https://github.com/enjoy-digital/liteeth', 'Github', 'Github Project') -] -%} - - - - - - - - - -
- {% block body %}{% endblock %} -
- {% include "footer.html" %} -
-
- -
- -
- {% include "versions.html" %} - - {% if not embedded %} - - - {%- for scriptfile in script_files %} - - {%- endfor %} - - {% endif %} - - {# RTD hosts this file, so just load on non RTD builds #} - {% if not READTHEDOCS %} - - {% endif %} - - {# STICKY NAVIGATION #} - {% if theme_sticky_navigation %} - - {% endif %} - - {%- block footer %} {% endblock %} -
- - diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/layout_old.html b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/layout_old.html deleted file mode 100644 index deb8df2a..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/layout_old.html +++ /dev/null @@ -1,205 +0,0 @@ -{# - basic/layout.html - ~~~~~~~~~~~~~~~~~ - - Master layout template for Sphinx themes. - - :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -#} -{%- block doctype -%} - -{%- endblock %} -{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} -{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} -{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and - (sidebars != []) %} -{%- set url_root = pathto('', 1) %} -{# XXX necessary? #} -{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} -{%- if not embedded and docstitle %} - {%- set titlesuffix = " — "|safe + docstitle|e %} -{%- else %} - {%- set titlesuffix = "" %} -{%- endif %} - -{%- macro relbar() %} - -{%- endmacro %} - -{%- macro sidebar() %} - {%- if render_sidebar %} -
-
- {%- block sidebarlogo %} - {%- if logo %} - - {%- endif %} - {%- endblock %} - {%- if sidebars != None %} - {#- new style sidebar: explicitly include/exclude templates #} - {%- for sidebartemplate in sidebars %} - {%- include sidebartemplate %} - {%- endfor %} - {%- else %} - {#- old style sidebars: using blocks -- should be deprecated #} - {%- block sidebartoc %} - {%- include "localtoc.html" %} - {%- endblock %} - {%- block sidebarrel %} - {%- include "relations.html" %} - {%- endblock %} - {%- block sidebarsourcelink %} - {%- include "sourcelink.html" %} - {%- endblock %} - {%- if customsidebar %} - {%- include customsidebar %} - {%- endif %} - {%- block sidebarsearch %} - {%- include "searchbox.html" %} - {%- endblock %} - {%- endif %} -
-
- {%- endif %} -{%- endmacro %} - -{%- macro script() %} - - {%- for scriptfile in script_files %} - - {%- endfor %} -{%- endmacro %} - -{%- macro css() %} - - - {%- for cssfile in css_files %} - - {%- endfor %} -{%- endmacro %} - - - - - {{ metatags }} - {%- block htmltitle %} - {{ title|striptags|e }}{{ titlesuffix }} - {%- endblock %} - {{ css() }} - {%- if not embedded %} - {{ script() }} - {%- if use_opensearch %} - - {%- endif %} - {%- if favicon %} - - {%- endif %} - {%- endif %} -{%- block linktags %} - {%- if hasdoc('about') %} - - {%- endif %} - {%- if hasdoc('genindex') %} - - {%- endif %} - {%- if hasdoc('search') %} - - {%- endif %} - {%- if hasdoc('copyright') %} - - {%- endif %} - - {%- if parents %} - - {%- endif %} - {%- if next %} - - {%- endif %} - {%- if prev %} - - {%- endif %} -{%- endblock %} -{%- block extrahead %} {% endblock %} - - -{%- block header %}{% endblock %} - -{%- block relbar1 %}{{ relbar() }}{% endblock %} - -{%- block content %} - {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %} - -
- {%- block document %} -
- {%- if render_sidebar %} -
- {%- endif %} -
- {% block body %} {% endblock %} -
- {%- if render_sidebar %} -
- {%- endif %} -
- {%- endblock %} - - {%- block sidebar2 %}{{ sidebar() }}{% endblock %} -
-
-{%- endblock %} - -{%- block relbar2 %}{{ relbar() }}{% endblock %} - -{%- block footer %} - -

asdf asdf asdf asdf 22

-{%- endblock %} - - - diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/search.html b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/search.html deleted file mode 100644 index e3aa9b5c..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/search.html +++ /dev/null @@ -1,50 +0,0 @@ -{# - basic/search.html - ~~~~~~~~~~~~~~~~~ - - Template for the search page. - - :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -#} -{%- extends "layout.html" %} -{% set title = _('Search') %} -{% set script_files = script_files + ['_static/searchtools.js'] %} -{% block footer %} - - {# this is used when loading the search index using $.ajax fails, - such as on Chrome for documents on localhost #} - - {{ super() }} -{% endblock %} -{% block body %} - - - {% if search_performed %} -

{{ _('Search Results') }}

- {% if not search_results %} -

{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}

- {% endif %} - {% endif %} -
- {% if search_results %} -
    - {% for href, caption, context in search_results %} -
  • - {{ caption }} -

    {{ context|e }}

    -
  • - {% endfor %} -
- {% endif %} -
-{% endblock %} diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/searchbox.html b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/searchbox.html deleted file mode 100644 index 24418d32..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/searchbox.html +++ /dev/null @@ -1,7 +0,0 @@ -
-
- - - -
-
diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/badge_only.css b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/badge_only.css deleted file mode 100644 index 4868a002..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/badge_only.css +++ /dev/null @@ -1 +0,0 @@ -.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:"\f02d"}.icon-book:before{content:"\f02d"}.fa-caret-down:before{content:"\f0d7"}.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}} diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/theme - prior to centering.css b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/theme - prior to centering.css deleted file mode 100644 index cbffe01a..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/theme - prior to centering.css +++ /dev/null @@ -1,331 +0,0 @@ -*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:20px 0;padding:0}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}} -.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1} -.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! - * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.0.3");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff?v=4.0.3") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.0.3") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.pull-left.icon{margin-right:.3em}.fa.pull-right,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before,.icon-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before,.icon-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:"\f057"}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before,.icon-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before,.icon-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:"\f0a8"}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before,.icon-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-asc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-desc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-reply-all:before{content:"\f122"}.fa-mail-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before,.icon-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .icon,.nav .fa,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .icon{display:inline}.btn .fa.fa-large,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a{color:#2980b9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:60px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:60px}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27ae60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27ae60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#e74c3c !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#e67e22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980b9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:0.5em 1em 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:0.5em}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#999;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{display:block;float:left;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{display:block;float:left;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{display:block;float:left;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:0.5em 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#ccc;font-size:70%;margin-top:0.3125em;font-style:italic}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#f3f6f6;color:#cad2d3}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e74c3c}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:0.8em;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fff;color:#cad2d3;border-color:transparent}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{padding:6px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #e74c3c}.wy-control-group.wy-control-group-error textarea{border:solid 1px #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px;margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980b9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27ae60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#e74c3c !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}code,.rst-content tt{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:"Incosolata","Consolata","Monaco",monospace;color:#e74c3c;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9b59b6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:"Incosolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:"Incosolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#eaf2f5} - .wy-breadcrumbs li{display:inline-block} - .wy-breadcrumbs li.wy-breadcrumbs-aside { - float:right; - padding-left:5px; - font-size:0.8em; - } - - .wy-breadcrumbs li a{ - display:inline-block;padding:5px - } - - .wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block} - - @media screen and (max-width: 480px) - {.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#2980b9;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3} - - .wy-menu-vertical li.current a{ - color:gray; - border-right:solid 1px #c9c9c9; - padding:0.4045em 2.427em - } - - .wy-menu-vertical li.current a:hover{background:#d6d6d6} - - .wy-menu-vertical li.on a { - color:#404040; - padding:0.4045em 1.618em; - font-weight:bold; - position:relative; - background:#fcfcfc; - border:none; - border-bottom:solid 1px #c9c9c9; - border-top:solid 1px #c9c9c9; - padding-left:1.618em -4px - } - - -.wy-menu-vertical li.current>a { - color:#404040; - padding:0.4045em 1.618em; - font-weight:bold; - position:relative; - background:#fcfcfc; - border:none; - border-bottom:solid 1px #c9c9c9; - border-top:solid 1px #c9c9c9; - padding-left:1.618em -4px - } - - .wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc} - .wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff} - - .wy-side-nav-search {z-index:200; - background-color:#2980b9; - text-align:center; - /* padding:0.809em; */ - /* padding-top: 0.809em;*/ - padding-right: 0.809em; - padding-bottom: 0.809em; - padding-left: 0.809em; - display:block; - color:#fcfcfc; - margin-bottom:0.809em - } - - .wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4} - - - .wy-side-nav-search img{ - display:block; - margin:auto auto 0.809em auto; - /*height:45px;*/ - /*width:45px;*/ - width:200px; - /*background-color:#2980b9;*/ - padding:5px; - /*border-radius:100%*/ - } - - .wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a { - color:#fcfcfc; - font-size:100%; - font-weight:bold; - display:inline-block; - padding-top: 4px; - padding-right: 6px; - /*padding-bottom: 4px;*/ - padding-left: 6px; - /* margin-bottom:0.809em */ - } - - .wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:absolute;top:0;left:0;width:300px;overflow:hidden;min-height:100%;background:#343131;z-index:200} - - .wy-nav-top{ - display:none; - background:#2980b9; - color:#fff; - padding:0.4045em 0.809em; - position:relative; - line-height:50px; - text-align:center; - font-size:100%; - *zoom:1} - - .wy-nav-top:before,.wy-nav-top:after{display:table;content:""} - - .wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold} - - .wy-nav-top img{ - margin-right:12px; - /*height:45px; */ - /*width:45px;*/ - width:200px; - background-color:#2980b9; - padding:5px; - /*border-radius:100%*/ - } - - .wy-nav-top i{font-size:30px;float:left;cursor:pointer}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%} - - .wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}nav.stickynav{position:absolute/* previously fixed hamishw */ ;top:0}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after{visibility:visible;content:"\f0c1";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt{color:#000}.rst-content tt big,.rst-content tt em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt{font-weight:bold}.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040} -/*! - * HamishW - some CSS for nav bar - */ - -.main-nav-bar { - display:block; - max-width: 1100px; - border-bottom: solid; - border-bottom-width: thin; - padding-bottom: 10px; - margin-bottom:20px; -} - - -#menu-options { - display: table; - background-color:#F8F8F8; - /*height: 87px;*/ - width: 100%; -} - -#menu-options li { - display: table-cell; - padding-left: 5px; - padding-right: 5px; - padding-top: 10px; - padding-bottom: 10px; - width: 5.0%; /*(100 / numItems)% */ - text-align: center; - font-weight:bold; - /*background: #ddd;*/ - white-space: nowrap; -}​ - - - -.navlink-long { - display:inline-block; - vertical-align: top; - padding:5px; -} - -.navlink-short { - display:none; - vertical-align: top; - padding:5px; -} - - -.footer-nav-bar { - display:block; - background-color:#F8F8F8; - max-width: 1100px; - /*border-bottom: solid;*/ - padding-bottom: 10px; - margin-top:15px; - border-top:solid; - /* border-top-width:thin; */ -} - -.footer-options { -/* display:block; -width:inherit; -font-size:0.8em; -font-weight:normal; -*/ -display:block; -text-align:justify; -font-size:0.8em; -width:inherit; -} - -.footer-navlink-long { - display: inline-block; - vertical-align: top; - padding:5px; -} - -.footer-navlink-short { - display:none; - padding:5px; -} - -.footer-options:after { - content: ""; - width: 100%; - line-height:1px; - line-spacing:1px; - display: inline-block; - } - -.copyright-box { - border-top:solid; - border-top-width:thin; - margin-top:10px; - background-color:#F8F8F8; - padding-bottom:5px; - -} - -.copyright-box p { - font-size:0.8em; -} - - -/* HamishW - some CSS for the breadcrumb (make elements inline blocks) */ - - -.breadcrumb-box { - margin-top:10px; - font-size:0.8em; - } - -.breadcrumb-box-item{ - display: inline-block; - } - -/* indent third level item -*/ - -/* set background to selected headings as dark */ - - -.wy-menu-vertical li.toctree-l2.current>a, -.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>a, -.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4.current>a -.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4.current li.toctree-l5.current>a - { -background: #c9c9c9; -} - - -/* .wy-menu-vertical li.current>a .wy-menu-vertical .current>a */ -/* -.wy-menu-vertical li.current>a { -background: purple; -} -*/ - -.wy-menu-vertical li.toctree-l2 li.toctree-l3>a { -display:none; -} - -.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a { -display:block; -font-size:0.8em; -/*padding-top: 0.4045em;*/ -padding-right: 2.427em; -padding-bottom: 0.4045em; -padding-left: 4.25em; -width:100%; -} - - - - -.wy-menu-vertical li.toctree-l2 li.toctree-l3 li.toctree-l4>a { -display:none; -} - -.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4>a { -/* -background: #F0EEEE; -background: purple; */ -display:block; -font-size:0.8em; -/*padding-top: 0.4045em;*/ -padding-right: 2.427em; -padding-bottom: 0.4045em; -padding-left: 5.0em; -width:100%; -} - - { -background: #c9c9c9; -} - -.wy-menu-vertical a[href^="#"] { -background:#F0EEEE; -} - - -@media only screen -and (max-width : 480px) { -/* Styles */ - .navlink-long, .footer-navlink-long { - display:none; - } - - .navlink-short, .footer-navlink-short { - display:inline-block; - vertical-align: top;} - -} - -@media screen and (min-width: 480px) and (max-width: 768px) { - .navlink-long, .footer-navlink-long { - display:inline-block; - } - .navlink-short, .footer-navlink-short { - display:none; - } -} - diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/theme.css b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/theme.css deleted file mode 100644 index 90d729cd..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/theme.css +++ /dev/null @@ -1,512 +0,0 @@ -*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%} - -body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:20px 0;padding:0}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical} -table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline} -.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""} -pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}} -.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1} -.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! - * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.0.3");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff?v=4.0.3") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.0.3") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.pull-left.icon{margin-right:.3em}.fa.pull-right,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before,.icon-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before,.icon-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:"\f057"}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before,.icon-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before,.icon-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:"\f0a8"}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before,.icon-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-asc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-desc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-reply-all:before{content:"\f122"}.fa-mail-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before,.icon-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .icon,.nav .fa,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .icon{display:inline}.btn .fa.fa-large,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a{color:#2980b9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:60px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:60px}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27ae60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27ae60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#e74c3c !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#e67e22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980b9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:0.5em 1em 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:0.5em}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#999;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{display:block;float:left;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{display:block;float:left;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{display:block;float:left;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:0.5em 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#ccc;font-size:70%;margin-top:0.3125em;font-style:italic}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#f3f6f6;color:#cad2d3}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e74c3c}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:0.8em;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fff;color:#cad2d3;border-color:transparent}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{padding:6px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #e74c3c}.wy-control-group.wy-control-group-error textarea{border:solid 1px #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}} - - @media screen and (max-width: 768px){.tablet-hide{display:none}} - @media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px;margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0} - - .wy-table-responsive{ - margin-bottom:24px; - max-width:100%; - overflow:auto - } - -.wy-table-responsive table{ - margin-bottom:0 !important - } - -.wy-table-responsive table td {white-space:nowrap} - -.wy-table-responsive table th{white-space:nowrap} - -a{color:#2980b9;text-decoration:none}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%;overflow-x:hidden} - -body{ - /* font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif; */ - font-family:"proxima-nova","Helvetica Neue",Arial,sans-serif; - font-weight:normal; - color:#404040; - min-height:100%; - overflow-x:hidden; - background:#edf0f2 - } - -.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980b9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27ae60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#e74c3c !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%} - -code, .rst-content tt - { - white-space:nowrap; - max-width:100%; - background:#fff; - border:solid 1px #e1e4e5; - /* font-size:75%; */ - font-size:90%; - padding:0 5px; - font-family:"Inconsolata","Consolata","Monaco",monospace;color:#e74c3c; - overflow-x:auto - } - -code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px} -.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li { - list-style:disc;margin-left:24px} - -.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px} - -/* .wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li { */ -.wy-plain-list-decimal ol li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li { - list-style:decimal;margin-left:24px - } - -/* HamishW - add to fix unordered bullets inside decimal lists */ -.rst-content ol.arabic ul li { - list-style:disc; - } - - - -.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9b59b6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:"Inconsolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:"Inconsolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#eaf2f5} - .wy-breadcrumbs li{display:inline-block} - .wy-breadcrumbs li.wy-breadcrumbs-aside { - float:right; - padding-left:5px; - font-size:0.8em; - } - - .wy-breadcrumbs li a{ - display:inline-block;padding:5px - } - - .wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block} - - @media screen and (max-width: 480px) - {.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#2980b9;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3} - - .wy-menu-vertical li.current a{ - color:gray; - border-right:solid 1px #c9c9c9; - padding:0.4045em 2.427em - } - - .wy-menu-vertical li.current a:hover{background:#d6d6d6} - - .wy-menu-vertical li.on a { - color:#404040; - padding:0.4045em 1.618em; - font-weight:bold; - position:relative; - background:#fcfcfc; - border:none; - border-bottom:solid 1px #c9c9c9; - border-top:solid 1px #c9c9c9; - padding-left:1.618em -4px - } - - -.wy-menu-vertical li.current>a { - color:#404040; - padding:0.4045em 1.618em; - font-weight:bold; - position:relative; - background:#fcfcfc; - border:none; - border-bottom:solid 1px #c9c9c9; - border-top:solid 1px #c9c9c9; - padding-left:1.618em -4px - } - - .wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc} - .wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff} - - .wy-side-nav-search {z-index:200; - background-color:#2980b9; - text-align:center; - /* padding:0.809em; */ - /* padding-top: 0.809em;*/ - padding-right: 0.809em; - padding-bottom: 0.809em; - padding-left: 0.809em; - display:block; - color:#fcfcfc; - margin-bottom:0.809em - } - - .wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4} - - - .wy-side-nav-search img{ - display:block; - margin:auto auto 0.809em auto; - /*height:45px;*/ - /*width:45px;*/ - width:200px; - /*background-color:#2980b9;*/ - padding:5px; - /*border-radius:100%*/ - } - - .wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a { - color:#fcfcfc; - font-size:100%; - font-weight:bold; - display:inline-block; - padding-top: 4px; - padding-right: 6px; - /*padding-bottom: 4px;*/ - padding-left: 6px; - /* margin-bottom:0.809em */ - } - - .wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0} - - .wy-body-for-nav{ - /* background:left repeat-y #F1F0F0; */ - /* - background:left repeat-y rgb(97, 6, 6); - background-size:300px 1px - */ /* This is deep red colour - removed */ - /* background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC); */ - - } - - .wy-grid-for-nav { - /* position:absolute; */ - position:relative; /* Make left column full length */ - width:100%; - height:100% - } - - .wy-nav-side{position:absolute;top:0;left:0;width:300px;overflow:hidden;min-height:100%;background:#343131;z-index:200} - - .wy-nav-top{ - display:none; - background:#2980b9; - color:#fff; - padding:0.4045em 0.809em; - position:relative; - line-height:50px; - text-align:center; - font-size:100%; - *zoom:1} - - .wy-nav-top:before,.wy-nav-top:after{display:table;content:""} - - .wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold} - - .wy-nav-top img{ - margin-right:12px; - /*height:45px; */ - /*width:45px;*/ - width:200px; - background-color:#2980b9; - padding:5px; - /*border-radius:100%*/ - } - - .wy-nav-top i{font-size:30px;float:left;cursor:pointer} - .wy-nav-content-wrap{ - margin-left:300px; - /* background:#fcfcfc; */ - min-height:100% - } - - .wy-nav-content{ - /* padding:1.618em 3.236em; */ - /* padding-top: 1.618em; */ - padding-right: 3.236em; - padding-bottom: 1.618em; - padding-left: 3.236em; - height:100%; - min-height: 100vh; /* ensure is always full height of browser window */ - max-width:800px; - /* margin:auto; */ - margin-left:0px; - background: #fcfcfc; - } - - .wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%} - - @media screen and (max-width: 768px){ - .wy-body-for-nav{background:#fcfcfc} - .wy-nav-top{display:block}.wy-nav-side{left:-300px} - .wy-nav-side.shift{width:85%;left:0} - .wy-nav-content-wrap{margin-left:0} - .wy-nav-content-wrap .wy-nav-content { - /* padding:1.618em */ - /* padding-top: 1.618em; */ - padding-right: 1.618em; - padding-bottom: 1.618em; - padding-left: 1.618em; - } - .wy-nav-content-wrap.shift{ - position:relative; /* position:fixed; */ - min-width:100%; - left:85%; - top:0;height:100%; - overflow:hidden - } - } - - @media screen and (min-width: 1400px) { - /* .wy-nav-content-wrap{background:rgba(0,0,0,0.05)} */ - .wy-nav-content{ - /* margin:0; */ - background:#fcfcfc} - } - - @media print{.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}nav.stickynav{position:absolute/* previously fixed hamishw */ ;top:0}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center} - - @media screen and (max-width: 768px){ - .rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after{visibility:visible;content:"\f0c1";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt{color:#000}.rst-content tt big,.rst-content tt em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt{font-weight:bold}.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right} - - @media screen and (max-width: 480px){ - .rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040} -/*! - * HamishW - some CSS for nav bar - */ - -.main-nav-bar { - display:block; - max-width: 1100px; - border-bottom: solid; - border-bottom-width: thin; - padding-bottom: 10px; - margin-bottom:20px; - padding-top: 10px; -} - - -#menu-options { - display: table; - /* background-color:#F8F8F8; */ - /*height: 87px;*/ - - width: 100%; -} - -#menu-options li { - display: table-cell; - /* padding-left: 5px; - padding-right: 5px; */ - padding-top: 10px; - padding-bottom: 10px; - width: 5.0%; /*(100 / numItems)% */ - text-align: center; - font-weight:bold; - /*background: #ddd;*/ - white-space: nowrap; -}​ - - - -.navlink-long { - display:inline-block; - vertical-align: top; - padding:5px; -} - -.navlink-short { - display:none; - vertical-align: top; - padding:5px; -} - - -.footer-nav-bar { - display:block; - background-color:#F8F8F8; - max-width: 1100px; - /*border-bottom: solid;*/ - padding-bottom: 10px; - margin-top:15px; - border-top:solid; - /* border-top-width:thin; */ -} - -.footer-options { -/* display:block; -width:inherit; -font-size:0.8em; -font-weight:normal; -*/ -display:block; -text-align:justify; -font-size:0.8em; -width:inherit; -} - -.footer-navlink-long { - display: inline-block; - vertical-align: top; - padding:5px; -} - -.footer-navlink-short { - display:none; - padding:5px; -} - -.footer-options:after { - content: ""; - width: 100%; - line-height:1px; - line-spacing:1px; - display: inline-block; - } - -.copyright-box { - border-top:solid; - border-top-width:thin; - margin-top:10px; - background-color:#F8F8F8; - padding-bottom:5px; - -} - -.copyright-box p { - font-size:0.8em; -} - - - -/* HamishW - Attempt to wrap table columns/remove the "responsive" behaviour on some tables. */ -table.wrap-table-content td, table.wrap-table-content th { - white-space: normal; -} - - - -/* HamishW - add clear markup for external links */ - -a.external:after { - content: ""; - display: inline-block; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAV0lEQVR4Xq2QwQ2AAAwC3cmd2Kk7sRP64CEJ9qOX8OPatMc/QKppnEPhTmJh23CLiwAqIw21CybKQ28qQi37WGFYBJcwfJQpP8LlEHKyZMF0IdmF13zlAjZ/6H4wb+mUAAAAAElFTkSuQmCC); - background-repeat: no-repeat; - background-position: right top; - background-origin: border-box; - width: 12px; - height: 16px; -} - - -/* HamishW - some CSS for the breadcrumb (make elements inline blocks) */ - - -.breadcrumb-box { - margin-top:10px; - font-size:0.8em; - } - -.breadcrumb-box-item{ - display: inline-block; - } - -/* indent third level item -*/ - -/* set background to selected headings as dark */ - - -.wy-menu-vertical li.toctree-l2.current>a, -.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>a, -.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4.current>a -.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4.current li.toctree-l5.current>a - { -background: #c9c9c9; -} - - -/* .wy-menu-vertical li.current>a .wy-menu-vertical .current>a */ -/* -.wy-menu-vertical li.current>a { -background: purple; -} -*/ - -.wy-menu-vertical li.toctree-l2 li.toctree-l3>a { -display:none; -} - -.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a { -display:block; -font-size:0.8em; -/*padding-top: 0.4045em;*/ -padding-right: 2.427em; -padding-bottom: 0.4045em; -padding-left: 4.25em; -width:100%; -} - - - - -.wy-menu-vertical li.toctree-l2 li.toctree-l3 li.toctree-l4>a { -display:none; -} - -.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4>a { -/* -background: #F0EEEE; -background: purple; */ -display:block; -font-size:0.8em; -/*padding-top: 0.4045em;*/ -padding-right: 2.427em; -padding-bottom: 0.4045em; -padding-left: 5.0em; -width:100%; -} - - { -background: #c9c9c9; -} - -.wy-menu-vertical a[href^="#"] { -background:#F0EEEE; -} - -.grid-to-center-rtd-theme { - margin-left:0; - margin-right:auto; - max-width: 1100px; - } - -@media only screen and (min-width : 1100px){ - -.grid-to-center-rtd-theme { - margin-left:auto; - /* max-width: 1100px; */ - } - -} - -@media only screen -and (max-width : 480px) { -/* Styles */ - .navlink-long, .footer-navlink-long { - display:none; - } - - .navlink-short, .footer-navlink-short { - display:inline-block; - vertical-align: top;} - -} - -@media screen and (min-width: 480px) and (max-width: 768px) { - .navlink-long, .footer-navlink-long { - display:inline-block; - } - .navlink-short, .footer-navlink-short { - display:none; - } -} - diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot deleted file mode 100644 index 7c79c6a6..00000000 Binary files a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg deleted file mode 100644 index 45fdf338..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,414 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf deleted file mode 100644 index e89738de..00000000 Binary files a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff deleted file mode 100644 index 8c1748aa..00000000 Binary files a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/js/theme.js b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/js/theme.js deleted file mode 100644 index 60520cc3..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/js/theme.js +++ /dev/null @@ -1,47 +0,0 @@ -$( document ).ready(function() { - // Shift nav in mobile when clicking the menu. - $(document).on('click', "[data-toggle='wy-nav-top']", function() { - $("[data-toggle='wy-nav-shift']").toggleClass("shift"); - $("[data-toggle='rst-versions']").toggleClass("shift"); - }); - // Close menu when you click a link. - $(document).on('click', ".wy-menu-vertical .current ul li a", function() { - $("[data-toggle='wy-nav-shift']").removeClass("shift"); - $("[data-toggle='rst-versions']").toggleClass("shift"); - }); - $(document).on('click', "[data-toggle='rst-current-version']", function() { - $("[data-toggle='rst-versions']").toggleClass("shift-up"); - }); - // Make tables responsive - $("table.docutils:not(.field-list)").wrap("
"); -}); - -window.SphinxRtdTheme = (function (jquery) { - var stickyNav = (function () { - var navBar, - win, - stickyNavCssClass = 'stickynav', - applyStickNav = function () { - if (navBar.height() <= win.height()) { - navBar.addClass(stickyNavCssClass); - } else { - navBar.removeClass(stickyNavCssClass); - } - }, - enable = function () { - applyStickNav(); - win.on('resize', applyStickNav); - }, - init = function () { - navBar = jquery('nav.wy-nav-side:first'); - win = jquery(window); - }; - jquery(init); - return { - enable : enable - }; - }()); - return { - StickyNav : stickyNav - }; -}($)); diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/theme.conf b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/theme.conf deleted file mode 100644 index a72f4546..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/theme.conf +++ /dev/null @@ -1,9 +0,0 @@ -[theme] -inherit = basic -stylesheet = css/theme.css - -[options] -typekit_id = hiw1hhg -analytics_id = -sticky_navigation = True - diff --git a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/versions.html b/doc/source/_themes/enjoydigital_sphinx_rtd_theme/versions.html deleted file mode 100644 index 8b3eb79d..00000000 --- a/doc/source/_themes/enjoydigital_sphinx_rtd_theme/versions.html +++ /dev/null @@ -1,37 +0,0 @@ -{% if READTHEDOCS %} -{# Add rst-badge after rst-versions for small badge style. #} -
- - Read the Docs - v: {{ current_version }} - - -
-
-
Versions
- {% for slug, url in versions %} -
{{ slug }}
- {% endfor %} -
-
-
Downloads
- {% for type, url in downloads %} -
{{ type }}
- {% endfor %} -
-
-
On Read the Docs
-
- Project Home -
-
- Builds -
-
-
- Free document hosting provided by Read the Docs. - -
-
-{% endif %} - diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100644 index b1d4d3ce..00000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,402 +0,0 @@ -# -*- coding: utf-8 -*- -# -# LiteEth documentation build configuration file, created by -# sphinx-quickstart on Tue Jul 01 09:20:29 2014. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys -import os - - -# At the top. #HamishW https://pypi.python.org/pypi/sphinx-bootstrap-theme/ ... -#import sphinx_bootstrap_theme - - -# At the top. #HamishW http://sphinx-better-theme.readthedocs.org/en/latest/installation.html easy_install sphinx_better_theme -#from better import better_theme_path -#html_theme_path = [better_theme_path] - - -# At the top. #HamishW https://github.com/snide/sphinx_rtd_theme easy_install sphinx_rtd_theme -#import sphinx_rtd_theme -#html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - -html_theme_path = ['_themes',] - - - - - - - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', - 'sphinx.ext.ifconfig', - 'sphinx.ext.viewcode', - # 'breathe', #added by HamishW -] - - -#Build "Todo" notes into the source -#todo_include_todos = 'True' - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'LiteEth' -copyright = u'2015, ' - # Note, theme was modified to allow this to display (fix breaks the translation code, which was breaking the above link and rendering it as text). - # Also so I could link to specific copyright page. - - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# - -liteeth_version = open(os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'liteeth-version.txt'))).read().strip() - -# The short X.Y version. -version = liteeth_version[:liteeth_version.rindex('.')] -# The full version, including alpha/beta/rc tags. -release = liteeth_version - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -#html_theme = 'default' -#html_theme = 'sphinxdoc' -#html_theme = 'agogo' #like this -#html_theme = 'scrolls' -#html_theme = 'bootstrap' -#html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() -#html_theme = 'better' -#html_theme = 'pyramid' -#html_theme = 'nature' -#html_theme = 'haiku' -#html_theme = "sphinx_rtd_theme" -html_theme = "enjoydigital_sphinx_rtd_theme" - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = { -# "rightsidebar": "true", -# "relbarbgcolor": "black" -#} - - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -html_logo = '_static/LiteEth_logo_full.png' - - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -html_favicon = 'liteeth.ico' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -#html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'LiteEthdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'LiteEth.tex', u'LiteEth Documentation', - u'Kermarrec Florent', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'liteeth', u'LiteEth Documentation', - [u'Florent Kermarrec'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'LiteEth', u'LiteEth Documentation', - u'Florent Kermarrec', 'LiteEth', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False - - -# -- Options for Epub output ---------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = u'LiteEth' -epub_author = u'Florent Kermarrec' -epub_publisher = u'Florent Kermarrec' -epub_copyright = u'2015, LiteEth Contributors' - -# The basename for the epub file. It defaults to the project name. -#epub_basename = u'LiteEth' - -# The HTML theme for the epub output. Since the default themes are not optimized -# for small screen space, using the same theme for HTML and epub output is -# usually not wise. This defaults to 'epub', a theme designed to save visual -# space. -#epub_theme = 'epub' - -# The language of the text. It defaults to the language option -# or en if the language is not set. -#epub_language = '' - -# The scheme of the identifier. Typical schemes are ISBN or URL. -#epub_scheme = '' - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -#epub_identifier = '' - -# A unique identification for the text. -#epub_uid = '' - -# A tuple containing the cover image and cover page html template filenames. -#epub_cover = () - -# A sequence of (type, uri, title) tuples for the guide element of content.opf. -#epub_guide = () - -# HTML files that should be inserted before the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_pre_files = [] - -# HTML files shat should be inserted after the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_post_files = [] - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] - -# The depth of the table of contents in toc.ncx. -#epub_tocdepth = 3 - -# Allow duplicate toc entries. -#epub_tocdup = True - -# Choose between 'default' and 'includehidden'. -#epub_tocscope = 'default' - -# Fix unsupported image types using the PIL. -#epub_fix_images = False - -# Scale large images. -#epub_max_image_width = 0 - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#epub_show_urls = 'inline' - -# If false, no index is generated. -#epub_use_index = True - - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'http://docs.python.org/': None} - -# HamishW - set highlighting language. -highlight_language = 'cpp' - -# HamishW - set domain (cpp) -primary_domain = 'cpp' - -# HamishW - tell Breathe about projects. Breathe is tool to convert Doxygen to Python objects, for import into Sphinx. -#breathe_projects = { "myproject": "/home/me/docproj/doxyxml/", "nutshell":"./headers/xml/", } -# HamishW - Specify a default project: -#breathe_default_project = "nutshell" - diff --git a/doc/source/docs/core/index.rst b/doc/source/docs/core/index.rst deleted file mode 100644 index 33ae1eab..00000000 --- a/doc/source/docs/core/index.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. _core-index: - -======================== -Core -======================== -.. note:: - Please contribute to this document, or support us financially to write it. \ No newline at end of file diff --git a/doc/source/docs/frontend/index.rst b/doc/source/docs/frontend/index.rst deleted file mode 100644 index d112402e..00000000 --- a/doc/source/docs/frontend/index.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. _frontend-index: - -======================== -Frontend -======================== -.. note:: - Please contribute to this document, or support us financially to write it. diff --git a/doc/source/docs/getting_started/FAQ.rst b/doc/source/docs/getting_started/FAQ.rst deleted file mode 100644 index 87c3401f..00000000 --- a/doc/source/docs/getting_started/FAQ.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. _FAQ: - -=== -FAQ -=== -.. note:: - Please contribute to this document. diff --git a/doc/source/docs/getting_started/bug_reports.rst b/doc/source/docs/getting_started/bug_reports.rst deleted file mode 100644 index 5ab902f5..00000000 --- a/doc/source/docs/getting_started/bug_reports.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _bug-reports: - -============= -Bug Reporting -============= -- send us feedback and suggestions for improvements -- send us bug reports when something goes wrong -- send us the modifications and improvements you have done to LiteEth. -The use of "git format-patch" is recommended. If your submission is large and -complex and/or you are not sure how to proceed, feel free to discuss with us -about it. \ No newline at end of file diff --git a/doc/source/docs/getting_started/downloads.rst b/doc/source/docs/getting_started/downloads.rst deleted file mode 100644 index d4368a15..00000000 --- a/doc/source/docs/getting_started/downloads.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. _sdk-download-and-install: - -==================== -Download and install -==================== -1. Install Python3 and your vendor's software - -2. Obtain Migen and install it: - - git clone https://github.com/m-labs/migen - - cd migen - - python3 setup.py install - - cd .. - -.. note:: - In case you have issues with Migen, please retry with our forks at: - https://github.com/enjoy-digital/migen - until new features are merged. - -3. Obtain LiteScope and install it: - - git clone https://github.com/enjoy-digital/litescope - - cd litescope - - python3 setup.py install - - cd .. - -4. Obtain LiteEth - - git clone https://github.com/enjoy-digital/liteeth - -5. Build and load UDP loopback design (only for KC705 for now): - - python3 make.py -t udp all - -6. Test design (only for KC705 for now): - - try to ping 192.168.1.40 - - go to ./test directory: - - change com port in config.py to your com port - - run make test_udp - -7. Build and load Etherbone design (only for KC705 for now): - - python3 make.py -t etherbone all - -8. Test design (only for KC705 for now): - - try to ping 192.168.1.40 - - go to ./test directory run: - - run make test_etherbone \ No newline at end of file diff --git a/doc/source/docs/getting_started/index.rst b/doc/source/docs/getting_started/index.rst deleted file mode 100644 index 92b32a36..00000000 --- a/doc/source/docs/getting_started/index.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _getting-started-index: - -=============== -Getting Started -=============== - -Now you know why LiteEth is :ref:`core for you `, it's time to *get started*. - -This section provides a walk-through of :ref:`downloading and installing the tools`. - -.. toctree:: - :maxdepth: 1 - - downloads - FAQ - bug_reports - diff --git a/doc/source/docs/index.rst b/doc/source/docs/index.rst deleted file mode 100644 index 22ec86c6..00000000 --- a/doc/source/docs/index.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _documentation-home: - -======================== -LiteEth Documentation -======================== - -This comprehensive documentation set contains everything you need to know to use LiteEth and integrate it in your design. - -**Getting started:** - -- :ref:`intro-index` explains what LiteEth does, why it is needed, its limitations and its licensing. It will help you understand whether LiteEth is the right core for you. -- :ref:`getting-started-index` walks you through downloading, installing and using the LiteEth core. - -**LiteEth Internals:** - -- :ref:`core-index` describes core building blocks. -- :ref:`frontend-index` describes core building blocks. - -The full hierarchy of articles, opened to the second level, is shown below. - -.. toctree:: - :maxdepth: 2 - - intro/index - getting_started/index - core/index - frontend/index diff --git a/doc/source/docs/intro/about.rst b/doc/source/docs/intro/about.rst deleted file mode 100644 index 4d02b3dc..00000000 --- a/doc/source/docs/intro/about.rst +++ /dev/null @@ -1,59 +0,0 @@ -.. _about: - -================ -About LiteEth -================ - -LiteEth provides a small footprint and configurable Ethernet core. - -LiteEth is part of LiteX libraries whose aims are to lower entry level of -complex FPGA cores by providing simple, elegant and efficient implementations -ofcomponents used in today's SoC such as Ethernet, SATA, PCIe, SDRAM Controller... - -The core uses simple and specific streaming buses and will provides in the future -adapters to use standardized AXI or Avalon-ST streaming buses. - -Since Python is used to describe the HDL, the core is highly and easily -configurable. - -LiteEth uses technologies developed in partnership with M-Labs Ltd: - - Migen enables generating HDL with Python in an efficient way. - - MiSoC provides the basic blocks to build a powerful and small footprint SoC. - -LiteEth can be used as a Migen/MiSoC library (by simply installing it -with the provided setup.py) or can be integrated with your standard design flow -by generating the verilog rtl that you will use as a standard core. - -.. _about-toolchain: - -Features -======== -- Ethernet MAC with various interfaces and various PHYs (GMII, MII, Loopback) -- Hardware UDP/IP stack with ARP and ICMP - -Possibles improvements -====================== -- add standardized interfaces (AXI, Avalon-ST) -- add DMA interface to MAC -- add hardware Etherbone support -- add RGMII/SGMII PHYs -- ... See below Support and Consulting :) - -Support and Consulting -====================== -We love open-source hardware and like sharing our designs with others. - -LiteEth is developed and maintained by EnjoyDigital. - -If you would like to know more about LiteEth or if you are already a happy user -and would like to extend it for your needs, EnjoyDigital can provide standard -commercial support as well as consulting services. - -So feel free to contact us, we'd love to work with you! (and eventually shorten -the list of the possible improvements :) - -Contact -======= -E-mail: florent [AT] enjoy-digital.fr - - diff --git a/doc/source/docs/intro/community.rst b/doc/source/docs/intro/community.rst deleted file mode 100644 index 6d1ef429..00000000 --- a/doc/source/docs/intro/community.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. _community: - -========= -Community -========= diff --git a/doc/source/docs/intro/index.rst b/doc/source/docs/intro/index.rst deleted file mode 100644 index 9b013af1..00000000 --- a/doc/source/docs/intro/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _intro-index: - -====================== -Introducing LiteEth -====================== - -This section explains what LiteEth does, why it is needed, its limitations and its licensing. After reading, you will understand whether LiteEth is the right core for you, and where to go if you have :ref:`further questions `. - -.. toctree:: - :maxdepth: 1 - - about - license - release_notes - talks_and_publications diff --git a/doc/source/docs/intro/license.rst b/doc/source/docs/intro/license.rst deleted file mode 100644 index 7bedbf93..00000000 --- a/doc/source/docs/intro/license.rst +++ /dev/null @@ -1,47 +0,0 @@ -.. _license: - -=================== -Open Source License -=================== - -LiteEth is released under the very permissive two-clause BSD license. Under the -terms of this license, you are authorized to use LiteEth for closed-source -proprietary designs. -Even though we do not require you to do so, those things are awesome, so please -do them if possible: - - tell us that you are using LiteEth - - cite LiteEth in publications related to research it has helped - - send us feedback and suggestions for improvements - - send us bug reports when something goes wrong - - send us the modifications and improvements you have done to LiteEth. - -:: - - Unless otherwise noted, LiteEth is copyright (C) 2015 Florent Kermarrec. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - Other authors retain ownership of their contributions. If a submission can - reasonably be considered independently copyrightable, it's yours and we - encourage you to claim it with appropriate copyright notices. This submission - then falls under the "otherwise noted" category. All submissions are strongly - encouraged to use the two-clause BSD license reproduced above. diff --git a/doc/source/docs/intro/release_notes.rst b/doc/source/docs/intro/release_notes.rst deleted file mode 100644 index 9cf90a83..00000000 --- a/doc/source/docs/intro/release_notes.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _release-notes: - -============= -Release Notes -============= - -.. _ChangeLog: - -ChangeLog -========= -0.9.0: - - First 0.9.0 release supporting hardware UDP/IP stack with ICMP and ARP. - diff --git a/doc/source/docs/intro/talks_and_publications.rst b/doc/source/docs/intro/talks_and_publications.rst deleted file mode 100644 index 071a4e6a..00000000 --- a/doc/source/docs/intro/talks_and_publications.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _talks-and-publications: - -====================== -Talks and Publications -====================== - -- Migen / MiSoC documentation: - - `User guide `_ (`m-labs `_) - - `Tutorial: An introduction to Migen `_ (`m-labs `_) - -- Migen / MiSoC presentations: - - `Lecture slides `_ (`sbourdeauducq `_) - - `EHSM 2012 presentation `_ (`sbourdeauducq `_) - - `ORCONF2014 `_ (`fallen `_) diff --git a/doc/source/home_page_layout.html b/doc/source/home_page_layout.html deleted file mode 100644 index 6c8e98c1..00000000 --- a/doc/source/home_page_layout.html +++ /dev/null @@ -1,26 +0,0 @@ -./_static/LiteEth_logo_full.png - -

A small footprint and configurable Ethernet core with UDP/IP hw stack and Etherbone frontend.

- -
- - - - -
- diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index 78dd8240..00000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. title:: Main - -.. _home-page: - -.. raw:: html - :file: home_page_layout.html - - ------ - -News -==== - -- First 0.9.0 release supporting hardware UDP/IP stack with ICMP and ARP. - -.. toctree:: - :hidden: - - docs/intro/index - docs/getting_started/index - docs/core/index - docs/frontend/index - docs/site/about - diff --git a/liteeth-version.txt b/liteeth-version.txt deleted file mode 100644 index eba33402..00000000 --- a/liteeth-version.txt +++ /dev/null @@ -1,2 +0,0 @@ -0.9.0 - diff --git a/liteeth/__init__.py b/liteeth/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/liteeth/common.py b/liteeth/common.py deleted file mode 100644 index 3d00ad2f..00000000 --- a/liteeth/common.py +++ /dev/null @@ -1,284 +0,0 @@ -import math -from collections import OrderedDict - -from migen.fhdl.std import * -from migen.fhdl.decorators import ModuleDecorator -from migen.genlib.resetsync import AsyncResetSynchronizer -from migen.genlib.record import * -from migen.genlib.fsm import FSM, NextState -from migen.genlib.misc import chooser -from migen.flow.actor import * -from migen.flow.plumbing import Buffer -from migen.actorlib.structuring import Converter, Pipeline -from migen.actorlib.fifo import SyncFIFO, AsyncFIFO -from migen.bank.description import * - -eth_mtu = 1532 -eth_min_len = 46 -eth_interpacket_gap = 12 -eth_preamble = 0xD555555555555555 -buffer_depth = 2**log2_int(eth_mtu, need_pow2=False) - -class HField(): - def __init__(self, byte, offset, width): - self.byte = byte - self.offset = offset - self.width = width - -ethernet_type_ip = 0x800 -ethernet_type_arp = 0x806 - -mac_header_len = 14 -mac_header = { - "target_mac": HField(0, 0, 48), - "sender_mac": HField(6, 0, 48), - "ethernet_type": HField(12, 0, 16) -} - -arp_hwtype_ethernet = 0x0001 -arp_proto_ip = 0x0800 -arp_opcode_request = 0x0001 -arp_opcode_reply = 0x0002 - -arp_header_len = 28 -arp_header = { - "hwtype": HField( 0, 0, 16), - "proto": HField( 2, 0, 16), - "hwsize": HField( 4, 0, 8), - "protosize": HField( 5, 0, 8), - "opcode": HField( 6, 0, 16), - "sender_mac": HField( 8, 0, 48), - "sender_ip": HField(14, 0, 32), - "target_mac": HField(18, 0, 48), - "target_ip": HField(24, 0, 32) -} - -ipv4_header_len = 20 -ipv4_header = { - "ihl": HField(0, 0, 4), - "version": HField(0, 4, 4), - "total_length": HField(2, 0, 16), - "identification": HField(4, 0, 16), - "ttl": HField(8, 0, 8), - "protocol": HField(9, 0, 8), - "checksum": HField(10, 0, 16), - "sender_ip": HField(12, 0, 32), - "target_ip": HField(16, 0, 32) -} - -icmp_header_len = 8 -icmp_header = { - "msgtype": HField( 0, 0, 8), - "code": HField( 1, 0, 8), - "checksum": HField( 2, 0, 16), - "quench": HField( 4, 0, 32) -} -icmp_protocol = 0x01 - -udp_header_len = 8 -udp_header = { - "src_port": HField( 0, 0, 16), - "dst_port": HField( 2, 0, 16), - "length": HField( 4, 0, 16), - "checksum": HField( 6, 0, 16) -} - -udp_protocol = 0x11 - -etherbone_magic = 0x4e6f -etherbone_version = 1 -etherbone_packet_header_len = 8 -etherbone_packet_header = { - "magic": HField( 0, 0, 16), - - "version": HField( 2, 4, 4), - "nr": HField( 2, 2, 1), - "pr": HField( 2, 1, 1), - "pf": HField( 2, 0, 1), - - "addr_size": HField( 3, 4, 4), - "port_size": HField( 3, 0, 4) -} - -etherbone_record_header_len = 4 -etherbone_record_header = { - "bca": HField( 0, 0, 1), - "rca": HField( 0, 1, 1), - "rff": HField( 0, 2, 1), - "cyc": HField( 0, 4, 1), - "wca": HField( 0, 5, 1), - "wff": HField( 0, 6, 1), - - "byte_enable": HField( 1, 0, 8), - - "wcount": HField( 2, 0, 8), - - "rcount": HField( 3, 0, 8) -} - -def reverse_bytes(v): - n = math.ceil(flen(v)/8) - r = [] - for i in reversed(range(n)): - r.append(v[i*8:min((i+1)*8, flen(v))]) - return Cat(iter(r)) - -# layouts -def _layout_from_header(header): - _layout = [] - for k, v in sorted(header.items()): - _layout.append((k, v.width)) - return _layout - -def _remove_from_layout(layout, *args): - r = [] - for f in layout: - remove = False - for arg in args: - if f[0] == arg: - remove = True - if not remove: - r.append(f) - return r -def eth_phy_description(dw): - payload_layout = [ - ("data", dw), - ("last_be", dw//8), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, packetized=True) - -def eth_mac_description(dw): - payload_layout = _layout_from_header(mac_header) + [ - ("data", dw), - ("last_be", dw//8), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, packetized=True) - -def eth_arp_description(dw): - param_layout = _layout_from_header(arp_header) - payload_layout = [ - ("data", dw), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, param_layout, packetized=True) - -arp_table_request_layout = [ - ("ip_address", 32) -] - -arp_table_response_layout = [ - ("failed", 1), - ("mac_address", 48) -] - -def eth_ipv4_description(dw): - param_layout = _layout_from_header(ipv4_header) - payload_layout = [ - ("data", dw), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, param_layout, packetized=True) - -def eth_ipv4_user_description(dw): - param_layout = [ - ("length", 16), - ("protocol", 8), - ("ip_address", 32) - ] - payload_layout = [ - ("data", dw), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, param_layout, packetized=True) - -def convert_ip(s): - ip = 0 - for e in s.split("."): - ip = ip << 8 - ip += int(e) - return ip - -def eth_icmp_description(dw): - param_layout = _layout_from_header(icmp_header) - payload_layout = [ - ("data", dw), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, param_layout, packetized=True) - -def eth_icmp_user_description(dw): - param_layout = _layout_from_header(icmp_header) + [ - ("ip_address", 32), - ("length", 16) - ] - payload_layout = [ - ("data", dw), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, param_layout, packetized=True) - -def eth_udp_description(dw): - param_layout = _layout_from_header(udp_header) - payload_layout = [ - ("data", dw), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, param_layout, packetized=True) - -def eth_udp_user_description(dw): - param_layout = [ - ("src_port", 16), - ("dst_port", 16), - ("ip_address", 32), - ("length", 16) - ] - payload_layout = [ - ("data", dw), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, param_layout, packetized=True) - -def eth_etherbone_packet_description(dw): - param_layout = _layout_from_header(etherbone_packet_header) - payload_layout = [ - ("data", dw), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, param_layout, packetized=True) - -def eth_etherbone_packet_user_description(dw): - param_layout = _layout_from_header(etherbone_packet_header) - param_layout = _remove_from_layout(param_layout, "magic", "portsize", "addrsize", "version") - param_layout += eth_udp_user_description(dw).param_layout - payload_layout = [ - ("data", dw), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, param_layout, packetized=True) - -def eth_etherbone_record_description(dw): - param_layout = _layout_from_header(etherbone_record_header) - payload_layout = [ - ("data", dw), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, param_layout, packetized=True) - -def eth_etherbone_mmap_description(dw): - param_layout = [ - ("we", 1), - ("count", 8), - ("base_addr", 32), - ("be", dw//8) - ] - payload_layout = [ - ("addr", 32), - ("data", dw) - ] - return EndpointDescription(payload_layout, param_layout, packetized=True) - -def eth_tty_description(dw): - payload_layout = [("data", dw)] - return EndpointDescription(payload_layout, packetized=False) diff --git a/liteeth/core/__init__.py b/liteeth/core/__init__.py deleted file mode 100644 index 4d9b7147..00000000 --- a/liteeth/core/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from liteeth.mac import LiteEthMAC -from liteeth.core.arp import LiteEthARP -from liteeth.core.ip import LiteEthIP -from liteeth.core.udp import LiteEthUDP -from liteeth.core.icmp import LiteEthICMP - -class LiteEthIPCore(Module, AutoCSR): - def __init__(self, phy, mac_address, ip_address, clk_freq): - self.submodules.mac = LiteEthMAC(phy, 8, interface="crossbar", with_hw_preamble_crc=True) - self.submodules.arp = LiteEthARP(self.mac, mac_address, ip_address, clk_freq) - self.submodules.ip = LiteEthIP(self.mac, mac_address, ip_address, self.arp.table) - self.submodules.icmp = LiteEthICMP(self.ip, ip_address) - -class LiteEthUDPIPCore(LiteEthIPCore): - def __init__(self, phy, mac_address, ip_address, clk_freq): - LiteEthIPCore.__init__(self, phy, mac_address, ip_address, clk_freq) - self.submodules.udp = LiteEthUDP(self.ip, ip_address) diff --git a/liteeth/core/arp/__init__.py b/liteeth/core/arp/__init__.py deleted file mode 100644 index 638505c3..00000000 --- a/liteeth/core/arp/__init__.py +++ /dev/null @@ -1,263 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from liteeth.generic.depacketizer import LiteEthDepacketizer -from liteeth.generic.packetizer import LiteEthPacketizer - -_arp_table_layout = [ - ("reply", 1), - ("request", 1), - ("ip_address", 32), - ("mac_address", 48) - ] - -class LiteEthARPPacketizer(LiteEthPacketizer): - def __init__(self): - LiteEthPacketizer.__init__(self, - eth_arp_description(8), - eth_mac_description(8), - arp_header, - arp_header_len) - -class LiteEthARPTX(Module): - def __init__(self, mac_address, ip_address): - self.sink = sink = Sink(_arp_table_layout) - self.source = source = Source(eth_mac_description(8)) - ### - self.submodules.packetizer = packetizer = LiteEthARPPacketizer() - - counter = Counter(max=max(arp_header_len, eth_min_len)) - self.submodules += counter - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - sink.ack.eq(1), - counter.reset.eq(1), - If(sink.stb, - sink.ack.eq(0), - NextState("SEND") - ) - ) - self.comb += [ - packetizer.sink.sop.eq(counter.value == 0), - packetizer.sink.eop.eq(counter.value == max(arp_header_len, eth_min_len)-1), - packetizer.sink.hwtype.eq(arp_hwtype_ethernet), - packetizer.sink.proto.eq(arp_proto_ip), - packetizer.sink.hwsize.eq(6), - packetizer.sink.protosize.eq(4), - packetizer.sink.sender_mac.eq(mac_address), - packetizer.sink.sender_ip.eq(ip_address), - If(sink.reply, - packetizer.sink.opcode.eq(arp_opcode_reply), - packetizer.sink.target_mac.eq(sink.mac_address), - packetizer.sink.target_ip.eq(sink.ip_address) - ).Elif(sink.request, - - packetizer.sink.opcode.eq(arp_opcode_request), - packetizer.sink.target_mac.eq(0xffffffffffff), - packetizer.sink.target_ip.eq(sink.ip_address) - ) - ] - fsm.act("SEND", - packetizer.sink.stb.eq(1), - Record.connect(packetizer.source, source), - source.target_mac.eq(packetizer.sink.target_mac), - source.sender_mac.eq(mac_address), - source.ethernet_type.eq(ethernet_type_arp), - If(source.stb & source.ack, - counter.ce.eq(1), - If(source.eop, - sink.ack.eq(1), - NextState("IDLE") - ) - ) - ) - -class LiteEthARPDepacketizer(LiteEthDepacketizer): - def __init__(self): - LiteEthDepacketizer.__init__(self, - eth_mac_description(8), - eth_arp_description(8), - arp_header, - arp_header_len) - -class LiteEthARPRX(Module): - def __init__(self, mac_address, ip_address): - self.sink = sink = Sink(eth_mac_description(8)) - self.source = source = Source(_arp_table_layout) - ### - self.submodules.depacketizer = depacketizer = LiteEthARPDepacketizer() - self.comb += Record.connect(sink, depacketizer.sink) - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - depacketizer.source.ack.eq(1), - If(depacketizer.source.stb & depacketizer.source.sop, - depacketizer.source.ack.eq(0), - NextState("CHECK") - ) - ) - valid = Signal() - self.sync += valid.eq( - depacketizer.source.stb & - (depacketizer.source.hwtype == arp_hwtype_ethernet) & - (depacketizer.source.proto == arp_proto_ip) & - (depacketizer.source.hwsize == 6) & - (depacketizer.source.protosize == 4) & - (depacketizer.source.target_ip == ip_address) - ) - reply = Signal() - request = Signal() - self.comb += Case(depacketizer.source.opcode, { - arp_opcode_request : [request.eq(1)], - arp_opcode_reply : [reply.eq(1)], - "default" : [] - }) - self.comb += [ - source.ip_address.eq(depacketizer.source.sender_ip), - source.mac_address.eq(depacketizer.source.sender_mac) - ] - fsm.act("CHECK", - If(valid, - source.stb.eq(1), - source.reply.eq(reply), - source.request.eq(request) - ), - NextState("TERMINATE") - ), - fsm.act("TERMINATE", - depacketizer.source.ack.eq(1), - If(depacketizer.source.stb & depacketizer.source.eop, - NextState("IDLE") - ) - ) - -class LiteEthARPTable(Module): - def __init__(self, clk_freq, max_requests=8): - self.sink = sink = Sink(_arp_table_layout) # from arp_rx - self.source = source = Source(_arp_table_layout) # to arp_tx - - # Request/Response interface - self.request = request = Sink(arp_table_request_layout) - self.response = response = Source(arp_table_response_layout) - ### - request_timeout = Timeout(clk_freq//10) - request_counter = Counter(max=max_requests) - request_pending = FlipFlop() - request_ip_address = FlipFlop(32) - self.submodules += request_timeout, request_counter, request_pending, request_ip_address - self.comb += [ - request_timeout.ce.eq(request_pending.q), - request_pending.d.eq(1), - request_ip_address.d.eq(request.ip_address) - ] - - # Note: Store only 1 IP/MAC couple, can be improved with a real - # table in the future to improve performance when packets are - # targeting multiple destinations. - update = Signal() - cached_valid = Signal() - cached_ip_address = Signal(32) - cached_mac_address = Signal(48) - cached_timeout = Timeout(clk_freq*10) - self.submodules += cached_timeout - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - # Note: for simplicicy, if APR table is busy response from arp_rx - # is lost. This is compensated by the protocol (retries) - If(sink.stb & sink.request, - NextState("SEND_REPLY") - ).Elif(sink.stb & sink.reply & request_pending.q, - NextState("UPDATE_TABLE"), - ).Elif(request_counter.value == max_requests-1, - NextState("PRESENT_RESPONSE") - ).Elif(request.stb | (request_pending.q & request_timeout.reached), - NextState("CHECK_TABLE") - ) - ) - fsm.act("SEND_REPLY", - source.stb.eq(1), - source.reply.eq(1), - source.ip_address.eq(sink.ip_address), - If(source.ack, - NextState("IDLE") - ) - ) - fsm.act("UPDATE_TABLE", - request_pending.reset.eq(1), - update.eq(1), - NextState("CHECK_TABLE") - ) - self.sync += [ - If(update, - cached_valid.eq(1), - cached_ip_address.eq(sink.ip_address), - cached_mac_address.eq(sink.mac_address), - cached_timeout.reset.eq(1) - ).Else( - cached_timeout.ce.eq(1), - If(cached_timeout.reached, - cached_valid.eq(0) - ) - ) - ] - found = Signal() - fsm.act("CHECK_TABLE", - If(cached_valid, - If(request_ip_address.q == cached_ip_address, - request_ip_address.reset.eq(1), - NextState("PRESENT_RESPONSE"), - ).Elif(request.ip_address == cached_ip_address, - request.ack.eq(request.stb), - NextState("PRESENT_RESPONSE"), - ).Else( - request_ip_address.ce.eq(request.stb), - NextState("SEND_REQUEST") - ) - ).Else( - request_ip_address.ce.eq(request.stb), - NextState("SEND_REQUEST") - ) - ) - fsm.act("SEND_REQUEST", - source.stb.eq(1), - source.request.eq(1), - source.ip_address.eq(request_ip_address.q), - If(source.ack, - request_timeout.reset.eq(1), - request_counter.reset.eq(request.stb), - request_counter.ce.eq(1), - request_pending.ce.eq(1), - request.ack.eq(1), - NextState("IDLE") - ) - ) - self.comb += [ - If(request_counter == max_requests-1, - response.failed.eq(1), - request_counter.reset.eq(1), - request_pending.reset.eq(1) - ), - response.mac_address.eq(cached_mac_address) - ] - fsm.act("PRESENT_RESPONSE", - response.stb.eq(1), - If(response.ack, - NextState("IDLE") - ) - ) - -class LiteEthARP(Module): - def __init__(self, mac, mac_address, ip_address, clk_freq): - self.submodules.tx = tx = LiteEthARPTX(mac_address, ip_address) - self.submodules.rx = rx = LiteEthARPRX(mac_address, ip_address) - self.submodules.table = table = LiteEthARPTable(clk_freq) - self.comb += [ - Record.connect(rx.source, table.sink), - Record.connect(table.source, tx.sink) - ] - mac_port = mac.crossbar.get_port(ethernet_type_arp) - self.comb += [ - Record.connect(tx.source, mac_port.sink), - Record.connect(mac_port.source, rx.sink) - ] diff --git a/liteeth/core/etherbone/__init__.py b/liteeth/core/etherbone/__init__.py deleted file mode 100644 index 78f3d738..00000000 --- a/liteeth/core/etherbone/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from liteeth.generic.arbiter import Arbiter -from liteeth.generic.dispatcher import Dispatcher -from liteeth.core.etherbone.packet import * -from liteeth.core.etherbone.probe import * -from liteeth.core.etherbone.record import * -from liteeth.core.etherbone.wishbone import * - -class LiteEthEtherbone(Module): - def __init__(self, udp, udp_port): - # decode/encode etherbone packets - self.submodules.packet = packet = LiteEthEtherbonePacket(udp, udp_port) - - # packets can be probe (etherbone discovering) or records with - # writes and reads - self.submodules.probe = probe = LiteEthEtherboneProbe() - self.submodules.record = record = LiteEthEtherboneRecord() - - # arbitrate/dispatch probe/records packets - dispatcher = Dispatcher(packet.source, [probe.sink, record.sink]) - self.comb += dispatcher.sel.eq(~packet.source.pf) - arbiter = Arbiter([probe.source, record.source], packet.sink) - self.submodules += dispatcher, arbiter - - # create mmap ŵishbone master - self.submodules.master = master = LiteEthEtherboneWishboneMaster() - self.comb += [ - Record.connect(record.receiver.source, master.sink), - Record.connect(master.source, record.sender.sink) - ] diff --git a/liteeth/core/etherbone/dissector/bit.lua b/liteeth/core/etherbone/dissector/bit.lua deleted file mode 100644 index d9fd4ceb..00000000 --- a/liteeth/core/etherbone/dissector/bit.lua +++ /dev/null @@ -1,260 +0,0 @@ ---[[--------------- -LuaBit v0.4 -------------------- -a bitwise operation lib for lua. - -http://luaforge.net/projects/bit/ - -How to use: -------------------- - bit.bnot(n) -- bitwise not (~n) - bit.band(m, n) -- bitwise and (m & n) - bit.bor(m, n) -- bitwise or (m | n) - bit.bxor(m, n) -- bitwise xor (m ^ n) - bit.brshift(n, bits) -- right shift (n >> bits) - bit.blshift(n, bits) -- left shift (n << bits) - bit.blogic_rshift(n, bits) -- logic right shift(zero fill >>>) - -Please note that bit.brshift and bit.blshift only support number within -32 bits. - -2 utility functions are provided too: - bit.tobits(n) -- convert n into a bit table(which is a 1/0 sequence) - -- high bits first - bit.tonumb(bit_tbl) -- convert a bit table into a number -------------------- - -Under the MIT license. - -copyright(c) 2006~2007 hanzhao (abrash_han@hotmail.com) ---]]--------------- - -do - ------------------------- --- bit lib implementions - -local function check_int(n) - -- checking not float - if(n - math.floor(n) > 0) then - error("trying to use bitwise operation on non-integer!") - end -end - -local function to_bits(n) - check_int(n) - if(n < 0) then - -- negative - return to_bits(bit.bnot(math.abs(n)) + 1) - end - -- to bits table - local tbl = {} - local cnt = 1 - while (n > 0) do - local last = math.mod(n,2) - if(last == 1) then - tbl[cnt] = 1 - else - tbl[cnt] = 0 - end - n = (n-last)/2 - cnt = cnt + 1 - end - - return tbl -end - -local function tbl_to_number(tbl) - local n = table.getn(tbl) - - local rslt = 0 - local power = 1 - for i = 1, n do - rslt = rslt + tbl[i]*power - power = power*2 - end - - return rslt -end - -local function expand(tbl_m, tbl_n) - local big = {} - local small = {} - if(table.getn(tbl_m) > table.getn(tbl_n)) then - big = tbl_m - small = tbl_n - else - big = tbl_n - small = tbl_m - end - -- expand small - for i = table.getn(small) + 1, table.getn(big) do - small[i] = 0 - end - -end - -local function bit_or(m, n) - local tbl_m = to_bits(m) - local tbl_n = to_bits(n) - expand(tbl_m, tbl_n) - - local tbl = {} - local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) - for i = 1, rslt do - if(tbl_m[i]== 0 and tbl_n[i] == 0) then - tbl[i] = 0 - else - tbl[i] = 1 - end - end - - return tbl_to_number(tbl) -end - -local function bit_and(m, n) - local tbl_m = to_bits(m) - local tbl_n = to_bits(n) - expand(tbl_m, tbl_n) - - local tbl = {} - local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) - for i = 1, rslt do - if(tbl_m[i]== 0 or tbl_n[i] == 0) then - tbl[i] = 0 - else - tbl[i] = 1 - end - end - - return tbl_to_number(tbl) -end - -local function bit_not(n) - - local tbl = to_bits(n) - local size = math.max(table.getn(tbl), 32) - for i = 1, size do - if(tbl[i] == 1) then - tbl[i] = 0 - else - tbl[i] = 1 - end - end - return tbl_to_number(tbl) -end - -local function bit_xor(m, n) - local tbl_m = to_bits(m) - local tbl_n = to_bits(n) - expand(tbl_m, tbl_n) - - local tbl = {} - local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) - for i = 1, rslt do - if(tbl_m[i] ~= tbl_n[i]) then - tbl[i] = 1 - else - tbl[i] = 0 - end - end - - --table.foreach(tbl, print) - - return tbl_to_number(tbl) -end - -local function bit_rshift(n, bits) - check_int(n) - - local high_bit = 0 - if(n < 0) then - -- negative - n = bit_not(math.abs(n)) + 1 - high_bit = 2147483648 -- 0x80000000 - end - - for i=1, bits do - n = n/2 - n = bit_or(math.floor(n), high_bit) - end - return math.floor(n) -end - --- logic rightshift assures zero filling shift -local function bit_logic_rshift(n, bits) - check_int(n) - if(n < 0) then - -- negative - n = bit_not(math.abs(n)) + 1 - end - for i=1, bits do - n = n/2 - end - return math.floor(n) -end - -local function bit_lshift(n, bits) - check_int(n) - - if(n < 0) then - -- negative - n = bit_not(math.abs(n)) + 1 - end - - for i=1, bits do - n = n*2 - end - return bit_and(n, 4294967295) -- 0xFFFFFFFF -end - -local function bit_xor2(m, n) - local rhs = bit_or(bit_not(m), bit_not(n)) - local lhs = bit_or(m, n) - local rslt = bit_and(lhs, rhs) - return rslt -end - --------------------- --- bit lib interface - -bit = { - -- bit operations - bnot = bit_not, - band = bit_and, - bor = bit_or, - bxor = bit_xor, - brshift = bit_rshift, - blshift = bit_lshift, - bxor2 = bit_xor2, - blogic_rshift = bit_logic_rshift, - - -- utility func - tobits = to_bits, - tonumb = tbl_to_number, -} - -end - ---[[ -for i = 1, 100 do - for j = 1, 100 do - if(bit.bxor(i, j) ~= bit.bxor2(i, j)) then - error("bit.xor failed.") - end - end -end ---]] - - - - - - - - - - - - - diff --git a/liteeth/core/etherbone/dissector/etherbone.lua b/liteeth/core/etherbone/dissector/etherbone.lua deleted file mode 100644 index 0a629a43..00000000 --- a/liteeth/core/etherbone/dissector/etherbone.lua +++ /dev/null @@ -1,223 +0,0 @@ --- Etherbone Dissector --- Copyright 2013 OHWR.org --- Copyright 2015 EnjoyDigital (global clean up) - -local bit = require("bit") - -local VALS_BOOL = {[0] = "False", [1] = "True"} -local VALS_RES = {[0] = "not set", [1] = "set, bad data?"} -local VALS_SIZE = { -[0x00] = "Bad Value", -[0x01] = "8 bit", -[0x02] = "16 bit", -[0x03] = "16,8 bit", -[0x04] = "32 bit", -[0x05] = "32,8 bit", -[0x06] = "32,16 bit", -[0x07] = "32,16,8 bit", -[0x08] = "64 bit", -[0x09] = "64,8 bit", -[0x0A] = "64,16 bit", -[0x0B] = "64,16,8 bit", -[0x0C] = "64,32 bit", -[0x0D] = "64,32,8 bit", -[0x0E] = "64,32,16 bit", -[0x0F] = "64,32,16,8 bit", -} - -function num2hex(num) - local hexstr = '0123456789abcdef' - local s = '' - while num > 0 do - local mod = math.fmod(num, 16) - s = string.sub(hexstr, mod+1, mod+1) .. s - num = math.floor(num / 16) - end - if s == '' then s = '0' end - return s -end - -function max(a, b) - if a > b then - return a - else - return b - end -end - --- declare protocol -proto_eb = Proto("eb", "Etherbone") - --- declare fields -local eb = proto_eb.fields -eb.hdr = ProtoField.uint32("eb.hdr", "Header", base.HEX) -eb.rec = ProtoField.bytes("eb.rec", "Record ", base.HEX) - -eb.hdr_magic = ProtoField.uint16("eb.hdr.magic", "Magic ", base.HEX, nil, 0xFFFF) -eb.hdr_ver = ProtoField.uint16("eb.hdr.ver", "Version ", base.DEC, nil, 0xF000) -eb.hdr_noreads = ProtoField.uint16("eb.hdr.noreads", "No Reads ", base.DEC, VALS_BOOL, 0x0400) -eb.hdr_proberep = ProtoField.uint16("eb.hdr.proberes", "Probe Reply ", base.DEC, VALS_BOOL, 0x0200) -eb.hdr_probereq = ProtoField.uint16("eb.hdr.probereq", "Probe Flag ", base.DEC, VALS_BOOL, 0x0100) -eb.hdr_adrs = ProtoField.uint16("eb.hdr.adrw", "Address Width ", base.DEC, VALS_SIZE , 0x00F0) -eb.hdr_ports = ProtoField.uint16("eb.hdr.portw", "Port Width ", base.DEC, VALS_SIZE , 0x000F) - -eb.rec_hdr = ProtoField.uint32("eb.rec.hdr", "Header ", base.HEX) -eb.rec_writes = ProtoField.bytes("eb.rec.writes", "Writes ", base.HEX) -eb.rec_reads = ProtoField.bytes("eb.rec.reads", "Reads ", base.HEX) - -eb.rec_hdr_flags = ProtoField.uint8("eb.rec.hdr.flags", "Flags ", base.HEX) -eb.rec_hdr_select = ProtoField.uint8("eb.rec.hdr.select", "Select ", base.HEX) -eb.rec_hdr_wr = ProtoField.uint8("eb.rec.hdr.wr", "Writes ", base.DEC) -eb.rec_hdr_rd = ProtoField.uint8("eb.rec.hdr.rd", "Reads ", base.DEC) - -eb.rec_hdr_flags_adrcfg = ProtoField.uint8("eb.rec.hdr.flags.adrcfg", "ReplyToCfgSpace ", base.DEC, VALS_BOOL, 0x80) -eb.rec_hdr_flags_rbacfg = ProtoField.uint8("eb.rec.hdr.adrcfg", "ReadFromCfgSpace ", base.DEC, VALS_BOOL, 0x40) -eb.rec_hdr_flags_rdfifo = ProtoField.uint8("eb.rec.hdr.adrcfg", "ReadFIFO ", base.DEC, VALS_BOOL, 0x20) -eb.rec_hdr_flags_dropcyc= ProtoField.uint8("eb.rec.hdr.adrcfg", "DropCycle ", base.DEC, VALS_BOOL, 0x08) -eb.rec_hdr_flags_wbacfg = ProtoField.uint8("eb.rec.hdr.adrcfg", "WriteToCfgSpace ", base.DEC, VALS_BOOL, 0x04) -eb.rec_hdr_flags_wrfifo = ProtoField.uint8("eb.rec.hdr.adrcfg", "WriteFIFO ", base.DEC, VALS_BOOL, 0x02) - -eb.rec_wrsadr8 = ProtoField.uint8("eb.rec.wrsadr8", "BaseAddr8 ", base.HEX) -eb.rec_wrsadr16 = ProtoField.uint16("eb.rec.wrsadr16", "BaseAddr16 ", base.HEX) -eb.rec_wrsadr32 = ProtoField.uint32("eb.rec.wrsadr32", "BaseAddr32 ", base.HEX) -eb.rec_wrsadr64 = ProtoField.uint64("eb.rec.wrsadr64", "BaseAddr64 ", base.HEX) -eb.rec_wrdata8 = ProtoField.uint8("eb.rec.wrdata8", "Value8 ", base.HEX) -eb.rec_wrdata16 = ProtoField.uint16("eb.rec.wrdata16", "Value16 ", base.HEX) -eb.rec_wrdata32 = ProtoField.uint32("eb.rec.wrdata32", "Value32 ", base.HEX) -eb.rec_wrdata64 = ProtoField.uint64("eb.rec.wrdata64", "Value64 ", base.HEX) - -eb.rec_rdbadr8 = ProtoField.uint8("eb.rec.rdbadr8", "ReplyAddr8 ", base.HEX) -eb.rec_rdbadr16 = ProtoField.uint16("eb.rec.rdbadr16", "ReplyAddr16 ", base.HEX) -eb.rec_rdbadr32 = ProtoField.uint32("eb.rec.rdbadr32", "ReplyAddr32 ", base.HEX) -eb.rec_rdbadr64 = ProtoField.uint64("eb.rec.rdbadr64", "ReplyAddr64 ", base.HEX) -eb.rec_rddata8 = ProtoField.uint8("eb.rec.rddata8", "Address8 ", base.HEX) -eb.rec_rddata16 = ProtoField.uint16("eb.rec.rddata16", "Address16 ", base.HEX) -eb.rec_rddata32 = ProtoField.uint32("eb.rec.rddata32", "Address32 ", base.HEX) -eb.rec_rddata64 = ProtoField.uint64("eb.rec.rddata64", "Address64 ", base.HEX) - --- define the dissector -function proto_eb.dissector(buf, pinfo, tree) - if (buf:len() < 4) then - return 0 -- too short, go to default protocol - end - - local mylen = buf:len() - pinfo.cols.protocol = "eb" - - -- add packet to the tree root, fields will be added to subtree - local t = tree:add( proto_eb, buf(0, mylen) ) - local t_hdr = t:add( eb.hdr, buf(0,4) ) - - local magic = num2hex(tonumber(buf(0,2):uint())) - if(magic == "4e6f") then - - t_hdr:add( eb.hdr_magic, buf(0,2)) -- magic - t_hdr:add( eb.hdr_ver, buf(2,2)) -- version - t_hdr:add( eb.hdr_noreads, buf(2,2)) -- no reads - t_hdr:add( eb.hdr_proberep, buf(2,2)) -- probe response - t_hdr:add( eb.hdr_probereq, buf(2,2)) -- probe request - - t_hdr:add( eb.hdr_adrs, buf(2,2)) -- supported addr size - t_hdr:add( eb.hdr_ports, buf(2,2)) -- supported port size - - local probe = tonumber(buf(2,1):uint()) % 4 - if (probe == 0) then - local widths = tonumber(buf(3,1):uint()) - local data_width = widths % 16 - local addr_width = (widths - data_width) / 16 - local alignment = max(max(addr_width, data_width), 2) - - local record_alignment = max(alignment, 4) - local offset = max(alignment, 4) - - local recordcnt = 0 - while (offset < buf:len()) do - local wr = tonumber(buf(offset+2,1):uint()) - local rd = tonumber(buf(offset+3,1):uint()) - - local rdadr = 0 - local wradr = 0 - if(rd > 0) then - rdadr = 1 - end - if(wr > 0) then - wradr = 1 - end - - if((wr == 0) and (rd == 0)) then - offset = offset + record_alignment - else - local t_rec = t:add( "Record "..tostring(recordcnt).." (W"..tostring(wr).." R"..tostring(rd)..")", buf(offset, (record_alignment+(rd+wr+rdadr+wradr)*alignment))) - recordcnt = recordcnt + 1 - - local t_rec_hdr = t_rec:add( eb.rec_hdr, buf(offset,4)) - local t_rec_hdr_flags = t_rec_hdr:add( eb.rec_hdr_flags, buf(offset,1)) - t_rec_hdr_flags:add( eb.rec_hdr_flags_adrcfg, buf(offset,1)) - t_rec_hdr_flags:add( eb.rec_hdr_flags_rbacfg, buf(offset,1)) - t_rec_hdr_flags:add( eb.rec_hdr_flags_rdfifo, buf(offset,1)) - t_rec_hdr_flags:add( eb.rec_hdr_flags_dropcyc , buf(offset,1)) - t_rec_hdr_flags:add( eb.rec_hdr_flags_wbacfg , buf(offset,1)) - t_rec_hdr_flags:add( eb.rec_hdr_flags_wrfifo, buf(offset,1)) - t_rec_hdr:add( eb.rec_hdr_select, buf(offset+1,1)) - t_rec_hdr:add( eb.rec_hdr_wr, buf(offset+2,1)) - t_rec_hdr:add( eb.rec_hdr_rd, buf(offset+3,1)) - offset = offset + record_alignment - local tmp_offset - - if(wr > 0) then - local t_writes = t_rec:add( eb.rec_writes, buf(offset,(1+wr)*alignment)) - - if addr_width==1 then t_writes:add(eb.rec_wrsadr8, buf(offset+alignment-1, 1)) - elseif addr_width==2 then t_writes:add(eb.rec_wrsadr16, buf(offset+alignment-2, 2)) - elseif addr_width==4 then t_writes:add(eb.rec_wrsadr32, buf(offset+alignment-4, 4)) - elseif addr_width==8 then t_writes:add(eb.rec_wrsadr64, buf(offset+alignment-8, 8)) - end - offset = offset + alignment - - tmp_offset = offset - while (tmp_offset < offset+wr*alignment) do - if data_width==1 then t_writes:add( eb.rec_wrdata8, buf(tmp_offset+alignment-1, 1)) - elseif data_width==2 then t_writes:add( eb.rec_wrdata16, buf(tmp_offset+alignment-2, 2)) - elseif data_width==4 then t_writes:add( eb.rec_wrdata32, buf(tmp_offset+alignment-4, 4)) - elseif data_width==8 then t_writes:add( eb.rec_wrdata64, buf(tmp_offset+alignment-8, 8)) - end - tmp_offset = tmp_offset + alignment - end - offset = tmp_offset - end - - if(rd > 0) then - local t_reads = t_rec:add( eb.rec_reads, buf(offset,(1+rd)*alignment)) - - if addr_width==1 then t_reads:add( eb.rec_rdbadr8, buf(offset+alignment-1, 1)) - elseif addr_width==2 then t_reads:add( eb.rec_rdbadr16, buf(offset+alignment-2, 2)) - elseif addr_width==4 then t_reads:add( eb.rec_rdbadr32, buf(offset+alignment-4, 4)) - elseif addr_width==8 then t_reads:add( eb.rec_rdbadr64, buf(offset+alignment-8, 8)) - end - offset = offset + alignment - - tmp_offset = offset - while (tmp_offset < offset+rd*alignment) do - if addr_width==1 then t_reads:add( eb.rec_rddata8, buf(tmp_offset+alignment-1, 1)) - elseif addr_width==2 then t_reads:add( eb.rec_rddata16, buf(tmp_offset+alignment-2, 2)) - elseif addr_width==4 then t_reads:add( eb.rec_rddata32, buf(tmp_offset+alignment-4, 4)) - elseif addr_width==8 then t_reads:add( eb.rec_rddata64, buf(tmp_offset+alignment-8, 8)) - end - tmp_offset = tmp_offset + alignment - end - offset = tmp_offset - end - end - end - - end - - else - return 0 - end - -end - --- register eb protocol on UDP port 20000 -local tab = DissectorTable.get("udp.port") -tab:add(20000, proto_eb) diff --git a/liteeth/core/etherbone/packet.py b/liteeth/core/etherbone/packet.py deleted file mode 100644 index 22f72c5a..00000000 --- a/liteeth/core/etherbone/packet.py +++ /dev/null @@ -1,129 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from liteeth.generic.depacketizer import LiteEthDepacketizer -from liteeth.generic.packetizer import LiteEthPacketizer - -class LiteEthEtherbonePacketPacketizer(LiteEthPacketizer): - def __init__(self): - LiteEthPacketizer.__init__(self, - eth_etherbone_packet_description(32), - eth_udp_user_description(32), - etherbone_packet_header, - etherbone_packet_header_len) - -class LiteEthEtherbonePacketTX(Module): - def __init__(self, udp_port): - self.sink = sink = Sink(eth_etherbone_packet_user_description(32)) - self.source = source = Source(eth_udp_user_description(32)) - ### - self.submodules.packetizer = packetizer = LiteEthEtherbonePacketPacketizer() - self.comb += [ - packetizer.sink.stb.eq(sink.stb), - packetizer.sink.sop.eq(sink.sop), - packetizer.sink.eop.eq(sink.eop), - sink.ack.eq(packetizer.sink.ack), - - packetizer.sink.magic.eq(etherbone_magic), - packetizer.sink.port_size.eq(32//8), - packetizer.sink.addr_size.eq(32//8), - packetizer.sink.pf.eq(sink.pf), - packetizer.sink.pr.eq(sink.pr), - packetizer.sink.nr.eq(sink.nr), - packetizer.sink.version.eq(etherbone_version), - - packetizer.sink.data.eq(sink.data) - ] - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - packetizer.source.ack.eq(1), - If(packetizer.source.stb & packetizer.source.sop, - packetizer.source.ack.eq(0), - NextState("SEND") - ) - ) - fsm.act("SEND", - Record.connect(packetizer.source, source), - source.src_port.eq(udp_port), - source.dst_port.eq(udp_port), - source.ip_address.eq(sink.ip_address), - source.length.eq(sink.length + etherbone_packet_header_len), - If(source.stb & source.eop & source.ack, - NextState("IDLE") - ) - ) - -class LiteEthEtherbonePacketDepacketizer(LiteEthDepacketizer): - def __init__(self): - LiteEthDepacketizer.__init__(self, - eth_udp_user_description(32), - eth_etherbone_packet_description(32), - etherbone_packet_header, - etherbone_packet_header_len) - -class LiteEthEtherbonePacketRX(Module): - def __init__(self): - self.sink = sink = Sink(eth_udp_user_description(32)) - self.source = source = Source(eth_etherbone_packet_user_description(32)) - ### - self.submodules.depacketizer = depacketizer = LiteEthEtherbonePacketDepacketizer() - self.comb += Record.connect(sink, depacketizer.sink) - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - depacketizer.source.ack.eq(1), - If(depacketizer.source.stb & depacketizer.source.sop, - depacketizer.source.ack.eq(0), - NextState("CHECK") - ) - ) - valid = Signal() - self.sync += valid.eq( - depacketizer.source.stb & - (depacketizer.source.magic == etherbone_magic) - ) - fsm.act("CHECK", - If(valid, - NextState("PRESENT") - ).Else( - NextState("DROP") - ) - ) - self.comb += [ - source.sop.eq(depacketizer.source.sop), - source.eop.eq(depacketizer.source.eop), - - source.pf.eq(depacketizer.source.pf), - source.pr.eq(depacketizer.source.pr), - source.nr.eq(depacketizer.source.nr), - - source.data.eq(depacketizer.source.data), - - source.src_port.eq(sink.src_port), - source.dst_port.eq(sink.dst_port), - source.ip_address.eq(sink.ip_address), - source.length.eq(sink.length - etherbone_packet_header_len) - ] - fsm.act("PRESENT", - source.stb.eq(depacketizer.source.stb), - depacketizer.source.ack.eq(source.ack), - If(source.stb & source.eop & source.ack, - NextState("IDLE") - ) - ) - fsm.act("DROP", - depacketizer.source.ack.eq(1), - If(depacketizer.source.stb & depacketizer.source.eop & depacketizer.source.ack, - NextState("IDLE") - ) - ) - -class LiteEthEtherbonePacket(Module): - def __init__(self, udp, udp_port): - self.submodules.tx = tx = LiteEthEtherbonePacketTX(udp_port) - self.submodules.rx = rx = LiteEthEtherbonePacketRX() - udp_port = udp.crossbar.get_port(udp_port, dw=32) - self.comb += [ - Record.connect(tx.source, udp_port.sink), - Record.connect(udp_port.source, rx.sink) - ] - self.sink, self.source = self.tx.sink, self.rx.source diff --git a/liteeth/core/etherbone/probe.py b/liteeth/core/etherbone/probe.py deleted file mode 100644 index 04eb9f6a..00000000 --- a/liteeth/core/etherbone/probe.py +++ /dev/null @@ -1,24 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -class LiteEthEtherboneProbe(Module): - def __init__(self): - self.sink = sink = Sink(eth_etherbone_packet_user_description(32)) - self.source = source = Source(eth_etherbone_packet_user_description(32)) - ### - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - sink.ack.eq(1), - If(sink.stb & sink.sop, - sink.ack.eq(0), - NextState("PROBE_RESPONSE") - ) - ) - fsm.act("PROBE_RESPONSE", - Record.connect(sink, source), - source.pf.eq(0), - source.pr.eq(1), - If(source.stb & source.eop & source.ack, - NextState("IDLE") - ) - ) diff --git a/liteeth/core/etherbone/record.py b/liteeth/core/etherbone/record.py deleted file mode 100644 index 77bc0378..00000000 --- a/liteeth/core/etherbone/record.py +++ /dev/null @@ -1,176 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from liteeth.generic.depacketizer import LiteEthDepacketizer -from liteeth.generic.packetizer import LiteEthPacketizer - -class LiteEthEtherboneRecordPacketizer(LiteEthPacketizer): - def __init__(self): - LiteEthPacketizer.__init__(self, - eth_etherbone_record_description(32), - eth_etherbone_packet_user_description(32), - etherbone_record_header, - etherbone_record_header_len) - -class LiteEthEtherboneRecordDepacketizer(LiteEthDepacketizer): - def __init__(self): - LiteEthDepacketizer.__init__(self, - eth_etherbone_packet_user_description(32), - eth_etherbone_record_description(32), - etherbone_record_header, - etherbone_record_header_len) - -class LiteEthEtherboneRecordReceiver(Module): - def __init__(self, buffer_depth=256): - self.sink = sink = Sink(eth_etherbone_record_description(32)) - self.source = source = Source(eth_etherbone_mmap_description(32)) - ### - fifo = SyncFIFO(eth_etherbone_record_description(32), buffer_depth, buffered=True) - self.submodules += fifo - self.comb += Record.connect(sink, fifo.sink) - - self.submodules.base_addr = base_addr = FlipFlop(32) - self.comb += base_addr.d.eq(fifo.source.data) - - self.submodules.counter = counter = Counter(max=512) - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - fifo.source.ack.eq(1), - counter.reset.eq(1), - If(fifo.source.stb & fifo.source.sop, - base_addr.ce.eq(1), - If(fifo.source.wcount, - NextState("RECEIVE_WRITES") - ).Elif(fifo.source.rcount, - NextState("RECEIVE_READS") - ) - ) - ) - fsm.act("RECEIVE_WRITES", - source.stb.eq(fifo.source.stb), - source.sop.eq(counter.value == 0), - source.eop.eq(counter.value == fifo.source.wcount-1), - source.count.eq(fifo.source.wcount), - source.be.eq(fifo.source.byte_enable), - source.addr.eq(base_addr.q[2:] + counter.value), - source.we.eq(1), - source.data.eq(fifo.source.data), - fifo.source.ack.eq(source.ack), - If(source.stb & source.ack, - counter.ce.eq(1), - If(source.eop, - If(fifo.source.rcount, - NextState("RECEIVE_BASE_RET_ADDR") - ).Else( - NextState("IDLE") - ) - ) - ) - ) - fsm.act("RECEIVE_BASE_RET_ADDR", - counter.reset.eq(1), - If(fifo.source.stb & fifo.source.sop, - base_addr.ce.eq(1), - NextState("RECEIVE_READS") - ) - ) - fsm.act("RECEIVE_READS", - source.stb.eq(fifo.source.stb), - source.sop.eq(counter.value == 0), - source.eop.eq(counter.value == fifo.source.rcount-1), - source.count.eq(fifo.source.rcount), - source.base_addr.eq(base_addr.q), - source.addr.eq(fifo.source.data[2:]), - fifo.source.ack.eq(source.ack), - If(source.stb & source.ack, - counter.ce.eq(1), - If(source.eop, - NextState("IDLE") - ) - ) - ) - -class LiteEthEtherboneRecordSender(Module): - def __init__(self, buffer_depth=256): - self.sink = sink = Sink(eth_etherbone_mmap_description(32)) - self.source = source = Source(eth_etherbone_record_description(32)) - ### - pbuffer = PacketBuffer(eth_etherbone_mmap_description(32), buffer_depth) - self.submodules += pbuffer - self.comb += Record.connect(sink, pbuffer.sink) - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - pbuffer.source.ack.eq(1), - If(pbuffer.source.stb & pbuffer.source.sop, - pbuffer.source.ack.eq(0), - NextState("SEND_BASE_ADDRESS") - ) - ) - self.comb += [ - source.byte_enable.eq(pbuffer.source.be), - If(pbuffer.source.we, - source.wcount.eq(pbuffer.source.count) - ).Else( - source.rcount.eq(pbuffer.source.count) - ) - ] - - fsm.act("SEND_BASE_ADDRESS", - source.stb.eq(pbuffer.source.stb), - source.sop.eq(1), - source.eop.eq(0), - source.data.eq(pbuffer.source.base_addr), - If(source.ack, - NextState("SEND_DATA") - ) - ) - fsm.act("SEND_DATA", - source.stb.eq(pbuffer.source.stb), - source.sop.eq(0), - source.eop.eq(pbuffer.source.eop), - source.data.eq(pbuffer.source.data), - If(source.stb & source.ack, - pbuffer.source.ack.eq(1), - If(source.eop, - NextState("IDLE") - ) - ) - ) - -# Limitation: For simplicity we only support 1 record per packet -class LiteEthEtherboneRecord(Module): - def __init__(self, endianness="big"): - self.sink = sink = Sink(eth_etherbone_packet_user_description(32)) - self.source = source = Sink(eth_etherbone_packet_user_description(32)) - ### - - # receive record, decode it and generate mmap stream - self.submodules.depacketizer = depacketizer = LiteEthEtherboneRecordDepacketizer() - self.submodules.receiver = receiver = LiteEthEtherboneRecordReceiver() - self.comb += [ - Record.connect(sink, depacketizer.sink), - Record.connect(depacketizer.source, receiver.sink) - ] - if endianness is "big": - self.comb += receiver.sink.data.eq(reverse_bytes(depacketizer.source.data)) - - # save last ip address - last_ip_address = Signal(32) - self.sync += [ - If(sink.stb & sink.sop & sink.ack, - last_ip_address.eq(sink.ip_address) - ) - ] - - # receive mmap stream, encode it and send records - self.submodules.sender = sender = LiteEthEtherboneRecordSender() - self.submodules.packetizer = packetizer = LiteEthEtherboneRecordPacketizer() - self.comb += [ - Record.connect(sender.source, packetizer.sink), - Record.connect(packetizer.source, source), - source.length.eq(sender.source.wcount*4 + 4 +etherbone_record_header_len), # XXX improve this - source.ip_address.eq(last_ip_address) - ] - if endianness is "big": - self.comb += packetizer.sink.data.eq(reverse_bytes(sender.source.data)) diff --git a/liteeth/core/etherbone/wishbone.py b/liteeth/core/etherbone/wishbone.py deleted file mode 100644 index 2f5b1762..00000000 --- a/liteeth/core/etherbone/wishbone.py +++ /dev/null @@ -1,69 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from migen.bus import wishbone - -class LiteEthEtherboneWishboneMaster(Module): - def __init__(self): - self.sink = sink = Sink(eth_etherbone_mmap_description(32)) - self.source = source = Source(eth_etherbone_mmap_description(32)) - self.bus = bus = wishbone.Interface() - ###s - - self.submodules.data = data = FlipFlop(32) - self.comb += data.d.eq(bus.dat_r) - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - sink.ack.eq(1), - If(sink.stb & sink.sop, - sink.ack.eq(0), - If(sink.we, - NextState("WRITE_DATA") - ).Else( - NextState("READ_DATA") - ) - ) - ) - fsm.act("WRITE_DATA", - bus.adr.eq(sink.addr), - bus.dat_w.eq(sink.data), - bus.sel.eq(sink.be), - bus.stb.eq(sink.stb), - bus.we.eq(1), - bus.cyc.eq(1), - If(bus.stb & bus.ack, - sink.ack.eq(1), - If(sink.eop, - NextState("IDLE") - ) - ) - ) - fsm.act("READ_DATA", - bus.adr.eq(sink.addr), - bus.sel.eq(sink.be), - bus.stb.eq(sink.stb), - bus.cyc.eq(1), - If(bus.stb & bus.ack, - data.ce.eq(1), - NextState("SEND_DATA") - ) - ) - fsm.act("SEND_DATA", - source.stb.eq(sink.stb), - source.sop.eq(sink.sop), - source.eop.eq(sink.eop), - source.base_addr.eq(sink.base_addr), - source.addr.eq(sink.addr), - source.count.eq(sink.count), - source.be.eq(sink.be), - source.we.eq(1), - source.data.eq(data.q), - If(source.stb & source.ack, - sink.ack.eq(1), - If(source.eop, - NextState("IDLE") - ).Else( - NextState("READ_DATA") - ) - ) - ) diff --git a/liteeth/core/icmp/__init__.py b/liteeth/core/icmp/__init__.py deleted file mode 100644 index 0af3b624..00000000 --- a/liteeth/core/icmp/__init__.py +++ /dev/null @@ -1,138 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from liteeth.generic.depacketizer import LiteEthDepacketizer -from liteeth.generic.packetizer import LiteEthPacketizer - -class LiteEthICMPPacketizer(LiteEthPacketizer): - def __init__(self): - LiteEthPacketizer.__init__(self, - eth_icmp_description(8), - eth_ipv4_user_description(8), - icmp_header, - icmp_header_len) - -class LiteEthICMPTX(Module): - def __init__(self, ip_address): - self.sink = sink = Sink(eth_icmp_user_description(8)) - self.source = source = Source(eth_ipv4_user_description(8)) - ### - self.submodules.packetizer = packetizer = LiteEthICMPPacketizer() - self.comb += [ - packetizer.sink.stb.eq(sink.stb), - packetizer.sink.sop.eq(sink.sop), - packetizer.sink.eop.eq(sink.eop), - sink.ack.eq(packetizer.sink.ack), - packetizer.sink.msgtype.eq(sink.msgtype), - packetizer.sink.code.eq(sink.code), - packetizer.sink.checksum.eq(sink.checksum), - packetizer.sink.quench.eq(sink.quench), - packetizer.sink.data.eq(sink.data) - ] - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - packetizer.source.ack.eq(1), - If(packetizer.source.stb & packetizer.source.sop, - packetizer.source.ack.eq(0), - NextState("SEND") - ) - ) - fsm.act("SEND", - Record.connect(packetizer.source, source), - source.length.eq(sink.length + icmp_header_len), - source.protocol.eq(icmp_protocol), - source.ip_address.eq(sink.ip_address), - If(source.stb & source.eop & source.ack, - NextState("IDLE") - ) - ) - -class LiteEthICMPDepacketizer(LiteEthDepacketizer): - def __init__(self): - LiteEthDepacketizer.__init__(self, - eth_ipv4_user_description(8), - eth_icmp_description(8), - icmp_header, - icmp_header_len) - -class LiteEthICMPRX(Module): - def __init__(self, ip_address): - self.sink = sink = Sink(eth_ipv4_user_description(8)) - self.source = source = Source(eth_icmp_user_description(8)) - ### - self.submodules.depacketizer = depacketizer = LiteEthICMPDepacketizer() - self.comb += Record.connect(sink, depacketizer.sink) - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - depacketizer.source.ack.eq(1), - If(depacketizer.source.stb & depacketizer.source.sop, - depacketizer.source.ack.eq(0), - NextState("CHECK") - ) - ) - valid = Signal() - self.sync += valid.eq( - depacketizer.source.stb & - (sink.protocol == icmp_protocol) - ) - fsm.act("CHECK", - If(valid, - NextState("PRESENT") - ).Else( - NextState("DROP") - ) - ) - self.comb += [ - source.sop.eq(depacketizer.source.sop), - source.eop.eq(depacketizer.source.eop), - source.msgtype.eq(depacketizer.source.msgtype), - source.code.eq(depacketizer.source.code), - source.checksum.eq(depacketizer.source.checksum), - source.quench.eq(depacketizer.source.quench), - source.ip_address.eq(sink.ip_address), - source.length.eq(sink.length - icmp_header_len), - source.data.eq(depacketizer.source.data), - source.error.eq(depacketizer.source.error) - ] - fsm.act("PRESENT", - source.stb.eq(depacketizer.source.stb), - depacketizer.source.ack.eq(source.ack), - If(source.stb & source.eop & source.ack, - NextState("IDLE") - ) - ) - fsm.act("DROP", - depacketizer.source.ack.eq(1), - If(depacketizer.source.stb & depacketizer.source.eop & depacketizer.source.ack, - NextState("IDLE") - ) - ) - -class LiteEthICMPEcho(Module): - def __init__(self): - self.sink = sink = Sink(eth_icmp_user_description(8)) - self.source = source = Source(eth_icmp_user_description(8)) - ### - self.submodules.buffer = PacketBuffer(eth_icmp_user_description(8), 128, 2) - self.comb += [ - Record.connect(sink, self.buffer.sink), - Record.connect(self.buffer.source, source), - self.source.msgtype.eq(0x0), - self.source.checksum.eq(~((~self.buffer.source.checksum)-0x0800)) - ] - -class LiteEthICMP(Module): - def __init__(self, ip, ip_address): - self.submodules.tx = tx = LiteEthICMPTX(ip_address) - self.submodules.rx = rx = LiteEthICMPRX(ip_address) - self.submodules.echo = echo = LiteEthICMPEcho() - self.comb += [ - Record.connect(rx.source, echo.sink), - Record.connect(echo.source, tx.sink) - ] - ip_port = ip.crossbar.get_port(icmp_protocol) - self.comb += [ - Record.connect(tx.source, ip_port.sink), - Record.connect(ip_port.source, rx.sink) - ] diff --git a/liteeth/core/ip/__init__.py b/liteeth/core/ip/__init__.py deleted file mode 100644 index 4e24f186..00000000 --- a/liteeth/core/ip/__init__.py +++ /dev/null @@ -1,180 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from liteeth.core.ip.checksum import * -from liteeth.core.ip.crossbar import * -from liteeth.generic.depacketizer import LiteEthDepacketizer -from liteeth.generic.packetizer import LiteEthPacketizer - -class LiteEthIPV4Packetizer(LiteEthPacketizer): - def __init__(self): - LiteEthPacketizer.__init__(self, - eth_ipv4_description(8), - eth_mac_description(8), - ipv4_header, - ipv4_header_len) - -class LiteEthIPTX(Module): - def __init__(self, mac_address, ip_address, arp_table): - self.sink = sink = Sink(eth_ipv4_user_description(8)) - self.source = source = Source(eth_mac_description(8)) - self.target_unreachable = Signal() - ### - self.submodules.checksum = checksum = LiteEthIPV4Checksum(skip_checksum=True) - self.comb += [ - checksum.ce.eq(sink.stb & sink.sop), - checksum.reset.eq(source.stb & source.eop & source.ack) - ] - - self.submodules.packetizer = packetizer = LiteEthIPV4Packetizer() - self.comb += [ - packetizer.sink.stb.eq(sink.stb & checksum.done), - packetizer.sink.sop.eq(sink.sop), - packetizer.sink.eop.eq(sink.eop), - sink.ack.eq(packetizer.sink.ack & checksum.done), - packetizer.sink.target_ip.eq(sink.ip_address), - packetizer.sink.protocol.eq(sink.protocol), - packetizer.sink.total_length.eq(sink.length + (0x5*4)), - packetizer.sink.version.eq(0x4), # ipv4 - packetizer.sink.ihl.eq(0x5), # 20 bytes - packetizer.sink.identification.eq(0), - packetizer.sink.ttl.eq(0x80), - packetizer.sink.sender_ip.eq(ip_address), - packetizer.sink.data.eq(sink.data), - checksum.header.eq(packetizer.header), - packetizer.sink.checksum.eq(checksum.value) - ] - - target_mac = Signal(48) - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - packetizer.source.ack.eq(1), - If(packetizer.source.stb & packetizer.source.sop, - packetizer.source.ack.eq(0), - NextState("SEND_MAC_ADDRESS_REQUEST") - ) - ) - self.comb += arp_table.request.ip_address.eq(sink.ip_address) - fsm.act("SEND_MAC_ADDRESS_REQUEST", - arp_table.request.stb.eq(1), - If(arp_table.request.stb & arp_table.request.ack, - NextState("WAIT_MAC_ADDRESS_RESPONSE") - ) - ) - fsm.act("WAIT_MAC_ADDRESS_RESPONSE", - If(arp_table.response.stb, - arp_table.response.ack.eq(1), - If(arp_table.response.failed, - self.target_unreachable.eq(1), - NextState("DROP"), - ).Else( - NextState("SEND") - ) - ) - ) - self.sync += \ - If(arp_table.response.stb, - target_mac.eq(arp_table.response.mac_address) - ) - fsm.act("SEND", - Record.connect(packetizer.source, source), - source.ethernet_type.eq(ethernet_type_ip), - source.target_mac.eq(target_mac), - source.sender_mac.eq(mac_address), - If(source.stb & source.eop & source.ack, - NextState("IDLE") - ) - ) - fsm.act("DROP", - packetizer.source.ack.eq(1), - If(packetizer.source.stb & packetizer.source.eop & packetizer.source.ack, - NextState("IDLE") - ) - ) - -class LiteEthIPV4Depacketizer(LiteEthDepacketizer): - def __init__(self): - LiteEthDepacketizer.__init__(self, - eth_mac_description(8), - eth_ipv4_description(8), - ipv4_header, - ipv4_header_len) - -class LiteEthIPRX(Module): - def __init__(self, mac_address, ip_address): - self.sink = sink = Sink(eth_mac_description(8)) - self.source = source = Source(eth_ipv4_user_description(8)) - ### - self.submodules.depacketizer = depacketizer = LiteEthIPV4Depacketizer() - self.comb += Record.connect(sink, depacketizer.sink) - - self.submodules.checksum = checksum = LiteEthIPV4Checksum(skip_checksum=False) - self.comb += [ - checksum.header.eq(depacketizer.header), - checksum.reset.eq(depacketizer.source.stb & depacketizer.source.eop), - checksum.ce.eq(depacketizer.source.stb & depacketizer.source.sop) - ] - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - depacketizer.source.ack.eq(1), - If(depacketizer.source.stb & depacketizer.source.sop, - depacketizer.source.ack.eq(0), - NextState("CHECK") - ) - ) - valid = Signal() - self.sync += valid.eq( - depacketizer.source.stb & - (depacketizer.source.target_ip == ip_address) & - (depacketizer.source.version == 0x4) & - (depacketizer.source.ihl == 0x5) & - (checksum.value == 0) - ) - - fsm.act("CHECK", - If(checksum.done, - If(valid, - NextState("PRESENT") - ).Else( - NextState("DROP") - ) - ) - ) - self.comb += [ - source.sop.eq(depacketizer.source.sop), - source.eop.eq(depacketizer.source.eop), - source.length.eq(depacketizer.source.total_length - (0x5*4)), - source.protocol.eq(depacketizer.source.protocol), - source.ip_address.eq(depacketizer.source.sender_ip), - source.data.eq(depacketizer.source.data), - source.error.eq(depacketizer.source.error) - ] - fsm.act("PRESENT", - source.stb.eq(depacketizer.source.stb), - depacketizer.source.ack.eq(source.ack), - If(source.stb & source.eop & source.ack, - NextState("IDLE") - ) - ) - fsm.act("DROP", - depacketizer.source.ack.eq(1), - If(depacketizer.source.stb & depacketizer.source.eop & depacketizer.source.ack, - NextState("IDLE") - ) - ) - -class LiteEthIP(Module): - def __init__(self, mac, mac_address, ip_address, arp_table): - self.submodules.tx = tx = LiteEthIPTX(mac_address, ip_address, arp_table) - self.submodules.rx = rx = LiteEthIPRX(mac_address, ip_address) - mac_port = mac.crossbar.get_port(ethernet_type_ip) - self.comb += [ - Record.connect(tx.source, mac_port.sink), - Record.connect(mac_port.source, rx.sink) - ] - self.submodules.crossbar = crossbar = LiteEthIPV4Crossbar() - self.comb += [ - Record.connect(crossbar.master.source, tx.sink), - Record.connect(rx.source, crossbar.master.sink) - ] diff --git a/liteeth/core/ip/checksum.py b/liteeth/core/ip/checksum.py deleted file mode 100644 index f0e187e6..00000000 --- a/liteeth/core/ip/checksum.py +++ /dev/null @@ -1,43 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -class LiteEthIPV4Checksum(Module): - def __init__(self, words_per_clock_cycle=1, skip_checksum=False): - self.reset = Signal() # XXX FIXME InsertReset generates incorrect verilog - self.ce = Signal() # XXX FIXME InsertCE generates incorrect verilog - self.header = Signal(ipv4_header_len*8) - self.value = Signal(16) - self.done = Signal() - ### - s = Signal(17) - r = Signal(17) - n_cycles = 0 - for i in range(ipv4_header_len//2): - if skip_checksum and (i == ipv4_header["checksum"].byte//2): - pass - else: - s_next = Signal(17) - r_next = Signal(17) - self.comb += s_next.eq(r + self.header[i*16:(i+1)*16]) - r_next_eq = r_next.eq(Cat(s_next[:16]+s_next[16], Signal())) - if (i%words_per_clock_cycle) != 0: - self.comb += r_next_eq - else: - self.sync += \ - If(self.reset, - r_next.eq(0) - ).Elif(self.ce & ~self.done, - r_next_eq - ) - n_cycles += 1 - s, r = s_next, r_next - self.comb += self.value.eq(~Cat(r[8:16], r[:8])) - - if not skip_checksum: - n_cycles += 1 - self.submodules.counter = counter = Counter(max=n_cycles+1) - self.comb += [ - counter.reset.eq(self.reset), - counter.ce.eq(self.ce & ~self.done), - self.done.eq(counter.value == n_cycles) - ] diff --git a/liteeth/core/ip/crossbar.py b/liteeth/core/ip/crossbar.py deleted file mode 100644 index 19e88b2e..00000000 --- a/liteeth/core/ip/crossbar.py +++ /dev/null @@ -1,30 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from liteeth.generic.crossbar import LiteEthCrossbar - -class LiteEthIPV4MasterPort: - def __init__(self, dw): - self.dw = dw - self.source = Source(eth_ipv4_user_description(dw)) - self.sink = Sink(eth_ipv4_user_description(dw)) - -class LiteEthIPV4SlavePort: - def __init__(self, dw): - self.dw = dw - self.sink = Sink(eth_ipv4_user_description(dw)) - self.source = Source(eth_ipv4_user_description(dw)) - -class LiteEthIPV4UserPort(LiteEthIPV4SlavePort): - def __init__(self, dw): - LiteEthIPV4SlavePort.__init__(self, dw) - -class LiteEthIPV4Crossbar(LiteEthCrossbar): - def __init__(self): - LiteEthCrossbar.__init__(self, LiteEthIPV4MasterPort, "protocol") - - def get_port(self, protocol): - if protocol in self.users.keys(): - raise ValueError("Protocol {0:#x} already assigned".format(protocol)) - port = LiteEthIPV4UserPort(8) - self.users[protocol] = port - return port diff --git a/liteeth/core/tty/__init__.py b/liteeth/core/tty/__init__.py deleted file mode 100644 index a0102c4f..00000000 --- a/liteeth/core/tty/__init__.py +++ /dev/null @@ -1,97 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -class LiteEthTTYTX(Module): - def __init__(self, ip_address, udp_port, fifo_depth=None): - self.sink = sink = Sink(eth_tty_description(8)) - self.source = source = Source(eth_udp_user_description(8)) - ### - if fifo_depth is None: - self.comb += [ - source.stb.eq(sink.stb), - source.sop.eq(1), - source.eop.eq(1), - source.length.eq(1), - source.data.eq(sink.data), - sink.ack.eq(source.ack) - ] - else: - self.submodules.fifo = fifo = SyncFIFO([("data", 8)], fifo_depth) - self.comb += Record.connect(sink, fifo.sink) - - self.submodules.level = level = FlipFlop(max=fifo_depth) - self.comb += level.d.eq(fifo.fifo.level) - - self.submodules.counter = counter = Counter(max=fifo_depth) - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - If(fifo.source.stb, - level.ce.eq(1), - counter.reset.eq(1), - NextState("SEND") - ) - ) - fsm.act("SEND", - source.stb.eq(fifo.source.stb), - source.sop.eq(counter.value == 0), - If(level.q == 0, - source.eop.eq(1), - ).Else( - source.eop.eq(counter.value == (level.q-1)), - ), - source.src_port.eq(udp_port), - source.dst_port.eq(udp_port), - source.ip_address.eq(ip_address), - If(level.q == 0, - source.length.eq(1), - ).Else( - source.length.eq(level.q), - ), - source.data.eq(fifo.source.data), - fifo.source.ack.eq(source.ack), - If(source.stb & source.ack, - counter.ce.eq(1), - If(source.eop, - NextState("IDLE") - ) - ) - ) - -class LiteEthTTYRX(Module): - def __init__(self, ip_address, udp_port, fifo_depth=None): - self.sink = sink = Sink(eth_udp_user_description(8)) - self.source = source = Source(eth_tty_description(8)) - ### - valid = Signal() - self.comb += valid.eq( - (sink.ip_address == ip_address) & - (sink.dst_port == udp_port) - ) - if fifo_depth is None: - self.comb += [ - source.stb.eq(sink.stb & valid), - source.data.eq(sink.data), - sink.ack.eq(source.ack) - ] - else: - self.submodules.fifo = fifo = SyncFIFO([("data", 8)], fifo_depth) - self.comb += [ - fifo.sink.stb.eq(sink.stb & valid), - fifo.sink.data.eq(sink.data), - sink.ack.eq(fifo.sink.ack), - Record.connect(fifo.source, source) - ] - -class LiteEthTTY(Module): - def __init__(self, udp, ip_address, udp_port, - rx_fifo_depth=64, - tx_fifo_depth=64): - self.submodules.tx = tx = LiteEthTTYTX(ip_address, udp_port, tx_fifo_depth) - self.submodules.rx = rx = LiteEthTTYRX(ip_address, udp_port, rx_fifo_depth) - udp_port = udp.crossbar.get_port(udp_port, dw=8) - self.comb += [ - Record.connect(tx.source, udp_port.sink), - Record.connect(udp_port.source, rx.sink) - ] - self.sink, self.source = self.tx.sink, self.rx.source diff --git a/liteeth/core/udp/__init__.py b/liteeth/core/udp/__init__.py deleted file mode 100644 index 1873d76f..00000000 --- a/liteeth/core/udp/__init__.py +++ /dev/null @@ -1,125 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from liteeth.core.udp.crossbar import * -from liteeth.generic.depacketizer import LiteEthDepacketizer -from liteeth.generic.packetizer import LiteEthPacketizer - -class LiteEthUDPPacketizer(LiteEthPacketizer): - def __init__(self): - LiteEthPacketizer.__init__(self, - eth_udp_description(8), - eth_ipv4_user_description(8), - udp_header, - udp_header_len) - -class LiteEthUDPTX(Module): - def __init__(self, ip_address): - self.sink = sink = Sink(eth_udp_user_description(8)) - self.source = source = Source(eth_ipv4_user_description(8)) - ### - self.submodules.packetizer = packetizer = LiteEthUDPPacketizer() - self.comb += [ - packetizer.sink.stb.eq(sink.stb), - packetizer.sink.sop.eq(sink.sop), - packetizer.sink.eop.eq(sink.eop), - sink.ack.eq(packetizer.sink.ack), - packetizer.sink.src_port.eq(sink.src_port), - packetizer.sink.dst_port.eq(sink.dst_port), - packetizer.sink.length.eq(sink.length + udp_header_len), - packetizer.sink.checksum.eq(0), # Disabled (MAC CRC is enough) - packetizer.sink.data.eq(sink.data) - ] - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - packetizer.source.ack.eq(1), - If(packetizer.source.stb & packetizer.source.sop, - packetizer.source.ack.eq(0), - NextState("SEND") - ) - ) - fsm.act("SEND", - Record.connect(packetizer.source, source), - source.length.eq(packetizer.sink.length), - source.protocol.eq(udp_protocol), - source.ip_address.eq(sink.ip_address), - If(source.stb & source.eop & source.ack, - NextState("IDLE") - ) - ) - -class LiteEthUDPDepacketizer(LiteEthDepacketizer): - def __init__(self): - LiteEthDepacketizer.__init__(self, - eth_ipv4_user_description(8), - eth_udp_description(8), - udp_header, - udp_header_len) - -class LiteEthUDPRX(Module): - def __init__(self, ip_address): - self.sink = sink = Sink(eth_ipv4_user_description(8)) - self.source = source = Source(eth_udp_user_description(8)) - ### - self.submodules.depacketizer = depacketizer = LiteEthUDPDepacketizer() - self.comb += Record.connect(sink, depacketizer.sink) - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - depacketizer.source.ack.eq(1), - If(depacketizer.source.stb & depacketizer.source.sop, - depacketizer.source.ack.eq(0), - NextState("CHECK") - ) - ) - valid = Signal() - self.sync += valid.eq( - depacketizer.source.stb & - (sink.protocol == udp_protocol) - ) - - fsm.act("CHECK", - If(valid, - NextState("PRESENT") - ).Else( - NextState("DROP") - ) - ) - self.comb += [ - source.sop.eq(depacketizer.source.sop), - source.eop.eq(depacketizer.source.eop), - source.src_port.eq(depacketizer.source.src_port), - source.dst_port.eq(depacketizer.source.dst_port), - source.ip_address.eq(sink.ip_address), - source.length.eq(depacketizer.source.length - udp_header_len), - source.data.eq(depacketizer.source.data), - source.error.eq(depacketizer.source.error) - ] - fsm.act("PRESENT", - source.stb.eq(depacketizer.source.stb), - depacketizer.source.ack.eq(source.ack), - If(source.stb & source.eop & source.ack, - NextState("IDLE") - ) - ) - fsm.act("DROP", - depacketizer.source.ack.eq(1), - If(depacketizer.source.stb & depacketizer.source.eop & depacketizer.source.ack, - NextState("IDLE") - ) - ) - -class LiteEthUDP(Module): - def __init__(self, ip, ip_address): - self.submodules.tx = tx = LiteEthUDPTX(ip_address) - self.submodules.rx = rx = LiteEthUDPRX(ip_address) - ip_port = ip.crossbar.get_port(udp_protocol) - self.comb += [ - Record.connect(tx.source, ip_port.sink), - Record.connect(ip_port.source, rx.sink) - ] - self.submodules.crossbar = crossbar = LiteEthUDPCrossbar() - self.comb += [ - Record.connect(crossbar.master.source, tx.sink), - Record.connect(rx.source, crossbar.master.sink) - ] diff --git a/liteeth/core/udp/crossbar.py b/liteeth/core/udp/crossbar.py deleted file mode 100644 index 82843498..00000000 --- a/liteeth/core/udp/crossbar.py +++ /dev/null @@ -1,47 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -from liteeth.generic.crossbar import LiteEthCrossbar - -class LiteEthUDPMasterPort: - def __init__(self, dw): - self.dw = dw - self.source = Source(eth_udp_user_description(dw)) - self.sink = Sink(eth_udp_user_description(dw)) - -class LiteEthUDPSlavePort: - def __init__(self, dw): - self.dw =dw - self.sink = Sink(eth_udp_user_description(dw)) - self.source = Source(eth_udp_user_description(dw)) - -class LiteEthUDPUserPort(LiteEthUDPSlavePort): - def __init__(self, dw): - LiteEthUDPSlavePort.__init__(self, dw) - -class LiteEthUDPCrossbar(LiteEthCrossbar): - def __init__(self): - LiteEthCrossbar.__init__(self, LiteEthUDPMasterPort, "dst_port") - - def get_port(self, udp_port, dw=8): - if udp_port in self.users.keys(): - raise ValueError("Port {0:#x} already assigned".format(udp_port)) - user_port = LiteEthUDPUserPort(dw) - internal_port = LiteEthUDPUserPort(8) - if dw != 8: - converter = Converter(eth_udp_user_description(user_port.dw), eth_udp_user_description(8)) - self.submodules += converter - self.comb += [ - Record.connect(user_port.sink, converter.sink), - Record.connect(converter.source, internal_port.sink) - ] - converter = Converter(eth_udp_user_description(8), eth_udp_user_description(user_port.dw)) - self.submodules += converter - self.comb += [ - Record.connect(internal_port.source, converter.sink), - Record.connect(converter.source, user_port.source) - ] - self.users[udp_port] = internal_port - else: - self.users[udp_port] = user_port - return user_port diff --git a/liteeth/generic/__init__.py b/liteeth/generic/__init__.py deleted file mode 100644 index 643e9872..00000000 --- a/liteeth/generic/__init__.py +++ /dev/null @@ -1,151 +0,0 @@ -from liteeth.common import * - -# Generic classes -class Port: - def connect(self, port): - r = [ - Record.connect(self.source, port.sink), - Record.connect(port.source, self.sink) - ] - return r - -# Generic modules -@DecorateModule(InsertReset) -@DecorateModule(InsertCE) -class FlipFlop(Module): - def __init__(self, *args, **kwargs): - self.d = Signal(*args, **kwargs) - self.q = Signal(*args, **kwargs) - self.sync += self.q.eq(self.d) - -@DecorateModule(InsertReset) -@DecorateModule(InsertCE) -class Counter(Module): - def __init__(self, signal=None, **kwargs): - if signal is None: - self.value = Signal(**kwargs) - else: - self.value = signal - self.width = flen(self.value) - self.sync += self.value.eq(self.value+1) - -@DecorateModule(InsertReset) -@DecorateModule(InsertCE) -class Timeout(Module): - def __init__(self, length): - self.reached = Signal() - ### - value = Signal(max=length) - self.sync += If(~self.reached, value.eq(value+1)) - self.comb += self.reached.eq(value == (length-1)) - -class BufferizeEndpoints(ModuleDecorator): - def __init__(self, submodule, *args): - ModuleDecorator.__init__(self, submodule) - - endpoints = get_endpoints(submodule) - sinks = {} - sources = {} - for name, endpoint in endpoints.items(): - if name in args or len(args) == 0: - if isinstance(endpoint, Sink): - sinks.update({name : endpoint}) - elif isinstance(endpoint, Source): - sources.update({name : endpoint}) - - # add buffer on sinks - for name, sink in sinks.items(): - buf = Buffer(sink.description) - self.submodules += buf - setattr(self, name, buf.d) - self.comb += Record.connect(buf.q, sink) - - # add buffer on sources - for name, source in sources.items(): - buf = Buffer(source.description) - self.submodules += buf - self.comb += Record.connect(source, buf.d) - setattr(self, name, buf.q) - -class EndpointPacketStatus(Module): - def __init__(self, endpoint): - self.start = Signal() - self.done = Signal() - self.ongoing = Signal() - - ongoing = Signal() - self.comb += [ - self.start.eq(endpoint.stb & endpoint.sop & endpoint.ack), - self.done.eq(endpoint.stb & endpoint.eop & endpoint.ack) - ] - self.sync += \ - If(self.start, - ongoing.eq(1) - ).Elif(self.done, - ongoing.eq(0) - ) - self.comb += self.ongoing.eq((self.start | ongoing) & ~self.done) - -class PacketBuffer(Module): - def __init__(self, description, data_depth, cmd_depth=4, almost_full=None): - self.sink = sink = Sink(description) - self.source = source = Source(description) - - ### - sink_status = EndpointPacketStatus(self.sink) - source_status = EndpointPacketStatus(self.source) - self.submodules += sink_status, source_status - - # store incoming packets - # cmds - def cmd_description(): - layout = [("error", 1)] - return EndpointDescription(layout) - cmd_fifo = SyncFIFO(cmd_description(), cmd_depth) - self.submodules += cmd_fifo - self.comb += cmd_fifo.sink.stb.eq(sink_status.done) - if hasattr(sink, "error"): - self.comb += cmd_fifo.sink.error.eq(sink.error) - - # data - data_fifo = SyncFIFO(description, data_depth, buffered=True) - self.submodules += data_fifo - self.comb += [ - Record.connect(self.sink, data_fifo.sink), - data_fifo.sink.stb.eq(self.sink.stb & cmd_fifo.sink.ack), - self.sink.ack.eq(data_fifo.sink.ack & cmd_fifo.sink.ack), - ] - - # output packets - self.fsm = fsm = FSM(reset_state="IDLE") - self.submodules += fsm - fsm.act("IDLE", - If(cmd_fifo.source.stb, - NextState("SEEK_SOP") - ) - ) - fsm.act("SEEK_SOP", - If(~data_fifo.source.sop, - data_fifo.source.ack.eq(1) - ).Else( - NextState("OUTPUT") - ) - ) - if hasattr(source, "error"): - source_error = self.source.error - else: - source_error = Signal() - - fsm.act("OUTPUT", - Record.connect(data_fifo.source, self.source), - source_error.eq(cmd_fifo.source.error), - If(source_status.done, - cmd_fifo.source.ack.eq(1), - NextState("IDLE") - ) - ) - - # compute almost full - if almost_full is not None: - self.almost_full = Signal() - self.comb += self.almost_full.eq(data_fifo.fifo.level > almost_full) diff --git a/liteeth/generic/arbiter.py b/liteeth/generic/arbiter.py deleted file mode 100644 index ca77377e..00000000 --- a/liteeth/generic/arbiter.py +++ /dev/null @@ -1,27 +0,0 @@ -from migen.fhdl.std import * -from migen.genlib.roundrobin import * -from migen.genlib.record import * - -class Arbiter(Module): - def __init__(self, sources, sink): - if len(sources) == 0: - pass - elif len(sources) == 1: - self.grant = Signal() - self.comb += Record.connect(sources.pop(), sink) - else: - self.submodules.rr = RoundRobin(len(sources)) - self.grant = self.rr.grant - cases = {} - for i, source in enumerate(sources): - sop = Signal() - eop = Signal() - ongoing = Signal() - self.comb += [ - sop.eq(source.stb & source.sop), - eop.eq(source.stb & source.eop & source.ack), - ] - self.sync += ongoing.eq((sop | ongoing) & ~eop) - self.comb += self.rr.request[i].eq((sop | ongoing) & ~eop) - cases[i] = [Record.connect(source, sink)] - self.comb += Case(self.grant, cases) diff --git a/liteeth/generic/crossbar.py b/liteeth/generic/crossbar.py deleted file mode 100644 index b60da949..00000000 --- a/liteeth/generic/crossbar.py +++ /dev/null @@ -1,31 +0,0 @@ -from collections import OrderedDict - -from liteeth.common import * -from liteeth.generic import * -from liteeth.generic.arbiter import Arbiter -from liteeth.generic.dispatcher import Dispatcher - -class LiteEthCrossbar(Module): - def __init__(self, master_port, dispatch_param): - self.users = OrderedDict() - self.master = master_port(8) - self.dispatch_param = dispatch_param - - # overload this in derived classes - def get_port(self, *args, **kwargs): - pass - - def do_finalize(self): - # TX arbitrate - sinks = [port.sink for port in self.users.values()] - self.submodules.arbiter = Arbiter(sinks, self.master.source) - - # RX dispatch - sources = [port.source for port in self.users.values()] - self.submodules.dispatcher = Dispatcher(self.master.sink, sources, one_hot=True) - cases = {} - cases["default"] = self.dispatcher.sel.eq(0) - for i, (k, v) in enumerate(self.users.items()): - cases[k] = self.dispatcher.sel.eq(2**i) - self.comb += \ - Case(getattr(self.master.sink, self.dispatch_param), cases) diff --git a/liteeth/generic/depacketizer.py b/liteeth/generic/depacketizer.py deleted file mode 100644 index 2d8ca86c..00000000 --- a/liteeth/generic/depacketizer.py +++ /dev/null @@ -1,84 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -def _decode_header(h_dict, h_signal, obj): - r = [] - for k, v in sorted(h_dict.items()): - start = v.byte*8+v.offset - end = start+v.width - r.append(getattr(obj, k).eq(reverse_bytes(h_signal[start:end]))) - return r - -class LiteEthDepacketizer(Module): - def __init__(self, sink_description, source_description, header_type, header_length): - self.sink = sink = Sink(sink_description) - self.source = source = Source(source_description) - self.header = Signal(header_length*8) - ### - dw = flen(sink.data) - - header_words = (header_length*8)//dw - - shift = Signal() - counter = Counter(max=max(header_words, 2)) - self.submodules += counter - - if header_words == 1: - self.sync += \ - If(shift, - self.header.eq(sink.data) - ) - else: - self.sync += \ - If(shift, - self.header.eq(Cat(self.header[dw:], sink.data)) - ) - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - - if header_words == 1: - idle_next_state = "COPY" - else: - idle_next_state = "RECEIVE_HEADER" - - fsm.act("IDLE", - sink.ack.eq(1), - counter.reset.eq(1), - If(sink.stb, - shift.eq(1), - NextState(idle_next_state) - ) - ) - if header_words != 1: - fsm.act("RECEIVE_HEADER", - sink.ack.eq(1), - If(sink.stb, - counter.ce.eq(1), - shift.eq(1), - If(counter.value == header_words-2, - NextState("COPY") - ) - ) - ) - no_payload = Signal() - self.sync += \ - If(fsm.before_entering("COPY"), - source.sop.eq(1), - no_payload.eq(sink.eop) - ).Elif(source.stb & source.ack, - source.sop.eq(0) - ) - self.comb += [ - source.eop.eq(sink.eop | no_payload), - source.data.eq(sink.data), - source.error.eq(sink.error), - _decode_header(header_type, self.header, source) - ] - fsm.act("COPY", - sink.ack.eq(source.ack), - source.stb.eq(sink.stb | no_payload), - If(source.stb & source.ack & source.eop, - NextState("IDLE") - ) - ) diff --git a/liteeth/generic/dispatcher.py b/liteeth/generic/dispatcher.py deleted file mode 100644 index 3acb9f90..00000000 --- a/liteeth/generic/dispatcher.py +++ /dev/null @@ -1,39 +0,0 @@ -from migen.fhdl.std import * -from migen.genlib.record import * - -class Dispatcher(Module): - def __init__(self, source, sinks, one_hot=False): - if len(sinks) == 0: - self.sel = Signal() - elif len(sinks) == 1: - self.comb += Record.connect(source, sinks.pop()) - self.sel = Signal() - else: - if one_hot: - self.sel = Signal(len(sinks)) - else: - self.sel = Signal(max=len(sinks)) - ### - sop = Signal() - self.comb += sop.eq(source.stb & source.sop) - sel = Signal(flen(self.sel)) - sel_r = Signal(flen(self.sel)) - self.sync += \ - If(sop, - sel_r.eq(self.sel) - ) - self.comb += \ - If(sop, - sel.eq(self.sel) - ).Else( - sel.eq(sel_r) - ) - cases = {} - for i, sink in enumerate(sinks): - if one_hot: - idx = 2**i - else: - idx = i - cases[idx] = [Record.connect(source, sink)] - cases["default"] = [source.ack.eq(1)] - self.comb += Case(sel, cases) diff --git a/liteeth/generic/packetizer.py b/liteeth/generic/packetizer.py deleted file mode 100644 index 418da814..00000000 --- a/liteeth/generic/packetizer.py +++ /dev/null @@ -1,92 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -def _encode_header(h_dict, h_signal, obj): - r = [] - for k, v in sorted(h_dict.items()): - start = v.byte*8+v.offset - end = start+v.width - r.append(h_signal[start:end].eq(reverse_bytes(getattr(obj, k)))) - return r - -class LiteEthPacketizer(Module): - def __init__(self, sink_description, source_description, header_type, header_length): - self.sink = sink = Sink(sink_description) - self.source = source = Source(source_description) - self.header = Signal(header_length*8) - ### - dw = flen(self.sink.data) - - header_reg = Signal(header_length*8) - header_words = (header_length*8)//dw - load = Signal() - shift = Signal() - counter = Counter(max=max(header_words, 2)) - self.submodules += counter - - self.comb += _encode_header(header_type, self.header, sink) - if header_words == 1: - self.sync += [ - If(load, - header_reg.eq(self.header) - ) - ] - else: - self.sync += [ - If(load, - header_reg.eq(self.header) - ).Elif(shift, - header_reg.eq(Cat(header_reg[dw:], Signal(dw))) - ) - ] - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - - if header_words == 1: - idle_next_state = "COPY" - else: - idle_next_state = "SEND_HEADER" - - fsm.act("IDLE", - sink.ack.eq(1), - counter.reset.eq(1), - If(sink.stb & sink.sop, - sink.ack.eq(0), - source.stb.eq(1), - source.sop.eq(1), - source.eop.eq(0), - source.data.eq(self.header[:dw]), - If(source.stb & source.ack, - load.eq(1), - NextState(idle_next_state) - ) - ) - ) - if header_words != 1: - fsm.act("SEND_HEADER", - source.stb.eq(1), - source.sop.eq(0), - source.eop.eq(0), - source.data.eq(header_reg[dw:2*dw]), - If(source.stb & source.ack, - shift.eq(1), - counter.ce.eq(1), - If(counter.value == header_words-2, - NextState("COPY") - ) - ) - ) - fsm.act("COPY", - source.stb.eq(sink.stb), - source.sop.eq(0), - source.eop.eq(sink.eop), - source.data.eq(sink.data), - source.error.eq(sink.error), - If(source.stb & source.ack, - sink.ack.eq(1), - If(source.eop, - NextState("IDLE") - ) - ) - ) diff --git a/liteeth/mac/__init__.py b/liteeth/mac/__init__.py deleted file mode 100644 index 1517b7bd..00000000 --- a/liteeth/mac/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from liteeth.mac.common import * -from liteeth.mac.core import LiteEthMACCore -from liteeth.mac.frontend.wishbone import LiteEthMACWishboneInterface - -class LiteEthMAC(Module, AutoCSR): - def __init__(self, phy, dw, interface="crossbar", endianness="big", - with_hw_preamble_crc=True): - self.submodules.core = LiteEthMACCore(phy, dw, endianness, with_hw_preamble_crc) - self.csrs = [] - if interface == "crossbar": - self.submodules.crossbar = LiteEthMACCrossbar() - self.submodules.packetizer = LiteEthMACPacketizer() - self.submodules.depacketizer = LiteEthMACDepacketizer() - self.comb += [ - Record.connect(self.crossbar.master.source, self.packetizer.sink), - Record.connect(self.packetizer.source, self.core.sink), - Record.connect(self.core.source, self.depacketizer.sink), - Record.connect(self.depacketizer.source, self.crossbar.master.sink) - ] - elif interface == "wishbone": - self.submodules.interface = LiteEthMACWishboneInterface(dw, 2, 2) - self.comb += Port.connect(self.interface, self.core) - self.ev, self.bus = self.interface.sram.ev, self.interface.bus - self.csrs = self.interface.get_csrs() + self.core.get_csrs() - elif interface == "dma": - raise NotImplementedError - else: - raise ValueError(interface + " not supported by LiteEthMac!") - - def get_csrs(self): - return self.csrs diff --git a/liteeth/mac/common.py b/liteeth/mac/common.py deleted file mode 100644 index 3a68c632..00000000 --- a/liteeth/mac/common.py +++ /dev/null @@ -1,46 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from liteeth.generic.depacketizer import LiteEthDepacketizer -from liteeth.generic.packetizer import LiteEthPacketizer -from liteeth.generic.crossbar import LiteEthCrossbar - -class LiteEthMACDepacketizer(LiteEthDepacketizer): - def __init__(self): - LiteEthDepacketizer.__init__(self, - eth_phy_description(8), - eth_mac_description(8), - mac_header, - mac_header_len) - -class LiteEthMACPacketizer(LiteEthPacketizer): - def __init__(self): - LiteEthPacketizer.__init__(self, - eth_mac_description(8), - eth_phy_description(8), - mac_header, - mac_header_len) - -class LiteEthMACMasterPort: - def __init__(self, dw): - self.source = Source(eth_mac_description(dw)) - self.sink = Sink(eth_mac_description(dw)) - -class LiteEthMACSlavePort: - def __init__(self, dw): - self.sink = Sink(eth_mac_description(dw)) - self.source = Source(eth_mac_description(dw)) - -class LiteEthMACUserPort(LiteEthMACSlavePort): - def __init__(self, dw): - LiteEthMACSlavePort.__init__(self, dw) - -class LiteEthMACCrossbar(LiteEthCrossbar): - def __init__(self): - LiteEthCrossbar.__init__(self, LiteEthMACMasterPort, "ethernet_type") - - def get_port(self, ethernet_type): - port = LiteEthMACUserPort(8) - if ethernet_type in self.users.keys(): - raise ValueError("Ethernet type {0:#x} already assigned".format(ethernet_type)) - self.users[ethernet_type] = port - return port diff --git a/liteeth/mac/core/__init__.py b/liteeth/mac/core/__init__.py deleted file mode 100644 index 34a21ad1..00000000 --- a/liteeth/mac/core/__init__.py +++ /dev/null @@ -1,83 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from liteeth.mac.core import gap, preamble, crc, padding, last_be - -class LiteEthMACCore(Module, AutoCSR): - def __init__(self, phy, dw, endianness="big", with_hw_preamble_crc=True): - if dw < phy.dw: - raise ValueError("Core data width({}) must be larger than PHY data width({})".format(dw, phy.dw)) - - rx_pipeline = [phy] - tx_pipeline = [phy] - - # Interpacket gap - tx_gap_inserter = gap.LiteEthMACGap(phy.dw) - rx_gap_checker = gap.LiteEthMACGap(phy.dw, ack_on_gap=True) - self.submodules += RenameClockDomains(tx_gap_inserter, "eth_tx") - self.submodules += RenameClockDomains(rx_gap_checker, "eth_rx") - - tx_pipeline += [tx_gap_inserter] - rx_pipeline += [rx_gap_checker] - - # Preamble / CRC - if with_hw_preamble_crc: - self._hw_preamble_crc = CSRStatus(reset=1) - # Preamble insert/check - preamble_inserter = preamble.LiteEthMACPreambleInserter(phy.dw) - preamble_checker = preamble.LiteEthMACPreambleChecker(phy.dw) - self.submodules += RenameClockDomains(preamble_inserter, "eth_tx") - self.submodules += RenameClockDomains(preamble_checker, "eth_rx") - - # CRC insert/check - crc32_inserter = crc.LiteEthMACCRC32Inserter(eth_phy_description(phy.dw)) - crc32_checker = crc.LiteEthMACCRC32Checker(eth_phy_description(phy.dw)) - self.submodules += RenameClockDomains(crc32_inserter, "eth_tx") - self.submodules += RenameClockDomains(crc32_checker, "eth_rx") - - tx_pipeline += [preamble_inserter, crc32_inserter] - rx_pipeline += [preamble_checker, crc32_checker] - - # Padding - padding_inserter = padding.LiteEthMACPaddingInserter(phy.dw, 60) - padding_checker = padding.LiteEthMACPaddingChecker(phy.dw, 60) - self.submodules += RenameClockDomains(padding_inserter, "eth_tx") - self.submodules += RenameClockDomains(padding_checker, "eth_rx") - - tx_pipeline += [padding_inserter] - rx_pipeline += [padding_checker] - - # Delimiters - if dw != 8: - tx_last_be = last_be.LiteEthMACTXLastBE(phy.dw) - rx_last_be = last_be.LiteEthMACRXLastBE(phy.dw) - self.submodules += RenameClockDomains(tx_last_be, "eth_tx") - self.submodules += RenameClockDomains(rx_last_be, "eth_rx") - - tx_pipeline += [tx_last_be] - rx_pipeline += [rx_last_be] - - # Converters - if dw != phy.dw: - reverse = endianness == "big" - tx_converter = Converter(eth_phy_description(dw), eth_phy_description(phy.dw), reverse=reverse) - rx_converter = Converter(eth_phy_description(phy.dw), eth_phy_description(dw), reverse=reverse) - self.submodules += RenameClockDomains(tx_converter, "eth_tx") - self.submodules += RenameClockDomains(rx_converter, "eth_rx") - - tx_pipeline += [tx_converter] - rx_pipeline += [rx_converter] - - # Cross Domain Crossing - tx_cdc = AsyncFIFO(eth_phy_description(dw), 64) - rx_cdc = AsyncFIFO(eth_phy_description(dw), 64) - self.submodules += RenameClockDomains(tx_cdc, {"write": "sys", "read": "eth_tx"}) - self.submodules += RenameClockDomains(rx_cdc, {"write": "eth_rx", "read": "sys"}) - - tx_pipeline += [tx_cdc] - rx_pipeline += [rx_cdc] - - # Graph - self.submodules.tx_pipeline = Pipeline(*reversed(tx_pipeline)) - self.submodules.rx_pipeline = Pipeline(*rx_pipeline) - - self.sink, self.source = self.tx_pipeline.sink, self.rx_pipeline.source diff --git a/liteeth/mac/core/crc.py b/liteeth/mac/core/crc.py deleted file mode 100644 index 80321e4c..00000000 --- a/liteeth/mac/core/crc.py +++ /dev/null @@ -1,277 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -class LiteEthMACCRCEngine(Module): - """Cyclic Redundancy Check Engine - - Compute next CRC value from last CRC value and data input using - an optimized asynchronous LFSR. - - Parameters - ---------- - data_width : int - Width of the data bus. - width : int - Width of the CRC. - polynom : int - Polynom of the CRC (ex: 0x04C11DB7 for IEEE 802.3 CRC) - - Attributes - ---------- - data : in - Data input. - last : in - last CRC value. - next : - next CRC value. - """ - def __init__(self, data_width, width, polynom): - self.data = Signal(data_width) - self.last = Signal(width) - self.next = Signal(width) - - ### - - def _optimize_eq(l): - """ - Replace even numbers of XORs in the equation - with an equivalent XOR - """ - d = OrderedDict() - for e in l: - if e in d: - d[e] += 1 - else: - d[e] = 1 - r = [] - for key, value in d.items(): - if value%2 != 0: - r.append(key) - return r - - # compute and optimize CRC's LFSR - curval = [[("state", i)] for i in range(width)] - for i in range(data_width): - feedback = curval.pop() + [("din", i)] - for j in range(width-1): - if (polynom & (1<<(j+1))): - curval[j] += feedback - curval[j] = _optimize_eq(curval[j]) - curval.insert(0, feedback) - - # implement logic - for i in range(width): - xors = [] - for t, n in curval[i]: - if t == "state": - xors += [self.last[n]] - elif t == "din": - xors += [self.data[n]] - self.comb += self.next[i].eq(optree("^", xors)) - -@DecorateModule(InsertReset) -@DecorateModule(InsertCE) -class LiteEthMACCRC32(Module): - """IEEE 802.3 CRC - - Implement an IEEE 802.3 CRC generator/checker. - - Parameters - ---------- - data_width : int - Width of the data bus. - - Attributes - ---------- - d : in - Data input. - value : out - CRC value (used for generator). - error : out - CRC error (used for checker). - """ - width = 32 - polynom = 0x04C11DB7 - init = 2**width-1 - check = 0xC704DD7B - def __init__(self, data_width): - self.data = Signal(data_width) - self.value = Signal(self.width) - self.error = Signal() - - ### - - self.submodules.engine = LiteEthMACCRCEngine(data_width, self.width, self.polynom) - reg = Signal(self.width, reset=self.init) - self.sync += reg.eq(self.engine.next) - self.comb += [ - self.engine.data.eq(self.data), - self.engine.last.eq(reg), - - self.value.eq(~reg[::-1]), - self.error.eq(self.engine.next != self.check) - ] - -class LiteEthMACCRCInserter(Module): - """CRC Inserter - - Append a CRC at the end of each packet. - - Parameters - ---------- - description : description - description of the dataflow. - - Attributes - ---------- - sink : in - Packets input without CRC. - source : out - Packets output with CRC. - """ - def __init__(self, crc_class, description): - self.sink = sink = Sink(description) - self.source = source = Source(description) - self.busy = Signal() - - ### - - dw = flen(sink.data) - crc = crc_class(dw) - fsm = FSM(reset_state="IDLE") - self.submodules += crc, fsm - - fsm.act("IDLE", - crc.reset.eq(1), - sink.ack.eq(1), - If(sink.stb & sink.sop, - sink.ack.eq(0), - NextState("COPY"), - ) - ) - fsm.act("COPY", - crc.ce.eq(sink.stb & source.ack), - crc.data.eq(sink.data), - Record.connect(sink, source), - source.eop.eq(0), - If(sink.stb & sink.eop & source.ack, - NextState("INSERT"), - ) - ) - ratio = crc.width//dw - if ratio > 1: - cnt = Signal(max=ratio, reset=ratio-1) - cnt_done = Signal() - fsm.act("INSERT", - source.stb.eq(1), - chooser(crc.value, cnt, source.data, reverse=True), - If(cnt_done, - source.eop.eq(1), - If(source.ack, NextState("IDLE")) - ) - ) - self.comb += cnt_done.eq(cnt == 0) - self.sync += \ - If(fsm.ongoing("IDLE"), - cnt.eq(cnt.reset) - ).Elif(fsm.ongoing("INSERT") & ~cnt_done, - cnt.eq(cnt - source.ack) - ) - else: - fsm.act("INSERT", - source.stb.eq(1), - source.eop.eq(1), - source.data.eq(crc.value), - If(source.ack, NextState("IDLE")) - ) - self.comb += self.busy.eq(~fsm.ongoing("IDLE")) - -class LiteEthMACCRC32Inserter(LiteEthMACCRCInserter): - def __init__(self, description): - LiteEthMACCRCInserter.__init__(self, LiteEthMACCRC32, description) - -class LiteEthMACCRCChecker(Module): - """CRC Checker - - Check CRC at the end of each packet. - - Parameters - ---------- - description : description - description of the dataflow. - - Attributes - ---------- - sink : in - Packets input with CRC. - source : out - Packets output without CRC and "error" set to 0 - on eop when CRC OK / set to 1 when CRC KO. - """ - def __init__(self, crc_class, description): - self.sink = sink = Sink(description) - self.source = source = Source(description) - self.busy = Signal() - - ### - - dw = flen(sink.data) - crc = crc_class(dw) - self.submodules += crc - ratio = crc.width//dw - - error = Signal() - fifo = InsertReset(SyncFIFO(description, ratio + 1)) - self.submodules += fifo - - fsm = FSM(reset_state="RESET") - self.submodules += fsm - - fifo_in = Signal() - fifo_out = Signal() - fifo_full = Signal() - - self.comb += [ - fifo_full.eq(fifo.fifo.level == ratio), - fifo_in.eq(sink.stb & (~fifo_full | fifo_out)), - fifo_out.eq(source.stb & source.ack), - - Record.connect(sink, fifo.sink), - fifo.sink.stb.eq(fifo_in), - self.sink.ack.eq(fifo_in), - - source.stb.eq(sink.stb & fifo_full), - source.sop.eq(fifo.source.sop), - source.eop.eq(sink.eop), - fifo.source.ack.eq(fifo_out), - source.payload.eq(fifo.source.payload), - - source.error.eq(sink.error | crc.error), - ] - - fsm.act("RESET", - crc.reset.eq(1), - fifo.reset.eq(1), - NextState("IDLE"), - ) - fsm.act("IDLE", - crc.data.eq(sink.data), - If(sink.stb & sink.sop & sink.ack, - crc.ce.eq(1), - NextState("COPY") - ) - ) - fsm.act("COPY", - crc.data.eq(sink.data), - If(sink.stb & sink.ack, - crc.ce.eq(1), - If(sink.eop, - NextState("RESET") - ) - ) - ) - self.comb += self.busy.eq(~fsm.ongoing("IDLE")) - -class LiteEthMACCRC32Checker(LiteEthMACCRCChecker): - def __init__(self, description): - LiteEthMACCRCChecker.__init__(self, LiteEthMACCRC32, description) diff --git a/liteeth/mac/core/gap.py b/liteeth/mac/core/gap.py deleted file mode 100644 index 84f738aa..00000000 --- a/liteeth/mac/core/gap.py +++ /dev/null @@ -1,26 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -class LiteEthMACGap(Module): - def __init__(self, dw, ack_on_gap=False): - self.sink = sink = Sink(eth_phy_description(dw)) - self.source = source = Source(eth_phy_description(dw)) - ### - gap = math.ceil(eth_interpacket_gap/(dw//8)) - self.submodules.counter = counter = Counter(max=gap) - - self.submodules.fsm = fsm = FSM(reset_state="COPY") - fsm.act("COPY", - counter.reset.eq(1), - Record.connect(sink, source), - If(sink.stb & sink.eop & sink.ack, - NextState("GAP") - ) - ) - fsm.act("GAP", - counter.ce.eq(1), - sink.ack.eq(int(ack_on_gap)), - If(counter.value == (gap-1), - NextState("COPY") - ) - ) diff --git a/liteeth/mac/core/last_be.py b/liteeth/mac/core/last_be.py deleted file mode 100644 index 2b67ad45..00000000 --- a/liteeth/mac/core/last_be.py +++ /dev/null @@ -1,38 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -class LiteEthMACTXLastBE(Module): - def __init__(self, dw): - self.sink = sink = Sink(eth_phy_description(dw)) - self.source = source = Source(eth_phy_description(dw)) - ### - ongoing = Signal() - self.sync += \ - If(sink.stb & sink.ack, - If(sink.sop, - ongoing.eq(1) - ).Elif(sink.last_be, - ongoing.eq(0) - ) - ) - self.comb += [ - source.stb.eq(sink.stb & (sink.sop | ongoing)), - source.sop.eq(sink.sop), - source.eop.eq(sink.last_be), - source.data.eq(sink.data), - sink.ack.eq(source.ack) - ] - -class LiteEthMACRXLastBE(Module): - def __init__(self, dw): - self.sink = sink = Sink(eth_phy_description(dw)) - self.source = source = Source(eth_phy_description(dw)) - ### - self.comb += [ - source.stb.eq(sink.stb), - source.sop.eq(sink.sop), - source.eop.eq(sink.eop), - source.data.eq(sink.data), - source.last_be.eq(sink.eop), - sink.ack.eq(source.ack) - ] diff --git a/liteeth/mac/core/padding.py b/liteeth/mac/core/padding.py deleted file mode 100644 index 12970fe4..00000000 --- a/liteeth/mac/core/padding.py +++ /dev/null @@ -1,47 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -class LiteEthMACPaddingInserter(Module): - def __init__(self, dw, packet_min_length): - self.sink = sink = Sink(eth_phy_description(dw)) - self.source = source = Source(eth_phy_description(dw)) - ### - packet_min_data = math.ceil(packet_min_length/(dw/8)) - - self.submodules.counter = counter = Counter(max=eth_mtu) - - self.submodules.fsm = fsm = FSM(reset_state="COPY") - fsm.act("COPY", - counter.reset.eq(sink.stb & sink.sop), - Record.connect(sink, source), - If(sink.stb & sink.ack, - counter.ce.eq(1), - If(sink.eop, - If(counter.value < (packet_min_data-1), - source.eop.eq(0), - NextState("PADDING") - ) - ) - ) - ) - fsm.act("PADDING", - source.stb.eq(1), - source.eop.eq(counter.value == (packet_min_data-1)), - source.data.eq(0), - If(source.ack, - counter.ce.eq(1), - If(source.eop, - NextState("COPY") - ) - ) - ) - -class LiteEthMACPaddingChecker(Module): - def __init__(self, dw, packet_min_length): - self.sink = sink = Sink(eth_phy_description(dw)) - self.source = source = Source(eth_phy_description(dw)) - ### - # XXX see if we should drop the packet when - # payload size < minimum ethernet payload size - self.comb += Record.connect(sink, source) - diff --git a/liteeth/mac/core/preamble.py b/liteeth/mac/core/preamble.py deleted file mode 100644 index fd3cb975..00000000 --- a/liteeth/mac/core/preamble.py +++ /dev/null @@ -1,140 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -class LiteEthMACPreambleInserter(Module): - def __init__(self, dw): - self.sink = Sink(eth_phy_description(dw)) - self.source = Source(eth_phy_description(dw)) - - ### - - preamble = Signal(64, reset=eth_preamble) - cnt_max = (64//dw)-1 - cnt = Signal(max=cnt_max+1) - clr_cnt = Signal() - inc_cnt = Signal() - - self.sync += \ - If(clr_cnt, - cnt.eq(0) - ).Elif(inc_cnt, - cnt.eq(cnt+1) - ) - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - fsm.act("IDLE", - self.sink.ack.eq(1), - clr_cnt.eq(1), - If(self.sink.stb & self.sink.sop, - self.sink.ack.eq(0), - NextState("INSERT"), - ) - ) - fsm.act("INSERT", - self.source.stb.eq(1), - self.source.sop.eq(cnt==0), - chooser(preamble, cnt, self.source.data), - If(cnt == cnt_max, - If(self.source.ack, NextState("COPY")) - ).Else( - inc_cnt.eq(self.source.ack) - ) - ) - fsm.act("COPY", - Record.connect(self.sink, self.source), - self.source.sop.eq(0), - - If(self.sink.stb & self.sink.eop & self.source.ack, - NextState("IDLE"), - ) - ) - -class LiteEthMACPreambleChecker(Module): - def __init__(self, dw): - self.sink = Sink(eth_phy_description(dw)) - self.source = Source(eth_phy_description(dw)) - - ### - - preamble = Signal(64, reset=eth_preamble) - cnt_max = (64//dw) - 1 - cnt = Signal(max=cnt_max+1) - clr_cnt = Signal() - inc_cnt = Signal() - - self.sync += \ - If(clr_cnt, - cnt.eq(0) - ).Elif(inc_cnt, - cnt.eq(cnt+1) - ) - - discard = Signal() - clr_discard = Signal() - set_discard = Signal() - - self.sync += \ - If(clr_discard, - discard.eq(0) - ).Elif(set_discard, - discard.eq(1) - ) - - sop = Signal() - clr_sop = Signal() - set_sop = Signal() - self.sync += \ - If(clr_sop, - sop.eq(0) - ).Elif(set_sop, - sop.eq(1) - ) - - ref = Signal(dw) - match = Signal() - self.comb += [ - chooser(preamble, cnt, ref), - match.eq(self.sink.data == ref) - ] - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - - fsm.act("IDLE", - self.sink.ack.eq(1), - clr_cnt.eq(1), - clr_discard.eq(1), - If(self.sink.stb & self.sink.sop, - clr_cnt.eq(0), - inc_cnt.eq(1), - clr_discard.eq(0), - set_discard.eq(~match), - NextState("CHECK"), - ) - ) - fsm.act("CHECK", - self.sink.ack.eq(1), - If(self.sink.stb, - set_discard.eq(~match), - If(cnt == cnt_max, - If(discard | (~match), - NextState("IDLE") - ).Else( - set_sop.eq(1), - NextState("COPY") - ) - ).Else( - inc_cnt.eq(1) - ) - ) - ) - fsm.act("COPY", - Record.connect(self.sink, self.source), - self.source.sop.eq(sop), - clr_sop.eq(self.source.stb & self.source.ack), - - If(self.source.stb & self.source.eop & self.source.ack, - NextState("IDLE"), - ) - ) diff --git a/liteeth/mac/frontend/__init__.py b/liteeth/mac/frontend/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/liteeth/mac/frontend/sram.py b/liteeth/mac/frontend/sram.py deleted file mode 100644 index cffe71b4..00000000 --- a/liteeth/mac/frontend/sram.py +++ /dev/null @@ -1,254 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -from migen.bank.description import * -from migen.bank.eventmanager import * - -class LiteEthMACSRAMWriter(Module, AutoCSR): - def __init__(self, dw, depth, nslots=2): - self.sink = sink = Sink(eth_phy_description(dw)) - self.crc_error = Signal() - - slotbits = max(log2_int(nslots), 1) - lengthbits = log2_int(depth*4) # length in bytes - - self._slot = CSRStatus(slotbits) - self._length = CSRStatus(lengthbits) - - self.submodules.ev = EventManager() - self.ev.available = EventSourceLevel() - self.ev.finalize() - - ### - - # packet dropped if no slot available - sink.ack.reset = 1 - - # length computation - cnt = Signal(lengthbits) - clr_cnt = Signal() - inc_cnt = Signal() - inc_val = Signal(3) - self.comb += \ - If(sink.last_be[3], - inc_val.eq(1) - ).Elif(sink.last_be[2], - inc_val.eq(2) - ).Elif(sink.last_be[1], - inc_val.eq(3) - ).Else( - inc_val.eq(4) - ) - self.sync += \ - If(clr_cnt, - cnt.eq(0) - ).Elif(inc_cnt, - cnt.eq(cnt+inc_val) - ) - - # slot computation - slot = Signal(slotbits) - inc_slot = Signal() - self.sync += \ - If(inc_slot, - If(slot == nslots-1, - slot.eq(0), - ).Else( - slot.eq(slot+1) - ) - ) - ongoing = Signal() - discard = Signal() - - # status fifo - fifo = SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots) - self.submodules += fifo - - # fsm - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - - fsm.act("IDLE", - inc_cnt.eq(sink.stb), - If(sink.stb & sink.sop, - ongoing.eq(1), - If(fifo.sink.ack, - NextState("WRITE") - ) - ) - ) - fsm.act("WRITE", - inc_cnt.eq(sink.stb), - ongoing.eq(1), - If(sink.stb & sink.eop, - If((sink.error & sink.last_be) != 0, - NextState("DISCARD") - ).Else( - NextState("TERMINATE") - ) - ) - ) - fsm.act("DISCARD", - clr_cnt.eq(1), - NextState("IDLE") - ) - fsm.act("TERMINATE", - clr_cnt.eq(1), - inc_slot.eq(1), - fifo.sink.stb.eq(1), - fifo.sink.slot.eq(slot), - fifo.sink.length.eq(cnt), - NextState("IDLE") - ) - - self.comb += [ - fifo.source.ack.eq(self.ev.available.clear), - self.ev.available.trigger.eq(fifo.source.stb), - self._slot.status.eq(fifo.source.slot), - self._length.status.eq(fifo.source.length), - ] - - # memory - mems = [None]*nslots - ports = [None]*nslots - for n in range(nslots): - mems[n] = Memory(dw, depth) - ports[n] = mems[n].get_port(write_capable=True) - self.specials += ports[n] - self.mems = mems - - cases = {} - for n, port in enumerate(ports): - cases[n] = [ - ports[n].adr.eq(cnt[2:]), - ports[n].dat_w.eq(sink.data), - If(sink.stb & ongoing, - ports[n].we.eq(0xf) - ) - ] - self.comb += Case(slot, cases) - - -class LiteEthMACSRAMReader(Module, AutoCSR): - def __init__(self, dw, depth, nslots=2): - self.source = source = Source(eth_phy_description(dw)) - - slotbits = max(log2_int(nslots), 1) - lengthbits = log2_int(depth*4) # length in bytes - self.lengthbits = lengthbits - - self._start = CSR() - self._ready = CSRStatus() - self._slot = CSRStorage(slotbits) - self._length = CSRStorage(lengthbits) - - self.submodules.ev = EventManager() - self.ev.done = EventSourcePulse() - self.ev.finalize() - - ### - - # command fifo - fifo = SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots) - self.submodules += fifo - self.comb += [ - fifo.sink.stb.eq(self._start.re), - fifo.sink.slot.eq(self._slot.storage), - fifo.sink.length.eq(self._length.storage), - self._ready.status.eq(fifo.sink.ack) - ] - - # length computation - cnt = Signal(lengthbits) - clr_cnt = Signal() - inc_cnt = Signal() - - self.sync += \ - If(clr_cnt, - cnt.eq(0) - ).Elif(inc_cnt, - cnt.eq(cnt+4) - ) - - # fsm - first = Signal() - last = Signal() - last_d = Signal() - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - - fsm.act("IDLE", - clr_cnt.eq(1), - If(fifo.source.stb, - NextState("CHECK") - ) - ) - fsm.act("CHECK", - If(~last_d, - NextState("SEND"), - ).Else( - NextState("END"), - ) - ) - length_lsb = fifo.source.length[0:2] - fsm.act("SEND", - source.stb.eq(1), - source.sop.eq(first), - source.eop.eq(last), - If(last, - If(length_lsb == 3, - source.last_be.eq(0b0010) - ).Elif(length_lsb == 2, - source.last_be.eq(0b0100) - ).Elif(length_lsb == 1, - source.last_be.eq(0b1000) - ).Else( - source.last_be.eq(0b0001) - ) - ), - If(source.ack, - inc_cnt.eq(~last), - NextState("CHECK") - ) - ) - fsm.act("END", - fifo.source.ack.eq(1), - self.ev.done.trigger.eq(1), - NextState("IDLE") - ) - - # first/last computation - self.sync += [ - If(fsm.ongoing("IDLE"), - first.eq(1) - ).Elif(source.stb & source.ack, - first.eq(0) - ) - ] - self.comb += last.eq(cnt + 4 >= fifo.source.length) - self.sync += last_d.eq(last) - - # memory - rd_slot = fifo.source.slot - - mems = [None]*nslots - ports = [None]*nslots - for n in range(nslots): - mems[n] = Memory(dw, depth) - ports[n] = mems[n].get_port() - self.specials += ports[n] - self.mems = mems - - cases = {} - for n, port in enumerate(ports): - self.comb += ports[n].adr.eq(cnt[2:]) - cases[n] = [source.data.eq(port.dat_r)] - self.comb += Case(rd_slot, cases) - -class LiteEthMACSRAM(Module, AutoCSR): - def __init__(self, dw, depth, nrxslots, ntxslots): - self.submodules.writer = LiteEthMACSRAMWriter(dw, depth, nrxslots) - self.submodules.reader = LiteEthMACSRAMReader(dw, depth, ntxslots) - self.submodules.ev = SharedIRQ(self.writer.ev, self.reader.ev) - self.sink, self.source = self.writer.sink, self.reader.source diff --git a/liteeth/mac/frontend/wishbone.py b/liteeth/mac/frontend/wishbone.py deleted file mode 100644 index a68fe382..00000000 --- a/liteeth/mac/frontend/wishbone.py +++ /dev/null @@ -1,39 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * -from liteeth.mac.frontend import sram - -from migen.bus import wishbone -from migen.fhdl.simplify import FullMemoryWE - -class LiteEthMACWishboneInterface(Module, AutoCSR): - def __init__(self, dw, nrxslots=2, ntxslots=2): - self.sink = Sink(eth_phy_description(dw)) - self.source = Source(eth_phy_description(dw)) - self.bus = wishbone.Interface() - ### - # storage in SRAM - sram_depth = buffer_depth//(dw//8) - self.submodules.sram = sram.LiteEthMACSRAM(dw, sram_depth, nrxslots, ntxslots) - self.comb += [ - Record.connect(self.sink, self.sram.sink), - Record.connect(self.sram.source, self.source) - ] - - # Wishbone interface - wb_rx_sram_ifs = [wishbone.SRAM(self.sram.writer.mems[n], read_only=True) - for n in range(nrxslots)] - # TODO: FullMemoryWE should move to Mibuild - wb_tx_sram_ifs = [FullMemoryWE(wishbone.SRAM(self.sram.reader.mems[n], read_only=False)) - for n in range(ntxslots)] - wb_sram_ifs = wb_rx_sram_ifs + wb_tx_sram_ifs - - wb_slaves = [] - decoderoffset = log2_int(sram_depth) - decoderbits = log2_int(len(wb_sram_ifs)) - for n, wb_sram_if in enumerate(wb_sram_ifs): - def slave_filter(a, v=n): - return a[decoderoffset:decoderoffset+decoderbits] == v - wb_slaves.append((slave_filter, wb_sram_if.bus)) - self.submodules += wb_sram_if - wb_con = wishbone.Decoder(self.bus, wb_slaves, register=True) - self.submodules += wb_con diff --git a/liteeth/phy/__init__.py b/liteeth/phy/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/liteeth/phy/gmii.py b/liteeth/phy/gmii.py deleted file mode 100644 index 180f7da5..00000000 --- a/liteeth/phy/gmii.py +++ /dev/null @@ -1,76 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -class LiteEthPHYGMIITX(Module): - def __init__(self, pads): - self.sink = sink = Sink(eth_phy_description(8)) - ### - self.sync += [ - pads.tx_er.eq(0), - pads.tx_en.eq(sink.stb), - pads.tx_data.eq(sink.data) - ] - self.comb += sink.ack.eq(1) - -class LiteEthPHYGMIIRX(Module): - def __init__(self, pads): - self.source = source = Source(eth_phy_description(8)) - ### - dv_d = Signal() - self.sync += dv_d.eq(pads.dv) - - sop = Signal() - eop = Signal() - self.comb += [ - sop.eq(pads.dv & ~dv_d), - eop.eq(~pads.dv & dv_d) - ] - self.sync += [ - source.stb.eq(pads.dv), - source.sop.eq(sop), - source.data.eq(pads.rx_data) - ] - self.comb += source.eop.eq(eop) - -# CRG is the only Xilinx specific module. -# TODO: use generic code or add support for others vendors -class LiteEthPHYGMIICRG(Module, AutoCSR): - def __init__(self, clock_pads, pads, with_hw_init_reset): - self._reset = CSRStorage() - ### - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.specials += [ - Instance("ODDR", - p_DDR_CLK_EDGE="SAME_EDGE", - i_C=ClockSignal("eth_tx"), i_CE=1, i_S=0, i_R=0, - i_D1=1, i_D2=0, o_Q=clock_pads.gtx, - ), - Instance("BUFG", i_I=clock_pads.rx, o_O=self.cd_eth_rx.clk), - ] - self.comb += self.cd_eth_tx.clk.eq(self.cd_eth_rx.clk) - - if with_hw_init_reset: - reset = Signal() - counter_done = Signal() - self.submodules.counter = counter = Counter(max=512) - self.comb += [ - counter_done.eq(counter.value == 256), - counter.ce.eq(~counter_done), - reset.eq(~counter_done | self._reset.storage) - ] - else: - reset = self._reset.storage - self.comb += pads.rst_n.eq(~reset) - self.specials += [ - AsyncResetSynchronizer(self.cd_eth_tx, reset), - AsyncResetSynchronizer(self.cd_eth_rx, reset), - ] - -class LiteEthPHYGMII(Module, AutoCSR): - def __init__(self, clock_pads, pads, with_hw_init_reset=True): - self.dw = 8 - self.submodules.crg = LiteEthPHYGMIICRG(clock_pads, pads, with_hw_init_reset) - self.submodules.tx = RenameClockDomains(LiteEthPHYGMIITX(pads), "eth_tx") - self.submodules.rx = RenameClockDomains(LiteEthPHYGMIIRX(pads), "eth_rx") - self.sink, self.source = self.tx.sink, self.rx.source diff --git a/liteeth/phy/loopback.py b/liteeth/phy/loopback.py deleted file mode 100644 index 89ffcb3a..00000000 --- a/liteeth/phy/loopback.py +++ /dev/null @@ -1,27 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -class LiteEthPHYLoopbackCRG(Module, AutoCSR): - def __init__(self): - self._reset = CSRStorage() - ### - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.comb += [ - self.cd_eth_rx.clk.eq(ClockSignal()), - self.cd_eth_tx.clk.eq(ClockSignal()) - ] - - reset = self._reset.storage - self.comb += [ - self.cd_eth_rx.rst.eq(reset), - self.cd_eth_tx.rst.eq(reset) - ] - -class LiteEthPHYLoopback(Module, AutoCSR): - def __init__(self): - self.dw = 8 - self.submodules.crg = LiteEthLoopbackPHYCRG() - self.sink = sink = Sink(eth_phy_description(8)) - self.source = source = Source(eth_phy_description(8)) - self.comb += Record.connect(self.sink, self.source) diff --git a/liteeth/phy/mii.py b/liteeth/phy/mii.py deleted file mode 100644 index 0d3f61be..00000000 --- a/liteeth/phy/mii.py +++ /dev/null @@ -1,127 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -class LiteEthPHYMIITX(Module): - def __init__(self, pads): - self.sink = sink = Sink(eth_phy_description(8)) - ### - tx_en_r = Signal() - tx_data_r = Signal(4) - self.sync += [ - pads.tx_er.eq(0), - pads.tx_en.eq(tx_en_r), - pads.tx_data.eq(tx_data_r), - ] - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - fsm.act("IDLE", - sink.ack.eq(1), - If(sink.stb & sink.sop, - sink.ack.eq(0), - NextState("SEND_LO") - ) - ) - fsm.act("SEND_LO", - tx_data_r.eq(sink.data[0:4]), - tx_en_r.eq(1), - NextState("SEND_HI") - ) - fsm.act("SEND_HI", - tx_data_r.eq(sink.data[4:8]), - tx_en_r.eq(1), - sink.ack.eq(1), - If(sink.stb & sink.eop, - NextState("IDLE") - ).Else( - NextState("SEND_LO") - ) - ) - -class LiteEthPHYMIIRX(Module): - def __init__(self, pads): - self.source = source = Source(eth_phy_description(8)) - ### - sop = source.sop - set_sop = Signal() - clr_sop = Signal() - self.sync += \ - If(clr_sop, - sop.eq(0) - ).Elif(set_sop, - sop.eq(1) - ) - - lo = Signal(4) - hi = Signal(4) - load_nibble = Signal(2) - self.sync += \ - If(load_nibble[0], - lo.eq(pads.rx_data) - ).Elif(load_nibble[1], - hi.eq(pads.rx_data) - ) - self.comb += [ - source.data.eq(Cat(lo, hi)) - ] - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - fsm.act("IDLE", - set_sop.eq(1), - If(pads.dv, - load_nibble.eq(0b01), - NextState("LOAD_HI") - ) - ) - fsm.act("LOAD_LO", - source.stb.eq(1), - If(pads.dv, - clr_sop.eq(1), - load_nibble.eq(0b01), - NextState("LOAD_HI") - ).Else( - source.eop.eq(1), - NextState("IDLE") - ) - ) - fsm.act("LOAD_HI", - load_nibble.eq(0b10), - NextState("LOAD_LO") - ) - -class LiteEthPHYMIICRG(Module, AutoCSR): - def __init__(self, clock_pads, pads, with_hw_init_reset): - self._reset = CSRStorage() - ### - self.sync.base50 += clock_pads.phy.eq(~clock_pads.phy) - - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.comb += self.cd_eth_rx.clk.eq(clock_pads.rx) - self.comb += self.cd_eth_tx.clk.eq(clock_pads.tx) - - if with_hw_init_reset: - reset = Signal() - counter_done = Signal() - self.submodules.counter = counter = Counter(max=512) - self.comb += [ - counter_done.eq(counter.value == 256), - counter.ce.eq(~counter_done), - reset.eq(~counter_done | self._reset.storage) - ] - else: - reset = self._reset.storage - self.comb += pads.rst_n.eq(~reset) - self.specials += [ - AsyncResetSynchronizer(self.cd_eth_tx, reset), - AsyncResetSynchronizer(self.cd_eth_rx, reset), - ] - -class LiteEthPHYMII(Module, AutoCSR): - def __init__(self, clock_pads, pads, with_hw_init_reset=True): - self.dw = 8 - self.submodules.crg = LiteEthPHYMIICRG(clock_pads, pads, with_hw_init_reset) - self.submodules.tx = RenameClockDomains(LiteEthPHYMIITX(pads), "eth_tx") - self.submodules.rx = RenameClockDomains(LiteEthPHYMIIRX(pads), "eth_rx") - self.sink, self.source = self.tx.sink, self.rx.source diff --git a/liteeth/phy/sim.py b/liteeth/phy/sim.py deleted file mode 100644 index f7069dd9..00000000 --- a/liteeth/phy/sim.py +++ /dev/null @@ -1,43 +0,0 @@ -from liteeth.common import * -from liteeth.generic import * - -class LiteEthPHYSimCRG(Module, AutoCSR): - def __init__(self): - self._reset = CSRStorage() - - ### - - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.comb += [ - self.cd_eth_rx.clk.eq(ClockSignal()), - self.cd_eth_tx.clk.eq(ClockSignal()) - ] - - reset = self._reset.storage - self.comb += [ - self.cd_eth_rx.rst.eq(reset), - self.cd_eth_tx.rst.eq(reset) - ] - -class LiteEthPHYSim(Module, AutoCSR): - def __init__(self, pads): - self.dw = 8 - self.submodules.crg = LiteEthPHYSimCRG() - self.sink = sink = Sink(eth_phy_description(8)) - self.source = source = Source(eth_phy_description(8)) - - self.comb += [ - pads.source_stb.eq(self.sink.stb), - pads.source_data.eq(self.sink.data), - self.sink.ack.eq(1) - ] - - self.sync += [ - self.source.stb.eq(pads.sink_stb), - self.source.sop.eq(pads.sink_stb & ~self.source.stb), - self.source.data.eq(pads.sink_data), - ] - self.comb += [ - self.source.eop.eq(~pads.sink_stb & self.source.stb), - ] diff --git a/liteeth/test/Makefile b/liteeth/test/Makefile deleted file mode 100644 index 1682923d..00000000 --- a/liteeth/test/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -LEDIR = ../../ -PYTHON = python3 - -CMD = PYTHONPATH=$(LEDIR) $(PYTHON) - -model_tb: - $(CMD) ./model/mac.py - $(CMD) ./model/arp.py - $(CMD) ./model/ip.py - $(CMD) ./model/udp.py - $(CMD) ./model/icmp.py - $(CMD) ./model/etherbone.py - -mac_core_tb: - $(CMD) mac_core_tb.py - -mac_wishbone_tb: - $(CMD) mac_wishbone_tb.py - -arp_tb: - $(CMD) arp_tb.py - -ip_tb: - $(CMD) ip_tb.py - -udp_tb: - $(CMD) udp_tb.py - -icmp_tb: - $(CMD) icmp_tb.py - -etherbone_tb: - $(CMD) etherbone_tb.py diff --git a/liteeth/test/__init__.py b/liteeth/test/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/liteeth/test/arp_tb.py b/liteeth/test/arp_tb.py deleted file mode 100644 index 3cd9ad36..00000000 --- a/liteeth/test/arp_tb.py +++ /dev/null @@ -1,57 +0,0 @@ -from migen.fhdl.std import * -from migen.bus import wishbone -from migen.bus.transactions import * -from migen.sim.generic import run_simulation - -from liteeth.common import * -from liteeth.mac import LiteEthMAC -from liteeth.core.arp import LiteEthARP - -from liteeth.test.common import * -from liteeth.test.model import phy, mac, arp - -ip_address = 0x12345678 -mac_address = 0x12345678abcd - -class TB(Module): - def __init__(self): - self.submodules.phy_model = phy.PHY(8, debug=False) - self.submodules.mac_model = mac.MAC(self.phy_model, debug=False, loopback=False) - self.submodules.arp_model = arp.ARP(self.mac_model, mac_address, ip_address, debug=False) - - self.submodules.mac = LiteEthMAC(self.phy_model, dw=8, with_hw_preamble_crc=True) - self.submodules.arp = LiteEthARP(self.mac, mac_address, ip_address, 100000) - - # use sys_clk for each clock_domain - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.comb += [ - self.cd_eth_rx.clk.eq(ClockSignal()), - self.cd_eth_rx.rst.eq(ResetSignal()), - self.cd_eth_tx.clk.eq(ClockSignal()), - self.cd_eth_tx.rst.eq(ResetSignal()), - ] - - def gen_simulation(self, selfp): - selfp.cd_eth_rx.rst = 1 - selfp.cd_eth_tx.rst = 1 - yield - selfp.cd_eth_rx.rst = 0 - selfp.cd_eth_tx.rst = 0 - - for i in range(100): - yield - - while selfp.arp.table.request.ack != 1: - selfp.arp.table.request.stb = 1 - selfp.arp.table.request.ip_address = 0x12345678 - yield - selfp.arp.table.request.stb = 0 - while selfp.arp.table.response.stb != 1: - selfp.arp.table.response.ack = 1 - yield - print("Received MAC : 0x{:12x}".format(selfp.arp.table.response.mac_address)) - - -if __name__ == "__main__": - run_simulation(TB(), ncycles=2048, vcd_name="my.vcd", keep_files=True) diff --git a/liteeth/test/common.py b/liteeth/test/common.py deleted file mode 100644 index d780565b..00000000 --- a/liteeth/test/common.py +++ /dev/null @@ -1,162 +0,0 @@ -import random, copy - -from migen.fhdl.std import * -from migen.flow.actor import Sink, Source -from migen.genlib.record import * - -from liteeth.common import * - -def print_with_prefix(s, prefix=""): - if not isinstance(s, str): - s = s.__repr__() - s = s.split("\n") - for l in s: - print(prefix + l) - -def seed_to_data(seed, random=True): - if random: - return (seed * 0x31415979 + 1) & 0xffffffff - else: - return seed - -def split_bytes(v, n, endianness="big"): - r = [] - r_bytes = v.to_bytes(n, byteorder=endianness) - for byte in r_bytes: - r.append(int(byte)) - return r - -def merge_bytes(b, endianness="big"): - return int.from_bytes(bytes(b), endianness) - -def get_field_data(field, datas): - v = merge_bytes(datas[field.byte:field.byte+math.ceil(field.width/8)]) - return (v >> field.offset) & (2**field.width-1) - -def comp(p1, p2): - r = True - for x, y in zip(p1, p2): - if x != y: - r = False - return r - -def check(p1, p2): - p1 = copy.deepcopy(p1) - p2 = copy.deepcopy(p2) - if isinstance(p1, int): - return 0, 1, int(p1 != p2) - else: - if len(p1) >= len(p2): - ref, res = p1, p2 - else: - ref, res = p2, p1 - shift = 0 - while((ref[0] != res[0]) and (len(res)>1)): - res.pop(0) - shift += 1 - length = min(len(ref), len(res)) - errors = 0 - for i in range(length): - if ref.pop(0) != res.pop(0): - errors += 1 - return shift, length, errors - -def randn(max_n): - return random.randint(0, max_n-1) - -class Packet(list): - def __init__(self, init=[]): - self.ongoing = False - self.done = False - for data in init: - self.append(data) - -class PacketStreamer(Module): - def __init__(self, description, last_be=None): - self.source = Source(description) - self.last_be = last_be - ### - self.packets = [] - self.packet = Packet() - self.packet.done = True - - def send(self, packet): - packet = copy.deepcopy(packet) - self.packets.append(packet) - return packet - - def send_blocking(self, packet): - packet = self.send(packet) - while not packet.done: - yield - - def do_simulation(self, selfp): - if len(self.packets) and self.packet.done: - self.packet = self.packets.pop(0) - if not self.packet.ongoing and not self.packet.done: - selfp.source.stb = 1 - selfp.source.sop = 1 - selfp.source.data = self.packet.pop(0) - self.packet.ongoing = True - elif selfp.source.stb == 1 and selfp.source.ack == 1: - selfp.source.sop = 0 - if len(self.packet) == 1: - selfp.source.eop = 1 - if self.last_be is not None: - selfp.source.last_be = self.last_be - else: - selfp.source.eop = 0 - if self.last_be is not None: - selfp.source.last_be = 0 - if len(self.packet) > 0: - selfp.source.stb = 1 - selfp.source.data = self.packet.pop(0) - else: - self.packet.done = True - selfp.source.stb = 0 - -class PacketLogger(Module): - def __init__(self, description): - self.sink = Sink(description) - ### - self.packet = Packet() - - def receive(self): - self.packet.done = False - while not self.packet.done: - yield - - def do_simulation(self, selfp): - selfp.sink.ack = 1 - if selfp.sink.stb == 1 and selfp.sink.sop == 1: - self.packet = Packet() - self.packet.append(selfp.sink.data) - elif selfp.sink.stb: - self.packet.append(selfp.sink.data) - if selfp.sink.stb == 1 and selfp.sink.eop == 1: - self.packet.done = True - -class AckRandomizer(Module): - def __init__(self, description, level=0): - self.level = level - - self.sink = Sink(description) - self.source = Source(description) - - self.run = Signal() - - self.comb += \ - If(self.run, - Record.connect(self.sink, self.source) - ).Else( - self.source.stb.eq(0), - self.sink.ack.eq(0), - ) - - def do_simulation(self, selfp): - n = randn(100) - if n < self.level: - selfp.run = 0 - else: - selfp.run = 1 - diff --git a/liteeth/test/etherbone_tb.py b/liteeth/test/etherbone_tb.py deleted file mode 100644 index 275dc2e6..00000000 --- a/liteeth/test/etherbone_tb.py +++ /dev/null @@ -1,116 +0,0 @@ -from migen.fhdl.std import * -from migen.bus import wishbone -from migen.bus.transactions import * -from migen.sim.generic import run_simulation - -from liteeth.common import * -from liteeth.core import LiteEthUDPIPCore -from liteeth.core.etherbone import LiteEthEtherbone - -from liteeth.test.common import * -from liteeth.test.model import phy, mac, arp, ip, udp, etherbone - -ip_address = 0x12345678 -mac_address = 0x12345678abcd - -class TB(Module): - def __init__(self): - self.submodules.phy_model = phy.PHY(8, debug=False) - self.submodules.mac_model = mac.MAC(self.phy_model, debug=False, loopback=False) - self.submodules.arp_model = arp.ARP(self.mac_model, mac_address, ip_address, debug=False) - self.submodules.ip_model = ip.IP(self.mac_model, mac_address, ip_address, debug=False, loopback=False) - self.submodules.udp_model = udp.UDP(self.ip_model, ip_address, debug=False, loopback=False) - self.submodules.etherbone_model = etherbone.Etherbone(self.udp_model, debug=False) - - self.submodules.core = LiteEthUDPIPCore(self.phy_model, mac_address, ip_address, 100000) - self.submodules.etherbone = LiteEthEtherbone(self.core.udp, 20000) - - self.submodules.sram = wishbone.SRAM(1024) - self.submodules.interconnect = wishbone.InterconnectPointToPoint(self.etherbone.master.bus, self.sram.bus) - - # use sys_clk for each clock_domain - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.comb += [ - self.cd_eth_rx.clk.eq(ClockSignal()), - self.cd_eth_rx.rst.eq(ResetSignal()), - self.cd_eth_tx.clk.eq(ClockSignal()), - self.cd_eth_tx.rst.eq(ResetSignal()), - ] - - def gen_simulation(self, selfp): - selfp.cd_eth_rx.rst = 1 - selfp.cd_eth_tx.rst = 1 - yield - selfp.cd_eth_rx.rst = 0 - selfp.cd_eth_tx.rst = 0 - - for i in range(100): - yield - - test_probe = True - test_writes = True - test_reads = True - - # test probe - if test_probe: - packet = etherbone.EtherbonePacket() - packet.pf = 1 - self.etherbone_model.send(packet) - yield from self.etherbone_model.receive() - print("probe: " + str(bool(self.etherbone_model.rx_packet.pr))) - - for i in range(8): - # test writes - if test_writes: - writes_datas = [j for j in range(16)] - writes = etherbone.EtherboneWrites(base_addr=0x1000, datas=writes_datas) - record = etherbone.EtherboneRecord() - record.writes = writes - record.reads = None - record.bca = 0 - record.rca = 0 - record.rff = 0 - record.cyc = 0 - record.wca = 0 - record.wff = 0 - record.byte_enable = 0xf - record.wcount = len(writes_datas) - record.rcount = 0 - - packet = etherbone.EtherbonePacket() - packet.records = [record] - self.etherbone_model.send(packet) - for i in range(256): - yield - - # test reads - if test_reads: - reads_addrs = [0x1000 + 4*j for j in range(16)] - reads = etherbone.EtherboneReads(base_ret_addr=0x1000, addrs=reads_addrs) - record = etherbone.EtherboneRecord() - record.writes = None - record.reads = reads - record.bca = 0 - record.rca = 0 - record.rff = 0 - record.cyc = 0 - record.wca = 0 - record.wff = 0 - record.byte_enable = 0xf - record.wcount = 0 - record.rcount = len(reads_addrs) - - packet = etherbone.EtherbonePacket() - packet.records = [record] - self.etherbone_model.send(packet) - yield from self.etherbone_model.receive() - loopback_writes_datas = [] - loopback_writes_datas = self.etherbone_model.rx_packet.records.pop().writes.get_datas() - - # check results - s, l, e = check(writes_datas, loopback_writes_datas) - print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) - -if __name__ == "__main__": - run_simulation(TB(), ncycles=4096, vcd_name="my.vcd", keep_files=True) \ No newline at end of file diff --git a/liteeth/test/icmp_tb.py b/liteeth/test/icmp_tb.py deleted file mode 100644 index f5e2d907..00000000 --- a/liteeth/test/icmp_tb.py +++ /dev/null @@ -1,58 +0,0 @@ -from migen.fhdl.std import * -from migen.bus import wishbone -from migen.bus.transactions import * -from migen.sim.generic import run_simulation - -from liteeth.common import * -from liteeth.core import LiteEthIPCore - -from liteeth.test.common import * -from liteeth.test.model.dumps import * -from liteeth.test.model.mac import * -from liteeth.test.model.ip import * -from liteeth.test.model.icmp import * -from liteeth.test.model import phy, mac, arp, ip, icmp - -ip_address = 0x12345678 -mac_address = 0x12345678abcd - -class TB(Module): - def __init__(self): - self.submodules.phy_model = phy.PHY(8, debug=True) - self.submodules.mac_model = mac.MAC(self.phy_model, debug=True, loopback=False) - self.submodules.arp_model = arp.ARP(self.mac_model, mac_address, ip_address, debug=True) - self.submodules.ip_model = ip.IP(self.mac_model, mac_address, ip_address, debug=True, loopback=False) - self.submodules.icmp_model = icmp.ICMP(self.ip_model, ip_address, debug=True) - - self.submodules.ip = LiteEthIPCore(self.phy_model, mac_address, ip_address, 100000) - - # use sys_clk for each clock_domain - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.comb += [ - self.cd_eth_rx.clk.eq(ClockSignal()), - self.cd_eth_rx.rst.eq(ResetSignal()), - self.cd_eth_tx.clk.eq(ClockSignal()), - self.cd_eth_tx.rst.eq(ResetSignal()), - ] - - def gen_simulation(self, selfp): - selfp.cd_eth_rx.rst = 1 - selfp.cd_eth_tx.rst = 1 - yield - selfp.cd_eth_rx.rst = 0 - selfp.cd_eth_tx.rst = 0 - - for i in range(100): - yield - - packet = MACPacket(ping_request) - packet.decode_remove_header() - packet = IPPacket(packet) - packet.decode() - packet = ICMPPacket(packet) - packet.decode() - self.icmp_model.send(packet) - -if __name__ == "__main__": - run_simulation(TB(), ncycles=2048, vcd_name="my.vcd", keep_files=True) diff --git a/liteeth/test/ip_tb.py b/liteeth/test/ip_tb.py deleted file mode 100644 index 1c21ffa5..00000000 --- a/liteeth/test/ip_tb.py +++ /dev/null @@ -1,59 +0,0 @@ -from migen.fhdl.std import * -from migen.bus import wishbone -from migen.bus.transactions import * -from migen.sim.generic import run_simulation - -from liteeth.common import * -from liteeth.core import LiteEthIPCore - -from liteeth.test.common import * -from liteeth.test.model import phy, mac, arp, ip - -ip_address = 0x12345678 -mac_address = 0x12345678abcd - -class TB(Module): - def __init__(self): - self.submodules.phy_model = phy.PHY(8, debug=False) - self.submodules.mac_model = mac.MAC(self.phy_model, debug=False, loopback=False) - self.submodules.arp_model = arp.ARP(self.mac_model, mac_address, ip_address, debug=False) - self.submodules.ip_model = ip.IP(self.mac_model, mac_address, ip_address, debug=False, loopback=True) - - self.submodules.ip = LiteEthIPCore(self.phy_model, mac_address, ip_address, 100000) - self.ip_port = self.ip.ip.crossbar.get_port(udp_protocol) - - # use sys_clk for each clock_domain - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.comb += [ - self.cd_eth_rx.clk.eq(ClockSignal()), - self.cd_eth_rx.rst.eq(ResetSignal()), - self.cd_eth_tx.clk.eq(ClockSignal()), - self.cd_eth_tx.rst.eq(ResetSignal()), - ] - - def gen_simulation(self, selfp): - selfp.cd_eth_rx.rst = 1 - selfp.cd_eth_tx.rst = 1 - yield - selfp.cd_eth_rx.rst = 0 - selfp.cd_eth_tx.rst = 0 - - for i in range(100): - yield - - while True: - selfp.ip_port.sink.stb = 1 - selfp.ip_port.sink.sop = 1 - selfp.ip_port.sink.eop = 1 - selfp.ip_port.sink.ip_address = 0x12345678 - selfp.ip_port.sink.protocol = udp_protocol - - selfp.ip_port.source.ack = 1 - if selfp.ip_port.source.stb == 1 and selfp.ip_port.source.sop == 1: - print("packet from from IP 0x{:08x}".format(selfp.ip_port.sink.ip_address)) - - yield - -if __name__ == "__main__": - run_simulation(TB(), ncycles=2048, vcd_name="my.vcd", keep_files=True) diff --git a/liteeth/test/mac_core_tb.py b/liteeth/test/mac_core_tb.py deleted file mode 100644 index 341028b7..00000000 --- a/liteeth/test/mac_core_tb.py +++ /dev/null @@ -1,62 +0,0 @@ -from migen.fhdl.std import * -from migen.bus import wishbone -from migen.bus.transactions import * -from migen.sim.generic import run_simulation - -from liteeth.common import * -from liteeth.mac.core import LiteEthMACCore - -from liteeth.test.common import * -from liteeth.test.model import phy, mac - -class TB(Module): - def __init__(self): - self.submodules.phy_model = phy.PHY(8, debug=False) - self.submodules.mac_model = mac.MAC(self.phy_model, debug=False, loopback=True) - self.submodules.core = LiteEthMACCore(phy=self.phy_model, dw=8, with_hw_preamble_crc=True) - - self.submodules.streamer = PacketStreamer(eth_phy_description(8), last_be=1) - self.submodules.streamer_randomizer = AckRandomizer(eth_phy_description(8), level=50) - - self.submodules.logger_randomizer = AckRandomizer(eth_phy_description(8), level=50) - self.submodules.logger = PacketLogger(eth_phy_description(8)) - - # use sys_clk for each clock_domain - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.comb += [ - self.cd_eth_rx.clk.eq(ClockSignal()), - self.cd_eth_rx.rst.eq(ResetSignal()), - self.cd_eth_tx.clk.eq(ClockSignal()), - self.cd_eth_tx.rst.eq(ResetSignal()), - ] - - self.comb += [ - Record.connect(self.streamer.source, self.streamer_randomizer.sink), - Record.connect(self.streamer_randomizer.source, self.core.sink), - Record.connect(self.core.source, self.logger_randomizer.sink), - Record.connect(self.logger_randomizer.source, self.logger.sink) - ] - - def gen_simulation(self, selfp): - selfp.cd_eth_rx.rst = 1 - selfp.cd_eth_tx.rst = 1 - yield - selfp.cd_eth_rx.rst = 0 - selfp.cd_eth_tx.rst = 0 - - for i in range(8): - packet = mac.MACPacket([i for i in range(64)]) - packet.target_mac = 0x010203040506 - packet.sender_mac = 0x090A0B0C0C0D - packet.ethernet_type = 0x0800 - packet.encode_header() - yield from self.streamer.send(packet) - yield from self.logger.receive() - - # check results - s, l, e = check(packet, self.logger.packet) - print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) - -if __name__ == "__main__": - run_simulation(TB(), ncycles=4000, vcd_name="my.vcd", keep_files=True) diff --git a/liteeth/test/mac_wishbone_tb.py b/liteeth/test/mac_wishbone_tb.py deleted file mode 100644 index ca6740eb..00000000 --- a/liteeth/test/mac_wishbone_tb.py +++ /dev/null @@ -1,145 +0,0 @@ -from migen.fhdl.std import * -from migen.bus import wishbone -from migen.bus.transactions import * -from migen.sim.generic import run_simulation - -from liteeth.common import * -from liteeth.mac import LiteEthMAC - -from liteeth.test.common import * -from liteeth.test.model import phy, mac - -class WishboneMaster: - def __init__(self, obj): - self.obj = obj - self.dat = 0 - - def write(self, adr, dat): - self.obj.cyc = 1 - self.obj.stb = 1 - self.obj.adr = adr - self.obj.we = 1 - self.obj.sel = 0xF - self.obj.dat_w = dat - while self.obj.ack == 0: - yield - self.obj.cyc = 0 - self.obj.stb = 0 - yield - - def read(self, adr): - self.obj.cyc = 1 - self.obj.stb = 1 - self.obj.adr = adr - self.obj.we = 0 - self.obj.sel = 0xF - self.obj.dat_w = 0 - while self.obj.ack == 0: - yield - self.dat = self.obj.dat_r - self.obj.cyc = 0 - self.obj.stb = 0 - yield - -class SRAMReaderDriver: - def __init__(self, obj): - self.obj = obj - - def start(self, slot, length): - self.obj._slot.storage = slot - self.obj._length.storage = length - self.obj._start.re = 1 - yield - self.obj._start.re = 0 - yield - - def wait_done(self): - while self.obj.ev.done.pending == 0: - yield - - def clear_done(self): - self.obj.ev.done.clear = 1 - yield - self.obj.ev.done.clear = 0 - yield - -class SRAMWriterDriver: - def __init__(self, obj): - self.obj = obj - - def wait_available(self): - while self.obj.ev.available.pending == 0: - yield - - def clear_available(self): - self.obj.ev.available.clear = 1 - yield - self.obj.ev.available.clear = 0 - yield - -class TB(Module): - def __init__(self): - self.submodules.phy_model = phy.PHY(8, debug=False) - self.submodules.mac_model = mac.MAC(self.phy_model, debug=False, loopback=True) - self.submodules.ethmac = LiteEthMAC(phy=self.phy_model, dw=32, interface="wishbone", with_hw_preamble_crc=True) - - # use sys_clk for each clock_domain - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.comb += [ - self.cd_eth_rx.clk.eq(ClockSignal()), - self.cd_eth_rx.rst.eq(ResetSignal()), - self.cd_eth_tx.clk.eq(ClockSignal()), - self.cd_eth_tx.rst.eq(ResetSignal()), - ] - - def gen_simulation(self, selfp): - selfp.cd_eth_rx.rst = 1 - selfp.cd_eth_tx.rst = 1 - yield - selfp.cd_eth_rx.rst = 0 - selfp.cd_eth_tx.rst = 0 - - wishbone_master = WishboneMaster(selfp.ethmac.bus) - sram_reader_driver = SRAMReaderDriver(selfp.ethmac.interface.sram.reader) - sram_writer_driver = SRAMWriterDriver(selfp.ethmac.interface.sram.writer) - - sram_writer_slots_offset = [0x000, 0x200] - sram_reader_slots_offset = [0x400, 0x600] - - length = 150+2 - - tx_payload = [seed_to_data(i, True) % 0xFF for i in range(length)] + [0, 0, 0, 0] - - errors = 0 - - while True: - for slot in range(2): - print("slot {}: ".format(slot), end="") - # fill tx memory - for i in range(length//4+1): - dat = int.from_bytes(tx_payload[4*i:4*(i+1)], "big") - yield from wishbone_master.write(sram_reader_slots_offset[slot]+i, dat) - - # send tx payload & wait - yield from sram_reader_driver.start(slot, length) - yield from sram_reader_driver.wait_done() - yield from sram_reader_driver.clear_done() - - # wait rx - yield from sram_writer_driver.wait_available() - yield from sram_writer_driver.clear_available() - - # get rx payload (loopback on PHY Model) - rx_payload = [] - for i in range(length//4+1): - yield from wishbone_master.read(sram_writer_slots_offset[slot]+i) - dat = wishbone_master.dat - rx_payload += list(dat.to_bytes(4, byteorder='big')) - - # check results - s, l, e = check(tx_payload[:length], rx_payload[:min(length, len(rx_payload))]) - print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) - -if __name__ == "__main__": - run_simulation(TB(), ncycles=3000, vcd_name="my.vcd", keep_files=True) diff --git a/liteeth/test/model/__init__.py b/liteeth/test/model/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/liteeth/test/model/arp.py b/liteeth/test/model/arp.py deleted file mode 100644 index 677904cc..00000000 --- a/liteeth/test/model/arp.py +++ /dev/null @@ -1,152 +0,0 @@ -import math - -from liteeth.common import * -from liteeth.test.common import * - -from liteeth.test.model import mac - -def print_arp(s): - print_with_prefix(s, "[ARP]") - -preamble = split_bytes(eth_preamble, 8) - -# ARP model -class ARPPacket(Packet): - def __init__(self, init=[]): - Packet.__init__(self, init) - - def decode(self): - header = [] - for byte in self[:arp_header_len]: - header.append(self.pop(0)) - for k, v in sorted(arp_header.items()): - setattr(self, k, get_field_data(v, header)) - - def encode(self): - header = 0 - for k, v in sorted(arp_header.items()): - value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") - header += (value << v.offset+(v.byte*8)) - for d in split_bytes(header, arp_header_len): - self.insert(0, d) - - def __repr__(self): - r = "--------\n" - for k in sorted(arp_header.keys()): - r += k + " : 0x{:0x}\n".format(getattr(self,k)) - r += "payload: " - for d in self: - r += "{:02x}".format(d) - return r - -class ARP(Module): - def __init__(self, mac, mac_address, ip_address, debug=False): - self.mac = mac - self.mac_address = mac_address - self.ip_address = ip_address - self.debug = debug - self.tx_packets = [] - self.tx_packet = ARPPacket() - self.rx_packet = ARPPacket() - self.table = {} - self.request_pending = False - - self.mac.set_arp_callback(self.callback) - - def send(self, packet): - packet.encode() - if self.debug: - print_arp(">>>>>>>>") - print_arp(packet) - mac_packet = mac.MACPacket(packet) - mac_packet.target_mac = packet.target_mac - mac_packet.sender_mac = packet.sender_mac - mac_packet.ethernet_type = ethernet_type_arp - self.mac.send(mac_packet) - - def callback(self, packet): - packet = ARPPacket(packet) - packet.decode() - if self.debug: - print_arp("<<<<<<<<") - print_arp(packet) - self.process(packet) - - def process(self, packet): - if len(packet) != eth_min_len-arp_header_len: - raise ValueError - if packet.hwtype != arp_hwtype_ethernet: - raise ValueError - if packet.proto != arp_proto_ip: - raise ValueError - if packet.hwsize != 6: - raise ValueError - if packet.protosize != 4: - raise ValueError - if packet.opcode == arp_opcode_request: - self.process_request(packet) - elif packet.opcode == arp_opcode_reply: - self.process_reply(packet) - - def process_request(self, request): - if request.target_ip == self.ip_address: - reply = ARPPacket([0]*(eth_min_len-arp_header_len)) - reply.hwtype = arp_hwtype_ethernet - reply.proto = arp_proto_ip - reply.opcode = arp_opcode_reply - reply.hwsize = 6 - reply.protosize = 4 - reply.sender_mac = self.mac_address - reply.sender_ip = self.ip_address - reply.target_mac = request.sender_mac - reply.target_ip = request.sender_ip - self.send(reply) - - def process_reply(self, reply): - self.table[reply.sender_ip] = reply.sender_mac - - def request(self, ip_address): - request = ARPPacket([0]*(eth_min_len-arp_header_len)) - request.hwtype = arp_hwtype_ethernet - request.proto = arp_proto_ip - request.opcode = arp_opcode_request - request.hwsize = 6 - request.protosize = 4 - request.sender_mac = self.mac_address - request.sender_ip = self.ip_address - request.target_mac = 0xffffffffffff - request.target_ip = ip_address - -if __name__ == "__main__": - from liteeth.test.model.dumps import * - from liteeth.test.model.mac import * - errors = 0 - # ARP request - packet = MACPacket(arp_request) - packet.decode_remove_header() - packet = ARPPacket(packet) - # check decoding - packet.decode() - #print(packet) - errors += verify_packet(packet, arp_request_infos) - # check encoding - packet.encode() - packet.decode() - #print(packet) - errors += verify_packet(packet, arp_request_infos) - - # ARP Reply - packet = MACPacket(arp_reply) - packet.decode_remove_header() - packet = ARPPacket(packet) - # check decoding - packet.decode() - #print(packet) - errors += verify_packet(packet, arp_reply_infos) - # check encoding - packet.encode() - packet.decode() - #print(packet) - errors += verify_packet(packet, arp_reply_infos) - - print("arp errors " + str(errors)) \ No newline at end of file diff --git a/liteeth/test/model/dumps.py b/liteeth/test/model/dumps.py deleted file mode 100644 index ae618e0f..00000000 --- a/liteeth/test/model/dumps.py +++ /dev/null @@ -1,90 +0,0 @@ -import re - -def format_dump(dump): - return [int(s, 16) for s in re.split(r'[;,\s\n]\s*', dump) if s is not ""] - -def verify_packet(packet, infos): - errors = 0 - for k, v in infos.items(): - if hasattr(packet, k): - if getattr(packet, k) != v: - print("[Error] " + k) - errors += 1 - return errors - -arp_request = format_dump(""" -00 22 19 22 54 9e 00 12 3f 97 92 01 08 06 00 01 -08 00 06 04 00 01 00 12 3f 97 92 01 a9 fe ff 42 -00 22 19 22 54 9e a9 fe 64 62""") - -arp_request_infos = { - "sender_mac" : 0x00123f979201, - "target_mac" : 0x00221922549e, - "ethernet_type" : 0x806, - "hwtype" : 0x1, - "opcode" : 0x1, - "protosize" : 0x4, - "proto" : 0x800, - "sender_ip" : 0xa9feff42, - "target_ip" : 0xa9fe6462 - -} - -arp_reply = format_dump(""" -00 12 3f 97 92 01 00 22 19 22 54 9e 08 06 00 01 -08 00 06 04 00 02 00 22 19 22 54 9e a9 fe 64 62 -00 12 3f 97 92 01 a9 fe ff 42 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00""") - -arp_reply_infos = { - "sender_mac" : 0x00221922549e, - "target_mac" : 0x00123f979201, - "ethernet_type" : 0x806, - "hwtype" : 0x1, - "opcode" : 0x2, - "protosize" : 0x4, - "proto" : 0x800, - "sender_ip" : 0xa9fe6462, - "target_ip" : 0xa9feff42 -} - -udp = format_dump(""" -d0 7a b5 96 cd 0a 00 14 0b 33 33 27 08 00 45 00 -00 5f 31 16 00 00 80 11 87 77 c0 a8 01 65 b2 7b -0d 78 a6 3f 69 0f 00 4b 6a 54 64 31 3a 61 64 32 -3a 69 64 32 30 3a 5a fa 29 99 3a 5e ce 19 d1 8b -aa 9b 4e 4d f9 2e 51 52 fe ff 65 31 3a 71 34 3a -70 69 6e 67 31 3a 74 34 3a 85 72 00 00 31 3a 76 -34 3a 55 54 7e 62 31 3a 79 31 3a 71 65""") - -udp_infos = { - "sender_mac" : 0x00140b333327, - "target_mac" : 0xd07ab596cd0a, - "protocol" : 0x11, - "sender_ip" : 0xc0a80165, - "target_ip" : 0xb27b0d78, - "src_port" : 0xa63f, - "dst_port" : 0x690f -} - -ping_request = format_dump(""" -00 50 56 e0 14 49 00 0c 29 34 0b de 08 00 45 00 -00 3c d7 43 00 00 80 01 2b 73 c0 a8 9e 8b ae 89 -2a 4d 08 00 2a 5c 02 00 21 00 61 62 63 64 65 66 -67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 -77 61 62 63 64 65 66 67 68 69""") - -ping_request_infos = { - "code" : 0x0, - "msgtype" : 0x8, - "quench" : 0x2002100 -} - -ping_reply = format_dump(""" -00 0c 29 34 0b de 00 50 56 e0 14 49 08 00 45 00 -00 3c 76 e1 00 00 80 01 8b d5 ae 89 2a 4d c0 a8 -9e 8b 00 00 32 5c 02 00 21 00 61 62 63 64 65 66 -67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 -77 61 62 63 64 65 66 67 68 69""") - -ping_reply_infos = {} diff --git a/liteeth/test/model/etherbone.py b/liteeth/test/model/etherbone.py deleted file mode 100644 index 96cf49f7..00000000 --- a/liteeth/test/model/etherbone.py +++ /dev/null @@ -1,354 +0,0 @@ -import math, copy - -from liteeth.common import * -from liteeth.test.common import * - -from liteeth.test.model import udp - -def print_etherbone(s): - print_with_prefix(s, "[ETHERBONE]") - -# Etherbone model -class EtherboneWrite: - def __init__(self, data): - self.data = data - - def __repr__(self): - return "WR32 0x{:08x}".format(self.data) - -class EtherboneRead: - def __init__(self, addr): - self.addr = addr - - def __repr__(self): - return "RD32 @ 0x{:08x}".format(self.addr) - -class EtherboneWrites(Packet): - def __init__(self, init=[], base_addr=0, datas=[]): - Packet.__init__(self, init) - self.base_addr = base_addr - self.writes = [] - self.encoded = init != [] - for data in datas: - self.add(EtherboneWrite(data)) - - def add(self, write): - self.writes.append(write) - - def get_datas(self): - datas = [] - for write in self.writes: - datas.append(write.data) - return datas - - def encode(self): - if self.encoded: - raise ValueError - for byte in split_bytes(self.base_addr, 4): - self.append(byte) - for write in self.writes: - for byte in split_bytes(write.data, 4): - self.append(byte) - self.encoded = True - - def decode(self): - if not self.encoded: - raise ValueError - base_addr = [] - for i in range(4): - base_addr.append(self.pop(0)) - self.base_addr = merge_bytes(base_addr) - self.writes = [] - while len(self) != 0: - write = [] - for i in range(4): - write.append(self.pop(0)) - self.writes.append(EtherboneWrite(merge_bytes(write))) - self.encoded = False - - def __repr__(self): - r = "Writes\n" - r += "--------\n" - r += "BaseAddr @ 0x{:08x}\n".format(self.base_addr) - for write in self.writes: - r += write.__repr__() + "\n" - return r - -class EtherboneReads(Packet): - def __init__(self, init=[], base_ret_addr=0, addrs=[]): - Packet.__init__(self, init) - self.base_ret_addr = base_ret_addr - self.reads = [] - self.encoded = init != [] - for addr in addrs: - self.add(EtherboneRead(addr)) - - def add(self, read): - self.reads.append(read) - - def get_addrs(self): - addrs = [] - for read in self.reads: - addrs.append(read.addr) - return addrs - - def encode(self): - if self.encoded: - raise ValueError - for byte in split_bytes(self.base_ret_addr, 4): - self.append(byte) - for read in self.reads: - for byte in split_bytes(read.addr, 4): - self.append(byte) - self.encoded = True - - def decode(self): - if not self.encoded: - raise ValueError - base_ret_addr = [] - for i in range(4): - base_ret_addr.append(self.pop(0)) - self.base_ret_addr = merge_bytes(base_ret_addr) - self.reads = [] - while len(self) != 0: - read = [] - for i in range(4): - read.append(self.pop(0)) - self.reads.append(EtherboneRead(merge_bytes(read))) - self.encoded = False - - def __repr__(self): - r = "Reads\n" - r += "--------\n" - r += "BaseRetAddr @ 0x{:08x}\n".format(self.base_ret_addr) - for read in self.reads: - r += read.__repr__() + "\n" - return r - -class EtherboneRecord(Packet): - def __init__(self, init=[]): - Packet.__init__(self, init) - self.writes = None - self.reads = None - self.encoded = init != [] - - def get_writes(self): - if self.wcount == 0: - return None - else: - writes = [] - for i in range((self.wcount+1)*4): - writes.append(self.pop(0)) - return EtherboneWrites(writes) - - def get_reads(self): - if self.rcount == 0: - return None - else: - reads = [] - for i in range((self.rcount+1)*4): - reads.append(self.pop(0)) - return EtherboneReads(reads) - - def decode(self): - if not self.encoded: - raise ValueError - header = [] - for byte in self[:etherbone_record_header_len]: - header.append(self.pop(0)) - for k, v in sorted(etherbone_record_header.items()): - setattr(self, k, get_field_data(v, header)) - self.writes = self.get_writes() - if self.writes is not None: - self.writes.decode() - self.reads = self.get_reads() - if self.reads is not None: - self.reads.decode() - self.encoded = False - - def set_writes(self, writes): - self.wcount = len(writes.writes) - writes.encode() - for byte in writes: - self.append(byte) - - def set_reads(self, reads): - self.rcount = len(reads.reads) - reads.encode() - for byte in reads: - self.append(byte) - - def encode(self): - if self.encoded: - raise ValueError - if self.writes is not None: - self.set_writes(self.writes) - if self.reads is not None: - self.set_reads(self.reads) - header = 0 - for k, v in sorted(etherbone_record_header.items()): - value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") - header += (value << v.offset+(v.byte*8)) - for d in split_bytes(header, etherbone_record_header_len): - self.insert(0, d) - self.encoded = True - - def __repr__(self, n=0): - r = "Record {}\n".format(n) - r += "--------\n" - if self.encoded: - for d in self: - r += "{:02x}".format(d) - else: - for k in sorted(etherbone_record_header.keys()): - r += k + " : 0x{:0x}\n".format(getattr(self,k)) - if self.wcount != 0: - r += self.writes.__repr__() - if self.rcount != 0: - r += self.reads.__repr__() - return r - -class EtherbonePacket(Packet): - def __init__(self, init=[]): - Packet.__init__(self, init) - self.encoded = init != [] - self.records = [] - - self.magic = etherbone_magic - self.version = etherbone_version - self.addr_size = 32//8 - self.port_size = 32//8 - self.nr = 0 - self.pr = 0 - self.pf = 0 - - def get_records(self): - records = [] - done = False - payload = self - while len(payload) != 0: - record = EtherboneRecord(payload) - record.decode() - records.append(copy.deepcopy(record)) - payload = record - return records - - def decode(self): - if not self.encoded: - raise ValueError - header = [] - for byte in self[:etherbone_packet_header_len]: - header.append(self.pop(0)) - for k, v in sorted(etherbone_packet_header.items()): - setattr(self, k, get_field_data(v, header)) - self.records = self.get_records() - self.encoded = False - - def set_records(self, records): - for record in records: - record.encode() - for byte in record: - self.append(byte) - - def encode(self): - if self.encoded: - raise ValueError - self.set_records(self.records) - header = 0 - for k, v in sorted(etherbone_packet_header.items()): - value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") - header += (value << v.offset+(v.byte*8)) - for d in split_bytes(header, etherbone_packet_header_len): - self.insert(0, d) - self.encoded = True - - def __repr__(self): - r = "Packet\n" - r += "--------\n" - if self.encoded: - for d in self: - r += "{:02x}".format(d) - else: - for k in sorted(etherbone_packet_header.keys()): - r += k + " : 0x{:0x}\n".format(getattr(self,k)) - for i, record in enumerate(self.records): - r += record.__repr__(i) - return r - -class Etherbone(Module): - def __init__(self, udp, debug=False): - self.udp = udp - self.debug = debug - self.tx_packets = [] - self.tx_packet = EtherbonePacket() - self.rx_packet = EtherbonePacket() - - udp.set_etherbone_callback(self.callback) - - def send(self, packet): - packet.encode() - if self.debug: - print_etherbone(">>>>>>>>") - print_etherbone(packet) - udp_packet = udp.UDPPacket(packet) - udp_packet.src_port = 0x1234 # XXX - udp_packet.dst_port = 20000 # XXX - udp_packet.length = len(packet) - udp_packet.checksum = 0 - self.udp.send(udp_packet) - - def receive(self): - self.rx_packet = EtherbonePacket() - while not self.rx_packet.done: - yield - - def callback(self, packet): - packet = EtherbonePacket(packet) - packet.decode() - if self.debug: - print_etherbone("<<<<<<<<") - print_etherbone(packet) - self.rx_packet = packet - self.rx_packet.done = True - self.process(packet) - - def process(self, packet): - pass - -if __name__ == "__main__": - # Writes/Reads - writes = EtherboneWrites(base_addr=0x1000, datas=[i for i in range(16)]) - reads = EtherboneReads(base_ret_addr=0x2000, addrs=[i for i in range(16)]) - - # Record - record = EtherboneRecord() - record.writes = writes - record.reads = reads - record.bca = 0 - record.rca = 0 - record.rff = 0 - record.cyc = 0 - record.wca = 0 - record.wff = 0 - record.byte_enable = 0 - record.wcount = len(writes.get_datas()) - record.rcount = len(reads.get_addrs()) - - # Packet - packet = EtherbonePacket() - packet.records = [copy.deepcopy(record) for i in range(8)] - packet.nr = 0 - packet.pr = 0 - packet.pf = 0 - #print(packet) - packet.encode() - #print(packet) - - # Send packet over UDP to check against Wireshark dissector - import socket - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.sendto(bytes(packet), ("192.168.1.1", 20000)) - - packet = EtherbonePacket(packet) - packet.decode() - print(packet) diff --git a/liteeth/test/model/icmp.py b/liteeth/test/model/icmp.py deleted file mode 100644 index cf54b17c..00000000 --- a/liteeth/test/model/icmp.py +++ /dev/null @@ -1,102 +0,0 @@ -import math - -from liteeth.common import * -from liteeth.test.common import * - -from liteeth.test.model import ip - -def print_icmp(s): - print_with_prefix(s, "[ICMP]") - -# ICMP model -class ICMPPacket(Packet): - def __init__(self, init=[]): - Packet.__init__(self, init) - - def decode(self): - header = [] - for byte in self[:icmp_header_len]: - header.append(self.pop(0)) - for k, v in sorted(icmp_header.items()): - setattr(self, k, get_field_data(v, header)) - - def encode(self): - header = 0 - for k, v in sorted(icmp_header.items()): - value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") - header += (value << v.offset+(v.byte*8)) - for d in split_bytes(header, icmp_header_len): - self.insert(0, d) - - def __repr__(self): - r = "--------\n" - for k in sorted(icmp_header.keys()): - r += k + " : 0x{:0x}\n".format(getattr(self,k)) - r += "payload: " - for d in self: - r += "{:02x}".format(d) - return r - -class ICMP(Module): - def __init__(self, ip, ip_address, debug=False): - self.ip = ip - self.ip_address = ip_address - self.debug = debug - self.tx_packets = [] - self.tx_packet = ICMPPacket() - self.rx_packet = ICMPPacket() - - self.ip.set_icmp_callback(self.callback) - - def send(self, packet): - packet.encode() - if self.debug: - print_icmp(">>>>>>>>") - print_icmp(packet) - ip_packet = ip.IPPacket(packet) - ip_packet.version = 0x4 - ip_packet.ihl = 0x5 - ip_packet.total_length = len(packet) + ip_packet.ihl - ip_packet.identification = 0 - ip_packet.flags = 0 - ip_packet.fragment_offset = 0 - ip_packet.ttl = 0x80 - ip_packet.sender_ip = self.ip_address - ip_packet.target_ip = 0x12345678 # XXX - ip_packet.checksum = 0 - ip_packet.protocol = icmp_protocol - self.ip.send(ip_packet) - - def callback(self, packet): - packet = ICMPPacket(packet) - packet.decode() - if self.debug: - print_icmp("<<<<<<<<") - print_icmp(packet) - self.process(packet) - - def process(self, packet): - pass - -if __name__ == "__main__": - from liteeth.test.model.dumps import * - from liteeth.test.model.mac import * - from liteeth.test.model.ip import * - errors = 0 - # ICMP packet - packet = MACPacket(ping_request) - packet.decode_remove_header() - #print(packet) - packet = IPPacket(packet) - packet.decode() - #print(packet) - packet = ICMPPacket(packet) - packet.decode() - #print(packet) - errors += verify_packet(packet, ping_request_infos) - packet.encode() - packet.decode() - #print(packet) - errors += verify_packet(packet, ping_request_infos) - - print("icmp errors " + str(errors)) \ No newline at end of file diff --git a/liteeth/test/model/ip.py b/liteeth/test/model/ip.py deleted file mode 100644 index 03c5f8b5..00000000 --- a/liteeth/test/model/ip.py +++ /dev/null @@ -1,148 +0,0 @@ -import math - -from liteeth.common import * -from liteeth.test.common import * - -from liteeth.test.model import mac - -def print_ip(s): - print_with_prefix(s, "[IP]") - -def carry_around_add(a, b): - c = a + b - return (c & 0xffff) + (c >> 16) - -def checksum(msg): - s = 0 - for i in range(0, len(msg), 2): - w = msg[i] + (msg[i+1] << 8) - s = carry_around_add(s, w) - return ~s & 0xffff - -# IP model -class IPPacket(Packet): - def __init__(self, init=[]): - Packet.__init__(self, init) - - def get_checksum(self): - return self[10] | (self[11] << 8) - - def check_checksum(self): - return checksum(self[:ipv4_header_len]) == 0 - - def decode(self): - header = [] - for byte in self[:ipv4_header_len]: - header.append(self.pop(0)) - for k, v in sorted(ipv4_header.items()): - setattr(self, k, get_field_data(v, header)) - - def encode(self): - header = 0 - for k, v in sorted(ipv4_header.items()): - value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") - header += (value << v.offset+(v.byte*8)) - for d in split_bytes(header, ipv4_header_len): - self.insert(0, d) - - def insert_checksum(self): - self[10] = 0 - self[11] = 0 - c = checksum(self[:ipv4_header_len]) - self[10] = c & 0xff - self[11] = (c >> 8) & 0xff - - def __repr__(self): - r = "--------\n" - for k in sorted(ipv4_header.keys()): - r += k + " : 0x{:0x}\n".format(getattr(self,k)) - r += "payload: " - for d in self: - r += "{:02x}".format(d) - return r - -class IP(Module): - def __init__(self, mac, mac_address, ip_address, debug=False, loopback=False): - self.mac = mac - self.mac_address = mac_address - self.ip_address = ip_address - self.debug = debug - self.loopback = loopback - self.rx_packet = IPPacket() - self.table = {} - self.request_pending = False - - self.udp_callback = None - self.icmp_callback = None - - self.mac.set_ip_callback(self.callback) - - def set_udp_callback(self, callback): - self.udp_callback = callback - - def set_icmp_callback(self, callback): - self.icmp_callback = callback - - def send(self, packet): - packet.encode() - packet.insert_checksum() - if self.debug: - print_ip(">>>>>>>>") - print_ip(packet) - mac_packet = mac.MACPacket(packet) - mac_packet.target_mac = 0x12345678abcd # XXX - mac_packet.sender_mac = self.mac_address - mac_packet.ethernet_type = ethernet_type_ip - self.mac.send(mac_packet) - - def callback(self, packet): - packet = IPPacket(packet) - if not packet.check_checksum(): - received = packet.get_checksum() - packet.insert_checksum() - expected = packet.get_checksum() - raise ValueError("Checksum error received {:04x} / expected {:04x}".format(received, expected)) - packet.decode() - if self.debug: - print_ip("<<<<<<<<") - print_ip(packet) - if self.loopback: - self.send(packet) - else: - if packet.version != 0x4: - raise ValueError - if packet.ihl != 0x5: - raise ValueError - self.process(packet) - - def process(self, packet): - if packet.protocol == udp_protocol: - if self.udp_callback is not None: - self.udp_callback(packet) - elif packet.protocol == icmp_protocol: - if self.icmp_callback is not None: - self.icmp_callback(packet) - -if __name__ == "__main__": - from liteeth.test.model.dumps import * - from liteeth.test.model.mac import * - errors = 0 - # UDP packet - packet = MACPacket(udp) - packet.decode_remove_header() - #print(packet) - packet = IPPacket(packet) - # check decoding - errors += not packet.check_checksum() - packet.decode() - #print(packet) - errors += verify_packet(packet, {}) - # check encoding - packet.encode() - packet.insert_checksum() - errors += not packet.check_checksum() - packet.decode() - #print(packet) - errors += verify_packet(packet, {}) - - print("ip errors " + str(errors)) \ No newline at end of file diff --git a/liteeth/test/model/mac.py b/liteeth/test/model/mac.py deleted file mode 100644 index 9267f88b..00000000 --- a/liteeth/test/model/mac.py +++ /dev/null @@ -1,150 +0,0 @@ -import math, binascii - -from liteeth.common import * -from liteeth.test.common import * - -def print_mac(s): - print_with_prefix(s, "[MAC]") - -preamble = split_bytes(eth_preamble, 8, "little") - -def crc32(l): - crc = [] - crc_bytes = split_bytes(binascii.crc32(bytes(l)), 4, "little") - for byte in crc_bytes: - crc.append(int(byte)) - return crc - -# MAC model -class MACPacket(Packet): - def __init__(self, init=[]): - Packet.__init__(self, init) - self.preamble_error = False - self.crc_error = False - - def check_remove_preamble(self): - if comp(self[0:8], preamble): - for i in range(8): - self.pop(0) - return False - else: - return True - - def check_remove_crc(self): - if comp(self[-4:], crc32(self[:-4])): - for i in range(4): - self.pop() - return False - else: - return True - - def decode_remove_header(self): - header = [] - for byte in self[:mac_header_len]: - header.append(self.pop(0)) - for k, v in sorted(mac_header.items()): - setattr(self, k, get_field_data(v, header)) - - def decode(self): - self.preamble_error = self.check_remove_preamble() - self.crc_error = self.check_remove_crc() - if self.crc_error or self.preamble_error: - raise ValueError # XXX handle this properly - else: - self.decode_remove_header() - - def encode_header(self): - header = 0 - for k, v in sorted(mac_header.items()): - value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") - header += (value << v.offset+(v.byte*8)) - for d in split_bytes(header, mac_header_len): - self.insert(0, d) - - def insert_crc(self): - for d in crc32(self): - self.append(d) - - def insert_preamble(self): - for d in reversed(preamble): - self.insert(0, d) - - def encode(self): - self.encode_header() - self.insert_crc() - self.insert_preamble() - - def __repr__(self): - r = "--------\n" - for k in sorted(mac_header.keys()): - r += k + " : 0x{:0x}\n".format(getattr(self,k)) - r += "payload: " - for d in self: - r += "{:02x}".format(d) - return r - -class MAC(Module): - def __init__(self, phy, debug=False, loopback=False): - self.phy = phy - self.debug = debug - self.loopback = loopback - self.rx_packet = MACPacket() - - self.ip_callback = None - self.arp_callback = None - - phy.set_mac_callback(self.callback) - - def set_ip_callback(self, callback): - self.ip_callback = callback - - def set_arp_callback(self, callback): - self.arp_callback = callback - - def send(self, packet): - if self.debug: - print_mac(">>>>>>>>") - print_mac(packet) - packet.encode() - self.phy.send(packet) - - def callback(self, datas): - packet = MACPacket(datas) - packet.decode() - if self.debug: - print_mac("<<<<<<<<") - print_mac(packet) - if self.loopback: - self.send(packet) - else: - if packet.ethernet_type == ethernet_type_ip: - if self.ip_callback is not None: - self.ip_callback(packet) - elif packet.ethernet_type == ethernet_type_arp: - if self.arp_callback is not None: - self.arp_callback(packet) - else: - raise ValueError # XXX handle this properly - -if __name__ == "__main__": - from liteeth.test.model.dumps import * - errors = 0 - packet = MACPacket(arp_request) - packet.decode_remove_header() - #print(packet) - errors += verify_packet(packet, arp_request_infos) - packet.encode_header() - packet.decode_remove_header() - #print(packet) - errors += verify_packet(packet, arp_request_infos) - - #print(packet) - packet = MACPacket(arp_reply) - packet.decode_remove_header() - errors += verify_packet(packet, arp_reply_infos) - packet.encode_header() - packet.decode_remove_header() - #print(packet) - errors += verify_packet(packet, arp_reply_infos) - - print("mac errors " + str(errors)) diff --git a/liteeth/test/model/phy.py b/liteeth/test/model/phy.py deleted file mode 100644 index 1401110f..00000000 --- a/liteeth/test/model/phy.py +++ /dev/null @@ -1,56 +0,0 @@ -from liteeth.common import * -from liteeth.test.common import * - -def print_phy(s): - print_with_prefix(s, "[PHY]") - -# PHY model -class PHYSource(PacketStreamer): - def __init__(self, dw): - PacketStreamer.__init__(self, eth_phy_description(dw)) - -class PHYSink(PacketLogger): - def __init__(self, dw): - PacketLogger.__init__(self, eth_phy_description(dw)) - -class PHY(Module): - def __init__(self, dw, debug=False): - self.dw = dw - self.debug = debug - - self.submodules.phy_source = PHYSource(dw) - self.submodules.phy_sink = PHYSink(dw) - - self.source = self.phy_source.source - self.sink = self.phy_sink.sink - - self.mac_callback = None - - def set_mac_callback(self, callback): - self.mac_callback = callback - - def send(self, datas): - packet = Packet(datas) - if self.debug: - r = ">>>>>>>>\n" - r += "length " + str(len(datas)) + "\n" - for d in datas: - r += "{:02x}".format(d) - print_phy(r) - self.phy_source.send(packet) - - def receive(self): - yield from self.phy_sink.receive() - if self.debug: - r = "<<<<<<<<\n" - r += "length " + str(len(self.phy_sink.packet)) + "\n" - for d in self.phy_sink.packet: - r += "{:02x}".format(d) - print_phy(r) - self.packet = self.phy_sink.packet - - def gen_simulation(self, selfp): - while True: - yield from self.receive() - if self.mac_callback is not None: - self.mac_callback(self.packet) diff --git a/liteeth/test/model/udp.py b/liteeth/test/model/udp.py deleted file mode 100644 index 20e0e6f2..00000000 --- a/liteeth/test/model/udp.py +++ /dev/null @@ -1,117 +0,0 @@ -import math - -from liteeth.common import * -from liteeth.test.common import * - -from liteeth.test.model import ip - -def print_udp(s): - print_with_prefix(s, "[UDP]") - -# UDP model -class UDPPacket(Packet): - def __init__(self, init=[]): - Packet.__init__(self, init) - - def decode(self): - header = [] - for byte in self[:udp_header_len]: - header.append(self.pop(0)) - for k, v in sorted(udp_header.items()): - setattr(self, k, get_field_data(v, header)) - - def encode(self): - header = 0 - for k, v in sorted(udp_header.items()): - value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") - header += (value << v.offset+(v.byte*8)) - for d in split_bytes(header, udp_header_len): - self.insert(0, d) - - def __repr__(self): - r = "--------\n" - for k in sorted(udp_header.keys()): - r += k + " : 0x{:0x}\n".format(getattr(self,k)) - r += "payload: " - for d in self: - r += "{:02x}".format(d) - return r - -class UDP(Module): - def __init__(self, ip, ip_address, debug=False, loopback=False): - self.ip = ip - self.ip_address = ip_address - self.debug = debug - self.loopback = loopback - self.tx_packets = [] - self.tx_packet = UDPPacket() - self.rx_packet = UDPPacket() - - self.etherbone_callback = None - - self.ip.set_udp_callback(self.callback) - - def set_etherbone_callback(self, callback): - self.etherbone_callback = callback - - def send(self, packet): - packet.encode() - if self.debug: - print_udp(">>>>>>>>") - print_udp(packet) - ip_packet = ip.IPPacket(packet) - ip_packet.version = 0x4 - ip_packet.ihl = 0x5 - ip_packet.total_length = len(packet) + ip_packet.ihl - ip_packet.identification = 0 - ip_packet.flags = 0 - ip_packet.fragment_offset = 0 - ip_packet.ttl = 0x80 - ip_packet.sender_ip = self.ip_address - ip_packet.target_ip = 0x12345678 # XXX - ip_packet.checksum = 0 - ip_packet.protocol = udp_protocol - self.ip.send(ip_packet) - - def callback(self, packet): - packet = UDPPacket(packet) - packet.decode() - if self.debug: - print_udp("<<<<<<<<") - print_udp(packet) - if self.loopback: - self.send(packet) - else: - self.process(packet) - - def process(self, packet): - if packet.dst_port == 20000: - if self.etherbone_callback is not None: - self.etherbone_callback(packet) - -if __name__ == "__main__": - from liteeth.test.model.dumps import * - from liteeth.test.model.mac import * - from liteeth.test.model.ip import * - errors = 0 - # UDP packet - packet = MACPacket(udp) - packet.decode_remove_header() - #print(packet) - packet = IPPacket(packet) - packet.decode() - #print(packet) - packet = UDPPacket(packet) - packet.decode() - #print(packet) - if packet.length != (len(packet)+udp_header_len): - errors += 1 - errors += verify_packet(packet, udp_infos) - packet.encode() - packet.decode() - #print(packet) - if packet.length != (len(packet)+udp_header_len): - errors += 1 - errors += verify_packet(packet, udp_infos) - - print("udp errors " + str(errors)) \ No newline at end of file diff --git a/liteeth/test/udp_tb.py b/liteeth/test/udp_tb.py deleted file mode 100644 index 74811dc7..00000000 --- a/liteeth/test/udp_tb.py +++ /dev/null @@ -1,70 +0,0 @@ -from migen.fhdl.std import * -from migen.bus import wishbone -from migen.bus.transactions import * -from migen.sim.generic import run_simulation - -from liteeth.common import * -from liteeth.core import LiteEthUDPIPCore - -from liteeth.test.common import * -from liteeth.test.model import phy, mac, arp, ip, udp - -ip_address = 0x12345678 -mac_address = 0x12345678abcd - -class TB(Module): - def __init__(self, dw=8): - self.dw = dw - self.submodules.phy_model = phy.PHY(8, debug=False) - self.submodules.mac_model = mac.MAC(self.phy_model, debug=False, loopback=False) - self.submodules.arp_model = arp.ARP(self.mac_model, mac_address, ip_address, debug=False) - self.submodules.ip_model = ip.IP(self.mac_model, mac_address, ip_address, debug=False, loopback=False) - self.submodules.udp_model = udp.UDP(self.ip_model, ip_address, debug=False, loopback=True) - - self.submodules.core = LiteEthUDPIPCore(self.phy_model, mac_address, ip_address, 100000) - udp_port = self.core.udp.crossbar.get_port(0x5678, dw) - self.submodules.streamer = PacketStreamer(eth_udp_user_description(dw)) - self.submodules.logger = PacketLogger(eth_udp_user_description(dw)) - self.comb += [ - Record.connect(self.streamer.source, udp_port.sink), - udp_port.sink.ip_address.eq(0x12345678), - udp_port.sink.src_port.eq(0x1234), - udp_port.sink.dst_port.eq(0x5678), - udp_port.sink.length.eq(64//(dw//8)), - Record.connect(udp_port.source, self.logger.sink) - ] - - # use sys_clk for each clock_domain - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.comb += [ - self.cd_eth_rx.clk.eq(ClockSignal()), - self.cd_eth_rx.rst.eq(ResetSignal()), - self.cd_eth_tx.clk.eq(ClockSignal()), - self.cd_eth_tx.rst.eq(ResetSignal()), - ] - - def gen_simulation(self, selfp): - selfp.cd_eth_rx.rst = 1 - selfp.cd_eth_tx.rst = 1 - yield - selfp.cd_eth_rx.rst = 0 - selfp.cd_eth_tx.rst = 0 - - for i in range(100): - yield - - while True: - packet = Packet([i for i in range(64//(self.dw//8))]) - yield from self.streamer.send(packet) - yield from self.logger.receive() - - # check results - s, l, e = check(packet, self.logger.packet) - print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) - - -if __name__ == "__main__": - run_simulation(TB(8), ncycles=2048, vcd_name="my.vcd", keep_files=True) - run_simulation(TB(16), ncycles=2048, vcd_name="my.vcd", keep_files=True) - run_simulation(TB(32), ncycles=2048, vcd_name="my.vcd", keep_files=True) \ No newline at end of file diff --git a/make.py b/make.py deleted file mode 100644 index c55c8339..00000000 --- a/make.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python3 - -import sys, os, argparse, subprocess, struct, importlib - -from mibuild.tools import write_to_file -from migen.util.misc import autotype -from migen.fhdl import verilog, edif -from migen.fhdl.structure import _Fragment -from migen.bank.description import CSRStatus -from mibuild import tools -from mibuild.xilinx_common import * - -from liteeth.common import * - -def get_csr_csv(regions): - r = "" - for name, origin, busword, obj in regions: - if not isinstance(obj, Memory): - for csr in obj: - nr = (csr.size + busword - 1)//busword - r += "{}_{},0x{:08x},{},{}\n".format(name, csr.name, origin, nr, "ro" if isinstance(csr, CSRStatus) else "rw") - origin += 4*nr - return r - -def _import(default, name): - return importlib.import_module(default + "." + name) - -def _get_args(): - parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, - description="""\ -LiteEth - based on Migen. - -This program builds and/or loads LiteEth components. -One or several actions can be specified: - -clean delete previous build(s). -build-rtl build verilog rtl. -build-bitstream build-bitstream build FPGA bitstream. -build-csr-csv save CSR map into CSV file. - -load-bitstream load bitstream into volatile storage. - -all clean, build-csr-csv, build-bitstream, load-bitstream. -""") - - parser.add_argument("-t", "--target", default="base", help="Core type to build") - parser.add_argument("-s", "--sub-target", default="", help="variant of the Core type to build") - parser.add_argument("-p", "--platform", default=None, help="platform to build for") - parser.add_argument("-Ot", "--target-option", default=[], nargs=2, action="append", help="set target-specific option") - parser.add_argument("-Op", "--platform-option", default=[("programmer", "vivado")], nargs=2, action="append", help="set platform-specific option") - parser.add_argument("--csr_csv", default="./test/csr.csv", help="CSV file to save the CSR map into") - - parser.add_argument("action", nargs="+", help="specify an action") - - return parser.parse_args() - -# Note: misoclib need to be installed as a python library - -if __name__ == "__main__": - args = _get_args() - - # create top-level Core object - target_module = _import("targets", args.target) - if args.sub_target: - top_class = getattr(target_module, args.sub_target) - else: - top_class = target_module.default_subtarget - - if args.platform is None: - platform_name = top_class.default_platform - else: - platform_name = args.platform - platform_module = _import("platforms", platform_name) - platform_kwargs = dict((k, autotype(v)) for k, v in args.platform_option) - platform = platform_module.Platform(**platform_kwargs) - - build_name = top_class.__name__.lower() + "-" + platform_name - top_kwargs = dict((k, autotype(v)) for k, v in args.target_option) - soc = top_class(platform, **top_kwargs) - soc.finalize() - - # decode actions - action_list = ["clean", "build-csr-csv", "build-bitstream", "load-bitstream", "all"] - actions = {k: False for k in action_list} - for action in args.action: - if action in actions: - actions[action] = True - else: - print("Unknown action: "+action+". Valid actions are:") - for a in action_list: - print(" "+a) - sys.exit(1) - - print(""" - __ _ __ ______ __ - / / (_) /____ / __/ /_/ / - / /__/ / __/ -_) _// __/ _ \\ - /____/_/\__/\__/___/\__/_//_/ - - A small footprint and configurable Ethernet - core powered by Migen -====== Building options: ====== -Platform: {} -Target: {} -Subtarget: {} -System Clk: {} MHz -===============================""".format( - platform_name, - args.target, - top_class.__name__, - soc.clk_freq/1000000 - ) -) - - # dependencies - if actions["all"]: - actions["build-csr-csv"] = True - actions["build-bitstream"] = True - actions["load-bitstream"] = True - - if actions["build-bitstream"]: - actions["build-csr-csv"] = True - actions["build-bitstream"] = True - actions["load-bitstream"] = True - - if actions["clean"]: - subprocess.call(["rm", "-rf", "build/*"]) - - if actions["build-csr-csv"]: - csr_csv = get_csr_csv(soc.cpu_csr_regions) - write_to_file(args.csr_csv, csr_csv) - - if actions["build-bitstream"]: - vns = platform.build(soc, build_name=build_name) - if hasattr(soc, "do_exit") and vns is not None: - if hasattr(soc.do_exit, '__call__'): - soc.do_exit(vns) - - if actions["load-bitstream"]: - prog = platform.create_programmer() - prog.load_bitstream("build/" + build_name + platform.bitstream_ext) diff --git a/misoclib/LICENSE b/misoclib/LICENSE new file mode 100644 index 00000000..cbbfe8be --- /dev/null +++ b/misoclib/LICENSE @@ -0,0 +1,28 @@ +Unless otherwise noted, LiteEth is copyright (C) 2015 Florent Kermarrec. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Other authors retain ownership of their contributions. If a submission can +reasonably be considered independently copyrightable, it's yours and we +encourage you to claim it with appropriate copyright notices. This submission +then falls under the "otherwise noted" category. All submissions are strongly +encouraged to use the two-clause BSD license reproduced above. diff --git a/misoclib/README b/misoclib/README new file mode 100644 index 00000000..b3308328 --- /dev/null +++ b/misoclib/README @@ -0,0 +1,142 @@ + __ _ __ ______ __ + / / (_) /____ / __/ /_/ / + / /__/ / __/ -_) _// __/ _ \ + /____/_/\__/\__/___/\__/_//_/ + + Copyright 2012-2015 / EnjoyDigital + florent@enjoy-digital.fr + + A small footprint and configurable Ethernet core + with UDP/IP hw stack and Etherbone frontend + powered by Migen + +[> Doc +--------- +HTML : www.enjoy-digital.fr/litex/liteeth/ +PDF : www.enjoy-digital.fr/litex/liteeth.pdf + +[> Intro +--------- +LiteEth provides a small footprint and configurable Ethernet core. + +LiteEth is part of LiteX libraries whose aims are to lower entry level of +complex FPGA cores by providing simple, elegant and efficient implementations +ofcomponents used in today's SoC such as Ethernet, SATA, PCIe, SDRAM Controller... + +The core uses simple and specific streaming buses and will provides in the future +adapters to use standardized AXI or Avalon-ST streaming buses. + +Since Python is used to describe the HDL, the core is highly and easily +configurable. + +LiteEth uses technologies developed in partnership with M-Labs Ltd: + - Migen enables generating HDL with Python in an efficient way. + - MiSoC provides the basic blocks to build a powerful and small footprint SoC. + +LiteEth can be used as a Migen/MiSoC library (by simply installing it +with the provided setup.py) or can be integrated with your standard design flow +by generating the verilog rtl that you will use as a standard core. + +[> Features +----------- +- Ethernet MAC with various interfaces and various PHYs (GMII, MII, Loopback) +- Hardware UDP/IP stack with ARP and ICMP + +[> Possible improvements +------------------------- +- add standardized interfaces (AXI, Avalon-ST) +- add DMA interface to MAC +- add RGMII/SGMII PHYs +- ... See below Support and consulting :) + +If you want to support these features, please contact us at florent [AT] +enjoy-digital.fr. You can also contact our partner on the public mailing list +devel [AT] lists.m-labs.hk. + + +[> Getting started +------------------ +1. Install Python3 and your vendor's software + +2. Obtain Migen and install it: + git clone https://github.com/m-labs/migen + cd migen + python3 setup.py install + cd .. + +Note: in case you have issues with Migen, please retry +with our fork at: + https://github.com/enjoy-digital/misoc +until new features are merged. + +3. Obtain LiteScope and install it: + git clone https://github.com/enjoy-digital/litescope + cd litescope + python3 setup.py install + cd .. + +4. Obtain LiteEth + git clone https://github.com/enjoy-digital/liteeth + +5. Build and load UDP loopback design (only for KC705 for now): + python3 make.py -t udp all + +6. Test design (only for KC705 for now): + try to ping 192.168.1.40 + go to ./test directory: + change com port in config.py to your com port + run make test_udp + +7. Build and load Etherbone design (only for KC705 for now): + python3 make.py -t etherbone all + +8. Test design (only for KC705 for now): + try to ping 192.168.1.40 + go to ./test directory run: + run make test_etherbone + +[> Simulations: + Simulations are available in ./liteth/test/: + - mac_core_tb + - mac_wishbone_tb + - arp_tb + - ip_tb + - icmp_tb + - udp_tb + All ethernet layers have their own model tested against real Ethernet dumps (dumps.py) + To run a simulation, move to ./liteeth/test and run: + make simulation_name + +[> Tests : + An UDP loopback example is provided and be controlled with: ./test/test_udp.py + An Etherbone example with Wishbone SRAM is provided and can be controlled with: + ./test/test_etherbone.py + +[> License +----------- +LiteEth is released under the very permissive two-clause BSD license. Under +the terms of this license, you are authorized to use LiteEth for closed-source +proprietary designs. +Even though we do not require you to do so, those things are awesome, so please +do them if possible: + - tell us that you are using LiteEth + - cite LiteEth in publications related to research it has helped + - send us feedback and suggestions for improvements + - send us bug reports when something goes wrong + - send us the modifications and improvements you have done to LiteEth. + +[> Support and consulting +-------------------------- +We love open-source hardware and like sharing our designs with others. + +LiteEth is developed and maintained by EnjoyDigital. + +If you would like to know more about LiteEth or if you are already a happy +user and would like to extend it for your needs, EnjoyDigital can provide standard +commercial support as well as consulting services. + +So feel free to contact us, we'd love to work with you! (and eventually shorten +the list of the possible improvements :) + +[> Contact +E-mail: florent [AT] enjoy-digital.fr \ No newline at end of file diff --git a/misoclib/doc/.gitignore b/misoclib/doc/.gitignore new file mode 100644 index 00000000..873d48e7 --- /dev/null +++ b/misoclib/doc/.gitignore @@ -0,0 +1,8 @@ +*.diff +*.pyc +*~ +*.bc +*.md + +# Ignore generated files +build/ diff --git a/misoclib/doc/Makefile b/misoclib/doc/Makefile new file mode 100644 index 00000000..512689bd --- /dev/null +++ b/misoclib/doc/Makefile @@ -0,0 +1,157 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/misoclib/doc/make.bat b/misoclib/doc/make.bat new file mode 100644 index 00000000..ef155313 --- /dev/null +++ b/misoclib/doc/make.bat @@ -0,0 +1,220 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source +set I18NSPHINXOPTS=%SPHINXOPTS% source +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +%SPHINXBUILD% 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 +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/misoclib/doc/source/_static/LiteEth_logo_full.png b/misoclib/doc/source/_static/LiteEth_logo_full.png new file mode 100644 index 00000000..b8e14b9f Binary files /dev/null and b/misoclib/doc/source/_static/LiteEth_logo_full.png differ diff --git a/misoclib/doc/source/_static/LiteEth_logo_full.svg b/misoclib/doc/source/_static/LiteEth_logo_full.svg new file mode 100644 index 00000000..4ce07aff --- /dev/null +++ b/misoclib/doc/source/_static/LiteEth_logo_full.svg @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + LITE + ETH + + powered by + + + + + + + + + + + + + + + + + + + + + diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/__init__.py b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/__init__.py new file mode 100644 index 00000000..bfcf0d6c --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/__init__.py @@ -0,0 +1,17 @@ +"""Sphinx ReadTheDocs theme. + +From https://github.com/ryan-roemer/sphinx-bootstrap-theme. + +""" +import os + +VERSION = (0, 1, 6) + +__version__ = ".".join(str(v) for v in VERSION) +__version_full__ = __version__ + + +def get_html_theme_path(): + """Return list of HTML theme paths.""" + cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + return cur_dir diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/breadcrumbs.html b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/breadcrumbs.html new file mode 100644 index 00000000..605cb9d2 --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/breadcrumbs.html @@ -0,0 +1,11 @@ +
+ + + +
\ No newline at end of file diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/footer.html b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/footer.html new file mode 100644 index 00000000..b1788192 --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/footer.html @@ -0,0 +1,100 @@ +
+ {% if next or prev %} + + {% endif %} + + + + + + + + + + + + {% set footer_links = [ + ('docs/getting_started/bug_reports', 'Report Bug', 'Report Bug'), + ('docs/introducing_liteeth/license', 'Licensing', 'Licensing'), + ('docs/contributing/contributing', 'Contributing', 'Contributing'), + ('docs/introducing_liteeth/release_notes', 'Release notes', 'Release notes'), + ('docs/introducing_liteeth/community', 'Help', 'Contact'), +] -%} + + + + + + + + + + + + + +
diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/layout.html b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/layout.html new file mode 100644 index 00000000..8cca0aa2 --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/layout.html @@ -0,0 +1,208 @@ +{# TEMPLATE VAR SETTINGS #} +{%- set url_root = pathto('', 1) %} +{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} +{%- if not embedded and docstitle %} + {%- set titlesuffix = " — "|safe + docstitle|e %} +{%- else %} + {%- set titlesuffix = "" %} +{%- endif %} + + + + + + + + {% block htmltitle %} + {{ title|striptags|e }}{{ titlesuffix }} + {% endblock %} + + {# FAVICON #} + {% if favicon %} + + {% endif %} + + {# CSS #} + + + {# OPENSEARCH #} + {% if not embedded %} + {% if use_opensearch %} + + {% endif %} + + {% endif %} + + {# RTD hosts this file, so just load on non RTD builds #} + {% if not READTHEDOCS %} + + {% endif %} + + {% for cssfile in css_files %} + + {% endfor %} + + {%- block linktags %} + {%- if hasdoc('about') %} + + {%- endif %} + {%- if hasdoc('genindex') %} + + {%- endif %} + {%- if hasdoc('search') %} + + {%- endif %} + {%- if hasdoc('copyright') %} + + {%- endif %} + + {%- if parents %} + + {%- endif %} + {%- if next %} + + {%- endif %} + {%- if prev %} + + {%- endif %} + {%- endblock %} + {%- block extrahead %} {% endblock %} + + {# Keep modernizr in head - http://modernizr.com/docs/#installing #} + + + + + + + +
+ +
+ + + + {# SIDE NAV, TOGGLES ON MOBILE #} + + +
+ + {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #} + + + + + {# PAGE CONTENT #} +
+
+ + + + + {% set navigation_bar = [ + ('docs/index', 'Docs', 'Documentation'), + ('docs/getting_started/downloads', 'SDK', 'Downloads'), + ('docs/introducing_liteeth/community', 'Help', 'Community'), + ('https://github.com/enjoy-digital/liteeth', 'Github', 'Github Project') +] -%} + + + + + + + + + +
+ {% block body %}{% endblock %} +
+ {% include "footer.html" %} +
+
+ +
+ +
+ {% include "versions.html" %} + + {% if not embedded %} + + + {%- for scriptfile in script_files %} + + {%- endfor %} + + {% endif %} + + {# RTD hosts this file, so just load on non RTD builds #} + {% if not READTHEDOCS %} + + {% endif %} + + {# STICKY NAVIGATION #} + {% if theme_sticky_navigation %} + + {% endif %} + + {%- block footer %} {% endblock %} +
+ + diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/layout_old.html b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/layout_old.html new file mode 100644 index 00000000..deb8df2a --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/layout_old.html @@ -0,0 +1,205 @@ +{# + basic/layout.html + ~~~~~~~~~~~~~~~~~ + + Master layout template for Sphinx themes. + + :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- block doctype -%} + +{%- endblock %} +{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} +{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} +{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and + (sidebars != []) %} +{%- set url_root = pathto('', 1) %} +{# XXX necessary? #} +{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} +{%- if not embedded and docstitle %} + {%- set titlesuffix = " — "|safe + docstitle|e %} +{%- else %} + {%- set titlesuffix = "" %} +{%- endif %} + +{%- macro relbar() %} + +{%- endmacro %} + +{%- macro sidebar() %} + {%- if render_sidebar %} +
+
+ {%- block sidebarlogo %} + {%- if logo %} + + {%- endif %} + {%- endblock %} + {%- if sidebars != None %} + {#- new style sidebar: explicitly include/exclude templates #} + {%- for sidebartemplate in sidebars %} + {%- include sidebartemplate %} + {%- endfor %} + {%- else %} + {#- old style sidebars: using blocks -- should be deprecated #} + {%- block sidebartoc %} + {%- include "localtoc.html" %} + {%- endblock %} + {%- block sidebarrel %} + {%- include "relations.html" %} + {%- endblock %} + {%- block sidebarsourcelink %} + {%- include "sourcelink.html" %} + {%- endblock %} + {%- if customsidebar %} + {%- include customsidebar %} + {%- endif %} + {%- block sidebarsearch %} + {%- include "searchbox.html" %} + {%- endblock %} + {%- endif %} +
+
+ {%- endif %} +{%- endmacro %} + +{%- macro script() %} + + {%- for scriptfile in script_files %} + + {%- endfor %} +{%- endmacro %} + +{%- macro css() %} + + + {%- for cssfile in css_files %} + + {%- endfor %} +{%- endmacro %} + + + + + {{ metatags }} + {%- block htmltitle %} + {{ title|striptags|e }}{{ titlesuffix }} + {%- endblock %} + {{ css() }} + {%- if not embedded %} + {{ script() }} + {%- if use_opensearch %} + + {%- endif %} + {%- if favicon %} + + {%- endif %} + {%- endif %} +{%- block linktags %} + {%- if hasdoc('about') %} + + {%- endif %} + {%- if hasdoc('genindex') %} + + {%- endif %} + {%- if hasdoc('search') %} + + {%- endif %} + {%- if hasdoc('copyright') %} + + {%- endif %} + + {%- if parents %} + + {%- endif %} + {%- if next %} + + {%- endif %} + {%- if prev %} + + {%- endif %} +{%- endblock %} +{%- block extrahead %} {% endblock %} + + +{%- block header %}{% endblock %} + +{%- block relbar1 %}{{ relbar() }}{% endblock %} + +{%- block content %} + {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %} + +
+ {%- block document %} +
+ {%- if render_sidebar %} +
+ {%- endif %} +
+ {% block body %} {% endblock %} +
+ {%- if render_sidebar %} +
+ {%- endif %} +
+ {%- endblock %} + + {%- block sidebar2 %}{{ sidebar() }}{% endblock %} +
+
+{%- endblock %} + +{%- block relbar2 %}{{ relbar() }}{% endblock %} + +{%- block footer %} + +

asdf asdf asdf asdf 22

+{%- endblock %} + + + diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/search.html b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/search.html new file mode 100644 index 00000000..e3aa9b5c --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/search.html @@ -0,0 +1,50 @@ +{# + basic/search.html + ~~~~~~~~~~~~~~~~~ + + Template for the search page. + + :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- extends "layout.html" %} +{% set title = _('Search') %} +{% set script_files = script_files + ['_static/searchtools.js'] %} +{% block footer %} + + {# this is used when loading the search index using $.ajax fails, + such as on Chrome for documents on localhost #} + + {{ super() }} +{% endblock %} +{% block body %} + + + {% if search_performed %} +

{{ _('Search Results') }}

+ {% if not search_results %} +

{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}

+ {% endif %} + {% endif %} +
+ {% if search_results %} +
    + {% for href, caption, context in search_results %} +
  • + {{ caption }} +

    {{ context|e }}

    +
  • + {% endfor %} +
+ {% endif %} +
+{% endblock %} diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/searchbox.html b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/searchbox.html new file mode 100644 index 00000000..24418d32 --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/searchbox.html @@ -0,0 +1,7 @@ +
+
+ + + +
+
diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/badge_only.css b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/badge_only.css new file mode 100644 index 00000000..4868a002 --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/badge_only.css @@ -0,0 +1 @@ +.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:"\f02d"}.icon-book:before{content:"\f02d"}.fa-caret-down:before{content:"\f0d7"}.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}} diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/theme - prior to centering.css b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/theme - prior to centering.css new file mode 100644 index 00000000..cbffe01a --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/theme - prior to centering.css @@ -0,0 +1,331 @@ +*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:20px 0;padding:0}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}} +.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1} +.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.0.3");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff?v=4.0.3") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.0.3") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.pull-left.icon{margin-right:.3em}.fa.pull-right,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before,.icon-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before,.icon-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:"\f057"}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before,.icon-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before,.icon-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:"\f0a8"}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before,.icon-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-asc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-desc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-reply-all:before{content:"\f122"}.fa-mail-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before,.icon-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .icon,.nav .fa,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .icon{display:inline}.btn .fa.fa-large,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a{color:#2980b9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:60px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:60px}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27ae60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27ae60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#e74c3c !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#e67e22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980b9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:0.5em 1em 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:0.5em}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#999;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{display:block;float:left;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{display:block;float:left;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{display:block;float:left;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:0.5em 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#ccc;font-size:70%;margin-top:0.3125em;font-style:italic}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#f3f6f6;color:#cad2d3}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e74c3c}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:0.8em;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fff;color:#cad2d3;border-color:transparent}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{padding:6px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #e74c3c}.wy-control-group.wy-control-group-error textarea{border:solid 1px #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px;margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980b9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27ae60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#e74c3c !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}code,.rst-content tt{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:"Incosolata","Consolata","Monaco",monospace;color:#e74c3c;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9b59b6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:"Incosolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:"Incosolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#eaf2f5} + .wy-breadcrumbs li{display:inline-block} + .wy-breadcrumbs li.wy-breadcrumbs-aside { + float:right; + padding-left:5px; + font-size:0.8em; + } + + .wy-breadcrumbs li a{ + display:inline-block;padding:5px + } + + .wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block} + + @media screen and (max-width: 480px) + {.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#2980b9;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3} + + .wy-menu-vertical li.current a{ + color:gray; + border-right:solid 1px #c9c9c9; + padding:0.4045em 2.427em + } + + .wy-menu-vertical li.current a:hover{background:#d6d6d6} + + .wy-menu-vertical li.on a { + color:#404040; + padding:0.4045em 1.618em; + font-weight:bold; + position:relative; + background:#fcfcfc; + border:none; + border-bottom:solid 1px #c9c9c9; + border-top:solid 1px #c9c9c9; + padding-left:1.618em -4px + } + + +.wy-menu-vertical li.current>a { + color:#404040; + padding:0.4045em 1.618em; + font-weight:bold; + position:relative; + background:#fcfcfc; + border:none; + border-bottom:solid 1px #c9c9c9; + border-top:solid 1px #c9c9c9; + padding-left:1.618em -4px + } + + .wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc} + .wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff} + + .wy-side-nav-search {z-index:200; + background-color:#2980b9; + text-align:center; + /* padding:0.809em; */ + /* padding-top: 0.809em;*/ + padding-right: 0.809em; + padding-bottom: 0.809em; + padding-left: 0.809em; + display:block; + color:#fcfcfc; + margin-bottom:0.809em + } + + .wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4} + + + .wy-side-nav-search img{ + display:block; + margin:auto auto 0.809em auto; + /*height:45px;*/ + /*width:45px;*/ + width:200px; + /*background-color:#2980b9;*/ + padding:5px; + /*border-radius:100%*/ + } + + .wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a { + color:#fcfcfc; + font-size:100%; + font-weight:bold; + display:inline-block; + padding-top: 4px; + padding-right: 6px; + /*padding-bottom: 4px;*/ + padding-left: 6px; + /* margin-bottom:0.809em */ + } + + .wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:absolute;top:0;left:0;width:300px;overflow:hidden;min-height:100%;background:#343131;z-index:200} + + .wy-nav-top{ + display:none; + background:#2980b9; + color:#fff; + padding:0.4045em 0.809em; + position:relative; + line-height:50px; + text-align:center; + font-size:100%; + *zoom:1} + + .wy-nav-top:before,.wy-nav-top:after{display:table;content:""} + + .wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold} + + .wy-nav-top img{ + margin-right:12px; + /*height:45px; */ + /*width:45px;*/ + width:200px; + background-color:#2980b9; + padding:5px; + /*border-radius:100%*/ + } + + .wy-nav-top i{font-size:30px;float:left;cursor:pointer}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%} + + .wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}nav.stickynav{position:absolute/* previously fixed hamishw */ ;top:0}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after{visibility:visible;content:"\f0c1";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt{color:#000}.rst-content tt big,.rst-content tt em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt{font-weight:bold}.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040} +/*! + * HamishW - some CSS for nav bar + */ + +.main-nav-bar { + display:block; + max-width: 1100px; + border-bottom: solid; + border-bottom-width: thin; + padding-bottom: 10px; + margin-bottom:20px; +} + + +#menu-options { + display: table; + background-color:#F8F8F8; + /*height: 87px;*/ + width: 100%; +} + +#menu-options li { + display: table-cell; + padding-left: 5px; + padding-right: 5px; + padding-top: 10px; + padding-bottom: 10px; + width: 5.0%; /*(100 / numItems)% */ + text-align: center; + font-weight:bold; + /*background: #ddd;*/ + white-space: nowrap; +}​ + + + +.navlink-long { + display:inline-block; + vertical-align: top; + padding:5px; +} + +.navlink-short { + display:none; + vertical-align: top; + padding:5px; +} + + +.footer-nav-bar { + display:block; + background-color:#F8F8F8; + max-width: 1100px; + /*border-bottom: solid;*/ + padding-bottom: 10px; + margin-top:15px; + border-top:solid; + /* border-top-width:thin; */ +} + +.footer-options { +/* display:block; +width:inherit; +font-size:0.8em; +font-weight:normal; +*/ +display:block; +text-align:justify; +font-size:0.8em; +width:inherit; +} + +.footer-navlink-long { + display: inline-block; + vertical-align: top; + padding:5px; +} + +.footer-navlink-short { + display:none; + padding:5px; +} + +.footer-options:after { + content: ""; + width: 100%; + line-height:1px; + line-spacing:1px; + display: inline-block; + } + +.copyright-box { + border-top:solid; + border-top-width:thin; + margin-top:10px; + background-color:#F8F8F8; + padding-bottom:5px; + +} + +.copyright-box p { + font-size:0.8em; +} + + +/* HamishW - some CSS for the breadcrumb (make elements inline blocks) */ + + +.breadcrumb-box { + margin-top:10px; + font-size:0.8em; + } + +.breadcrumb-box-item{ + display: inline-block; + } + +/* indent third level item +*/ + +/* set background to selected headings as dark */ + + +.wy-menu-vertical li.toctree-l2.current>a, +.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>a, +.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4.current>a +.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4.current li.toctree-l5.current>a + { +background: #c9c9c9; +} + + +/* .wy-menu-vertical li.current>a .wy-menu-vertical .current>a */ +/* +.wy-menu-vertical li.current>a { +background: purple; +} +*/ + +.wy-menu-vertical li.toctree-l2 li.toctree-l3>a { +display:none; +} + +.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a { +display:block; +font-size:0.8em; +/*padding-top: 0.4045em;*/ +padding-right: 2.427em; +padding-bottom: 0.4045em; +padding-left: 4.25em; +width:100%; +} + + + + +.wy-menu-vertical li.toctree-l2 li.toctree-l3 li.toctree-l4>a { +display:none; +} + +.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4>a { +/* +background: #F0EEEE; +background: purple; */ +display:block; +font-size:0.8em; +/*padding-top: 0.4045em;*/ +padding-right: 2.427em; +padding-bottom: 0.4045em; +padding-left: 5.0em; +width:100%; +} + + { +background: #c9c9c9; +} + +.wy-menu-vertical a[href^="#"] { +background:#F0EEEE; +} + + +@media only screen +and (max-width : 480px) { +/* Styles */ + .navlink-long, .footer-navlink-long { + display:none; + } + + .navlink-short, .footer-navlink-short { + display:inline-block; + vertical-align: top;} + +} + +@media screen and (min-width: 480px) and (max-width: 768px) { + .navlink-long, .footer-navlink-long { + display:inline-block; + } + .navlink-short, .footer-navlink-short { + display:none; + } +} + diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/theme.css b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/theme.css new file mode 100644 index 00000000..90d729cd --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/css/theme.css @@ -0,0 +1,512 @@ +*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%} + +body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:20px 0;padding:0}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical} +table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline} +.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""} +pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}} +.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1} +.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.0.3");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff?v=4.0.3") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.0.3") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.pull-left.icon{margin-right:.3em}.fa.pull-right,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before,.icon-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before,.icon-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:"\f057"}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before,.icon-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before,.icon-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:"\f0a8"}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before,.icon-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-asc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-desc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-reply-all:before{content:"\f122"}.fa-mail-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before,.icon-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .icon,.nav .fa,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .icon{display:inline}.btn .fa.fa-large,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a{color:#2980b9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:60px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:60px}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27ae60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27ae60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#e74c3c !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#e67e22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980b9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:0.5em 1em 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:0.5em}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#999;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{display:block;float:left;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{display:block;float:left;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{display:block;float:left;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:0.5em 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#ccc;font-size:70%;margin-top:0.3125em;font-style:italic}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#f3f6f6;color:#cad2d3}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e74c3c}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:0.8em;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fff;color:#cad2d3;border-color:transparent}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{padding:6px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #e74c3c}.wy-control-group.wy-control-group-error textarea{border:solid 1px #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}} + + @media screen and (max-width: 768px){.tablet-hide{display:none}} + @media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px;margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0} + + .wy-table-responsive{ + margin-bottom:24px; + max-width:100%; + overflow:auto + } + +.wy-table-responsive table{ + margin-bottom:0 !important + } + +.wy-table-responsive table td {white-space:nowrap} + +.wy-table-responsive table th{white-space:nowrap} + +a{color:#2980b9;text-decoration:none}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%;overflow-x:hidden} + +body{ + /* font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif; */ + font-family:"proxima-nova","Helvetica Neue",Arial,sans-serif; + font-weight:normal; + color:#404040; + min-height:100%; + overflow-x:hidden; + background:#edf0f2 + } + +.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980b9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27ae60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#e74c3c !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%} + +code, .rst-content tt + { + white-space:nowrap; + max-width:100%; + background:#fff; + border:solid 1px #e1e4e5; + /* font-size:75%; */ + font-size:90%; + padding:0 5px; + font-family:"Inconsolata","Consolata","Monaco",monospace;color:#e74c3c; + overflow-x:auto + } + +code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px} +.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li { + list-style:disc;margin-left:24px} + +.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px} + +/* .wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li { */ +.wy-plain-list-decimal ol li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li { + list-style:decimal;margin-left:24px + } + +/* HamishW - add to fix unordered bullets inside decimal lists */ +.rst-content ol.arabic ul li { + list-style:disc; + } + + + +.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9b59b6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:"Inconsolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:"Inconsolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#eaf2f5} + .wy-breadcrumbs li{display:inline-block} + .wy-breadcrumbs li.wy-breadcrumbs-aside { + float:right; + padding-left:5px; + font-size:0.8em; + } + + .wy-breadcrumbs li a{ + display:inline-block;padding:5px + } + + .wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block} + + @media screen and (max-width: 480px) + {.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#2980b9;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3} + + .wy-menu-vertical li.current a{ + color:gray; + border-right:solid 1px #c9c9c9; + padding:0.4045em 2.427em + } + + .wy-menu-vertical li.current a:hover{background:#d6d6d6} + + .wy-menu-vertical li.on a { + color:#404040; + padding:0.4045em 1.618em; + font-weight:bold; + position:relative; + background:#fcfcfc; + border:none; + border-bottom:solid 1px #c9c9c9; + border-top:solid 1px #c9c9c9; + padding-left:1.618em -4px + } + + +.wy-menu-vertical li.current>a { + color:#404040; + padding:0.4045em 1.618em; + font-weight:bold; + position:relative; + background:#fcfcfc; + border:none; + border-bottom:solid 1px #c9c9c9; + border-top:solid 1px #c9c9c9; + padding-left:1.618em -4px + } + + .wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc} + .wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff} + + .wy-side-nav-search {z-index:200; + background-color:#2980b9; + text-align:center; + /* padding:0.809em; */ + /* padding-top: 0.809em;*/ + padding-right: 0.809em; + padding-bottom: 0.809em; + padding-left: 0.809em; + display:block; + color:#fcfcfc; + margin-bottom:0.809em + } + + .wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4} + + + .wy-side-nav-search img{ + display:block; + margin:auto auto 0.809em auto; + /*height:45px;*/ + /*width:45px;*/ + width:200px; + /*background-color:#2980b9;*/ + padding:5px; + /*border-radius:100%*/ + } + + .wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a { + color:#fcfcfc; + font-size:100%; + font-weight:bold; + display:inline-block; + padding-top: 4px; + padding-right: 6px; + /*padding-bottom: 4px;*/ + padding-left: 6px; + /* margin-bottom:0.809em */ + } + + .wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0} + + .wy-body-for-nav{ + /* background:left repeat-y #F1F0F0; */ + /* + background:left repeat-y rgb(97, 6, 6); + background-size:300px 1px + */ /* This is deep red colour - removed */ + /* background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC); */ + + } + + .wy-grid-for-nav { + /* position:absolute; */ + position:relative; /* Make left column full length */ + width:100%; + height:100% + } + + .wy-nav-side{position:absolute;top:0;left:0;width:300px;overflow:hidden;min-height:100%;background:#343131;z-index:200} + + .wy-nav-top{ + display:none; + background:#2980b9; + color:#fff; + padding:0.4045em 0.809em; + position:relative; + line-height:50px; + text-align:center; + font-size:100%; + *zoom:1} + + .wy-nav-top:before,.wy-nav-top:after{display:table;content:""} + + .wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold} + + .wy-nav-top img{ + margin-right:12px; + /*height:45px; */ + /*width:45px;*/ + width:200px; + background-color:#2980b9; + padding:5px; + /*border-radius:100%*/ + } + + .wy-nav-top i{font-size:30px;float:left;cursor:pointer} + .wy-nav-content-wrap{ + margin-left:300px; + /* background:#fcfcfc; */ + min-height:100% + } + + .wy-nav-content{ + /* padding:1.618em 3.236em; */ + /* padding-top: 1.618em; */ + padding-right: 3.236em; + padding-bottom: 1.618em; + padding-left: 3.236em; + height:100%; + min-height: 100vh; /* ensure is always full height of browser window */ + max-width:800px; + /* margin:auto; */ + margin-left:0px; + background: #fcfcfc; + } + + .wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%} + + @media screen and (max-width: 768px){ + .wy-body-for-nav{background:#fcfcfc} + .wy-nav-top{display:block}.wy-nav-side{left:-300px} + .wy-nav-side.shift{width:85%;left:0} + .wy-nav-content-wrap{margin-left:0} + .wy-nav-content-wrap .wy-nav-content { + /* padding:1.618em */ + /* padding-top: 1.618em; */ + padding-right: 1.618em; + padding-bottom: 1.618em; + padding-left: 1.618em; + } + .wy-nav-content-wrap.shift{ + position:relative; /* position:fixed; */ + min-width:100%; + left:85%; + top:0;height:100%; + overflow:hidden + } + } + + @media screen and (min-width: 1400px) { + /* .wy-nav-content-wrap{background:rgba(0,0,0,0.05)} */ + .wy-nav-content{ + /* margin:0; */ + background:#fcfcfc} + } + + @media print{.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}nav.stickynav{position:absolute/* previously fixed hamishw */ ;top:0}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center} + + @media screen and (max-width: 768px){ + .rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after{visibility:visible;content:"\f0c1";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt{color:#000}.rst-content tt big,.rst-content tt em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt{font-weight:bold}.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right} + + @media screen and (max-width: 480px){ + .rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040} +/*! + * HamishW - some CSS for nav bar + */ + +.main-nav-bar { + display:block; + max-width: 1100px; + border-bottom: solid; + border-bottom-width: thin; + padding-bottom: 10px; + margin-bottom:20px; + padding-top: 10px; +} + + +#menu-options { + display: table; + /* background-color:#F8F8F8; */ + /*height: 87px;*/ + + width: 100%; +} + +#menu-options li { + display: table-cell; + /* padding-left: 5px; + padding-right: 5px; */ + padding-top: 10px; + padding-bottom: 10px; + width: 5.0%; /*(100 / numItems)% */ + text-align: center; + font-weight:bold; + /*background: #ddd;*/ + white-space: nowrap; +}​ + + + +.navlink-long { + display:inline-block; + vertical-align: top; + padding:5px; +} + +.navlink-short { + display:none; + vertical-align: top; + padding:5px; +} + + +.footer-nav-bar { + display:block; + background-color:#F8F8F8; + max-width: 1100px; + /*border-bottom: solid;*/ + padding-bottom: 10px; + margin-top:15px; + border-top:solid; + /* border-top-width:thin; */ +} + +.footer-options { +/* display:block; +width:inherit; +font-size:0.8em; +font-weight:normal; +*/ +display:block; +text-align:justify; +font-size:0.8em; +width:inherit; +} + +.footer-navlink-long { + display: inline-block; + vertical-align: top; + padding:5px; +} + +.footer-navlink-short { + display:none; + padding:5px; +} + +.footer-options:after { + content: ""; + width: 100%; + line-height:1px; + line-spacing:1px; + display: inline-block; + } + +.copyright-box { + border-top:solid; + border-top-width:thin; + margin-top:10px; + background-color:#F8F8F8; + padding-bottom:5px; + +} + +.copyright-box p { + font-size:0.8em; +} + + + +/* HamishW - Attempt to wrap table columns/remove the "responsive" behaviour on some tables. */ +table.wrap-table-content td, table.wrap-table-content th { + white-space: normal; +} + + + +/* HamishW - add clear markup for external links */ + +a.external:after { + content: ""; + display: inline-block; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAV0lEQVR4Xq2QwQ2AAAwC3cmd2Kk7sRP64CEJ9qOX8OPatMc/QKppnEPhTmJh23CLiwAqIw21CybKQ28qQi37WGFYBJcwfJQpP8LlEHKyZMF0IdmF13zlAjZ/6H4wb+mUAAAAAElFTkSuQmCC); + background-repeat: no-repeat; + background-position: right top; + background-origin: border-box; + width: 12px; + height: 16px; +} + + +/* HamishW - some CSS for the breadcrumb (make elements inline blocks) */ + + +.breadcrumb-box { + margin-top:10px; + font-size:0.8em; + } + +.breadcrumb-box-item{ + display: inline-block; + } + +/* indent third level item +*/ + +/* set background to selected headings as dark */ + + +.wy-menu-vertical li.toctree-l2.current>a, +.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>a, +.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4.current>a +.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4.current li.toctree-l5.current>a + { +background: #c9c9c9; +} + + +/* .wy-menu-vertical li.current>a .wy-menu-vertical .current>a */ +/* +.wy-menu-vertical li.current>a { +background: purple; +} +*/ + +.wy-menu-vertical li.toctree-l2 li.toctree-l3>a { +display:none; +} + +.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a { +display:block; +font-size:0.8em; +/*padding-top: 0.4045em;*/ +padding-right: 2.427em; +padding-bottom: 0.4045em; +padding-left: 4.25em; +width:100%; +} + + + + +.wy-menu-vertical li.toctree-l2 li.toctree-l3 li.toctree-l4>a { +display:none; +} + +.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current li.toctree-l4>a { +/* +background: #F0EEEE; +background: purple; */ +display:block; +font-size:0.8em; +/*padding-top: 0.4045em;*/ +padding-right: 2.427em; +padding-bottom: 0.4045em; +padding-left: 5.0em; +width:100%; +} + + { +background: #c9c9c9; +} + +.wy-menu-vertical a[href^="#"] { +background:#F0EEEE; +} + +.grid-to-center-rtd-theme { + margin-left:0; + margin-right:auto; + max-width: 1100px; + } + +@media only screen and (min-width : 1100px){ + +.grid-to-center-rtd-theme { + margin-left:auto; + /* max-width: 1100px; */ + } + +} + +@media only screen +and (max-width : 480px) { +/* Styles */ + .navlink-long, .footer-navlink-long { + display:none; + } + + .navlink-short, .footer-navlink-short { + display:inline-block; + vertical-align: top;} + +} + +@media screen and (min-width: 480px) and (max-width: 768px) { + .navlink-long, .footer-navlink-long { + display:inline-block; + } + .navlink-short, .footer-navlink-short { + display:none; + } +} + diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot new file mode 100644 index 00000000..7c79c6a6 Binary files /dev/null and b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot differ diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg new file mode 100644 index 00000000..45fdf338 --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf new file mode 100644 index 00000000..e89738de Binary files /dev/null and b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf differ diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff new file mode 100644 index 00000000..8c1748aa Binary files /dev/null and b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff differ diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/js/theme.js b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/js/theme.js new file mode 100644 index 00000000..60520cc3 --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/static/js/theme.js @@ -0,0 +1,47 @@ +$( document ).ready(function() { + // Shift nav in mobile when clicking the menu. + $(document).on('click', "[data-toggle='wy-nav-top']", function() { + $("[data-toggle='wy-nav-shift']").toggleClass("shift"); + $("[data-toggle='rst-versions']").toggleClass("shift"); + }); + // Close menu when you click a link. + $(document).on('click', ".wy-menu-vertical .current ul li a", function() { + $("[data-toggle='wy-nav-shift']").removeClass("shift"); + $("[data-toggle='rst-versions']").toggleClass("shift"); + }); + $(document).on('click', "[data-toggle='rst-current-version']", function() { + $("[data-toggle='rst-versions']").toggleClass("shift-up"); + }); + // Make tables responsive + $("table.docutils:not(.field-list)").wrap("
"); +}); + +window.SphinxRtdTheme = (function (jquery) { + var stickyNav = (function () { + var navBar, + win, + stickyNavCssClass = 'stickynav', + applyStickNav = function () { + if (navBar.height() <= win.height()) { + navBar.addClass(stickyNavCssClass); + } else { + navBar.removeClass(stickyNavCssClass); + } + }, + enable = function () { + applyStickNav(); + win.on('resize', applyStickNav); + }, + init = function () { + navBar = jquery('nav.wy-nav-side:first'); + win = jquery(window); + }; + jquery(init); + return { + enable : enable + }; + }()); + return { + StickyNav : stickyNav + }; +}($)); diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/theme.conf b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/theme.conf new file mode 100644 index 00000000..a72f4546 --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/theme.conf @@ -0,0 +1,9 @@ +[theme] +inherit = basic +stylesheet = css/theme.css + +[options] +typekit_id = hiw1hhg +analytics_id = +sticky_navigation = True + diff --git a/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/versions.html b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/versions.html new file mode 100644 index 00000000..8b3eb79d --- /dev/null +++ b/misoclib/doc/source/_themes/enjoydigital_sphinx_rtd_theme/versions.html @@ -0,0 +1,37 @@ +{% if READTHEDOCS %} +{# Add rst-badge after rst-versions for small badge style. #} +
+ + Read the Docs + v: {{ current_version }} + + +
+
+
Versions
+ {% for slug, url in versions %} +
{{ slug }}
+ {% endfor %} +
+
+
Downloads
+ {% for type, url in downloads %} +
{{ type }}
+ {% endfor %} +
+
+
On Read the Docs
+
+ Project Home +
+
+ Builds +
+
+
+ Free document hosting provided by Read the Docs. + +
+
+{% endif %} + diff --git a/misoclib/doc/source/conf.py b/misoclib/doc/source/conf.py new file mode 100644 index 00000000..b1d4d3ce --- /dev/null +++ b/misoclib/doc/source/conf.py @@ -0,0 +1,402 @@ +# -*- coding: utf-8 -*- +# +# LiteEth documentation build configuration file, created by +# sphinx-quickstart on Tue Jul 01 09:20:29 2014. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + + +# At the top. #HamishW https://pypi.python.org/pypi/sphinx-bootstrap-theme/ ... +#import sphinx_bootstrap_theme + + +# At the top. #HamishW http://sphinx-better-theme.readthedocs.org/en/latest/installation.html easy_install sphinx_better_theme +#from better import better_theme_path +#html_theme_path = [better_theme_path] + + +# At the top. #HamishW https://github.com/snide/sphinx_rtd_theme easy_install sphinx_rtd_theme +#import sphinx_rtd_theme +#html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + +html_theme_path = ['_themes',] + + + + + + + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.ifconfig', + 'sphinx.ext.viewcode', + # 'breathe', #added by HamishW +] + + +#Build "Todo" notes into the source +#todo_include_todos = 'True' + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'LiteEth' +copyright = u'2015, ' + # Note, theme was modified to allow this to display (fix breaks the translation code, which was breaking the above link and rendering it as text). + # Also so I could link to specific copyright page. + + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# + +liteeth_version = open(os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'liteeth-version.txt'))).read().strip() + +# The short X.Y version. +version = liteeth_version[:liteeth_version.rindex('.')] +# The full version, including alpha/beta/rc tags. +release = liteeth_version + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +#html_theme = 'default' +#html_theme = 'sphinxdoc' +#html_theme = 'agogo' #like this +#html_theme = 'scrolls' +#html_theme = 'bootstrap' +#html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() +#html_theme = 'better' +#html_theme = 'pyramid' +#html_theme = 'nature' +#html_theme = 'haiku' +#html_theme = "sphinx_rtd_theme" +html_theme = "enjoydigital_sphinx_rtd_theme" + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = { +# "rightsidebar": "true", +# "relbarbgcolor": "black" +#} + + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = '_static/LiteEth_logo_full.png' + + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = 'liteeth.ico' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'LiteEthdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'LiteEth.tex', u'LiteEth Documentation', + u'Kermarrec Florent', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'liteeth', u'LiteEth Documentation', + [u'Florent Kermarrec'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'LiteEth', u'LiteEth Documentation', + u'Florent Kermarrec', 'LiteEth', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = u'LiteEth' +epub_author = u'Florent Kermarrec' +epub_publisher = u'Florent Kermarrec' +epub_copyright = u'2015, LiteEth Contributors' + +# The basename for the epub file. It defaults to the project name. +#epub_basename = u'LiteEth' + +# The HTML theme for the epub output. Since the default themes are not optimized +# for small screen space, using the same theme for HTML and epub output is +# usually not wise. This defaults to 'epub', a theme designed to save visual +# space. +#epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or en if the language is not set. +#epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# A unique identification for the text. +#epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +#epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +#epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# The depth of the table of contents in toc.ncx. +#epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +#epub_tocscope = 'default' + +# Fix unsupported image types using the PIL. +#epub_fix_images = False + +# Scale large images. +#epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#epub_show_urls = 'inline' + +# If false, no index is generated. +#epub_use_index = True + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'http://docs.python.org/': None} + +# HamishW - set highlighting language. +highlight_language = 'cpp' + +# HamishW - set domain (cpp) +primary_domain = 'cpp' + +# HamishW - tell Breathe about projects. Breathe is tool to convert Doxygen to Python objects, for import into Sphinx. +#breathe_projects = { "myproject": "/home/me/docproj/doxyxml/", "nutshell":"./headers/xml/", } +# HamishW - Specify a default project: +#breathe_default_project = "nutshell" + diff --git a/misoclib/doc/source/docs/core/index.rst b/misoclib/doc/source/docs/core/index.rst new file mode 100644 index 00000000..33ae1eab --- /dev/null +++ b/misoclib/doc/source/docs/core/index.rst @@ -0,0 +1,7 @@ +.. _core-index: + +======================== +Core +======================== +.. note:: + Please contribute to this document, or support us financially to write it. \ No newline at end of file diff --git a/misoclib/doc/source/docs/frontend/index.rst b/misoclib/doc/source/docs/frontend/index.rst new file mode 100644 index 00000000..d112402e --- /dev/null +++ b/misoclib/doc/source/docs/frontend/index.rst @@ -0,0 +1,7 @@ +.. _frontend-index: + +======================== +Frontend +======================== +.. note:: + Please contribute to this document, or support us financially to write it. diff --git a/misoclib/doc/source/docs/getting_started/FAQ.rst b/misoclib/doc/source/docs/getting_started/FAQ.rst new file mode 100644 index 00000000..87c3401f --- /dev/null +++ b/misoclib/doc/source/docs/getting_started/FAQ.rst @@ -0,0 +1,7 @@ +.. _FAQ: + +=== +FAQ +=== +.. note:: + Please contribute to this document. diff --git a/misoclib/doc/source/docs/getting_started/bug_reports.rst b/misoclib/doc/source/docs/getting_started/bug_reports.rst new file mode 100644 index 00000000..5ab902f5 --- /dev/null +++ b/misoclib/doc/source/docs/getting_started/bug_reports.rst @@ -0,0 +1,11 @@ +.. _bug-reports: + +============= +Bug Reporting +============= +- send us feedback and suggestions for improvements +- send us bug reports when something goes wrong +- send us the modifications and improvements you have done to LiteEth. +The use of "git format-patch" is recommended. If your submission is large and +complex and/or you are not sure how to proceed, feel free to discuss with us +about it. \ No newline at end of file diff --git a/misoclib/doc/source/docs/getting_started/downloads.rst b/misoclib/doc/source/docs/getting_started/downloads.rst new file mode 100644 index 00000000..d4368a15 --- /dev/null +++ b/misoclib/doc/source/docs/getting_started/downloads.rst @@ -0,0 +1,43 @@ +.. _sdk-download-and-install: + +==================== +Download and install +==================== +1. Install Python3 and your vendor's software + +2. Obtain Migen and install it: + - git clone https://github.com/m-labs/migen + - cd migen + - python3 setup.py install + - cd .. + +.. note:: + In case you have issues with Migen, please retry with our forks at: + https://github.com/enjoy-digital/migen + until new features are merged. + +3. Obtain LiteScope and install it: + - git clone https://github.com/enjoy-digital/litescope + - cd litescope + - python3 setup.py install + - cd .. + +4. Obtain LiteEth + - git clone https://github.com/enjoy-digital/liteeth + +5. Build and load UDP loopback design (only for KC705 for now): + - python3 make.py -t udp all + +6. Test design (only for KC705 for now): + - try to ping 192.168.1.40 + - go to ./test directory: + - change com port in config.py to your com port + - run make test_udp + +7. Build and load Etherbone design (only for KC705 for now): + - python3 make.py -t etherbone all + +8. Test design (only for KC705 for now): + - try to ping 192.168.1.40 + - go to ./test directory run: + - run make test_etherbone \ No newline at end of file diff --git a/misoclib/doc/source/docs/getting_started/index.rst b/misoclib/doc/source/docs/getting_started/index.rst new file mode 100644 index 00000000..92b32a36 --- /dev/null +++ b/misoclib/doc/source/docs/getting_started/index.rst @@ -0,0 +1,17 @@ +.. _getting-started-index: + +=============== +Getting Started +=============== + +Now you know why LiteEth is :ref:`core for you `, it's time to *get started*. + +This section provides a walk-through of :ref:`downloading and installing the tools`. + +.. toctree:: + :maxdepth: 1 + + downloads + FAQ + bug_reports + diff --git a/misoclib/doc/source/docs/index.rst b/misoclib/doc/source/docs/index.rst new file mode 100644 index 00000000..22ec86c6 --- /dev/null +++ b/misoclib/doc/source/docs/index.rst @@ -0,0 +1,27 @@ +.. _documentation-home: + +======================== +LiteEth Documentation +======================== + +This comprehensive documentation set contains everything you need to know to use LiteEth and integrate it in your design. + +**Getting started:** + +- :ref:`intro-index` explains what LiteEth does, why it is needed, its limitations and its licensing. It will help you understand whether LiteEth is the right core for you. +- :ref:`getting-started-index` walks you through downloading, installing and using the LiteEth core. + +**LiteEth Internals:** + +- :ref:`core-index` describes core building blocks. +- :ref:`frontend-index` describes core building blocks. + +The full hierarchy of articles, opened to the second level, is shown below. + +.. toctree:: + :maxdepth: 2 + + intro/index + getting_started/index + core/index + frontend/index diff --git a/misoclib/doc/source/docs/intro/about.rst b/misoclib/doc/source/docs/intro/about.rst new file mode 100644 index 00000000..4d02b3dc --- /dev/null +++ b/misoclib/doc/source/docs/intro/about.rst @@ -0,0 +1,59 @@ +.. _about: + +================ +About LiteEth +================ + +LiteEth provides a small footprint and configurable Ethernet core. + +LiteEth is part of LiteX libraries whose aims are to lower entry level of +complex FPGA cores by providing simple, elegant and efficient implementations +ofcomponents used in today's SoC such as Ethernet, SATA, PCIe, SDRAM Controller... + +The core uses simple and specific streaming buses and will provides in the future +adapters to use standardized AXI or Avalon-ST streaming buses. + +Since Python is used to describe the HDL, the core is highly and easily +configurable. + +LiteEth uses technologies developed in partnership with M-Labs Ltd: + - Migen enables generating HDL with Python in an efficient way. + - MiSoC provides the basic blocks to build a powerful and small footprint SoC. + +LiteEth can be used as a Migen/MiSoC library (by simply installing it +with the provided setup.py) or can be integrated with your standard design flow +by generating the verilog rtl that you will use as a standard core. + +.. _about-toolchain: + +Features +======== +- Ethernet MAC with various interfaces and various PHYs (GMII, MII, Loopback) +- Hardware UDP/IP stack with ARP and ICMP + +Possibles improvements +====================== +- add standardized interfaces (AXI, Avalon-ST) +- add DMA interface to MAC +- add hardware Etherbone support +- add RGMII/SGMII PHYs +- ... See below Support and Consulting :) + +Support and Consulting +====================== +We love open-source hardware and like sharing our designs with others. + +LiteEth is developed and maintained by EnjoyDigital. + +If you would like to know more about LiteEth or if you are already a happy user +and would like to extend it for your needs, EnjoyDigital can provide standard +commercial support as well as consulting services. + +So feel free to contact us, we'd love to work with you! (and eventually shorten +the list of the possible improvements :) + +Contact +======= +E-mail: florent [AT] enjoy-digital.fr + + diff --git a/misoclib/doc/source/docs/intro/community.rst b/misoclib/doc/source/docs/intro/community.rst new file mode 100644 index 00000000..6d1ef429 --- /dev/null +++ b/misoclib/doc/source/docs/intro/community.rst @@ -0,0 +1,5 @@ +.. _community: + +========= +Community +========= diff --git a/misoclib/doc/source/docs/intro/index.rst b/misoclib/doc/source/docs/intro/index.rst new file mode 100644 index 00000000..9b013af1 --- /dev/null +++ b/misoclib/doc/source/docs/intro/index.rst @@ -0,0 +1,15 @@ +.. _intro-index: + +====================== +Introducing LiteEth +====================== + +This section explains what LiteEth does, why it is needed, its limitations and its licensing. After reading, you will understand whether LiteEth is the right core for you, and where to go if you have :ref:`further questions `. + +.. toctree:: + :maxdepth: 1 + + about + license + release_notes + talks_and_publications diff --git a/misoclib/doc/source/docs/intro/license.rst b/misoclib/doc/source/docs/intro/license.rst new file mode 100644 index 00000000..7bedbf93 --- /dev/null +++ b/misoclib/doc/source/docs/intro/license.rst @@ -0,0 +1,47 @@ +.. _license: + +=================== +Open Source License +=================== + +LiteEth is released under the very permissive two-clause BSD license. Under the +terms of this license, you are authorized to use LiteEth for closed-source +proprietary designs. +Even though we do not require you to do so, those things are awesome, so please +do them if possible: + - tell us that you are using LiteEth + - cite LiteEth in publications related to research it has helped + - send us feedback and suggestions for improvements + - send us bug reports when something goes wrong + - send us the modifications and improvements you have done to LiteEth. + +:: + + Unless otherwise noted, LiteEth is copyright (C) 2015 Florent Kermarrec. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Other authors retain ownership of their contributions. If a submission can + reasonably be considered independently copyrightable, it's yours and we + encourage you to claim it with appropriate copyright notices. This submission + then falls under the "otherwise noted" category. All submissions are strongly + encouraged to use the two-clause BSD license reproduced above. diff --git a/misoclib/doc/source/docs/intro/release_notes.rst b/misoclib/doc/source/docs/intro/release_notes.rst new file mode 100644 index 00000000..9cf90a83 --- /dev/null +++ b/misoclib/doc/source/docs/intro/release_notes.rst @@ -0,0 +1,13 @@ +.. _release-notes: + +============= +Release Notes +============= + +.. _ChangeLog: + +ChangeLog +========= +0.9.0: + - First 0.9.0 release supporting hardware UDP/IP stack with ICMP and ARP. + diff --git a/misoclib/doc/source/docs/intro/talks_and_publications.rst b/misoclib/doc/source/docs/intro/talks_and_publications.rst new file mode 100644 index 00000000..071a4e6a --- /dev/null +++ b/misoclib/doc/source/docs/intro/talks_and_publications.rst @@ -0,0 +1,14 @@ +.. _talks-and-publications: + +====================== +Talks and Publications +====================== + +- Migen / MiSoC documentation: + - `User guide `_ (`m-labs `_) + - `Tutorial: An introduction to Migen `_ (`m-labs `_) + +- Migen / MiSoC presentations: + - `Lecture slides `_ (`sbourdeauducq `_) + - `EHSM 2012 presentation `_ (`sbourdeauducq `_) + - `ORCONF2014 `_ (`fallen `_) diff --git a/misoclib/doc/source/home_page_layout.html b/misoclib/doc/source/home_page_layout.html new file mode 100644 index 00000000..6c8e98c1 --- /dev/null +++ b/misoclib/doc/source/home_page_layout.html @@ -0,0 +1,26 @@ +./_static/LiteEth_logo_full.png + +

A small footprint and configurable Ethernet core with UDP/IP hw stack and Etherbone frontend.

+ +
+ + + + +
+ diff --git a/misoclib/doc/source/index.rst b/misoclib/doc/source/index.rst new file mode 100644 index 00000000..78dd8240 --- /dev/null +++ b/misoclib/doc/source/index.rst @@ -0,0 +1,24 @@ +.. title:: Main + +.. _home-page: + +.. raw:: html + :file: home_page_layout.html + + +----- + +News +==== + +- First 0.9.0 release supporting hardware UDP/IP stack with ICMP and ARP. + +.. toctree:: + :hidden: + + docs/intro/index + docs/getting_started/index + docs/core/index + docs/frontend/index + docs/site/about + diff --git a/misoclib/liteeth-version.txt b/misoclib/liteeth-version.txt new file mode 100644 index 00000000..eba33402 --- /dev/null +++ b/misoclib/liteeth-version.txt @@ -0,0 +1,2 @@ +0.9.0 + diff --git a/misoclib/liteeth/__init__.py b/misoclib/liteeth/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/misoclib/liteeth/common.py b/misoclib/liteeth/common.py new file mode 100644 index 00000000..3d00ad2f --- /dev/null +++ b/misoclib/liteeth/common.py @@ -0,0 +1,284 @@ +import math +from collections import OrderedDict + +from migen.fhdl.std import * +from migen.fhdl.decorators import ModuleDecorator +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.genlib.record import * +from migen.genlib.fsm import FSM, NextState +from migen.genlib.misc import chooser +from migen.flow.actor import * +from migen.flow.plumbing import Buffer +from migen.actorlib.structuring import Converter, Pipeline +from migen.actorlib.fifo import SyncFIFO, AsyncFIFO +from migen.bank.description import * + +eth_mtu = 1532 +eth_min_len = 46 +eth_interpacket_gap = 12 +eth_preamble = 0xD555555555555555 +buffer_depth = 2**log2_int(eth_mtu, need_pow2=False) + +class HField(): + def __init__(self, byte, offset, width): + self.byte = byte + self.offset = offset + self.width = width + +ethernet_type_ip = 0x800 +ethernet_type_arp = 0x806 + +mac_header_len = 14 +mac_header = { + "target_mac": HField(0, 0, 48), + "sender_mac": HField(6, 0, 48), + "ethernet_type": HField(12, 0, 16) +} + +arp_hwtype_ethernet = 0x0001 +arp_proto_ip = 0x0800 +arp_opcode_request = 0x0001 +arp_opcode_reply = 0x0002 + +arp_header_len = 28 +arp_header = { + "hwtype": HField( 0, 0, 16), + "proto": HField( 2, 0, 16), + "hwsize": HField( 4, 0, 8), + "protosize": HField( 5, 0, 8), + "opcode": HField( 6, 0, 16), + "sender_mac": HField( 8, 0, 48), + "sender_ip": HField(14, 0, 32), + "target_mac": HField(18, 0, 48), + "target_ip": HField(24, 0, 32) +} + +ipv4_header_len = 20 +ipv4_header = { + "ihl": HField(0, 0, 4), + "version": HField(0, 4, 4), + "total_length": HField(2, 0, 16), + "identification": HField(4, 0, 16), + "ttl": HField(8, 0, 8), + "protocol": HField(9, 0, 8), + "checksum": HField(10, 0, 16), + "sender_ip": HField(12, 0, 32), + "target_ip": HField(16, 0, 32) +} + +icmp_header_len = 8 +icmp_header = { + "msgtype": HField( 0, 0, 8), + "code": HField( 1, 0, 8), + "checksum": HField( 2, 0, 16), + "quench": HField( 4, 0, 32) +} +icmp_protocol = 0x01 + +udp_header_len = 8 +udp_header = { + "src_port": HField( 0, 0, 16), + "dst_port": HField( 2, 0, 16), + "length": HField( 4, 0, 16), + "checksum": HField( 6, 0, 16) +} + +udp_protocol = 0x11 + +etherbone_magic = 0x4e6f +etherbone_version = 1 +etherbone_packet_header_len = 8 +etherbone_packet_header = { + "magic": HField( 0, 0, 16), + + "version": HField( 2, 4, 4), + "nr": HField( 2, 2, 1), + "pr": HField( 2, 1, 1), + "pf": HField( 2, 0, 1), + + "addr_size": HField( 3, 4, 4), + "port_size": HField( 3, 0, 4) +} + +etherbone_record_header_len = 4 +etherbone_record_header = { + "bca": HField( 0, 0, 1), + "rca": HField( 0, 1, 1), + "rff": HField( 0, 2, 1), + "cyc": HField( 0, 4, 1), + "wca": HField( 0, 5, 1), + "wff": HField( 0, 6, 1), + + "byte_enable": HField( 1, 0, 8), + + "wcount": HField( 2, 0, 8), + + "rcount": HField( 3, 0, 8) +} + +def reverse_bytes(v): + n = math.ceil(flen(v)/8) + r = [] + for i in reversed(range(n)): + r.append(v[i*8:min((i+1)*8, flen(v))]) + return Cat(iter(r)) + +# layouts +def _layout_from_header(header): + _layout = [] + for k, v in sorted(header.items()): + _layout.append((k, v.width)) + return _layout + +def _remove_from_layout(layout, *args): + r = [] + for f in layout: + remove = False + for arg in args: + if f[0] == arg: + remove = True + if not remove: + r.append(f) + return r +def eth_phy_description(dw): + payload_layout = [ + ("data", dw), + ("last_be", dw//8), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, packetized=True) + +def eth_mac_description(dw): + payload_layout = _layout_from_header(mac_header) + [ + ("data", dw), + ("last_be", dw//8), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, packetized=True) + +def eth_arp_description(dw): + param_layout = _layout_from_header(arp_header) + payload_layout = [ + ("data", dw), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, param_layout, packetized=True) + +arp_table_request_layout = [ + ("ip_address", 32) +] + +arp_table_response_layout = [ + ("failed", 1), + ("mac_address", 48) +] + +def eth_ipv4_description(dw): + param_layout = _layout_from_header(ipv4_header) + payload_layout = [ + ("data", dw), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, param_layout, packetized=True) + +def eth_ipv4_user_description(dw): + param_layout = [ + ("length", 16), + ("protocol", 8), + ("ip_address", 32) + ] + payload_layout = [ + ("data", dw), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, param_layout, packetized=True) + +def convert_ip(s): + ip = 0 + for e in s.split("."): + ip = ip << 8 + ip += int(e) + return ip + +def eth_icmp_description(dw): + param_layout = _layout_from_header(icmp_header) + payload_layout = [ + ("data", dw), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, param_layout, packetized=True) + +def eth_icmp_user_description(dw): + param_layout = _layout_from_header(icmp_header) + [ + ("ip_address", 32), + ("length", 16) + ] + payload_layout = [ + ("data", dw), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, param_layout, packetized=True) + +def eth_udp_description(dw): + param_layout = _layout_from_header(udp_header) + payload_layout = [ + ("data", dw), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, param_layout, packetized=True) + +def eth_udp_user_description(dw): + param_layout = [ + ("src_port", 16), + ("dst_port", 16), + ("ip_address", 32), + ("length", 16) + ] + payload_layout = [ + ("data", dw), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, param_layout, packetized=True) + +def eth_etherbone_packet_description(dw): + param_layout = _layout_from_header(etherbone_packet_header) + payload_layout = [ + ("data", dw), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, param_layout, packetized=True) + +def eth_etherbone_packet_user_description(dw): + param_layout = _layout_from_header(etherbone_packet_header) + param_layout = _remove_from_layout(param_layout, "magic", "portsize", "addrsize", "version") + param_layout += eth_udp_user_description(dw).param_layout + payload_layout = [ + ("data", dw), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, param_layout, packetized=True) + +def eth_etherbone_record_description(dw): + param_layout = _layout_from_header(etherbone_record_header) + payload_layout = [ + ("data", dw), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, param_layout, packetized=True) + +def eth_etherbone_mmap_description(dw): + param_layout = [ + ("we", 1), + ("count", 8), + ("base_addr", 32), + ("be", dw//8) + ] + payload_layout = [ + ("addr", 32), + ("data", dw) + ] + return EndpointDescription(payload_layout, param_layout, packetized=True) + +def eth_tty_description(dw): + payload_layout = [("data", dw)] + return EndpointDescription(payload_layout, packetized=False) diff --git a/misoclib/liteeth/core/__init__.py b/misoclib/liteeth/core/__init__.py new file mode 100644 index 00000000..4d9b7147 --- /dev/null +++ b/misoclib/liteeth/core/__init__.py @@ -0,0 +1,19 @@ +from liteeth.common import * +from liteeth.generic import * +from liteeth.mac import LiteEthMAC +from liteeth.core.arp import LiteEthARP +from liteeth.core.ip import LiteEthIP +from liteeth.core.udp import LiteEthUDP +from liteeth.core.icmp import LiteEthICMP + +class LiteEthIPCore(Module, AutoCSR): + def __init__(self, phy, mac_address, ip_address, clk_freq): + self.submodules.mac = LiteEthMAC(phy, 8, interface="crossbar", with_hw_preamble_crc=True) + self.submodules.arp = LiteEthARP(self.mac, mac_address, ip_address, clk_freq) + self.submodules.ip = LiteEthIP(self.mac, mac_address, ip_address, self.arp.table) + self.submodules.icmp = LiteEthICMP(self.ip, ip_address) + +class LiteEthUDPIPCore(LiteEthIPCore): + def __init__(self, phy, mac_address, ip_address, clk_freq): + LiteEthIPCore.__init__(self, phy, mac_address, ip_address, clk_freq) + self.submodules.udp = LiteEthUDP(self.ip, ip_address) diff --git a/misoclib/liteeth/core/arp/__init__.py b/misoclib/liteeth/core/arp/__init__.py new file mode 100644 index 00000000..638505c3 --- /dev/null +++ b/misoclib/liteeth/core/arp/__init__.py @@ -0,0 +1,263 @@ +from liteeth.common import * +from liteeth.generic import * +from liteeth.generic.depacketizer import LiteEthDepacketizer +from liteeth.generic.packetizer import LiteEthPacketizer + +_arp_table_layout = [ + ("reply", 1), + ("request", 1), + ("ip_address", 32), + ("mac_address", 48) + ] + +class LiteEthARPPacketizer(LiteEthPacketizer): + def __init__(self): + LiteEthPacketizer.__init__(self, + eth_arp_description(8), + eth_mac_description(8), + arp_header, + arp_header_len) + +class LiteEthARPTX(Module): + def __init__(self, mac_address, ip_address): + self.sink = sink = Sink(_arp_table_layout) + self.source = source = Source(eth_mac_description(8)) + ### + self.submodules.packetizer = packetizer = LiteEthARPPacketizer() + + counter = Counter(max=max(arp_header_len, eth_min_len)) + self.submodules += counter + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + sink.ack.eq(1), + counter.reset.eq(1), + If(sink.stb, + sink.ack.eq(0), + NextState("SEND") + ) + ) + self.comb += [ + packetizer.sink.sop.eq(counter.value == 0), + packetizer.sink.eop.eq(counter.value == max(arp_header_len, eth_min_len)-1), + packetizer.sink.hwtype.eq(arp_hwtype_ethernet), + packetizer.sink.proto.eq(arp_proto_ip), + packetizer.sink.hwsize.eq(6), + packetizer.sink.protosize.eq(4), + packetizer.sink.sender_mac.eq(mac_address), + packetizer.sink.sender_ip.eq(ip_address), + If(sink.reply, + packetizer.sink.opcode.eq(arp_opcode_reply), + packetizer.sink.target_mac.eq(sink.mac_address), + packetizer.sink.target_ip.eq(sink.ip_address) + ).Elif(sink.request, + + packetizer.sink.opcode.eq(arp_opcode_request), + packetizer.sink.target_mac.eq(0xffffffffffff), + packetizer.sink.target_ip.eq(sink.ip_address) + ) + ] + fsm.act("SEND", + packetizer.sink.stb.eq(1), + Record.connect(packetizer.source, source), + source.target_mac.eq(packetizer.sink.target_mac), + source.sender_mac.eq(mac_address), + source.ethernet_type.eq(ethernet_type_arp), + If(source.stb & source.ack, + counter.ce.eq(1), + If(source.eop, + sink.ack.eq(1), + NextState("IDLE") + ) + ) + ) + +class LiteEthARPDepacketizer(LiteEthDepacketizer): + def __init__(self): + LiteEthDepacketizer.__init__(self, + eth_mac_description(8), + eth_arp_description(8), + arp_header, + arp_header_len) + +class LiteEthARPRX(Module): + def __init__(self, mac_address, ip_address): + self.sink = sink = Sink(eth_mac_description(8)) + self.source = source = Source(_arp_table_layout) + ### + self.submodules.depacketizer = depacketizer = LiteEthARPDepacketizer() + self.comb += Record.connect(sink, depacketizer.sink) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & depacketizer.source.sop, + depacketizer.source.ack.eq(0), + NextState("CHECK") + ) + ) + valid = Signal() + self.sync += valid.eq( + depacketizer.source.stb & + (depacketizer.source.hwtype == arp_hwtype_ethernet) & + (depacketizer.source.proto == arp_proto_ip) & + (depacketizer.source.hwsize == 6) & + (depacketizer.source.protosize == 4) & + (depacketizer.source.target_ip == ip_address) + ) + reply = Signal() + request = Signal() + self.comb += Case(depacketizer.source.opcode, { + arp_opcode_request : [request.eq(1)], + arp_opcode_reply : [reply.eq(1)], + "default" : [] + }) + self.comb += [ + source.ip_address.eq(depacketizer.source.sender_ip), + source.mac_address.eq(depacketizer.source.sender_mac) + ] + fsm.act("CHECK", + If(valid, + source.stb.eq(1), + source.reply.eq(reply), + source.request.eq(request) + ), + NextState("TERMINATE") + ), + fsm.act("TERMINATE", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & depacketizer.source.eop, + NextState("IDLE") + ) + ) + +class LiteEthARPTable(Module): + def __init__(self, clk_freq, max_requests=8): + self.sink = sink = Sink(_arp_table_layout) # from arp_rx + self.source = source = Source(_arp_table_layout) # to arp_tx + + # Request/Response interface + self.request = request = Sink(arp_table_request_layout) + self.response = response = Source(arp_table_response_layout) + ### + request_timeout = Timeout(clk_freq//10) + request_counter = Counter(max=max_requests) + request_pending = FlipFlop() + request_ip_address = FlipFlop(32) + self.submodules += request_timeout, request_counter, request_pending, request_ip_address + self.comb += [ + request_timeout.ce.eq(request_pending.q), + request_pending.d.eq(1), + request_ip_address.d.eq(request.ip_address) + ] + + # Note: Store only 1 IP/MAC couple, can be improved with a real + # table in the future to improve performance when packets are + # targeting multiple destinations. + update = Signal() + cached_valid = Signal() + cached_ip_address = Signal(32) + cached_mac_address = Signal(48) + cached_timeout = Timeout(clk_freq*10) + self.submodules += cached_timeout + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + # Note: for simplicicy, if APR table is busy response from arp_rx + # is lost. This is compensated by the protocol (retries) + If(sink.stb & sink.request, + NextState("SEND_REPLY") + ).Elif(sink.stb & sink.reply & request_pending.q, + NextState("UPDATE_TABLE"), + ).Elif(request_counter.value == max_requests-1, + NextState("PRESENT_RESPONSE") + ).Elif(request.stb | (request_pending.q & request_timeout.reached), + NextState("CHECK_TABLE") + ) + ) + fsm.act("SEND_REPLY", + source.stb.eq(1), + source.reply.eq(1), + source.ip_address.eq(sink.ip_address), + If(source.ack, + NextState("IDLE") + ) + ) + fsm.act("UPDATE_TABLE", + request_pending.reset.eq(1), + update.eq(1), + NextState("CHECK_TABLE") + ) + self.sync += [ + If(update, + cached_valid.eq(1), + cached_ip_address.eq(sink.ip_address), + cached_mac_address.eq(sink.mac_address), + cached_timeout.reset.eq(1) + ).Else( + cached_timeout.ce.eq(1), + If(cached_timeout.reached, + cached_valid.eq(0) + ) + ) + ] + found = Signal() + fsm.act("CHECK_TABLE", + If(cached_valid, + If(request_ip_address.q == cached_ip_address, + request_ip_address.reset.eq(1), + NextState("PRESENT_RESPONSE"), + ).Elif(request.ip_address == cached_ip_address, + request.ack.eq(request.stb), + NextState("PRESENT_RESPONSE"), + ).Else( + request_ip_address.ce.eq(request.stb), + NextState("SEND_REQUEST") + ) + ).Else( + request_ip_address.ce.eq(request.stb), + NextState("SEND_REQUEST") + ) + ) + fsm.act("SEND_REQUEST", + source.stb.eq(1), + source.request.eq(1), + source.ip_address.eq(request_ip_address.q), + If(source.ack, + request_timeout.reset.eq(1), + request_counter.reset.eq(request.stb), + request_counter.ce.eq(1), + request_pending.ce.eq(1), + request.ack.eq(1), + NextState("IDLE") + ) + ) + self.comb += [ + If(request_counter == max_requests-1, + response.failed.eq(1), + request_counter.reset.eq(1), + request_pending.reset.eq(1) + ), + response.mac_address.eq(cached_mac_address) + ] + fsm.act("PRESENT_RESPONSE", + response.stb.eq(1), + If(response.ack, + NextState("IDLE") + ) + ) + +class LiteEthARP(Module): + def __init__(self, mac, mac_address, ip_address, clk_freq): + self.submodules.tx = tx = LiteEthARPTX(mac_address, ip_address) + self.submodules.rx = rx = LiteEthARPRX(mac_address, ip_address) + self.submodules.table = table = LiteEthARPTable(clk_freq) + self.comb += [ + Record.connect(rx.source, table.sink), + Record.connect(table.source, tx.sink) + ] + mac_port = mac.crossbar.get_port(ethernet_type_arp) + self.comb += [ + Record.connect(tx.source, mac_port.sink), + Record.connect(mac_port.source, rx.sink) + ] diff --git a/misoclib/liteeth/core/etherbone/__init__.py b/misoclib/liteeth/core/etherbone/__init__.py new file mode 100644 index 00000000..78f3d738 --- /dev/null +++ b/misoclib/liteeth/core/etherbone/__init__.py @@ -0,0 +1,31 @@ +from liteeth.common import * +from liteeth.generic import * +from liteeth.generic.arbiter import Arbiter +from liteeth.generic.dispatcher import Dispatcher +from liteeth.core.etherbone.packet import * +from liteeth.core.etherbone.probe import * +from liteeth.core.etherbone.record import * +from liteeth.core.etherbone.wishbone import * + +class LiteEthEtherbone(Module): + def __init__(self, udp, udp_port): + # decode/encode etherbone packets + self.submodules.packet = packet = LiteEthEtherbonePacket(udp, udp_port) + + # packets can be probe (etherbone discovering) or records with + # writes and reads + self.submodules.probe = probe = LiteEthEtherboneProbe() + self.submodules.record = record = LiteEthEtherboneRecord() + + # arbitrate/dispatch probe/records packets + dispatcher = Dispatcher(packet.source, [probe.sink, record.sink]) + self.comb += dispatcher.sel.eq(~packet.source.pf) + arbiter = Arbiter([probe.source, record.source], packet.sink) + self.submodules += dispatcher, arbiter + + # create mmap ŵishbone master + self.submodules.master = master = LiteEthEtherboneWishboneMaster() + self.comb += [ + Record.connect(record.receiver.source, master.sink), + Record.connect(master.source, record.sender.sink) + ] diff --git a/misoclib/liteeth/core/etherbone/dissector/bit.lua b/misoclib/liteeth/core/etherbone/dissector/bit.lua new file mode 100644 index 00000000..d9fd4ceb --- /dev/null +++ b/misoclib/liteeth/core/etherbone/dissector/bit.lua @@ -0,0 +1,260 @@ +--[[--------------- +LuaBit v0.4 +------------------- +a bitwise operation lib for lua. + +http://luaforge.net/projects/bit/ + +How to use: +------------------- + bit.bnot(n) -- bitwise not (~n) + bit.band(m, n) -- bitwise and (m & n) + bit.bor(m, n) -- bitwise or (m | n) + bit.bxor(m, n) -- bitwise xor (m ^ n) + bit.brshift(n, bits) -- right shift (n >> bits) + bit.blshift(n, bits) -- left shift (n << bits) + bit.blogic_rshift(n, bits) -- logic right shift(zero fill >>>) + +Please note that bit.brshift and bit.blshift only support number within +32 bits. + +2 utility functions are provided too: + bit.tobits(n) -- convert n into a bit table(which is a 1/0 sequence) + -- high bits first + bit.tonumb(bit_tbl) -- convert a bit table into a number +------------------- + +Under the MIT license. + +copyright(c) 2006~2007 hanzhao (abrash_han@hotmail.com) +--]]--------------- + +do + +------------------------ +-- bit lib implementions + +local function check_int(n) + -- checking not float + if(n - math.floor(n) > 0) then + error("trying to use bitwise operation on non-integer!") + end +end + +local function to_bits(n) + check_int(n) + if(n < 0) then + -- negative + return to_bits(bit.bnot(math.abs(n)) + 1) + end + -- to bits table + local tbl = {} + local cnt = 1 + while (n > 0) do + local last = math.mod(n,2) + if(last == 1) then + tbl[cnt] = 1 + else + tbl[cnt] = 0 + end + n = (n-last)/2 + cnt = cnt + 1 + end + + return tbl +end + +local function tbl_to_number(tbl) + local n = table.getn(tbl) + + local rslt = 0 + local power = 1 + for i = 1, n do + rslt = rslt + tbl[i]*power + power = power*2 + end + + return rslt +end + +local function expand(tbl_m, tbl_n) + local big = {} + local small = {} + if(table.getn(tbl_m) > table.getn(tbl_n)) then + big = tbl_m + small = tbl_n + else + big = tbl_n + small = tbl_m + end + -- expand small + for i = table.getn(small) + 1, table.getn(big) do + small[i] = 0 + end + +end + +local function bit_or(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) + for i = 1, rslt do + if(tbl_m[i]== 0 and tbl_n[i] == 0) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + + return tbl_to_number(tbl) +end + +local function bit_and(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) + for i = 1, rslt do + if(tbl_m[i]== 0 or tbl_n[i] == 0) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + + return tbl_to_number(tbl) +end + +local function bit_not(n) + + local tbl = to_bits(n) + local size = math.max(table.getn(tbl), 32) + for i = 1, size do + if(tbl[i] == 1) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + return tbl_to_number(tbl) +end + +local function bit_xor(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) + for i = 1, rslt do + if(tbl_m[i] ~= tbl_n[i]) then + tbl[i] = 1 + else + tbl[i] = 0 + end + end + + --table.foreach(tbl, print) + + return tbl_to_number(tbl) +end + +local function bit_rshift(n, bits) + check_int(n) + + local high_bit = 0 + if(n < 0) then + -- negative + n = bit_not(math.abs(n)) + 1 + high_bit = 2147483648 -- 0x80000000 + end + + for i=1, bits do + n = n/2 + n = bit_or(math.floor(n), high_bit) + end + return math.floor(n) +end + +-- logic rightshift assures zero filling shift +local function bit_logic_rshift(n, bits) + check_int(n) + if(n < 0) then + -- negative + n = bit_not(math.abs(n)) + 1 + end + for i=1, bits do + n = n/2 + end + return math.floor(n) +end + +local function bit_lshift(n, bits) + check_int(n) + + if(n < 0) then + -- negative + n = bit_not(math.abs(n)) + 1 + end + + for i=1, bits do + n = n*2 + end + return bit_and(n, 4294967295) -- 0xFFFFFFFF +end + +local function bit_xor2(m, n) + local rhs = bit_or(bit_not(m), bit_not(n)) + local lhs = bit_or(m, n) + local rslt = bit_and(lhs, rhs) + return rslt +end + +-------------------- +-- bit lib interface + +bit = { + -- bit operations + bnot = bit_not, + band = bit_and, + bor = bit_or, + bxor = bit_xor, + brshift = bit_rshift, + blshift = bit_lshift, + bxor2 = bit_xor2, + blogic_rshift = bit_logic_rshift, + + -- utility func + tobits = to_bits, + tonumb = tbl_to_number, +} + +end + +--[[ +for i = 1, 100 do + for j = 1, 100 do + if(bit.bxor(i, j) ~= bit.bxor2(i, j)) then + error("bit.xor failed.") + end + end +end +--]] + + + + + + + + + + + + + diff --git a/misoclib/liteeth/core/etherbone/dissector/etherbone.lua b/misoclib/liteeth/core/etherbone/dissector/etherbone.lua new file mode 100644 index 00000000..0a629a43 --- /dev/null +++ b/misoclib/liteeth/core/etherbone/dissector/etherbone.lua @@ -0,0 +1,223 @@ +-- Etherbone Dissector +-- Copyright 2013 OHWR.org +-- Copyright 2015 EnjoyDigital (global clean up) + +local bit = require("bit") + +local VALS_BOOL = {[0] = "False", [1] = "True"} +local VALS_RES = {[0] = "not set", [1] = "set, bad data?"} +local VALS_SIZE = { +[0x00] = "Bad Value", +[0x01] = "8 bit", +[0x02] = "16 bit", +[0x03] = "16,8 bit", +[0x04] = "32 bit", +[0x05] = "32,8 bit", +[0x06] = "32,16 bit", +[0x07] = "32,16,8 bit", +[0x08] = "64 bit", +[0x09] = "64,8 bit", +[0x0A] = "64,16 bit", +[0x0B] = "64,16,8 bit", +[0x0C] = "64,32 bit", +[0x0D] = "64,32,8 bit", +[0x0E] = "64,32,16 bit", +[0x0F] = "64,32,16,8 bit", +} + +function num2hex(num) + local hexstr = '0123456789abcdef' + local s = '' + while num > 0 do + local mod = math.fmod(num, 16) + s = string.sub(hexstr, mod+1, mod+1) .. s + num = math.floor(num / 16) + end + if s == '' then s = '0' end + return s +end + +function max(a, b) + if a > b then + return a + else + return b + end +end + +-- declare protocol +proto_eb = Proto("eb", "Etherbone") + +-- declare fields +local eb = proto_eb.fields +eb.hdr = ProtoField.uint32("eb.hdr", "Header", base.HEX) +eb.rec = ProtoField.bytes("eb.rec", "Record ", base.HEX) + +eb.hdr_magic = ProtoField.uint16("eb.hdr.magic", "Magic ", base.HEX, nil, 0xFFFF) +eb.hdr_ver = ProtoField.uint16("eb.hdr.ver", "Version ", base.DEC, nil, 0xF000) +eb.hdr_noreads = ProtoField.uint16("eb.hdr.noreads", "No Reads ", base.DEC, VALS_BOOL, 0x0400) +eb.hdr_proberep = ProtoField.uint16("eb.hdr.proberes", "Probe Reply ", base.DEC, VALS_BOOL, 0x0200) +eb.hdr_probereq = ProtoField.uint16("eb.hdr.probereq", "Probe Flag ", base.DEC, VALS_BOOL, 0x0100) +eb.hdr_adrs = ProtoField.uint16("eb.hdr.adrw", "Address Width ", base.DEC, VALS_SIZE , 0x00F0) +eb.hdr_ports = ProtoField.uint16("eb.hdr.portw", "Port Width ", base.DEC, VALS_SIZE , 0x000F) + +eb.rec_hdr = ProtoField.uint32("eb.rec.hdr", "Header ", base.HEX) +eb.rec_writes = ProtoField.bytes("eb.rec.writes", "Writes ", base.HEX) +eb.rec_reads = ProtoField.bytes("eb.rec.reads", "Reads ", base.HEX) + +eb.rec_hdr_flags = ProtoField.uint8("eb.rec.hdr.flags", "Flags ", base.HEX) +eb.rec_hdr_select = ProtoField.uint8("eb.rec.hdr.select", "Select ", base.HEX) +eb.rec_hdr_wr = ProtoField.uint8("eb.rec.hdr.wr", "Writes ", base.DEC) +eb.rec_hdr_rd = ProtoField.uint8("eb.rec.hdr.rd", "Reads ", base.DEC) + +eb.rec_hdr_flags_adrcfg = ProtoField.uint8("eb.rec.hdr.flags.adrcfg", "ReplyToCfgSpace ", base.DEC, VALS_BOOL, 0x80) +eb.rec_hdr_flags_rbacfg = ProtoField.uint8("eb.rec.hdr.adrcfg", "ReadFromCfgSpace ", base.DEC, VALS_BOOL, 0x40) +eb.rec_hdr_flags_rdfifo = ProtoField.uint8("eb.rec.hdr.adrcfg", "ReadFIFO ", base.DEC, VALS_BOOL, 0x20) +eb.rec_hdr_flags_dropcyc= ProtoField.uint8("eb.rec.hdr.adrcfg", "DropCycle ", base.DEC, VALS_BOOL, 0x08) +eb.rec_hdr_flags_wbacfg = ProtoField.uint8("eb.rec.hdr.adrcfg", "WriteToCfgSpace ", base.DEC, VALS_BOOL, 0x04) +eb.rec_hdr_flags_wrfifo = ProtoField.uint8("eb.rec.hdr.adrcfg", "WriteFIFO ", base.DEC, VALS_BOOL, 0x02) + +eb.rec_wrsadr8 = ProtoField.uint8("eb.rec.wrsadr8", "BaseAddr8 ", base.HEX) +eb.rec_wrsadr16 = ProtoField.uint16("eb.rec.wrsadr16", "BaseAddr16 ", base.HEX) +eb.rec_wrsadr32 = ProtoField.uint32("eb.rec.wrsadr32", "BaseAddr32 ", base.HEX) +eb.rec_wrsadr64 = ProtoField.uint64("eb.rec.wrsadr64", "BaseAddr64 ", base.HEX) +eb.rec_wrdata8 = ProtoField.uint8("eb.rec.wrdata8", "Value8 ", base.HEX) +eb.rec_wrdata16 = ProtoField.uint16("eb.rec.wrdata16", "Value16 ", base.HEX) +eb.rec_wrdata32 = ProtoField.uint32("eb.rec.wrdata32", "Value32 ", base.HEX) +eb.rec_wrdata64 = ProtoField.uint64("eb.rec.wrdata64", "Value64 ", base.HEX) + +eb.rec_rdbadr8 = ProtoField.uint8("eb.rec.rdbadr8", "ReplyAddr8 ", base.HEX) +eb.rec_rdbadr16 = ProtoField.uint16("eb.rec.rdbadr16", "ReplyAddr16 ", base.HEX) +eb.rec_rdbadr32 = ProtoField.uint32("eb.rec.rdbadr32", "ReplyAddr32 ", base.HEX) +eb.rec_rdbadr64 = ProtoField.uint64("eb.rec.rdbadr64", "ReplyAddr64 ", base.HEX) +eb.rec_rddata8 = ProtoField.uint8("eb.rec.rddata8", "Address8 ", base.HEX) +eb.rec_rddata16 = ProtoField.uint16("eb.rec.rddata16", "Address16 ", base.HEX) +eb.rec_rddata32 = ProtoField.uint32("eb.rec.rddata32", "Address32 ", base.HEX) +eb.rec_rddata64 = ProtoField.uint64("eb.rec.rddata64", "Address64 ", base.HEX) + +-- define the dissector +function proto_eb.dissector(buf, pinfo, tree) + if (buf:len() < 4) then + return 0 -- too short, go to default protocol + end + + local mylen = buf:len() + pinfo.cols.protocol = "eb" + + -- add packet to the tree root, fields will be added to subtree + local t = tree:add( proto_eb, buf(0, mylen) ) + local t_hdr = t:add( eb.hdr, buf(0,4) ) + + local magic = num2hex(tonumber(buf(0,2):uint())) + if(magic == "4e6f") then + + t_hdr:add( eb.hdr_magic, buf(0,2)) -- magic + t_hdr:add( eb.hdr_ver, buf(2,2)) -- version + t_hdr:add( eb.hdr_noreads, buf(2,2)) -- no reads + t_hdr:add( eb.hdr_proberep, buf(2,2)) -- probe response + t_hdr:add( eb.hdr_probereq, buf(2,2)) -- probe request + + t_hdr:add( eb.hdr_adrs, buf(2,2)) -- supported addr size + t_hdr:add( eb.hdr_ports, buf(2,2)) -- supported port size + + local probe = tonumber(buf(2,1):uint()) % 4 + if (probe == 0) then + local widths = tonumber(buf(3,1):uint()) + local data_width = widths % 16 + local addr_width = (widths - data_width) / 16 + local alignment = max(max(addr_width, data_width), 2) + + local record_alignment = max(alignment, 4) + local offset = max(alignment, 4) + + local recordcnt = 0 + while (offset < buf:len()) do + local wr = tonumber(buf(offset+2,1):uint()) + local rd = tonumber(buf(offset+3,1):uint()) + + local rdadr = 0 + local wradr = 0 + if(rd > 0) then + rdadr = 1 + end + if(wr > 0) then + wradr = 1 + end + + if((wr == 0) and (rd == 0)) then + offset = offset + record_alignment + else + local t_rec = t:add( "Record "..tostring(recordcnt).." (W"..tostring(wr).." R"..tostring(rd)..")", buf(offset, (record_alignment+(rd+wr+rdadr+wradr)*alignment))) + recordcnt = recordcnt + 1 + + local t_rec_hdr = t_rec:add( eb.rec_hdr, buf(offset,4)) + local t_rec_hdr_flags = t_rec_hdr:add( eb.rec_hdr_flags, buf(offset,1)) + t_rec_hdr_flags:add( eb.rec_hdr_flags_adrcfg, buf(offset,1)) + t_rec_hdr_flags:add( eb.rec_hdr_flags_rbacfg, buf(offset,1)) + t_rec_hdr_flags:add( eb.rec_hdr_flags_rdfifo, buf(offset,1)) + t_rec_hdr_flags:add( eb.rec_hdr_flags_dropcyc , buf(offset,1)) + t_rec_hdr_flags:add( eb.rec_hdr_flags_wbacfg , buf(offset,1)) + t_rec_hdr_flags:add( eb.rec_hdr_flags_wrfifo, buf(offset,1)) + t_rec_hdr:add( eb.rec_hdr_select, buf(offset+1,1)) + t_rec_hdr:add( eb.rec_hdr_wr, buf(offset+2,1)) + t_rec_hdr:add( eb.rec_hdr_rd, buf(offset+3,1)) + offset = offset + record_alignment + local tmp_offset + + if(wr > 0) then + local t_writes = t_rec:add( eb.rec_writes, buf(offset,(1+wr)*alignment)) + + if addr_width==1 then t_writes:add(eb.rec_wrsadr8, buf(offset+alignment-1, 1)) + elseif addr_width==2 then t_writes:add(eb.rec_wrsadr16, buf(offset+alignment-2, 2)) + elseif addr_width==4 then t_writes:add(eb.rec_wrsadr32, buf(offset+alignment-4, 4)) + elseif addr_width==8 then t_writes:add(eb.rec_wrsadr64, buf(offset+alignment-8, 8)) + end + offset = offset + alignment + + tmp_offset = offset + while (tmp_offset < offset+wr*alignment) do + if data_width==1 then t_writes:add( eb.rec_wrdata8, buf(tmp_offset+alignment-1, 1)) + elseif data_width==2 then t_writes:add( eb.rec_wrdata16, buf(tmp_offset+alignment-2, 2)) + elseif data_width==4 then t_writes:add( eb.rec_wrdata32, buf(tmp_offset+alignment-4, 4)) + elseif data_width==8 then t_writes:add( eb.rec_wrdata64, buf(tmp_offset+alignment-8, 8)) + end + tmp_offset = tmp_offset + alignment + end + offset = tmp_offset + end + + if(rd > 0) then + local t_reads = t_rec:add( eb.rec_reads, buf(offset,(1+rd)*alignment)) + + if addr_width==1 then t_reads:add( eb.rec_rdbadr8, buf(offset+alignment-1, 1)) + elseif addr_width==2 then t_reads:add( eb.rec_rdbadr16, buf(offset+alignment-2, 2)) + elseif addr_width==4 then t_reads:add( eb.rec_rdbadr32, buf(offset+alignment-4, 4)) + elseif addr_width==8 then t_reads:add( eb.rec_rdbadr64, buf(offset+alignment-8, 8)) + end + offset = offset + alignment + + tmp_offset = offset + while (tmp_offset < offset+rd*alignment) do + if addr_width==1 then t_reads:add( eb.rec_rddata8, buf(tmp_offset+alignment-1, 1)) + elseif addr_width==2 then t_reads:add( eb.rec_rddata16, buf(tmp_offset+alignment-2, 2)) + elseif addr_width==4 then t_reads:add( eb.rec_rddata32, buf(tmp_offset+alignment-4, 4)) + elseif addr_width==8 then t_reads:add( eb.rec_rddata64, buf(tmp_offset+alignment-8, 8)) + end + tmp_offset = tmp_offset + alignment + end + offset = tmp_offset + end + end + end + + end + + else + return 0 + end + +end + +-- register eb protocol on UDP port 20000 +local tab = DissectorTable.get("udp.port") +tab:add(20000, proto_eb) diff --git a/misoclib/liteeth/core/etherbone/packet.py b/misoclib/liteeth/core/etherbone/packet.py new file mode 100644 index 00000000..22f72c5a --- /dev/null +++ b/misoclib/liteeth/core/etherbone/packet.py @@ -0,0 +1,129 @@ +from liteeth.common import * +from liteeth.generic import * +from liteeth.generic.depacketizer import LiteEthDepacketizer +from liteeth.generic.packetizer import LiteEthPacketizer + +class LiteEthEtherbonePacketPacketizer(LiteEthPacketizer): + def __init__(self): + LiteEthPacketizer.__init__(self, + eth_etherbone_packet_description(32), + eth_udp_user_description(32), + etherbone_packet_header, + etherbone_packet_header_len) + +class LiteEthEtherbonePacketTX(Module): + def __init__(self, udp_port): + self.sink = sink = Sink(eth_etherbone_packet_user_description(32)) + self.source = source = Source(eth_udp_user_description(32)) + ### + self.submodules.packetizer = packetizer = LiteEthEtherbonePacketPacketizer() + self.comb += [ + packetizer.sink.stb.eq(sink.stb), + packetizer.sink.sop.eq(sink.sop), + packetizer.sink.eop.eq(sink.eop), + sink.ack.eq(packetizer.sink.ack), + + packetizer.sink.magic.eq(etherbone_magic), + packetizer.sink.port_size.eq(32//8), + packetizer.sink.addr_size.eq(32//8), + packetizer.sink.pf.eq(sink.pf), + packetizer.sink.pr.eq(sink.pr), + packetizer.sink.nr.eq(sink.nr), + packetizer.sink.version.eq(etherbone_version), + + packetizer.sink.data.eq(sink.data) + ] + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + packetizer.source.ack.eq(1), + If(packetizer.source.stb & packetizer.source.sop, + packetizer.source.ack.eq(0), + NextState("SEND") + ) + ) + fsm.act("SEND", + Record.connect(packetizer.source, source), + source.src_port.eq(udp_port), + source.dst_port.eq(udp_port), + source.ip_address.eq(sink.ip_address), + source.length.eq(sink.length + etherbone_packet_header_len), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + +class LiteEthEtherbonePacketDepacketizer(LiteEthDepacketizer): + def __init__(self): + LiteEthDepacketizer.__init__(self, + eth_udp_user_description(32), + eth_etherbone_packet_description(32), + etherbone_packet_header, + etherbone_packet_header_len) + +class LiteEthEtherbonePacketRX(Module): + def __init__(self): + self.sink = sink = Sink(eth_udp_user_description(32)) + self.source = source = Source(eth_etherbone_packet_user_description(32)) + ### + self.submodules.depacketizer = depacketizer = LiteEthEtherbonePacketDepacketizer() + self.comb += Record.connect(sink, depacketizer.sink) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & depacketizer.source.sop, + depacketizer.source.ack.eq(0), + NextState("CHECK") + ) + ) + valid = Signal() + self.sync += valid.eq( + depacketizer.source.stb & + (depacketizer.source.magic == etherbone_magic) + ) + fsm.act("CHECK", + If(valid, + NextState("PRESENT") + ).Else( + NextState("DROP") + ) + ) + self.comb += [ + source.sop.eq(depacketizer.source.sop), + source.eop.eq(depacketizer.source.eop), + + source.pf.eq(depacketizer.source.pf), + source.pr.eq(depacketizer.source.pr), + source.nr.eq(depacketizer.source.nr), + + source.data.eq(depacketizer.source.data), + + source.src_port.eq(sink.src_port), + source.dst_port.eq(sink.dst_port), + source.ip_address.eq(sink.ip_address), + source.length.eq(sink.length - etherbone_packet_header_len) + ] + fsm.act("PRESENT", + source.stb.eq(depacketizer.source.stb), + depacketizer.source.ack.eq(source.ack), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + fsm.act("DROP", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & depacketizer.source.eop & depacketizer.source.ack, + NextState("IDLE") + ) + ) + +class LiteEthEtherbonePacket(Module): + def __init__(self, udp, udp_port): + self.submodules.tx = tx = LiteEthEtherbonePacketTX(udp_port) + self.submodules.rx = rx = LiteEthEtherbonePacketRX() + udp_port = udp.crossbar.get_port(udp_port, dw=32) + self.comb += [ + Record.connect(tx.source, udp_port.sink), + Record.connect(udp_port.source, rx.sink) + ] + self.sink, self.source = self.tx.sink, self.rx.source diff --git a/misoclib/liteeth/core/etherbone/probe.py b/misoclib/liteeth/core/etherbone/probe.py new file mode 100644 index 00000000..04eb9f6a --- /dev/null +++ b/misoclib/liteeth/core/etherbone/probe.py @@ -0,0 +1,24 @@ +from liteeth.common import * +from liteeth.generic import * + +class LiteEthEtherboneProbe(Module): + def __init__(self): + self.sink = sink = Sink(eth_etherbone_packet_user_description(32)) + self.source = source = Source(eth_etherbone_packet_user_description(32)) + ### + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + sink.ack.eq(1), + If(sink.stb & sink.sop, + sink.ack.eq(0), + NextState("PROBE_RESPONSE") + ) + ) + fsm.act("PROBE_RESPONSE", + Record.connect(sink, source), + source.pf.eq(0), + source.pr.eq(1), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) diff --git a/misoclib/liteeth/core/etherbone/record.py b/misoclib/liteeth/core/etherbone/record.py new file mode 100644 index 00000000..77bc0378 --- /dev/null +++ b/misoclib/liteeth/core/etherbone/record.py @@ -0,0 +1,176 @@ +from liteeth.common import * +from liteeth.generic import * +from liteeth.generic.depacketizer import LiteEthDepacketizer +from liteeth.generic.packetizer import LiteEthPacketizer + +class LiteEthEtherboneRecordPacketizer(LiteEthPacketizer): + def __init__(self): + LiteEthPacketizer.__init__(self, + eth_etherbone_record_description(32), + eth_etherbone_packet_user_description(32), + etherbone_record_header, + etherbone_record_header_len) + +class LiteEthEtherboneRecordDepacketizer(LiteEthDepacketizer): + def __init__(self): + LiteEthDepacketizer.__init__(self, + eth_etherbone_packet_user_description(32), + eth_etherbone_record_description(32), + etherbone_record_header, + etherbone_record_header_len) + +class LiteEthEtherboneRecordReceiver(Module): + def __init__(self, buffer_depth=256): + self.sink = sink = Sink(eth_etherbone_record_description(32)) + self.source = source = Source(eth_etherbone_mmap_description(32)) + ### + fifo = SyncFIFO(eth_etherbone_record_description(32), buffer_depth, buffered=True) + self.submodules += fifo + self.comb += Record.connect(sink, fifo.sink) + + self.submodules.base_addr = base_addr = FlipFlop(32) + self.comb += base_addr.d.eq(fifo.source.data) + + self.submodules.counter = counter = Counter(max=512) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + fifo.source.ack.eq(1), + counter.reset.eq(1), + If(fifo.source.stb & fifo.source.sop, + base_addr.ce.eq(1), + If(fifo.source.wcount, + NextState("RECEIVE_WRITES") + ).Elif(fifo.source.rcount, + NextState("RECEIVE_READS") + ) + ) + ) + fsm.act("RECEIVE_WRITES", + source.stb.eq(fifo.source.stb), + source.sop.eq(counter.value == 0), + source.eop.eq(counter.value == fifo.source.wcount-1), + source.count.eq(fifo.source.wcount), + source.be.eq(fifo.source.byte_enable), + source.addr.eq(base_addr.q[2:] + counter.value), + source.we.eq(1), + source.data.eq(fifo.source.data), + fifo.source.ack.eq(source.ack), + If(source.stb & source.ack, + counter.ce.eq(1), + If(source.eop, + If(fifo.source.rcount, + NextState("RECEIVE_BASE_RET_ADDR") + ).Else( + NextState("IDLE") + ) + ) + ) + ) + fsm.act("RECEIVE_BASE_RET_ADDR", + counter.reset.eq(1), + If(fifo.source.stb & fifo.source.sop, + base_addr.ce.eq(1), + NextState("RECEIVE_READS") + ) + ) + fsm.act("RECEIVE_READS", + source.stb.eq(fifo.source.stb), + source.sop.eq(counter.value == 0), + source.eop.eq(counter.value == fifo.source.rcount-1), + source.count.eq(fifo.source.rcount), + source.base_addr.eq(base_addr.q), + source.addr.eq(fifo.source.data[2:]), + fifo.source.ack.eq(source.ack), + If(source.stb & source.ack, + counter.ce.eq(1), + If(source.eop, + NextState("IDLE") + ) + ) + ) + +class LiteEthEtherboneRecordSender(Module): + def __init__(self, buffer_depth=256): + self.sink = sink = Sink(eth_etherbone_mmap_description(32)) + self.source = source = Source(eth_etherbone_record_description(32)) + ### + pbuffer = PacketBuffer(eth_etherbone_mmap_description(32), buffer_depth) + self.submodules += pbuffer + self.comb += Record.connect(sink, pbuffer.sink) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + pbuffer.source.ack.eq(1), + If(pbuffer.source.stb & pbuffer.source.sop, + pbuffer.source.ack.eq(0), + NextState("SEND_BASE_ADDRESS") + ) + ) + self.comb += [ + source.byte_enable.eq(pbuffer.source.be), + If(pbuffer.source.we, + source.wcount.eq(pbuffer.source.count) + ).Else( + source.rcount.eq(pbuffer.source.count) + ) + ] + + fsm.act("SEND_BASE_ADDRESS", + source.stb.eq(pbuffer.source.stb), + source.sop.eq(1), + source.eop.eq(0), + source.data.eq(pbuffer.source.base_addr), + If(source.ack, + NextState("SEND_DATA") + ) + ) + fsm.act("SEND_DATA", + source.stb.eq(pbuffer.source.stb), + source.sop.eq(0), + source.eop.eq(pbuffer.source.eop), + source.data.eq(pbuffer.source.data), + If(source.stb & source.ack, + pbuffer.source.ack.eq(1), + If(source.eop, + NextState("IDLE") + ) + ) + ) + +# Limitation: For simplicity we only support 1 record per packet +class LiteEthEtherboneRecord(Module): + def __init__(self, endianness="big"): + self.sink = sink = Sink(eth_etherbone_packet_user_description(32)) + self.source = source = Sink(eth_etherbone_packet_user_description(32)) + ### + + # receive record, decode it and generate mmap stream + self.submodules.depacketizer = depacketizer = LiteEthEtherboneRecordDepacketizer() + self.submodules.receiver = receiver = LiteEthEtherboneRecordReceiver() + self.comb += [ + Record.connect(sink, depacketizer.sink), + Record.connect(depacketizer.source, receiver.sink) + ] + if endianness is "big": + self.comb += receiver.sink.data.eq(reverse_bytes(depacketizer.source.data)) + + # save last ip address + last_ip_address = Signal(32) + self.sync += [ + If(sink.stb & sink.sop & sink.ack, + last_ip_address.eq(sink.ip_address) + ) + ] + + # receive mmap stream, encode it and send records + self.submodules.sender = sender = LiteEthEtherboneRecordSender() + self.submodules.packetizer = packetizer = LiteEthEtherboneRecordPacketizer() + self.comb += [ + Record.connect(sender.source, packetizer.sink), + Record.connect(packetizer.source, source), + source.length.eq(sender.source.wcount*4 + 4 +etherbone_record_header_len), # XXX improve this + source.ip_address.eq(last_ip_address) + ] + if endianness is "big": + self.comb += packetizer.sink.data.eq(reverse_bytes(sender.source.data)) diff --git a/misoclib/liteeth/core/etherbone/wishbone.py b/misoclib/liteeth/core/etherbone/wishbone.py new file mode 100644 index 00000000..2f5b1762 --- /dev/null +++ b/misoclib/liteeth/core/etherbone/wishbone.py @@ -0,0 +1,69 @@ +from liteeth.common import * +from liteeth.generic import * +from migen.bus import wishbone + +class LiteEthEtherboneWishboneMaster(Module): + def __init__(self): + self.sink = sink = Sink(eth_etherbone_mmap_description(32)) + self.source = source = Source(eth_etherbone_mmap_description(32)) + self.bus = bus = wishbone.Interface() + ###s + + self.submodules.data = data = FlipFlop(32) + self.comb += data.d.eq(bus.dat_r) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + sink.ack.eq(1), + If(sink.stb & sink.sop, + sink.ack.eq(0), + If(sink.we, + NextState("WRITE_DATA") + ).Else( + NextState("READ_DATA") + ) + ) + ) + fsm.act("WRITE_DATA", + bus.adr.eq(sink.addr), + bus.dat_w.eq(sink.data), + bus.sel.eq(sink.be), + bus.stb.eq(sink.stb), + bus.we.eq(1), + bus.cyc.eq(1), + If(bus.stb & bus.ack, + sink.ack.eq(1), + If(sink.eop, + NextState("IDLE") + ) + ) + ) + fsm.act("READ_DATA", + bus.adr.eq(sink.addr), + bus.sel.eq(sink.be), + bus.stb.eq(sink.stb), + bus.cyc.eq(1), + If(bus.stb & bus.ack, + data.ce.eq(1), + NextState("SEND_DATA") + ) + ) + fsm.act("SEND_DATA", + source.stb.eq(sink.stb), + source.sop.eq(sink.sop), + source.eop.eq(sink.eop), + source.base_addr.eq(sink.base_addr), + source.addr.eq(sink.addr), + source.count.eq(sink.count), + source.be.eq(sink.be), + source.we.eq(1), + source.data.eq(data.q), + If(source.stb & source.ack, + sink.ack.eq(1), + If(source.eop, + NextState("IDLE") + ).Else( + NextState("READ_DATA") + ) + ) + ) diff --git a/misoclib/liteeth/core/icmp/__init__.py b/misoclib/liteeth/core/icmp/__init__.py new file mode 100644 index 00000000..0af3b624 --- /dev/null +++ b/misoclib/liteeth/core/icmp/__init__.py @@ -0,0 +1,138 @@ +from liteeth.common import * +from liteeth.generic import * +from liteeth.generic.depacketizer import LiteEthDepacketizer +from liteeth.generic.packetizer import LiteEthPacketizer + +class LiteEthICMPPacketizer(LiteEthPacketizer): + def __init__(self): + LiteEthPacketizer.__init__(self, + eth_icmp_description(8), + eth_ipv4_user_description(8), + icmp_header, + icmp_header_len) + +class LiteEthICMPTX(Module): + def __init__(self, ip_address): + self.sink = sink = Sink(eth_icmp_user_description(8)) + self.source = source = Source(eth_ipv4_user_description(8)) + ### + self.submodules.packetizer = packetizer = LiteEthICMPPacketizer() + self.comb += [ + packetizer.sink.stb.eq(sink.stb), + packetizer.sink.sop.eq(sink.sop), + packetizer.sink.eop.eq(sink.eop), + sink.ack.eq(packetizer.sink.ack), + packetizer.sink.msgtype.eq(sink.msgtype), + packetizer.sink.code.eq(sink.code), + packetizer.sink.checksum.eq(sink.checksum), + packetizer.sink.quench.eq(sink.quench), + packetizer.sink.data.eq(sink.data) + ] + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + packetizer.source.ack.eq(1), + If(packetizer.source.stb & packetizer.source.sop, + packetizer.source.ack.eq(0), + NextState("SEND") + ) + ) + fsm.act("SEND", + Record.connect(packetizer.source, source), + source.length.eq(sink.length + icmp_header_len), + source.protocol.eq(icmp_protocol), + source.ip_address.eq(sink.ip_address), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + +class LiteEthICMPDepacketizer(LiteEthDepacketizer): + def __init__(self): + LiteEthDepacketizer.__init__(self, + eth_ipv4_user_description(8), + eth_icmp_description(8), + icmp_header, + icmp_header_len) + +class LiteEthICMPRX(Module): + def __init__(self, ip_address): + self.sink = sink = Sink(eth_ipv4_user_description(8)) + self.source = source = Source(eth_icmp_user_description(8)) + ### + self.submodules.depacketizer = depacketizer = LiteEthICMPDepacketizer() + self.comb += Record.connect(sink, depacketizer.sink) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & depacketizer.source.sop, + depacketizer.source.ack.eq(0), + NextState("CHECK") + ) + ) + valid = Signal() + self.sync += valid.eq( + depacketizer.source.stb & + (sink.protocol == icmp_protocol) + ) + fsm.act("CHECK", + If(valid, + NextState("PRESENT") + ).Else( + NextState("DROP") + ) + ) + self.comb += [ + source.sop.eq(depacketizer.source.sop), + source.eop.eq(depacketizer.source.eop), + source.msgtype.eq(depacketizer.source.msgtype), + source.code.eq(depacketizer.source.code), + source.checksum.eq(depacketizer.source.checksum), + source.quench.eq(depacketizer.source.quench), + source.ip_address.eq(sink.ip_address), + source.length.eq(sink.length - icmp_header_len), + source.data.eq(depacketizer.source.data), + source.error.eq(depacketizer.source.error) + ] + fsm.act("PRESENT", + source.stb.eq(depacketizer.source.stb), + depacketizer.source.ack.eq(source.ack), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + fsm.act("DROP", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & depacketizer.source.eop & depacketizer.source.ack, + NextState("IDLE") + ) + ) + +class LiteEthICMPEcho(Module): + def __init__(self): + self.sink = sink = Sink(eth_icmp_user_description(8)) + self.source = source = Source(eth_icmp_user_description(8)) + ### + self.submodules.buffer = PacketBuffer(eth_icmp_user_description(8), 128, 2) + self.comb += [ + Record.connect(sink, self.buffer.sink), + Record.connect(self.buffer.source, source), + self.source.msgtype.eq(0x0), + self.source.checksum.eq(~((~self.buffer.source.checksum)-0x0800)) + ] + +class LiteEthICMP(Module): + def __init__(self, ip, ip_address): + self.submodules.tx = tx = LiteEthICMPTX(ip_address) + self.submodules.rx = rx = LiteEthICMPRX(ip_address) + self.submodules.echo = echo = LiteEthICMPEcho() + self.comb += [ + Record.connect(rx.source, echo.sink), + Record.connect(echo.source, tx.sink) + ] + ip_port = ip.crossbar.get_port(icmp_protocol) + self.comb += [ + Record.connect(tx.source, ip_port.sink), + Record.connect(ip_port.source, rx.sink) + ] diff --git a/misoclib/liteeth/core/ip/__init__.py b/misoclib/liteeth/core/ip/__init__.py new file mode 100644 index 00000000..4e24f186 --- /dev/null +++ b/misoclib/liteeth/core/ip/__init__.py @@ -0,0 +1,180 @@ +from liteeth.common import * +from liteeth.generic import * +from liteeth.core.ip.checksum import * +from liteeth.core.ip.crossbar import * +from liteeth.generic.depacketizer import LiteEthDepacketizer +from liteeth.generic.packetizer import LiteEthPacketizer + +class LiteEthIPV4Packetizer(LiteEthPacketizer): + def __init__(self): + LiteEthPacketizer.__init__(self, + eth_ipv4_description(8), + eth_mac_description(8), + ipv4_header, + ipv4_header_len) + +class LiteEthIPTX(Module): + def __init__(self, mac_address, ip_address, arp_table): + self.sink = sink = Sink(eth_ipv4_user_description(8)) + self.source = source = Source(eth_mac_description(8)) + self.target_unreachable = Signal() + ### + self.submodules.checksum = checksum = LiteEthIPV4Checksum(skip_checksum=True) + self.comb += [ + checksum.ce.eq(sink.stb & sink.sop), + checksum.reset.eq(source.stb & source.eop & source.ack) + ] + + self.submodules.packetizer = packetizer = LiteEthIPV4Packetizer() + self.comb += [ + packetizer.sink.stb.eq(sink.stb & checksum.done), + packetizer.sink.sop.eq(sink.sop), + packetizer.sink.eop.eq(sink.eop), + sink.ack.eq(packetizer.sink.ack & checksum.done), + packetizer.sink.target_ip.eq(sink.ip_address), + packetizer.sink.protocol.eq(sink.protocol), + packetizer.sink.total_length.eq(sink.length + (0x5*4)), + packetizer.sink.version.eq(0x4), # ipv4 + packetizer.sink.ihl.eq(0x5), # 20 bytes + packetizer.sink.identification.eq(0), + packetizer.sink.ttl.eq(0x80), + packetizer.sink.sender_ip.eq(ip_address), + packetizer.sink.data.eq(sink.data), + checksum.header.eq(packetizer.header), + packetizer.sink.checksum.eq(checksum.value) + ] + + target_mac = Signal(48) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + packetizer.source.ack.eq(1), + If(packetizer.source.stb & packetizer.source.sop, + packetizer.source.ack.eq(0), + NextState("SEND_MAC_ADDRESS_REQUEST") + ) + ) + self.comb += arp_table.request.ip_address.eq(sink.ip_address) + fsm.act("SEND_MAC_ADDRESS_REQUEST", + arp_table.request.stb.eq(1), + If(arp_table.request.stb & arp_table.request.ack, + NextState("WAIT_MAC_ADDRESS_RESPONSE") + ) + ) + fsm.act("WAIT_MAC_ADDRESS_RESPONSE", + If(arp_table.response.stb, + arp_table.response.ack.eq(1), + If(arp_table.response.failed, + self.target_unreachable.eq(1), + NextState("DROP"), + ).Else( + NextState("SEND") + ) + ) + ) + self.sync += \ + If(arp_table.response.stb, + target_mac.eq(arp_table.response.mac_address) + ) + fsm.act("SEND", + Record.connect(packetizer.source, source), + source.ethernet_type.eq(ethernet_type_ip), + source.target_mac.eq(target_mac), + source.sender_mac.eq(mac_address), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + fsm.act("DROP", + packetizer.source.ack.eq(1), + If(packetizer.source.stb & packetizer.source.eop & packetizer.source.ack, + NextState("IDLE") + ) + ) + +class LiteEthIPV4Depacketizer(LiteEthDepacketizer): + def __init__(self): + LiteEthDepacketizer.__init__(self, + eth_mac_description(8), + eth_ipv4_description(8), + ipv4_header, + ipv4_header_len) + +class LiteEthIPRX(Module): + def __init__(self, mac_address, ip_address): + self.sink = sink = Sink(eth_mac_description(8)) + self.source = source = Source(eth_ipv4_user_description(8)) + ### + self.submodules.depacketizer = depacketizer = LiteEthIPV4Depacketizer() + self.comb += Record.connect(sink, depacketizer.sink) + + self.submodules.checksum = checksum = LiteEthIPV4Checksum(skip_checksum=False) + self.comb += [ + checksum.header.eq(depacketizer.header), + checksum.reset.eq(depacketizer.source.stb & depacketizer.source.eop), + checksum.ce.eq(depacketizer.source.stb & depacketizer.source.sop) + ] + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & depacketizer.source.sop, + depacketizer.source.ack.eq(0), + NextState("CHECK") + ) + ) + valid = Signal() + self.sync += valid.eq( + depacketizer.source.stb & + (depacketizer.source.target_ip == ip_address) & + (depacketizer.source.version == 0x4) & + (depacketizer.source.ihl == 0x5) & + (checksum.value == 0) + ) + + fsm.act("CHECK", + If(checksum.done, + If(valid, + NextState("PRESENT") + ).Else( + NextState("DROP") + ) + ) + ) + self.comb += [ + source.sop.eq(depacketizer.source.sop), + source.eop.eq(depacketizer.source.eop), + source.length.eq(depacketizer.source.total_length - (0x5*4)), + source.protocol.eq(depacketizer.source.protocol), + source.ip_address.eq(depacketizer.source.sender_ip), + source.data.eq(depacketizer.source.data), + source.error.eq(depacketizer.source.error) + ] + fsm.act("PRESENT", + source.stb.eq(depacketizer.source.stb), + depacketizer.source.ack.eq(source.ack), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + fsm.act("DROP", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & depacketizer.source.eop & depacketizer.source.ack, + NextState("IDLE") + ) + ) + +class LiteEthIP(Module): + def __init__(self, mac, mac_address, ip_address, arp_table): + self.submodules.tx = tx = LiteEthIPTX(mac_address, ip_address, arp_table) + self.submodules.rx = rx = LiteEthIPRX(mac_address, ip_address) + mac_port = mac.crossbar.get_port(ethernet_type_ip) + self.comb += [ + Record.connect(tx.source, mac_port.sink), + Record.connect(mac_port.source, rx.sink) + ] + self.submodules.crossbar = crossbar = LiteEthIPV4Crossbar() + self.comb += [ + Record.connect(crossbar.master.source, tx.sink), + Record.connect(rx.source, crossbar.master.sink) + ] diff --git a/misoclib/liteeth/core/ip/checksum.py b/misoclib/liteeth/core/ip/checksum.py new file mode 100644 index 00000000..f0e187e6 --- /dev/null +++ b/misoclib/liteeth/core/ip/checksum.py @@ -0,0 +1,43 @@ +from liteeth.common import * +from liteeth.generic import * + +class LiteEthIPV4Checksum(Module): + def __init__(self, words_per_clock_cycle=1, skip_checksum=False): + self.reset = Signal() # XXX FIXME InsertReset generates incorrect verilog + self.ce = Signal() # XXX FIXME InsertCE generates incorrect verilog + self.header = Signal(ipv4_header_len*8) + self.value = Signal(16) + self.done = Signal() + ### + s = Signal(17) + r = Signal(17) + n_cycles = 0 + for i in range(ipv4_header_len//2): + if skip_checksum and (i == ipv4_header["checksum"].byte//2): + pass + else: + s_next = Signal(17) + r_next = Signal(17) + self.comb += s_next.eq(r + self.header[i*16:(i+1)*16]) + r_next_eq = r_next.eq(Cat(s_next[:16]+s_next[16], Signal())) + if (i%words_per_clock_cycle) != 0: + self.comb += r_next_eq + else: + self.sync += \ + If(self.reset, + r_next.eq(0) + ).Elif(self.ce & ~self.done, + r_next_eq + ) + n_cycles += 1 + s, r = s_next, r_next + self.comb += self.value.eq(~Cat(r[8:16], r[:8])) + + if not skip_checksum: + n_cycles += 1 + self.submodules.counter = counter = Counter(max=n_cycles+1) + self.comb += [ + counter.reset.eq(self.reset), + counter.ce.eq(self.ce & ~self.done), + self.done.eq(counter.value == n_cycles) + ] diff --git a/misoclib/liteeth/core/ip/crossbar.py b/misoclib/liteeth/core/ip/crossbar.py new file mode 100644 index 00000000..19e88b2e --- /dev/null +++ b/misoclib/liteeth/core/ip/crossbar.py @@ -0,0 +1,30 @@ +from liteeth.common import * +from liteeth.generic import * +from liteeth.generic.crossbar import LiteEthCrossbar + +class LiteEthIPV4MasterPort: + def __init__(self, dw): + self.dw = dw + self.source = Source(eth_ipv4_user_description(dw)) + self.sink = Sink(eth_ipv4_user_description(dw)) + +class LiteEthIPV4SlavePort: + def __init__(self, dw): + self.dw = dw + self.sink = Sink(eth_ipv4_user_description(dw)) + self.source = Source(eth_ipv4_user_description(dw)) + +class LiteEthIPV4UserPort(LiteEthIPV4SlavePort): + def __init__(self, dw): + LiteEthIPV4SlavePort.__init__(self, dw) + +class LiteEthIPV4Crossbar(LiteEthCrossbar): + def __init__(self): + LiteEthCrossbar.__init__(self, LiteEthIPV4MasterPort, "protocol") + + def get_port(self, protocol): + if protocol in self.users.keys(): + raise ValueError("Protocol {0:#x} already assigned".format(protocol)) + port = LiteEthIPV4UserPort(8) + self.users[protocol] = port + return port diff --git a/misoclib/liteeth/core/tty/__init__.py b/misoclib/liteeth/core/tty/__init__.py new file mode 100644 index 00000000..a0102c4f --- /dev/null +++ b/misoclib/liteeth/core/tty/__init__.py @@ -0,0 +1,97 @@ +from liteeth.common import * +from liteeth.generic import * + +class LiteEthTTYTX(Module): + def __init__(self, ip_address, udp_port, fifo_depth=None): + self.sink = sink = Sink(eth_tty_description(8)) + self.source = source = Source(eth_udp_user_description(8)) + ### + if fifo_depth is None: + self.comb += [ + source.stb.eq(sink.stb), + source.sop.eq(1), + source.eop.eq(1), + source.length.eq(1), + source.data.eq(sink.data), + sink.ack.eq(source.ack) + ] + else: + self.submodules.fifo = fifo = SyncFIFO([("data", 8)], fifo_depth) + self.comb += Record.connect(sink, fifo.sink) + + self.submodules.level = level = FlipFlop(max=fifo_depth) + self.comb += level.d.eq(fifo.fifo.level) + + self.submodules.counter = counter = Counter(max=fifo_depth) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + If(fifo.source.stb, + level.ce.eq(1), + counter.reset.eq(1), + NextState("SEND") + ) + ) + fsm.act("SEND", + source.stb.eq(fifo.source.stb), + source.sop.eq(counter.value == 0), + If(level.q == 0, + source.eop.eq(1), + ).Else( + source.eop.eq(counter.value == (level.q-1)), + ), + source.src_port.eq(udp_port), + source.dst_port.eq(udp_port), + source.ip_address.eq(ip_address), + If(level.q == 0, + source.length.eq(1), + ).Else( + source.length.eq(level.q), + ), + source.data.eq(fifo.source.data), + fifo.source.ack.eq(source.ack), + If(source.stb & source.ack, + counter.ce.eq(1), + If(source.eop, + NextState("IDLE") + ) + ) + ) + +class LiteEthTTYRX(Module): + def __init__(self, ip_address, udp_port, fifo_depth=None): + self.sink = sink = Sink(eth_udp_user_description(8)) + self.source = source = Source(eth_tty_description(8)) + ### + valid = Signal() + self.comb += valid.eq( + (sink.ip_address == ip_address) & + (sink.dst_port == udp_port) + ) + if fifo_depth is None: + self.comb += [ + source.stb.eq(sink.stb & valid), + source.data.eq(sink.data), + sink.ack.eq(source.ack) + ] + else: + self.submodules.fifo = fifo = SyncFIFO([("data", 8)], fifo_depth) + self.comb += [ + fifo.sink.stb.eq(sink.stb & valid), + fifo.sink.data.eq(sink.data), + sink.ack.eq(fifo.sink.ack), + Record.connect(fifo.source, source) + ] + +class LiteEthTTY(Module): + def __init__(self, udp, ip_address, udp_port, + rx_fifo_depth=64, + tx_fifo_depth=64): + self.submodules.tx = tx = LiteEthTTYTX(ip_address, udp_port, tx_fifo_depth) + self.submodules.rx = rx = LiteEthTTYRX(ip_address, udp_port, rx_fifo_depth) + udp_port = udp.crossbar.get_port(udp_port, dw=8) + self.comb += [ + Record.connect(tx.source, udp_port.sink), + Record.connect(udp_port.source, rx.sink) + ] + self.sink, self.source = self.tx.sink, self.rx.source diff --git a/misoclib/liteeth/core/udp/__init__.py b/misoclib/liteeth/core/udp/__init__.py new file mode 100644 index 00000000..1873d76f --- /dev/null +++ b/misoclib/liteeth/core/udp/__init__.py @@ -0,0 +1,125 @@ +from liteeth.common import * +from liteeth.generic import * +from liteeth.core.udp.crossbar import * +from liteeth.generic.depacketizer import LiteEthDepacketizer +from liteeth.generic.packetizer import LiteEthPacketizer + +class LiteEthUDPPacketizer(LiteEthPacketizer): + def __init__(self): + LiteEthPacketizer.__init__(self, + eth_udp_description(8), + eth_ipv4_user_description(8), + udp_header, + udp_header_len) + +class LiteEthUDPTX(Module): + def __init__(self, ip_address): + self.sink = sink = Sink(eth_udp_user_description(8)) + self.source = source = Source(eth_ipv4_user_description(8)) + ### + self.submodules.packetizer = packetizer = LiteEthUDPPacketizer() + self.comb += [ + packetizer.sink.stb.eq(sink.stb), + packetizer.sink.sop.eq(sink.sop), + packetizer.sink.eop.eq(sink.eop), + sink.ack.eq(packetizer.sink.ack), + packetizer.sink.src_port.eq(sink.src_port), + packetizer.sink.dst_port.eq(sink.dst_port), + packetizer.sink.length.eq(sink.length + udp_header_len), + packetizer.sink.checksum.eq(0), # Disabled (MAC CRC is enough) + packetizer.sink.data.eq(sink.data) + ] + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + packetizer.source.ack.eq(1), + If(packetizer.source.stb & packetizer.source.sop, + packetizer.source.ack.eq(0), + NextState("SEND") + ) + ) + fsm.act("SEND", + Record.connect(packetizer.source, source), + source.length.eq(packetizer.sink.length), + source.protocol.eq(udp_protocol), + source.ip_address.eq(sink.ip_address), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + +class LiteEthUDPDepacketizer(LiteEthDepacketizer): + def __init__(self): + LiteEthDepacketizer.__init__(self, + eth_ipv4_user_description(8), + eth_udp_description(8), + udp_header, + udp_header_len) + +class LiteEthUDPRX(Module): + def __init__(self, ip_address): + self.sink = sink = Sink(eth_ipv4_user_description(8)) + self.source = source = Source(eth_udp_user_description(8)) + ### + self.submodules.depacketizer = depacketizer = LiteEthUDPDepacketizer() + self.comb += Record.connect(sink, depacketizer.sink) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & depacketizer.source.sop, + depacketizer.source.ack.eq(0), + NextState("CHECK") + ) + ) + valid = Signal() + self.sync += valid.eq( + depacketizer.source.stb & + (sink.protocol == udp_protocol) + ) + + fsm.act("CHECK", + If(valid, + NextState("PRESENT") + ).Else( + NextState("DROP") + ) + ) + self.comb += [ + source.sop.eq(depacketizer.source.sop), + source.eop.eq(depacketizer.source.eop), + source.src_port.eq(depacketizer.source.src_port), + source.dst_port.eq(depacketizer.source.dst_port), + source.ip_address.eq(sink.ip_address), + source.length.eq(depacketizer.source.length - udp_header_len), + source.data.eq(depacketizer.source.data), + source.error.eq(depacketizer.source.error) + ] + fsm.act("PRESENT", + source.stb.eq(depacketizer.source.stb), + depacketizer.source.ack.eq(source.ack), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + fsm.act("DROP", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & depacketizer.source.eop & depacketizer.source.ack, + NextState("IDLE") + ) + ) + +class LiteEthUDP(Module): + def __init__(self, ip, ip_address): + self.submodules.tx = tx = LiteEthUDPTX(ip_address) + self.submodules.rx = rx = LiteEthUDPRX(ip_address) + ip_port = ip.crossbar.get_port(udp_protocol) + self.comb += [ + Record.connect(tx.source, ip_port.sink), + Record.connect(ip_port.source, rx.sink) + ] + self.submodules.crossbar = crossbar = LiteEthUDPCrossbar() + self.comb += [ + Record.connect(crossbar.master.source, tx.sink), + Record.connect(rx.source, crossbar.master.sink) + ] diff --git a/misoclib/liteeth/core/udp/crossbar.py b/misoclib/liteeth/core/udp/crossbar.py new file mode 100644 index 00000000..82843498 --- /dev/null +++ b/misoclib/liteeth/core/udp/crossbar.py @@ -0,0 +1,47 @@ +from liteeth.common import * +from liteeth.generic import * + +from liteeth.generic.crossbar import LiteEthCrossbar + +class LiteEthUDPMasterPort: + def __init__(self, dw): + self.dw = dw + self.source = Source(eth_udp_user_description(dw)) + self.sink = Sink(eth_udp_user_description(dw)) + +class LiteEthUDPSlavePort: + def __init__(self, dw): + self.dw =dw + self.sink = Sink(eth_udp_user_description(dw)) + self.source = Source(eth_udp_user_description(dw)) + +class LiteEthUDPUserPort(LiteEthUDPSlavePort): + def __init__(self, dw): + LiteEthUDPSlavePort.__init__(self, dw) + +class LiteEthUDPCrossbar(LiteEthCrossbar): + def __init__(self): + LiteEthCrossbar.__init__(self, LiteEthUDPMasterPort, "dst_port") + + def get_port(self, udp_port, dw=8): + if udp_port in self.users.keys(): + raise ValueError("Port {0:#x} already assigned".format(udp_port)) + user_port = LiteEthUDPUserPort(dw) + internal_port = LiteEthUDPUserPort(8) + if dw != 8: + converter = Converter(eth_udp_user_description(user_port.dw), eth_udp_user_description(8)) + self.submodules += converter + self.comb += [ + Record.connect(user_port.sink, converter.sink), + Record.connect(converter.source, internal_port.sink) + ] + converter = Converter(eth_udp_user_description(8), eth_udp_user_description(user_port.dw)) + self.submodules += converter + self.comb += [ + Record.connect(internal_port.source, converter.sink), + Record.connect(converter.source, user_port.source) + ] + self.users[udp_port] = internal_port + else: + self.users[udp_port] = user_port + return user_port diff --git a/misoclib/liteeth/generic/__init__.py b/misoclib/liteeth/generic/__init__.py new file mode 100644 index 00000000..643e9872 --- /dev/null +++ b/misoclib/liteeth/generic/__init__.py @@ -0,0 +1,151 @@ +from liteeth.common import * + +# Generic classes +class Port: + def connect(self, port): + r = [ + Record.connect(self.source, port.sink), + Record.connect(port.source, self.sink) + ] + return r + +# Generic modules +@DecorateModule(InsertReset) +@DecorateModule(InsertCE) +class FlipFlop(Module): + def __init__(self, *args, **kwargs): + self.d = Signal(*args, **kwargs) + self.q = Signal(*args, **kwargs) + self.sync += self.q.eq(self.d) + +@DecorateModule(InsertReset) +@DecorateModule(InsertCE) +class Counter(Module): + def __init__(self, signal=None, **kwargs): + if signal is None: + self.value = Signal(**kwargs) + else: + self.value = signal + self.width = flen(self.value) + self.sync += self.value.eq(self.value+1) + +@DecorateModule(InsertReset) +@DecorateModule(InsertCE) +class Timeout(Module): + def __init__(self, length): + self.reached = Signal() + ### + value = Signal(max=length) + self.sync += If(~self.reached, value.eq(value+1)) + self.comb += self.reached.eq(value == (length-1)) + +class BufferizeEndpoints(ModuleDecorator): + def __init__(self, submodule, *args): + ModuleDecorator.__init__(self, submodule) + + endpoints = get_endpoints(submodule) + sinks = {} + sources = {} + for name, endpoint in endpoints.items(): + if name in args or len(args) == 0: + if isinstance(endpoint, Sink): + sinks.update({name : endpoint}) + elif isinstance(endpoint, Source): + sources.update({name : endpoint}) + + # add buffer on sinks + for name, sink in sinks.items(): + buf = Buffer(sink.description) + self.submodules += buf + setattr(self, name, buf.d) + self.comb += Record.connect(buf.q, sink) + + # add buffer on sources + for name, source in sources.items(): + buf = Buffer(source.description) + self.submodules += buf + self.comb += Record.connect(source, buf.d) + setattr(self, name, buf.q) + +class EndpointPacketStatus(Module): + def __init__(self, endpoint): + self.start = Signal() + self.done = Signal() + self.ongoing = Signal() + + ongoing = Signal() + self.comb += [ + self.start.eq(endpoint.stb & endpoint.sop & endpoint.ack), + self.done.eq(endpoint.stb & endpoint.eop & endpoint.ack) + ] + self.sync += \ + If(self.start, + ongoing.eq(1) + ).Elif(self.done, + ongoing.eq(0) + ) + self.comb += self.ongoing.eq((self.start | ongoing) & ~self.done) + +class PacketBuffer(Module): + def __init__(self, description, data_depth, cmd_depth=4, almost_full=None): + self.sink = sink = Sink(description) + self.source = source = Source(description) + + ### + sink_status = EndpointPacketStatus(self.sink) + source_status = EndpointPacketStatus(self.source) + self.submodules += sink_status, source_status + + # store incoming packets + # cmds + def cmd_description(): + layout = [("error", 1)] + return EndpointDescription(layout) + cmd_fifo = SyncFIFO(cmd_description(), cmd_depth) + self.submodules += cmd_fifo + self.comb += cmd_fifo.sink.stb.eq(sink_status.done) + if hasattr(sink, "error"): + self.comb += cmd_fifo.sink.error.eq(sink.error) + + # data + data_fifo = SyncFIFO(description, data_depth, buffered=True) + self.submodules += data_fifo + self.comb += [ + Record.connect(self.sink, data_fifo.sink), + data_fifo.sink.stb.eq(self.sink.stb & cmd_fifo.sink.ack), + self.sink.ack.eq(data_fifo.sink.ack & cmd_fifo.sink.ack), + ] + + # output packets + self.fsm = fsm = FSM(reset_state="IDLE") + self.submodules += fsm + fsm.act("IDLE", + If(cmd_fifo.source.stb, + NextState("SEEK_SOP") + ) + ) + fsm.act("SEEK_SOP", + If(~data_fifo.source.sop, + data_fifo.source.ack.eq(1) + ).Else( + NextState("OUTPUT") + ) + ) + if hasattr(source, "error"): + source_error = self.source.error + else: + source_error = Signal() + + fsm.act("OUTPUT", + Record.connect(data_fifo.source, self.source), + source_error.eq(cmd_fifo.source.error), + If(source_status.done, + cmd_fifo.source.ack.eq(1), + NextState("IDLE") + ) + ) + + # compute almost full + if almost_full is not None: + self.almost_full = Signal() + self.comb += self.almost_full.eq(data_fifo.fifo.level > almost_full) diff --git a/misoclib/liteeth/generic/arbiter.py b/misoclib/liteeth/generic/arbiter.py new file mode 100644 index 00000000..ca77377e --- /dev/null +++ b/misoclib/liteeth/generic/arbiter.py @@ -0,0 +1,27 @@ +from migen.fhdl.std import * +from migen.genlib.roundrobin import * +from migen.genlib.record import * + +class Arbiter(Module): + def __init__(self, sources, sink): + if len(sources) == 0: + pass + elif len(sources) == 1: + self.grant = Signal() + self.comb += Record.connect(sources.pop(), sink) + else: + self.submodules.rr = RoundRobin(len(sources)) + self.grant = self.rr.grant + cases = {} + for i, source in enumerate(sources): + sop = Signal() + eop = Signal() + ongoing = Signal() + self.comb += [ + sop.eq(source.stb & source.sop), + eop.eq(source.stb & source.eop & source.ack), + ] + self.sync += ongoing.eq((sop | ongoing) & ~eop) + self.comb += self.rr.request[i].eq((sop | ongoing) & ~eop) + cases[i] = [Record.connect(source, sink)] + self.comb += Case(self.grant, cases) diff --git a/misoclib/liteeth/generic/crossbar.py b/misoclib/liteeth/generic/crossbar.py new file mode 100644 index 00000000..b60da949 --- /dev/null +++ b/misoclib/liteeth/generic/crossbar.py @@ -0,0 +1,31 @@ +from collections import OrderedDict + +from liteeth.common import * +from liteeth.generic import * +from liteeth.generic.arbiter import Arbiter +from liteeth.generic.dispatcher import Dispatcher + +class LiteEthCrossbar(Module): + def __init__(self, master_port, dispatch_param): + self.users = OrderedDict() + self.master = master_port(8) + self.dispatch_param = dispatch_param + + # overload this in derived classes + def get_port(self, *args, **kwargs): + pass + + def do_finalize(self): + # TX arbitrate + sinks = [port.sink for port in self.users.values()] + self.submodules.arbiter = Arbiter(sinks, self.master.source) + + # RX dispatch + sources = [port.source for port in self.users.values()] + self.submodules.dispatcher = Dispatcher(self.master.sink, sources, one_hot=True) + cases = {} + cases["default"] = self.dispatcher.sel.eq(0) + for i, (k, v) in enumerate(self.users.items()): + cases[k] = self.dispatcher.sel.eq(2**i) + self.comb += \ + Case(getattr(self.master.sink, self.dispatch_param), cases) diff --git a/misoclib/liteeth/generic/depacketizer.py b/misoclib/liteeth/generic/depacketizer.py new file mode 100644 index 00000000..2d8ca86c --- /dev/null +++ b/misoclib/liteeth/generic/depacketizer.py @@ -0,0 +1,84 @@ +from liteeth.common import * +from liteeth.generic import * + +def _decode_header(h_dict, h_signal, obj): + r = [] + for k, v in sorted(h_dict.items()): + start = v.byte*8+v.offset + end = start+v.width + r.append(getattr(obj, k).eq(reverse_bytes(h_signal[start:end]))) + return r + +class LiteEthDepacketizer(Module): + def __init__(self, sink_description, source_description, header_type, header_length): + self.sink = sink = Sink(sink_description) + self.source = source = Source(source_description) + self.header = Signal(header_length*8) + ### + dw = flen(sink.data) + + header_words = (header_length*8)//dw + + shift = Signal() + counter = Counter(max=max(header_words, 2)) + self.submodules += counter + + if header_words == 1: + self.sync += \ + If(shift, + self.header.eq(sink.data) + ) + else: + self.sync += \ + If(shift, + self.header.eq(Cat(self.header[dw:], sink.data)) + ) + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + if header_words == 1: + idle_next_state = "COPY" + else: + idle_next_state = "RECEIVE_HEADER" + + fsm.act("IDLE", + sink.ack.eq(1), + counter.reset.eq(1), + If(sink.stb, + shift.eq(1), + NextState(idle_next_state) + ) + ) + if header_words != 1: + fsm.act("RECEIVE_HEADER", + sink.ack.eq(1), + If(sink.stb, + counter.ce.eq(1), + shift.eq(1), + If(counter.value == header_words-2, + NextState("COPY") + ) + ) + ) + no_payload = Signal() + self.sync += \ + If(fsm.before_entering("COPY"), + source.sop.eq(1), + no_payload.eq(sink.eop) + ).Elif(source.stb & source.ack, + source.sop.eq(0) + ) + self.comb += [ + source.eop.eq(sink.eop | no_payload), + source.data.eq(sink.data), + source.error.eq(sink.error), + _decode_header(header_type, self.header, source) + ] + fsm.act("COPY", + sink.ack.eq(source.ack), + source.stb.eq(sink.stb | no_payload), + If(source.stb & source.ack & source.eop, + NextState("IDLE") + ) + ) diff --git a/misoclib/liteeth/generic/dispatcher.py b/misoclib/liteeth/generic/dispatcher.py new file mode 100644 index 00000000..3acb9f90 --- /dev/null +++ b/misoclib/liteeth/generic/dispatcher.py @@ -0,0 +1,39 @@ +from migen.fhdl.std import * +from migen.genlib.record import * + +class Dispatcher(Module): + def __init__(self, source, sinks, one_hot=False): + if len(sinks) == 0: + self.sel = Signal() + elif len(sinks) == 1: + self.comb += Record.connect(source, sinks.pop()) + self.sel = Signal() + else: + if one_hot: + self.sel = Signal(len(sinks)) + else: + self.sel = Signal(max=len(sinks)) + ### + sop = Signal() + self.comb += sop.eq(source.stb & source.sop) + sel = Signal(flen(self.sel)) + sel_r = Signal(flen(self.sel)) + self.sync += \ + If(sop, + sel_r.eq(self.sel) + ) + self.comb += \ + If(sop, + sel.eq(self.sel) + ).Else( + sel.eq(sel_r) + ) + cases = {} + for i, sink in enumerate(sinks): + if one_hot: + idx = 2**i + else: + idx = i + cases[idx] = [Record.connect(source, sink)] + cases["default"] = [source.ack.eq(1)] + self.comb += Case(sel, cases) diff --git a/misoclib/liteeth/generic/packetizer.py b/misoclib/liteeth/generic/packetizer.py new file mode 100644 index 00000000..418da814 --- /dev/null +++ b/misoclib/liteeth/generic/packetizer.py @@ -0,0 +1,92 @@ +from liteeth.common import * +from liteeth.generic import * + +def _encode_header(h_dict, h_signal, obj): + r = [] + for k, v in sorted(h_dict.items()): + start = v.byte*8+v.offset + end = start+v.width + r.append(h_signal[start:end].eq(reverse_bytes(getattr(obj, k)))) + return r + +class LiteEthPacketizer(Module): + def __init__(self, sink_description, source_description, header_type, header_length): + self.sink = sink = Sink(sink_description) + self.source = source = Source(source_description) + self.header = Signal(header_length*8) + ### + dw = flen(self.sink.data) + + header_reg = Signal(header_length*8) + header_words = (header_length*8)//dw + load = Signal() + shift = Signal() + counter = Counter(max=max(header_words, 2)) + self.submodules += counter + + self.comb += _encode_header(header_type, self.header, sink) + if header_words == 1: + self.sync += [ + If(load, + header_reg.eq(self.header) + ) + ] + else: + self.sync += [ + If(load, + header_reg.eq(self.header) + ).Elif(shift, + header_reg.eq(Cat(header_reg[dw:], Signal(dw))) + ) + ] + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + if header_words == 1: + idle_next_state = "COPY" + else: + idle_next_state = "SEND_HEADER" + + fsm.act("IDLE", + sink.ack.eq(1), + counter.reset.eq(1), + If(sink.stb & sink.sop, + sink.ack.eq(0), + source.stb.eq(1), + source.sop.eq(1), + source.eop.eq(0), + source.data.eq(self.header[:dw]), + If(source.stb & source.ack, + load.eq(1), + NextState(idle_next_state) + ) + ) + ) + if header_words != 1: + fsm.act("SEND_HEADER", + source.stb.eq(1), + source.sop.eq(0), + source.eop.eq(0), + source.data.eq(header_reg[dw:2*dw]), + If(source.stb & source.ack, + shift.eq(1), + counter.ce.eq(1), + If(counter.value == header_words-2, + NextState("COPY") + ) + ) + ) + fsm.act("COPY", + source.stb.eq(sink.stb), + source.sop.eq(0), + source.eop.eq(sink.eop), + source.data.eq(sink.data), + source.error.eq(sink.error), + If(source.stb & source.ack, + sink.ack.eq(1), + If(source.eop, + NextState("IDLE") + ) + ) + ) diff --git a/misoclib/liteeth/mac/__init__.py b/misoclib/liteeth/mac/__init__.py new file mode 100644 index 00000000..1517b7bd --- /dev/null +++ b/misoclib/liteeth/mac/__init__.py @@ -0,0 +1,33 @@ +from liteeth.common import * +from liteeth.generic import * +from liteeth.mac.common import * +from liteeth.mac.core import LiteEthMACCore +from liteeth.mac.frontend.wishbone import LiteEthMACWishboneInterface + +class LiteEthMAC(Module, AutoCSR): + def __init__(self, phy, dw, interface="crossbar", endianness="big", + with_hw_preamble_crc=True): + self.submodules.core = LiteEthMACCore(phy, dw, endianness, with_hw_preamble_crc) + self.csrs = [] + if interface == "crossbar": + self.submodules.crossbar = LiteEthMACCrossbar() + self.submodules.packetizer = LiteEthMACPacketizer() + self.submodules.depacketizer = LiteEthMACDepacketizer() + self.comb += [ + Record.connect(self.crossbar.master.source, self.packetizer.sink), + Record.connect(self.packetizer.source, self.core.sink), + Record.connect(self.core.source, self.depacketizer.sink), + Record.connect(self.depacketizer.source, self.crossbar.master.sink) + ] + elif interface == "wishbone": + self.submodules.interface = LiteEthMACWishboneInterface(dw, 2, 2) + self.comb += Port.connect(self.interface, self.core) + self.ev, self.bus = self.interface.sram.ev, self.interface.bus + self.csrs = self.interface.get_csrs() + self.core.get_csrs() + elif interface == "dma": + raise NotImplementedError + else: + raise ValueError(interface + " not supported by LiteEthMac!") + + def get_csrs(self): + return self.csrs diff --git a/misoclib/liteeth/mac/common.py b/misoclib/liteeth/mac/common.py new file mode 100644 index 00000000..3a68c632 --- /dev/null +++ b/misoclib/liteeth/mac/common.py @@ -0,0 +1,46 @@ +from liteeth.common import * +from liteeth.generic import * +from liteeth.generic.depacketizer import LiteEthDepacketizer +from liteeth.generic.packetizer import LiteEthPacketizer +from liteeth.generic.crossbar import LiteEthCrossbar + +class LiteEthMACDepacketizer(LiteEthDepacketizer): + def __init__(self): + LiteEthDepacketizer.__init__(self, + eth_phy_description(8), + eth_mac_description(8), + mac_header, + mac_header_len) + +class LiteEthMACPacketizer(LiteEthPacketizer): + def __init__(self): + LiteEthPacketizer.__init__(self, + eth_mac_description(8), + eth_phy_description(8), + mac_header, + mac_header_len) + +class LiteEthMACMasterPort: + def __init__(self, dw): + self.source = Source(eth_mac_description(dw)) + self.sink = Sink(eth_mac_description(dw)) + +class LiteEthMACSlavePort: + def __init__(self, dw): + self.sink = Sink(eth_mac_description(dw)) + self.source = Source(eth_mac_description(dw)) + +class LiteEthMACUserPort(LiteEthMACSlavePort): + def __init__(self, dw): + LiteEthMACSlavePort.__init__(self, dw) + +class LiteEthMACCrossbar(LiteEthCrossbar): + def __init__(self): + LiteEthCrossbar.__init__(self, LiteEthMACMasterPort, "ethernet_type") + + def get_port(self, ethernet_type): + port = LiteEthMACUserPort(8) + if ethernet_type in self.users.keys(): + raise ValueError("Ethernet type {0:#x} already assigned".format(ethernet_type)) + self.users[ethernet_type] = port + return port diff --git a/misoclib/liteeth/mac/core/__init__.py b/misoclib/liteeth/mac/core/__init__.py new file mode 100644 index 00000000..34a21ad1 --- /dev/null +++ b/misoclib/liteeth/mac/core/__init__.py @@ -0,0 +1,83 @@ +from liteeth.common import * +from liteeth.generic import * +from liteeth.mac.core import gap, preamble, crc, padding, last_be + +class LiteEthMACCore(Module, AutoCSR): + def __init__(self, phy, dw, endianness="big", with_hw_preamble_crc=True): + if dw < phy.dw: + raise ValueError("Core data width({}) must be larger than PHY data width({})".format(dw, phy.dw)) + + rx_pipeline = [phy] + tx_pipeline = [phy] + + # Interpacket gap + tx_gap_inserter = gap.LiteEthMACGap(phy.dw) + rx_gap_checker = gap.LiteEthMACGap(phy.dw, ack_on_gap=True) + self.submodules += RenameClockDomains(tx_gap_inserter, "eth_tx") + self.submodules += RenameClockDomains(rx_gap_checker, "eth_rx") + + tx_pipeline += [tx_gap_inserter] + rx_pipeline += [rx_gap_checker] + + # Preamble / CRC + if with_hw_preamble_crc: + self._hw_preamble_crc = CSRStatus(reset=1) + # Preamble insert/check + preamble_inserter = preamble.LiteEthMACPreambleInserter(phy.dw) + preamble_checker = preamble.LiteEthMACPreambleChecker(phy.dw) + self.submodules += RenameClockDomains(preamble_inserter, "eth_tx") + self.submodules += RenameClockDomains(preamble_checker, "eth_rx") + + # CRC insert/check + crc32_inserter = crc.LiteEthMACCRC32Inserter(eth_phy_description(phy.dw)) + crc32_checker = crc.LiteEthMACCRC32Checker(eth_phy_description(phy.dw)) + self.submodules += RenameClockDomains(crc32_inserter, "eth_tx") + self.submodules += RenameClockDomains(crc32_checker, "eth_rx") + + tx_pipeline += [preamble_inserter, crc32_inserter] + rx_pipeline += [preamble_checker, crc32_checker] + + # Padding + padding_inserter = padding.LiteEthMACPaddingInserter(phy.dw, 60) + padding_checker = padding.LiteEthMACPaddingChecker(phy.dw, 60) + self.submodules += RenameClockDomains(padding_inserter, "eth_tx") + self.submodules += RenameClockDomains(padding_checker, "eth_rx") + + tx_pipeline += [padding_inserter] + rx_pipeline += [padding_checker] + + # Delimiters + if dw != 8: + tx_last_be = last_be.LiteEthMACTXLastBE(phy.dw) + rx_last_be = last_be.LiteEthMACRXLastBE(phy.dw) + self.submodules += RenameClockDomains(tx_last_be, "eth_tx") + self.submodules += RenameClockDomains(rx_last_be, "eth_rx") + + tx_pipeline += [tx_last_be] + rx_pipeline += [rx_last_be] + + # Converters + if dw != phy.dw: + reverse = endianness == "big" + tx_converter = Converter(eth_phy_description(dw), eth_phy_description(phy.dw), reverse=reverse) + rx_converter = Converter(eth_phy_description(phy.dw), eth_phy_description(dw), reverse=reverse) + self.submodules += RenameClockDomains(tx_converter, "eth_tx") + self.submodules += RenameClockDomains(rx_converter, "eth_rx") + + tx_pipeline += [tx_converter] + rx_pipeline += [rx_converter] + + # Cross Domain Crossing + tx_cdc = AsyncFIFO(eth_phy_description(dw), 64) + rx_cdc = AsyncFIFO(eth_phy_description(dw), 64) + self.submodules += RenameClockDomains(tx_cdc, {"write": "sys", "read": "eth_tx"}) + self.submodules += RenameClockDomains(rx_cdc, {"write": "eth_rx", "read": "sys"}) + + tx_pipeline += [tx_cdc] + rx_pipeline += [rx_cdc] + + # Graph + self.submodules.tx_pipeline = Pipeline(*reversed(tx_pipeline)) + self.submodules.rx_pipeline = Pipeline(*rx_pipeline) + + self.sink, self.source = self.tx_pipeline.sink, self.rx_pipeline.source diff --git a/misoclib/liteeth/mac/core/crc.py b/misoclib/liteeth/mac/core/crc.py new file mode 100644 index 00000000..80321e4c --- /dev/null +++ b/misoclib/liteeth/mac/core/crc.py @@ -0,0 +1,277 @@ +from liteeth.common import * +from liteeth.generic import * + +class LiteEthMACCRCEngine(Module): + """Cyclic Redundancy Check Engine + + Compute next CRC value from last CRC value and data input using + an optimized asynchronous LFSR. + + Parameters + ---------- + data_width : int + Width of the data bus. + width : int + Width of the CRC. + polynom : int + Polynom of the CRC (ex: 0x04C11DB7 for IEEE 802.3 CRC) + + Attributes + ---------- + data : in + Data input. + last : in + last CRC value. + next : + next CRC value. + """ + def __init__(self, data_width, width, polynom): + self.data = Signal(data_width) + self.last = Signal(width) + self.next = Signal(width) + + ### + + def _optimize_eq(l): + """ + Replace even numbers of XORs in the equation + with an equivalent XOR + """ + d = OrderedDict() + for e in l: + if e in d: + d[e] += 1 + else: + d[e] = 1 + r = [] + for key, value in d.items(): + if value%2 != 0: + r.append(key) + return r + + # compute and optimize CRC's LFSR + curval = [[("state", i)] for i in range(width)] + for i in range(data_width): + feedback = curval.pop() + [("din", i)] + for j in range(width-1): + if (polynom & (1<<(j+1))): + curval[j] += feedback + curval[j] = _optimize_eq(curval[j]) + curval.insert(0, feedback) + + # implement logic + for i in range(width): + xors = [] + for t, n in curval[i]: + if t == "state": + xors += [self.last[n]] + elif t == "din": + xors += [self.data[n]] + self.comb += self.next[i].eq(optree("^", xors)) + +@DecorateModule(InsertReset) +@DecorateModule(InsertCE) +class LiteEthMACCRC32(Module): + """IEEE 802.3 CRC + + Implement an IEEE 802.3 CRC generator/checker. + + Parameters + ---------- + data_width : int + Width of the data bus. + + Attributes + ---------- + d : in + Data input. + value : out + CRC value (used for generator). + error : out + CRC error (used for checker). + """ + width = 32 + polynom = 0x04C11DB7 + init = 2**width-1 + check = 0xC704DD7B + def __init__(self, data_width): + self.data = Signal(data_width) + self.value = Signal(self.width) + self.error = Signal() + + ### + + self.submodules.engine = LiteEthMACCRCEngine(data_width, self.width, self.polynom) + reg = Signal(self.width, reset=self.init) + self.sync += reg.eq(self.engine.next) + self.comb += [ + self.engine.data.eq(self.data), + self.engine.last.eq(reg), + + self.value.eq(~reg[::-1]), + self.error.eq(self.engine.next != self.check) + ] + +class LiteEthMACCRCInserter(Module): + """CRC Inserter + + Append a CRC at the end of each packet. + + Parameters + ---------- + description : description + description of the dataflow. + + Attributes + ---------- + sink : in + Packets input without CRC. + source : out + Packets output with CRC. + """ + def __init__(self, crc_class, description): + self.sink = sink = Sink(description) + self.source = source = Source(description) + self.busy = Signal() + + ### + + dw = flen(sink.data) + crc = crc_class(dw) + fsm = FSM(reset_state="IDLE") + self.submodules += crc, fsm + + fsm.act("IDLE", + crc.reset.eq(1), + sink.ack.eq(1), + If(sink.stb & sink.sop, + sink.ack.eq(0), + NextState("COPY"), + ) + ) + fsm.act("COPY", + crc.ce.eq(sink.stb & source.ack), + crc.data.eq(sink.data), + Record.connect(sink, source), + source.eop.eq(0), + If(sink.stb & sink.eop & source.ack, + NextState("INSERT"), + ) + ) + ratio = crc.width//dw + if ratio > 1: + cnt = Signal(max=ratio, reset=ratio-1) + cnt_done = Signal() + fsm.act("INSERT", + source.stb.eq(1), + chooser(crc.value, cnt, source.data, reverse=True), + If(cnt_done, + source.eop.eq(1), + If(source.ack, NextState("IDLE")) + ) + ) + self.comb += cnt_done.eq(cnt == 0) + self.sync += \ + If(fsm.ongoing("IDLE"), + cnt.eq(cnt.reset) + ).Elif(fsm.ongoing("INSERT") & ~cnt_done, + cnt.eq(cnt - source.ack) + ) + else: + fsm.act("INSERT", + source.stb.eq(1), + source.eop.eq(1), + source.data.eq(crc.value), + If(source.ack, NextState("IDLE")) + ) + self.comb += self.busy.eq(~fsm.ongoing("IDLE")) + +class LiteEthMACCRC32Inserter(LiteEthMACCRCInserter): + def __init__(self, description): + LiteEthMACCRCInserter.__init__(self, LiteEthMACCRC32, description) + +class LiteEthMACCRCChecker(Module): + """CRC Checker + + Check CRC at the end of each packet. + + Parameters + ---------- + description : description + description of the dataflow. + + Attributes + ---------- + sink : in + Packets input with CRC. + source : out + Packets output without CRC and "error" set to 0 + on eop when CRC OK / set to 1 when CRC KO. + """ + def __init__(self, crc_class, description): + self.sink = sink = Sink(description) + self.source = source = Source(description) + self.busy = Signal() + + ### + + dw = flen(sink.data) + crc = crc_class(dw) + self.submodules += crc + ratio = crc.width//dw + + error = Signal() + fifo = InsertReset(SyncFIFO(description, ratio + 1)) + self.submodules += fifo + + fsm = FSM(reset_state="RESET") + self.submodules += fsm + + fifo_in = Signal() + fifo_out = Signal() + fifo_full = Signal() + + self.comb += [ + fifo_full.eq(fifo.fifo.level == ratio), + fifo_in.eq(sink.stb & (~fifo_full | fifo_out)), + fifo_out.eq(source.stb & source.ack), + + Record.connect(sink, fifo.sink), + fifo.sink.stb.eq(fifo_in), + self.sink.ack.eq(fifo_in), + + source.stb.eq(sink.stb & fifo_full), + source.sop.eq(fifo.source.sop), + source.eop.eq(sink.eop), + fifo.source.ack.eq(fifo_out), + source.payload.eq(fifo.source.payload), + + source.error.eq(sink.error | crc.error), + ] + + fsm.act("RESET", + crc.reset.eq(1), + fifo.reset.eq(1), + NextState("IDLE"), + ) + fsm.act("IDLE", + crc.data.eq(sink.data), + If(sink.stb & sink.sop & sink.ack, + crc.ce.eq(1), + NextState("COPY") + ) + ) + fsm.act("COPY", + crc.data.eq(sink.data), + If(sink.stb & sink.ack, + crc.ce.eq(1), + If(sink.eop, + NextState("RESET") + ) + ) + ) + self.comb += self.busy.eq(~fsm.ongoing("IDLE")) + +class LiteEthMACCRC32Checker(LiteEthMACCRCChecker): + def __init__(self, description): + LiteEthMACCRCChecker.__init__(self, LiteEthMACCRC32, description) diff --git a/misoclib/liteeth/mac/core/gap.py b/misoclib/liteeth/mac/core/gap.py new file mode 100644 index 00000000..84f738aa --- /dev/null +++ b/misoclib/liteeth/mac/core/gap.py @@ -0,0 +1,26 @@ +from liteeth.common import * +from liteeth.generic import * + +class LiteEthMACGap(Module): + def __init__(self, dw, ack_on_gap=False): + self.sink = sink = Sink(eth_phy_description(dw)) + self.source = source = Source(eth_phy_description(dw)) + ### + gap = math.ceil(eth_interpacket_gap/(dw//8)) + self.submodules.counter = counter = Counter(max=gap) + + self.submodules.fsm = fsm = FSM(reset_state="COPY") + fsm.act("COPY", + counter.reset.eq(1), + Record.connect(sink, source), + If(sink.stb & sink.eop & sink.ack, + NextState("GAP") + ) + ) + fsm.act("GAP", + counter.ce.eq(1), + sink.ack.eq(int(ack_on_gap)), + If(counter.value == (gap-1), + NextState("COPY") + ) + ) diff --git a/misoclib/liteeth/mac/core/last_be.py b/misoclib/liteeth/mac/core/last_be.py new file mode 100644 index 00000000..2b67ad45 --- /dev/null +++ b/misoclib/liteeth/mac/core/last_be.py @@ -0,0 +1,38 @@ +from liteeth.common import * +from liteeth.generic import * + +class LiteEthMACTXLastBE(Module): + def __init__(self, dw): + self.sink = sink = Sink(eth_phy_description(dw)) + self.source = source = Source(eth_phy_description(dw)) + ### + ongoing = Signal() + self.sync += \ + If(sink.stb & sink.ack, + If(sink.sop, + ongoing.eq(1) + ).Elif(sink.last_be, + ongoing.eq(0) + ) + ) + self.comb += [ + source.stb.eq(sink.stb & (sink.sop | ongoing)), + source.sop.eq(sink.sop), + source.eop.eq(sink.last_be), + source.data.eq(sink.data), + sink.ack.eq(source.ack) + ] + +class LiteEthMACRXLastBE(Module): + def __init__(self, dw): + self.sink = sink = Sink(eth_phy_description(dw)) + self.source = source = Source(eth_phy_description(dw)) + ### + self.comb += [ + source.stb.eq(sink.stb), + source.sop.eq(sink.sop), + source.eop.eq(sink.eop), + source.data.eq(sink.data), + source.last_be.eq(sink.eop), + sink.ack.eq(source.ack) + ] diff --git a/misoclib/liteeth/mac/core/padding.py b/misoclib/liteeth/mac/core/padding.py new file mode 100644 index 00000000..12970fe4 --- /dev/null +++ b/misoclib/liteeth/mac/core/padding.py @@ -0,0 +1,47 @@ +from liteeth.common import * +from liteeth.generic import * + +class LiteEthMACPaddingInserter(Module): + def __init__(self, dw, packet_min_length): + self.sink = sink = Sink(eth_phy_description(dw)) + self.source = source = Source(eth_phy_description(dw)) + ### + packet_min_data = math.ceil(packet_min_length/(dw/8)) + + self.submodules.counter = counter = Counter(max=eth_mtu) + + self.submodules.fsm = fsm = FSM(reset_state="COPY") + fsm.act("COPY", + counter.reset.eq(sink.stb & sink.sop), + Record.connect(sink, source), + If(sink.stb & sink.ack, + counter.ce.eq(1), + If(sink.eop, + If(counter.value < (packet_min_data-1), + source.eop.eq(0), + NextState("PADDING") + ) + ) + ) + ) + fsm.act("PADDING", + source.stb.eq(1), + source.eop.eq(counter.value == (packet_min_data-1)), + source.data.eq(0), + If(source.ack, + counter.ce.eq(1), + If(source.eop, + NextState("COPY") + ) + ) + ) + +class LiteEthMACPaddingChecker(Module): + def __init__(self, dw, packet_min_length): + self.sink = sink = Sink(eth_phy_description(dw)) + self.source = source = Source(eth_phy_description(dw)) + ### + # XXX see if we should drop the packet when + # payload size < minimum ethernet payload size + self.comb += Record.connect(sink, source) + diff --git a/misoclib/liteeth/mac/core/preamble.py b/misoclib/liteeth/mac/core/preamble.py new file mode 100644 index 00000000..fd3cb975 --- /dev/null +++ b/misoclib/liteeth/mac/core/preamble.py @@ -0,0 +1,140 @@ +from liteeth.common import * +from liteeth.generic import * + +class LiteEthMACPreambleInserter(Module): + def __init__(self, dw): + self.sink = Sink(eth_phy_description(dw)) + self.source = Source(eth_phy_description(dw)) + + ### + + preamble = Signal(64, reset=eth_preamble) + cnt_max = (64//dw)-1 + cnt = Signal(max=cnt_max+1) + clr_cnt = Signal() + inc_cnt = Signal() + + self.sync += \ + If(clr_cnt, + cnt.eq(0) + ).Elif(inc_cnt, + cnt.eq(cnt+1) + ) + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + fsm.act("IDLE", + self.sink.ack.eq(1), + clr_cnt.eq(1), + If(self.sink.stb & self.sink.sop, + self.sink.ack.eq(0), + NextState("INSERT"), + ) + ) + fsm.act("INSERT", + self.source.stb.eq(1), + self.source.sop.eq(cnt==0), + chooser(preamble, cnt, self.source.data), + If(cnt == cnt_max, + If(self.source.ack, NextState("COPY")) + ).Else( + inc_cnt.eq(self.source.ack) + ) + ) + fsm.act("COPY", + Record.connect(self.sink, self.source), + self.source.sop.eq(0), + + If(self.sink.stb & self.sink.eop & self.source.ack, + NextState("IDLE"), + ) + ) + +class LiteEthMACPreambleChecker(Module): + def __init__(self, dw): + self.sink = Sink(eth_phy_description(dw)) + self.source = Source(eth_phy_description(dw)) + + ### + + preamble = Signal(64, reset=eth_preamble) + cnt_max = (64//dw) - 1 + cnt = Signal(max=cnt_max+1) + clr_cnt = Signal() + inc_cnt = Signal() + + self.sync += \ + If(clr_cnt, + cnt.eq(0) + ).Elif(inc_cnt, + cnt.eq(cnt+1) + ) + + discard = Signal() + clr_discard = Signal() + set_discard = Signal() + + self.sync += \ + If(clr_discard, + discard.eq(0) + ).Elif(set_discard, + discard.eq(1) + ) + + sop = Signal() + clr_sop = Signal() + set_sop = Signal() + self.sync += \ + If(clr_sop, + sop.eq(0) + ).Elif(set_sop, + sop.eq(1) + ) + + ref = Signal(dw) + match = Signal() + self.comb += [ + chooser(preamble, cnt, ref), + match.eq(self.sink.data == ref) + ] + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + fsm.act("IDLE", + self.sink.ack.eq(1), + clr_cnt.eq(1), + clr_discard.eq(1), + If(self.sink.stb & self.sink.sop, + clr_cnt.eq(0), + inc_cnt.eq(1), + clr_discard.eq(0), + set_discard.eq(~match), + NextState("CHECK"), + ) + ) + fsm.act("CHECK", + self.sink.ack.eq(1), + If(self.sink.stb, + set_discard.eq(~match), + If(cnt == cnt_max, + If(discard | (~match), + NextState("IDLE") + ).Else( + set_sop.eq(1), + NextState("COPY") + ) + ).Else( + inc_cnt.eq(1) + ) + ) + ) + fsm.act("COPY", + Record.connect(self.sink, self.source), + self.source.sop.eq(sop), + clr_sop.eq(self.source.stb & self.source.ack), + + If(self.source.stb & self.source.eop & self.source.ack, + NextState("IDLE"), + ) + ) diff --git a/misoclib/liteeth/mac/frontend/__init__.py b/misoclib/liteeth/mac/frontend/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/misoclib/liteeth/mac/frontend/sram.py b/misoclib/liteeth/mac/frontend/sram.py new file mode 100644 index 00000000..cffe71b4 --- /dev/null +++ b/misoclib/liteeth/mac/frontend/sram.py @@ -0,0 +1,254 @@ +from liteeth.common import * +from liteeth.generic import * + +from migen.bank.description import * +from migen.bank.eventmanager import * + +class LiteEthMACSRAMWriter(Module, AutoCSR): + def __init__(self, dw, depth, nslots=2): + self.sink = sink = Sink(eth_phy_description(dw)) + self.crc_error = Signal() + + slotbits = max(log2_int(nslots), 1) + lengthbits = log2_int(depth*4) # length in bytes + + self._slot = CSRStatus(slotbits) + self._length = CSRStatus(lengthbits) + + self.submodules.ev = EventManager() + self.ev.available = EventSourceLevel() + self.ev.finalize() + + ### + + # packet dropped if no slot available + sink.ack.reset = 1 + + # length computation + cnt = Signal(lengthbits) + clr_cnt = Signal() + inc_cnt = Signal() + inc_val = Signal(3) + self.comb += \ + If(sink.last_be[3], + inc_val.eq(1) + ).Elif(sink.last_be[2], + inc_val.eq(2) + ).Elif(sink.last_be[1], + inc_val.eq(3) + ).Else( + inc_val.eq(4) + ) + self.sync += \ + If(clr_cnt, + cnt.eq(0) + ).Elif(inc_cnt, + cnt.eq(cnt+inc_val) + ) + + # slot computation + slot = Signal(slotbits) + inc_slot = Signal() + self.sync += \ + If(inc_slot, + If(slot == nslots-1, + slot.eq(0), + ).Else( + slot.eq(slot+1) + ) + ) + ongoing = Signal() + discard = Signal() + + # status fifo + fifo = SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots) + self.submodules += fifo + + # fsm + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + fsm.act("IDLE", + inc_cnt.eq(sink.stb), + If(sink.stb & sink.sop, + ongoing.eq(1), + If(fifo.sink.ack, + NextState("WRITE") + ) + ) + ) + fsm.act("WRITE", + inc_cnt.eq(sink.stb), + ongoing.eq(1), + If(sink.stb & sink.eop, + If((sink.error & sink.last_be) != 0, + NextState("DISCARD") + ).Else( + NextState("TERMINATE") + ) + ) + ) + fsm.act("DISCARD", + clr_cnt.eq(1), + NextState("IDLE") + ) + fsm.act("TERMINATE", + clr_cnt.eq(1), + inc_slot.eq(1), + fifo.sink.stb.eq(1), + fifo.sink.slot.eq(slot), + fifo.sink.length.eq(cnt), + NextState("IDLE") + ) + + self.comb += [ + fifo.source.ack.eq(self.ev.available.clear), + self.ev.available.trigger.eq(fifo.source.stb), + self._slot.status.eq(fifo.source.slot), + self._length.status.eq(fifo.source.length), + ] + + # memory + mems = [None]*nslots + ports = [None]*nslots + for n in range(nslots): + mems[n] = Memory(dw, depth) + ports[n] = mems[n].get_port(write_capable=True) + self.specials += ports[n] + self.mems = mems + + cases = {} + for n, port in enumerate(ports): + cases[n] = [ + ports[n].adr.eq(cnt[2:]), + ports[n].dat_w.eq(sink.data), + If(sink.stb & ongoing, + ports[n].we.eq(0xf) + ) + ] + self.comb += Case(slot, cases) + + +class LiteEthMACSRAMReader(Module, AutoCSR): + def __init__(self, dw, depth, nslots=2): + self.source = source = Source(eth_phy_description(dw)) + + slotbits = max(log2_int(nslots), 1) + lengthbits = log2_int(depth*4) # length in bytes + self.lengthbits = lengthbits + + self._start = CSR() + self._ready = CSRStatus() + self._slot = CSRStorage(slotbits) + self._length = CSRStorage(lengthbits) + + self.submodules.ev = EventManager() + self.ev.done = EventSourcePulse() + self.ev.finalize() + + ### + + # command fifo + fifo = SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots) + self.submodules += fifo + self.comb += [ + fifo.sink.stb.eq(self._start.re), + fifo.sink.slot.eq(self._slot.storage), + fifo.sink.length.eq(self._length.storage), + self._ready.status.eq(fifo.sink.ack) + ] + + # length computation + cnt = Signal(lengthbits) + clr_cnt = Signal() + inc_cnt = Signal() + + self.sync += \ + If(clr_cnt, + cnt.eq(0) + ).Elif(inc_cnt, + cnt.eq(cnt+4) + ) + + # fsm + first = Signal() + last = Signal() + last_d = Signal() + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + fsm.act("IDLE", + clr_cnt.eq(1), + If(fifo.source.stb, + NextState("CHECK") + ) + ) + fsm.act("CHECK", + If(~last_d, + NextState("SEND"), + ).Else( + NextState("END"), + ) + ) + length_lsb = fifo.source.length[0:2] + fsm.act("SEND", + source.stb.eq(1), + source.sop.eq(first), + source.eop.eq(last), + If(last, + If(length_lsb == 3, + source.last_be.eq(0b0010) + ).Elif(length_lsb == 2, + source.last_be.eq(0b0100) + ).Elif(length_lsb == 1, + source.last_be.eq(0b1000) + ).Else( + source.last_be.eq(0b0001) + ) + ), + If(source.ack, + inc_cnt.eq(~last), + NextState("CHECK") + ) + ) + fsm.act("END", + fifo.source.ack.eq(1), + self.ev.done.trigger.eq(1), + NextState("IDLE") + ) + + # first/last computation + self.sync += [ + If(fsm.ongoing("IDLE"), + first.eq(1) + ).Elif(source.stb & source.ack, + first.eq(0) + ) + ] + self.comb += last.eq(cnt + 4 >= fifo.source.length) + self.sync += last_d.eq(last) + + # memory + rd_slot = fifo.source.slot + + mems = [None]*nslots + ports = [None]*nslots + for n in range(nslots): + mems[n] = Memory(dw, depth) + ports[n] = mems[n].get_port() + self.specials += ports[n] + self.mems = mems + + cases = {} + for n, port in enumerate(ports): + self.comb += ports[n].adr.eq(cnt[2:]) + cases[n] = [source.data.eq(port.dat_r)] + self.comb += Case(rd_slot, cases) + +class LiteEthMACSRAM(Module, AutoCSR): + def __init__(self, dw, depth, nrxslots, ntxslots): + self.submodules.writer = LiteEthMACSRAMWriter(dw, depth, nrxslots) + self.submodules.reader = LiteEthMACSRAMReader(dw, depth, ntxslots) + self.submodules.ev = SharedIRQ(self.writer.ev, self.reader.ev) + self.sink, self.source = self.writer.sink, self.reader.source diff --git a/misoclib/liteeth/mac/frontend/wishbone.py b/misoclib/liteeth/mac/frontend/wishbone.py new file mode 100644 index 00000000..a68fe382 --- /dev/null +++ b/misoclib/liteeth/mac/frontend/wishbone.py @@ -0,0 +1,39 @@ +from liteeth.common import * +from liteeth.generic import * +from liteeth.mac.frontend import sram + +from migen.bus import wishbone +from migen.fhdl.simplify import FullMemoryWE + +class LiteEthMACWishboneInterface(Module, AutoCSR): + def __init__(self, dw, nrxslots=2, ntxslots=2): + self.sink = Sink(eth_phy_description(dw)) + self.source = Source(eth_phy_description(dw)) + self.bus = wishbone.Interface() + ### + # storage in SRAM + sram_depth = buffer_depth//(dw//8) + self.submodules.sram = sram.LiteEthMACSRAM(dw, sram_depth, nrxslots, ntxslots) + self.comb += [ + Record.connect(self.sink, self.sram.sink), + Record.connect(self.sram.source, self.source) + ] + + # Wishbone interface + wb_rx_sram_ifs = [wishbone.SRAM(self.sram.writer.mems[n], read_only=True) + for n in range(nrxslots)] + # TODO: FullMemoryWE should move to Mibuild + wb_tx_sram_ifs = [FullMemoryWE(wishbone.SRAM(self.sram.reader.mems[n], read_only=False)) + for n in range(ntxslots)] + wb_sram_ifs = wb_rx_sram_ifs + wb_tx_sram_ifs + + wb_slaves = [] + decoderoffset = log2_int(sram_depth) + decoderbits = log2_int(len(wb_sram_ifs)) + for n, wb_sram_if in enumerate(wb_sram_ifs): + def slave_filter(a, v=n): + return a[decoderoffset:decoderoffset+decoderbits] == v + wb_slaves.append((slave_filter, wb_sram_if.bus)) + self.submodules += wb_sram_if + wb_con = wishbone.Decoder(self.bus, wb_slaves, register=True) + self.submodules += wb_con diff --git a/misoclib/liteeth/phy/__init__.py b/misoclib/liteeth/phy/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/misoclib/liteeth/phy/gmii.py b/misoclib/liteeth/phy/gmii.py new file mode 100644 index 00000000..180f7da5 --- /dev/null +++ b/misoclib/liteeth/phy/gmii.py @@ -0,0 +1,76 @@ +from liteeth.common import * +from liteeth.generic import * + +class LiteEthPHYGMIITX(Module): + def __init__(self, pads): + self.sink = sink = Sink(eth_phy_description(8)) + ### + self.sync += [ + pads.tx_er.eq(0), + pads.tx_en.eq(sink.stb), + pads.tx_data.eq(sink.data) + ] + self.comb += sink.ack.eq(1) + +class LiteEthPHYGMIIRX(Module): + def __init__(self, pads): + self.source = source = Source(eth_phy_description(8)) + ### + dv_d = Signal() + self.sync += dv_d.eq(pads.dv) + + sop = Signal() + eop = Signal() + self.comb += [ + sop.eq(pads.dv & ~dv_d), + eop.eq(~pads.dv & dv_d) + ] + self.sync += [ + source.stb.eq(pads.dv), + source.sop.eq(sop), + source.data.eq(pads.rx_data) + ] + self.comb += source.eop.eq(eop) + +# CRG is the only Xilinx specific module. +# TODO: use generic code or add support for others vendors +class LiteEthPHYGMIICRG(Module, AutoCSR): + def __init__(self, clock_pads, pads, with_hw_init_reset): + self._reset = CSRStorage() + ### + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.specials += [ + Instance("ODDR", + p_DDR_CLK_EDGE="SAME_EDGE", + i_C=ClockSignal("eth_tx"), i_CE=1, i_S=0, i_R=0, + i_D1=1, i_D2=0, o_Q=clock_pads.gtx, + ), + Instance("BUFG", i_I=clock_pads.rx, o_O=self.cd_eth_rx.clk), + ] + self.comb += self.cd_eth_tx.clk.eq(self.cd_eth_rx.clk) + + if with_hw_init_reset: + reset = Signal() + counter_done = Signal() + self.submodules.counter = counter = Counter(max=512) + self.comb += [ + counter_done.eq(counter.value == 256), + counter.ce.eq(~counter_done), + reset.eq(~counter_done | self._reset.storage) + ] + else: + reset = self._reset.storage + self.comb += pads.rst_n.eq(~reset) + self.specials += [ + AsyncResetSynchronizer(self.cd_eth_tx, reset), + AsyncResetSynchronizer(self.cd_eth_rx, reset), + ] + +class LiteEthPHYGMII(Module, AutoCSR): + def __init__(self, clock_pads, pads, with_hw_init_reset=True): + self.dw = 8 + self.submodules.crg = LiteEthPHYGMIICRG(clock_pads, pads, with_hw_init_reset) + self.submodules.tx = RenameClockDomains(LiteEthPHYGMIITX(pads), "eth_tx") + self.submodules.rx = RenameClockDomains(LiteEthPHYGMIIRX(pads), "eth_rx") + self.sink, self.source = self.tx.sink, self.rx.source diff --git a/misoclib/liteeth/phy/loopback.py b/misoclib/liteeth/phy/loopback.py new file mode 100644 index 00000000..89ffcb3a --- /dev/null +++ b/misoclib/liteeth/phy/loopback.py @@ -0,0 +1,27 @@ +from liteeth.common import * +from liteeth.generic import * + +class LiteEthPHYLoopbackCRG(Module, AutoCSR): + def __init__(self): + self._reset = CSRStorage() + ### + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += [ + self.cd_eth_rx.clk.eq(ClockSignal()), + self.cd_eth_tx.clk.eq(ClockSignal()) + ] + + reset = self._reset.storage + self.comb += [ + self.cd_eth_rx.rst.eq(reset), + self.cd_eth_tx.rst.eq(reset) + ] + +class LiteEthPHYLoopback(Module, AutoCSR): + def __init__(self): + self.dw = 8 + self.submodules.crg = LiteEthLoopbackPHYCRG() + self.sink = sink = Sink(eth_phy_description(8)) + self.source = source = Source(eth_phy_description(8)) + self.comb += Record.connect(self.sink, self.source) diff --git a/misoclib/liteeth/phy/mii.py b/misoclib/liteeth/phy/mii.py new file mode 100644 index 00000000..0d3f61be --- /dev/null +++ b/misoclib/liteeth/phy/mii.py @@ -0,0 +1,127 @@ +from liteeth.common import * +from liteeth.generic import * + +class LiteEthPHYMIITX(Module): + def __init__(self, pads): + self.sink = sink = Sink(eth_phy_description(8)) + ### + tx_en_r = Signal() + tx_data_r = Signal(4) + self.sync += [ + pads.tx_er.eq(0), + pads.tx_en.eq(tx_en_r), + pads.tx_data.eq(tx_data_r), + ] + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + fsm.act("IDLE", + sink.ack.eq(1), + If(sink.stb & sink.sop, + sink.ack.eq(0), + NextState("SEND_LO") + ) + ) + fsm.act("SEND_LO", + tx_data_r.eq(sink.data[0:4]), + tx_en_r.eq(1), + NextState("SEND_HI") + ) + fsm.act("SEND_HI", + tx_data_r.eq(sink.data[4:8]), + tx_en_r.eq(1), + sink.ack.eq(1), + If(sink.stb & sink.eop, + NextState("IDLE") + ).Else( + NextState("SEND_LO") + ) + ) + +class LiteEthPHYMIIRX(Module): + def __init__(self, pads): + self.source = source = Source(eth_phy_description(8)) + ### + sop = source.sop + set_sop = Signal() + clr_sop = Signal() + self.sync += \ + If(clr_sop, + sop.eq(0) + ).Elif(set_sop, + sop.eq(1) + ) + + lo = Signal(4) + hi = Signal(4) + load_nibble = Signal(2) + self.sync += \ + If(load_nibble[0], + lo.eq(pads.rx_data) + ).Elif(load_nibble[1], + hi.eq(pads.rx_data) + ) + self.comb += [ + source.data.eq(Cat(lo, hi)) + ] + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + fsm.act("IDLE", + set_sop.eq(1), + If(pads.dv, + load_nibble.eq(0b01), + NextState("LOAD_HI") + ) + ) + fsm.act("LOAD_LO", + source.stb.eq(1), + If(pads.dv, + clr_sop.eq(1), + load_nibble.eq(0b01), + NextState("LOAD_HI") + ).Else( + source.eop.eq(1), + NextState("IDLE") + ) + ) + fsm.act("LOAD_HI", + load_nibble.eq(0b10), + NextState("LOAD_LO") + ) + +class LiteEthPHYMIICRG(Module, AutoCSR): + def __init__(self, clock_pads, pads, with_hw_init_reset): + self._reset = CSRStorage() + ### + self.sync.base50 += clock_pads.phy.eq(~clock_pads.phy) + + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += self.cd_eth_rx.clk.eq(clock_pads.rx) + self.comb += self.cd_eth_tx.clk.eq(clock_pads.tx) + + if with_hw_init_reset: + reset = Signal() + counter_done = Signal() + self.submodules.counter = counter = Counter(max=512) + self.comb += [ + counter_done.eq(counter.value == 256), + counter.ce.eq(~counter_done), + reset.eq(~counter_done | self._reset.storage) + ] + else: + reset = self._reset.storage + self.comb += pads.rst_n.eq(~reset) + self.specials += [ + AsyncResetSynchronizer(self.cd_eth_tx, reset), + AsyncResetSynchronizer(self.cd_eth_rx, reset), + ] + +class LiteEthPHYMII(Module, AutoCSR): + def __init__(self, clock_pads, pads, with_hw_init_reset=True): + self.dw = 8 + self.submodules.crg = LiteEthPHYMIICRG(clock_pads, pads, with_hw_init_reset) + self.submodules.tx = RenameClockDomains(LiteEthPHYMIITX(pads), "eth_tx") + self.submodules.rx = RenameClockDomains(LiteEthPHYMIIRX(pads), "eth_rx") + self.sink, self.source = self.tx.sink, self.rx.source diff --git a/misoclib/liteeth/phy/sim.py b/misoclib/liteeth/phy/sim.py new file mode 100644 index 00000000..f7069dd9 --- /dev/null +++ b/misoclib/liteeth/phy/sim.py @@ -0,0 +1,43 @@ +from liteeth.common import * +from liteeth.generic import * + +class LiteEthPHYSimCRG(Module, AutoCSR): + def __init__(self): + self._reset = CSRStorage() + + ### + + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += [ + self.cd_eth_rx.clk.eq(ClockSignal()), + self.cd_eth_tx.clk.eq(ClockSignal()) + ] + + reset = self._reset.storage + self.comb += [ + self.cd_eth_rx.rst.eq(reset), + self.cd_eth_tx.rst.eq(reset) + ] + +class LiteEthPHYSim(Module, AutoCSR): + def __init__(self, pads): + self.dw = 8 + self.submodules.crg = LiteEthPHYSimCRG() + self.sink = sink = Sink(eth_phy_description(8)) + self.source = source = Source(eth_phy_description(8)) + + self.comb += [ + pads.source_stb.eq(self.sink.stb), + pads.source_data.eq(self.sink.data), + self.sink.ack.eq(1) + ] + + self.sync += [ + self.source.stb.eq(pads.sink_stb), + self.source.sop.eq(pads.sink_stb & ~self.source.stb), + self.source.data.eq(pads.sink_data), + ] + self.comb += [ + self.source.eop.eq(~pads.sink_stb & self.source.stb), + ] diff --git a/misoclib/liteeth/test/Makefile b/misoclib/liteeth/test/Makefile new file mode 100644 index 00000000..1682923d --- /dev/null +++ b/misoclib/liteeth/test/Makefile @@ -0,0 +1,33 @@ +LEDIR = ../../ +PYTHON = python3 + +CMD = PYTHONPATH=$(LEDIR) $(PYTHON) + +model_tb: + $(CMD) ./model/mac.py + $(CMD) ./model/arp.py + $(CMD) ./model/ip.py + $(CMD) ./model/udp.py + $(CMD) ./model/icmp.py + $(CMD) ./model/etherbone.py + +mac_core_tb: + $(CMD) mac_core_tb.py + +mac_wishbone_tb: + $(CMD) mac_wishbone_tb.py + +arp_tb: + $(CMD) arp_tb.py + +ip_tb: + $(CMD) ip_tb.py + +udp_tb: + $(CMD) udp_tb.py + +icmp_tb: + $(CMD) icmp_tb.py + +etherbone_tb: + $(CMD) etherbone_tb.py diff --git a/misoclib/liteeth/test/__init__.py b/misoclib/liteeth/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/misoclib/liteeth/test/arp_tb.py b/misoclib/liteeth/test/arp_tb.py new file mode 100644 index 00000000..3cd9ad36 --- /dev/null +++ b/misoclib/liteeth/test/arp_tb.py @@ -0,0 +1,57 @@ +from migen.fhdl.std import * +from migen.bus import wishbone +from migen.bus.transactions import * +from migen.sim.generic import run_simulation + +from liteeth.common import * +from liteeth.mac import LiteEthMAC +from liteeth.core.arp import LiteEthARP + +from liteeth.test.common import * +from liteeth.test.model import phy, mac, arp + +ip_address = 0x12345678 +mac_address = 0x12345678abcd + +class TB(Module): + def __init__(self): + self.submodules.phy_model = phy.PHY(8, debug=False) + self.submodules.mac_model = mac.MAC(self.phy_model, debug=False, loopback=False) + self.submodules.arp_model = arp.ARP(self.mac_model, mac_address, ip_address, debug=False) + + self.submodules.mac = LiteEthMAC(self.phy_model, dw=8, with_hw_preamble_crc=True) + self.submodules.arp = LiteEthARP(self.mac, mac_address, ip_address, 100000) + + # use sys_clk for each clock_domain + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += [ + self.cd_eth_rx.clk.eq(ClockSignal()), + self.cd_eth_rx.rst.eq(ResetSignal()), + self.cd_eth_tx.clk.eq(ClockSignal()), + self.cd_eth_tx.rst.eq(ResetSignal()), + ] + + def gen_simulation(self, selfp): + selfp.cd_eth_rx.rst = 1 + selfp.cd_eth_tx.rst = 1 + yield + selfp.cd_eth_rx.rst = 0 + selfp.cd_eth_tx.rst = 0 + + for i in range(100): + yield + + while selfp.arp.table.request.ack != 1: + selfp.arp.table.request.stb = 1 + selfp.arp.table.request.ip_address = 0x12345678 + yield + selfp.arp.table.request.stb = 0 + while selfp.arp.table.response.stb != 1: + selfp.arp.table.response.ack = 1 + yield + print("Received MAC : 0x{:12x}".format(selfp.arp.table.response.mac_address)) + + +if __name__ == "__main__": + run_simulation(TB(), ncycles=2048, vcd_name="my.vcd", keep_files=True) diff --git a/misoclib/liteeth/test/common.py b/misoclib/liteeth/test/common.py new file mode 100644 index 00000000..d780565b --- /dev/null +++ b/misoclib/liteeth/test/common.py @@ -0,0 +1,162 @@ +import random, copy + +from migen.fhdl.std import * +from migen.flow.actor import Sink, Source +from migen.genlib.record import * + +from liteeth.common import * + +def print_with_prefix(s, prefix=""): + if not isinstance(s, str): + s = s.__repr__() + s = s.split("\n") + for l in s: + print(prefix + l) + +def seed_to_data(seed, random=True): + if random: + return (seed * 0x31415979 + 1) & 0xffffffff + else: + return seed + +def split_bytes(v, n, endianness="big"): + r = [] + r_bytes = v.to_bytes(n, byteorder=endianness) + for byte in r_bytes: + r.append(int(byte)) + return r + +def merge_bytes(b, endianness="big"): + return int.from_bytes(bytes(b), endianness) + +def get_field_data(field, datas): + v = merge_bytes(datas[field.byte:field.byte+math.ceil(field.width/8)]) + return (v >> field.offset) & (2**field.width-1) + +def comp(p1, p2): + r = True + for x, y in zip(p1, p2): + if x != y: + r = False + return r + +def check(p1, p2): + p1 = copy.deepcopy(p1) + p2 = copy.deepcopy(p2) + if isinstance(p1, int): + return 0, 1, int(p1 != p2) + else: + if len(p1) >= len(p2): + ref, res = p1, p2 + else: + ref, res = p2, p1 + shift = 0 + while((ref[0] != res[0]) and (len(res)>1)): + res.pop(0) + shift += 1 + length = min(len(ref), len(res)) + errors = 0 + for i in range(length): + if ref.pop(0) != res.pop(0): + errors += 1 + return shift, length, errors + +def randn(max_n): + return random.randint(0, max_n-1) + +class Packet(list): + def __init__(self, init=[]): + self.ongoing = False + self.done = False + for data in init: + self.append(data) + +class PacketStreamer(Module): + def __init__(self, description, last_be=None): + self.source = Source(description) + self.last_be = last_be + ### + self.packets = [] + self.packet = Packet() + self.packet.done = True + + def send(self, packet): + packet = copy.deepcopy(packet) + self.packets.append(packet) + return packet + + def send_blocking(self, packet): + packet = self.send(packet) + while not packet.done: + yield + + def do_simulation(self, selfp): + if len(self.packets) and self.packet.done: + self.packet = self.packets.pop(0) + if not self.packet.ongoing and not self.packet.done: + selfp.source.stb = 1 + selfp.source.sop = 1 + selfp.source.data = self.packet.pop(0) + self.packet.ongoing = True + elif selfp.source.stb == 1 and selfp.source.ack == 1: + selfp.source.sop = 0 + if len(self.packet) == 1: + selfp.source.eop = 1 + if self.last_be is not None: + selfp.source.last_be = self.last_be + else: + selfp.source.eop = 0 + if self.last_be is not None: + selfp.source.last_be = 0 + if len(self.packet) > 0: + selfp.source.stb = 1 + selfp.source.data = self.packet.pop(0) + else: + self.packet.done = True + selfp.source.stb = 0 + +class PacketLogger(Module): + def __init__(self, description): + self.sink = Sink(description) + ### + self.packet = Packet() + + def receive(self): + self.packet.done = False + while not self.packet.done: + yield + + def do_simulation(self, selfp): + selfp.sink.ack = 1 + if selfp.sink.stb == 1 and selfp.sink.sop == 1: + self.packet = Packet() + self.packet.append(selfp.sink.data) + elif selfp.sink.stb: + self.packet.append(selfp.sink.data) + if selfp.sink.stb == 1 and selfp.sink.eop == 1: + self.packet.done = True + +class AckRandomizer(Module): + def __init__(self, description, level=0): + self.level = level + + self.sink = Sink(description) + self.source = Source(description) + + self.run = Signal() + + self.comb += \ + If(self.run, + Record.connect(self.sink, self.source) + ).Else( + self.source.stb.eq(0), + self.sink.ack.eq(0), + ) + + def do_simulation(self, selfp): + n = randn(100) + if n < self.level: + selfp.run = 0 + else: + selfp.run = 1 + diff --git a/misoclib/liteeth/test/etherbone_tb.py b/misoclib/liteeth/test/etherbone_tb.py new file mode 100644 index 00000000..275dc2e6 --- /dev/null +++ b/misoclib/liteeth/test/etherbone_tb.py @@ -0,0 +1,116 @@ +from migen.fhdl.std import * +from migen.bus import wishbone +from migen.bus.transactions import * +from migen.sim.generic import run_simulation + +from liteeth.common import * +from liteeth.core import LiteEthUDPIPCore +from liteeth.core.etherbone import LiteEthEtherbone + +from liteeth.test.common import * +from liteeth.test.model import phy, mac, arp, ip, udp, etherbone + +ip_address = 0x12345678 +mac_address = 0x12345678abcd + +class TB(Module): + def __init__(self): + self.submodules.phy_model = phy.PHY(8, debug=False) + self.submodules.mac_model = mac.MAC(self.phy_model, debug=False, loopback=False) + self.submodules.arp_model = arp.ARP(self.mac_model, mac_address, ip_address, debug=False) + self.submodules.ip_model = ip.IP(self.mac_model, mac_address, ip_address, debug=False, loopback=False) + self.submodules.udp_model = udp.UDP(self.ip_model, ip_address, debug=False, loopback=False) + self.submodules.etherbone_model = etherbone.Etherbone(self.udp_model, debug=False) + + self.submodules.core = LiteEthUDPIPCore(self.phy_model, mac_address, ip_address, 100000) + self.submodules.etherbone = LiteEthEtherbone(self.core.udp, 20000) + + self.submodules.sram = wishbone.SRAM(1024) + self.submodules.interconnect = wishbone.InterconnectPointToPoint(self.etherbone.master.bus, self.sram.bus) + + # use sys_clk for each clock_domain + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += [ + self.cd_eth_rx.clk.eq(ClockSignal()), + self.cd_eth_rx.rst.eq(ResetSignal()), + self.cd_eth_tx.clk.eq(ClockSignal()), + self.cd_eth_tx.rst.eq(ResetSignal()), + ] + + def gen_simulation(self, selfp): + selfp.cd_eth_rx.rst = 1 + selfp.cd_eth_tx.rst = 1 + yield + selfp.cd_eth_rx.rst = 0 + selfp.cd_eth_tx.rst = 0 + + for i in range(100): + yield + + test_probe = True + test_writes = True + test_reads = True + + # test probe + if test_probe: + packet = etherbone.EtherbonePacket() + packet.pf = 1 + self.etherbone_model.send(packet) + yield from self.etherbone_model.receive() + print("probe: " + str(bool(self.etherbone_model.rx_packet.pr))) + + for i in range(8): + # test writes + if test_writes: + writes_datas = [j for j in range(16)] + writes = etherbone.EtherboneWrites(base_addr=0x1000, datas=writes_datas) + record = etherbone.EtherboneRecord() + record.writes = writes + record.reads = None + record.bca = 0 + record.rca = 0 + record.rff = 0 + record.cyc = 0 + record.wca = 0 + record.wff = 0 + record.byte_enable = 0xf + record.wcount = len(writes_datas) + record.rcount = 0 + + packet = etherbone.EtherbonePacket() + packet.records = [record] + self.etherbone_model.send(packet) + for i in range(256): + yield + + # test reads + if test_reads: + reads_addrs = [0x1000 + 4*j for j in range(16)] + reads = etherbone.EtherboneReads(base_ret_addr=0x1000, addrs=reads_addrs) + record = etherbone.EtherboneRecord() + record.writes = None + record.reads = reads + record.bca = 0 + record.rca = 0 + record.rff = 0 + record.cyc = 0 + record.wca = 0 + record.wff = 0 + record.byte_enable = 0xf + record.wcount = 0 + record.rcount = len(reads_addrs) + + packet = etherbone.EtherbonePacket() + packet.records = [record] + self.etherbone_model.send(packet) + yield from self.etherbone_model.receive() + loopback_writes_datas = [] + loopback_writes_datas = self.etherbone_model.rx_packet.records.pop().writes.get_datas() + + # check results + s, l, e = check(writes_datas, loopback_writes_datas) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + +if __name__ == "__main__": + run_simulation(TB(), ncycles=4096, vcd_name="my.vcd", keep_files=True) \ No newline at end of file diff --git a/misoclib/liteeth/test/icmp_tb.py b/misoclib/liteeth/test/icmp_tb.py new file mode 100644 index 00000000..f5e2d907 --- /dev/null +++ b/misoclib/liteeth/test/icmp_tb.py @@ -0,0 +1,58 @@ +from migen.fhdl.std import * +from migen.bus import wishbone +from migen.bus.transactions import * +from migen.sim.generic import run_simulation + +from liteeth.common import * +from liteeth.core import LiteEthIPCore + +from liteeth.test.common import * +from liteeth.test.model.dumps import * +from liteeth.test.model.mac import * +from liteeth.test.model.ip import * +from liteeth.test.model.icmp import * +from liteeth.test.model import phy, mac, arp, ip, icmp + +ip_address = 0x12345678 +mac_address = 0x12345678abcd + +class TB(Module): + def __init__(self): + self.submodules.phy_model = phy.PHY(8, debug=True) + self.submodules.mac_model = mac.MAC(self.phy_model, debug=True, loopback=False) + self.submodules.arp_model = arp.ARP(self.mac_model, mac_address, ip_address, debug=True) + self.submodules.ip_model = ip.IP(self.mac_model, mac_address, ip_address, debug=True, loopback=False) + self.submodules.icmp_model = icmp.ICMP(self.ip_model, ip_address, debug=True) + + self.submodules.ip = LiteEthIPCore(self.phy_model, mac_address, ip_address, 100000) + + # use sys_clk for each clock_domain + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += [ + self.cd_eth_rx.clk.eq(ClockSignal()), + self.cd_eth_rx.rst.eq(ResetSignal()), + self.cd_eth_tx.clk.eq(ClockSignal()), + self.cd_eth_tx.rst.eq(ResetSignal()), + ] + + def gen_simulation(self, selfp): + selfp.cd_eth_rx.rst = 1 + selfp.cd_eth_tx.rst = 1 + yield + selfp.cd_eth_rx.rst = 0 + selfp.cd_eth_tx.rst = 0 + + for i in range(100): + yield + + packet = MACPacket(ping_request) + packet.decode_remove_header() + packet = IPPacket(packet) + packet.decode() + packet = ICMPPacket(packet) + packet.decode() + self.icmp_model.send(packet) + +if __name__ == "__main__": + run_simulation(TB(), ncycles=2048, vcd_name="my.vcd", keep_files=True) diff --git a/misoclib/liteeth/test/ip_tb.py b/misoclib/liteeth/test/ip_tb.py new file mode 100644 index 00000000..1c21ffa5 --- /dev/null +++ b/misoclib/liteeth/test/ip_tb.py @@ -0,0 +1,59 @@ +from migen.fhdl.std import * +from migen.bus import wishbone +from migen.bus.transactions import * +from migen.sim.generic import run_simulation + +from liteeth.common import * +from liteeth.core import LiteEthIPCore + +from liteeth.test.common import * +from liteeth.test.model import phy, mac, arp, ip + +ip_address = 0x12345678 +mac_address = 0x12345678abcd + +class TB(Module): + def __init__(self): + self.submodules.phy_model = phy.PHY(8, debug=False) + self.submodules.mac_model = mac.MAC(self.phy_model, debug=False, loopback=False) + self.submodules.arp_model = arp.ARP(self.mac_model, mac_address, ip_address, debug=False) + self.submodules.ip_model = ip.IP(self.mac_model, mac_address, ip_address, debug=False, loopback=True) + + self.submodules.ip = LiteEthIPCore(self.phy_model, mac_address, ip_address, 100000) + self.ip_port = self.ip.ip.crossbar.get_port(udp_protocol) + + # use sys_clk for each clock_domain + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += [ + self.cd_eth_rx.clk.eq(ClockSignal()), + self.cd_eth_rx.rst.eq(ResetSignal()), + self.cd_eth_tx.clk.eq(ClockSignal()), + self.cd_eth_tx.rst.eq(ResetSignal()), + ] + + def gen_simulation(self, selfp): + selfp.cd_eth_rx.rst = 1 + selfp.cd_eth_tx.rst = 1 + yield + selfp.cd_eth_rx.rst = 0 + selfp.cd_eth_tx.rst = 0 + + for i in range(100): + yield + + while True: + selfp.ip_port.sink.stb = 1 + selfp.ip_port.sink.sop = 1 + selfp.ip_port.sink.eop = 1 + selfp.ip_port.sink.ip_address = 0x12345678 + selfp.ip_port.sink.protocol = udp_protocol + + selfp.ip_port.source.ack = 1 + if selfp.ip_port.source.stb == 1 and selfp.ip_port.source.sop == 1: + print("packet from from IP 0x{:08x}".format(selfp.ip_port.sink.ip_address)) + + yield + +if __name__ == "__main__": + run_simulation(TB(), ncycles=2048, vcd_name="my.vcd", keep_files=True) diff --git a/misoclib/liteeth/test/mac_core_tb.py b/misoclib/liteeth/test/mac_core_tb.py new file mode 100644 index 00000000..341028b7 --- /dev/null +++ b/misoclib/liteeth/test/mac_core_tb.py @@ -0,0 +1,62 @@ +from migen.fhdl.std import * +from migen.bus import wishbone +from migen.bus.transactions import * +from migen.sim.generic import run_simulation + +from liteeth.common import * +from liteeth.mac.core import LiteEthMACCore + +from liteeth.test.common import * +from liteeth.test.model import phy, mac + +class TB(Module): + def __init__(self): + self.submodules.phy_model = phy.PHY(8, debug=False) + self.submodules.mac_model = mac.MAC(self.phy_model, debug=False, loopback=True) + self.submodules.core = LiteEthMACCore(phy=self.phy_model, dw=8, with_hw_preamble_crc=True) + + self.submodules.streamer = PacketStreamer(eth_phy_description(8), last_be=1) + self.submodules.streamer_randomizer = AckRandomizer(eth_phy_description(8), level=50) + + self.submodules.logger_randomizer = AckRandomizer(eth_phy_description(8), level=50) + self.submodules.logger = PacketLogger(eth_phy_description(8)) + + # use sys_clk for each clock_domain + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += [ + self.cd_eth_rx.clk.eq(ClockSignal()), + self.cd_eth_rx.rst.eq(ResetSignal()), + self.cd_eth_tx.clk.eq(ClockSignal()), + self.cd_eth_tx.rst.eq(ResetSignal()), + ] + + self.comb += [ + Record.connect(self.streamer.source, self.streamer_randomizer.sink), + Record.connect(self.streamer_randomizer.source, self.core.sink), + Record.connect(self.core.source, self.logger_randomizer.sink), + Record.connect(self.logger_randomizer.source, self.logger.sink) + ] + + def gen_simulation(self, selfp): + selfp.cd_eth_rx.rst = 1 + selfp.cd_eth_tx.rst = 1 + yield + selfp.cd_eth_rx.rst = 0 + selfp.cd_eth_tx.rst = 0 + + for i in range(8): + packet = mac.MACPacket([i for i in range(64)]) + packet.target_mac = 0x010203040506 + packet.sender_mac = 0x090A0B0C0C0D + packet.ethernet_type = 0x0800 + packet.encode_header() + yield from self.streamer.send(packet) + yield from self.logger.receive() + + # check results + s, l, e = check(packet, self.logger.packet) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + +if __name__ == "__main__": + run_simulation(TB(), ncycles=4000, vcd_name="my.vcd", keep_files=True) diff --git a/misoclib/liteeth/test/mac_wishbone_tb.py b/misoclib/liteeth/test/mac_wishbone_tb.py new file mode 100644 index 00000000..ca6740eb --- /dev/null +++ b/misoclib/liteeth/test/mac_wishbone_tb.py @@ -0,0 +1,145 @@ +from migen.fhdl.std import * +from migen.bus import wishbone +from migen.bus.transactions import * +from migen.sim.generic import run_simulation + +from liteeth.common import * +from liteeth.mac import LiteEthMAC + +from liteeth.test.common import * +from liteeth.test.model import phy, mac + +class WishboneMaster: + def __init__(self, obj): + self.obj = obj + self.dat = 0 + + def write(self, adr, dat): + self.obj.cyc = 1 + self.obj.stb = 1 + self.obj.adr = adr + self.obj.we = 1 + self.obj.sel = 0xF + self.obj.dat_w = dat + while self.obj.ack == 0: + yield + self.obj.cyc = 0 + self.obj.stb = 0 + yield + + def read(self, adr): + self.obj.cyc = 1 + self.obj.stb = 1 + self.obj.adr = adr + self.obj.we = 0 + self.obj.sel = 0xF + self.obj.dat_w = 0 + while self.obj.ack == 0: + yield + self.dat = self.obj.dat_r + self.obj.cyc = 0 + self.obj.stb = 0 + yield + +class SRAMReaderDriver: + def __init__(self, obj): + self.obj = obj + + def start(self, slot, length): + self.obj._slot.storage = slot + self.obj._length.storage = length + self.obj._start.re = 1 + yield + self.obj._start.re = 0 + yield + + def wait_done(self): + while self.obj.ev.done.pending == 0: + yield + + def clear_done(self): + self.obj.ev.done.clear = 1 + yield + self.obj.ev.done.clear = 0 + yield + +class SRAMWriterDriver: + def __init__(self, obj): + self.obj = obj + + def wait_available(self): + while self.obj.ev.available.pending == 0: + yield + + def clear_available(self): + self.obj.ev.available.clear = 1 + yield + self.obj.ev.available.clear = 0 + yield + +class TB(Module): + def __init__(self): + self.submodules.phy_model = phy.PHY(8, debug=False) + self.submodules.mac_model = mac.MAC(self.phy_model, debug=False, loopback=True) + self.submodules.ethmac = LiteEthMAC(phy=self.phy_model, dw=32, interface="wishbone", with_hw_preamble_crc=True) + + # use sys_clk for each clock_domain + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += [ + self.cd_eth_rx.clk.eq(ClockSignal()), + self.cd_eth_rx.rst.eq(ResetSignal()), + self.cd_eth_tx.clk.eq(ClockSignal()), + self.cd_eth_tx.rst.eq(ResetSignal()), + ] + + def gen_simulation(self, selfp): + selfp.cd_eth_rx.rst = 1 + selfp.cd_eth_tx.rst = 1 + yield + selfp.cd_eth_rx.rst = 0 + selfp.cd_eth_tx.rst = 0 + + wishbone_master = WishboneMaster(selfp.ethmac.bus) + sram_reader_driver = SRAMReaderDriver(selfp.ethmac.interface.sram.reader) + sram_writer_driver = SRAMWriterDriver(selfp.ethmac.interface.sram.writer) + + sram_writer_slots_offset = [0x000, 0x200] + sram_reader_slots_offset = [0x400, 0x600] + + length = 150+2 + + tx_payload = [seed_to_data(i, True) % 0xFF for i in range(length)] + [0, 0, 0, 0] + + errors = 0 + + while True: + for slot in range(2): + print("slot {}: ".format(slot), end="") + # fill tx memory + for i in range(length//4+1): + dat = int.from_bytes(tx_payload[4*i:4*(i+1)], "big") + yield from wishbone_master.write(sram_reader_slots_offset[slot]+i, dat) + + # send tx payload & wait + yield from sram_reader_driver.start(slot, length) + yield from sram_reader_driver.wait_done() + yield from sram_reader_driver.clear_done() + + # wait rx + yield from sram_writer_driver.wait_available() + yield from sram_writer_driver.clear_available() + + # get rx payload (loopback on PHY Model) + rx_payload = [] + for i in range(length//4+1): + yield from wishbone_master.read(sram_writer_slots_offset[slot]+i) + dat = wishbone_master.dat + rx_payload += list(dat.to_bytes(4, byteorder='big')) + + # check results + s, l, e = check(tx_payload[:length], rx_payload[:min(length, len(rx_payload))]) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + +if __name__ == "__main__": + run_simulation(TB(), ncycles=3000, vcd_name="my.vcd", keep_files=True) diff --git a/misoclib/liteeth/test/model/__init__.py b/misoclib/liteeth/test/model/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/misoclib/liteeth/test/model/arp.py b/misoclib/liteeth/test/model/arp.py new file mode 100644 index 00000000..677904cc --- /dev/null +++ b/misoclib/liteeth/test/model/arp.py @@ -0,0 +1,152 @@ +import math + +from liteeth.common import * +from liteeth.test.common import * + +from liteeth.test.model import mac + +def print_arp(s): + print_with_prefix(s, "[ARP]") + +preamble = split_bytes(eth_preamble, 8) + +# ARP model +class ARPPacket(Packet): + def __init__(self, init=[]): + Packet.__init__(self, init) + + def decode(self): + header = [] + for byte in self[:arp_header_len]: + header.append(self.pop(0)) + for k, v in sorted(arp_header.items()): + setattr(self, k, get_field_data(v, header)) + + def encode(self): + header = 0 + for k, v in sorted(arp_header.items()): + value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") + header += (value << v.offset+(v.byte*8)) + for d in split_bytes(header, arp_header_len): + self.insert(0, d) + + def __repr__(self): + r = "--------\n" + for k in sorted(arp_header.keys()): + r += k + " : 0x{:0x}\n".format(getattr(self,k)) + r += "payload: " + for d in self: + r += "{:02x}".format(d) + return r + +class ARP(Module): + def __init__(self, mac, mac_address, ip_address, debug=False): + self.mac = mac + self.mac_address = mac_address + self.ip_address = ip_address + self.debug = debug + self.tx_packets = [] + self.tx_packet = ARPPacket() + self.rx_packet = ARPPacket() + self.table = {} + self.request_pending = False + + self.mac.set_arp_callback(self.callback) + + def send(self, packet): + packet.encode() + if self.debug: + print_arp(">>>>>>>>") + print_arp(packet) + mac_packet = mac.MACPacket(packet) + mac_packet.target_mac = packet.target_mac + mac_packet.sender_mac = packet.sender_mac + mac_packet.ethernet_type = ethernet_type_arp + self.mac.send(mac_packet) + + def callback(self, packet): + packet = ARPPacket(packet) + packet.decode() + if self.debug: + print_arp("<<<<<<<<") + print_arp(packet) + self.process(packet) + + def process(self, packet): + if len(packet) != eth_min_len-arp_header_len: + raise ValueError + if packet.hwtype != arp_hwtype_ethernet: + raise ValueError + if packet.proto != arp_proto_ip: + raise ValueError + if packet.hwsize != 6: + raise ValueError + if packet.protosize != 4: + raise ValueError + if packet.opcode == arp_opcode_request: + self.process_request(packet) + elif packet.opcode == arp_opcode_reply: + self.process_reply(packet) + + def process_request(self, request): + if request.target_ip == self.ip_address: + reply = ARPPacket([0]*(eth_min_len-arp_header_len)) + reply.hwtype = arp_hwtype_ethernet + reply.proto = arp_proto_ip + reply.opcode = arp_opcode_reply + reply.hwsize = 6 + reply.protosize = 4 + reply.sender_mac = self.mac_address + reply.sender_ip = self.ip_address + reply.target_mac = request.sender_mac + reply.target_ip = request.sender_ip + self.send(reply) + + def process_reply(self, reply): + self.table[reply.sender_ip] = reply.sender_mac + + def request(self, ip_address): + request = ARPPacket([0]*(eth_min_len-arp_header_len)) + request.hwtype = arp_hwtype_ethernet + request.proto = arp_proto_ip + request.opcode = arp_opcode_request + request.hwsize = 6 + request.protosize = 4 + request.sender_mac = self.mac_address + request.sender_ip = self.ip_address + request.target_mac = 0xffffffffffff + request.target_ip = ip_address + +if __name__ == "__main__": + from liteeth.test.model.dumps import * + from liteeth.test.model.mac import * + errors = 0 + # ARP request + packet = MACPacket(arp_request) + packet.decode_remove_header() + packet = ARPPacket(packet) + # check decoding + packet.decode() + #print(packet) + errors += verify_packet(packet, arp_request_infos) + # check encoding + packet.encode() + packet.decode() + #print(packet) + errors += verify_packet(packet, arp_request_infos) + + # ARP Reply + packet = MACPacket(arp_reply) + packet.decode_remove_header() + packet = ARPPacket(packet) + # check decoding + packet.decode() + #print(packet) + errors += verify_packet(packet, arp_reply_infos) + # check encoding + packet.encode() + packet.decode() + #print(packet) + errors += verify_packet(packet, arp_reply_infos) + + print("arp errors " + str(errors)) \ No newline at end of file diff --git a/misoclib/liteeth/test/model/dumps.py b/misoclib/liteeth/test/model/dumps.py new file mode 100644 index 00000000..ae618e0f --- /dev/null +++ b/misoclib/liteeth/test/model/dumps.py @@ -0,0 +1,90 @@ +import re + +def format_dump(dump): + return [int(s, 16) for s in re.split(r'[;,\s\n]\s*', dump) if s is not ""] + +def verify_packet(packet, infos): + errors = 0 + for k, v in infos.items(): + if hasattr(packet, k): + if getattr(packet, k) != v: + print("[Error] " + k) + errors += 1 + return errors + +arp_request = format_dump(""" +00 22 19 22 54 9e 00 12 3f 97 92 01 08 06 00 01 +08 00 06 04 00 01 00 12 3f 97 92 01 a9 fe ff 42 +00 22 19 22 54 9e a9 fe 64 62""") + +arp_request_infos = { + "sender_mac" : 0x00123f979201, + "target_mac" : 0x00221922549e, + "ethernet_type" : 0x806, + "hwtype" : 0x1, + "opcode" : 0x1, + "protosize" : 0x4, + "proto" : 0x800, + "sender_ip" : 0xa9feff42, + "target_ip" : 0xa9fe6462 + +} + +arp_reply = format_dump(""" +00 12 3f 97 92 01 00 22 19 22 54 9e 08 06 00 01 +08 00 06 04 00 02 00 22 19 22 54 9e a9 fe 64 62 +00 12 3f 97 92 01 a9 fe ff 42 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00""") + +arp_reply_infos = { + "sender_mac" : 0x00221922549e, + "target_mac" : 0x00123f979201, + "ethernet_type" : 0x806, + "hwtype" : 0x1, + "opcode" : 0x2, + "protosize" : 0x4, + "proto" : 0x800, + "sender_ip" : 0xa9fe6462, + "target_ip" : 0xa9feff42 +} + +udp = format_dump(""" +d0 7a b5 96 cd 0a 00 14 0b 33 33 27 08 00 45 00 +00 5f 31 16 00 00 80 11 87 77 c0 a8 01 65 b2 7b +0d 78 a6 3f 69 0f 00 4b 6a 54 64 31 3a 61 64 32 +3a 69 64 32 30 3a 5a fa 29 99 3a 5e ce 19 d1 8b +aa 9b 4e 4d f9 2e 51 52 fe ff 65 31 3a 71 34 3a +70 69 6e 67 31 3a 74 34 3a 85 72 00 00 31 3a 76 +34 3a 55 54 7e 62 31 3a 79 31 3a 71 65""") + +udp_infos = { + "sender_mac" : 0x00140b333327, + "target_mac" : 0xd07ab596cd0a, + "protocol" : 0x11, + "sender_ip" : 0xc0a80165, + "target_ip" : 0xb27b0d78, + "src_port" : 0xa63f, + "dst_port" : 0x690f +} + +ping_request = format_dump(""" +00 50 56 e0 14 49 00 0c 29 34 0b de 08 00 45 00 +00 3c d7 43 00 00 80 01 2b 73 c0 a8 9e 8b ae 89 +2a 4d 08 00 2a 5c 02 00 21 00 61 62 63 64 65 66 +67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 +77 61 62 63 64 65 66 67 68 69""") + +ping_request_infos = { + "code" : 0x0, + "msgtype" : 0x8, + "quench" : 0x2002100 +} + +ping_reply = format_dump(""" +00 0c 29 34 0b de 00 50 56 e0 14 49 08 00 45 00 +00 3c 76 e1 00 00 80 01 8b d5 ae 89 2a 4d c0 a8 +9e 8b 00 00 32 5c 02 00 21 00 61 62 63 64 65 66 +67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 +77 61 62 63 64 65 66 67 68 69""") + +ping_reply_infos = {} diff --git a/misoclib/liteeth/test/model/etherbone.py b/misoclib/liteeth/test/model/etherbone.py new file mode 100644 index 00000000..96cf49f7 --- /dev/null +++ b/misoclib/liteeth/test/model/etherbone.py @@ -0,0 +1,354 @@ +import math, copy + +from liteeth.common import * +from liteeth.test.common import * + +from liteeth.test.model import udp + +def print_etherbone(s): + print_with_prefix(s, "[ETHERBONE]") + +# Etherbone model +class EtherboneWrite: + def __init__(self, data): + self.data = data + + def __repr__(self): + return "WR32 0x{:08x}".format(self.data) + +class EtherboneRead: + def __init__(self, addr): + self.addr = addr + + def __repr__(self): + return "RD32 @ 0x{:08x}".format(self.addr) + +class EtherboneWrites(Packet): + def __init__(self, init=[], base_addr=0, datas=[]): + Packet.__init__(self, init) + self.base_addr = base_addr + self.writes = [] + self.encoded = init != [] + for data in datas: + self.add(EtherboneWrite(data)) + + def add(self, write): + self.writes.append(write) + + def get_datas(self): + datas = [] + for write in self.writes: + datas.append(write.data) + return datas + + def encode(self): + if self.encoded: + raise ValueError + for byte in split_bytes(self.base_addr, 4): + self.append(byte) + for write in self.writes: + for byte in split_bytes(write.data, 4): + self.append(byte) + self.encoded = True + + def decode(self): + if not self.encoded: + raise ValueError + base_addr = [] + for i in range(4): + base_addr.append(self.pop(0)) + self.base_addr = merge_bytes(base_addr) + self.writes = [] + while len(self) != 0: + write = [] + for i in range(4): + write.append(self.pop(0)) + self.writes.append(EtherboneWrite(merge_bytes(write))) + self.encoded = False + + def __repr__(self): + r = "Writes\n" + r += "--------\n" + r += "BaseAddr @ 0x{:08x}\n".format(self.base_addr) + for write in self.writes: + r += write.__repr__() + "\n" + return r + +class EtherboneReads(Packet): + def __init__(self, init=[], base_ret_addr=0, addrs=[]): + Packet.__init__(self, init) + self.base_ret_addr = base_ret_addr + self.reads = [] + self.encoded = init != [] + for addr in addrs: + self.add(EtherboneRead(addr)) + + def add(self, read): + self.reads.append(read) + + def get_addrs(self): + addrs = [] + for read in self.reads: + addrs.append(read.addr) + return addrs + + def encode(self): + if self.encoded: + raise ValueError + for byte in split_bytes(self.base_ret_addr, 4): + self.append(byte) + for read in self.reads: + for byte in split_bytes(read.addr, 4): + self.append(byte) + self.encoded = True + + def decode(self): + if not self.encoded: + raise ValueError + base_ret_addr = [] + for i in range(4): + base_ret_addr.append(self.pop(0)) + self.base_ret_addr = merge_bytes(base_ret_addr) + self.reads = [] + while len(self) != 0: + read = [] + for i in range(4): + read.append(self.pop(0)) + self.reads.append(EtherboneRead(merge_bytes(read))) + self.encoded = False + + def __repr__(self): + r = "Reads\n" + r += "--------\n" + r += "BaseRetAddr @ 0x{:08x}\n".format(self.base_ret_addr) + for read in self.reads: + r += read.__repr__() + "\n" + return r + +class EtherboneRecord(Packet): + def __init__(self, init=[]): + Packet.__init__(self, init) + self.writes = None + self.reads = None + self.encoded = init != [] + + def get_writes(self): + if self.wcount == 0: + return None + else: + writes = [] + for i in range((self.wcount+1)*4): + writes.append(self.pop(0)) + return EtherboneWrites(writes) + + def get_reads(self): + if self.rcount == 0: + return None + else: + reads = [] + for i in range((self.rcount+1)*4): + reads.append(self.pop(0)) + return EtherboneReads(reads) + + def decode(self): + if not self.encoded: + raise ValueError + header = [] + for byte in self[:etherbone_record_header_len]: + header.append(self.pop(0)) + for k, v in sorted(etherbone_record_header.items()): + setattr(self, k, get_field_data(v, header)) + self.writes = self.get_writes() + if self.writes is not None: + self.writes.decode() + self.reads = self.get_reads() + if self.reads is not None: + self.reads.decode() + self.encoded = False + + def set_writes(self, writes): + self.wcount = len(writes.writes) + writes.encode() + for byte in writes: + self.append(byte) + + def set_reads(self, reads): + self.rcount = len(reads.reads) + reads.encode() + for byte in reads: + self.append(byte) + + def encode(self): + if self.encoded: + raise ValueError + if self.writes is not None: + self.set_writes(self.writes) + if self.reads is not None: + self.set_reads(self.reads) + header = 0 + for k, v in sorted(etherbone_record_header.items()): + value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") + header += (value << v.offset+(v.byte*8)) + for d in split_bytes(header, etherbone_record_header_len): + self.insert(0, d) + self.encoded = True + + def __repr__(self, n=0): + r = "Record {}\n".format(n) + r += "--------\n" + if self.encoded: + for d in self: + r += "{:02x}".format(d) + else: + for k in sorted(etherbone_record_header.keys()): + r += k + " : 0x{:0x}\n".format(getattr(self,k)) + if self.wcount != 0: + r += self.writes.__repr__() + if self.rcount != 0: + r += self.reads.__repr__() + return r + +class EtherbonePacket(Packet): + def __init__(self, init=[]): + Packet.__init__(self, init) + self.encoded = init != [] + self.records = [] + + self.magic = etherbone_magic + self.version = etherbone_version + self.addr_size = 32//8 + self.port_size = 32//8 + self.nr = 0 + self.pr = 0 + self.pf = 0 + + def get_records(self): + records = [] + done = False + payload = self + while len(payload) != 0: + record = EtherboneRecord(payload) + record.decode() + records.append(copy.deepcopy(record)) + payload = record + return records + + def decode(self): + if not self.encoded: + raise ValueError + header = [] + for byte in self[:etherbone_packet_header_len]: + header.append(self.pop(0)) + for k, v in sorted(etherbone_packet_header.items()): + setattr(self, k, get_field_data(v, header)) + self.records = self.get_records() + self.encoded = False + + def set_records(self, records): + for record in records: + record.encode() + for byte in record: + self.append(byte) + + def encode(self): + if self.encoded: + raise ValueError + self.set_records(self.records) + header = 0 + for k, v in sorted(etherbone_packet_header.items()): + value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") + header += (value << v.offset+(v.byte*8)) + for d in split_bytes(header, etherbone_packet_header_len): + self.insert(0, d) + self.encoded = True + + def __repr__(self): + r = "Packet\n" + r += "--------\n" + if self.encoded: + for d in self: + r += "{:02x}".format(d) + else: + for k in sorted(etherbone_packet_header.keys()): + r += k + " : 0x{:0x}\n".format(getattr(self,k)) + for i, record in enumerate(self.records): + r += record.__repr__(i) + return r + +class Etherbone(Module): + def __init__(self, udp, debug=False): + self.udp = udp + self.debug = debug + self.tx_packets = [] + self.tx_packet = EtherbonePacket() + self.rx_packet = EtherbonePacket() + + udp.set_etherbone_callback(self.callback) + + def send(self, packet): + packet.encode() + if self.debug: + print_etherbone(">>>>>>>>") + print_etherbone(packet) + udp_packet = udp.UDPPacket(packet) + udp_packet.src_port = 0x1234 # XXX + udp_packet.dst_port = 20000 # XXX + udp_packet.length = len(packet) + udp_packet.checksum = 0 + self.udp.send(udp_packet) + + def receive(self): + self.rx_packet = EtherbonePacket() + while not self.rx_packet.done: + yield + + def callback(self, packet): + packet = EtherbonePacket(packet) + packet.decode() + if self.debug: + print_etherbone("<<<<<<<<") + print_etherbone(packet) + self.rx_packet = packet + self.rx_packet.done = True + self.process(packet) + + def process(self, packet): + pass + +if __name__ == "__main__": + # Writes/Reads + writes = EtherboneWrites(base_addr=0x1000, datas=[i for i in range(16)]) + reads = EtherboneReads(base_ret_addr=0x2000, addrs=[i for i in range(16)]) + + # Record + record = EtherboneRecord() + record.writes = writes + record.reads = reads + record.bca = 0 + record.rca = 0 + record.rff = 0 + record.cyc = 0 + record.wca = 0 + record.wff = 0 + record.byte_enable = 0 + record.wcount = len(writes.get_datas()) + record.rcount = len(reads.get_addrs()) + + # Packet + packet = EtherbonePacket() + packet.records = [copy.deepcopy(record) for i in range(8)] + packet.nr = 0 + packet.pr = 0 + packet.pf = 0 + #print(packet) + packet.encode() + #print(packet) + + # Send packet over UDP to check against Wireshark dissector + import socket + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.sendto(bytes(packet), ("192.168.1.1", 20000)) + + packet = EtherbonePacket(packet) + packet.decode() + print(packet) diff --git a/misoclib/liteeth/test/model/icmp.py b/misoclib/liteeth/test/model/icmp.py new file mode 100644 index 00000000..cf54b17c --- /dev/null +++ b/misoclib/liteeth/test/model/icmp.py @@ -0,0 +1,102 @@ +import math + +from liteeth.common import * +from liteeth.test.common import * + +from liteeth.test.model import ip + +def print_icmp(s): + print_with_prefix(s, "[ICMP]") + +# ICMP model +class ICMPPacket(Packet): + def __init__(self, init=[]): + Packet.__init__(self, init) + + def decode(self): + header = [] + for byte in self[:icmp_header_len]: + header.append(self.pop(0)) + for k, v in sorted(icmp_header.items()): + setattr(self, k, get_field_data(v, header)) + + def encode(self): + header = 0 + for k, v in sorted(icmp_header.items()): + value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") + header += (value << v.offset+(v.byte*8)) + for d in split_bytes(header, icmp_header_len): + self.insert(0, d) + + def __repr__(self): + r = "--------\n" + for k in sorted(icmp_header.keys()): + r += k + " : 0x{:0x}\n".format(getattr(self,k)) + r += "payload: " + for d in self: + r += "{:02x}".format(d) + return r + +class ICMP(Module): + def __init__(self, ip, ip_address, debug=False): + self.ip = ip + self.ip_address = ip_address + self.debug = debug + self.tx_packets = [] + self.tx_packet = ICMPPacket() + self.rx_packet = ICMPPacket() + + self.ip.set_icmp_callback(self.callback) + + def send(self, packet): + packet.encode() + if self.debug: + print_icmp(">>>>>>>>") + print_icmp(packet) + ip_packet = ip.IPPacket(packet) + ip_packet.version = 0x4 + ip_packet.ihl = 0x5 + ip_packet.total_length = len(packet) + ip_packet.ihl + ip_packet.identification = 0 + ip_packet.flags = 0 + ip_packet.fragment_offset = 0 + ip_packet.ttl = 0x80 + ip_packet.sender_ip = self.ip_address + ip_packet.target_ip = 0x12345678 # XXX + ip_packet.checksum = 0 + ip_packet.protocol = icmp_protocol + self.ip.send(ip_packet) + + def callback(self, packet): + packet = ICMPPacket(packet) + packet.decode() + if self.debug: + print_icmp("<<<<<<<<") + print_icmp(packet) + self.process(packet) + + def process(self, packet): + pass + +if __name__ == "__main__": + from liteeth.test.model.dumps import * + from liteeth.test.model.mac import * + from liteeth.test.model.ip import * + errors = 0 + # ICMP packet + packet = MACPacket(ping_request) + packet.decode_remove_header() + #print(packet) + packet = IPPacket(packet) + packet.decode() + #print(packet) + packet = ICMPPacket(packet) + packet.decode() + #print(packet) + errors += verify_packet(packet, ping_request_infos) + packet.encode() + packet.decode() + #print(packet) + errors += verify_packet(packet, ping_request_infos) + + print("icmp errors " + str(errors)) \ No newline at end of file diff --git a/misoclib/liteeth/test/model/ip.py b/misoclib/liteeth/test/model/ip.py new file mode 100644 index 00000000..03c5f8b5 --- /dev/null +++ b/misoclib/liteeth/test/model/ip.py @@ -0,0 +1,148 @@ +import math + +from liteeth.common import * +from liteeth.test.common import * + +from liteeth.test.model import mac + +def print_ip(s): + print_with_prefix(s, "[IP]") + +def carry_around_add(a, b): + c = a + b + return (c & 0xffff) + (c >> 16) + +def checksum(msg): + s = 0 + for i in range(0, len(msg), 2): + w = msg[i] + (msg[i+1] << 8) + s = carry_around_add(s, w) + return ~s & 0xffff + +# IP model +class IPPacket(Packet): + def __init__(self, init=[]): + Packet.__init__(self, init) + + def get_checksum(self): + return self[10] | (self[11] << 8) + + def check_checksum(self): + return checksum(self[:ipv4_header_len]) == 0 + + def decode(self): + header = [] + for byte in self[:ipv4_header_len]: + header.append(self.pop(0)) + for k, v in sorted(ipv4_header.items()): + setattr(self, k, get_field_data(v, header)) + + def encode(self): + header = 0 + for k, v in sorted(ipv4_header.items()): + value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") + header += (value << v.offset+(v.byte*8)) + for d in split_bytes(header, ipv4_header_len): + self.insert(0, d) + + def insert_checksum(self): + self[10] = 0 + self[11] = 0 + c = checksum(self[:ipv4_header_len]) + self[10] = c & 0xff + self[11] = (c >> 8) & 0xff + + def __repr__(self): + r = "--------\n" + for k in sorted(ipv4_header.keys()): + r += k + " : 0x{:0x}\n".format(getattr(self,k)) + r += "payload: " + for d in self: + r += "{:02x}".format(d) + return r + +class IP(Module): + def __init__(self, mac, mac_address, ip_address, debug=False, loopback=False): + self.mac = mac + self.mac_address = mac_address + self.ip_address = ip_address + self.debug = debug + self.loopback = loopback + self.rx_packet = IPPacket() + self.table = {} + self.request_pending = False + + self.udp_callback = None + self.icmp_callback = None + + self.mac.set_ip_callback(self.callback) + + def set_udp_callback(self, callback): + self.udp_callback = callback + + def set_icmp_callback(self, callback): + self.icmp_callback = callback + + def send(self, packet): + packet.encode() + packet.insert_checksum() + if self.debug: + print_ip(">>>>>>>>") + print_ip(packet) + mac_packet = mac.MACPacket(packet) + mac_packet.target_mac = 0x12345678abcd # XXX + mac_packet.sender_mac = self.mac_address + mac_packet.ethernet_type = ethernet_type_ip + self.mac.send(mac_packet) + + def callback(self, packet): + packet = IPPacket(packet) + if not packet.check_checksum(): + received = packet.get_checksum() + packet.insert_checksum() + expected = packet.get_checksum() + raise ValueError("Checksum error received {:04x} / expected {:04x}".format(received, expected)) + packet.decode() + if self.debug: + print_ip("<<<<<<<<") + print_ip(packet) + if self.loopback: + self.send(packet) + else: + if packet.version != 0x4: + raise ValueError + if packet.ihl != 0x5: + raise ValueError + self.process(packet) + + def process(self, packet): + if packet.protocol == udp_protocol: + if self.udp_callback is not None: + self.udp_callback(packet) + elif packet.protocol == icmp_protocol: + if self.icmp_callback is not None: + self.icmp_callback(packet) + +if __name__ == "__main__": + from liteeth.test.model.dumps import * + from liteeth.test.model.mac import * + errors = 0 + # UDP packet + packet = MACPacket(udp) + packet.decode_remove_header() + #print(packet) + packet = IPPacket(packet) + # check decoding + errors += not packet.check_checksum() + packet.decode() + #print(packet) + errors += verify_packet(packet, {}) + # check encoding + packet.encode() + packet.insert_checksum() + errors += not packet.check_checksum() + packet.decode() + #print(packet) + errors += verify_packet(packet, {}) + + print("ip errors " + str(errors)) \ No newline at end of file diff --git a/misoclib/liteeth/test/model/mac.py b/misoclib/liteeth/test/model/mac.py new file mode 100644 index 00000000..9267f88b --- /dev/null +++ b/misoclib/liteeth/test/model/mac.py @@ -0,0 +1,150 @@ +import math, binascii + +from liteeth.common import * +from liteeth.test.common import * + +def print_mac(s): + print_with_prefix(s, "[MAC]") + +preamble = split_bytes(eth_preamble, 8, "little") + +def crc32(l): + crc = [] + crc_bytes = split_bytes(binascii.crc32(bytes(l)), 4, "little") + for byte in crc_bytes: + crc.append(int(byte)) + return crc + +# MAC model +class MACPacket(Packet): + def __init__(self, init=[]): + Packet.__init__(self, init) + self.preamble_error = False + self.crc_error = False + + def check_remove_preamble(self): + if comp(self[0:8], preamble): + for i in range(8): + self.pop(0) + return False + else: + return True + + def check_remove_crc(self): + if comp(self[-4:], crc32(self[:-4])): + for i in range(4): + self.pop() + return False + else: + return True + + def decode_remove_header(self): + header = [] + for byte in self[:mac_header_len]: + header.append(self.pop(0)) + for k, v in sorted(mac_header.items()): + setattr(self, k, get_field_data(v, header)) + + def decode(self): + self.preamble_error = self.check_remove_preamble() + self.crc_error = self.check_remove_crc() + if self.crc_error or self.preamble_error: + raise ValueError # XXX handle this properly + else: + self.decode_remove_header() + + def encode_header(self): + header = 0 + for k, v in sorted(mac_header.items()): + value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") + header += (value << v.offset+(v.byte*8)) + for d in split_bytes(header, mac_header_len): + self.insert(0, d) + + def insert_crc(self): + for d in crc32(self): + self.append(d) + + def insert_preamble(self): + for d in reversed(preamble): + self.insert(0, d) + + def encode(self): + self.encode_header() + self.insert_crc() + self.insert_preamble() + + def __repr__(self): + r = "--------\n" + for k in sorted(mac_header.keys()): + r += k + " : 0x{:0x}\n".format(getattr(self,k)) + r += "payload: " + for d in self: + r += "{:02x}".format(d) + return r + +class MAC(Module): + def __init__(self, phy, debug=False, loopback=False): + self.phy = phy + self.debug = debug + self.loopback = loopback + self.rx_packet = MACPacket() + + self.ip_callback = None + self.arp_callback = None + + phy.set_mac_callback(self.callback) + + def set_ip_callback(self, callback): + self.ip_callback = callback + + def set_arp_callback(self, callback): + self.arp_callback = callback + + def send(self, packet): + if self.debug: + print_mac(">>>>>>>>") + print_mac(packet) + packet.encode() + self.phy.send(packet) + + def callback(self, datas): + packet = MACPacket(datas) + packet.decode() + if self.debug: + print_mac("<<<<<<<<") + print_mac(packet) + if self.loopback: + self.send(packet) + else: + if packet.ethernet_type == ethernet_type_ip: + if self.ip_callback is not None: + self.ip_callback(packet) + elif packet.ethernet_type == ethernet_type_arp: + if self.arp_callback is not None: + self.arp_callback(packet) + else: + raise ValueError # XXX handle this properly + +if __name__ == "__main__": + from liteeth.test.model.dumps import * + errors = 0 + packet = MACPacket(arp_request) + packet.decode_remove_header() + #print(packet) + errors += verify_packet(packet, arp_request_infos) + packet.encode_header() + packet.decode_remove_header() + #print(packet) + errors += verify_packet(packet, arp_request_infos) + + #print(packet) + packet = MACPacket(arp_reply) + packet.decode_remove_header() + errors += verify_packet(packet, arp_reply_infos) + packet.encode_header() + packet.decode_remove_header() + #print(packet) + errors += verify_packet(packet, arp_reply_infos) + + print("mac errors " + str(errors)) diff --git a/misoclib/liteeth/test/model/phy.py b/misoclib/liteeth/test/model/phy.py new file mode 100644 index 00000000..1401110f --- /dev/null +++ b/misoclib/liteeth/test/model/phy.py @@ -0,0 +1,56 @@ +from liteeth.common import * +from liteeth.test.common import * + +def print_phy(s): + print_with_prefix(s, "[PHY]") + +# PHY model +class PHYSource(PacketStreamer): + def __init__(self, dw): + PacketStreamer.__init__(self, eth_phy_description(dw)) + +class PHYSink(PacketLogger): + def __init__(self, dw): + PacketLogger.__init__(self, eth_phy_description(dw)) + +class PHY(Module): + def __init__(self, dw, debug=False): + self.dw = dw + self.debug = debug + + self.submodules.phy_source = PHYSource(dw) + self.submodules.phy_sink = PHYSink(dw) + + self.source = self.phy_source.source + self.sink = self.phy_sink.sink + + self.mac_callback = None + + def set_mac_callback(self, callback): + self.mac_callback = callback + + def send(self, datas): + packet = Packet(datas) + if self.debug: + r = ">>>>>>>>\n" + r += "length " + str(len(datas)) + "\n" + for d in datas: + r += "{:02x}".format(d) + print_phy(r) + self.phy_source.send(packet) + + def receive(self): + yield from self.phy_sink.receive() + if self.debug: + r = "<<<<<<<<\n" + r += "length " + str(len(self.phy_sink.packet)) + "\n" + for d in self.phy_sink.packet: + r += "{:02x}".format(d) + print_phy(r) + self.packet = self.phy_sink.packet + + def gen_simulation(self, selfp): + while True: + yield from self.receive() + if self.mac_callback is not None: + self.mac_callback(self.packet) diff --git a/misoclib/liteeth/test/model/udp.py b/misoclib/liteeth/test/model/udp.py new file mode 100644 index 00000000..20e0e6f2 --- /dev/null +++ b/misoclib/liteeth/test/model/udp.py @@ -0,0 +1,117 @@ +import math + +from liteeth.common import * +from liteeth.test.common import * + +from liteeth.test.model import ip + +def print_udp(s): + print_with_prefix(s, "[UDP]") + +# UDP model +class UDPPacket(Packet): + def __init__(self, init=[]): + Packet.__init__(self, init) + + def decode(self): + header = [] + for byte in self[:udp_header_len]: + header.append(self.pop(0)) + for k, v in sorted(udp_header.items()): + setattr(self, k, get_field_data(v, header)) + + def encode(self): + header = 0 + for k, v in sorted(udp_header.items()): + value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") + header += (value << v.offset+(v.byte*8)) + for d in split_bytes(header, udp_header_len): + self.insert(0, d) + + def __repr__(self): + r = "--------\n" + for k in sorted(udp_header.keys()): + r += k + " : 0x{:0x}\n".format(getattr(self,k)) + r += "payload: " + for d in self: + r += "{:02x}".format(d) + return r + +class UDP(Module): + def __init__(self, ip, ip_address, debug=False, loopback=False): + self.ip = ip + self.ip_address = ip_address + self.debug = debug + self.loopback = loopback + self.tx_packets = [] + self.tx_packet = UDPPacket() + self.rx_packet = UDPPacket() + + self.etherbone_callback = None + + self.ip.set_udp_callback(self.callback) + + def set_etherbone_callback(self, callback): + self.etherbone_callback = callback + + def send(self, packet): + packet.encode() + if self.debug: + print_udp(">>>>>>>>") + print_udp(packet) + ip_packet = ip.IPPacket(packet) + ip_packet.version = 0x4 + ip_packet.ihl = 0x5 + ip_packet.total_length = len(packet) + ip_packet.ihl + ip_packet.identification = 0 + ip_packet.flags = 0 + ip_packet.fragment_offset = 0 + ip_packet.ttl = 0x80 + ip_packet.sender_ip = self.ip_address + ip_packet.target_ip = 0x12345678 # XXX + ip_packet.checksum = 0 + ip_packet.protocol = udp_protocol + self.ip.send(ip_packet) + + def callback(self, packet): + packet = UDPPacket(packet) + packet.decode() + if self.debug: + print_udp("<<<<<<<<") + print_udp(packet) + if self.loopback: + self.send(packet) + else: + self.process(packet) + + def process(self, packet): + if packet.dst_port == 20000: + if self.etherbone_callback is not None: + self.etherbone_callback(packet) + +if __name__ == "__main__": + from liteeth.test.model.dumps import * + from liteeth.test.model.mac import * + from liteeth.test.model.ip import * + errors = 0 + # UDP packet + packet = MACPacket(udp) + packet.decode_remove_header() + #print(packet) + packet = IPPacket(packet) + packet.decode() + #print(packet) + packet = UDPPacket(packet) + packet.decode() + #print(packet) + if packet.length != (len(packet)+udp_header_len): + errors += 1 + errors += verify_packet(packet, udp_infos) + packet.encode() + packet.decode() + #print(packet) + if packet.length != (len(packet)+udp_header_len): + errors += 1 + errors += verify_packet(packet, udp_infos) + + print("udp errors " + str(errors)) \ No newline at end of file diff --git a/misoclib/liteeth/test/udp_tb.py b/misoclib/liteeth/test/udp_tb.py new file mode 100644 index 00000000..74811dc7 --- /dev/null +++ b/misoclib/liteeth/test/udp_tb.py @@ -0,0 +1,70 @@ +from migen.fhdl.std import * +from migen.bus import wishbone +from migen.bus.transactions import * +from migen.sim.generic import run_simulation + +from liteeth.common import * +from liteeth.core import LiteEthUDPIPCore + +from liteeth.test.common import * +from liteeth.test.model import phy, mac, arp, ip, udp + +ip_address = 0x12345678 +mac_address = 0x12345678abcd + +class TB(Module): + def __init__(self, dw=8): + self.dw = dw + self.submodules.phy_model = phy.PHY(8, debug=False) + self.submodules.mac_model = mac.MAC(self.phy_model, debug=False, loopback=False) + self.submodules.arp_model = arp.ARP(self.mac_model, mac_address, ip_address, debug=False) + self.submodules.ip_model = ip.IP(self.mac_model, mac_address, ip_address, debug=False, loopback=False) + self.submodules.udp_model = udp.UDP(self.ip_model, ip_address, debug=False, loopback=True) + + self.submodules.core = LiteEthUDPIPCore(self.phy_model, mac_address, ip_address, 100000) + udp_port = self.core.udp.crossbar.get_port(0x5678, dw) + self.submodules.streamer = PacketStreamer(eth_udp_user_description(dw)) + self.submodules.logger = PacketLogger(eth_udp_user_description(dw)) + self.comb += [ + Record.connect(self.streamer.source, udp_port.sink), + udp_port.sink.ip_address.eq(0x12345678), + udp_port.sink.src_port.eq(0x1234), + udp_port.sink.dst_port.eq(0x5678), + udp_port.sink.length.eq(64//(dw//8)), + Record.connect(udp_port.source, self.logger.sink) + ] + + # use sys_clk for each clock_domain + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += [ + self.cd_eth_rx.clk.eq(ClockSignal()), + self.cd_eth_rx.rst.eq(ResetSignal()), + self.cd_eth_tx.clk.eq(ClockSignal()), + self.cd_eth_tx.rst.eq(ResetSignal()), + ] + + def gen_simulation(self, selfp): + selfp.cd_eth_rx.rst = 1 + selfp.cd_eth_tx.rst = 1 + yield + selfp.cd_eth_rx.rst = 0 + selfp.cd_eth_tx.rst = 0 + + for i in range(100): + yield + + while True: + packet = Packet([i for i in range(64//(self.dw//8))]) + yield from self.streamer.send(packet) + yield from self.logger.receive() + + # check results + s, l, e = check(packet, self.logger.packet) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + + +if __name__ == "__main__": + run_simulation(TB(8), ncycles=2048, vcd_name="my.vcd", keep_files=True) + run_simulation(TB(16), ncycles=2048, vcd_name="my.vcd", keep_files=True) + run_simulation(TB(32), ncycles=2048, vcd_name="my.vcd", keep_files=True) \ No newline at end of file diff --git a/misoclib/make.py b/misoclib/make.py new file mode 100644 index 00000000..c55c8339 --- /dev/null +++ b/misoclib/make.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 + +import sys, os, argparse, subprocess, struct, importlib + +from mibuild.tools import write_to_file +from migen.util.misc import autotype +from migen.fhdl import verilog, edif +from migen.fhdl.structure import _Fragment +from migen.bank.description import CSRStatus +from mibuild import tools +from mibuild.xilinx_common import * + +from liteeth.common import * + +def get_csr_csv(regions): + r = "" + for name, origin, busword, obj in regions: + if not isinstance(obj, Memory): + for csr in obj: + nr = (csr.size + busword - 1)//busword + r += "{}_{},0x{:08x},{},{}\n".format(name, csr.name, origin, nr, "ro" if isinstance(csr, CSRStatus) else "rw") + origin += 4*nr + return r + +def _import(default, name): + return importlib.import_module(default + "." + name) + +def _get_args(): + parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, + description="""\ +LiteEth - based on Migen. + +This program builds and/or loads LiteEth components. +One or several actions can be specified: + +clean delete previous build(s). +build-rtl build verilog rtl. +build-bitstream build-bitstream build FPGA bitstream. +build-csr-csv save CSR map into CSV file. + +load-bitstream load bitstream into volatile storage. + +all clean, build-csr-csv, build-bitstream, load-bitstream. +""") + + parser.add_argument("-t", "--target", default="base", help="Core type to build") + parser.add_argument("-s", "--sub-target", default="", help="variant of the Core type to build") + parser.add_argument("-p", "--platform", default=None, help="platform to build for") + parser.add_argument("-Ot", "--target-option", default=[], nargs=2, action="append", help="set target-specific option") + parser.add_argument("-Op", "--platform-option", default=[("programmer", "vivado")], nargs=2, action="append", help="set platform-specific option") + parser.add_argument("--csr_csv", default="./test/csr.csv", help="CSV file to save the CSR map into") + + parser.add_argument("action", nargs="+", help="specify an action") + + return parser.parse_args() + +# Note: misoclib need to be installed as a python library + +if __name__ == "__main__": + args = _get_args() + + # create top-level Core object + target_module = _import("targets", args.target) + if args.sub_target: + top_class = getattr(target_module, args.sub_target) + else: + top_class = target_module.default_subtarget + + if args.platform is None: + platform_name = top_class.default_platform + else: + platform_name = args.platform + platform_module = _import("platforms", platform_name) + platform_kwargs = dict((k, autotype(v)) for k, v in args.platform_option) + platform = platform_module.Platform(**platform_kwargs) + + build_name = top_class.__name__.lower() + "-" + platform_name + top_kwargs = dict((k, autotype(v)) for k, v in args.target_option) + soc = top_class(platform, **top_kwargs) + soc.finalize() + + # decode actions + action_list = ["clean", "build-csr-csv", "build-bitstream", "load-bitstream", "all"] + actions = {k: False for k in action_list} + for action in args.action: + if action in actions: + actions[action] = True + else: + print("Unknown action: "+action+". Valid actions are:") + for a in action_list: + print(" "+a) + sys.exit(1) + + print(""" + __ _ __ ______ __ + / / (_) /____ / __/ /_/ / + / /__/ / __/ -_) _// __/ _ \\ + /____/_/\__/\__/___/\__/_//_/ + + A small footprint and configurable Ethernet + core powered by Migen +====== Building options: ====== +Platform: {} +Target: {} +Subtarget: {} +System Clk: {} MHz +===============================""".format( + platform_name, + args.target, + top_class.__name__, + soc.clk_freq/1000000 + ) +) + + # dependencies + if actions["all"]: + actions["build-csr-csv"] = True + actions["build-bitstream"] = True + actions["load-bitstream"] = True + + if actions["build-bitstream"]: + actions["build-csr-csv"] = True + actions["build-bitstream"] = True + actions["load-bitstream"] = True + + if actions["clean"]: + subprocess.call(["rm", "-rf", "build/*"]) + + if actions["build-csr-csv"]: + csr_csv = get_csr_csv(soc.cpu_csr_regions) + write_to_file(args.csr_csv, csr_csv) + + if actions["build-bitstream"]: + vns = platform.build(soc, build_name=build_name) + if hasattr(soc, "do_exit") and vns is not None: + if hasattr(soc.do_exit, '__call__'): + soc.do_exit(vns) + + if actions["load-bitstream"]: + prog = platform.create_programmer() + prog.load_bitstream("build/" + build_name + platform.bitstream_ext) diff --git a/misoclib/platforms/kc705.py b/misoclib/platforms/kc705.py new file mode 100644 index 00000000..500d1b07 --- /dev/null +++ b/misoclib/platforms/kc705.py @@ -0,0 +1,138 @@ +from mibuild.generic_platform import * +from mibuild.crg import SimpleCRG +from mibuild.xilinx_common import CRG_DS +from mibuild.xilinx_ise import XilinxISEPlatform +from mibuild.xilinx_vivado import XilinxVivadoPlatform +from mibuild.programmer import * + +def _run_vivado(cmds): + with subprocess.Popen("vivado -mode tcl", stdin=subprocess.PIPE, shell=True) as process: + process.stdin.write(cmds.encode("ASCII")) + process.communicate() + +class VivadoProgrammer(Programmer): + needs_bitreverse = False + + def load_bitstream(self, bitstream_file): + cmds = """open_hw +connect_hw_server +open_hw_target [lindex [get_hw_targets -of_objects [get_hw_servers localhost]] 0] + +set_property PROBES.FILE {{}} [lindex [get_hw_devices] 0] +set_property PROGRAM.FILE {{{bitstream}}} [lindex [get_hw_devices] 0] + +program_hw_devices [lindex [get_hw_devices] 0] +refresh_hw_device [lindex [get_hw_devices] 0] + +quit +""".format(bitstream=bitstream_file) + _run_vivado(cmds) + + def flash(self, address, data_file): + raise NotImplementedError + +_io = [ + ("user_led", 0, Pins("AB8"), IOStandard("LVCMOS15")), + ("user_led", 1, Pins("AA8"), IOStandard("LVCMOS15")), + ("user_led", 2, Pins("AC9"), IOStandard("LVCMOS15")), + ("user_led", 3, Pins("AB9"), IOStandard("LVCMOS15")), + ("user_led", 4, Pins("AE26"), IOStandard("LVCMOS25")), + ("user_led", 5, Pins("G19"), IOStandard("LVCMOS25")), + ("user_led", 6, Pins("E18"), IOStandard("LVCMOS25")), + ("user_led", 7, Pins("F16"), IOStandard("LVCMOS25")), + + ("cpu_reset", 0, Pins("AB7"), IOStandard("LVCMOS15")), + + ("clk200", 0, + Subsignal("p", Pins("AD12"), IOStandard("LVDS")), + Subsignal("n", Pins("AD11"), IOStandard("LVDS")) + ), + + ("clk156", 0, + Subsignal("p", Pins("K28"), IOStandard("LVDS_25")), + Subsignal("n", Pins("K29"), IOStandard("LVDS_25")) + ), + + + ("serial", 0, + Subsignal("cts", Pins("L27")), + Subsignal("rts", Pins("K23")), + Subsignal("tx", Pins("K24")), + Subsignal("rx", Pins("M19")), + IOStandard("LVCMOS25") + ), + + ("eth_clocks", 0, + Subsignal("tx", Pins("M28")), + Subsignal("gtx", Pins("K30")), + Subsignal("rx", Pins("U27")), + IOStandard("LVCMOS25") + ), + ("eth", 0, + Subsignal("rst_n", Pins("L20")), + Subsignal("int_n", Pins("N30")), + Subsignal("mdio", Pins("J21")), + Subsignal("mdc", Pins("R23")), + Subsignal("dv", Pins("R28")), + Subsignal("rx_er", Pins("V26")), + Subsignal("rx_data", Pins("U30 U25 T25 U28 R19 T27 T26 T28")), + Subsignal("tx_en", Pins("M27")), + Subsignal("tx_er", Pins("N29")), + Subsignal("tx_data", Pins("N27 N25 M29 L28 J26 K26 L30 J28")), + Subsignal("col", Pins("W19")), + Subsignal("crs", Pins("R30")), + IOStandard("LVCMOS25") + ), + +] + +def Platform(*args, toolchain="vivado", programmer="xc3sprog", **kwargs): + if toolchain == "ise": + xilinx_platform = XilinxISEPlatform + elif toolchain == "vivado": + xilinx_platform = XilinxVivadoPlatform + else: + raise ValueError + + class RealPlatform(xilinx_platform): + bitgen_opt = "-g LCK_cycle:6 -g Binary:Yes -w -g ConfigRate:12 -g SPI_buswidth:4" + + def __init__(self, crg_factory=lambda p: CRG_DS(p, "clk200", "cpu_reset")): + xilinx_platform.__init__(self, "xc7k325t-ffg900-2", _io, crg_factory) + + def create_programmer(self): + if programmer == "xc3sprog": + return XC3SProg("jtaghs1_fast", "bscan_spi_kc705.bit") + elif programmer == "vivado": + return VivadoProgrammer() + else: + raise ValueError + + def do_finalize(self, fragment): + try: + self.add_period_constraint(self.lookup_request("clk156").p, 6.4) + except ConstraintError: + pass + try: + self.add_period_constraint(self.lookup_request("clk200").p, 5.0) + except ConstraintError: + pass + try: + self.add_period_constraint(self.lookup_request("eth_clocks").rx, 8.0) + except ConstraintError: + pass + self.add_platform_command(""" +create_clock -name sys_clk -period 6 [get_nets sys_clk] +create_clock -name eth_rx_clk -period 8 [get_nets eth_rx_clk] +create_clock -name eth_tx_clk -period 8 [get_nets eth_tx_clk] + +set_false_path -from [get_clocks sys_clk] -to [get_clocks eth_rx_clk] +set_false_path -from [get_clocks sys_clk] -to [get_clocks eth_tx_clk] +set_false_path -from [get_clocks eth_rx_clk] -to [get_clocks sys_clk] +set_false_path -from [get_clocks eth_tx_clk] -to [get_clocks sys_clk] + +set_property CFGBVS VCCO [current_design] +set_property CONFIG_VOLTAGE 2.5 [current_design] +""") + + return RealPlatform(*args, **kwargs) diff --git a/misoclib/setup.py b/misoclib/setup.py new file mode 100644 index 00000000..0f84b945 --- /dev/null +++ b/misoclib/setup.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +import sys, os +from setuptools import setup +from setuptools import find_packages + +here = os.path.abspath(os.path.dirname(__file__)) +README = open(os.path.join(here, "README")).read() + +required_version = (3, 3) +if sys.version_info < required_version: + raise SystemExit("LiteEth requires python {0} or greater".format( + ".".join(map(str, required_version)))) + +setup( + name="liteeth", + version="unknown", + description="small footprint and configurable Ethernet core", + long_description=README, + author="Florent Kermarrec", + author_email="florent@enjoy-digital.fr", + url="http://enjoy-digital.fr", + download_url="https://github.com/enjoy-digital/liteeth", + packages=find_packages(here), + license="GPL", + platforms=["Any"], + keywords="HDL ASIC FPGA hardware design", + classifiers=[ + "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", + "Environment :: Console", + "Development Status :: Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU General Public License (GPL)", + "Operating System :: OS Independent", + "Programming Language :: Python", + ], +) diff --git a/misoclib/targets/__init__.py b/misoclib/targets/__init__.py new file mode 100644 index 00000000..85efe5d9 --- /dev/null +++ b/misoclib/targets/__init__.py @@ -0,0 +1,26 @@ +import subprocess + +from migen.fhdl.std import * +from migen.bank.description import * + +def get_id(): + output = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode("ascii") + return int(output[:8], 16) + +class Identifier(Module, AutoCSR): + def __init__(self, sysid, frequency, revision=None): + self._r_sysid = CSRStatus(16) + self._r_revision = CSRStatus(32) + self._r_frequency = CSRStatus(32) + + ### + + if revision is None: + revision = get_id() + + self.comb += [ + self._r_sysid.status.eq(sysid), + self._r_revision.status.eq(revision), + self._r_frequency.status.eq(frequency), + ] + diff --git a/misoclib/targets/base.py b/misoclib/targets/base.py new file mode 100644 index 00000000..3fe1f2a4 --- /dev/null +++ b/misoclib/targets/base.py @@ -0,0 +1,224 @@ +import os + +from migen.bank import csrgen +from migen.bus import wishbone, csr +from migen.bus import wishbone2csr +from migen.genlib.cdc import * +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.bank.description import * + +from targets import * + +from litescope.common import * +from litescope.bridge.uart2wb import LiteScopeUART2WB +from litescope.frontend.la import LiteScopeLA +from litescope.core.port import LiteScopeTerm + +from liteeth.common import * +from liteeth.generic import * +from liteeth.phy.gmii import LiteEthPHYGMII +from liteeth.core import LiteEthUDPIPCore + +class _CRG(Module): + def __init__(self, platform): + self.clock_domains.cd_sys = ClockDomain() + self.reset = Signal() + + clk200 = platform.request("clk200") + clk200_se = Signal() + self.specials += Instance("IBUFDS", i_I=clk200.p, i_IB=clk200.n, o_O=clk200_se) + + pll_locked = Signal() + pll_fb = Signal() + pll_sys = Signal() + self.specials += [ + Instance("PLLE2_BASE", + p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, + + # VCO @ 1GHz + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=5.0, + p_CLKFBOUT_MULT=5, p_DIVCLK_DIVIDE=1, + i_CLKIN1=clk200_se, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, + + # 166MHz + p_CLKOUT0_DIVIDE=6, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=pll_sys, + + p_CLKOUT1_DIVIDE=2, p_CLKOUT1_PHASE=0.0, #o_CLKOUT1=, + + p_CLKOUT2_DIVIDE=2, p_CLKOUT2_PHASE=0.0, #o_CLKOUT2=, + + p_CLKOUT3_DIVIDE=2, p_CLKOUT3_PHASE=0.0, #o_CLKOUT3=, + + p_CLKOUT4_DIVIDE=2, p_CLKOUT4_PHASE=0.0, #o_CLKOUT4= + ), + Instance("BUFG", i_I=pll_sys, o_O=self.cd_sys.clk), + AsyncResetSynchronizer(self.cd_sys, ~pll_locked | platform.request("cpu_reset") | self.reset), + ] + +class GenSoC(Module): + csr_base = 0x00000000 + csr_data_width = 32 + csr_map = { + "bridge": 0, + "identifier": 1, + } + interrupt_map = {} + cpu_type = None + def __init__(self, platform, clk_freq): + self.clk_freq = clk_freq + # UART <--> Wishbone bridge + self.submodules.bridge = LiteScopeUART2WB(platform.request("serial"), clk_freq, baud=921600) + + # CSR bridge 0x00000000 (shadow @0x00000000) + self.submodules.wishbone2csr = wishbone2csr.WB2CSR(bus_csr=csr.Interface(self.csr_data_width)) + self._wb_masters = [self.bridge.wishbone] + self._wb_slaves = [(lambda a: a[23:25] == 0, self.wishbone2csr.wishbone)] + self.cpu_csr_regions = [] # list of (name, origin, busword, csr_list/Memory) + + # CSR + self.submodules.identifier = Identifier(0, int(clk_freq), 0) + + def add_wb_master(self, wbm): + if self.finalized: + raise FinalizeError + self._wb_masters.append(wbm) + + def add_wb_slave(self, address_decoder, interface): + if self.finalized: + raise FinalizeError + self._wb_slaves.append((address_decoder, interface)) + + def add_cpu_memory_region(self, name, origin, length): + self.cpu_memory_regions.append((name, origin, length)) + + def add_cpu_csr_region(self, name, origin, busword, obj): + self.cpu_csr_regions.append((name, origin, busword, obj)) + + def do_finalize(self): + # Wishbone + self.submodules.wishbonecon = wishbone.InterconnectShared(self._wb_masters, + self._wb_slaves, register=True) + + # CSR + self.submodules.csrbankarray = csrgen.BankArray(self, + lambda name, memory: self.csr_map[name if memory is None else name + "_" + memory.name_override], + data_width=self.csr_data_width) + self.submodules.csrcon = csr.Interconnect(self.wishbone2csr.csr, self.csrbankarray.get_buses()) + for name, csrs, mapaddr, rmap in self.csrbankarray.banks: + self.add_cpu_csr_region(name, 0xe0000000+0x800*mapaddr, flen(rmap.bus.dat_w), csrs) + for name, memory, mapaddr, mmap in self.csrbankarray.srams: + self.add_cpu_csr_region(name, 0xe0000000+0x800*mapaddr, flen(rmap.bus.dat_w), memory) + +class BaseSoC(GenSoC, AutoCSR): + default_platform = "kc705" + csr_map = { + "phy": 11, + "core": 12 + } + csr_map.update(GenSoC.csr_map) + def __init__(self, platform, clk_freq=166*1000000, + mac_address=0x10e2d5000000, + ip_address="192.168.1.40"): + GenSoC.__init__(self, platform, clk_freq) + self.submodules.crg = _CRG(platform) + + # wishbone SRAM (to test Wishbone over UART and Etherbone) + self.submodules.sram = wishbone.SRAM(1024) + self.add_wb_slave(lambda a: a[23:25] == 1, self.sram.bus) + + # ethernet PHY and UDP/IP stack + self.submodules.phy = LiteEthPHYGMII(platform.request("eth_clocks"), platform.request("eth")) + self.submodules.core = LiteEthUDPIPCore(self.phy, mac_address, convert_ip(ip_address), clk_freq) + +class BaseSoCDevel(BaseSoC, AutoCSR): + csr_map = { + "la": 20 + } + csr_map.update(BaseSoC.csr_map) + def __init__(self, platform): + BaseSoC.__init__(self, platform) + + self.core_icmp_rx_fsm_state = Signal(4) + self.core_icmp_tx_fsm_state = Signal(4) + self.core_udp_rx_fsm_state = Signal(4) + self.core_udp_tx_fsm_state = Signal(4) + self.core_ip_rx_fsm_state = Signal(4) + self.core_ip_tx_fsm_state = Signal(4) + self.core_arp_rx_fsm_state = Signal(4) + self.core_arp_tx_fsm_state = Signal(4) + self.core_arp_table_fsm_state = Signal(4) + + debug = ( + # MAC interface + self.core.mac.core.sink.stb, + self.core.mac.core.sink.sop, + self.core.mac.core.sink.eop, + self.core.mac.core.sink.ack, + self.core.mac.core.sink.data, + + self.core.mac.core.source.stb, + self.core.mac.core.source.sop, + self.core.mac.core.source.eop, + self.core.mac.core.source.ack, + self.core.mac.core.source.data, + + # ICMP interface + self.core.icmp.echo.sink.stb, + self.core.icmp.echo.sink.sop, + self.core.icmp.echo.sink.eop, + self.core.icmp.echo.sink.ack, + self.core.icmp.echo.sink.data, + + self.core.icmp.echo.source.stb, + self.core.icmp.echo.source.sop, + self.core.icmp.echo.source.eop, + self.core.icmp.echo.source.ack, + self.core.icmp.echo.source.data, + + # IP interface + self.core.ip.crossbar.master.sink.stb, + self.core.ip.crossbar.master.sink.sop, + self.core.ip.crossbar.master.sink.eop, + self.core.ip.crossbar.master.sink.ack, + self.core.ip.crossbar.master.sink.data, + self.core.ip.crossbar.master.sink.ip_address, + self.core.ip.crossbar.master.sink.protocol, + + # State machines + self.core_icmp_rx_fsm_state, + self.core_icmp_tx_fsm_state, + + self.core_arp_rx_fsm_state, + self.core_arp_tx_fsm_state, + self.core_arp_table_fsm_state, + + self.core_ip_rx_fsm_state, + self.core_ip_tx_fsm_state, + + self.core_udp_rx_fsm_state, + self.core_udp_tx_fsm_state + ) + self.submodules.la = LiteScopeLA(debug, 4096) + self.la.trigger.add_port(LiteScopeTerm(self.la.dw)) + + def do_finalize(self): + BaseSoC.do_finalize(self) + self.comb += [ + self.core_icmp_rx_fsm_state.eq(self.core.icmp.rx.fsm.state), + self.core_icmp_tx_fsm_state.eq(self.core.icmp.tx.fsm.state), + + self.core_arp_rx_fsm_state.eq(self.core.arp.rx.fsm.state), + self.core_arp_tx_fsm_state.eq(self.core.arp.tx.fsm.state), + self.core_arp_table_fsm_state.eq(self.core.arp.table.fsm.state), + + self.core_ip_rx_fsm_state.eq(self.core.ip.rx.fsm.state), + self.core_ip_tx_fsm_state.eq(self.core.ip.tx.fsm.state), + + self.core_udp_rx_fsm_state.eq(self.core.udp.rx.fsm.state), + self.core_udp_tx_fsm_state.eq(self.core.udp.tx.fsm.state) + ] + + def do_exit(self, vns): + self.la.export(vns, "test/la.csv") + +default_subtarget = BaseSoC diff --git a/misoclib/targets/etherbone.py b/misoclib/targets/etherbone.py new file mode 100644 index 00000000..0aaaf2d2 --- /dev/null +++ b/misoclib/targets/etherbone.py @@ -0,0 +1,71 @@ +from litescope.common import * +from litescope.frontend.la import LiteScopeLA +from litescope.core.port import LiteScopeTerm + +from liteeth.common import * +from liteeth.generic import * + +from targets.base import BaseSoC +from liteeth.core.etherbone import LiteEthEtherbone + +class EtherboneSoC(BaseSoC): + default_platform = "kc705" + def __init__(self, platform): + BaseSoC.__init__(self, platform, + mac_address=0x10e2d5000000, + ip_address="192.168.1.40") + self.submodules.etherbone = LiteEthEtherbone(self.core.udp, 20000) + self.add_wb_master(self.etherbone.master.bus) + +class EtherboneSoCDevel(EtherboneSoC): + csr_map = { + "la": 20 + } + csr_map.update(EtherboneSoC.csr_map) + def __init__(self, platform): + EtherboneSoC.__init__(self, platform) + debug = ( + # mmap stream from HOST + self.etherbone.master.sink.stb, + self.etherbone.master.sink.sop, + self.etherbone.master.sink.eop, + self.etherbone.master.sink.ack, + self.etherbone.master.sink.we, + self.etherbone.master.sink.count, + self.etherbone.master.sink.base_addr, + self.etherbone.master.sink.be, + self.etherbone.master.sink.addr, + self.etherbone.master.sink.data, + + # mmap stream to HOST + self.etherbone.master.source.stb, + self.etherbone.master.source.sop, + self.etherbone.master.source.eop, + self.etherbone.master.source.ack, + self.etherbone.master.source.we, + self.etherbone.master.source.count, + self.etherbone.master.source.base_addr, + self.etherbone.master.source.be, + self.etherbone.master.source.addr, + self.etherbone.master.source.data, + + # etherbone wishbone master + self.etherbone.master.bus.dat_w, + self.etherbone.master.bus.dat_r, + self.etherbone.master.bus.adr, + self.etherbone.master.bus.sel, + self.etherbone.master.bus.cyc, + self.etherbone.master.bus.stb, + self.etherbone.master.bus.ack, + self.etherbone.master.bus.we, + self.etherbone.master.bus.cti, + self.etherbone.master.bus.bte, + self.etherbone.master.bus.err + ) + self.submodules.la = LiteScopeLA(debug, 4096) + self.la.trigger.add_port(LiteScopeTerm(self.la.dw)) + + def do_exit(self, vns): + self.la.export(vns, "test/la.csv") + +default_subtarget = EtherboneSoC diff --git a/misoclib/targets/tty.py b/misoclib/targets/tty.py new file mode 100644 index 00000000..28cda9d8 --- /dev/null +++ b/misoclib/targets/tty.py @@ -0,0 +1,42 @@ +from litescope.common import * +from litescope.frontend.la import LiteScopeLA +from litescope.core.port import LiteScopeTerm + +from liteeth.common import * +from liteeth.generic import * + +from targets.base import BaseSoC +from liteeth.core.tty import LiteEthTTY + +class TTYSoC(BaseSoC): + default_platform = "kc705" + def __init__(self, platform): + BaseSoC.__init__(self, platform, + mac_address=0x10e2d5000000, + ip_address="192.168.1.40") + self.submodules.tty = LiteEthTTY(self.core.udp, convert_ip("192.168.1.12"), 10000) + self.comb += Record.connect(self.tty.source, self.tty.sink) + +class TTYSoCDevel(TTYSoC): + csr_map = { + "la": 20 + } + csr_map.update(TTYSoC.csr_map) + def __init__(self, platform): + TTYSoC.__init__(self, platform) + debug = ( + self.tty.sink.stb, + self.tty.sink.ack, + self.tty.sink.data, + + self.tty.source.stb, + self.tty.source.ack, + self.tty.source.data + ) + self.submodules.la = LiteScopeLA(debug, 4096) + self.la.trigger.add_port(LiteScopeTerm(self.la.dw)) + + def do_exit(self, vns): + self.la.export(vns, "test/la.csv") + +default_subtarget = TTYSoC diff --git a/misoclib/targets/udp.py b/misoclib/targets/udp.py new file mode 100644 index 00000000..9ce15ce5 --- /dev/null +++ b/misoclib/targets/udp.py @@ -0,0 +1,69 @@ +from litescope.common import * +from litescope.frontend.la import LiteScopeLA +from litescope.core.port import LiteScopeTerm + +from liteeth.common import * +from liteeth.generic import * + +from targets.base import BaseSoC + +class UDPSoC(BaseSoC): + default_platform = "kc705" + def __init__(self, platform): + BaseSoC.__init__(self, platform, + mac_address=0x10e2d5000000, + ip_address="192.168.1.40") + + # add udp loopback on port 6000 with dw=8 + self.add_udp_loopback(6000, 8, 8192, "loopback_8") + # add udp loopback on port 8000 with dw=32 + self.add_udp_loopback(8000, 32, 8192, "loopback_32") + + def add_udp_loopback(self, port, dw, depth, name=None): + port = self.core.udp.crossbar.get_port(port, dw) + buf = PacketBuffer(eth_udp_user_description(dw), depth//(dw//8), 8) + if name is None: + self.submodules += buf + else: + setattr(self.submodules, name, buf) + self.comb += Port.connect(port, buf) + +class UDPSoCDevel(UDPSoC): + csr_map = { + "la": 20 + } + csr_map.update(UDPSoC.csr_map) + def __init__(self, platform): + UDPSoC.__init__(self, platform) + debug = ( + self.loopback_8.sink.stb, + self.loopback_8.sink.sop, + self.loopback_8.sink.eop, + self.loopback_8.sink.ack, + self.loopback_8.sink.data, + + self.loopback_8.source.stb, + self.loopback_8.source.sop, + self.loopback_8.source.eop, + self.loopback_8.source.ack, + self.loopback_8.source.data, + + self.loopback_32.sink.stb, + self.loopback_32.sink.sop, + self.loopback_32.sink.eop, + self.loopback_32.sink.ack, + self.loopback_32.sink.data, + + self.loopback_32.source.stb, + self.loopback_32.source.sop, + self.loopback_32.source.eop, + self.loopback_32.source.ack, + self.loopback_32.source.data + ) + self.submodules.la = LiteScopeLA(debug, 4096) + self.la.trigger.add_port(LiteScopeTerm(self.la.dw)) + + def do_exit(self, vns): + self.la.export(vns, "test/la.csv") + +default_subtarget = UDPSoC diff --git a/misoclib/test/make.py b/misoclib/test/make.py new file mode 100644 index 00000000..05e13aad --- /dev/null +++ b/misoclib/test/make.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +import argparse, importlib + +def _get_args(): + parser = argparse.ArgumentParser() + parser.add_argument("-b", "--bridge", default="uart", help="Bridge to use") + parser.add_argument("--port", default=2, help="UART port") + parser.add_argument("--baudrate", default=921600, help="UART baudrate") + parser.add_argument("--ip_address", default="192.168.1.40", help="Etherbone IP address") + parser.add_argument("--udp_port", default=20000, help="Etherbone UDP port") + parser.add_argument("--busword", default=32, help="CSR busword") + + parser.add_argument("test", nargs="+", help="specify a test") + + return parser.parse_args() + +if __name__ == "__main__": + args = _get_args() + if args.bridge == "uart": + from litescope.host.driver.uart import LiteScopeUARTDriver + wb = LiteScopeUARTDriver(args.port, args.baudrate, "./csr.csv", int(args.busword), debug=False) + elif args.bridge == "etherbone": + from litescope.host.driver.etherbone import LiteScopeEtherboneDriver + wb = LiteScopeEtherboneDriver(args.ip_address, int(args.udp_port), "./csr.csv", int(args.busword), debug=False) + else: + ValueError("Invalid bridge {}".format(args.bridge)) + + def _import(name): + return importlib.import_module(name) + + for test in args.test: + t = _import("test_"+test) + t.main(wb) diff --git a/misoclib/test/test_etherbone.py b/misoclib/test/test_etherbone.py new file mode 100644 index 00000000..4622d524 --- /dev/null +++ b/misoclib/test/test_etherbone.py @@ -0,0 +1,60 @@ +import socket, time +from liteeth.test.model.etherbone import * + +SRAM_BASE = 0x02000000 + +import socket + +def main(wb): + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + # test probe + packet = EtherbonePacket() + packet.pf = 1 + packet.encode() + sock.sendto(bytes(packet), ("192.168.1.40", 20000)) + time.sleep(0.01) + + # test writes + writes_datas = [j for j in range(16)] + writes = EtherboneWrites(base_addr=SRAM_BASE, datas=writes_datas) + record = EtherboneRecord() + record.writes = writes + record.reads = None + record.bca = 0 + record.rca = 0 + record.rff = 0 + record.cyc = 0 + record.wca = 0 + record.wff = 0 + record.byte_enable = 0xf + record.wcount = len(writes_datas) + record.rcount = 0 + + packet = EtherbonePacket() + packet.records = [record] + packet.encode() + sock.sendto(bytes(packet), ("192.168.1.40", 20000)) + time.sleep(0.01) + + # test reads + reads_addrs = [SRAM_BASE+4*j for j in range(16)] + reads = EtherboneReads(base_ret_addr=0x1000, addrs=reads_addrs) + record = EtherboneRecord() + record.writes = None + record.reads = reads + record.bca = 0 + record.rca = 0 + record.rff = 0 + record.cyc = 0 + record.wca = 0 + record.wff = 0 + record.byte_enable = 0xf + record.wcount = 0 + record.rcount = len(reads_addrs) + + packet = EtherbonePacket() + packet.records = [record] + packet.encode() + sock.sendto(bytes(packet), ("192.168.1.40", 20000)) + time.sleep(0.01) diff --git a/misoclib/test/test_la.py b/misoclib/test/test_la.py new file mode 100644 index 00000000..c15de8c8 --- /dev/null +++ b/misoclib/test/test_la.py @@ -0,0 +1,24 @@ +import time +from litescope.host.driver.la import LiteScopeLADriver + +def main(wb): + la = LiteScopeLADriver(wb.regs, "la", debug=True) + + wb.open() + regs = wb.regs + ### + + conditions = {} + la.configure_term(port=0, cond=conditions) + la.configure_sum("term") + # Run Logic Analyzer + la.run(offset=2048, length=4000) + + while not la.done(): + pass + + la.upload() + la.save("dump.vcd") + + ### + wb.close() diff --git a/misoclib/test/test_regs.py b/misoclib/test/test_regs.py new file mode 100644 index 00000000..f2685cf5 --- /dev/null +++ b/misoclib/test/test_regs.py @@ -0,0 +1,12 @@ +def main(wb): + wb.open() + regs = wb.regs + ### + print("sysid : 0x%04x" %regs.identifier_sysid.read()) + print("revision : 0x%04x" %regs.identifier_revision.read()) + print("frequency : %d MHz" %(regs.identifier_frequency.read()/1000000)) + SRAM_BASE = 0x02000000 + wb.write(SRAM_BASE, [i for i in range(64)]) + print(wb.read(SRAM_BASE, 64)) + ### + wb.close() diff --git a/misoclib/test/test_tty.py b/misoclib/test/test_tty.py new file mode 100644 index 00000000..af6f2df0 --- /dev/null +++ b/misoclib/test/test_tty.py @@ -0,0 +1,36 @@ +import socket +import threading + +def test(fpga_ip, udp_port, test_message): + tx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + rx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + rx_sock.bind(("", udp_port)) + rx_sock.settimeout(0.5) + + def receive(): + while True: + try: + msg = rx_sock.recv(8192) + for byte in msg: + print(chr(byte), end="") + except: + break + + def send(): + tx_sock.sendto(bytes(test_message, "utf-8"), (fpga_ip, udp_port)) + + receive_thread = threading.Thread(target=receive) + receive_thread.start() + + send_thread = threading.Thread(target=send) + send_thread.start() + + try: + send_thread.join(5) + send_thread.join(5) + except KeyboardInterrupt: + pass + +def main(wb): + test_message = "LiteEth virtual TTY Hello world\n" + test("192.168.1.40", 10000, test_message) diff --git a/misoclib/test/test_udp.py b/misoclib/test/test_udp.py new file mode 100644 index 00000000..65d47b74 --- /dev/null +++ b/misoclib/test/test_udp.py @@ -0,0 +1,81 @@ +import socket +import time +import threading +import copy + +KB = 1024 +MB = 1024*KB +GB = 1024*MB + +def seed_to_data(seed, random=True): + if random: + return (seed * 0x31415979 + 1) & 0xffffffff + else: + return seed + +def check(p1, p2): + p1 = copy.deepcopy(p1) + p2 = copy.deepcopy(p2) + if isinstance(p1, int): + return 0, 1, int(p1 != p2) + else: + if len(p1) >= len(p2): + ref, res = p1, p2 + else: + ref, res = p2, p1 + shift = 0 + while((ref[0] != res[0]) and (len(res)>1)): + res.pop(0) + shift += 1 + length = min(len(ref), len(res)) + errors = 0 + for i in range(length): + if ref.pop(0) != res.pop(0): + errors += 1 + return shift, length, errors + +def generate_packet(seed, length): + r = [] + for i in range(length): + r.append(seed_to_data(seed, True)%0xff) # XXX FIXME + seed += 1 + return r, seed + +def test(fpga_ip, udp_port, test_size): + tx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + rx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + rx_sock.bind(("", udp_port)) + + def receive(): + rx_seed = 0 + while rx_seed < test_size: + data, addr = rx_sock.recvfrom(8192) + rx_packet = [] + for byte in data: + rx_packet.append(int(byte)) + rx_reference_packet, rx_seed = generate_packet(rx_seed, 1024) + s, l, e = check(rx_reference_packet, rx_packet) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + + def send(): + tx_seed = 0 + while tx_seed < test_size: + tx_packet, tx_seed = generate_packet(tx_seed, 1024) + tx_sock.sendto(bytes(tx_packet), (fpga_ip, udp_port)) + time.sleep(0.001) # XXX: FIXME, Python limitation? + + receive_thread = threading.Thread(target=receive) + receive_thread.start() + + send_thread = threading.Thread(target=send) + send_thread.start() + + try: + send_thread.join(10) + receive_thread.join(0.1) + except KeyboardInterrupt: + pass + +def main(wb): + test("192.168.1.40", 6000, 128*KB) + test("192.168.1.40", 8000, 128*KB) diff --git a/platforms/kc705.py b/platforms/kc705.py deleted file mode 100644 index 500d1b07..00000000 --- a/platforms/kc705.py +++ /dev/null @@ -1,138 +0,0 @@ -from mibuild.generic_platform import * -from mibuild.crg import SimpleCRG -from mibuild.xilinx_common import CRG_DS -from mibuild.xilinx_ise import XilinxISEPlatform -from mibuild.xilinx_vivado import XilinxVivadoPlatform -from mibuild.programmer import * - -def _run_vivado(cmds): - with subprocess.Popen("vivado -mode tcl", stdin=subprocess.PIPE, shell=True) as process: - process.stdin.write(cmds.encode("ASCII")) - process.communicate() - -class VivadoProgrammer(Programmer): - needs_bitreverse = False - - def load_bitstream(self, bitstream_file): - cmds = """open_hw -connect_hw_server -open_hw_target [lindex [get_hw_targets -of_objects [get_hw_servers localhost]] 0] - -set_property PROBES.FILE {{}} [lindex [get_hw_devices] 0] -set_property PROGRAM.FILE {{{bitstream}}} [lindex [get_hw_devices] 0] - -program_hw_devices [lindex [get_hw_devices] 0] -refresh_hw_device [lindex [get_hw_devices] 0] - -quit -""".format(bitstream=bitstream_file) - _run_vivado(cmds) - - def flash(self, address, data_file): - raise NotImplementedError - -_io = [ - ("user_led", 0, Pins("AB8"), IOStandard("LVCMOS15")), - ("user_led", 1, Pins("AA8"), IOStandard("LVCMOS15")), - ("user_led", 2, Pins("AC9"), IOStandard("LVCMOS15")), - ("user_led", 3, Pins("AB9"), IOStandard("LVCMOS15")), - ("user_led", 4, Pins("AE26"), IOStandard("LVCMOS25")), - ("user_led", 5, Pins("G19"), IOStandard("LVCMOS25")), - ("user_led", 6, Pins("E18"), IOStandard("LVCMOS25")), - ("user_led", 7, Pins("F16"), IOStandard("LVCMOS25")), - - ("cpu_reset", 0, Pins("AB7"), IOStandard("LVCMOS15")), - - ("clk200", 0, - Subsignal("p", Pins("AD12"), IOStandard("LVDS")), - Subsignal("n", Pins("AD11"), IOStandard("LVDS")) - ), - - ("clk156", 0, - Subsignal("p", Pins("K28"), IOStandard("LVDS_25")), - Subsignal("n", Pins("K29"), IOStandard("LVDS_25")) - ), - - - ("serial", 0, - Subsignal("cts", Pins("L27")), - Subsignal("rts", Pins("K23")), - Subsignal("tx", Pins("K24")), - Subsignal("rx", Pins("M19")), - IOStandard("LVCMOS25") - ), - - ("eth_clocks", 0, - Subsignal("tx", Pins("M28")), - Subsignal("gtx", Pins("K30")), - Subsignal("rx", Pins("U27")), - IOStandard("LVCMOS25") - ), - ("eth", 0, - Subsignal("rst_n", Pins("L20")), - Subsignal("int_n", Pins("N30")), - Subsignal("mdio", Pins("J21")), - Subsignal("mdc", Pins("R23")), - Subsignal("dv", Pins("R28")), - Subsignal("rx_er", Pins("V26")), - Subsignal("rx_data", Pins("U30 U25 T25 U28 R19 T27 T26 T28")), - Subsignal("tx_en", Pins("M27")), - Subsignal("tx_er", Pins("N29")), - Subsignal("tx_data", Pins("N27 N25 M29 L28 J26 K26 L30 J28")), - Subsignal("col", Pins("W19")), - Subsignal("crs", Pins("R30")), - IOStandard("LVCMOS25") - ), - -] - -def Platform(*args, toolchain="vivado", programmer="xc3sprog", **kwargs): - if toolchain == "ise": - xilinx_platform = XilinxISEPlatform - elif toolchain == "vivado": - xilinx_platform = XilinxVivadoPlatform - else: - raise ValueError - - class RealPlatform(xilinx_platform): - bitgen_opt = "-g LCK_cycle:6 -g Binary:Yes -w -g ConfigRate:12 -g SPI_buswidth:4" - - def __init__(self, crg_factory=lambda p: CRG_DS(p, "clk200", "cpu_reset")): - xilinx_platform.__init__(self, "xc7k325t-ffg900-2", _io, crg_factory) - - def create_programmer(self): - if programmer == "xc3sprog": - return XC3SProg("jtaghs1_fast", "bscan_spi_kc705.bit") - elif programmer == "vivado": - return VivadoProgrammer() - else: - raise ValueError - - def do_finalize(self, fragment): - try: - self.add_period_constraint(self.lookup_request("clk156").p, 6.4) - except ConstraintError: - pass - try: - self.add_period_constraint(self.lookup_request("clk200").p, 5.0) - except ConstraintError: - pass - try: - self.add_period_constraint(self.lookup_request("eth_clocks").rx, 8.0) - except ConstraintError: - pass - self.add_platform_command(""" -create_clock -name sys_clk -period 6 [get_nets sys_clk] -create_clock -name eth_rx_clk -period 8 [get_nets eth_rx_clk] -create_clock -name eth_tx_clk -period 8 [get_nets eth_tx_clk] - -set_false_path -from [get_clocks sys_clk] -to [get_clocks eth_rx_clk] -set_false_path -from [get_clocks sys_clk] -to [get_clocks eth_tx_clk] -set_false_path -from [get_clocks eth_rx_clk] -to [get_clocks sys_clk] -set_false_path -from [get_clocks eth_tx_clk] -to [get_clocks sys_clk] - -set_property CFGBVS VCCO [current_design] -set_property CONFIG_VOLTAGE 2.5 [current_design] -""") - - return RealPlatform(*args, **kwargs) diff --git a/setup.py b/setup.py deleted file mode 100644 index 0f84b945..00000000 --- a/setup.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 - -import sys, os -from setuptools import setup -from setuptools import find_packages - -here = os.path.abspath(os.path.dirname(__file__)) -README = open(os.path.join(here, "README")).read() - -required_version = (3, 3) -if sys.version_info < required_version: - raise SystemExit("LiteEth requires python {0} or greater".format( - ".".join(map(str, required_version)))) - -setup( - name="liteeth", - version="unknown", - description="small footprint and configurable Ethernet core", - long_description=README, - author="Florent Kermarrec", - author_email="florent@enjoy-digital.fr", - url="http://enjoy-digital.fr", - download_url="https://github.com/enjoy-digital/liteeth", - packages=find_packages(here), - license="GPL", - platforms=["Any"], - keywords="HDL ASIC FPGA hardware design", - classifiers=[ - "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", - "Environment :: Console", - "Development Status :: Alpha", - "Intended Audience :: Developers", - "License :: OSI Approved :: GNU General Public License (GPL)", - "Operating System :: OS Independent", - "Programming Language :: Python", - ], -) diff --git a/targets/__init__.py b/targets/__init__.py deleted file mode 100644 index 85efe5d9..00000000 --- a/targets/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -import subprocess - -from migen.fhdl.std import * -from migen.bank.description import * - -def get_id(): - output = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode("ascii") - return int(output[:8], 16) - -class Identifier(Module, AutoCSR): - def __init__(self, sysid, frequency, revision=None): - self._r_sysid = CSRStatus(16) - self._r_revision = CSRStatus(32) - self._r_frequency = CSRStatus(32) - - ### - - if revision is None: - revision = get_id() - - self.comb += [ - self._r_sysid.status.eq(sysid), - self._r_revision.status.eq(revision), - self._r_frequency.status.eq(frequency), - ] - diff --git a/targets/base.py b/targets/base.py deleted file mode 100644 index 3fe1f2a4..00000000 --- a/targets/base.py +++ /dev/null @@ -1,224 +0,0 @@ -import os - -from migen.bank import csrgen -from migen.bus import wishbone, csr -from migen.bus import wishbone2csr -from migen.genlib.cdc import * -from migen.genlib.resetsync import AsyncResetSynchronizer -from migen.bank.description import * - -from targets import * - -from litescope.common import * -from litescope.bridge.uart2wb import LiteScopeUART2WB -from litescope.frontend.la import LiteScopeLA -from litescope.core.port import LiteScopeTerm - -from liteeth.common import * -from liteeth.generic import * -from liteeth.phy.gmii import LiteEthPHYGMII -from liteeth.core import LiteEthUDPIPCore - -class _CRG(Module): - def __init__(self, platform): - self.clock_domains.cd_sys = ClockDomain() - self.reset = Signal() - - clk200 = platform.request("clk200") - clk200_se = Signal() - self.specials += Instance("IBUFDS", i_I=clk200.p, i_IB=clk200.n, o_O=clk200_se) - - pll_locked = Signal() - pll_fb = Signal() - pll_sys = Signal() - self.specials += [ - Instance("PLLE2_BASE", - p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, - - # VCO @ 1GHz - p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=5.0, - p_CLKFBOUT_MULT=5, p_DIVCLK_DIVIDE=1, - i_CLKIN1=clk200_se, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, - - # 166MHz - p_CLKOUT0_DIVIDE=6, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=pll_sys, - - p_CLKOUT1_DIVIDE=2, p_CLKOUT1_PHASE=0.0, #o_CLKOUT1=, - - p_CLKOUT2_DIVIDE=2, p_CLKOUT2_PHASE=0.0, #o_CLKOUT2=, - - p_CLKOUT3_DIVIDE=2, p_CLKOUT3_PHASE=0.0, #o_CLKOUT3=, - - p_CLKOUT4_DIVIDE=2, p_CLKOUT4_PHASE=0.0, #o_CLKOUT4= - ), - Instance("BUFG", i_I=pll_sys, o_O=self.cd_sys.clk), - AsyncResetSynchronizer(self.cd_sys, ~pll_locked | platform.request("cpu_reset") | self.reset), - ] - -class GenSoC(Module): - csr_base = 0x00000000 - csr_data_width = 32 - csr_map = { - "bridge": 0, - "identifier": 1, - } - interrupt_map = {} - cpu_type = None - def __init__(self, platform, clk_freq): - self.clk_freq = clk_freq - # UART <--> Wishbone bridge - self.submodules.bridge = LiteScopeUART2WB(platform.request("serial"), clk_freq, baud=921600) - - # CSR bridge 0x00000000 (shadow @0x00000000) - self.submodules.wishbone2csr = wishbone2csr.WB2CSR(bus_csr=csr.Interface(self.csr_data_width)) - self._wb_masters = [self.bridge.wishbone] - self._wb_slaves = [(lambda a: a[23:25] == 0, self.wishbone2csr.wishbone)] - self.cpu_csr_regions = [] # list of (name, origin, busword, csr_list/Memory) - - # CSR - self.submodules.identifier = Identifier(0, int(clk_freq), 0) - - def add_wb_master(self, wbm): - if self.finalized: - raise FinalizeError - self._wb_masters.append(wbm) - - def add_wb_slave(self, address_decoder, interface): - if self.finalized: - raise FinalizeError - self._wb_slaves.append((address_decoder, interface)) - - def add_cpu_memory_region(self, name, origin, length): - self.cpu_memory_regions.append((name, origin, length)) - - def add_cpu_csr_region(self, name, origin, busword, obj): - self.cpu_csr_regions.append((name, origin, busword, obj)) - - def do_finalize(self): - # Wishbone - self.submodules.wishbonecon = wishbone.InterconnectShared(self._wb_masters, - self._wb_slaves, register=True) - - # CSR - self.submodules.csrbankarray = csrgen.BankArray(self, - lambda name, memory: self.csr_map[name if memory is None else name + "_" + memory.name_override], - data_width=self.csr_data_width) - self.submodules.csrcon = csr.Interconnect(self.wishbone2csr.csr, self.csrbankarray.get_buses()) - for name, csrs, mapaddr, rmap in self.csrbankarray.banks: - self.add_cpu_csr_region(name, 0xe0000000+0x800*mapaddr, flen(rmap.bus.dat_w), csrs) - for name, memory, mapaddr, mmap in self.csrbankarray.srams: - self.add_cpu_csr_region(name, 0xe0000000+0x800*mapaddr, flen(rmap.bus.dat_w), memory) - -class BaseSoC(GenSoC, AutoCSR): - default_platform = "kc705" - csr_map = { - "phy": 11, - "core": 12 - } - csr_map.update(GenSoC.csr_map) - def __init__(self, platform, clk_freq=166*1000000, - mac_address=0x10e2d5000000, - ip_address="192.168.1.40"): - GenSoC.__init__(self, platform, clk_freq) - self.submodules.crg = _CRG(platform) - - # wishbone SRAM (to test Wishbone over UART and Etherbone) - self.submodules.sram = wishbone.SRAM(1024) - self.add_wb_slave(lambda a: a[23:25] == 1, self.sram.bus) - - # ethernet PHY and UDP/IP stack - self.submodules.phy = LiteEthPHYGMII(platform.request("eth_clocks"), platform.request("eth")) - self.submodules.core = LiteEthUDPIPCore(self.phy, mac_address, convert_ip(ip_address), clk_freq) - -class BaseSoCDevel(BaseSoC, AutoCSR): - csr_map = { - "la": 20 - } - csr_map.update(BaseSoC.csr_map) - def __init__(self, platform): - BaseSoC.__init__(self, platform) - - self.core_icmp_rx_fsm_state = Signal(4) - self.core_icmp_tx_fsm_state = Signal(4) - self.core_udp_rx_fsm_state = Signal(4) - self.core_udp_tx_fsm_state = Signal(4) - self.core_ip_rx_fsm_state = Signal(4) - self.core_ip_tx_fsm_state = Signal(4) - self.core_arp_rx_fsm_state = Signal(4) - self.core_arp_tx_fsm_state = Signal(4) - self.core_arp_table_fsm_state = Signal(4) - - debug = ( - # MAC interface - self.core.mac.core.sink.stb, - self.core.mac.core.sink.sop, - self.core.mac.core.sink.eop, - self.core.mac.core.sink.ack, - self.core.mac.core.sink.data, - - self.core.mac.core.source.stb, - self.core.mac.core.source.sop, - self.core.mac.core.source.eop, - self.core.mac.core.source.ack, - self.core.mac.core.source.data, - - # ICMP interface - self.core.icmp.echo.sink.stb, - self.core.icmp.echo.sink.sop, - self.core.icmp.echo.sink.eop, - self.core.icmp.echo.sink.ack, - self.core.icmp.echo.sink.data, - - self.core.icmp.echo.source.stb, - self.core.icmp.echo.source.sop, - self.core.icmp.echo.source.eop, - self.core.icmp.echo.source.ack, - self.core.icmp.echo.source.data, - - # IP interface - self.core.ip.crossbar.master.sink.stb, - self.core.ip.crossbar.master.sink.sop, - self.core.ip.crossbar.master.sink.eop, - self.core.ip.crossbar.master.sink.ack, - self.core.ip.crossbar.master.sink.data, - self.core.ip.crossbar.master.sink.ip_address, - self.core.ip.crossbar.master.sink.protocol, - - # State machines - self.core_icmp_rx_fsm_state, - self.core_icmp_tx_fsm_state, - - self.core_arp_rx_fsm_state, - self.core_arp_tx_fsm_state, - self.core_arp_table_fsm_state, - - self.core_ip_rx_fsm_state, - self.core_ip_tx_fsm_state, - - self.core_udp_rx_fsm_state, - self.core_udp_tx_fsm_state - ) - self.submodules.la = LiteScopeLA(debug, 4096) - self.la.trigger.add_port(LiteScopeTerm(self.la.dw)) - - def do_finalize(self): - BaseSoC.do_finalize(self) - self.comb += [ - self.core_icmp_rx_fsm_state.eq(self.core.icmp.rx.fsm.state), - self.core_icmp_tx_fsm_state.eq(self.core.icmp.tx.fsm.state), - - self.core_arp_rx_fsm_state.eq(self.core.arp.rx.fsm.state), - self.core_arp_tx_fsm_state.eq(self.core.arp.tx.fsm.state), - self.core_arp_table_fsm_state.eq(self.core.arp.table.fsm.state), - - self.core_ip_rx_fsm_state.eq(self.core.ip.rx.fsm.state), - self.core_ip_tx_fsm_state.eq(self.core.ip.tx.fsm.state), - - self.core_udp_rx_fsm_state.eq(self.core.udp.rx.fsm.state), - self.core_udp_tx_fsm_state.eq(self.core.udp.tx.fsm.state) - ] - - def do_exit(self, vns): - self.la.export(vns, "test/la.csv") - -default_subtarget = BaseSoC diff --git a/targets/etherbone.py b/targets/etherbone.py deleted file mode 100644 index 0aaaf2d2..00000000 --- a/targets/etherbone.py +++ /dev/null @@ -1,71 +0,0 @@ -from litescope.common import * -from litescope.frontend.la import LiteScopeLA -from litescope.core.port import LiteScopeTerm - -from liteeth.common import * -from liteeth.generic import * - -from targets.base import BaseSoC -from liteeth.core.etherbone import LiteEthEtherbone - -class EtherboneSoC(BaseSoC): - default_platform = "kc705" - def __init__(self, platform): - BaseSoC.__init__(self, platform, - mac_address=0x10e2d5000000, - ip_address="192.168.1.40") - self.submodules.etherbone = LiteEthEtherbone(self.core.udp, 20000) - self.add_wb_master(self.etherbone.master.bus) - -class EtherboneSoCDevel(EtherboneSoC): - csr_map = { - "la": 20 - } - csr_map.update(EtherboneSoC.csr_map) - def __init__(self, platform): - EtherboneSoC.__init__(self, platform) - debug = ( - # mmap stream from HOST - self.etherbone.master.sink.stb, - self.etherbone.master.sink.sop, - self.etherbone.master.sink.eop, - self.etherbone.master.sink.ack, - self.etherbone.master.sink.we, - self.etherbone.master.sink.count, - self.etherbone.master.sink.base_addr, - self.etherbone.master.sink.be, - self.etherbone.master.sink.addr, - self.etherbone.master.sink.data, - - # mmap stream to HOST - self.etherbone.master.source.stb, - self.etherbone.master.source.sop, - self.etherbone.master.source.eop, - self.etherbone.master.source.ack, - self.etherbone.master.source.we, - self.etherbone.master.source.count, - self.etherbone.master.source.base_addr, - self.etherbone.master.source.be, - self.etherbone.master.source.addr, - self.etherbone.master.source.data, - - # etherbone wishbone master - self.etherbone.master.bus.dat_w, - self.etherbone.master.bus.dat_r, - self.etherbone.master.bus.adr, - self.etherbone.master.bus.sel, - self.etherbone.master.bus.cyc, - self.etherbone.master.bus.stb, - self.etherbone.master.bus.ack, - self.etherbone.master.bus.we, - self.etherbone.master.bus.cti, - self.etherbone.master.bus.bte, - self.etherbone.master.bus.err - ) - self.submodules.la = LiteScopeLA(debug, 4096) - self.la.trigger.add_port(LiteScopeTerm(self.la.dw)) - - def do_exit(self, vns): - self.la.export(vns, "test/la.csv") - -default_subtarget = EtherboneSoC diff --git a/targets/tty.py b/targets/tty.py deleted file mode 100644 index 28cda9d8..00000000 --- a/targets/tty.py +++ /dev/null @@ -1,42 +0,0 @@ -from litescope.common import * -from litescope.frontend.la import LiteScopeLA -from litescope.core.port import LiteScopeTerm - -from liteeth.common import * -from liteeth.generic import * - -from targets.base import BaseSoC -from liteeth.core.tty import LiteEthTTY - -class TTYSoC(BaseSoC): - default_platform = "kc705" - def __init__(self, platform): - BaseSoC.__init__(self, platform, - mac_address=0x10e2d5000000, - ip_address="192.168.1.40") - self.submodules.tty = LiteEthTTY(self.core.udp, convert_ip("192.168.1.12"), 10000) - self.comb += Record.connect(self.tty.source, self.tty.sink) - -class TTYSoCDevel(TTYSoC): - csr_map = { - "la": 20 - } - csr_map.update(TTYSoC.csr_map) - def __init__(self, platform): - TTYSoC.__init__(self, platform) - debug = ( - self.tty.sink.stb, - self.tty.sink.ack, - self.tty.sink.data, - - self.tty.source.stb, - self.tty.source.ack, - self.tty.source.data - ) - self.submodules.la = LiteScopeLA(debug, 4096) - self.la.trigger.add_port(LiteScopeTerm(self.la.dw)) - - def do_exit(self, vns): - self.la.export(vns, "test/la.csv") - -default_subtarget = TTYSoC diff --git a/targets/udp.py b/targets/udp.py deleted file mode 100644 index 9ce15ce5..00000000 --- a/targets/udp.py +++ /dev/null @@ -1,69 +0,0 @@ -from litescope.common import * -from litescope.frontend.la import LiteScopeLA -from litescope.core.port import LiteScopeTerm - -from liteeth.common import * -from liteeth.generic import * - -from targets.base import BaseSoC - -class UDPSoC(BaseSoC): - default_platform = "kc705" - def __init__(self, platform): - BaseSoC.__init__(self, platform, - mac_address=0x10e2d5000000, - ip_address="192.168.1.40") - - # add udp loopback on port 6000 with dw=8 - self.add_udp_loopback(6000, 8, 8192, "loopback_8") - # add udp loopback on port 8000 with dw=32 - self.add_udp_loopback(8000, 32, 8192, "loopback_32") - - def add_udp_loopback(self, port, dw, depth, name=None): - port = self.core.udp.crossbar.get_port(port, dw) - buf = PacketBuffer(eth_udp_user_description(dw), depth//(dw//8), 8) - if name is None: - self.submodules += buf - else: - setattr(self.submodules, name, buf) - self.comb += Port.connect(port, buf) - -class UDPSoCDevel(UDPSoC): - csr_map = { - "la": 20 - } - csr_map.update(UDPSoC.csr_map) - def __init__(self, platform): - UDPSoC.__init__(self, platform) - debug = ( - self.loopback_8.sink.stb, - self.loopback_8.sink.sop, - self.loopback_8.sink.eop, - self.loopback_8.sink.ack, - self.loopback_8.sink.data, - - self.loopback_8.source.stb, - self.loopback_8.source.sop, - self.loopback_8.source.eop, - self.loopback_8.source.ack, - self.loopback_8.source.data, - - self.loopback_32.sink.stb, - self.loopback_32.sink.sop, - self.loopback_32.sink.eop, - self.loopback_32.sink.ack, - self.loopback_32.sink.data, - - self.loopback_32.source.stb, - self.loopback_32.source.sop, - self.loopback_32.source.eop, - self.loopback_32.source.ack, - self.loopback_32.source.data - ) - self.submodules.la = LiteScopeLA(debug, 4096) - self.la.trigger.add_port(LiteScopeTerm(self.la.dw)) - - def do_exit(self, vns): - self.la.export(vns, "test/la.csv") - -default_subtarget = UDPSoC diff --git a/test/make.py b/test/make.py deleted file mode 100644 index 05e13aad..00000000 --- a/test/make.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 -import argparse, importlib - -def _get_args(): - parser = argparse.ArgumentParser() - parser.add_argument("-b", "--bridge", default="uart", help="Bridge to use") - parser.add_argument("--port", default=2, help="UART port") - parser.add_argument("--baudrate", default=921600, help="UART baudrate") - parser.add_argument("--ip_address", default="192.168.1.40", help="Etherbone IP address") - parser.add_argument("--udp_port", default=20000, help="Etherbone UDP port") - parser.add_argument("--busword", default=32, help="CSR busword") - - parser.add_argument("test", nargs="+", help="specify a test") - - return parser.parse_args() - -if __name__ == "__main__": - args = _get_args() - if args.bridge == "uart": - from litescope.host.driver.uart import LiteScopeUARTDriver - wb = LiteScopeUARTDriver(args.port, args.baudrate, "./csr.csv", int(args.busword), debug=False) - elif args.bridge == "etherbone": - from litescope.host.driver.etherbone import LiteScopeEtherboneDriver - wb = LiteScopeEtherboneDriver(args.ip_address, int(args.udp_port), "./csr.csv", int(args.busword), debug=False) - else: - ValueError("Invalid bridge {}".format(args.bridge)) - - def _import(name): - return importlib.import_module(name) - - for test in args.test: - t = _import("test_"+test) - t.main(wb) diff --git a/test/test_etherbone.py b/test/test_etherbone.py deleted file mode 100644 index 4622d524..00000000 --- a/test/test_etherbone.py +++ /dev/null @@ -1,60 +0,0 @@ -import socket, time -from liteeth.test.model.etherbone import * - -SRAM_BASE = 0x02000000 - -import socket - -def main(wb): - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - - # test probe - packet = EtherbonePacket() - packet.pf = 1 - packet.encode() - sock.sendto(bytes(packet), ("192.168.1.40", 20000)) - time.sleep(0.01) - - # test writes - writes_datas = [j for j in range(16)] - writes = EtherboneWrites(base_addr=SRAM_BASE, datas=writes_datas) - record = EtherboneRecord() - record.writes = writes - record.reads = None - record.bca = 0 - record.rca = 0 - record.rff = 0 - record.cyc = 0 - record.wca = 0 - record.wff = 0 - record.byte_enable = 0xf - record.wcount = len(writes_datas) - record.rcount = 0 - - packet = EtherbonePacket() - packet.records = [record] - packet.encode() - sock.sendto(bytes(packet), ("192.168.1.40", 20000)) - time.sleep(0.01) - - # test reads - reads_addrs = [SRAM_BASE+4*j for j in range(16)] - reads = EtherboneReads(base_ret_addr=0x1000, addrs=reads_addrs) - record = EtherboneRecord() - record.writes = None - record.reads = reads - record.bca = 0 - record.rca = 0 - record.rff = 0 - record.cyc = 0 - record.wca = 0 - record.wff = 0 - record.byte_enable = 0xf - record.wcount = 0 - record.rcount = len(reads_addrs) - - packet = EtherbonePacket() - packet.records = [record] - packet.encode() - sock.sendto(bytes(packet), ("192.168.1.40", 20000)) - time.sleep(0.01) diff --git a/test/test_la.py b/test/test_la.py deleted file mode 100644 index c15de8c8..00000000 --- a/test/test_la.py +++ /dev/null @@ -1,24 +0,0 @@ -import time -from litescope.host.driver.la import LiteScopeLADriver - -def main(wb): - la = LiteScopeLADriver(wb.regs, "la", debug=True) - - wb.open() - regs = wb.regs - ### - - conditions = {} - la.configure_term(port=0, cond=conditions) - la.configure_sum("term") - # Run Logic Analyzer - la.run(offset=2048, length=4000) - - while not la.done(): - pass - - la.upload() - la.save("dump.vcd") - - ### - wb.close() diff --git a/test/test_regs.py b/test/test_regs.py deleted file mode 100644 index f2685cf5..00000000 --- a/test/test_regs.py +++ /dev/null @@ -1,12 +0,0 @@ -def main(wb): - wb.open() - regs = wb.regs - ### - print("sysid : 0x%04x" %regs.identifier_sysid.read()) - print("revision : 0x%04x" %regs.identifier_revision.read()) - print("frequency : %d MHz" %(regs.identifier_frequency.read()/1000000)) - SRAM_BASE = 0x02000000 - wb.write(SRAM_BASE, [i for i in range(64)]) - print(wb.read(SRAM_BASE, 64)) - ### - wb.close() diff --git a/test/test_tty.py b/test/test_tty.py deleted file mode 100644 index af6f2df0..00000000 --- a/test/test_tty.py +++ /dev/null @@ -1,36 +0,0 @@ -import socket -import threading - -def test(fpga_ip, udp_port, test_message): - tx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - rx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - rx_sock.bind(("", udp_port)) - rx_sock.settimeout(0.5) - - def receive(): - while True: - try: - msg = rx_sock.recv(8192) - for byte in msg: - print(chr(byte), end="") - except: - break - - def send(): - tx_sock.sendto(bytes(test_message, "utf-8"), (fpga_ip, udp_port)) - - receive_thread = threading.Thread(target=receive) - receive_thread.start() - - send_thread = threading.Thread(target=send) - send_thread.start() - - try: - send_thread.join(5) - send_thread.join(5) - except KeyboardInterrupt: - pass - -def main(wb): - test_message = "LiteEth virtual TTY Hello world\n" - test("192.168.1.40", 10000, test_message) diff --git a/test/test_udp.py b/test/test_udp.py deleted file mode 100644 index 65d47b74..00000000 --- a/test/test_udp.py +++ /dev/null @@ -1,81 +0,0 @@ -import socket -import time -import threading -import copy - -KB = 1024 -MB = 1024*KB -GB = 1024*MB - -def seed_to_data(seed, random=True): - if random: - return (seed * 0x31415979 + 1) & 0xffffffff - else: - return seed - -def check(p1, p2): - p1 = copy.deepcopy(p1) - p2 = copy.deepcopy(p2) - if isinstance(p1, int): - return 0, 1, int(p1 != p2) - else: - if len(p1) >= len(p2): - ref, res = p1, p2 - else: - ref, res = p2, p1 - shift = 0 - while((ref[0] != res[0]) and (len(res)>1)): - res.pop(0) - shift += 1 - length = min(len(ref), len(res)) - errors = 0 - for i in range(length): - if ref.pop(0) != res.pop(0): - errors += 1 - return shift, length, errors - -def generate_packet(seed, length): - r = [] - for i in range(length): - r.append(seed_to_data(seed, True)%0xff) # XXX FIXME - seed += 1 - return r, seed - -def test(fpga_ip, udp_port, test_size): - tx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - rx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - rx_sock.bind(("", udp_port)) - - def receive(): - rx_seed = 0 - while rx_seed < test_size: - data, addr = rx_sock.recvfrom(8192) - rx_packet = [] - for byte in data: - rx_packet.append(int(byte)) - rx_reference_packet, rx_seed = generate_packet(rx_seed, 1024) - s, l, e = check(rx_reference_packet, rx_packet) - print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) - - def send(): - tx_seed = 0 - while tx_seed < test_size: - tx_packet, tx_seed = generate_packet(tx_seed, 1024) - tx_sock.sendto(bytes(tx_packet), (fpga_ip, udp_port)) - time.sleep(0.001) # XXX: FIXME, Python limitation? - - receive_thread = threading.Thread(target=receive) - receive_thread.start() - - send_thread = threading.Thread(target=send) - send_thread.start() - - try: - send_thread.join(10) - receive_thread.join(0.1) - except KeyboardInterrupt: - pass - -def main(wb): - test("192.168.1.40", 6000, 128*KB) - test("192.168.1.40", 8000, 128*KB)