From: Florent Kermarrec Date: Sat, 7 Nov 2015 11:22:32 +0000 (+0100) Subject: import migen in litex/gen X-Git-Tag: 24jan2021_ls180~2098 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=637634f312ea40879df86d908549b20360b66683;p=litex.git import migen in litex/gen --- diff --git a/litex/gen/.gitignore b/litex/gen/.gitignore new file mode 100644 index 00000000..3fa21c42 --- /dev/null +++ b/litex/gen/.gitignore @@ -0,0 +1,7 @@ +__pycache__ +*.pyc +*.egg-info/ +vpi/*.o +vpi/migensim.vpi +examples/*.vcd +doc/_build diff --git a/litex/gen/.travis.yml b/litex/gen/.travis.yml new file mode 100644 index 00000000..315850e3 --- /dev/null +++ b/litex/gen/.travis.yml @@ -0,0 +1,52 @@ +language: python +python: + - "3.5" + +env: + global: + - PATH=$HOME/miniconda/bin:$PATH + +before_install: + # Install Miniconda + - wget https://raw.githubusercontent.com/m-labs/artiq/master/.travis/get-anaconda.sh + - chmod +x get-anaconda.sh + - ./get-anaconda.sh + - source $HOME/miniconda/bin/activate py35 + - conda install anaconda-client numpydoc +install: + # Install iverilog package. + # - "sudo add-apt-repository -y ppa:mithro/iverilog-backport" + # - "sudo apt-get update" + # - "sudo apt-get install iverilog" + # - "iverilog -v; true" + # Build the vpi module. + # - "(cd vpi; make; sudo make install)" + # Install verilator package + - "sudo apt-get install verilator" + - "verilator --version; true" + # Build and install Migen conda package + # workaround for https://github.com/conda/conda-build/issues/466 + - "mkdir -p /home/travis/miniconda/conda-bld/linux-64" + - "conda index /home/travis/miniconda/conda-bld/linux-64" + - "conda build --python 3.5 conda/migen" + - "conda install $(conda build --output --python 3.5 conda/migen)" + +script: + # Run tests + - "python setup.py test" + # Generate HTML documentation + - "make -C doc html" + +after_success: + # Upload Migen conda package to binstar + - if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then anaconda login --hostname $(hostname) --username $binstar_login --password $binstar_password; fi + - if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then anaconda upload --user $binstar_login --channel dev --force $HOME/miniconda/conda-bld/noarch/migen-*.tar.bz2; fi + +notifications: + email: false + irc: + channels: + - chat.freenode.net#m-labs + template: + - "%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}" + - "Build details : %{build_url}" diff --git a/litex/gen/LICENSE b/litex/gen/LICENSE new file mode 100644 index 00000000..4f290601 --- /dev/null +++ b/litex/gen/LICENSE @@ -0,0 +1,31 @@ +Unless otherwise noted, Migen is copyright (C) 2011-2013 Sebastien Bourdeauducq. +The simulation extension (as mentioned in the comments at the beginning of the +corresponding source files) is copyright (C) 2012 Vermeer Manufacturing Co. All +rights reserved. + +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/litex/gen/README.rst b/litex/gen/README.rst new file mode 100644 index 00000000..39f8e96a --- /dev/null +++ b/litex/gen/README.rst @@ -0,0 +1,82 @@ +### Migen (Milkymist generator) + +[![Build Status](https://travis-ci.org/m-labs/migen.svg)]( +https://travis-ci.org/m-labs/migen) + +#### A Python toolbox for building complex digital hardware + +Despite being faster than schematics entry, hardware design with Verilog and +VHDL remains tedious and inefficient for several reasons. The event-driven +model introduces issues and manual coding that are unnecessary for synchronous +circuits, which represent the lion's share of today's logic designs. Counter- +intuitive arithmetic rules result in steeper learning curves and provide a +fertile ground for subtle bugs in designs. Finally, support for procedural +generation of logic (metaprogramming) through "generate" statements is very +limited and restricts the ways code can be made generic, reused and organized. + +To address those issues, we have developed the **Migen FHDL** library that +replaces the event-driven paradigm with the notions of combinatorial and +synchronous statements, has arithmetic rules that make integers always behave +like mathematical integers, and most importantly allows the design's logic to +be constructed by a Python program. This last point enables hardware designers +to take advantage of the richness of the Python language - object oriented +programming, function parameters, generators, operator overloading, libraries, +etc. - to build well organized, reusable and elegant designs. + +Other Migen libraries are built on FHDL and provide various tools such as a +system-on-chip interconnect infrastructure, a dataflow programming system, a +more traditional high-level synthesizer that compiles Python routines into +state machines with datapaths, and a simulator that allows test benches to be +written in Python. + +See the doc/ folder for more technical information. + +Migen is designed for Python 3.3. Note that Migen is **not** spelled MiGen. + +#### Quick Links + +Code repository: +https://github.com/m-labs/migen + +System-on-chip design based on Migen: +https://github.com/m-labs/misoc + +Online documentation: +http://m-labs.hk/gateware.html + +#### Quick intro + +```python +from migen import * +from migen.build.platforms import m1 +plat = m1.Platform() +led = plat.request("user_led") +m = Module() +counter = Signal(26) +m.comb += led.eq(counter[25]) +m.sync += counter.eq(counter + 1) +plat.build_cmdline(m) +``` + +#### License + +Migen is released under the very permissive two-clause BSD license. Under the +terms of this license, you are authorized to use Migen 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 Migen +* put the Migen logo (doc/migen_logo.svg) on the page of a product using it, + with a link to http://m-labs.hk +* cite Migen 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 Migen. 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 it on + the mailing list or IRC (#m-labs on Freenode) beforehand. + +See LICENSE file for full copyright and license info. You can contact us on the +public mailing list devel [AT] lists.m-labs.hk. + + "Electricity! It's like magic!" diff --git a/litex/gen/conda/migen/bld.bat b/litex/gen/conda/migen/bld.bat new file mode 100644 index 00000000..39b5e1fe --- /dev/null +++ b/litex/gen/conda/migen/bld.bat @@ -0,0 +1 @@ +%PYTHON% setup.py install diff --git a/litex/gen/conda/migen/meta.yaml b/litex/gen/conda/migen/meta.yaml new file mode 100644 index 00000000..1cf9baee --- /dev/null +++ b/litex/gen/conda/migen/meta.yaml @@ -0,0 +1,28 @@ +package: + name: migen + version: {{ environ.get("GIT_DESCRIBE_TAG", "") }} + +source: + git_url: https://github.com/m-labs/migen + git_tag: master + +build: + noarch_python: true + number: {{ environ.get("GIT_DESCRIBE_NUMBER", 0) }} + string: py_{{ environ.get("GIT_DESCRIBE_NUMBER", 0) }}+git{{ environ.get("GIT_DESCRIBE_HASH", "")[1:] }} + script: $PYTHON setup.py install + +requirements: + build: + - python 3.5.* + run: + - python 3.5.* + +test: + imports: + - migen + +about: + home: http://m-labs.hk/gateware.html + license: 3-clause BSD + summary: 'A Python toolbox for building complex digital hardware' diff --git a/litex/gen/doc/Makefile b/litex/gen/doc/Makefile new file mode 100644 index 00000000..d6a5924b --- /dev/null +++ b/litex/gen/doc/Makefile @@ -0,0 +1,130 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +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 " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp 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 " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @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." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Migen.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Migen.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Migen" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Migen" + @echo "# devhelp" + +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." + +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." + +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." diff --git a/litex/gen/doc/_static/.keep_me b/litex/gen/doc/_static/.keep_me new file mode 100644 index 00000000..e69de29b diff --git a/litex/gen/doc/_templates/.keep_me b/litex/gen/doc/_templates/.keep_me new file mode 100644 index 00000000..e69de29b diff --git a/litex/gen/doc/conf.py b/litex/gen/doc/conf.py new file mode 100644 index 00000000..f134ce9b --- /dev/null +++ b/litex/gen/doc/conf.py @@ -0,0 +1,223 @@ +# -*- coding: utf-8 -*- +# +# Migen documentation build configuration file, created by +# sphinx-quickstart on Fri Mar 9 14:11:54 2012. +# +# 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. + +# 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.pngmath', + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.autosummary', + 'numpydoc', # to preprocess docstrings + ] + +# 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'Migen' +copyright = u'2011-2015, M-Labs Limited' + +# 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. +# +# The short X.Y version. +version = '1.0' +# The full version, including alpha/beta/rc tags. +release = '1.0' + +# 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 = ['_build'] + +# 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 = ['migen.'] + +numpydoc_show_class_members = 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 = 'alabaster' + +# 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 = {} + +# 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 = None + +# 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 = None + +# 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'] + +# 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 = 'Migendoc' + +html_use_modindex = False + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Migen.tex', u'Migen manual', + u'Sebastien Bourdeauducq', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +latex_logo = "migen_logo.png" + +# 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 + +# Additional stuff for the LaTeX preamble. +latex_preamble = '\setcounter{tocdepth}{3}' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + +latex_use_modindex = False + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'migen', u'Migen manual', + [u'Sebastien Bourdeauducq'], 1) +] diff --git a/litex/gen/doc/fhdl.rst b/litex/gen/doc/fhdl.rst new file mode 100644 index 00000000..b6225605 --- /dev/null +++ b/litex/gen/doc/fhdl.rst @@ -0,0 +1,362 @@ +The FHDL domain-specific language +################################# + +The Fragmented Hardware Description Language (FHDL) is the basis of Migen. It consists of a formal system to describe signals, and combinatorial and synchronous statements operating on them. The formal system itself is low level and close to the synthesizable subset of Verilog, and we then rely on Python algorithms to build complex structures by combining FHDL elements. +The FHDL module also contains a back-end to produce synthesizable Verilog, and some structure analysis and manipulation functionality. + +FHDL differs from MyHDL [myhdl]_ in fundamental ways. MyHDL follows the event-driven paradigm of traditional HDLs (see :ref:`background`) while FHDL separates the code into combinatorial statements, synchronous statements, and reset values. In MyHDL, the logic is described directly in the Python AST. The converter to Verilog or VHDL then examines the Python AST and recognizes a subset of Python that it translates into V*HDL statements. This seriously impedes the capability of MyHDL to generate logic procedurally. With FHDL, you manipulate a custom AST from Python, and you can more easily design algorithms that operate on it. + +.. [myhdl] http://www.myhdl.org + +FHDL is made of several elements, which are briefly explained below. They all can be imported directly from the ``migen`` module. + +Expressions +*********** + +Constants +========= + +The ``Constant`` object represents a constant, HDL-literal integer. It behaves like specifying integers and booleans but also supports slicing and can have a bit width or signedness different from what is implied by the value it represents. + +``True`` and ``False`` are interpreted as 1 and 0, respectively. + +Negative integers are explicitly supported. As with MyHDL [countin]_, arithmetic operations return the natural results. + +To lighten the syntax, assignments and operators automatically wrap Python integers and booleans into ``Constant``. Additionally, ``Constant`` is aliased to ``C``. The following are valid Migen statements: ``a.eq(0)``, ``a.eq(a + 1)``, ``a.eq(C(42)[0:1])``. + +.. [countin] http://www.jandecaluwe.com/hdldesign/counting.html + +Signal +====== + +The signal object represents a value that is expected to change in the circuit. It does exactly what Verilog's "wire" and "reg" and VHDL's "signal" do. + +The main point of the signal object is that it is identified by its Python ID (as returned by the :py:func:`id` function), and nothing else. It is the responsibility of the V*HDL back-end to establish an injective mapping between Python IDs and the V*HDL namespace. It should perform name mangling to ensure this. The consequence of this is that signal objects can safely become members of arbitrary Python classes, or be passed as parameters to functions or methods that generate logic involving them. + +The properties of a signal object are: + +* An integer or a (integer, boolean) pair that defines the number of bits and whether the bit of higher index of the signal is a sign bit (i.e. the signal is signed). The defaults are one bit and unsigned. Alternatively, the ``min`` and ``max`` parameters can be specified to define the range of the signal and determine its bit width and signedness. As with Python ranges, ``min`` is inclusive and defaults to 0, ``max`` is exclusive and defaults to 2. +* A name, used as a hint for the V*HDL back-end name mangler. +* The signal's reset value. It must be an integer, and defaults to 0. When the signal's value is modified with a synchronous statement, the reset value is the initialization value of the associated register. When the signal is assigned to in a conditional combinatorial statement (``If`` or ``Case``), the reset value is the value that the signal has when no condition that causes the signal to be driven is verified. This enforces the absence of latches in designs. If the signal is permanently driven using a combinatorial statement, the reset value has no effect. + +The sole purpose of the name property is to make the generated V*HDL code easier to understand and debug. From a purely functional point of view, it is perfectly OK to have several signals with the same name property. The back-end will generate a unique name for each object. If no name property is specified, Migen will analyze the code that created the signal object, and try to extract the variable or member name from there. For example, the following statements will create one or several signals named "bar": :: + + bar = Signal() + self.bar = Signal() + self.baz.bar = Signal() + bar = [Signal() for x in range(42)] + +In case of conflicts, Migen tries first to resolve the situation by prefixing the identifiers with names from the class and module hierarchy that created them. If the conflict persists (which can be the case if two signal objects are created with the same name in the same context), it will ultimately add number suffixes. + +Operators +========= + +Operators are represented by the ``_Operator`` object, which generally should not be used directly. Instead, most FHDL objects overload the usual Python logic and arithmetic operators, which allows a much lighter syntax to be used. For example, the expression: :: + + a * b + c + +is equivalent to:: + + _Operator("+", [_Operator("*", [a, b]), c]) + +Slices +====== + +Likewise, slices are represented by the ``_Slice`` object, which often should not be used in favor of the Python slice operation [x:y]. Implicit indices using the forms [x], [x:] and [:y] are supported. Beware! Slices work like Python slices, not like VHDL or Verilog slices. The first bound is the index of the LSB and is inclusive. The second bound is the index of MSB and is exclusive. In V*HDL, bounds are MSB:LSB and both are inclusive. + +Concatenations +============== + +Concatenations are done using the ``Cat`` object. To make the syntax lighter, its constructor takes a variable number of arguments, which are the signals to be concatenated together (you can use the Python "*" operator to pass a list instead). +To be consistent with slices, the first signal is connected to the bits with the lowest indices in the result. This is the opposite of the way the "{}" construct works in Verilog. + +Replications +============ + +The ``Replicate`` object represents the equivalent of {count{expression}} in Verilog. + +Statements +********** + +Assignment +========== + +Assignments are represented with the ``_Assign`` object. Since using it directly would result in a cluttered syntax, the preferred technique for assignments is to use the ``eq()`` method provided by objects that can have a value assigned to them. They are signals, and their combinations with the slice and concatenation operators. +As an example, the statement: :: + + a[0].eq(b) + +is equivalent to: :: + + _Assign(_Slice(a, 0, 1), b) + +If +== + +The ``If`` object takes a first parameter which must be an expression (combination of the ``Constant``, ``Signal``, ``_Operator``, ``_Slice``, etc. objects) representing the condition, then a variable number of parameters representing the statements (``_Assign``, ``If``, ``Case``, etc. objects) to be executed when the condition is verified. + +The ``If`` object defines a ``Else()`` method, which when called defines the statements to be executed when the condition is not true. Those statements are passed as parameters to the variadic method. + +For convenience, there is also a ``Elif()`` method. + +Example: :: + + If(tx_count16 == 0, + tx_bitcount.eq(tx_bitcount + 1), + If(tx_bitcount == 8, + self.tx.eq(1) + ).Elif(tx_bitcount == 9, + self.tx.eq(1), + tx_busy.eq(0) + ).Else( + self.tx.eq(tx_reg[0]), + tx_reg.eq(Cat(tx_reg[1:], 0)) + ) + ) + +Case +==== + +The ``Case`` object constructor takes as first parameter the expression to be tested, and a dictionary whose keys are the values to be matched, and values the statements to be executed in the case of a match. The special value ``"default"`` can be used as match value, which means the statements should be executed whenever there is no other match. + +Arrays +====== + +The ``Array`` object represents lists of other objects that can be indexed by FHDL expressions. It is explicitly possible to: + +* nest ``Array`` objects to create multidimensional tables. +* list any Python object in a ``Array`` as long as every expression appearing in a module ultimately evaluates to a ``Signal`` for all possible values of the indices. This allows the creation of lists of structured data. +* use expressions involving ``Array`` objects in both directions (assignment and reading). + +For example, this creates a 4x4 matrix of 1-bit signals: :: + + my_2d_array = Array(Array(Signal() for a in range(4)) for b in range(4)) + +You can then read the matrix with (``x`` and ``y`` being 2-bit signals): :: + + out.eq(my_2d_array[x][y]) + +and write it with: :: + + my_2d_array[x][y].eq(inp) + +Since they have no direct equivalent in Verilog, ``Array`` objects are lowered into multiplexers and conditional statements before the actual conversion takes place. Such lowering happens automatically without any user intervention. + +Specials +******** + +Tri-state I/O +============= + +A triplet (O, OE, I) of one-way signals defining a tri-state I/O port is represented by the ``TSTriple`` object. Such objects are only containers for signals that are intended to be later connected to a tri-state I/O buffer, and cannot be used as module specials. Such objects, however, should be kept in the design as long as possible as they allow the individual one-way signals to be manipulated in a non-ambiguous way. + +The object that can be used in as a module special is ``Tristate``, and it behaves exactly like an instance of a tri-state I/O buffer that would be defined as follows: :: + + Instance("Tristate", + io_target=target, + i_o=o, + i_oe=oe, + o_i=i + ) + +Signals ``target``, ``o`` and ``i`` can have any width, while ``oe`` is 1-bit wide. The ``target`` signal should go to a port and not be used elsewhere in the design. Like modern FPGA architectures, Migen does not support internal tri-states. + +A ``Tristate`` object can be created from a ``TSTriple`` object by calling the ``get_tristate`` method. + +By default, Migen emits technology-independent behavioral code for a tri-state buffer. If a specific code is needed, the tristate handler can be overriden using the appropriate parameter of the V*HDL conversion function. + +Instances +========= + +Instance objects represent the parametrized instantiation of a V*HDL module, and the connection of its ports to FHDL signals. They are useful in a number of cases: + +* Reusing legacy or third-party V*HDL code. +* Using special FPGA features (DCM, ICAP, ...). +* Implementing logic that cannot be expressed with FHDL (e.g. latches). +* Breaking down a Migen system into multiple sub-systems. + +The instance object constructor takes the type (i.e. name of the instantiated module) of the instance, then multiple parameters describing how to connect and parametrize the instance. + +These parameters can be: + +* ``Instance.Input``, ``Instance.Output`` or ``Instance.InOut`` to describe signal connections with the instance. The parameters are the name of the port at the instance, and the FHDL expression it should be connected to. +* ``Instance.Parameter`` sets a parameter (with a name and value) of the instance. +* ``Instance.ClockPort`` and ``Instance.ResetPort`` are used to connect clock and reset signals to the instance. The only mandatory parameter is the name of the port at the instance. Optionally, a clock domain name can be specified, and the ``invert`` option can be used to interface to those modules that require a 180-degree clock or a active-low reset. + +Memories +======== + +Memories (on-chip SRAM) are supported using a mechanism similar to instances. + +A memory object has the following parameters: + +* The width, which is the number of bits in each word. +* The depth, which represents the number of words in the memory. +* An optional list of integers used to initialize the memory. + +To access the memory in hardware, ports can be obtained by calling the ``get_port`` method. A port always has an address signal ``a`` and a data read signal ``dat_r``. Other signals may be available depending on the port's configuration. + +Options to ``get_port`` are: + +* ``write_capable`` (default: ``False``): if the port can be used to write to the memory. This creates an additional ``we`` signal. +* ``async_read`` (default: ``False``): whether reads are asychronous (combinatorial) or synchronous (registered). +* ``has_re`` (default: ``False``): adds a read clock-enable signal ``re`` (ignored for asychronous ports). +* ``we_granularity`` (default: ``0``): if non-zero, writes of less than a memory word can occur. The width of the ``we`` signal is increased to act as a selection signal for the sub-words. +* ``mode`` (default: ``WRITE_FIRST``, ignored for aynchronous ports). It can be: + + * ``READ_FIRST``: during a write, the previous value is read. + * ``WRITE_FIRST``: the written value is returned. + * ``NO_CHANGE``: the data read signal keeps its previous value on a write. + +* ``clock_domain`` (default: ``"sys"``): the clock domain used for reading and writing from this port. + +Migen generates behavioural V*HDL code that should be compatible with all simulators and, if the number of ports is <= 2, most FPGA synthesizers. If a specific code is needed, the memory handler can be overriden using the appropriate parameter of the V*HDL conversion function. + +Inline synthesis directives +=========================== + +Inline synthesis directives (pseudo-comments such as ``// synthesis attribute keep of clock_signal_name is true``) are supported using the ``SynthesisDirective`` object. Its constructor takes as parameters a string containing the body of the directive, and optional keyword parameters that are used to replace signal names similarly to the Python string method ``format``. The above example could be represented as follows: :: + + SynthesisDirective("attribute keep of {clksig} is true", clksig=clock_domain.clk) + +Modules +******* + +Modules play the same role as Verilog modules and VHDL entities. Similarly, they are organized in a tree structure. A FHDL module is a Python object that derives from the ``Module`` class. This class defines special attributes to be used by derived classes to describe their logic. They are explained below. + +Combinatorial statements +======================== + +A combinatorial statement is a statement that is executed whenever one of its inputs changes. + +Combinatorial statements are added to a module by using the ``comb`` special attribute. Like most module special attributes, it must be accessed using the ``+=`` incrementation operator, and either a single statement, a tuple of statements or a list of statements can appear on the right hand side. + +For example, the module below implements a OR gate: :: + + class ORGate(Module): + def __init__(self): + self.a = Signal() + self.b = Signal() + self.x = Signal() + + ### + + self.comb += x.eq(a | b) + +To improve code readability, it is recommended to place the interface of the module at the beginning of the ``__init__`` function, and separate it from the implementation using three hash signs. + +Synchronous statements +====================== + +A synchronous statements is a statement that is executed at each edge of some clock signal. + +They are added to a module by using the ``sync`` special attribute, which has the same properties as the ``comb`` attribute. + +The ``sync`` special attribute also has sub-attributes that correspond to abstract clock domains. For example, to add a statement to the clock domain named ``foo``, one would write ``self.sync.foo += statement``. The default clock domain is ``sys`` and writing ``self.sync += statement`` is equivalent to writing ``self.sync.sys += statement``. + +Submodules and specials +======================= + +Submodules and specials can be added by using the ``submodules`` and ``specials`` attributes respectively. This can be done in two ways: + +#. anonymously, by using the ``+=`` operator on the special attribute directly, e.g. ``self.submodules += some_other_module``. Like with the ``comb`` and ``sync`` attributes, a single module/special or a tuple or list can be specified. +#. by naming the submodule/special using a subattribute of the ``submodules`` or ``specials`` attribute, e.g. ``self.submodules.foo = module_foo``. The submodule/special is then accessible as an attribute of the object, e.g. ``self.foo`` (and not ``self.submodules.foo``). Only one submodule/special can be added at a time using this form. + +Clock domains +============= + +Specifying the implementation of a clock domain is done using the ``ClockDomain`` object. It contains the name of the clock domain, a clock signal that can be driven like any other signal in the design (for example, using a PLL instance), and optionally a reset signal. Clock domains without a reset signal are reset using e.g. ``initial`` statements in Verilog, which in many FPGA families initalize the registers during configuration. + +The name can be omitted if it can be extracted from the variable name. When using this automatic naming feature, prefixes ``_``, ``cd_`` and ``_cd_`` are removed. + +Clock domains are then added to a module using the ``clock_domains`` special attribute, which behaves exactly like ``submodules`` and ``specials``. + +Summary of special attributes +============================= + +.. table:: + + +--------------------------------------------+--------------------------------------------------------------+ + | Syntax | Action | + +============================================+==============================================================+ + | self.comb += stmt | Add combinatorial statement to current module. | + +--------------------------------------------+--------------------------------------------------------------+ + | self.comb += stmtA, stmtB | Add combinatorial statements A and B to current module. | + | | | + | self.comb += [stmtA, stmtB] | | + +--------------------------------------------+--------------------------------------------------------------+ + | self.sync += stmt | Add synchronous statement to current module, in default | + | | clock domain sys. | + +--------------------------------------------+--------------------------------------------------------------+ + | self.sync.foo += stmt | Add synchronous statement to current module, in clock domain | + | | foo. | + +--------------------------------------------+--------------------------------------------------------------+ + | self.sync.foo += stmtA, stmtB | Add synchronous statements A and B to current module, in | + | | clock domain foo. | + | self.sync.foo += [stmtA, stmtB] | | + +--------------------------------------------+--------------------------------------------------------------+ + | self.submodules += mod | Add anonymous submodule to current module. | + +--------------------------------------------+--------------------------------------------------------------+ + | self.submodules += modA, modB | Add anonymous submodules A and B to current module. | + | | | + | self.submodules += [modA, modB] | | + +--------------------------------------------+--------------------------------------------------------------+ + | self.submodules.bar = mod | Add submodule named bar to current module. The submodule can | + | | then be accessed using self.bar. | + +--------------------------------------------+--------------------------------------------------------------+ + | self.specials += spe | Add anonymous special to current module. | + +--------------------------------------------+--------------------------------------------------------------+ + | self.specials += speA, speB | Add anonymous specials A and B to current module. | + | | | + | self.specials += [speA, speB] | | + +--------------------------------------------+--------------------------------------------------------------+ + | self.specials.bar = spe | Add special named bar to current module. The special can | + | | then be accessed using self.bar. | + +--------------------------------------------+--------------------------------------------------------------+ + | self.clock_domains += cd | Add clock domain to current module. | + +--------------------------------------------+--------------------------------------------------------------+ + | self.clock_domains += cdA, cdB | Add clock domains A and B to current module. | + | | | + | self.clock_domains += [cdA, cdB] | | + +--------------------------------------------+--------------------------------------------------------------+ + | self.clock_domains.pix = ClockDomain() | Create and add clock domain pix to current module. The clock | + | | domain name is pix in all cases. It can be accessed using | + | self.clock_domains._pix = ClockDomain() | self.pix, self._pix, self.cd_pix and self._cd_pix, | + | | respectively. | + | self.clock_domains.cd_pix = ClockDomain() | | + | | | + | self.clock_domains._cd_pix = ClockDomain() | | + +--------------------------------------------+--------------------------------------------------------------+ + +Clock domain management +======================= + +When a module has named submodules that define one or several clock domains with the same name, those clock domain names are prefixed with the name of each submodule plus an underscore. + +An example use case of this feature is a system with two independent video outputs. Each video output module is made of a clock generator module that defines a clock domain ``pix`` and drives the clock signal, plus a driver module that has synchronous statements and other elements in clock domain ``pix``. The designer of the video output module can simply use the clock domain name ``pix`` in that module. In the top-level system module, the video output submodules are named ``video0`` and ``video1``. Migen then automatically renames the ``pix`` clock domain of each module to ``video0_pix`` and ``video1_pix``. Note that happens only because the clock domain is defined (using ClockDomain objects), not simply referenced (using e.g. synchronous statements) in the video output modules. + +Clock domain name overlap is an error condition when any of the submodules that defines the clock domains is anonymous. + +Finalization mechanism +====================== + +Sometimes, it is desirable that some of a module logic be created only after the user has finished manipulating that module. For example, the FSM module supports that states be defined dynamically, and the width of the state signal can be known only after all states have been added. One solution is to declare the final number of states in the FSM constructor, but this is not user-friendly. A better solution is to automatically create the state signal just before the FSM module is converted to V*HDL. Migen supports this using the so-called finalization mechanism. + +Modules can overload a ``do_finalize`` method that can create logic and is called using the algorithm below: + +#. Finalization of the current module begins. +#. If the module has already been finalized (e.g. manually), the procedure stops here. +#. Submodules of the current module are recursively finalized. +#. ``do_finalize`` is called for the current module. +#. Any new submodules created by the current module's ``do_finalize`` are recursively finalized. + +Finalization is automatically invoked at V*HDL conversion and at simulation. It can be manually invoked for any module by calling its ``finalize`` method. + +The clock domain management mechanism explained above happens during finalization. + +Conversion for synthesis +************************ + +Any FHDL module can be converted into synthesizable Verilog HDL. This is accomplished by using the ``convert`` function in the ``verilog`` module. + +The ``migen.build`` component provides scripts to interface third-party FPGA tools (from Xilinx, Altera and Lattice) to Migen, and a database of boards for the easy deployment of designs. diff --git a/litex/gen/doc/index.rst b/litex/gen/doc/index.rst new file mode 100644 index 00000000..36f73aa3 --- /dev/null +++ b/litex/gen/doc/index.rst @@ -0,0 +1,11 @@ +Migen manual +############ + +.. toctree:: + :maxdepth: 2 + + introduction + fhdl + simulation + synthesis + reference diff --git a/litex/gen/doc/introduction.rst b/litex/gen/doc/introduction.rst new file mode 100644 index 00000000..bc24e336 --- /dev/null +++ b/litex/gen/doc/introduction.rst @@ -0,0 +1,73 @@ +Introduction +############ + +Migen is a Python-based tool that aims at automating further the VLSI design process. + +Migen makes it possible to apply modern software concepts such as object-oriented programming and metaprogramming to design hardware. This results in more elegant and easily maintained designs and reduces the incidence of human errors. + +.. _background: + +Background +********** + +Even though the Milkymist system-on-chip [mm]_ had many successes, it suffers from several limitations stemming from its implementation in manually written Verilog HDL: + +.. [mm] http://m-labs.hk + +#. The "event-driven" paradigm of today's dominant hardware descriptions languages (Verilog and VHDL, collectively referred to as "V*HDL" in the rest of this document) is often too general. Today's FPGA architectures are optimized for the implementation of fully synchronous circuits. This means that the bulk of the code for an efficient FPGA design falls into three categories: + + #. Combinatorial statements + #. Synchronous statements + #. Initialization of registers at reset + + V*HDL do not follow this organization. This means that a lot of repetitive manual coding is needed, which brings sources of human errors, petty issues, and confusion for beginners: + + #. wire vs. reg in Verilog + #. forgetting to initialize a register at reset + #. deciding whether a combinatorial statement must go into a process/always block or not + #. simulation mismatches with combinatorial processes/always blocks + #. and more... + + A little-known fact about FPGAs is that many of them have the ability to initialize their registers from the bitstream contents. This can be done in a portable and standard way using an "initial" block in Verilog, and by affecting a value at the signal declaration in VHDL. This renders an explicit reset signal unnecessary in practice in some cases, which opens the way for further design optimization. However, this form of initialization is entirely not synthesizable for ASIC targets, and it is not easy to switch between the two forms of reset using V*HDL. + +#. V*HDL support for composite types is very limited. Signals having a record type in VHDL are unidirectional, which makes them clumsy to use e.g. in bus interfaces. There is no record type support in Verilog, which means that a lot of copy-and-paste has to be done when forwarding grouped signals. + +#. V*HDL support for procedurally generated logic is extremely limited. The most advanced forms of procedural generation of synthesizable logic that V*HDL offers are CPP-style directives in Verilog, combinatorial functions, and ``generate`` statements. Nothing really fancy, and it shows. To give a few examples: + + #. Building highly flexible bus interconnect is not possible. Even arbitrating any given number of bus masters for commonplace protocols such as Wishbone is difficult with the tools that V*HDL puts at our disposal. + #. Building a memory infrastructure (including bus interconnect, bridges and caches) that can automatically adapt itself at compile-time to any word size of the SDRAM is clumsy and tedious. + #. Building register banks for control, status and interrupt management of cores can also largely benefit from automation. + #. Many hardware acceleration problems can fit into the dataflow programming model. Manual dataflow implementation in V*HDL has, again, a lot of redundancy and potential for human errors. See the Milkymist texture mapping unit [mthesis]_ [mxcell]_ for an example of this. The amount of detail to deal with manually also makes the design space exploration difficult, and therefore hinders the design of efficient architectures. + #. Pre-computation of values, such as filter coefficients for DSP or even simply trigonometric tables, must often be done using external tools whose results are copy-and-pasted (in the best case, automatically) into the V*HDL source. + +.. [mthesis] http://m-labs.hk/thesis/thesis.pdf +.. [mxcell] http://www.xilinx.com/publications/archives/xcell/Xcell77.pdf p30-35 + +Enter Migen, a Python toolbox for building complex digital hardware. We could have designed a brand new programming language, but that would have been reinventing the wheel instead of being able to benefit from Python's rich features and immense library. The price to pay is a slightly cluttered syntax at times when writing descriptions in FHDL, but we believe this is totally acceptable, particularly when compared to VHDL ;-) + +Migen is made up of several related components: + +#. the base language, FHDL +#. a library of small generic cores +#. a simulator +#. a build system + +Installing Migen +**************** + +Either run the ``setup.py`` installation script or simply set ``PYTHONPATH`` to the root of the source directory. + +If you wish to contribute patches, the suggest way to install is; + #. Clone from the git repository at http://github.com/m-labs/migen + #. Install using ``python3 ./setup.py develop --user`` + #. Edit the code in your git checkout. + +Alternative install methods +=========================== + + * Migen is available for the Anaconda Python distribution. The package can be found at at https://anaconda.org/m-labs/migen + * Migen can be referenced in a requirements.txt file (used for ``pip install -r requirements.txt``) via ``-e git+http://github.com/m-labs/migen.git#egg=migen``. See the pip documentation for more information. + +Feedback +******** +Feedback concerning Migen or this manual should be sent to the M-Labs developers' mailing list ``devel`` on lists.m-labs.hk. diff --git a/litex/gen/doc/migen_logo.png b/litex/gen/doc/migen_logo.png new file mode 100644 index 00000000..8c86fc04 Binary files /dev/null and b/litex/gen/doc/migen_logo.png differ diff --git a/litex/gen/doc/migen_logo.svg b/litex/gen/doc/migen_logo.svg new file mode 100644 index 00000000..fdb855ec --- /dev/null +++ b/litex/gen/doc/migen_logo.svg @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + migen + + + + diff --git a/litex/gen/doc/reference.rst b/litex/gen/doc/reference.rst new file mode 100644 index 00000000..bec65ce8 --- /dev/null +++ b/litex/gen/doc/reference.rst @@ -0,0 +1,37 @@ +API reference +============= + +:mod:`fhdl.structure` module +---------------------------- + +.. automodule:: migen.fhdl.structure + :members: + :show-inheritance: + +:mod:`fhdl.bitcontainer` module +------------------------------- + +.. automodule:: migen.fhdl.bitcontainer + :members: + :show-inheritance: + +:mod:`genlib.fifo` module +------------------------- + +.. automodule:: migen.genlib.fifo + :members: + :show-inheritance: + +:mod:`genlib.coding` module +--------------------------- + +.. automodule:: migen.genlib.coding + :members: + :show-inheritance: + +:mod:`genlib.sort` module +------------------------- + +.. automodule:: migen.genlib.sort + :members: + :show-inheritance: diff --git a/litex/gen/doc/simulation.rst b/litex/gen/doc/simulation.rst new file mode 100644 index 00000000..eff0ba5f --- /dev/null +++ b/litex/gen/doc/simulation.rst @@ -0,0 +1,6 @@ +Simulating a Migen design +######################### + +Migen allows you to easily simulate your FHDL design and interface it with arbitrary Python code. The simulator is written in pure Python and interprets the FHDL structure directly without using an external Verilog simulator. + +[To be rewritten] diff --git a/litex/gen/doc/synthesis.rst b/litex/gen/doc/synthesis.rst new file mode 100644 index 00000000..a5b99502 --- /dev/null +++ b/litex/gen/doc/synthesis.rst @@ -0,0 +1,4 @@ +Synthesizing a Migen design +########################### + +[To be written] diff --git a/litex/gen/examples/basic/arrays.py b/litex/gen/examples/basic/arrays.py new file mode 100644 index 00000000..0753107c --- /dev/null +++ b/litex/gen/examples/basic/arrays.py @@ -0,0 +1,28 @@ +from migen import * +from migen.fhdl import verilog + + +class Example(Module): + def __init__(self): + dx = 2 + dy = 2 + + x = Signal(max=dx) + y = Signal(max=dy) + out = Signal() + + my_2d_array = Array(Array(Signal() for a in range(dx)) for b in range(dy)) + self.comb += out.eq(my_2d_array[x][y]) + + we = Signal() + inp = Signal() + self.sync += If(we, + my_2d_array[x][y].eq(inp) + ) + + ina = Array(Signal() for a in range(dx)) + outa = Array(Signal() for a in range(dy)) + self.specials += Instance("test", o_O=outa[y], i_I=ina[x]) + +if __name__ == "__main__": + print(verilog.convert(Example())) diff --git a/litex/gen/examples/basic/fsm.py b/litex/gen/examples/basic/fsm.py new file mode 100644 index 00000000..ca235bfe --- /dev/null +++ b/litex/gen/examples/basic/fsm.py @@ -0,0 +1,31 @@ +from migen import * +from migen.fhdl import verilog + +class Example(Module): + def __init__(self): + self.s = Signal() + self.counter = Signal(8) + x = Array(Signal(name="a") for i in range(7)) + + myfsm = FSM() + self.submodules += myfsm + + myfsm.act("FOO", + self.s.eq(1), + NextState("BAR") + ) + myfsm.act("BAR", + self.s.eq(0), + NextValue(self.counter, self.counter + 1), + NextValue(x[self.counter], 89), + NextState("FOO") + ) + + self.be = myfsm.before_entering("FOO") + self.ae = myfsm.after_entering("FOO") + self.bl = myfsm.before_leaving("FOO") + self.al = myfsm.after_leaving("FOO") + +if __name__ == "__main__": + example = Example() + print(verilog.convert(example, {example.s, example.counter, example.be, example.ae, example.bl, example.al})) diff --git a/litex/gen/examples/basic/graycounter.py b/litex/gen/examples/basic/graycounter.py new file mode 100644 index 00000000..28865a24 --- /dev/null +++ b/litex/gen/examples/basic/graycounter.py @@ -0,0 +1,18 @@ +from random import Random + +from migen import * +from migen.genlib.cdc import GrayCounter + + +def tb(dut): + prng = Random(7345) + for i in range(35): + print("{0:0{1}b} CE={2} bin={3}".format((yield dut.q), + len(dut.q), (yield dut.ce), (yield dut.q_binary))) + yield dut.ce.eq(prng.getrandbits(1)) + yield + + +if __name__ == "__main__": + dut = GrayCounter(3) + run_simulation(dut, tb(dut), vcd_name="graycounter.vcd") diff --git a/litex/gen/examples/basic/instance.py b/litex/gen/examples/basic/instance.py new file mode 100644 index 00000000..fcabecc1 --- /dev/null +++ b/litex/gen/examples/basic/instance.py @@ -0,0 +1,68 @@ +import subprocess + +from migen import * +from migen.fhdl.verilog import convert + + +# Create a parent module with two instances of a child module. +# Bind input ports to first module and output ports to second, +# and create internal signals to connect the first module to the +# second. +class ParentModule(Module): + def __init__(self): + self.inputs = [Signal(x+1, name="input{}".format(x)) for x in range(4)] + self.trans = [Signal(x+1) for x in range(4)] + self.outputs = [Signal(x+1, name="output{}".format(x)) for x in range(4)] + self.io = set(self.inputs) | set(self.outputs) + i = Instance("ChildModule", + i_master_clk=ClockSignal(), + i_master_rst=ResetSignal(), + i_input0=self.inputs[0], + i_input1=self.inputs[1], + i_input2=self.inputs[2], + i_input3=self.inputs[3], + o_output0=self.trans[0], + o_output1=self.trans[1], + o_output2=self.trans[2], + o_output3=self.trans[3] + ) + j = Instance("ChildModule", + i_master_clk=ClockSignal(), + i_master_rst=ResetSignal(), + i_input0=self.trans[0], + i_input1=self.trans[1], + i_input2=self.trans[2], + i_input3=self.trans[3], + o_output0=self.outputs[0], + o_output1=self.outputs[1], + o_output2=self.outputs[2], + o_output3=self.outputs[3] + ) + self.specials += i, j + + +class ChildModule(Module): + def __init__(self): + self.inputs = [Signal(x+1, name_override="input{}".format(x)) for x in range(4)] + self.outputs = [Signal(x+1, name_override="output{}".format(x)) for x in range(4)] + self.io = set() + for x in range(4): + self.sync.master += self.outputs[x].eq(self.inputs[x]) + self.io = self.io.union(self.inputs) + self.io = self.io.union(self.outputs) + + +# Generate RTL for the parent module and the submodule, run through +# icarus for a syntax check +def test_instance_module(): + sub = ChildModule() + convert(sub, sub.io, name="ChildModule").write("ChildModule.v") + + im = ParentModule() + convert(im, im.io, name="ParentModule").write("ParentModule.v") + + subprocess.check_call(["iverilog", "-W", "all", + "ParentModule.v", "ChildModule.v"]) + +if __name__ == "__main__": + test_instance_module() diff --git a/litex/gen/examples/basic/local_cd.py b/litex/gen/examples/basic/local_cd.py new file mode 100644 index 00000000..dc055060 --- /dev/null +++ b/litex/gen/examples/basic/local_cd.py @@ -0,0 +1,19 @@ +from migen import * +from migen.fhdl import verilog +from migen.genlib.divider import Divider + + +class CDM(Module): + def __init__(self): + self.submodules.divider = Divider(5) + self.clock_domains.cd_sys = ClockDomain(reset_less=True) + + +class MultiMod(Module): + def __init__(self): + self.submodules.foo = CDM() + self.submodules.bar = CDM() + +if __name__ == "__main__": + mm = MultiMod() + print(verilog.convert(mm, {mm.foo.cd_sys.clk, mm.bar.cd_sys.clk})) diff --git a/litex/gen/examples/basic/memory.py b/litex/gen/examples/basic/memory.py new file mode 100644 index 00000000..6b9c1f2c --- /dev/null +++ b/litex/gen/examples/basic/memory.py @@ -0,0 +1,17 @@ +from migen import * +from migen.fhdl import verilog + + +class Example(Module): + def __init__(self): + self.specials.mem = Memory(32, 100, init=[5, 18, 32]) + p1 = self.mem.get_port(write_capable=True, we_granularity=8) + p2 = self.mem.get_port(has_re=True, clock_domain="rd") + self.specials += p1, p2 + self.ios = {p1.adr, p1.dat_r, p1.we, p1.dat_w, + p2.adr, p2.dat_r, p2.re} + + +if __name__ == "__main__": + example = Example() + print(verilog.convert(example, example.ios)) diff --git a/litex/gen/examples/basic/namer.py b/litex/gen/examples/basic/namer.py new file mode 100644 index 00000000..0c30c814 --- /dev/null +++ b/litex/gen/examples/basic/namer.py @@ -0,0 +1,44 @@ +from migen import * +from migen.fhdl import verilog + +from functools import reduce +from operator import or_ + + +def gen_list(n): + s = [Signal() for i in range(n)] + return s + + +def gen_2list(n): + s = [Signal(2) for i in range(n)] + return s + + +class Foo: + def __init__(self): + la = gen_list(3) + lb = gen_2list(2) + self.sigs = la + lb + + +class Bar: + def __init__(self): + self.sigs = gen_list(2) + + +class Example(Module): + def __init__(self): + a = [Bar() for x in range(3)] + b = [Foo() for x in range(3)] + c = b + b = [Bar() for x in range(2)] + + output = Signal() + allsigs = [] + for lst in [a, b, c]: + for obj in lst: + allsigs.extend(obj.sigs) + self.comb += output.eq(reduce(or_, allsigs)) + +print(verilog.convert(Example())) diff --git a/litex/gen/examples/basic/psync.py b/litex/gen/examples/basic/psync.py new file mode 100644 index 00000000..034ca69f --- /dev/null +++ b/litex/gen/examples/basic/psync.py @@ -0,0 +1,23 @@ +from migen import * +from migen.fhdl.specials import SynthesisDirective +from migen.fhdl import verilog +from migen.genlib.cdc import * + + +class XilinxMultiRegImpl(MultiRegImpl): + def __init__(self, *args, **kwargs): + MultiRegImpl.__init__(self, *args, **kwargs) + self.specials += set(SynthesisDirective("attribute shreg_extract of {r} is no", r=r) + for r in self.regs) + + +class XilinxMultiReg: + @staticmethod + def lower(dr): + return XilinxMultiRegImpl(dr.i, dr.o, dr.odomain, dr.n) + + +if __name__ == "__main__": + ps = PulseSynchronizer("from", "to") + v = verilog.convert(ps, {ps.i, ps.o}, special_overrides={MultiReg: XilinxMultiReg}) + print(v) diff --git a/litex/gen/examples/basic/record.py b/litex/gen/examples/basic/record.py new file mode 100644 index 00000000..59e72774 --- /dev/null +++ b/litex/gen/examples/basic/record.py @@ -0,0 +1,26 @@ +from migen import * +from migen.fhdl import verilog + + +L = [ + ("position", [ + ("x", 10, DIR_M_TO_S), + ("y", 10, DIR_M_TO_S), + ]), + ("color", 32, DIR_M_TO_S), + ("stb", 1, DIR_M_TO_S), + ("ack", 1, DIR_S_TO_M) +] + + +class Test(Module): + def __init__(self): + master = Record(L) + slave = Record(L) + self.comb += master.connect(slave) + + +if __name__ == "__main__": + print(verilog.convert(Test())) + print(layout_len(L)) + print(layout_partial(L, "position/x", "color")) diff --git a/litex/gen/examples/basic/reslice.py b/litex/gen/examples/basic/reslice.py new file mode 100644 index 00000000..c5fb502a --- /dev/null +++ b/litex/gen/examples/basic/reslice.py @@ -0,0 +1,19 @@ +from migen import * +from migen.fhdl import verilog + + +class Example(Module): + def __init__(self): + a = Signal(3) + b = Signal(4) + c = Signal(5) + d = Signal(7) + s1 = c[:3][:2] + s2 = Cat(a, b)[:6] + s3 = Cat(s1, s2)[-5:] + self.comb += s3.eq(0) + self.comb += d.eq(Cat(d[::-1], Cat(s1[:1], s3[-4:])[:3])) + + +if __name__ == "__main__": + print(verilog.convert(Example())) diff --git a/litex/gen/examples/basic/tristate.py b/litex/gen/examples/basic/tristate.py new file mode 100644 index 00000000..51afa033 --- /dev/null +++ b/litex/gen/examples/basic/tristate.py @@ -0,0 +1,13 @@ +from migen import * +from migen.fhdl import verilog + + +class Example(Module): + def __init__(self, n=6): + self.pad = Signal(n) + self.t = TSTriple(n) + self.specials += self.t.get_tristate(self.pad) + +if __name__ == "__main__": + e = Example() + print(verilog.convert(e, ios={e.pad, e.t.o, e.t.oe, e.t.i})) diff --git a/litex/gen/examples/basic/two_dividers.py b/litex/gen/examples/basic/two_dividers.py new file mode 100644 index 00000000..6869d548 --- /dev/null +++ b/litex/gen/examples/basic/two_dividers.py @@ -0,0 +1,19 @@ +from migen import * +from migen.fhdl import verilog +from migen.genlib import divider + + +@ResetInserter() +@CEInserter() +class Example(Module): + def __init__(self, width): + d1 = divider.Divider(width) + d2 = divider.Divider(width) + self.submodules += d1, d2 + self.ios = { + d1.ready_o, d1.quotient_o, d1.remainder_o, d1.start_i, d1.dividend_i, d1.divisor_i, + d2.ready_o, d2.quotient_o, d2.remainder_o, d2.start_i, d2.dividend_i, d2.divisor_i} + +if __name__ == "__main__": + example = Example(16) + print(verilog.convert(example, example.ios | {example.ce, example.reset})) diff --git a/litex/gen/examples/sim/basic1.py b/litex/gen/examples/sim/basic1.py new file mode 100644 index 00000000..62afd21b --- /dev/null +++ b/litex/gen/examples/sim/basic1.py @@ -0,0 +1,29 @@ +from migen import * + + +# Our simple counter, which increments at every cycle. +class Counter(Module): + def __init__(self): + self.count = Signal(4) + + # At each cycle, increase the value of the count signal. + # We do it with convertible/synthesizable FHDL code. + self.sync += self.count.eq(self.count + 1) + + +# Simply read the count signal and print it. +# The output is: +# Count: 0 +# Count: 1 +# Count: 2 +# ... +def counter_test(dut): + for i in range(20): + print((yield dut.count)) # read and print + yield # next clock cycle + # simulation ends with this generator + + +if __name__ == "__main__": + dut = Counter() + run_simulation(dut, counter_test(dut), vcd_name="basic1.vcd") diff --git a/litex/gen/examples/sim/basic2.py b/litex/gen/examples/sim/basic2.py new file mode 100644 index 00000000..45bc676f --- /dev/null +++ b/litex/gen/examples/sim/basic2.py @@ -0,0 +1,37 @@ +from migen import * + + +# A slightly more elaborate counter. +# Has a clock enable (CE) signal, counts on more bits +# and resets with a negative number. +class Counter(Module): + def __init__(self): + self.ce = Signal() + # Demonstrate negative numbers and signals larger than 32 bits. + self.count = Signal((37, True), reset=-5) + + self.sync += If(self.ce, self.count.eq(self.count + 1)) + + +def counter_test(dut): + for cycle in range(20): + # Only assert CE every second cycle. + # => each counter value is held for two cycles. + if cycle % 2: + yield dut.ce.eq(0) # This is how you write to a signal. + else: + yield dut.ce.eq(1) + print("Cycle: {} Count: {}".format(cycle, (yield dut.count))) + yield + +# Output is: +# Cycle: 0 Count: -5 +# Cycle: 1 Count: -5 +# Cycle: 2 Count: -4 +# Cycle: 3 Count: -4 +# Cycle: 4 Count: -3 +# ... + +if __name__ == "__main__": + dut = Counter() + run_simulation(dut, counter_test(dut), vcd_name="basic2.vcd") diff --git a/litex/gen/examples/sim/fir.py b/litex/gen/examples/sim/fir.py new file mode 100644 index 00000000..67a78bc7 --- /dev/null +++ b/litex/gen/examples/sim/fir.py @@ -0,0 +1,67 @@ +from functools import reduce +from operator import add + +from math import cos, pi +from scipy import signal +import matplotlib.pyplot as plt + +from migen import * +from migen.fhdl import verilog + + +# A synthesizable FIR filter. +class FIR(Module): + def __init__(self, coef, wsize=16): + self.coef = coef + self.wsize = wsize + self.i = Signal((self.wsize, True)) + self.o = Signal((self.wsize, True)) + + ### + + muls = [] + src = self.i + for c in self.coef: + sreg = Signal((self.wsize, True)) + self.sync += sreg.eq(src) + src = sreg + c_fp = int(c*2**(self.wsize - 1)) + muls.append(c_fp*sreg) + sum_full = Signal((2*self.wsize-1, True)) + self.sync += sum_full.eq(reduce(add, muls)) + self.comb += self.o.eq(sum_full >> self.wsize-1) + + +# A test bench for our FIR filter. +# Generates a sine wave at the input and records the output. +def fir_tb(dut, frequency, inputs, outputs): + f = 2**(dut.wsize - 1) + for cycle in range(200): + v = 0.1*cos(2*pi*frequency*cycle) + yield dut.i.eq(int(f*v)) + inputs.append(v) + outputs.append((yield dut.o)/f) + yield + + +if __name__ == "__main__": + # Compute filter coefficients with SciPy. + coef = signal.remez(30, [0, 0.1, 0.2, 0.4, 0.45, 0.5], [0, 1, 0]) + + # Simulate for different frequencies and concatenate + # the results. + in_signals = [] + out_signals = [] + for frequency in [0.05, 0.1, 0.25]: + dut = FIR(coef) + tb = fir_tb(dut, frequency, in_signals, out_signals) + run_simulation(dut, tb) + + # Plot data from the input and output waveforms. + plt.plot(in_signals) + plt.plot(out_signals) + plt.show() + + # Print the Verilog source for the filter. + fir = FIR(coef) + print(verilog.convert(fir, ios={fir.i, fir.o})) diff --git a/litex/gen/examples/sim/memory.py b/litex/gen/examples/sim/memory.py new file mode 100644 index 00000000..51a0c329 --- /dev/null +++ b/litex/gen/examples/sim/memory.py @@ -0,0 +1,26 @@ +from migen import * + + +class Mem(Module): + def __init__(self): + # Initialize the beginning of the memory with integers + # from 0 to 19. + self.specials.mem = Memory(16, 2**12, init=list(range(20))) + + +def memory_test(dut): + # write (only first 5 values) + for i in range(5): + yield dut.mem[i].eq(42 + i) + # remember: values are written after the tick, and read before the tick. + # wait one tick for the memory to update. + yield + # read what we have written, plus some initialization data + for i in range(10): + value = yield dut.mem[i] + print(value) + + +if __name__ == "__main__": + dut = Mem() + run_simulation(dut, memory_test(dut)) diff --git a/litex/gen/migen/__init__.py b/litex/gen/migen/__init__.py new file mode 100644 index 00000000..492117f2 --- /dev/null +++ b/litex/gen/migen/__init__.py @@ -0,0 +1,10 @@ +from migen.fhdl.structure import * +from migen.fhdl.module import * +from migen.fhdl.specials import * +from migen.fhdl.bitcontainer import * +from migen.fhdl.decorators import * + +from migen.sim import * + +from migen.genlib.record import * +from migen.genlib.fsm import * diff --git a/litex/gen/migen/build/__init__.py b/litex/gen/migen/build/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/litex/gen/migen/build/altera/__init__.py b/litex/gen/migen/build/altera/__init__.py new file mode 100644 index 00000000..9fc45d4a --- /dev/null +++ b/litex/gen/migen/build/altera/__init__.py @@ -0,0 +1,2 @@ +from migen.build.altera.platform import AlteraPlatform +from migen.build.altera.programmer import USBBlaster diff --git a/litex/gen/migen/build/altera/common.py b/litex/gen/migen/build/altera/common.py new file mode 100644 index 00000000..fafb4983 --- /dev/null +++ b/litex/gen/migen/build/altera/common.py @@ -0,0 +1,39 @@ +from migen.fhdl.module import Module +from migen.fhdl.specials import Instance +from migen.genlib.io import DifferentialInput, DifferentialOutput + + +class AlteraDifferentialInputImpl(Module): + def __init__(self, i_p, i_n, o): + self.specials += Instance("ALT_INBUF_DIFF", + name="ibuf_diff", + i_i=i_p, + i_ibar=i_n, + o_o=o) + + +class AlteraDifferentialInput: + @staticmethod + def lower(dr): + return AlteraDifferentialInputImpl(dr.i_p, dr.i_n, dr.o) + + +class AlteraDifferentialOutputImpl(Module): + def __init__(self, i, o_p, o_n): + self.specials += Instance("ALT_OUTBUF_DIFF", + name="obuf_diff", + i_i=i, + o_o=o_p, + o_obar=o_n) + + +class AlteraDifferentialOutput: + @staticmethod + def lower(dr): + return AlteraDifferentialOutputImpl(dr.i, dr.o_p, dr.o_n) + + +altera_special_overrides = { + DifferentialInput: AlteraDifferentialInput, + DifferentialOutput: AlteraDifferentialOutput +} diff --git a/litex/gen/migen/build/altera/platform.py b/litex/gen/migen/build/altera/platform.py new file mode 100644 index 00000000..43841fa7 --- /dev/null +++ b/litex/gen/migen/build/altera/platform.py @@ -0,0 +1,27 @@ +from migen.build.generic_platform import GenericPlatform +from migen.build.altera import common, quartus + + +class AlteraPlatform(GenericPlatform): + bitstream_ext = ".sof" + + def __init__(self, *args, toolchain="quartus", **kwargs): + GenericPlatform.__init__(self, *args, **kwargs) + if toolchain == "quartus": + self.toolchain = quartus.AlteraQuartusToolchain() + else: + raise ValueError("Unknown toolchain") + + def get_verilog(self, *args, special_overrides=dict(), **kwargs): + so = dict(common.altera_special_overrides) + so.update(special_overrides) + return GenericPlatform.get_verilog(self, *args, special_overrides=so, + **kwargs) + + def build(self, *args, **kwargs): + return self.toolchain.build(self, *args, **kwargs) + + def add_period_constraint(self, clk, period): + if hasattr(clk, "p"): + clk = clk.p + self.toolchain.add_period_constraint(self, clk, period) diff --git a/litex/gen/migen/build/altera/programmer.py b/litex/gen/migen/build/altera/programmer.py new file mode 100644 index 00000000..369f7db1 --- /dev/null +++ b/litex/gen/migen/build/altera/programmer.py @@ -0,0 +1,13 @@ +import subprocess + +from migen.build.generic_programmer import GenericProgrammer + + +class USBBlaster(GenericProgrammer): + needs_bitreverse = False + + def load_bitstream(self, bitstream_file, port=0): + usb_port = "[USB-{}]".format(port) + subprocess.call(["quartus_pgm", "-m", "jtag", "-c", + "USB-Blaster{}".format(usb_port), "-o", + "p;{}".format(bitstream_file)]) diff --git a/litex/gen/migen/build/altera/quartus.py b/litex/gen/migen/build/altera/quartus.py new file mode 100644 index 00000000..99f6e978 --- /dev/null +++ b/litex/gen/migen/build/altera/quartus.py @@ -0,0 +1,151 @@ +# This file is Copyright (c) 2013 Florent Kermarrec +# License: BSD + +import os +import subprocess + +from migen.fhdl.structure import _Fragment + +from migen.build.generic_platform import Pins, IOStandard, Misc +from migen.build import tools + + +def _format_constraint(c, signame, fmt_r): + if isinstance(c, Pins): + return "set_location_assignment -comment \"{name}\" " \ + "-to {signame} Pin_{pin}".format( + signame=signame, + name=fmt_r, + pin=c.identifiers[0]) + elif isinstance(c, IOStandard): + return "set_instance_assignment -name io_standard " \ + "-comment \"{name}\" \"{std}\" -to {signame}".format( + signame=signame, + name=fmt_r, + std=c.name) + elif isinstance(c, Misc): + if not isinstance(c.misc, str) and len(c.misc) == 2: + return "set_instance_assignment -comment \"{name}\" " \ + "-name {misc[0]} \"{misc[1]}\" -to {signame}".format( + signame=signame, + name=fmt_r, + misc=c.misc) + else: + return "set_instance_assignment -comment \"{name}\" " \ + "-name {misc} " \ + "-to {signame}".format( + signame=signame, + name=fmt_r, + misc=c.misc) + + +def _format_qsf(signame, pin, others, resname): + fmt_r = "{}:{}".format(*resname[:2]) + if resname[2] is not None: + fmt_r += "." + resname[2] + + fmt_c = [_format_constraint(c, signame, fmt_r) for c in + ([Pins(pin)] + others)] + + return '\n'.join(fmt_c) + + +def _build_qsf(named_sc, named_pc): + lines = [] + for sig, pins, others, resname in named_sc: + if len(pins) > 1: + for i, p in enumerate(pins): + lines.append( + _format_qsf("{}[{}]".format(sig, i), p, others, resname)) + else: + lines.append(_format_qsf(sig, pins[0], others, resname)) + + if named_pc: + lines.append("") + lines.append("\n\n".join(named_pc)) + + lines.append("set_global_assignment -name top_level_entity top") + return "\n".join(lines) + + +def _build_files(device, sources, vincpaths, named_sc, named_pc, build_name): + lines = [] + for filename, language, library in sources: + # Enforce use of SystemVerilog + # (Quartus does not support global parameters in Verilog) + if language == "verilog": + language = "systemverilog" + lines.append( + "set_global_assignment -name {lang}_FILE {path} " + "-library {lib}".format( + lang=language.upper(), + path=filename.replace("\\", "/"), + lib=library)) + + for path in vincpaths: + lines.append("set_global_assignment -name SEARCH_PATH {}".format( + path.replace("\\", "/"))) + + lines.append(_build_qsf(named_sc, named_pc)) + lines.append("set_global_assignment -name DEVICE {}".format(device)) + tools.write_to_file("{}.qsf".format(build_name), "\n".join(lines)) + + +def _run_quartus(build_name, quartus_path): + build_script_contents = """# Autogenerated by Migen + +set -e + +quartus_map --read_settings_files=on --write_settings_files=off {build_name} -c {build_name} +quartus_fit --read_settings_files=off --write_settings_files=off {build_name} -c {build_name} +quartus_asm --read_settings_files=off --write_settings_files=off {build_name} -c {build_name} +quartus_sta {build_name} -c {build_name} + +""".format(build_name=build_name) # noqa + build_script_file = "build_" + build_name + ".sh" + tools.write_to_file(build_script_file, + build_script_contents, + force_unix=True) + + if subprocess.call(["bash", build_script_file]): + raise OSError("Subprocess failed") + + +class AlteraQuartusToolchain: + def build(self, platform, fragment, build_dir="build", build_name="top", + toolchain_path="/opt/Altera", run=True): + tools.mkdir_noerror(build_dir) + os.chdir(build_dir) + + if not isinstance(fragment, _Fragment): + fragment = fragment.get_fragment() + platform.finalize(fragment) + + v_output = platform.get_verilog(fragment) + named_sc, named_pc = platform.resolve_signals(v_output.ns) + v_file = build_name + ".v" + v_output.write(v_file) + sources = platform.sources | {(v_file, "verilog", "work")} + _build_files(platform.device, + sources, + platform.verilog_include_paths, + named_sc, + named_pc, + build_name) + if run: + _run_quartus(build_name, toolchain_path) + + os.chdir("..") + + return v_output.ns + + def add_period_constraint(self, platform, clk, period): + # TODO: handle differential clk + platform.add_platform_command( + "set_global_assignment -name duty_cycle 50 -section_id {clk}", + clk=clk) + platform.add_platform_command( + "set_global_assignment -name fmax_requirement \"{freq} MHz\" " + "-section_id {clk}".format(freq=(1. / period) * 1000, + clk="{clk}"), + clk=clk) diff --git a/litex/gen/migen/build/fpgalink_programmer.py b/litex/gen/migen/build/fpgalink_programmer.py new file mode 100644 index 00000000..fc7586dc --- /dev/null +++ b/litex/gen/migen/build/fpgalink_programmer.py @@ -0,0 +1,102 @@ +import os + +from migen.build.generic_programmer import GenericProgrammer +from migen.build.xilinx.programmer import _create_xsvf + +try: + import fl +except ImportError: + import fpgalink3 as fl + +fl.flInitialise(0) + + +class FPGALink(GenericProgrammer): + """Using the fpgalink library from makestuff + + You will need fpgalink library installed from + https://github.com/makestuff/libfpgalink + """ + + needs_bitreverse = False + + def __init__(self, initial_vidpid=None, pin_cfg="D0D2D3D4", + fpgalink_vidpid="1D50:602B:0002", flash_proxy_basename=None): + """ + Parameters + ---------- + initial_vidpid : string + The USB vendor and product id of the device before fpgalink + firmware is loaded onto the device. + + Format is vid:pid as 4 digit hex numbers. + + pin_cfg : string + FPGALink pin configuration string describing how the JTAG interface + is hooked up to the programmer. + + fpgalink_vidpid : string + The USB vendor, product and device id of the device after the + fpgalink firmware is loaded onto the device. + + Format is vid:pid:did as 4 digit hex numbers. + Defaults to 1D50:602B:0002 which is the makestuff FPGALink device. + """ + GenericProgrammer.__init__(self, flash_proxy_basename) + self.initial_vidpid = initial_vidpid + self.fpgalink_vidpid = fpgalink_vidpid + self.pin_cfg = pin_cfg + + def open_device(self): + ivp = self.initial_vidpid + vp = self.fpgalink_vidpid + + print("Attempting to open connection to FPGALink device", vp, "...") + try: + handle = fl.flOpen(self.fpgalink_vidpid) + except fl.FLException as ex: + if not ivp: + raise FLException( + "Could not open FPGALink device at {0} and" + " no initial VID:PID was supplied".format(vp)) + + print("Loading firmware into %s..." % ivp) + fl.flLoadStandardFirmware(ivp, vp) + + print("Awaiting renumeration...") + if not fl.flAwaitDevice(vp, 600): + raise fl.FLException( + "FPGALink device did not renumerate properly" + " as {0}".format(vp)) + + print("Attempting to open connection to FPGALink device", vp, + "again...") + handle = fl.flOpen(vp) + + # Only Nero capable hardware support doing programming. + assert fl.flIsNeroCapable(handle) + print("Cable connection opened.") + return handle + + def load_bitstream(self, bitstream_file): + n = 27 + + xsvf_file = os.path.splitext(bitstream_file)[0]+'.xsvf' + print("\nGenerating xsvf formatted bitstream") + print("="*n) + if os.path.exists(xsvf_file): + os.unlink(xsvf_file) + _create_xsvf(bitstream_file, xsvf_file) + print("\n"+"="*n+"\n") + + print("Programming %s to device." % xsvf_file) + print("="*n) + handle = self.open_device() + print("Programming device...") + fl.flProgram(handle, "J:"+self.pin_cfg, progFile=xsvf_file) + print("Programming successful!") + print("="*n+"\n") + fl.flClose(handle) + + def flash(self, address, data_file): + raise NotImplementedError("Not supported yet.") diff --git a/litex/gen/migen/build/generic_platform.py b/litex/gen/migen/build/generic_platform.py new file mode 100644 index 00000000..5163d922 --- /dev/null +++ b/litex/gen/migen/build/generic_platform.py @@ -0,0 +1,343 @@ +import os + +from migen.fhdl.structure import Signal +from migen.genlib.record import Record +from migen.genlib.io import CRG +from migen.fhdl import verilog, edif +from migen.build import tools + + +class ConstraintError(Exception): + pass + + +class Pins: + def __init__(self, *identifiers): + self.identifiers = [] + for i in identifiers: + self.identifiers += i.split() + + def __repr__(self): + return "{}('{}')".format(self.__class__.__name__, + " ".join(self.identifiers)) + + +class IOStandard: + def __init__(self, name): + self.name = name + + def __repr__(self): + return "{}('{}')".format(self.__class__.__name__, self.name) + + +class Drive: + def __init__(self, strength): + self.strength = strength + + def __repr__(self): + return "{}('{}')".format(self.__class__.__name__, self.strength) + + +class Misc: + def __init__(self, misc): + self.misc = misc + + def __repr__(self): + return "{}({})".format(self.__class__.__name__, repr(self.misc)) + + +class Subsignal: + def __init__(self, name, *constraints): + self.name = name + self.constraints = list(constraints) + + def __repr__(self): + return "{}('{}', {})".format( + self.__class__.__name__, + self.name, + ", ".join([repr(constr) for constr in self.constraints])) + + +class PlatformInfo: + def __init__(self, info): + self.info = info + + def __repr__(self): + return "{}({})".format(self.__class__.__name__, repr(self.info)) + + +def _lookup(description, name, number): + for resource in description: + if resource[0] == name and (number is None or resource[1] == number): + return resource + raise ConstraintError("Resource not found: {}:{}".format(name, number)) + + +def _resource_type(resource): + t = None + for element in resource[2:]: + if isinstance(element, Pins): + assert(t is None) + t = len(element.identifiers) + elif isinstance(element, Subsignal): + if t is None: + t = [] + + assert(isinstance(t, list)) + n_bits = None + for c in element.constraints: + if isinstance(c, Pins): + assert(n_bits is None) + n_bits = len(c.identifiers) + + t.append((element.name, n_bits)) + + return t + + +class ConnectorManager: + def __init__(self, connectors): + self.connector_table = dict() + for connector in connectors: + cit = iter(connector) + conn_name = next(cit) + if isinstance(connector[1], str): + pin_list = [] + for pins in cit: + pin_list += pins.split() + pin_list = [None if pin == "None" else pin for pin in pin_list] + elif isinstance(connector[1], dict): + pin_list = connector[1] + else: + raise ValueError("Unsupported pin list type {} for connector" + " {}".format(type(connector[1]), conn_name)) + if conn_name in self.connector_table: + raise ValueError( + "Connector specified more than once: {}".format(conn_name)) + + self.connector_table[conn_name] = pin_list + + def resolve_identifiers(self, identifiers): + r = [] + for identifier in identifiers: + if ":" in identifier: + conn, pn = identifier.split(":") + if pn.isdigit(): + pn = int(pn) + + r.append(self.connector_table[conn][pn]) + else: + r.append(identifier) + + return r + + +def _separate_pins(constraints): + pins = None + others = [] + for c in constraints: + if isinstance(c, Pins): + assert(pins is None) + pins = c.identifiers + else: + others.append(c) + + return pins, others + + +class ConstraintManager: + def __init__(self, io, connectors): + self.available = list(io) + self.matched = [] + self.platform_commands = [] + self.connector_manager = ConnectorManager(connectors) + + def add_extension(self, io): + self.available.extend(io) + + def request(self, name, number=None): + resource = _lookup(self.available, name, number) + rt = _resource_type(resource) + if isinstance(rt, int): + obj = Signal(rt, name_override=resource[0]) + else: + obj = Record(rt, name=resource[0]) + + for element in resource[2:]: + if isinstance(element, PlatformInfo): + obj.platform_info = element.info + break + + self.available.remove(resource) + self.matched.append((resource, obj)) + return obj + + def lookup_request(self, name, number=None): + for resource, obj in self.matched: + if resource[0] == name and (number is None or + resource[1] == number): + return obj + + raise ConstraintError("Resource not found: {}:{}".format(name, number)) + + def add_platform_command(self, command, **signals): + self.platform_commands.append((command, signals)) + + def get_io_signals(self): + r = set() + for resource, obj in self.matched: + if isinstance(obj, Signal): + r.add(obj) + else: + r.update(obj.flatten()) + + return r + + def get_sig_constraints(self): + r = [] + for resource, obj in self.matched: + name = resource[0] + number = resource[1] + has_subsignals = False + top_constraints = [] + for element in resource[2:]: + if isinstance(element, Subsignal): + has_subsignals = True + else: + top_constraints.append(element) + + if has_subsignals: + for element in resource[2:]: + if isinstance(element, Subsignal): + sig = getattr(obj, element.name) + pins, others = _separate_pins(top_constraints + + element.constraints) + pins = self.connector_manager.resolve_identifiers(pins) + r.append((sig, pins, others, + (name, number, element.name))) + else: + pins, others = _separate_pins(top_constraints) + pins = self.connector_manager.resolve_identifiers(pins) + r.append((obj, pins, others, (name, number, None))) + + return r + + def get_platform_commands(self): + return self.platform_commands + + +class GenericPlatform: + def __init__(self, device, io, connectors=[], name=None): + self.device = device + self.constraint_manager = ConstraintManager(io, connectors) + if name is None: + name = self.__module__.split(".")[-1] + self.name = name + self.sources = set() + self.verilog_include_paths = set() + self.finalized = False + + def request(self, *args, **kwargs): + return self.constraint_manager.request(*args, **kwargs) + + def lookup_request(self, *args, **kwargs): + return self.constraint_manager.lookup_request(*args, **kwargs) + + def add_period_constraint(self, clk, period): + raise NotImplementedError + + def add_platform_command(self, *args, **kwargs): + return self.constraint_manager.add_platform_command(*args, **kwargs) + + def add_extension(self, *args, **kwargs): + return self.constraint_manager.add_extension(*args, **kwargs) + + def finalize(self, fragment, *args, **kwargs): + if self.finalized: + raise ConstraintError("Already finalized") + # if none exists, create a default clock domain and drive it + if not fragment.clock_domains: + if not hasattr(self, "default_clk_name"): + raise NotImplementedError( + "No default clock and no clock domain defined") + crg = CRG(self.request(self.default_clk_name)) + fragment += crg.get_fragment() + + self.do_finalize(fragment, *args, **kwargs) + self.finalized = True + + def do_finalize(self, fragment, *args, **kwargs): + """overload this and e.g. add_platform_command()'s after the modules + had their say""" + if hasattr(self, "default_clk_period"): + try: + self.add_period_constraint( + self.lookup_request(self.default_clk_name), + self.default_clk_period) + except ConstraintError: + pass + + def add_source(self, filename, language=None, library=None): + if language is None: + language = tools.language_by_filename(filename) + if language is None: + language = "verilog" + + if library is None: + library = "work" + + self.sources.add((os.path.abspath(filename), language, library)) + + def add_sources(self, path, *filenames, language=None, library=None): + for f in filenames: + self.add_source(os.path.join(path, f), language, library) + + def add_source_dir(self, path, recursive=True, library=None): + dir_files = [] + if recursive: + for root, dirs, files in os.walk(path): + for filename in files: + dir_files.append(os.path.join(root, filename)) + else: + for item in os.listdir(path): + if os.path.isfile(os.path.join(path, item)): + dir_files.append(os.path.join(path, item)) + for filename in dir_files: + language = tools.language_by_filename(filename) + if language is not None: + self.add_source(filename, language, library) + + def add_verilog_include_path(self, path): + self.verilog_include_paths.add(os.path.abspath(path)) + + def resolve_signals(self, vns): + # resolve signal names in constraints + sc = self.constraint_manager.get_sig_constraints() + named_sc = [(vns.get_name(sig), pins, others, resource) + for sig, pins, others, resource in sc] + # resolve signal names in platform commands + pc = self.constraint_manager.get_platform_commands() + named_pc = [] + for template, args in pc: + name_dict = dict((k, vns.get_name(sig)) for k, sig in args.items()) + named_pc.append(template.format(**name_dict)) + + return named_sc, named_pc + + def get_verilog(self, fragment, **kwargs): + return verilog.convert( + fragment, + self.constraint_manager.get_io_signals(), + create_clock_domains=False, **kwargs) + + def get_edif(self, fragment, cell_library, vendor, device, **kwargs): + return edif.convert( + fragment, + self.constraint_manager.get_io_signals(), + cell_library, vendor, device, **kwargs) + + def build(self, fragment): + raise NotImplementedError("GenericPlatform.build must be overloaded") + + def create_programmer(self): + raise NotImplementedError diff --git a/litex/gen/migen/build/generic_programmer.py b/litex/gen/migen/build/generic_programmer.py new file mode 100644 index 00000000..82a1ebe0 --- /dev/null +++ b/litex/gen/migen/build/generic_programmer.py @@ -0,0 +1,31 @@ +import os + + +class GenericProgrammer: + def __init__(self, flash_proxy_basename=None): + self.flash_proxy_basename = flash_proxy_basename + self.flash_proxy_dirs = [ + "~/.migen", "/usr/local/share/migen", "/usr/share/migen", + "~/.mlabs", "/usr/local/share/mlabs", "/usr/share/mlabs"] + + def set_flash_proxy_dir(self, flash_proxy_dir): + if flash_proxy_dir is not None: + self.flash_proxy_dirs = [flash_proxy_dir] + + def find_flash_proxy(self): + for d in self.flash_proxy_dirs: + fulldir = os.path.abspath(os.path.expanduser(d)) + fullname = os.path.join(fulldir, self.flash_proxy_basename) + if os.path.exists(fullname): + return fullname + raise OSError("Failed to find flash proxy bitstream") + + # must be overloaded by specific programmer + def load_bitstream(self, bitstream_file): + raise NotImplementedError + + # must be overloaded by specific programmer + def flash(self, address, data_file): + raise NotImplementedError + + diff --git a/litex/gen/migen/build/lattice/__init__.py b/litex/gen/migen/build/lattice/__init__.py new file mode 100644 index 00000000..78b2035d --- /dev/null +++ b/litex/gen/migen/build/lattice/__init__.py @@ -0,0 +1,2 @@ +from migen.build.lattice.platform import LatticePlatform +from migen.build.lattice.programmer import LatticeProgrammer diff --git a/litex/gen/migen/build/lattice/common.py b/litex/gen/migen/build/lattice/common.py new file mode 100644 index 00000000..c25e8b90 --- /dev/null +++ b/litex/gen/migen/build/lattice/common.py @@ -0,0 +1,41 @@ +from migen.fhdl.module import Module +from migen.fhdl.specials import Instance +from migen.genlib.io import * +from migen.genlib.resetsync import AsyncResetSynchronizer + + +class LatticeAsyncResetSynchronizerImpl(Module): + def __init__(self, cd, async_reset): + rst1 = Signal() + self.specials += [ + Instance("FD1S3BX", i_D=0, i_PD=async_reset, + i_CK=cd.clk, o_Q=rst1), + Instance("FD1S3BX", i_D=rst1, i_PD=async_reset, + i_CK=cd.clk, o_Q=cd.rst) + ] + + +class LatticeAsyncResetSynchronizer: + @staticmethod + def lower(dr): + return LatticeAsyncResetSynchronizerImpl(dr.cd, dr.async_reset) + + +class LatticeDDROutputImpl(Module): + def __init__(self, i1, i2, o, clk): + self.specials += Instance("ODDRXD1", + synthesis_directive="ODDRAPPS=\"SCLK_ALIGNED\"", + i_SCLK=clk, + i_DA=i1, i_DB=i2, o_Q=o, + ) + + +class LatticeDDROutput: + @staticmethod + def lower(dr): + return LatticeDDROutputImpl(dr.i1, dr.i2, dr.o, dr.clk) + +lattice_special_overrides = { + AsyncResetSynchronizer: LatticeAsyncResetSynchronizer, + DDROutput: LatticeDDROutput +} diff --git a/litex/gen/migen/build/lattice/diamond.py b/litex/gen/migen/build/lattice/diamond.py new file mode 100644 index 00000000..1334040b --- /dev/null +++ b/litex/gen/migen/build/lattice/diamond.py @@ -0,0 +1,105 @@ +# This file is Copyright (c) 2015 Florent Kermarrec +# License: BSD + +import os +import sys +import subprocess +import shutil + +from migen.fhdl.structure import _Fragment + +from migen.build.generic_platform import * +from migen.build import tools +from migen.build.lattice import common + + +def _format_constraint(c): + if isinstance(c, Pins): + return ("LOCATE COMP ", " SITE " + "\"" + c.identifiers[0] + "\"") + elif isinstance(c, IOStandard): + return ("IOBUF PORT ", " IO_TYPE=" + c.name) + elif isinstance(c, Misc): + return c.misc + + +def _format_lpf(signame, pin, others, resname): + fmt_c = [_format_constraint(c) for c in ([Pins(pin)] + others)] + r = "" + for pre, suf in fmt_c: + r += pre + "\"" + signame + "\"" + suf + ";\n" + return r + + +def _build_lpf(named_sc, named_pc): + r = "BLOCK RESETPATHS;\n" + r += "BLOCK ASYNCPATHS;\n" + for sig, pins, others, resname in named_sc: + if len(pins) > 1: + for i, p in enumerate(pins): + r += _format_lpf(sig + "[" + str(i) + "]", p, others, resname) + else: + r += _format_lpf(sig, pins[0], others, resname) + if named_pc: + r += "\n" + "\n\n".join(named_pc) + return r + + +def _build_files(device, sources, vincpaths, build_name): + tcl = [] + tcl.append("prj_project new -name \"{}\" -impl \"implementation\" -dev {} -synthesis \"synplify\"".format(build_name, device)) + for path in vincpaths: + tcl.append("prj_impl option {include path} {\"" + path + "\"}") + for filename, language, library in sources: + tcl.append("prj_src add \"" + filename + "\" -work " + library) + tcl.append("prj_run Synthesis -impl implementation -forceOne") + tcl.append("prj_run Translate -impl implementation") + tcl.append("prj_run Map -impl implementation") + tcl.append("prj_run PAR -impl implementation") + tcl.append("prj_run Export -impl implementation -task Bitgen") + tools.write_to_file(build_name + ".tcl", "\n".join(tcl)) + + +def _run_diamond(build_name, source, ver=None): + if sys.platform == "win32" or sys.platform == "cygwin": + build_script_contents = "REM Autogenerated by Migen\n" + build_script_contents = "pnmainc " + build_name + ".tcl\n" + build_script_file = "build_" + build_name + ".bat" + tools.write_to_file(build_script_file, build_script_contents) + r = subprocess.call([build_script_file]) + shutil.copy(os.path.join("implementation", build_name + "_implementation.bit"), build_name + ".bit") + else: + raise NotImplementedError + + if r != 0: + raise OSError("Subprocess failed") + + +class LatticeDiamondToolchain: + def build(self, platform, fragment, build_dir="build", build_name="top", + toolchain_path="/opt/Diamond", run=True): + tools.mkdir_noerror(build_dir) + os.chdir(build_dir) + + if not isinstance(fragment, _Fragment): + fragment = fragment.get_fragment() + platform.finalize(fragment) + + v_output = platform.get_verilog(fragment) + named_sc, named_pc = platform.resolve_signals(v_output.ns) + v_file = build_name + ".v" + v_output.write(v_file) + sources = platform.sources | {(v_file, "verilog", "work")} + _build_files(platform.device, sources, platform.verilog_include_paths, build_name) + + tools.write_to_file(build_name + ".lpf", _build_lpf(named_sc, named_pc)) + + if run: + _run_diamond(build_name, toolchain_path) + + os.chdir("..") + + return v_output.ns + + def add_period_constraint(self, platform, clk, period): + # TODO: handle differential clk + platform.add_platform_command("""FREQUENCY PORT "{clk}" {freq} MHz;""".format(freq=str(float(1/period)*1000), clk="{clk}"), clk=clk) diff --git a/litex/gen/migen/build/lattice/platform.py b/litex/gen/migen/build/lattice/platform.py new file mode 100644 index 00000000..73742231 --- /dev/null +++ b/litex/gen/migen/build/lattice/platform.py @@ -0,0 +1,26 @@ +from migen.build.generic_platform import GenericPlatform +from migen.build.lattice import common, diamond + + +class LatticePlatform(GenericPlatform): + bitstream_ext = ".bit" + + def __init__(self, *args, toolchain="diamond", **kwargs): + GenericPlatform.__init__(self, *args, **kwargs) + if toolchain == "diamond": + self.toolchain = diamond.LatticeDiamondToolchain() + else: + raise ValueError("Unknown toolchain") + + def get_verilog(self, *args, special_overrides=dict(), **kwargs): + so = dict(common.lattice_special_overrides) + so.update(special_overrides) + return GenericPlatform.get_verilog(self, *args, special_overrides=so, **kwargs) + + def build(self, *args, **kwargs): + return self.toolchain.build(self, *args, **kwargs) + + def add_period_constraint(self, clk, period): + if hasattr(clk, "p"): + clk = clk.p + self.toolchain.add_period_constraint(self, clk, period) diff --git a/litex/gen/migen/build/lattice/programmer.py b/litex/gen/migen/build/lattice/programmer.py new file mode 100644 index 00000000..cc3c50bd --- /dev/null +++ b/litex/gen/migen/build/lattice/programmer.py @@ -0,0 +1,54 @@ +import os +import subprocess + +from migen.build.generic_programmer import GenericProgrammer +from migen.build import tools + + +# XXX Lattice programmer need an .xcf file, will need clean up and support for more parameters +_xcf_template = """ + + + + + + JTAG + + + 1 + Lattice + LatticeECP3 + LFE3-35EA + {bitstream_file} + Fast Program + + + + SEQUENTIAL + ENTIRED CHAIN + No Override + TLR + TLR + + + + USB2 + FTUSB-0 + Dual RS232-HS A Location 0000 Serial A + + TRST ABSENT; + ISPEN ABSENT; + + + +""" + + +class LatticeProgrammer(GenericProgrammer): + needs_bitreverse = False + + def load_bitstream(self, bitstream_file): + xcf_file = bitstream_file.replace(".bit", ".xcf") + xcf_content = _xcf_template.format(bitstream_file=bitstream_file) + tools.write_to_file(xcf_file, xcf_content) + subprocess.call(["pgrcmd", "-infile", xcf_file]) diff --git a/litex/gen/migen/build/openocd.py b/litex/gen/migen/build/openocd.py new file mode 100644 index 00000000..aec29980 --- /dev/null +++ b/litex/gen/migen/build/openocd.py @@ -0,0 +1,30 @@ +import subprocess + +from migen.build.generic_programmer import GenericProgrammer + + +class OpenOCD(GenericProgrammer): + needs_bitreverse = False + + def __init__(self, config, flash_proxy_basename=None): + GenericProgrammer.__init__(self, flash_proxy_basename) + self.config = config + + def load_bitstream(self, bitstream): + script = "; ".join([ + "init", + "pld load 0 {}".format(bitstream), + "exit", + ]) + subprocess.call(["openocd", "-f", self.config, "-c", script]) + + def flash(self, address, data): + flash_proxy = self.find_flash_proxy() + script = "; ".join([ + "init", + "jtagspi_init 0 {}".format(flash_proxy), + "jtagspi_program {} 0x{:x}".format(data, address), + "fpga_program", + "exit" + ]) + subprocess.call(["openocd", "-f", self.config, "-c", script]) diff --git a/litex/gen/migen/build/platforms/__init__.py b/litex/gen/migen/build/platforms/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/litex/gen/migen/build/platforms/apf27.py b/litex/gen/migen/build/platforms/apf27.py new file mode 100644 index 00000000..ea182e21 --- /dev/null +++ b/litex/gen/migen/build/platforms/apf27.py @@ -0,0 +1,150 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform + + +_ios = [ + ("clk0", 0, Pins("N9"), IOStandard("LVCMOS18")), + ("fpga_reset", 0, Pins("T9"), IOStandard("LVCMOS18"), Drive("8")), + ("fpga_initb", 0, Pins("T12"), IOStandard("LVCMOS18"), Drive("8")), + ("weim", 0, + Subsignal("cs4_dtack", Pins("R3"), IOStandard("LVCMOS18"), Drive("8")), + Subsignal("cs5n", Pins("P10"), IOStandard("LVCMOS18")), + Subsignal("eb0n", Pins("P9"), IOStandard("LVCMOS18")), + Subsignal("oen", Pins("R9"), IOStandard("LVCMOS18")), + Subsignal("data", + Pins("T5 T6 P7 N8 P12 T13 R13 T14 P5 N6 T3 T11 T4 R5 M10 T10"), + IOStandard("LVCMOS18"), Drive("8")), + Subsignal("addr", + Pins("N5 L7 M7 M8 L8 L9 L10 M11 P11 N11 N12 P13"), + IOStandard("LVCMOS18")) + ) +] + +_connectors = [ + ("J2", + "None", # no 0 pin + "None", # 1 +3v3 + "None", # 2 +3v3 + "None", # 3 GND + "None", # 4 GND + "None", # 5 DP USB_OTG_PHY +3V3 + "None", # 6 DM USB_OTG_PHY +3V3 + "None", # 7 VBUS USB_OTG_ PHY +3V3 + "None", # 8 PSW_N USB_OTG_PHY +3V3 + "None", # 9 ID USB_OTG_PHY +3V3 + "None", # 10 FAULT USB_OTG_PHY +3V3 + "None", # 11 RXP Ethernet_PHY +3V3 + "None", # 12 RXN Ethernet_PHY +3V3 + "None", # 13 ETH_LINK Ethernet_PHY +2V8 + "None", # 14 PC_VS2 PC +2V8 PF13 + "None", # 15 PC_VS1 PC +2V8 PF14 + "None", # 16 PC_PWRON PC +2V8 PF16 + "None", # 17 PC_READY PC +2V8 PF17 + "None", # 18 PWM0 PWM0 +2V8 PE5 + "None", # 19 TOUT GPT +2V8 PC14 + "None", # 20 GND POWER + "None", # 21 VCC01 (IN) BANK1 SUPPLY VCCO1 + "C16", # 22 IO_L24P_1 FPGA_BANK1 VCC01 + "C15", # 23 IO_L24N_1 FPGA_BANK1 VCC01 + "D16", # 24 IO_L22_P1 FPGA_BANK1 VCC01 + "None", # 25 GND POWER + "B14", # 26 IO_L02N_0 FPGA_BANK0 VCCO0 + "B15", # 27 IO_L02P_0 FPGA_BANK0 + "A13", # 28 IO_L04N_0 FPGA_BANK0 + "A14", # 29 IO_L04P_0 FPGA_BANK0 VCCO0 + "D11", # 30 IO_L03N_0 FPGA_BANK0 VCCO0 + "C12", # 31 IO_L03P_0 FPGA_BANK0 VCCO0 + "A10", # 32 IO_L08N_0 FPGA_BANK0 VCCO0 + "B10", # 33 IO_L08P_0 FPGA_BANK0 VCCO0 + "A9", # 34 IO_L10N_0 / GLCK7 FPGA_BANK0 VCCO0 + "C9", # 35 IO_L10P_0 / GCLK6 FPGA_BANK0 VCCO0 + "B8", # 36 IO_L12N_0 / GCLK11 FPGA_BANK0 VCCO0 + "A8", # 37 IO_L12P_0 / GCLK10 FPGA_BANK0 VCCO0 + "B6", # 38 IO_L15N_0 FPGA_BANK0 VCCO0 + "A6", # 39 IO_L15P_0 FPGA_BANK0 VCCO0 + "B4", # 40 IO_L18N_0 FPGA_BANK0 VCCO0 + "A4", # 41 IO_L18P_0 FPGA_BANK0 VCCO0 + "None", # 42 GND POWER + "N3", # 43 IO_L24P_3 FPGA_BANK3 VCCO3 + "R1", # 44 IO_L23P_3 FPGA_BANK3 VCCO3 + "P1", # 45 IO_L22N_3 FPGA_BANK3 VCCO3 + "N1", # 46 IO_L20N_3 FPGA_BANK3 VCCO3 + "M1", # 47 IO_L20P_3 FPGA_BANK3 VCCO3 + "H3", # 48 IO_L12P_3 FPGA_BANK3 VCCO3 + "K1", # 49 IO_L15N_3 FPGA_BANK3 VCCO3 + "J1", # 50 IO_L14N_3 FPGA_BANK3 VCCO3 + "H1", # 51 IO_L11N_3 FPGA_BANK3 VCCO3 + "G1", # 52 IO_L08N_3 FPGA_BANK3 VCCO3 + "F1", # 53 IO_L08P_3 FPGA_BANK3 VCCO3 + "E1", # 54 IO_L03N_3 FPGA_BANK3 VCCO3 + "D1", # 55 IO_LO3P_3 FPGA_BANK3 VCCO3 + "C1", # 56 IO_L01N_3 FPGA_BANK3 VCCO3 + "None", # 57 GND POWER + "None", # 58 TRSTN JTAG +2V8 + "None", # 59 TDI JTAG +2V8 + "None", # 60 TCK JTAG +2V8 + "None", # 61 TDO JTAG +2V8 + "None", # 62 TMS JTAG +2V8 + "None", # 63 GND POWER + "C2", # 64 IO_L01P_3 FPGA_BANK3 VCCO3 + "D3", # 65 IO_L02N_3 FPGA_BANK3 VCCO3 + "D4", # 66 IO_L02P_3 FPGA_BANK3 VCCO3 + "F4", # 67 IP_LO4N_3 FPGA_BANK3 VCCO3 + "G2", # 68 IO_L11P_3 FPGA_BANK3 VCCO3 + "J2", # 69 IO_L14P_3 FPGA_BANK3 VCCO3 + "K3", # 70 IO_L15P_3 FPGA_BANK3 VCCO3 + "J3", # 71 IO_L12N_3 FPGA_BANK3 VCCO3 + "N2", # 72 IO_L22P_3 FPGA_BANK3 VCCO3 + "P2", # 73 IO_L23N_3 FPGA_BANK3 VCCO3 + "M4", # 74 IO_L24N_3 FPGA_BANK3 VCCO3 + "L6", # 75 IP_L25N_3 FPGA_BANK3 VCCO3 + "None", # 76 VCCO3 (IN) BANK3 SUPPLY VCCO3 (3.3Vmax) + "None", # 77 VCCO3 (IN) BANK3 SUPPLY VCCO3 (3.3Vmax) + "A3", # 78 IO_L19P_0 FPGA_BANK0 VCCO0 + "B3", # 79 IO_L19N_0 FPGA_BANK0 VCCO0 + "A5", # 80 IO_L17P_0 FPGA_BANK0 VCCO0 + "C5", # 81 IO_L17N_0 FPGA_BANK0 VCCO0 + "D7", # 82 IO_L16P_0 FPGA_BANK0 VCCO0 + "C6", # 83 IO_L16N_0 FPGA_BANK0 VCCO0 + "C8", # 84 IO_L11P_0 / GCLK8 FPGA_BANK0 VCCO0 + "D8", # 85 IO_L11N_0 / GCLK9 FPGA_BANK0 VCCO0 + "C10", # 86 IO_L09P_0 / GCLK4 FPGA_BANK0 VCCO0 + "D9", # 87 IO_L09N_0 / GCLK5 FPGA_BANK0 VCCO0 + "C11", # 88 IO_L07P_0 FPGA_BANK0 VCCO0 + "A11", # 89 IO_L07N_0 FPGA_BANK0 VCCO0 + "D13", # 90 IO_L01P_0 FPGA_BANK0 VCCO0 + "C13", # 91 IO_L01N_0 FPGA_BANK0 VCCO0 + "None", # 92 VCCO0 (IN) BANK0 SUPPLY VCCO0 (3.3Vmax) + "None", # 93 VCCO0 (IN) BANK0 SUPPLY VCCO0 (3.3Vmax) + "None", # 94 GND POWER VCCO0 A13 + "D15", # 95 IO_L22N_1 FPGA_BANK1 VCC01 + "E13", # 96 IO_L23P_1 FPGA_BANK1 VCC01 + "D14", # 97 IO_L23N_1 FPGA_BANK1 VCC01 + "E14", # 98 IO_L20P_1 FPGA_BANK1 VCC01 + "F13", # 99 IO_L20N_1 FPGA_BANK1 VCC01 + "None", # 100 GND POWER (3.3Vmax) + "None", # 101 USR_RESETN (open CONFIG Pos PC15 +2V8 drain with pullup) + "None", # 102 TIN GPT +2V8 + "None", # 103 EXTAL_26M CONFIG +2V5 + "None", # 104 RX3 RS232_3 RS232 + "None", # 105 TX3 RS232_3 RS232 + "None", # 106 RX1 RS232_1 RS232 + "None", # 107 TX1 RS232_1 RS232 + "None", # 108 BOOT CONFIG +2V8 + "None", # 109 TXN Ethernet_PHY +3V3 + "None", # 110 TXP Ethernet_PHY +3V3 + "None", # 111 ETH_ACTIVITY Ethernet_PHY +2V8 + "None", # 112 USBH2_NXT USB_HOST2 +2V5 PA3 + "None", # 113 USBH2_DIR USB_HOST2 +2V5 PA1 + "None", # 114 USBH2_DATA7 USB_HOST2 +2V5 PA2 + "None", # 115 USBH2_STP USB_HOST2 +2V5 PA4 + "None") # 116 USBH2_CLK USB_HOST2 +2V5 PA0 +] + + +class Platform(XilinxPlatform): + default_clk_name = "clk0" + default_clk_period = 10 + + def __init__(self): + XilinxPlatform.__init__(self, "xc3s200a-ft256-4", _ios, _connectors) diff --git a/litex/gen/migen/build/platforms/apf51.py b/litex/gen/migen/build/platforms/apf51.py new file mode 100644 index 00000000..f776cdac --- /dev/null +++ b/litex/gen/migen/build/platforms/apf51.py @@ -0,0 +1,177 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform + + +_ios = [ + ("clk3", 0, Pins("N8"), IOStandard("LVCMOS33")), + ("clko", 0, Pins("N7"), IOStandard("LVCMOS33")), + ("fpga_initb", 0, Pins("P3"), IOStandard("LVCMOS33")), + ("fpga_program", 0, Pins("R2"), IOStandard("LVCMOS33")), + ("eim", 0, + Subsignal("bclk", Pins("N12")), + Subsignal("eb1", Pins("P13")), + Subsignal("cs1", Pins("R11")), + Subsignal("cs2", Pins("N9")), + Subsignal("lba", Pins("R9")), + Subsignal("eb0", Pins("P7")), + Subsignal("oe", Pins("R7")), + Subsignal("rw", Pins("R6")), + Subsignal("dtack", Pins("N4")), + Subsignal("wait", Pins("R4")), + Subsignal("da", Pins("N6 L5 L6 R5 P5 N11 M11 P11 L8 K8 M8 M10 L9 R10 N5 M5")), + IOStandard("LVCMOS33") + ) +] + +_connectors = [ + ("J2", + "None", # No 0 pin + "None", # 1 FPGA Bank1 power + "None", # 2 FPGA Bank1 power + "None", # 3 GND + "B14", # 4 IO_L1P_A25_1 + "B15", # 5 IO_L1N_A24_VREF_1 + "C14", # 6 IO_L33P_A15_M1A10_1 + "C15", # 7 IO_L33N_A14_M1A4_1 + "D13", # 8 IO_L35P_A11_M1A7_1 + "D15", # 9 IO_L35N_A10_M1A2_1 + "E14", # 10 IO_L37P_A7_M1A0_1 + "E15", # 11 IO_L37N_A6_M1A1_1 + "None", # 12 GND + "F13", # 13 IO_L39P_M1A3_1 + "F15", # 14 IO_L39N_M1ODT_1 + "G14", # 15 IO_L41P_GCLK9_IRDY1_M1RASN_1 + "G15", # 16 IO_L41N_GCLK8_M1CASN_1 + "H13", # 17 IO_L42P_GCLK7_M1UDM_1 + "H15", # 18 IO_L42N_GCLK6_TRDY1_M1LDM + "J14", # 19 IO_L43P_GCLK5_M1DQ4_1 + "J15", # 20 IO_L43N_GCLK4_M1DQ5_1 + "K13", # 21 IO_L44P_A3_M1DQ6_1 + "K15", # 22 IO_L44N_A2_M1DQ7_1 + "L14", # 23 IO_L45P_A1_M1LDQS_1 + "L15", # 24 IO_L45N_A0_M1LDQSN_1 + "None", # 25 GND + "E2", # 26 IO_L52P_M3A8_3 + "E1", # 27 IO_L52N_M3A9_3 + "D3", # 28 IO_L54P_M3RESET_3 + "D1", # 29 IO_L54N_M3A11_3 + "F3", # 30 IO_L46P_M3CLK_3 + "F1", # 31 IO_L46N_M3CLKN_3 + "G2", # 32 IO_L44P_GCLK21_M3A5_3 + "G1", # 33 IO_L44N_GCLK20_M3A6_3 + "H3", # 34 IO_L42P_GCLK25_TRDY2_M3UDM_3 + "H1", # 35 IO_L42N_GCLK24_M3LDM_3 + "K3", # 36 IO_L40P_M3DQ6_3 + "K1", # 37 IO_L40N_M3DQ7_3 + "None", # 38 GND + "None", # 39 GPIO4_16 + "None", # 40 GPIO4_17 + "None", # 41 BOOT_MODE0 + "None", # 42 AUD5_RXFS + "None", # 43 AUD5_RXC + "None", # 44 GND + "None", # 45 AUD5_RXD + "None", # 46 AUD5_TXC + "None", # 47 AUD5_TXFS + "None", # 48 GND + "None", # 49 SPI2_SCLK_GPT_CMPOUT3 + "None", # 50 SPI2_MISO + "None", # 51 SPI2_MOSI + "None", # 52 SPI2_SS1 + "None", # 53 SPI2_SS2 + "None", # 54 SPI2_SS3 + "None", # 55 SPI2_RDY + "None", # 56 OWIRE + "None", # 57 GND + "None", # 58 SPI1_SCLK + "None", # 59 SPI1_MISO + "None", # 60 SPI1_MOSI + "None", # 61 SPI1_SS0 + "None", # 62 SPI1_SS1 + "None", # 63 SPI1_RDY + "None", # 64 RESET# + "None", # 65 VIO_H2 + "None", # 66 PMIC_GPIO6 + "None", # 67 TOUCH_X+ + "None", # 68 TOUCH_X- + "None", # 69 TOUCH_Y+ + "None", # 70 TOUCH_Y- + "None", # 71 AUXADCIN4 + "None", # 72 AUXADCIN3 + "None", # 73 AUXADCIN2 + "None", # 74 AUXADCIN1 + "None", # 75 PMIC_GPIO7 + "None", # 76 +1v8 + "None", # 77 RESERVED + "None", # 78 UART3_TXD + "None", # 79 UART_3_RXD + "None", # 80 UART2_TXD + "None", # 81 UART2_RXD + "None", # 82 UART2_RTS_KEY_COL7 + "None", # 83 UART2_CTS_KEY_COL6 + "None", # 84 UART1_TXD + "None", # 85 UART1_RXD + "None", # 86 UART1_RTS + "None", # 87 UART1_CTS + "None", # 88 GND + "None", # 89 AUD3_TXD + "None", # 90 AUD3_RXD + "None", # 91 AUD3_FS + "None", # 92 AUD3_CK + "None", # 93 GND + "None", # 94 AUD6_TXFS_KEY_ROW7 + "None", # 95 AUD6_TXC_KEY_ROW6 + "None", # 96 AUD6_RXD_KEY_ROW5 + "None", # 97 AUD6_TXD_KEY_ROW4 + "None", # 98 I2C2_SDA_UART3_CTS + "None", # 99 I2C2_SCL_UART3_RTS + "None", # 100 BOOT_MODE1 + "None", # 101 PWM2 + "None", # 102 PWM1 + "None", # 103 GND + "L1", # 104 IO_L39N_M3LDQSN_3 + "L2", # 105 IO_L39P_M3LDQS_3 + "J1", # 106 IO_L41N_GCLK26_M3DQ5_3 + "J2", # 107 IO_L41P_GCLK27_M3DQ4_3 + "J3", # 108 IO_L43N_GCLK22_IRDY2_M3CASN_3 + "K4", # 109 IO_L43P_GCLK23_M3RASN_3 + "J4", # 110 IO_L45N_M3ODT_3 + "K5", # 111 IO_L45P_M3A3_3 + "C1", # 112 IO_L83N_VREF_3 + "C2", # 113 IO_L83P_3 + "E3", # 114 IO_L53N_M3A12_3 + "D4", # 115 IO_L53P_M3CKE_3 + "None", # 116 GND + "P15", # 117 IO_L74N_DOUT_BUSY_1 + "P14", # 118 IO_L74P_AWAKE_1 + "N15", # 119 IO_L47N_LDC_M1DQ1_1 + "N14", # 120 IO_L47P_FWE_B_M1DQ0_1 + "M15", # 121 IO_L46N_FOE_B_M1DQ3_1 + "M13", # 122 IO_L46P_FCS_B_M1DQS2_1 + "L12", # 123 IO_L40N_GCLK10_M1A6_1 + "K12", # 124 IO_L40P_GCLK11_M1A5_1 + "K11", # 125 IO_L38N_A4_M1CLKN_1 + "K10", # 126 IO_L38P_A5_M1CLK_1 + "J13", # 127 IO_L36N_A8_M1BA1_1 + "J11", # 128 IO_L36P_A9_M1BA0_1 + "None", # 129 GND + "G13", # 130 IO_L34N_A12_M1BA2_1_NOTLX4 + "H12", # 131 IO_L34P_A13_M1WE_1_NOTLX4 + "H11", # 132 IO_L32N_A16_M1A9_1_NOTLX4 + "H10", # 133 IO_L32P_A17_M1A8_1_NOTLX4 + "F12", # 134 IO_L31N_A18_M1A12_1_NOTLX4 + "F11", # 135 IO_L31P_A19_M1CKE_1_NOTLX4 + "G12", # 136 IO_L30N_A20_M1A11_1_NOTLX4 + "G11", # 137 IO_L30P_A21_M1RESET_1_NOTLX4 + "None", # 138 GND + "None", # 139 FPGA_BANK3_POWER + "None") # 140 FPGA_BANK3_POWER +] + + +class Platform(XilinxPlatform): + default_clk_name = "clk3" + default_clk_period = 10.526 + + def __init__(self): + XilinxPlatform.__init__(self, "xc6slx9-2csg225", _ios, _connectors) diff --git a/litex/gen/migen/build/platforms/de0nano.py b/litex/gen/migen/build/platforms/de0nano.py new file mode 100644 index 00000000..4ef34daf --- /dev/null +++ b/litex/gen/migen/build/platforms/de0nano.py @@ -0,0 +1,103 @@ +# This file is Copyright (c) 2013 Florent Kermarrec +# License: BSD + +from migen.build.generic_platform import * +from migen.build.altera import AlteraPlatform +from migen.build.altera.programmer import USBBlaster + + +_io = [ + ("clk50", 0, Pins("R8"), IOStandard("3.3-V LVTTL")), + + ("user_led", 0, Pins("A15"), IOStandard("3.3-V LVTTL")), + ("user_led", 1, Pins("A13"), IOStandard("3.3-V LVTTL")), + ("user_led", 2, Pins("B13"), IOStandard("3.3-V LVTTL")), + ("user_led", 3, Pins("A11"), IOStandard("3.3-V LVTTL")), + ("user_led", 4, Pins("D1"), IOStandard("3.3-V LVTTL")), + ("user_led", 5, Pins("F3"), IOStandard("3.3-V LVTTL")), + ("user_led", 6, Pins("B1"), IOStandard("3.3-V LVTTL")), + ("user_led", 7, Pins("L3"), IOStandard("3.3-V LVTTL")), + + ("key", 0, Pins("J15"), IOStandard("3.3-V LVTTL")), + ("key", 1, Pins("E1"), IOStandard("3.3-V LVTTL")), + + ("sw", 0, Pins("M1"), IOStandard("3.3-V LVTTL")), + ("sw", 1, Pins("T9"), IOStandard("3.3-V LVTTL")), + ("sw", 2, Pins("B9"), IOStandard("3.3-V LVTTL")), + ("sw", 3, Pins("M15"), IOStandard("3.3-V LVTTL")), + + ("serial", 0, + Subsignal("tx", Pins("D3"), IOStandard("3.3-V LVTTL")), + Subsignal("rx", Pins("C3"), IOStandard("3.3-V LVTTL")) + ), + + ("sdram_clock", 0, Pins("R4"), IOStandard("3.3-V LVTTL")), + ("sdram", 0, + Subsignal("a", Pins("P2 N5 N6 M8 P8 T7 N8 T6 R1 P1 N2 N1 L4")), + Subsignal("ba", Pins("M7 M6")), + Subsignal("cs_n", Pins("P6")), + Subsignal("cke", Pins("L7")), + Subsignal("ras_n", Pins("L2")), + Subsignal("cas_n", Pins("L1")), + Subsignal("we_n", Pins("C2")), + Subsignal("dq", Pins("G2 G1 L8 K5 K2 J2 J1 R7 T4 T2 T3 R3 R5 P3 N3 K1")), + Subsignal("dm", Pins("R6 T5")), + IOStandard("3.3-V LVTTL") + ), + + ("epcs", 0, + Subsignal("data0", Pins("H2")), + Subsignal("dclk", Pins("H1")), + Subsignal("ncs0", Pins("D2")), + Subsignal("asd0", Pins("C1")), + IOStandard("3.3-V LVTTL") + ), + + ("i2c", 0, + Subsignal("sclk", Pins("F2")), + Subsignal("sdat", Pins("F1")), + IOStandard("3.3-V LVTTL") + ), + + ("g_sensor", 0, + Subsignal("cs_n", Pins("G5")), + Subsignal("int", Pins("M2")), + IOStandard("3.3-V LVTTL") + ), + + ("adc", 0, + Subsignal("cs_n", Pins("A10")), + Subsignal("saddr", Pins("B10")), + Subsignal("sclk", Pins("B14")), + Subsignal("sdat", Pins("A9")), + IOStandard("3.3-V LVTTL") + ), + + ("gpio_0", 0, + Pins("D3 C3 A2 A3 B3 B4 A4 B5 A5 D5 B6 A6 B7 D6 A7 C6", + "C8 E6 E7 D8 E8 F8 F9 E9 C9 D9 E11 E10 C11 B11 A12 D11", + "D12 B12"), + IOStandard("3.3-V LVTTL") + ), + ("gpio_1", 0, + Pins("F13 T15 T14 T13 R13 T12 R12 T11 T10 R11 P11 R10 N12 P9 N9 N11", + "L16 K16 R16 L15 P15 P16 R14 N16 N15 P14 L14 N14 M10 L13 J16 K15", + "J13 J14"), + IOStandard("3.3-V LVTTL") + ), + ("gpio_2", 0, + Pins("A14 B16 C14 C16 C15 D16 D15 D14 F15 F16 F14 G16 G15"), + IOStandard("3.3-V LVTTL") + ), +] + + +class Platform(AlteraPlatform): + default_clk_name = "clk50" + default_clk_period = 20 + + def __init__(self): + AlteraPlatform.__init__(self, "EP4CE22F17C6", _io) + + def create_programmer(self): + return USBBlaster() diff --git a/litex/gen/migen/build/platforms/kc705.py b/litex/gen/migen/build/platforms/kc705.py new file mode 100644 index 00000000..eb51ab33 --- /dev/null +++ b/litex/gen/migen/build/platforms/kc705.py @@ -0,0 +1,458 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform, XC3SProg, VivadoProgrammer, iMPACT +from migen.build.xilinx.ise import XilinxISEToolchain + + +_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")), + + ("user_btn_c", 0, Pins("G12"), IOStandard("LVCMOS25")), + ("user_btn_n", 0, Pins("AA12"), IOStandard("LVCMOS15")), + ("user_btn_s", 0, Pins("AB12"), IOStandard("LVCMOS15")), + ("user_btn_w", 0, Pins("AC6"), IOStandard("LVCMOS15")), + ("user_btn_e", 0, Pins("AG5"), IOStandard("LVCMOS15")), + + ("user_dip_btn", 0, Pins("Y29"), IOStandard("LVCMOS25")), + ("user_dip_btn", 1, Pins("W29"), IOStandard("LVCMOS25")), + ("user_dip_btn", 2, Pins("AA28"), IOStandard("LVCMOS25")), + ("user_dip_btn", 3, Pins("Y28"), IOStandard("LVCMOS25")), + + ("user_sma_clock", 0, + Subsignal("p", Pins("L25"), IOStandard("LVDS_25")), + Subsignal("n", Pins("K25"), IOStandard("LVDS_25")) + ), + ("user_sma_clock_p", 0, Pins("L25"), IOStandard("LVCMOS25")), + ("user_sma_clock_n", 0, Pins("K25"), IOStandard("LVCMOS25")), + + ("user_sma_gpio_p", 0, Pins("Y23"), IOStandard("LVCMOS33")), + ("user_sma_gpio_n", 0, Pins("Y24"), IOStandard("LVCMOS33")), + + ("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")) + ), + + ("i2c", 0, + Subsignal("scl", Pins("K21")), + Subsignal("sda", Pins("L21")), + IOStandard("LVCMOS25")), + + ("serial", 0, + Subsignal("cts", Pins("L27")), + Subsignal("rts", Pins("K23")), + Subsignal("tx", Pins("K24")), + Subsignal("rx", Pins("M19")), + IOStandard("LVCMOS25")), + + ("spiflash", 0, # clock needs to be accessed through STARTUPE2 + Subsignal("cs_n", Pins("U19")), + Subsignal("dq", Pins("P24", "R25", "R20", "R21")), + IOStandard("LVCMOS25") + ), + + ("mmc", 0, + Subsignal("wp", Pins("Y21")), + Subsignal("det", Pins("AA21")), + Subsignal("cmd", Pins("AB22")), + Subsignal("clk", Pins("AB23")), + Subsignal("dat", Pins("AC20 AA23 AA22 AC21")), + IOStandard("LVCMOS25")), + + ("lcd", 0, + Subsignal("db", Pins("AA13 AA10 AA11 Y10")), + Subsignal("e", Pins("AB10")), + Subsignal("rs", Pins("Y11")), + Subsignal("rw", Pins("AB13")), + IOStandard("LVCMOS15")), + + ("rotary", 0, + Subsignal("a", Pins("Y26")), + Subsignal("b", Pins("Y25")), + Subsignal("push", Pins("AA26")), + IOStandard("LVCMOS25")), + + ("hdmi", 0, + Subsignal("d", Pins("B23 A23 E23 D23 F25 E25 E24 D24 F26 E26 G23 G24 J19 H19 L17 L18 K19 K20")), + Subsignal("de", Pins("H17")), + Subsignal("clk", Pins("K18")), + Subsignal("vsync", Pins("H20")), + Subsignal("hsync", Pins("J18")), + Subsignal("int", Pins("AH24")), + Subsignal("spdif", Pins("J17")), + Subsignal("spdif_out", Pins("G20")), + IOStandard("LVCMOS25")), + + ("ddram", 0, + Subsignal("a", Pins( + "AH12 AG13 AG12 AF12 AJ12 AJ13 AJ14 AH14", + "AK13 AK14 AF13 AE13 AJ11 AH11 AK10 AK11"), + IOStandard("SSTL15")), + Subsignal("ba", Pins("AH9 AG9 AK9"), IOStandard("SSTL15")), + Subsignal("ras_n", Pins("AD9"), IOStandard("SSTL15")), + Subsignal("cas_n", Pins("AC11"), IOStandard("SSTL15")), + Subsignal("we_n", Pins("AE9"), IOStandard("SSTL15")), + Subsignal("cs_n", Pins("AC12"), IOStandard("SSTL15")), + Subsignal("dm", Pins("Y16 AB17 AF17 AE16 AK5 AJ3 AF6 AC7"), + IOStandard("SSTL15")), + Subsignal("dq", Pins( + "AA15 AA16 AC14 AD14 AA17 AB15 AE15 Y15", + "AB19 AD16 AC19 AD17 AA18 AB18 AE18 AD18", + "AG19 AK19 AG18 AF18 AH19 AJ19 AE19 AD19", + "AK16 AJ17 AG15 AF15 AH17 AG14 AH15 AK15", + "AK8 AK6 AG7 AF7 AF8 AK4 AJ8 AJ6", + "AH5 AH6 AJ2 AH2 AH4 AJ4 AK1 AJ1", + "AF1 AF2 AE4 AE3 AF3 AF5 AE1 AE5", + "AC1 AD3 AC4 AC5 AE6 AD6 AC2 AD4"), + IOStandard("SSTL15_T_DCI")), + Subsignal("dqs_p", Pins("AC16 Y19 AJ18 AH16 AH7 AG2 AG4 AD2"), + IOStandard("DIFF_SSTL15")), + Subsignal("dqs_n", Pins("AC15 Y18 AK18 AJ16 AJ7 AH1 AG3 AD1"), + IOStandard("DIFF_SSTL15")), + Subsignal("clk_p", Pins("AG10"), IOStandard("DIFF_SSTL15")), + Subsignal("clk_n", Pins("AH10"), IOStandard("DIFF_SSTL15")), + Subsignal("cke", Pins("AF10"), IOStandard("SSTL15")), + Subsignal("odt", Pins("AD8"), IOStandard("SSTL15")), + Subsignal("reset_n", Pins("AK3"), IOStandard("LVCMOS15")), + Misc("SLEW=FAST"), + Misc("VCCAUX_IO=HIGH") + ), + + ("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") + ), + + ("pcie_x1", 0, + Subsignal("rst_n", Pins("G25"), IOStandard("LVCMOS25")), + Subsignal("clk_p", Pins("U8")), + Subsignal("clk_n", Pins("U7")), + Subsignal("rx_p", Pins("M6")), + Subsignal("rx_n", Pins("M5")), + Subsignal("tx_p", Pins("L4")), + Subsignal("tx_n", Pins("L3")) + ), + ("pcie_x2", 0, + Subsignal("rst_n", Pins("G25"), IOStandard("LVCMOS25")), + Subsignal("clk_p", Pins("U8")), + Subsignal("clk_n", Pins("U7")), + Subsignal("rx_p", Pins("M6 P6")), + Subsignal("rx_n", Pins("M5 P5")), + Subsignal("tx_p", Pins("L4 M2")), + Subsignal("tx_n", Pins("L3 M1")) + ), + ("pcie_x4", 0, + Subsignal("rst_n", Pins("G25"), IOStandard("LVCMOS25")), + Subsignal("clk_p", Pins("U8")), + Subsignal("clk_n", Pins("U7")), + Subsignal("rx_p", Pins("M6 P6 R4 T6")), + Subsignal("rx_n", Pins("M5 P5 R3 T5")), + Subsignal("tx_p", Pins("L4 M2 N4 P2")), + Subsignal("tx_n", Pins("L3 M1 N3 P1")) + ), + ("pcie_x8", 0, + Subsignal("rst_n", Pins("G25"), IOStandard("LVCMOS25")), + Subsignal("clk_p", Pins("U8")), + Subsignal("clk_n", Pins("U7")), + Subsignal("rx_p", Pins("M6 P6 R4 T6 V6 W4 Y6 AA4")), + Subsignal("rx_n", Pins("M5 P5 R3 T5 V5 W3 Y5 AA3")), + Subsignal("tx_p", Pins("L4 M2 N4 P2 T2 U4 V2 Y2")), + Subsignal("tx_n", Pins("L3 M1 N3 P1 T1 U3 V1 Y1")) + ) +] + +_connectors = [ + ("HPC", { + "DP1_M2C_P": "D6", + "DP1_M2C_N": "D5", + "DP2_M2C_P": "B6", + "DP2_M2C_N": "B5", + "DP3_M2C_P": "A8", + "DP3_M2C_N": "A7", + "DP1_C2M_P": "C4", + "DP1_C2M_N": "C3", + "DP2_C2M_P": "B2", + "DP2_C2M_N": "B1", + "DP3_C2M_P": "A4", + "DP3_C2M_N": "A3", + "DP0_C2M_P": "D2", + "DP0_C2M_N": "D1", + "DP0_M2C_P": "E4", + "DP0_M2C_N": "E3", + "LA06_P": "H30", + "LA06_N": "G30", + "LA10_P": "D29", + "LA10_N": "C30", + "LA14_P": "B28", + "LA14_N": "A28", + "LA18_CC_P": "F21", + "LA18_CC_N": "E21", + "LA27_P": "C19", + "LA27_N": "B19", + "HA01_CC_P": "H14", + "HA01_CC_N": "G14", + "HA05_P": "F15", + "HA05_N": "E16", + "HA09_P": "F12", + "HA09_N": "E13", + "HA13_P": "L16", + "HA13_N": "K16", + "HA16_P": "L15", + "HA16_N": "K15", + "HA20_P": "K13", + "HA20_N": "J13", + "CLK1_M2C_P": "D17", + "CLK1_M2C_N": "D18", + "LA00_CC_P": "C25", + "LA00_CC_N": "B25", + "LA03_P": "H26", + "LA03_N": "H27", + "LA08_P": "E29", + "LA08_N": "E30", + "LA12_P": "C29", + "LA12_N": "B29", + "LA16_P": "B27", + "LA16_N": "A27", + "LA20_P": "E19", + "LA20_N": "D19", + "LA22_P": "C20", + "LA22_N": "B20", + "LA25_P": "G17", + "LA25_N": "F17", + "LA29_P": "C17", + "LA29_N": "B17", + "LA31_P": "G22", + "LA31_N": "F22", + "LA33_P": "H21", + "LA33_N": "H22", + "HA03_P": "C12", + "HA03_N": "B12", + "HA07_P": "B14", + "HA07_N": "A15", + "HA11_P": "B13", + "HA11_N": "A13", + "HA14_P": "J16", + "HA14_N": "H16", + "HA18_P": "K14", + "HA18_N": "J14", + "HA22_P": "L11", + "HA22_N": "K11", + "GBTCLK1_M2C_P": "E8", + "GBTCLK1_M2C_N": "E7", + "GBTCLK0_M2C_P": "C8", + "GBTCLK0_M2C_N": "C7", + "LA01_CC_P": "D26", + "LA01_CC_N": "C26", + "LA05_P": "G29", + "LA05_N": "F30", + "LA09_P": "B30", + "LA09_N": "A30", + "LA13_P": "A25", + "LA13_N": "A26", + "LA17_CC_P": "F20", + "LA17_CC_N": "E20", + "LA23_P": "B22", + "LA23_N": "A22", + "LA26_P": "B18", + "LA26_N": "A18", + "PG_M2C": "J29", + "HA00_CC_P": "D12", + "HA00_CC_N": "D13", + "HA04_P": "F11", + "HA04_N": "E11", + "HA08_P": "E14", + "HA08_N": "E15", + "HA12_P": "C15", + "HA12_N": "B15", + "HA15_P": "H15", + "HA15_N": "G15", + "HA19_P": "H11", + "HA19_N": "H12", + "PRSNT_M2C_B": "M20", + "CLK0_M2C_P": "D27", + "CLK0_M2C_N": "C27", + "LA02_P": "H24", + "LA02_N": "H25", + "LA04_P": "G28", + "LA04_N": "F28", + "LA07_P": "E28", + "LA07_N": "D28", + "LA11_P": "G27", + "LA11_N": "F27", + "LA15_P": "C24", + "LA15_N": "B24", + "LA19_P": "G18", + "LA19_N": "F18", + "LA21_P": "A20", + "LA21_N": "A21", + "LA24_P": "A16", + "LA24_N": "A17", + "LA28_P": "D16", + "LA28_N": "C16", + "LA30_P": "D22", + "LA30_N": "C22", + "LA32_P": "D21", + "LA32_N": "C21", + "HA02_P": "D11", + "HA02_N": "C11", + "HA06_P": "D14", + "HA06_N": "C14", + "HA10_P": "A11", + "HA10_N": "A12", + "HA17_CC_P": "G13", + "HA17_CC_N": "F13", + "HA21_P": "J11", + "HA21_N": "J12", + "HA23_P": "L12", + "HA23_N": "L13", + } + ), + ("LPC", { + "GBTCLK0_M2C_P": "N8", + "GBTCLK0_M2C_N": "N7", + "LA01_CC_P": "AE23", + "LA01_CC_N": "AF23", + "LA05_P": "AG22", + "LA05_N": "AH22", + "LA09_P": "AK23", + "LA09_N": "AK24", + "LA13_P": "AB24", + "LA13_N": "AC25", + "LA17_CC_P": "AB27", + "LA17_CC_N": "AC27", + "LA23_P": "AH26", + "LA23_N": "AH27", + "LA26_P": "AK29", + "LA26_N": "AK30", + "CLK0_M2C_P": "AF22", + "CLK0_M2C_N": "AG23", + "LA02_P": "AF20", + "LA02_N": "AF21", + "LA04_P": "AH21", + "LA04_N": "AJ21", + "LA07_P": "AG25", + "LA07_N": "AH25", + "LA11_P": "AE25", + "LA11_N": "AF25", + "LA15_P": "AC24", + "LA15_N": "AD24", + "LA19_P": "AJ26", + "LA19_N": "AK26", + "LA21_P": "AG27", + "LA21_N": "AG28", + "LA24_P": "AG30", + "LA24_N": "AH30", + "LA28_P": "AE30", + "LA28_N": "AF30", + "LA30_P": "AB29", + "LA30_N": "AB30", + "LA32_P": "Y30", + "LA32_N": "AA30", + "LA06_P": "AK20", + "LA06_N": "AK21", + "LA10_P": "AJ24", + "LA10_N": "AK25", + "LA14_P": "AD21", + "LA14_N": "AE21", + "LA18_CC_P": "AD27", + "LA18_CC_N": "AD28", + "LA27_P": "AJ28", + "LA27_N": "AJ29", + "CLK1_M2C_P": "AG29", + "CLK1_M2C_N": "AH29", + "LA00_CC_P": "AD23", + "LA00_CC_N": "AE24", + "LA03_P": "AG20", + "LA03_N": "AH20", + "LA08_P": "AJ22", + "LA08_N": "AJ23", + "LA12_P": "AA20", + "LA12_N": "AB20", + "LA16_P": "AC22", + "LA16_N": "AD22", + "LA20_P": "AF26", + "LA20_N": "AF27", + "LA22_P": "AJ27", + "LA22_N": "AK28", + "LA25_P": "AC26", + "LA25_N": "AD26", + "LA29_P": "AE28", + "LA29_N": "AF28", + "LA31_P": "AD29", + "LA31_N": "AE29", + "LA33_P": "AC29", + "LA33_N": "AC30", + } + ) +] + + +class Platform(XilinxPlatform): + identifier = 0x4B37 + default_clk_name = "clk156" + default_clk_period = 6.4 + + def __init__(self, toolchain="vivado", programmer="xc3sprog"): + XilinxPlatform.__init__(self, "xc7k325t-ffg900-2", _io, _connectors, + toolchain=toolchain) + if toolchain == "ise": + self.toolchain.bitgen_opt = "-g LCK_cycle:6 -g Binary:Yes -w -g ConfigRate:12 -g SPI_buswidth:4" + elif toolchain == "vivado": + self.toolchain.bitstream_commands = ["set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]"] + self.toolchain.additional_commands = ["write_cfgmem -force -format bin -interface spix4 -size 16 -loadbit \"up 0x0 {build_name}.bit\" -file {build_name}.bin"] + self.programmer = programmer + + def create_programmer(self): + if self.programmer == "xc3sprog": + return XC3SProg("jtaghs1_fast", "bscan_spi_kc705.bit") + elif self.programmer == "vivado": + return VivadoProgrammer() + elif self.programmer == "impact": + return iMPACT() + else: + raise ValueError("{} programmer is not supported".format(programmer)) + + def do_finalize(self, fragment): + XilinxPlatform.do_finalize(self, fragment) + 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 + if isinstance(self.toolchain, XilinxISEToolchain): + self.add_platform_command("CONFIG DCI_CASCADE = \"33 32 34\";") + else: + self.add_platform_command("set_property DCI_CASCADE {{32 34}} [get_iobanks 33]") diff --git a/litex/gen/migen/build/platforms/lx9_microboard.py b/litex/gen/migen/build/platforms/lx9_microboard.py new file mode 100644 index 00000000..fb13b1cb --- /dev/null +++ b/litex/gen/migen/build/platforms/lx9_microboard.py @@ -0,0 +1,131 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform + + +_io = [ + ("user_btn", 0, Pins("V4"), IOStandard("LVCMOS33"), + Misc("PULLDOWN"), Misc("TIG")), + + ("user_led", 0, Pins("P4"), Misc("SLEW=QUIETIO"), IOStandard("LVCMOS18")), + ("user_led", 1, Pins("L6"), Misc("SLEW=QUIETIO"), IOStandard("LVCMOS18")), + ("user_led", 2, Pins("F5"), Misc("SLEW=QUIETIO"), IOStandard("LVCMOS18")), + ("user_led", 3, Pins("C2"), Misc("SLEW=QUIETIO"), IOStandard("LVCMOS18")), + + ("user_dip", 0, Pins("B3"), Misc("PULLDOWN"), IOStandard("LVCMOS33")), + ("user_dip", 1, Pins("A3"), Misc("PULLDOWN"), IOStandard("LVCMOS33")), + ("user_dip", 2, Pins("B4"), Misc("PULLDOWN"), IOStandard("LVCMOS33")), + ("user_dip", 3, Pins("A4"), Misc("PULLDOWN"), IOStandard("LVCMOS33")), + + # TI CDCE913 programmable triple-output PLL + ("clk_y1", 0, Pins("V10"), IOStandard("LVCMOS33")), # default: 40 MHz + ("clk_y2", 0, Pins("K15"), IOStandard("LVCMOS33")), # default: 66 2/3 MHz + ("clk_y3", 0, Pins("C10"), IOStandard("LVCMOS33")), # default: 100 MHz + + # Maxim DS1088LU oscillator, not populated + ("clk_backup", 0, Pins("R8"), IOStandard("LVCMOS33")), + + # TI CDCE913 PLL I2C control + ("pll", 0, + Subsignal("scl", Pins("P12")), + Subsignal("sda", Pins("U13")), + Misc("PULLUP"), + IOStandard("LVCMOS33")), + + # Micron N25Q128 SPI Flash + ("spiflash", 0, + Subsignal("clk", Pins("R15")), + Subsignal("cs_n", Pins("V3")), + Subsignal("dq", Pins("T13 R13 T14 V14")), + IOStandard("LVCMOS33")), + + # PMOD extension connectors + ("pmod", 0, + Subsignal("d", Pins("F15 F16 C17 C18 F14 G14 D17 D18")), + IOStandard("LVCMOS33")), + ("pmod", 1, + Subsignal("d", Pins("H12 G13 E16 E18 K12 K13 F17 F18")), + IOStandard("LVCMOS33")), + + ("pmod_diff", 0, + Subsignal("io", Pins("F15 C17 F14 D17 H12 E16 K12 F17")), + Subsignal("iob", Pins("F16 C18 G14 D18 G13 E18 K13 F18")), + IOStandard("LVCMOS33")), + + ("serial", 0, + Subsignal("tx", Pins("T7"), Misc("SLEW=SLOW")), + Subsignal("rx", Pins("R7"), Misc("PULLUP")), + IOStandard("LVCMOS33")), + + ("ddram_clock", 0, + Subsignal("p", Pins("G3")), + Subsignal("n", Pins("G1")), + IOStandard("MOBILE_DDR")), # actually DIFF_ + + # Micron MT46H32M16LFBF-5 LPDDR + ("ddram", 0, + Subsignal("a", Pins("J7 J6 H5 L7 F3 H4 H3 H6 " + "D2 D1 F4 D3 G6")), + Subsignal("ba", Pins("F2 F1")), + Subsignal("dq", Pins("L2 L1 K2 K1 H2 H1 J3 J1 " + "M3 M1 N2 N1 T2 T1 U2 U1")), + Subsignal("cke", Pins("H7")), + Subsignal("we_n", Pins("E3")), + Subsignal("cs_n", Pins("K6")), # NC! + Subsignal("cas_n", Pins("K5")), + Subsignal("ras_n", Pins("L5")), + Subsignal("dm", Pins("K3", "K4")), + Subsignal("dqs", Pins("L4", "P2")), + Subsignal("rzq", Pins("N4")), + IOStandard("MOBILE_DDR")), + + # Nat Semi DP83848J 10/100 Ethernet PHY + # pull-ups on col and rx_data set phy addr to 11111b + # and prevent isolate mode (addr 00000b) + ("eth_clocks", 0, + Subsignal("rx", Pins("L15")), + Subsignal("tx", Pins("H17")), + IOStandard("LVCMOS33")), + + ("eth", 0, + Subsignal("col", Pins("M18"), Misc("PULLUP")), + Subsignal("crs", Pins("N17"), Misc("PULLDOWN")), + Subsignal("mdc", Pins("M16"), Misc("PULLDOWN")), + Subsignal("mdio", Pins("L18"), Misc("PULLUP")), # 1k5 ext PULLUP + Subsignal("rst_n", Pins("T18"), Misc("TIG")), + Subsignal("rx_data", Pins("T17 N16 N15 P18"), Misc("PULLUP")), + Subsignal("dv", Pins("P17"), Misc("PULLDOWN")), # MII + Subsignal("rx_er", Pins("N18"), Misc("PULLUP")), # auto MDIX + Subsignal("tx_data", Pins("K18 K17 J18 J16")), + Subsignal("tx_en", Pins("L17")), + Subsignal("tx_er", Pins("L16")), # NC! + IOStandard("LVCMOS33")), + ] + + +class Platform(XilinxPlatform): + default_clk_name = "clk_y3" + default_clk_period = 10 + + def __init__(self): + XilinxPlatform.__init__(self, "xc6slx9-2csg324", _io) + self.add_platform_command(""" +CONFIG VCCAUX = "3.3"; +""") + self.toolchain.bitgen_opt = "-g LCK_cycle:6 -g Binary:Yes -w -g SPI_buswidth:4" + self.toolchain.ise_commands = """ +promgen -w -spi -c FF -p mcs -o {build_name}.mcs -u 0 {build_name}.bit +""" + + def do_finalize(self, fragment): + XilinxPlatform.do_finalize(self, fragment) + + try: + eth_clocks = self.lookup_request("eth_clocks") + self.add_period_constraint(eth_clocks.rx, 40) + self.add_period_constraint(eth_clocks.tx, 40) + self.add_platform_command(""" +TIMESPEC "TS{phy_tx_clk}_io" = FROM "GRP{phy_tx_clk}" TO "PADS" 10 ns; +TIMESPEC "TS{phy_rx_clk}_io" = FROM "PADS" TO "GRP{phy_rx_clk}" 10 ns; +""", phy_rx_clk=eth_clocks.rx, phy_tx_clk=eth_clocks.tx) + except ConstraintError: + pass diff --git a/litex/gen/migen/build/platforms/m1.py b/litex/gen/migen/build/platforms/m1.py new file mode 100644 index 00000000..65216c88 --- /dev/null +++ b/litex/gen/migen/build/platforms/m1.py @@ -0,0 +1,152 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform +from migen.build.xilinx.programmer import UrJTAG + + +_io = [ + ("user_led", 0, Pins("B16"), IOStandard("LVCMOS33"), Drive(24), Misc("SLEW=QUIETIO")), + ("user_led", 1, Pins("A16"), IOStandard("LVCMOS33"), Drive(24), Misc("SLEW=QUIETIO")), + + ("user_btn", 0, Pins("AB4"), IOStandard("LVCMOS33")), + ("user_btn", 1, Pins("AA4"), IOStandard("LVCMOS33")), + ("user_btn", 2, Pins("AB5"), IOStandard("LVCMOS33")), + + ("clk50", 0, Pins("AB11"), IOStandard("LVCMOS33")), + + # When executing softcore code in-place from the flash, we want + # the flash reset to be released before the system reset. + ("norflash_rst_n", 0, Pins("P22"), IOStandard("LVCMOS33"), Misc("SLEW=FAST"), Drive(8)), + ("norflash", 0, + Subsignal("adr", Pins("L22 L20 K22 K21 J19 H20 F22", + "F21 K17 J17 E22 E20 H18 H19 F20", + "G19 C22 C20 D22 D21 F19 F18 D20 D19")), + Subsignal("d", Pins("AA20 U14 U13 AA6 AB6 W4 Y4 Y7", + "AA2 AB2 V15 AA18 AB18 Y13 AA12 AB12"), Misc("PULLDOWN")), + Subsignal("oe_n", Pins("M22")), + Subsignal("we_n", Pins("N20")), + Subsignal("ce_n", Pins("M21")), + IOStandard("LVCMOS33"), Misc("SLEW=FAST"), Drive(8) + ), + + ("serial", 0, + Subsignal("tx", Pins("L17"), IOStandard("LVCMOS33"), Misc("SLEW=SLOW")), + Subsignal("rx", Pins("K18"), IOStandard("LVCMOS33"), Misc("PULLUP")) + ), + + ("ddram_clock", 0, + Subsignal("p", Pins("M3")), + Subsignal("n", Pins("L4")), + IOStandard("SSTL2_I") + ), + ("ddram", 0, + Subsignal("a", Pins("B1 B2 H8 J7 E4 D5 K7 F5 G6 C1 C3 D1 D2")), + Subsignal("ba", Pins("A2 E6")), + Subsignal("cs_n", Pins("F7")), + Subsignal("cke", Pins("G7")), + Subsignal("ras_n", Pins("E5")), + Subsignal("cas_n", Pins("C4")), + Subsignal("we_n", Pins("D3")), + Subsignal("dq", Pins("Y2 W3 W1 P8 P7 P6 P5 T4 T3", + "U4 V3 N6 N7 M7 M8 R4 P4 M6 L6 P3 N4", + "M5 V2 V1 U3 U1 T2 T1 R3 R1 P2 P1")), + Subsignal("dm", Pins("E1 E3 F3 G4")), + Subsignal("dqs", Pins("F1 F2 H5 H6")), + IOStandard("SSTL2_I") + ), + + ("eth_clocks", 0, + Subsignal("phy", Pins("M20")), + Subsignal("rx", Pins("H22")), + Subsignal("tx", Pins("H21")), + IOStandard("LVCMOS33") + ), + ("eth", 0, + Subsignal("rst_n", Pins("R22")), + Subsignal("dv", Pins("V21")), + Subsignal("rx_er", Pins("V22")), + Subsignal("rx_data", Pins("U22 U20 T22 T21")), + Subsignal("tx_en", Pins("N19")), + Subsignal("tx_er", Pins("M19")), + Subsignal("tx_data", Pins("M16 L15 P19 P20")), + Subsignal("col", Pins("W20")), + Subsignal("crs", Pins("W22")), + IOStandard("LVCMOS33") + ), + + ("vga_out", 0, + Subsignal("clk", Pins("A11")), + Subsignal("r", Pins("C6 B6 A6 C7 A7 B8 A8 D9")), + Subsignal("g", Pins("C8 C9 A9 D7 D8 D10 C10 B10")), + Subsignal("b", Pins("D11 C12 B12 A12 C13 A13 D14 C14")), + Subsignal("hsync_n", Pins("A14")), + Subsignal("vsync_n", Pins("C15")), + Subsignal("psave_n", Pins("B14")), + IOStandard("LVCMOS33") + ), + + ("mmc", 0, + Subsignal("clk", Pins("A10")), + Subsignal("cmd", Pins("B18")), + Subsignal("dat", Pins("A18 E16 C17 A17")), + IOStandard("LVCMOS33") + ), + + # Digital video mixer extension board + ("dvi_in", 0, + Subsignal("clk", Pins("A20")), + Subsignal("data0_n", Pins("A21")), + Subsignal("data1", Pins("B21")), + Subsignal("data2_n", Pins("B22")), + Subsignal("scl", Pins("G16")), + Subsignal("sda", Pins("G17")), + IOStandard("LVCMOS33") + ), + ("dvi_in", 1, + Subsignal("clk", Pins("H17")), + Subsignal("data0_n", Pins("H16")), + Subsignal("data1", Pins("F17")), + Subsignal("data2_n", Pins("F16")), + Subsignal("scl", Pins("J16")), + Subsignal("sda", Pins("K16")), + IOStandard("LVCMOS33") + ), + ("dvi_pots", 0, + Subsignal("charge", Pins("A18")), # SD_DAT0 + Subsignal("blackout", Pins("C17")), # SD_DAT2 + Subsignal("crossfade", Pins("A17")), # SD_DAT3 + IOStandard("LVCMOS33") + ) +] + + +class Platform(XilinxPlatform): + identifier = 0x4D31 + default_clk_name = "clk50" + default_clk_period = 20 + + def __init__(self): + XilinxPlatform.__init__(self, "xc6slx45-fgg484-2", _io) + + def create_programmer(self): + return UrJTAG(cable="milkymist", flash_proxy_basename="fjmem-m1.bit") + + def do_finalize(self, fragment): + XilinxPlatform.do_finalize(self, fragment) + + try: + eth_clocks = self.lookup_request("eth_clocks") + self.add_period_constraint(eth_clocks.rx, 40) + self.add_period_constraint(eth_clocks.tx, 40) + self.add_platform_command(""" +TIMESPEC "TS{phy_tx_clk}_io" = FROM "GRP{phy_tx_clk}" TO "PADS" 10 ns; +TIMESPEC "TS{phy_rx_clk}_io" = FROM "PADS" TO "GRP{phy_rx_clk}" 10 ns; +""", phy_rx_clk=eth_clocks.rx, phy_tx_clk=eth_clocks.tx) + except ConstraintError: + pass + + for i in range(2): + si = "dviclk"+str(i) + try: + self.add_period_constraint(self.lookup_request("dvi_in", i).clk, 26.7) + except ConstraintError: + pass diff --git a/litex/gen/migen/build/platforms/mercury.py b/litex/gen/migen/build/platforms/mercury.py new file mode 100644 index 00000000..699218df --- /dev/null +++ b/litex/gen/migen/build/platforms/mercury.py @@ -0,0 +1,138 @@ +# This file is Copyright (c) 2015 William D. Jones +# License: BSD + +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform +from migen.build.xilinx.programmer import XC3SProg + + +_io = [ + ("clk50", 0, Pins("P43"), IOStandard("LVCMOS33")), + + ("user_btn", 0, Pins("P41"), IOStandard("LVTTL")), + + # The serial interface and flash memory have a shared SPI bus. + # FPGA is secondary + ("spiserial", 0, + Subsignal("cs_n", Pins("P39"), IOStandard("LVTTL")), + Subsignal("clk", Pins("P53"), IOStandard("LVTTL")), + Subsignal("mosi", Pins("P46"), IOStandard("LVTTL")), + Subsignal("miso", Pins("P51"), IOStandard("LVTTL")) + ), + + # FPGA is primary + ("spiflash", 0, + Subsignal("cs_n", Pins("P27"), IOStandard("LVTTL")), + Subsignal("clk", Pins("P53"), IOStandard("LVTTL")), + Subsignal("mosi", Pins("P46"), IOStandard("LVTTL")), + Subsignal("miso", Pins("P51"), IOStandard("LVTTL")) + ), + + ("spiflash2x", 0, + Subsignal("cs_n", Pins("P27")), + Subsignal("clk", Pins("P53")), + Subsignal("dq", Pins("P46", "P51")), + IOStandard("LVTTL"), Misc("SLEW=FAST") + ), + + # ADC over SPI- FPGA is primary + ("adc", 0, + Subsignal("cs_n", Pins("P12"), IOStandard("LVTTL")), + Subsignal("clk", Pins("P9"), IOStandard("LVTTL")), + Subsignal("mosi", Pins("P10"), IOStandard("LVTTL")), + Subsignal("miso", Pins("P21"), IOStandard("LVTTL")) + ), + + # GPIO control- SRAM and connectors are shared: these pins control how + # to access each. Recommended to combine with gpio_sram_bus extension, + # since these pins are related but not exposed on connectors. + ("gpio_ctl", 0, + Subsignal("ce_n", Pins("P3")), # Memory chip-enable. Called MEM_CEN + # in schematic. + Subsignal("bussw_oe_n", Pins("P30")), # 5V tolerant GPIO is shared + # w/ memory using this pin. + IOStandard("LVTTL"), Misc("SLEW=FAST") + ) +] + +# Perhaps define some connectors as having a specific purpose- i.e. a 5V GPIO +# bus with data, peripheral-select, and control signals? +_connectors = [ + ("GPIO", """P59 P60 P61 P62 P64 P57 + P56 P52 P50 P49 P85 P84 + P83 P78 P77 P65 P70 P71 + P72 P73 P5 P4 P6 P98 + P94 P93 P90 P89 P88 P86"""), # 5V I/O- LVTTL + ("DIO", "P20 P32 P33 P34 P35 P36 P37"), # Fast 3.3V IO (Directly attached + # to FPGA)- LVCMOS33 + ("CLKIO", "P40 P44"), # Clock IO (Can be used as GPIO)- LVCMOS33 + ("INPUT", "P68 P97 P7 P82"), # Input-only pins- LVCMOS33 + ("LED", "P13 P15 P16 P19") # LEDs can be used as pins as well- LVTTL. +] + +# Some default useful extensions- use platform.add_extension() to use, e.g. +# from migen.build.platforms import mercury +# plat = mercury.Platform() +# plat.add_extension(mercury.gpio_sram) + +# SRAM and 5V-tolerant I/O share a parallel bus on 200k gate version. The SRAM +# controller needs to take care of switching the bus between the two. Meant to +# be Cat() into one GPIO bus, and combined with gpio_ctl. +gpio_sram = [ + ("gpio_sram_bus", 0, + Subsignal("a", Pins("""GPIO:0 GPIO:1 GPIO:2 GPIO:3 + GPIO:4 GPIO:5 GPIO:6 GPIO:7 + GPIO:8 GPIO:9 GPIO:10 GPIO:11 + GPIO:12 GPIO:13 GPIO:14 GPIO:15 + GPIO:16 GPIO:17 GPIO:18 GPIO:19""")), + # A19 is actually unused- free for GPIO + # 8-bit data bus + Subsignal("d", Pins("""GPIO:20 GPIO:21 GPIO:22 GPIO:23 + GPIO:24 GPIO:25 GPIO:26 GPIO:27""")), + Subsignal("we_n", Pins("GPIO:28")), + Subsignal("unused", Pins("GPIO:29")), # Only used by GPIO. + # Subsignal("oe_n", Pins()), # If OE wasn't tied to ground on Mercury, + # this pin would be here. + IOStandard("LVTTL"), Misc("SLEW=FAST") + ) +] + +# The "serial port" is in fact over SPI. The creators of the board provide a +# VHDL file for talking over this interface. In light of space constraints and +# the fact that both the FT245RL and FPGA can BOTH be SPI primaries, however, +# it may be necessary to sacrifice two "high-speed" (DIO, INPUT) pins instead. +serial = [ + ("serial", 0, + Subsignal("tx", Pins("DIO:0"), IOStandard("LVCMOS33")), # FTDI D1 + Subsignal("rx", Pins("INPUT:0"), IOStandard("LVCMOS33")) + ) # FTDI D0 +] + +leds = [ + ("user_led", 0, Pins("LED:0"), IOStandard("LVTTL")), + ("user_led", 1, Pins("LED:1"), IOStandard("LVTTL")), + ("user_led", 2, Pins("LED:2"), IOStandard("LVTTL")), + ("user_led", 3, Pins("LED:3"), IOStandard("LVTTL")) +] + +# See: http://www.micro-nova.com/mercury-baseboard/ +# Not implemented yet. +baseboard = [ +] + + +class Platform(XilinxPlatform): + default_clk_name = "clk50" + default_clk_period = 20 + + def __init__(self, device="xc3s200a-4-vq100"): + XilinxPlatform.__init__(self, device, _io, _connectors) + # Small device- optimize for AREA instead of SPEED (LM32 runs at about + # 60-65MHz in AREA configuration). + self.toolchain.xst_opt = """-ifmt MIXED +-use_new_parser yes +-opt_mode AREA +-register_balancing yes""" + + def create_programmer(self): + raise NotImplementedError diff --git a/litex/gen/migen/build/platforms/mimasv2.py b/litex/gen/migen/build/platforms/mimasv2.py new file mode 100644 index 00000000..703fdc49 --- /dev/null +++ b/litex/gen/migen/build/platforms/mimasv2.py @@ -0,0 +1,124 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform + + +_io = [ + ("clk100", 0, Pins("V10"), IOStandard("LVCMOS33")), + ("clk12", 0, Pins("D9"), IOStandard("LVCMOS33")), + + ("serial", 0, + Subsignal("tx", Pins("A8"), IOStandard("LVCMOS33"), + Misc("SLEW=FAST")), + Subsignal("rx", Pins("B8"), IOStandard("LVCMOS33"), + Misc("SLEW=FAST"))), + + ("spiflash", 0, + Subsignal("cs_n", Pins("V3")), + Subsignal("clk", Pins("R15")), + Subsignal("mosi", Pins("T13")), + Subsignal("miso", Pins("R13"), Misc("PULLUP")), + IOStandard("LVCMOS33"), Misc("SLEW=FAST")), + + ("ddram_clock", 0, + Subsignal("p", Pins("G3")), + Subsignal("n", Pins("G1")), + IOStandard("MOBILE_DDR")), + + ("ddram", 0, + Subsignal("a", Pins("J7 J6 H5 L7 F3 H4 H3 H6 D2 D1 F4 D3 G6")), + Subsignal("ba", Pins("F2 F1")), + Subsignal("cke", Pins("H7")), + Subsignal("ras_n", Pins("L5")), + Subsignal("cas_n", Pins("K5")), + Subsignal("we_n", Pins("E3")), + Subsignal( + "dq", Pins("L2 L1 K2 K1 H2 H1 J3 J1 M3 M1 N2 N1 T2 T1 U2 U1") + ), + Subsignal("dqs", Pins("L4 P2")), + Subsignal("dm", Pins("K3 K4")), + IOStandard("MOBILE_DDR")), + + ("dipswitch", 0, Pins("C17"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("dipswitch", 1, Pins("C18"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("dipswitch", 2, Pins("D17"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("dipswitch", 3, Pins("D18"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("dipswitch", 4, Pins("E18"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("dipswitch", 5, Pins("E16"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("dipswitch", 6, Pins("F18"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("dipswitch", 7, Pins("F17"), IOStandard("LVCMOS33"), Misc("PULLUP")), + + ("buttonswitch", 0, Pins("K18"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("buttonswitch", 1, Pins("K17"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("buttonswitch", 2, Pins("L17"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("buttonswitch", 3, Pins("M16"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("buttonswitch", 4, Pins("L18"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("buttonswitch", 5, Pins("M18"), IOStandard("LVCMOS33"), Misc("PULLUP")), + + ("user_led", 0, Pins("T18"), IOStandard("LVCMOS33"), Drive(8)), + ("user_led", 1, Pins("T17"), IOStandard("LVCMOS33"), Drive(8)), + ("user_led", 2, Pins("U18"), IOStandard("LVCMOS33"), Drive(8)), + ("user_led", 3, Pins("U17"), IOStandard("LVCMOS33"), Drive(8)), + ("user_led", 4, Pins("N16"), IOStandard("LVCMOS33"), Drive(8)), + ("user_led", 5, Pins("N15"), IOStandard("LVCMOS33"), Drive(8)), + ("user_led", 6, Pins("P16"), IOStandard("LVCMOS33"), Drive(8)), + ("user_led", 7, Pins("P15"), IOStandard("LVCMOS33"), Drive(8)), + + ("mmc", 0, + Subsignal("dat", Pins("K14 G18 J13 L13"), IOStandard("LVCMOS33"), + Misc("SLEW=FAST")), + + Subsignal("cmd", Pins("G16"), IOStandard("LVCMOS33"), + Misc("SLEW=FAST")), + + Subsignal("clk", Pins("L12"), IOStandard("LVCMOS33"), + Misc("SLEW=FAST"))), + + ("sevenseg", 0, + Subsignal("segment7", Pins("A3"), IOStandard("LVCMOS33")), # A + Subsignal("segment6", Pins("B4"), IOStandard("LVCMOS33")), # B + Subsignal("segment5", Pins("A4"), IOStandard("LVCMOS33")), # C + Subsignal("segment4", Pins("C4"), IOStandard("LVCMOS33")), # D + Subsignal("segment3", Pins("C5"), IOStandard("LVCMOS33")), # E + Subsignal("segment2", Pins("D6"), IOStandard("LVCMOS33")), # F + Subsignal("segment1", Pins("C6"), IOStandard("LVCMOS33")), # G + Subsignal("segment0", Pins("A5"), IOStandard("LVCMOS33")), # Dot + Subsignal("enable0", Pins("B2"), IOStandard("LVCMOS33")), # EN0 + Subsignal("enable1", Pins("A2"), IOStandard("LVCMOS33")), # EN1 + Subsignal("enable2", Pins("B3"), IOStandard("LVCMOS33"))), # EN2 + + + ("audio", 0, + Subsignal("channel1", Pins("B16"), IOStandard("LVCMOS33"), + Misc("SLEW=FAST")), + Subsignal("channel2", Pins("A16"), IOStandard("LVCMOS33"), + Misc("SLEW=FAST"))), + + ("vga_out", 0, + Subsignal("hsync_n", Pins("B12"), IOStandard("LVCMOS33"), + Misc("SLEW=FAST")), + Subsignal("vsync_n", Pins("A12"), IOStandard("LVCMOS33"), + Misc("SLEW=FAST")), + Subsignal("r", Pins("A9 B9 C9"), IOStandard("LVCMOS33"), + Misc("SLEW=FAST")), + Subsignal("g", Pins("C10 A10 C11"), IOStandard("LVCMOS33"), + Misc("SLEW=FAST")), + Subsignal("b", Pins("B11 A11"), IOStandard("LVCMOS33"), + Misc("SLEW=FAST"))) +] + +_connectors = [ + ("P6", "T3 R3 V5 U5 V4 T4 V7 U7"), + ("P7", "V11 U11 V13 U13 T10 R10 T11 R11"), + ("P8", "L16 L15 K16 K15 J18 J16 H18 H17") +] + + +class Platform(XilinxPlatform): + default_clk_name = "clk100" + default_clk_period = 10 + + def __init__(self): + XilinxPlatform.__init__(self, "xc6slx9-csg324-2", _io, _connectors) + + def create_programmer(self): + raise NotImplementedError diff --git a/litex/gen/migen/build/platforms/minispartan6.py b/litex/gen/migen/build/platforms/minispartan6.py new file mode 100644 index 00000000..c46fa29c --- /dev/null +++ b/litex/gen/migen/build/platforms/minispartan6.py @@ -0,0 +1,126 @@ +# This file is Copyright (c) 2015 Matt O'Gorman +# License: BSD + +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform +from migen.build.xilinx.programmer import XC3SProg, FpgaProg + + +_io = [ + ("user_led", 0, Pins("P11"), IOStandard("LVCMOS33")), + ("user_led", 1, Pins("N9"), IOStandard("LVCMOS33")), + ("user_led", 2, Pins("M9"), IOStandard("LVCMOS33")), + ("user_led", 3, Pins("P9"), IOStandard("LVCMOS33")), + ("user_led", 4, Pins("T8"), IOStandard("LVCMOS33")), + ("user_led", 5, Pins("N8"), IOStandard("LVCMOS33")), + ("user_led", 6, Pins("P8"), IOStandard("LVCMOS33")), + ("user_led", 7, Pins("P7"), IOStandard("LVCMOS33")), + + ("user_sw", 0, Pins("L1"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("user_sw", 1, Pins("L3"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("user_sw", 2, Pins("L4"), IOStandard("LVCMOS33"), Misc("PULLUP")), + ("user_sw", 3, Pins("L5"), IOStandard("LVCMOS33"), Misc("PULLUP")), + + ("clk32", 0, Pins("J4"), IOStandard("LVCMOS33")), + ("clk50", 0, Pins("K3"), IOStandard("LVCMOS33")), + + ("spiflash", 0, + Subsignal("cs_n", Pins("T3"), IOStandard("LVCMOS33")), + Subsignal("clk", Pins("R11"), IOStandard("LVCMOS33")), + Subsignal("mosi", Pins("T10"), IOStandard("LVCMOS33")), + Subsignal("miso", Pins("P10"), IOStandard("LVCMOS33")) + ), + + ("adc", 0, + Subsignal("cs_n", Pins("F6"), IOStandard("LVCMOS33")), + Subsignal("clk", Pins("G6"), IOStandard("LVCMOS33")), + Subsignal("mosi", Pins("H4"), IOStandard("LVCMOS33")), + Subsignal("miso", Pins("H5"), IOStandard("LVCMOS33")) + ), + + ("serial", 0, + Subsignal("tx", Pins("N6"), IOStandard("LVCMOS33")), # FTDI D1 + Subsignal("rx", Pins("M7"), IOStandard("LVCMOS33")) # FTDI D0 + ), + + ("audio", 0, + Subsignal("a0", Pins("B8"), IOStandard("LVCMOS33")), + Subsignal("a1", Pins("A8"), IOStandard("LVCMOS33")) + ), + + ("sdram_clock", 0, Pins("G16"), IOStandard("LVCMOS33"), Misc("SLEW=FAST")), + ("sdram", 0, + Subsignal("a", Pins("T15 R16 P15 P16 N16 M15 M16 L16 K15 K16 R15 J16 H15")), + Subsignal("dq", Pins("T13 T12 R12 T9 R9 T7 R7 T6 F16 E15 E16 D16 B16 B15 C16 C15")), + Subsignal("we_n", Pins("R5")), + Subsignal("ras_n", Pins("R2")), + Subsignal("cas_n", Pins("T4")), + Subsignal("cs_n", Pins("R1")), + Subsignal("cke", Pins("H16")), + Subsignal("ba", Pins("R14 T14")), + Subsignal("dm", Pins("T5 F15")), + IOStandard("LVCMOS33"), Misc("SLEW=FAST") + ), + + ("usb_fifo", 0, + Subsignal("data", Pins("M7 N6 M6 P5 N5 P4 P2 P1")), + Subsignal("rxf_n", Pins("N3")), + Subsignal("txe_n", Pins("N1")), + Subsignal("rd_n", Pins("M1")), + Subsignal("wr_n", Pins("M2")), + Subsignal("siwua", Pins("M3")), + IOStandard("LVCMOS33"), Drive(8), Misc("SLEW=FAST") + ), + + ("sd", 0, + Subsignal("sck", Pins("L12")), + Subsignal("d3", Pins("K12")), + Subsignal("d", Pins("M10")), + Subsignal("d1", Pins("L10")), + Subsignal("d2", Pins("J11")), + Subsignal("cmd", Pins("K11")), + IOStandard("LVCMOS33") + ), + + ("dvi_in", 0, + Subsignal("clk_p", Pins("C9"), IOStandard("TMDS_33")), + Subsignal("clk_n", Pins("A9"), IOStandard("TMDS_33")), + Subsignal("data_p", Pins("C7 B6 B5"), IOStandard("TMDS_33")), + Subsignal("data_n", Pins("A7 A6 A5"), IOStandard("TMDS_33")), + Subsignal("scl", Pins("C1"), IOStandard("LVCMOS33")), + Subsignal("sda", Pins("B1"), IOStandard("LVCMOS33")) + ), + + ("dvi_out", 0, + Subsignal("clk_p", Pins("B14"), IOStandard("TMDS_33")), + Subsignal("clk_n", Pins("A14"), IOStandard("TMDS_33")), + Subsignal("data_p", Pins("C13 B12 C11"), IOStandard("TMDS_33")), + Subsignal("data_n", Pins("A13 A12 A11"), IOStandard("TMDS_33")), + ) +] + +_connectors = [ + ("A", "E7 C8 D8 E8 D9 A10 B10 C10 E10 F9 F10 D11"), + ("B", "E11 D14 D12 E12 E13 F13 F12 F14 G12 H14 J14"), + ("C", "J13 J12 K14 L14 L13 M14 M13 N14 M12 N12 P12 M11"), + ("D", "D6 C6 E6 C5"), + ("E", "D5 A4 G5 A3 B3 A2 B2 C3 C2 D3 D1 E3"), + ("F", "E2 E1 E4 F4 F5 G3 F3 G1 H3 H1 H2 J1") +] + + +class Platform(XilinxPlatform): + default_clk_name = "clk32" + default_clk_period = 31.25 + + def __init__(self, device="xc6slx9", programmer="xc3sprog"): + self.programmer = programmer + XilinxPlatform.__init__(self, device+"-3-ftg256", _io, _connectors) + + def create_programmer(self): + if self.programmer == "xc3sprog": + return XC3SProg("minispartan6", "bscan_spi_minispartan6.bit") + elif self.programmer == "fpgaprog": + return FpgaProg() + else: + raise ValueError("{} programmer is not supported".format(programmer)) diff --git a/litex/gen/migen/build/platforms/mixxeo.py b/litex/gen/migen/build/platforms/mixxeo.py new file mode 100644 index 00000000..8d4c85d8 --- /dev/null +++ b/litex/gen/migen/build/platforms/mixxeo.py @@ -0,0 +1,188 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform +from migen.build.xilinx.programmer import UrJTAG + + +_io = [ + ("user_led", 0, Pins("V5"), IOStandard("LVCMOS33"), Drive(24), Misc("SLEW=QUIETIO")), + + ("clk50", 0, Pins("AB13"), IOStandard("LVCMOS33")), + + # When executing softcore code in-place from the flash, we want + # the flash reset to be released before the system reset. + ("norflash_rst_n", 0, Pins("P22"), IOStandard("LVCMOS33"), Misc("SLEW=FAST"), Drive(8)), + ("norflash", 0, + Subsignal("adr", Pins("L22 L20 K22 K21 J19 H20 F22", + "F21 K17 J17 E22 E20 H18 H19 F20", + "G19 C22 C20 D22 D21 F19 F18 D20 D19")), + Subsignal("d", Pins("AA20 U14 U13 AA6 AB6 W4 Y4 Y7", + "AA2 AB2 V15 AA18 AB18 Y13 AA12 AB12"), Misc("PULLDOWN")), + Subsignal("oe_n", Pins("M22")), + Subsignal("we_n", Pins("N20")), + Subsignal("ce_n", Pins("M21")), + IOStandard("LVCMOS33"), Misc("SLEW=FAST"), Drive(8) + ), + + ("serial", 0, + Subsignal("tx", Pins("L17"), IOStandard("LVCMOS33"), Misc("SLEW=SLOW")), + Subsignal("rx", Pins("K18"), IOStandard("LVCMOS33"), Misc("PULLUP")) + ), + + ("ddram_clock", 0, + Subsignal("p", Pins("M3")), + Subsignal("n", Pins("L4")), + IOStandard("SSTL2_I") + ), + ("ddram", 0, + Subsignal("a", Pins("B1 B2 H8 J7 E4 D5 K7 F5 G6 C1 C3 D1 D2")), + Subsignal("ba", Pins("A2 E6")), + Subsignal("cs_n", Pins("F7")), + Subsignal("cke", Pins("G7")), + Subsignal("ras_n", Pins("E5")), + Subsignal("cas_n", Pins("C4")), + Subsignal("we_n", Pins("D3")), + Subsignal("dq", Pins("Y2 W3 W1 P8 P7 P6 P5 T4 T3", + "U4 V3 N6 N7 M7 M8 R4 P4 M6 L6 P3 N4", + "M5 V2 V1 U3 U1 T2 T1 R3 R1 P2 P1")), + Subsignal("dm", Pins("E1 E3 F3 G4")), + Subsignal("dqs", Pins("F1 F2 H5 H6")), + IOStandard("SSTL2_I") + ), + + ("eth_clocks", 0, + Subsignal("phy", Pins("M20")), + Subsignal("rx", Pins("H22")), + Subsignal("tx", Pins("H21")), + IOStandard("LVCMOS33") + ), + ("eth", 0, + Subsignal("rst_n", Pins("R22")), + Subsignal("dv", Pins("V21")), + Subsignal("rx_er", Pins("V22")), + Subsignal("rx_data", Pins("U22 U20 T22 T21")), + Subsignal("tx_en", Pins("N19")), + Subsignal("tx_er", Pins("M19")), + Subsignal("tx_data", Pins("M16 L15 P19 P20")), + Subsignal("col", Pins("W20")), + Subsignal("crs", Pins("W22")), + IOStandard("LVCMOS33") + ), + + ("vga_out", 0, + Subsignal("clk", Pins("A10")), + Subsignal("r", Pins("C6 B6 A6 C7 A7 B8 A8 D9")), + Subsignal("g", Pins("C8 C9 A9 D7 D8 D10 C10 B10")), + Subsignal("b", Pins("D11 C12 B12 A12 C13 A13 D14 C14")), + Subsignal("hsync_n", Pins("A14")), + Subsignal("vsync_n", Pins("C15")), + Subsignal("psave_n", Pins("B14")), + IOStandard("LVCMOS33") + ), + ("dvi_out", 0, + Subsignal("clk_p", Pins("W12"), IOStandard("TMDS_33")), + Subsignal("clk_n", Pins("Y12"), IOStandard("TMDS_33")), + Subsignal("data0_p", Pins("Y16"), IOStandard("TMDS_33")), + Subsignal("data0_n", Pins("W15"), IOStandard("TMDS_33")), + Subsignal("data1_p", Pins("AA16"), IOStandard("TMDS_33")), + Subsignal("data1_n", Pins("AB16"), IOStandard("TMDS_33")), + Subsignal("data2_p", Pins("Y15"), IOStandard("TMDS_33")), + Subsignal("data2_n", Pins("AB15"), IOStandard("TMDS_33")), + ), + + ("mmc", 0, + Subsignal("clk", Pins("J3")), + Subsignal("cmd", Pins("K1")), + Subsignal("dat", Pins("J6 K6 N1 K5")), + IOStandard("LVCMOS33") + ), + + ("dvi_in", 0, + Subsignal("clk_p", Pins("K20"), IOStandard("TMDS_33")), + Subsignal("clk_n", Pins("K19"), IOStandard("TMDS_33")), + Subsignal("data0_p", Pins("B21"), IOStandard("TMDS_33")), + Subsignal("data0_n", Pins("B22"), IOStandard("TMDS_33")), + Subsignal("data1_p", Pins("A20"), IOStandard("TMDS_33")), + Subsignal("data1_n", Pins("A21"), IOStandard("TMDS_33")), + Subsignal("data2_p", Pins("K16"), IOStandard("TMDS_33")), + Subsignal("data2_n", Pins("J16"), IOStandard("TMDS_33")), + Subsignal("scl", Pins("G20"), IOStandard("LVCMOS33")), + Subsignal("sda", Pins("H16"), IOStandard("LVCMOS33")), + Subsignal("hpd_notif", Pins("G22"), IOStandard("LVCMOS33")), + Subsignal("hpd_en", Pins("G17"), IOStandard("LVCMOS33")) + ), + ("dvi_in", 1, + Subsignal("clk_p", Pins("C11"), IOStandard("TMDS_33")), + Subsignal("clk_n", Pins("A11"), IOStandard("TMDS_33")), + Subsignal("data0_p", Pins("B18"), IOStandard("TMDS_33")), + Subsignal("data0_n", Pins("A18"), IOStandard("TMDS_33")), + Subsignal("data1_p", Pins("C17"), IOStandard("TMDS_33")), + Subsignal("data1_n", Pins("A17"), IOStandard("TMDS_33")), + Subsignal("data2_p", Pins("E16"), IOStandard("TMDS_33")), + Subsignal("data2_n", Pins("D17"), IOStandard("TMDS_33")), + Subsignal("scl", Pins("F17"), IOStandard("LVCMOS33")), + Subsignal("sda", Pins("F16"), IOStandard("LVCMOS33")), + Subsignal("hpd_notif", Pins("G16"), IOStandard("LVCMOS33")), + Subsignal("hpd_en", Pins("B20"), IOStandard("LVCMOS33")) + ), + ("dvi_in", 2, + Subsignal("clk_p", Pins("Y11"), IOStandard("TMDS_33")), + Subsignal("clk_n", Pins("AB11"), IOStandard("TMDS_33")), + Subsignal("data0_p", Pins("V11"), IOStandard("TMDS_33")), + Subsignal("data0_n", Pins("W11"), IOStandard("TMDS_33")), + Subsignal("data1_p", Pins("AA10"), IOStandard("TMDS_33")), + Subsignal("data1_n", Pins("AB10"), IOStandard("TMDS_33")), + Subsignal("data2_p", Pins("R11"), IOStandard("TMDS_33")), + Subsignal("data2_n", Pins("T11"), IOStandard("TMDS_33")), + Subsignal("scl", Pins("C16"), IOStandard("LVCMOS33")), + Subsignal("sda", Pins("B16"), IOStandard("LVCMOS33")), + Subsignal("hpd_notif", Pins("D6"), IOStandard("LVCMOS33")), + Subsignal("hpd_en", Pins("A4"), IOStandard("LVCMOS33")) + ), + ("dvi_in", 3, + Subsignal("clk_p", Pins("J20"), IOStandard("TMDS_33")), + Subsignal("clk_n", Pins("J22"), IOStandard("TMDS_33")), + Subsignal("data0_p", Pins("P18"), IOStandard("TMDS_33")), + Subsignal("data0_n", Pins("R19"), IOStandard("TMDS_33")), + Subsignal("data1_p", Pins("P17"), IOStandard("TMDS_33")), + Subsignal("data1_n", Pins("N16"), IOStandard("TMDS_33")), + Subsignal("data2_p", Pins("M17"), IOStandard("TMDS_33")), + Subsignal("data2_n", Pins("M18"), IOStandard("TMDS_33")), + Subsignal("scl", Pins("P21"), IOStandard("LVCMOS33")), + Subsignal("sda", Pins("N22"), IOStandard("LVCMOS33")), + Subsignal("hpd_notif", Pins("H17"), IOStandard("LVCMOS33")), + Subsignal("hpd_en", Pins("C19"), IOStandard("LVCMOS33")) + ), +] + + +class Platform(XilinxPlatform): + identifier = 0x4D58 + default_clk_name = "clk50" + default_clk_period = 20 + + def __init__(self): + XilinxPlatform.__init__(self, "xc6slx45-fgg484-2", _io) + self.add_platform_command("CONFIG VCCAUX=\"3.3\";\n") + + def create_programmer(self): + return UrJTAG("fjmem-mixxeo.bit") + + def do_finalize(self, fragment): + XilinxPlatform.do_finalize(self, fragment) + + try: + eth_clocks = self.lookup_request("eth_clocks") + self.add_period_constraint(eth_clocks.rx, 40) + self.add_period_constraint(eth_clocks.tx, 40) + self.add_platform_command(""" +TIMESPEC "TS{phy_tx_clk}_io" = FROM "GRP{phy_tx_clk}" TO "PADS" 10 ns; +TIMESPEC "TS{phy_rx_clk}_io" = FROM "PADS" TO "GRP{phy_rx_clk}" 10 ns; +""", phy_rx_clk=eth_clocks.rx, phy_tx_clk=eth_clocks.tx) + except ConstraintError: + pass + + for i in range(4): + try: + self.add_period_constraint(self.lookup_request("dvi_in", i).clk_p, 12) + except ConstraintError: + pass diff --git a/litex/gen/migen/build/platforms/ml605.py b/litex/gen/migen/build/platforms/ml605.py new file mode 100644 index 00000000..04184b74 --- /dev/null +++ b/litex/gen/migen/build/platforms/ml605.py @@ -0,0 +1,60 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform + + +_io = [ + # System clock (Differential 200MHz) + ("clk200", 0, + Subsignal("p", Pins("J9"), IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE")), + Subsignal("n", Pins("H9"), IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE")) + ), + + # User clock (66MHz) + ("clk66", 0, Pins("U23"), IOStandard("LVCMOS25")), + + # CPU reset switch + ("cpu_reset", 0, Pins("H10"), IOStandard("SSTL15")), + + # LEDs + ("user_led", 0, Pins("AC22"), IOStandard("LVCMOS25"), Misc("SLEW=SLOW")), + ("user_led", 1, Pins("AC24"), IOStandard("LVCMOS25"), Misc("SLEW=SLOW")), + ("user_led", 2, Pins("AE22"), IOStandard("LVCMOS25"), Misc("SLEW=SLOW")), + ("user_led", 3, Pins("AE23"), IOStandard("LVCMOS25"), Misc("SLEW=SLOW")), + ("user_led", 4, Pins("AB23"), IOStandard("LVCMOS25"), Misc("SLEW=SLOW")), + ("user_led", 5, Pins("AG23"), IOStandard("LVCMOS25"), Misc("SLEW=SLOW")), + ("user_led", 6, Pins("AE24"), IOStandard("LVCMOS25"), Misc("SLEW=SLOW")), + ("user_led", 7, Pins("AD24"), IOStandard("LVCMOS25"), Misc("SLEW=SLOW")), + + # USB-to-UART + ("serial", 0, + Subsignal("tx", Pins("J25"), IOStandard("LVCMOS25")), + Subsignal("rx", Pins("J24"), IOStandard("LVCMOS25")) + ), + + # 10/100/1000 Tri-Speed Ethernet PHY + ("eth_clocks", 0, + Subsignal("rx", Pins("AP11")), + Subsignal("tx", Pins("AD12")), + IOStandard("LVCMOS25") + ), + ("eth", 0, + Subsignal("rst_n", Pins("AH13")), + Subsignal("dv", Pins("AM13")), + Subsignal("rx_er", Pins("AG12")), + Subsignal("rx_data", Pins("AN13 AF14 AE14 AN12 AM12 AD11 AC12 AC13")), + Subsignal("tx_en", Pins("AJ10")), + Subsignal("tx_er", Pins("AH10")), + Subsignal("tx_data", Pins("AM11 AL11 AG10 AG11 AL10 AM10 AE11 AF11")), + Subsignal("col", Pins("AK13")), + Subsignal("crs", Pins("AL13")), + IOStandard("LVCMOS25") + ) +] + + +class Platform(XilinxPlatform): + default_clk_name = "clk200" + default_clk_period = 5 + + def __init__(self): + XilinxPlatform.__init__(self, "xc6vlx240t-ff1156-1", _io) diff --git a/litex/gen/migen/build/platforms/papilio_pro.py b/litex/gen/migen/build/platforms/papilio_pro.py new file mode 100644 index 00000000..8ee33005 --- /dev/null +++ b/litex/gen/migen/build/platforms/papilio_pro.py @@ -0,0 +1,62 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform +from migen.build.xilinx.programmer import XC3SProg + + +_io = [ + ("user_led", 0, Pins("P112"), IOStandard("LVCMOS33"), Drive(24), Misc("SLEW=QUIETIO")), + + ("clk32", 0, Pins("P94"), IOStandard("LVCMOS33")), + + ("serial", 0, + Subsignal("tx", Pins("P105"), IOStandard("LVCMOS33"), Misc("SLEW=SLOW")), + Subsignal("rx", Pins("P101"), IOStandard("LVCMOS33"), Misc("PULLUP")) + ), + + ("spiflash", 0, + Subsignal("cs_n", Pins("P38")), + Subsignal("clk", Pins("P70")), + Subsignal("mosi", Pins("P64")), + Subsignal("miso", Pins("P65"), Misc("PULLUP")), + IOStandard("LVCMOS33"), Misc("SLEW=FAST") + ), + ("spiflash2x", 0, + Subsignal("cs_n", Pins("P38")), + Subsignal("clk", Pins("P70")), + Subsignal("dq", Pins("P64", "P65")), + IOStandard("LVCMOS33"), Misc("SLEW=FAST") + ), + + ("sdram_clock", 0, Pins("P32"), IOStandard("LVCMOS33"), Misc("SLEW=FAST")), + ("sdram", 0, + Subsignal("a", Pins("P140 P139 P138 P137 P46 P45 P44", + "P43 P41 P40 P141 P35 P34")), + Subsignal("ba", Pins("P143 P142")), + Subsignal("cs_n", Pins("P1")), + Subsignal("cke", Pins("P33")), + Subsignal("ras_n", Pins("P2")), + Subsignal("cas_n", Pins("P5")), + Subsignal("we_n", Pins("P6")), + Subsignal("dq", Pins("P9 P10 P11 P12 P14 P15 P16 P8 P21 P22 P23 P24 P26 P27 P29 P30")), + Subsignal("dm", Pins("P7 P17")), + IOStandard("LVCMOS33"), Misc("SLEW=FAST") + ) +] + +_connectors = [ + ("A", "P48 P51 P56 P58 P61 P66 P67 P75 P79 P81 P83 P85 P88 P93 P98 P100"), + ("B", "P99 P97 P92 P87 P84 P82 P80 P78 P74 P95 P62 P59 P57 P55 P50 P47"), + ("C", "P114 P115 P116 P117 P118 P119 P120 P121 P123 P124 P126 P127 P131 P132 P133 P134") +] + + +class Platform(XilinxPlatform): + identifier = 0x5050 + default_clk_name = "clk32" + default_clk_period = 31.25 + + def __init__(self): + XilinxPlatform.__init__(self, "xc6slx9-tqg144-2", _io, _connectors) + + def create_programmer(self): + return XC3SProg("papilio", "bscan_spi_lx9_papilio.bit") diff --git a/litex/gen/migen/build/platforms/pipistrello.py b/litex/gen/migen/build/platforms/pipistrello.py new file mode 100644 index 00000000..c202556a --- /dev/null +++ b/litex/gen/migen/build/platforms/pipistrello.py @@ -0,0 +1,138 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform +from migen.build.xilinx.programmer import XC3SProg + + +_io = [ + ("user_led", 0, Pins("V16"), IOStandard("LVTTL"), Drive(8), Misc("SLEW=QUIETIO")), # green at hdmi + ("user_led", 1, Pins("U16"), IOStandard("LVTTL"), Drive(8), Misc("SLEW=QUIETIO")), # red at hdmi + ("user_led", 2, Pins("A16"), IOStandard("LVTTL"), Drive(8), Misc("SLEW=QUIETIO")), # green at msd + ("user_led", 3, Pins("A15"), IOStandard("LVTTL"), Drive(8), Misc("SLEW=QUIETIO")), # red at msd + ("user_led", 4, Pins("A12"), IOStandard("LVTTL"), Drive(8), Misc("SLEW=QUIETIO")), # red at usb + + ("user_btn", 0, Pins("N14"), IOStandard("LVTTL"), Misc("PULLDOWN")), + + ("clk50", 0, Pins("H17"), IOStandard("LVTTL")), + + ("serial", 0, + Subsignal("tx", Pins("A10")), + Subsignal("rx", Pins("A11"), Misc("PULLUP")), + Subsignal("cts", Pins("C10"), Misc("PULLUP")), + Subsignal("rts", Pins("A9"), Misc("PULLUP")), + IOStandard("LVTTL"), + ), + + ("usb_fifo", 0, + Subsignal("data", Pins("A11 A10 C10 A9 B9 A8 B8 A7")), + Subsignal("rxf_n", Pins("C7")), + Subsignal("txe_n", Pins("A6")), + Subsignal("rd_n", Pins("B6")), + Subsignal("wr_n", Pins("A5")), + Subsignal("siwua", Pins("C5")), + IOStandard("LVTTL"), + ), + + ("hdmi", 0, + Subsignal("clk_p", Pins("U5"), IOStandard("TMDS_33")), + Subsignal("clk_n", Pins("V5"), IOStandard("TMDS_33")), + Subsignal("data0_p", Pins("T6"), IOStandard("TMDS_33")), + Subsignal("data0_n", Pins("V6"), IOStandard("TMDS_33")), + Subsignal("data1_p", Pins("U7"), IOStandard("TMDS_33")), + Subsignal("data1_n", Pins("V7"), IOStandard("TMDS_33")), + Subsignal("data2_p", Pins("U8"), IOStandard("TMDS_33")), + Subsignal("data2_n", Pins("V8"), IOStandard("TMDS_33")), + Subsignal("scl", Pins("V9"), IOStandard("I2C")), + Subsignal("sda", Pins("T9"), IOStandard("I2C")), + Subsignal("hpd_notif", Pins("R8"), IOStandard("LVTTL")), + ), + + ("spiflash", 0, + Subsignal("cs_n", Pins("V3")), + Subsignal("clk", Pins("R15")), + Subsignal("mosi", Pins("T13")), + Subsignal("miso", Pins("R13"), Misc("PULLUP")), + Subsignal("wp", Pins("T14")), + Subsignal("hold", Pins("V14")), + IOStandard("LVTTL"), Misc("SLEW=FAST") + ), + + ("spiflash2x", 0, + Subsignal("cs_n", Pins("V3")), + Subsignal("clk", Pins("R15")), + Subsignal("dq", Pins("T13 R13"), Misc("PULLUP")), + Subsignal("wp", Pins("T14")), + Subsignal("hold", Pins("V14")), + IOStandard("LVTTL"), Misc("SLEW=FAST") + ), + + ("spiflash4x", 0, + Subsignal("cs_n", Pins("V3")), + Subsignal("clk", Pins("R15")), + Subsignal("dq", Pins("T13 R13 T14 V14"), Misc("PULLUP")), + IOStandard("LVTTL"), Misc("SLEW=FAST") + ), + + ("mmc", 0, + Subsignal("clk", Pins("A3")), + Subsignal("cmd", Pins("B3"), Misc("PULLUP")), + Subsignal("dat", Pins("B4 A4 B2 A2"), Misc("PULLUP")), + IOStandard("SDIO") + ), + + ("mmc_spi", 0, + Subsignal("cs_n", Pins("A2"), Misc("PULLUP")), + Subsignal("clk", Pins("A3")), + Subsignal("mosi", Pins("B3")), + Subsignal("miso", Pins("B4"), Misc("PULLUP")), + IOStandard("SDIO") + ), + + ("audio", 0, + Subsignal("l", Pins("R7"), Misc("SLEW=SLOW")), + Subsignal("r", Pins("T7"), Misc("SLEW=SLOW")), + IOStandard("LVTTL"), + ), + + ("pmod", 0, + Subsignal("d", Pins("D9 C8 D6 C4 B11 C9 D8 C6")), + IOStandard("LVTTL") + ), + + ("ddram_clock", 0, + Subsignal("p", Pins("G3")), + Subsignal("n", Pins("G1")), + IOStandard("MOBILE_DDR") + ), + + ("ddram", 0, + Subsignal("a", Pins("J7 J6 H5 L7 F3 H4 H3 H6 D2 D1 F4 D3 G6")), + Subsignal("ba", Pins("F2 F1")), + Subsignal("cke", Pins("H7")), + Subsignal("ras_n", Pins("L5")), + Subsignal("cas_n", Pins("K5")), + Subsignal("we_n", Pins("E3")), + Subsignal("dq", Pins("L2 L1 K2 K1 H2 H1 J3 J1 M3 M1 N2 N1 T2 T1 U2 U1")), + Subsignal("dqs", Pins("L4 P2")), + Subsignal("dm", Pins("K3 K4")), + IOStandard("MOBILE_DDR") + ) +] + +_connectors = [ + ("A", "U18 T17 P17 P16 N16 N17 M16 L15 L17 K15 K17 J16 H15 H18 F18 D18"), + ("B", "C18 E18 G18 H16 J18 K18 K16 L18 L16 M18 N18 N15 P15 P18 T18 U17"), + ("C", "F17 F16 E16 G16 F15 G14 F14 H14 H13 J13 G13 H12 K14 K13 K12 L12"), +] + + +class Platform(XilinxPlatform): + identifier = 0x5049 + default_clk_name = "clk50" + default_clk_period = 20 + + def __init__(self): + XilinxPlatform.__init__(self, "xc6slx45-csg324-3", _io, _connectors) + self.toolchain.bitgen_opt += " -g Compress -g ConfigRate:6" + + def create_programmer(self): + return XC3SProg("papilio", "bscan_spi_lx45_csg324.bit") diff --git a/litex/gen/migen/build/platforms/rhino.py b/litex/gen/migen/build/platforms/rhino.py new file mode 100644 index 00000000..0cb99596 --- /dev/null +++ b/litex/gen/migen/build/platforms/rhino.py @@ -0,0 +1,142 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform + + +_io = [ + ("user_led", 0, Pins("Y3")), + ("user_led", 1, Pins("Y1")), + ("user_led", 2, Pins("W2")), + ("user_led", 3, Pins("W1")), + ("user_led", 4, Pins("V3")), + ("user_led", 5, Pins("V1")), + ("user_led", 6, Pins("U2")), + ("user_led", 7, Pins("U1")), + + ("clk100", 0, + Subsignal("p", Pins("B14"), IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE")), + Subsignal("n", Pins("A14"), IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE")) + ), + + ("gpio", 0, Pins("R8")), + + ("gpmc", 0, + Subsignal("clk", Pins("R26")), + Subsignal("a", Pins("N17 N18 L23 L24 N19 N20 N21 N22 P17 P19")), + Subsignal("d", Pins("N23 N24 R18 R19 P21 P22 R20 R21 P24 P26 R23 R24 T22 T23 U23 R25")), + Subsignal("we_n", Pins("W26")), + Subsignal("oe_n", Pins("AA25")), + Subsignal("ale_n", Pins("AA26")), + Subsignal("wait", Pins("AD26")), # WAIT1/BUSY0 + IOStandard("LVCMOS33")), + # Warning: CS are numbered 1-7 on ARM side and 0-6 on FPGA side. + # Numbers here are given on the FPGA side. + ("gpmc_ce_n", 0, Pins("V23"), IOStandard("LVCMOS33")), # nCS0 + ("gpmc_ce_n", 1, Pins("U25"), IOStandard("LVCMOS33")), # nCS1 + ("gpmc_ce_n", 2, Pins("W25"), IOStandard("LVCMOS33")), # nCS6 + ("gpmc_dmareq_n", 0, Pins("T24"), IOStandard("LVCMOS33")), # nCS2 + ("gpmc_dmareq_n", 1, Pins("T26"), IOStandard("LVCMOS33")), # nCS3 + ("gpmc_dmareq_n", 2, Pins("V24"), IOStandard("LVCMOS33")), # nCS4 + ("gpmc_dmareq_n", 3, Pins("V26"), IOStandard("LVCMOS33")), # nCS5 + + # FMC150 + ("fmc150_ctrl", 0, + Subsignal("spi_sclk", Pins("AE5")), + Subsignal("spi_data", Pins("AF5")), + + Subsignal("adc_sdo", Pins("U13")), + Subsignal("adc_en_n", Pins("AA15")), + Subsignal("adc_reset", Pins("V13")), + + Subsignal("cdce_sdo", Pins("AA8")), + Subsignal("cdce_en_n", Pins("Y9")), + Subsignal("cdce_reset_n", Pins("AB7")), + Subsignal("cdce_pd_n", Pins("AC6")), + Subsignal("cdce_pll_status", Pins("W7")), + Subsignal("cdce_ref_en", Pins("W8")), + + Subsignal("dac_sdo", Pins("W9")), + Subsignal("dac_en_n", Pins("W10")), + + Subsignal("mon_sdo", Pins("AC5")), + Subsignal("mon_en_n", Pins("AD6")), + Subsignal("mon_reset_n", Pins("AF6")), + Subsignal("mon_int_n", Pins("AD5")), + + Subsignal("pg_c2m", Pins("AA23"), IOStandard("LVCMOS33")) + ), + ("ti_dac", 0, # DAC3283 + Subsignal("dat_p", Pins("AA10 AA9 V11 Y11 W14 Y12 AD14 AE13"), IOStandard("LVDS_25")), + Subsignal("dat_n", Pins("AB11 AB9 V10 AA11 Y13 AA12 AF14 AF13"), IOStandard("LVDS_25")), + Subsignal("frame_p", Pins("AB13"), IOStandard("LVDS_25")), + Subsignal("frame_n", Pins("AA13"), IOStandard("LVDS_25")), + Subsignal("txenable", Pins("AB15"), IOStandard("LVCMOS25")) + ), + ("ti_adc", 0, # ADS62P49 + Subsignal("dat_a_p", Pins("AB14 Y21 W20 AB22 V18 W17 AA21")), + Subsignal("dat_a_n", Pins("AC14 AA22 Y20 AC22 W19 W18 AB21")), + Subsignal("dat_b_p", Pins("Y17 U15 AA19 W16 AA18 Y15 V14")), + Subsignal("dat_b_n", Pins("AA17 V16 AB19 Y16 AB17 AA16 V15")), + IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE") + ), + ("fmc150_clocks", 0, + Subsignal("dac_clk_p", Pins("V12"), IOStandard("LVDS_25")), + Subsignal("dac_clk_n", Pins("W12"), IOStandard("LVDS_25")), + Subsignal("adc_clk_p", Pins("AE15"), IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE")), + Subsignal("adc_clk_n", Pins("AF15"), IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE")), + Subsignal("clk_to_fpga", Pins("W24"), IOStandard("LVCMOS25")) + ), + + ("fmc150_ext_trigger", 0, Pins("U26")), + + # Vermeer radar testbed + # Switch controller + ("pca9555", 0, + Subsignal("sda", Pins("C13")), + Subsignal("scl", Pins("G8")), + IOStandard("LVCMOS33") + ), + # TX path + ("pe43602", 0, + Subsignal("d", Pins("H8")), + Subsignal("clk", Pins("B3")), + Subsignal("le", Pins("F7")), + IOStandard("LVCMOS33") + ), + ("rfmd2081", 0, + Subsignal("enx", Pins("E5")), + Subsignal("sclk", Pins("G6")), + Subsignal("sdata", Pins("F5")), + Subsignal("locked", Pins("E6")), + IOStandard("LVCMOS33") + ), + # RX path + ("lmh6521", 0, + Subsignal("scsb", Pins("C5")), + Subsignal("sclk", Pins("G10")), + Subsignal("sdi", Pins("D5")), + Subsignal("sdo", Pins("F9")), + IOStandard("LVCMOS33") + ), + ("lmh6521", 1, + Subsignal("scsb", Pins("E10")), + Subsignal("sclk", Pins("A4")), + Subsignal("sdi", Pins("B4")), + Subsignal("sdo", Pins("H10")), + IOStandard("LVCMOS33") + ), + ("rffc5071", 0, + Subsignal("enx", Pins("A2")), + Subsignal("sclk", Pins("G9")), + Subsignal("sdata", Pins("H9")), + Subsignal("locked", Pins("A3")), + IOStandard("LVCMOS33") + ) +] + + +class Platform(XilinxPlatform): + default_clk_name = "clk100" + default_clk_period = 10 + + def __init__(self): + XilinxPlatform.__init__(self, "xc6slx150t-fgg676-3", _io) diff --git a/litex/gen/migen/build/platforms/roach.py b/litex/gen/migen/build/platforms/roach.py new file mode 100644 index 00000000..19c1ccb4 --- /dev/null +++ b/litex/gen/migen/build/platforms/roach.py @@ -0,0 +1,35 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform + + +_io = [ + ("epb", 0, + Subsignal("cs_n", Pins("K13")), + Subsignal("r_w_n", Pins("AF20")), + Subsignal("be_n", Pins("AF14 AF18")), + Subsignal("oe_n", Pins("AF21")), + Subsignal("addr", Pins("AE23 AE22 AG18 AG12 AG15 AG23 AF19 AE12 AG16 AF13 AG20 AF23", + "AH17 AH15 L20 J22 H22 L15 L16 K22 K21 K16 J15")), + Subsignal("addr_gp", Pins("L21 G22 K23 K14 L14 J12")), + Subsignal("data", Pins("AF15 AE16 AE21 AD20 AF16 AE17 AE19 AD19 AG22 AH22 AH12 AG13", + "AH20 AH19 AH14 AH13")), + Subsignal("rdy", Pins("K12")), + IOStandard("LVCMOS33") + ), + ("roach_clocks", 0, + Subsignal("epb_clk", Pins("AH18"), IOStandard("LVCMOS33")), + Subsignal("sys_clk_n", Pins("H13")), + Subsignal("sys_clk_p", Pins("J14")), + Subsignal("aux0_clk_p", Pins("G15")), + Subsignal("aux0_clk_n", Pins("G16")), + Subsignal("aux1_clk_p", Pins("H14")), + Subsignal("aux1_clk_n", Pins("H15")), + Subsignal("dly_clk_n", Pins("J17")), + Subsignal("dly_clk_p", Pins("J16")), + ), +] + + +class Platform(XilinxPlatform): + def __init__(self): + XilinxPlatform.__init__(self, "xc5vsx95t-ff1136-1", _io) diff --git a/litex/gen/migen/build/platforms/sim.py b/litex/gen/migen/build/platforms/sim.py new file mode 100644 index 00000000..0e4a40d1 --- /dev/null +++ b/litex/gen/migen/build/platforms/sim.py @@ -0,0 +1,45 @@ +from migen.build.generic_platform import * +from migen.build.sim import SimPlatform + + +class SimPins(Pins): + def __init__(self, n): + Pins.__init__(self, "s "*n) + +_io = [ + ("sys_clk", 0, SimPins(1)), + ("sys_rst", 0, SimPins(1)), + ("serial", 0, + Subsignal("source_stb", SimPins(1)), + Subsignal("source_ack", SimPins(1)), + Subsignal("source_data", SimPins(8)), + + Subsignal("sink_stb", SimPins(1)), + Subsignal("sink_ack", SimPins(1)), + Subsignal("sink_data", SimPins(8)), + ), + ("eth_clocks", 0, + Subsignal("none", SimPins(1)), + ), + ("eth", 0, + Subsignal("source_stb", SimPins(1)), + Subsignal("source_ack", SimPins(1)), + Subsignal("source_data", SimPins(8)), + + Subsignal("sink_stb", SimPins(1)), + Subsignal("sink_ack", SimPins(1)), + Subsignal("sink_data", SimPins(8)), + ), +] + + +class Platform(SimPlatform): + is_sim = True + default_clk_name = "sys_clk" + default_clk_period = 1000 # on modern computers simulate at ~ 1MHz + + def __init__(self): + SimPlatform.__init__(self, "SIM", _io) + + def do_finalize(self, fragment): + pass diff --git a/litex/gen/migen/build/platforms/usrp_b100.py b/litex/gen/migen/build/platforms/usrp_b100.py new file mode 100644 index 00000000..d2541263 --- /dev/null +++ b/litex/gen/migen/build/platforms/usrp_b100.py @@ -0,0 +1,152 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform + + +_io = [ + ("clk64", 0, + Subsignal("p", Pins("R7")), + Subsignal("n", Pins("T7")), + IOStandard("LVDS_33"), + Misc("DIFF_TERM=TRUE"), + ), + + ("pps", 0, Pins("M14"), Misc("TIG")), + ("reset_n", 0, Pins("D5"), Misc("TIG")), + ("codec_reset", 0, Pins("B14")), + # recycles fpga_cfg_cclk for reset from fw + ("ext_reset", 0, Pins("R14")), + + ("i2c", 0, + Subsignal("sda", Pins("T13")), + Subsignal("scl", Pins("R13")), + ), + + ("cgen", 0, + Subsignal("st_ld", Pins("M13")), + Subsignal("st_refmon", Pins("J14")), + Subsignal("st_status", Pins("P6")), + Subsignal("ref_sel", Pins("T2")), + Subsignal("sync_b", Pins("H15")), + ), + + ("fx2_ifclk", 0, Pins("T8")), + ("fx2_gpif", 0, + Subsignal("d", Pins("P8 P9 N9 T9 R9 P11 P13 N12 " + "T3 R3 P5 N6 T6 T5 N8 P7")), + Subsignal("ctl", Pins("M7 M9 M11 P12")), + Subsignal("slwr", Pins("T4")), # rdy0 + Subsignal("slrd", Pins("R5")), # rdy1 + # Subsignal("rdy2", Pins("T10")), + # Subsignal("rdy3", Pins("N11")), + # Subsignal("cs", Pins("P12")), + Subsignal("sloe", Pins("R11")), + Subsignal("pktend", Pins("P10")), + Subsignal("adr", Pins("T11 H16")), + ), + + ("user_led", 0, Pins("P4"), Misc("TIG")), + ("user_led", 1, Pins("N4"), Misc("TIG")), + ("user_led", 2, Pins("R2"), Misc("TIG")), + + ("debug_clk", 0, Pins("K15 K14")), + ("debug", 0, Pins( + "K16 J16 C16 C15 E13 D14 D16 D15 " + "E14 F13 G13 F14 E16 F15 H13 G14 " + "G16 F16 J12 J13 L14 L16 M15 M16 " + "L13 K13 P16 N16 R15 P15 N13 N14")), + + ("adc", 0, + Subsignal("sync", Pins("D10")), + Subsignal("d", Pins("A4 B3 A3 D9 C10 A9 C9 D8 " + "C8 B8 A8 B15")), + ), + ("dac", 0, + Subsignal("blank", Pins("K1")), + Subsignal("sync", Pins("J2")), + Subsignal("d", Pins("J1 H3 J3 G2 H1 N3 M4 R1 " + "P2 P1 M1 N1 M3 L4")), + ), + ("codec_spi", 0, + Subsignal("sclk", Pins("K3")), + Subsignal("sen", Pins("D13")), + Subsignal("mosi", Pins("C13")), + Subsignal("miso", Pins("G4")), + ), + + ("aux_spi", 0, + Subsignal("sen", Pins("C12")), + Subsignal("sclk", Pins("D12")), + Subsignal("miso", Pins("J5")), + ), + ("rx_io", 0, Pins("D7 C6 A6 B6 E9 A7 C7 B10 " + "A10 C11 A11 D11 B12 A12 A14 A13")), + ("tx_io", 0, Pins("K4 L3 L2 F1 F3 G3 E3 E2 " + "E4 F4 D1 E1 D4 D3 C2 C1")), + ("rx_spi", 0, + Subsignal("miso", Pins("E6")), + Subsignal("sen", Pins("B4")), + Subsignal("mosi", Pins("A5")), + Subsignal("sclk", Pins("C5")), + ), + ("tx_spi", 0, + Subsignal("miso", Pins("J4")), + Subsignal("sen", Pins("N2")), + Subsignal("mosi", Pins("L1")), + Subsignal("sclk", Pins("G1")), + ), + + # these are just for information. do not request. + ("mystery_bus", 0, Pins("C4 E7")), + ("fpga_cfg", + Subsignal("din", Pins("T14")), + Subsignal("cclk", Pins("R14")), + Subsignal("init_b", Pins("T12")), + Subsignal("prog_b", Pins("A2")), + Subsignal("done", Pins("T15")), + ), + ("jtag", + Subsignal("tms", Pins("B2")), + Subsignal("tdo", Pins("B16")), + Subsignal("tdi", Pins("B1")), + Subsignal("tck", Pins("A15")), + ), +] + + +class Platform(XilinxPlatform): + default_clk_name = "clk64" + default_clk_period = 15.625 + + def __init__(self): + XilinxPlatform.__init__(self, "xc3s1400a-ft256-4", _io) + self.toolchain.bitgen_opt = "-g LCK_cycle:6 -g Binary:Yes -w -g UnusedPin:PullUp" + + def do_finalize(self, fragment): + XilinxPlatform.do_finalize(self, fragment) + + self.add_platform_command(""" +TIMESPEC TS_Pad2Pad = FROM PADS TO PADS 7 ns; +""") + + try: + ifclk = self.lookup_request("fx2_ifclk") + gpif = self.lookup_request("fx2_gpif") + for i, d in [(gpif.d, "in"), (gpif.d, "out"), + (gpif.ctl, "in"), (gpif.adr, "out"), + (gpif.slwr, "out"), (gpif.sloe, "out"), + (gpif.slrd, "out"), (gpif.pktend, "out")]: + if len(i) > 1: + q = "(*)" + else: + q = "" + self.add_platform_command(""" +INST "{i}%s" TNM = gpif_net_%s; +""" % (q, d), i=i) + self.add_platform_command(""" +NET "{ifclk}" TNM_NET = "GRPifclk"; +TIMESPEC "TSifclk" = PERIOD "GRPifclk" 20833 ps HIGH 50%; +TIMEGRP "gpif_net_in" OFFSET = IN 5 ns VALID 10 ns BEFORE "{ifclk}" RISING; +TIMEGRP "gpif_net_out" OFFSET = OUT 7 ns AFTER "{ifclk}" RISING; +""", ifclk=ifclk) + except ConstraintError: + pass diff --git a/litex/gen/migen/build/platforms/versa.py b/litex/gen/migen/build/platforms/versa.py new file mode 100644 index 00000000..dc8dd53d --- /dev/null +++ b/litex/gen/migen/build/platforms/versa.py @@ -0,0 +1,96 @@ +# This file is Copyright (c) 2013 Florent Kermarrec +# License: BSD + +from migen.build.generic_platform import * +from migen.build.lattice import LatticePlatform +from migen.build.lattice.programmer import LatticeProgrammer + + +_io = [ + ("clk100", 0, Pins("L5"), IOStandard("LVDS25")), + ("rst_n", 0, Pins("A21"), IOStandard("LVCMOS33")), + + ("user_led", 0, Pins("Y20"), IOStandard("LVCMOS33")), + ("user_led", 1, Pins("AA21"), IOStandard("LVCMOS33")), + ("user_led", 2, Pins("U18"), IOStandard("LVCMOS33")), + ("user_led", 3, Pins("U19"), IOStandard("LVCMOS33")), + ("user_led", 4, Pins("W19"), IOStandard("LVCMOS33")), + ("user_led", 5, Pins("V19"), IOStandard("LVCMOS33")), + ("user_led", 6, Pins("AB20"), IOStandard("LVCMOS33")), + ("user_led", 7, Pins("AA20"), IOStandard("LVCMOS33")), + + ("user_dip_btn", 0, Pins("J7"), IOStandard("LVCMOS15")), + ("user_dip_btn", 1, Pins("J6"), IOStandard("LVCMOS15")), + ("user_dip_btn", 2, Pins("H2"), IOStandard("LVCMOS15")), + ("user_dip_btn", 3, Pins("H3"), IOStandard("LVCMOS15")), + ("user_dip_btn", 4, Pins("J3"), IOStandard("LVCMOS15")), + ("user_dip_btn", 5, Pins("K3"), IOStandard("LVCMOS15")), + ("user_dip_btn", 6, Pins("J2"), IOStandard("LVCMOS15")), + ("user_dip_btn", 7, Pins("J1"), IOStandard("LVCMOS15")), + + ("serial", 0, + Subsignal("tx", Pins("B11"), IOStandard("LVCMOS33")), # X4 IO0 + Subsignal("rx", Pins("B12"), IOStandard("LVCMOS33")), # X4 IO1 + ), + + ("eth_clocks", 0, + Subsignal("tx", Pins("C12")), + Subsignal("gtx", Pins("M2")), + Subsignal("rx", Pins("L4")), + IOStandard("LVCMOS33") + ), + ("eth", 0, + Subsignal("rst_n", Pins("L3")), + Subsignal("mdio", Pins("L2")), + Subsignal("mdc", Pins("V4")), + Subsignal("dv", Pins("M1")), + Subsignal("rx_er", Pins("M4")), + Subsignal("rx_data", Pins("M5 N1 N6 P6 T2 R2 P5 P3")), + Subsignal("tx_en", Pins("V3")), + Subsignal("tx_data", Pins("V1 U1 R3 P1 N5 N3 N4 N2")), + Subsignal("col", Pins("R1")), + Subsignal("crs", Pins("P4")), + IOStandard("LVCMOS33") + ), + + ("eth_clocks", 1, + Subsignal("tx", Pins("M21")), + Subsignal("gtx", Pins("M19")), + Subsignal("rx", Pins("N19")), + IOStandard("LVCMOS33") + ), + ("eth", 1, + Subsignal("rst_n", Pins("R21")), + Subsignal("mdio", Pins("U16")), + Subsignal("mdc", Pins("Y18")), + Subsignal("dv", Pins("U15")), + Subsignal("rx_er", Pins("V20")), + Subsignal("rx_data", Pins("AB17 AA17 R19 V21 T17 R18 W21 Y21")), + Subsignal("tx_en", Pins("V22")), + Subsignal("tx_data", Pins("W22 R16 P17 Y22 T21 U22 P20 U20")), + Subsignal("col", Pins("N18")), + Subsignal("crs", Pins("P19")), + IOStandard("LVCMOS33") + ), +] + + +class Platform(LatticePlatform): + default_clk_name = "clk100" + default_clk_period = 10 + + def __init__(self): + LatticePlatform.__init__(self, "LFE3-35EA-6FN484C", _io) + + def do_finalize(self, fragment): + LatticePlatform.do_finalize(self, fragment) + try: + self.add_period_constraint(self.lookup_request("eth_clocks", 0).rx, 8.0) + except ConstraintError: + pass + try: + self.add_period_constraint(self.lookup_request("eth_clocks", 1).rx, 8.0) + except ConstraintError: + pass + def create_programmer(self): + return LatticeProgrammer() diff --git a/litex/gen/migen/build/platforms/zedboard.py b/litex/gen/migen/build/platforms/zedboard.py new file mode 100644 index 00000000..b5d3dcc2 --- /dev/null +++ b/litex/gen/migen/build/platforms/zedboard.py @@ -0,0 +1,145 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform + + +# Bank 34 and 35 voltage depend on J18 jumper setting +_io = [ + ("clk100", 0, Pins("Y9"), IOStandard("LVCMOS33")), + + ("user_btn", 0, Pins("P16"), IOStandard("LVCMOS18")), # center + ("user_btn", 1, Pins("R16"), IOStandard("LVCMOS18")), # down + ("user_btn", 2, Pins("N15"), IOStandard("LVCMOS18")), # left + ("user_btn", 3, Pins("R18"), IOStandard("LVCMOS18")), # right + ("user_btn", 4, Pins("T18"), IOStandard("LVCMOS18")), # up + + ("user_sw", 0, Pins("F22"), IOStandard("LVCMOS18")), + ("user_sw", 1, Pins("G22"), IOStandard("LVCMOS18")), + ("user_sw", 2, Pins("H22"), IOStandard("LVCMOS18")), + ("user_sw", 3, Pins("F21"), IOStandard("LVCMOS18")), + ("user_sw", 4, Pins("H19"), IOStandard("LVCMOS18")), + ("user_sw", 5, Pins("H18"), IOStandard("LVCMOS18")), + ("user_sw", 6, Pins("H17"), IOStandard("LVCMOS18")), + ("user_sw", 7, Pins("M15"), IOStandard("LVCMOS18")), + + ("user_led", 0, Pins("T22"), IOStandard("LVCMOS33")), + ("user_led", 1, Pins("T21"), IOStandard("LVCMOS33")), + ("user_led", 2, Pins("U22"), IOStandard("LVCMOS33")), + ("user_led", 3, Pins("U21"), IOStandard("LVCMOS33")), + ("user_led", 4, Pins("V22"), IOStandard("LVCMOS33")), + ("user_led", 5, Pins("W22"), IOStandard("LVCMOS33")), + ("user_led", 6, Pins("U19"), IOStandard("LVCMOS33")), + ("user_led", 7, Pins("U14"), IOStandard("LVCMOS33")), + + # A + ("pmod", 0, Pins("Y11 AA11 Y10 AA9 AB11 AB10 AB9 AA8"), + IOStandard("LVCMOS33")), + # B + ("pmod", 1, Pins("W12 W11 V10 W8 V12 W10 V9 V8"), + IOStandard("LVCMOS33")), + # C + ("pmod", 2, + Subsignal("n", Pins("AB6 AA4 T6 U4")), + Subsignal("p", Pins("AB7 Y4 R6 T4")), + IOStandard("LVCMOS33")), + # D + ("pmod", 3, + Subsignal("n", Pins("W7 V4 W5 U5")), + Subsignal("p", Pins("V7 V5 W6 U6")), + IOStandard("LVCMOS33")), + + ("audio", 0, + Subsignal("adr", Pins("AB1 Y5")), + Subsignal("gpio", Pins("Y8 AA7 AA6 Y6")), + Subsignal("mclk", Pins("AB2")), + Subsignal("sck", Pins("AB4")), + Subsignal("sda", Pins("AB5")), + IOStandard("LVCMOS33")), + + ("oled", 0, + Subsignal("dc", Pins("U10")), + Subsignal("res", Pins("U9")), + Subsignal("sclk", Pins("AB12")), + Subsignal("sdin", Pins("AA12")), + Subsignal("vbat", Pins("U11")), + Subsignal("vdd", Pins("U12")), + IOStandard("LVCMOS33")), + + ("hdmi", 0, + Subsignal("clk", Pins("W18")), + Subsignal("d", Pins( + "Y13 AA13 AA14 Y14 AB15 AB16 AA16 AB17 " + "AA17 Y15 W13 W15 V15 U17 V14 V13")), + Subsignal("de", Pins("U16")), + Subsignal("hsync", Pins("V17")), + Subsignal("vsync", Pins("W17")), + Subsignal("int", Pins("W16")), + Subsignal("scl", Pins("AA18")), + Subsignal("sda", Pins("Y16")), + Subsignal("spdif", Pins("U15")), + Subsignal("spdifo", Pins("Y18")), + IOStandard("LVCMOS33")), + + ("netic16", 0, + Subsignal("w20", Pins("W20")), + Subsignal("w21", Pins("W21")), + IOStandard("LVCMOS33")), + + ("vga", 0, + Subsignal("r", Pins("V20 U20 V19 V18")), + Subsignal("g", Pins("AB22 AA22 AB21 AA21")), + Subsignal("b", Pins("Y21 Y20 AB20 AB19")), + Subsignal("hsync_n", Pins("AA19")), + Subsignal("vsync_n", Pins("Y19")), + IOStandard("LVCMOS33")), + + ("usb_otg", 0, + Subsignal("vbusoc", Pins("L16")), + Subsignal("reset_n", Pins("G17")), + IOStandard("LVCMOS18")), + + ("pudc_b", 0, Pins("K16"), IOStandard("LVCMOS18")), + + ("xadc", 0, + Subsignal("gio", Pins("H15 R15 K15 J15")), + Subsignal("ad0_n", Pins("E16")), + Subsignal("ad0_p", Pins("F16")), + Subsignal("ad8_n", Pins("D17")), + Subsignal("ad8_p", Pins("D16")), + IOStandard("LVCMOS18")), + + ("fmc_clocks", 0, + Subsignal("clk0_n", Pins("L19")), + Subsignal("clk0_p", Pins("L18")), + Subsignal("clk1_n", Pins("C19")), + Subsignal("clk1_p", Pins("D18")), + IOStandard("LVCMOS18")), + + ("fmc", 0, + Subsignal("scl", Pins("R7")), + Subsignal("sda", Pins("U7")), + + Subsignal("prsnt", Pins("AB14")), + + # 0, 1, 17, 18 can be clock signals + Subsignal("la_n", Pins( + "M20 N20 P18 P22 M22 K18 L22 T17 " + "J22 R21 T19 N18 P21 M17 K20 J17 " + "K21 B20 C20 G16 G21 E20 F19 D15 " + "A19 C22 E18 D21 A17 C18 B15 B17 " + "A22 B22")), + Subsignal("la_p", Pins( + "M19 N19 P17 N22 M21 J18 L21 T16 " + "J21 R20 R19 N17 P20 L17 K19 J16 " + "J20 B19 D20 G15 G20 E19 G19 E15 " + "A18 D22 F18 E21 A16 C17 C15 B16 " + "A21 B21")), + IOStandard("LVCMOS18")), +] + + +class Platform(XilinxPlatform): + default_clk_name = "clk100" + default_clk_period = 10 + + def __init__(self): + XilinxPlatform.__init__(self, "xc7z020-clg484-1", _io) diff --git a/litex/gen/migen/build/platforms/ztex_115d.py b/litex/gen/migen/build/platforms/ztex_115d.py new file mode 100644 index 00000000..253a121c --- /dev/null +++ b/litex/gen/migen/build/platforms/ztex_115d.py @@ -0,0 +1,110 @@ +from migen.build.generic_platform import * +from migen.build.xilinx import XilinxPlatform + + +_io = [ + ("clk_fx", 0, Pins("L22"), IOStandard("LVCMOS33")), + ("clk_if", 0, Pins("K20"), IOStandard("LVCMOS33")), + ("rst", 0, Pins("A18")), + # PROG_B and DONE: AA1 U16 + + ("fx2", 0, + Subsignal("sloe", Pins("U15"), Drive(12)), # M1 + Subsignal("slrd", Pins("N22"), Drive(12)), + Subsignal("slwr", Pins("M22"), Drive(12)), + Subsignal("pktend", Pins("AB5"), Drive(12)), # CSO + Subsignal("fifoadr", Pins("W17 Y18"), Drive(12)), # CCLK M0 + Subsignal("cont", Pins("G20")), + Subsignal("fd", Pins("Y17 V13 W13 AA8 AB8 W6 Y6 Y9 " + "V21 V22 U20 U22 R20 R22 P18 P19")), + Subsignal("flag", Pins("F20 F19 F18 AB17")), # - - - CSI/MOSI + Subsignal("rdy25", Pins("M21 K21 K22 J21")), + Subsignal("ctl35", Pins("D19 E20 N20")), + Subsignal("int45", Pins("C18 V17")), + Subsignal("pc", Pins("G20 T10 V5 AB9 G19 H20 H19 H18")), + # - DOUT/BUSY INIT_B RDWR_B DO CS CLK DI + IOStandard("LVCMOS33")), + + ("mm", 0, + Subsignal("a", Pins("M20 M19 M18 N19 T19 T21 T22 R19 ", + "P20 P21 P22 J22 H21 H22 G22 F21")), + Subsignal("d", Pins("D20 C20 C19 B21 B20 J19 K19 L19"), Drive(2)), + Subsignal("wr_n", Pins("C22")), + Subsignal("rd_n", Pins("D21")), + Subsignal("psen_n", Pins("D22")), + IOStandard("LVCMOS33")), + + ("serial", 0, + Subsignal("tx", Pins("B22"), Misc("SLEW=QUIETIO")), + Subsignal("rx", Pins("A21"), Misc("PULLDOWN")), + IOStandard("LVCMOS33")), + + ("ddram_clock", 0, + Subsignal("p", Pins("F2"), Misc("OUT_TERM=UNTUNED_50")), + Subsignal("n", Pins("F1"), Misc("OUT_TERM=UNTUNED_50")), + IOStandard("SSTL18_II")), + + ("ddram", 0, + Subsignal("dqs", Pins("L3 T2"), IOStandard("SSTL18_II"), # DIFF_ + Misc("IN_TERM=NONE")), + Subsignal("dqs_n", Pins("L1 T1"), IOStandard("SSTL18_II"), # DIFF_ + Misc("IN_TERM=NONE")), + Subsignal("dm", Pins("H1 H2"), Misc("OUT_TERM=UNTUNED_50")), + Subsignal("dq", Pins("M1 M2 J1 K2 J3 K1 N3 N1 " + "U1 U3 P1 R3 P2 R1 V2 V1"), Misc("IN_TERM=NONE")), + Subsignal("ras_n", Pins("N4"), Misc("OUT_TERM=UNTUNED_50")), + Subsignal("cas_n", Pins("P3"), Misc("OUT_TERM=UNTUNED_50")), + Subsignal("a", Pins("M5 K6 B1 J4 L4 K3 M4 K5 G3 G1 K4 C3 C1"), + Misc("OUT_TERM=UNTUNED_50")), + Subsignal("ba", Pins("E3 E1 D1"), Misc("OUT_TERM=UNTUNED_50")), + Subsignal("cke", Pins("J6"), Misc("OUT_TERM=UNTUNED_50")), + Subsignal("cs_n", Pins("H6")), # NC! + Subsignal("odt", Pins("M3"), Misc("OUT_TERM=UNTUNED_50")), + Subsignal("we_n", Pins("D2")), + Subsignal("rzq", Pins("AA2")), + Subsignal("zio", Pins("Y2")), + IOStandard("SSTL18_II")), + + ("i2c", 0, + Subsignal("scl", Pins("F22")), + Subsignal("sda", Pins("E22")), + IOStandard("LVCMOS33")), + + ("sd", 0, + Subsignal("sck", Pins("H11")), + Subsignal("d3", Pins("H14")), + Subsignal("d", Pins("P10")), + Subsignal("d1", Pins("T18")), + Subsignal("d2", Pins("R17")), + Subsignal("cmd", Pins("H13")), + IOStandard("LVCMOS33")), + +] + + +class Platform(XilinxPlatform): + default_clk_name = "clk_if" + default_clk_period = 20 + + def __init__(self): + XilinxPlatform.__init__(self, "xc6slx150-3csg484", _io) + self.add_platform_command(""" +CONFIG VCCAUX = "2.5"; +""") + + def do_finalize(self, fragment): + XilinxPlatform.do_finalize(self, fragment) + + try: + clk_if = self.lookup_request("clk_if") + clk_fx = self.lookup_request("clk_fx") + self.add_platform_command(""" +NET "{clk_if}" TNM_NET = "GRPclk_if"; +NET "{clk_fx}" TNM_NET = "GRPclk_fx"; +TIMESPEC "TSclk_fx" = PERIOD "GRPclk_fx" 20.83333 ns HIGH 50%; +TIMESPEC "TSclk_if" = PERIOD "GRPclk_if" 20 ns HIGH 50%; +TIMESPEC "TSclk_fx2if" = FROM "GRPclk_fx" TO "GRPclk_if" 3 ns DATAPATHONLY; +TIMESPEC "TSclk_if2fx" = FROM "GRPclk_if" TO "GRPclk_fx" 3 ns DATAPATHONLY; +""", clk_if=clk_if, clk_fx=clk_fx) + except ConstraintError: + pass diff --git a/litex/gen/migen/build/sim/__init__.py b/litex/gen/migen/build/sim/__init__.py new file mode 100644 index 00000000..adbba1c4 --- /dev/null +++ b/litex/gen/migen/build/sim/__init__.py @@ -0,0 +1 @@ +from migen.build.sim.platform import SimPlatform diff --git a/litex/gen/migen/build/sim/common.py b/litex/gen/migen/build/sim/common.py new file mode 100644 index 00000000..38741fd4 --- /dev/null +++ b/litex/gen/migen/build/sim/common.py @@ -0,0 +1 @@ +sim_special_overrides = {} diff --git a/litex/gen/migen/build/sim/dut_tb.cpp b/litex/gen/migen/build/sim/dut_tb.cpp new file mode 100644 index 00000000..0e8dac7e --- /dev/null +++ b/litex/gen/migen/build/sim/dut_tb.cpp @@ -0,0 +1,399 @@ +// This file is Copyright (c) 2015 Florent Kermarrec +// License: BSD +#include "Vdut.h" +#include "verilated.h" +#include "verilated_vcd_c.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* ios */ + +#ifdef SERIAL_SOURCE_STB +#define WITH_SERIAL +#endif + +#ifdef ETH_SOURCE_STB +#define WITH_ETH +#endif + +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define MIN(a,b) (((a)<(b))?(a):(b)) + +int trace = 0; + +vluint64_t main_time = 0; +double sc_time_stamp() +{ + return main_time; +} + +/* Sim struct */ +struct sim { + bool run; + + unsigned int tick; + clock_t start; + clock_t end; + float speed; + +#ifdef WITH_SERIAL_PTY + char serial_dev[64]; + int serial_fd; + unsigned char serial_rx_data; + unsigned char serial_tx_data; +#endif +#ifdef WITH_ETH + const char *eth_dev; + const char *eth_tap; + int eth_fd; + unsigned char eth_txbuffer[2048]; + unsigned char eth_rxbuffer[2048]; + int eth_txbuffer_len; + int eth_rxbuffer_len; + int eth_rxbuffer_pos; + int eth_last_source_stb; +#endif +}; + +/* Serial functions */ +#ifndef WITH_SERIAL_PTY +struct termios orig_termios; + +void reset_terminal_mode(void) +{ + tcsetattr(0, TCSANOW, &orig_termios); +} + +void set_conio_terminal_mode(void) +{ + struct termios new_termios; + + /* take two copies - one for now, one for later */ + tcgetattr(0, &orig_termios); + memcpy(&new_termios, &orig_termios, sizeof(new_termios)); + + /* register cleanup handler, and set the new terminal mode */ + atexit(reset_terminal_mode); + cfmakeraw(&new_termios); + tcsetattr(0, TCSANOW, &new_termios); +} + +int kbhit(void) +{ + struct timeval tv = { 0L, 0L }; + fd_set fds; + FD_ZERO(&fds); + FD_SET(0, &fds); + return select(1, &fds, NULL, NULL, &tv); +} + +int getch(void) +{ + int r; + unsigned char c; + if((r = read(0, &c, sizeof(c))) < 0) { + return r; + } else { + return c; + } +} +#endif + +/* Ethernet functions */ +/* create tap: + openvpn --mktun --dev tap0 + ifconfig tap0 192.168.0.14 up + mknod /dev/net/tap0 c 10 200 + delete tap: + openvpn --rmtun --dev tap0 */ +#ifdef WITH_ETH +void eth_init(struct sim *s, const char *dev, const char*tap) +{ + s->eth_txbuffer_len = 0; + s->eth_rxbuffer_len = 0; + s->eth_rxbuffer_pos = 0; + s->eth_last_source_stb = 0; + s->eth_dev = dev; + s->eth_tap = tap; +} + +void eth_open(struct sim *s) +{ + + struct ifreq ifr; + s->eth_fd = open (s->eth_dev, O_RDWR); + if(s->eth_fd < 0) { + fprintf(stderr, " Could not open dev %s\n", s->eth_dev); + return; + } + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + strncpy(ifr.ifr_name, s->eth_tap, IFNAMSIZ); + + if(ioctl(s->eth_fd, TUNSETIFF, (void *) &ifr) < 0) { + fprintf(stderr, " Could not set %s\n", s->eth_tap); + close(s->eth_fd); + } + return; +} + +int eth_close(struct sim *s) +{ + if(s->eth_fd < 0) + close(s->eth_fd); +} + +void eth_write(struct sim *s, unsigned char *buf, int len) +{ + write(s->eth_fd, buf, len); +} + +int eth_read(struct sim *s, unsigned char *buf) +{ + + struct pollfd fds[1]; + int n; + int len; + + fds[0].fd = s->eth_fd; + fds[0].events = POLLIN; + + n = poll(fds, 1, 0); + if((n > 0) && ((fds[0].revents & POLLIN) == POLLIN)) { + len = read(s->eth_fd, buf, 1532); + } else { + len = 0; + } + return len; +} +#endif + +Vdut* dut; +VerilatedVcdC* tfp; + +#ifndef WITH_SERIAL_PTY +int console_service(struct sim *s) +{ + /* fpga --> console */ + SERIAL_SOURCE_ACK = 1; + if(SERIAL_SOURCE_STB == 1) { + if(SERIAL_SOURCE_DATA == '\n') + putchar('\r'); + putchar(SERIAL_SOURCE_DATA); + fflush(stdout); + } + + /* console --> fpga */ + SERIAL_SINK_STB = 0; + if(s->tick%(1000) == 0) { + if(kbhit()) { + char c = getch(); + if(c == 27 && !kbhit()) { + printf("\r\n"); + return -1; + } else { + SERIAL_SINK_STB = 1; + SERIAL_SINK_DATA = c; + } + } + } + return 0; +} +#else +void console_init(struct sim *s) +{ + FILE *f; + f = fopen("/tmp/simserial","r"); + fscanf(f, "%[^\n]", s->serial_dev); + fclose(f); + return; +} + +void console_open(struct sim *s) +{ + s->serial_fd = open(s->serial_dev, O_RDWR); + if(s->serial_fd < 0) { + fprintf(stderr, " Could not open dev %s\n", s->serial_dev); + return; + } + return; +} + +int console_close(struct sim *s) +{ + if(s->serial_fd < 0) + close(s->serial_fd); +} + +void console_write(struct sim *s, unsigned char *buf, int len) +{ + write(s->serial_fd, buf, len); +} + +int console_read(struct sim *s, unsigned char *buf) +{ + struct pollfd fds[1]; + int n; + int len; + + fds[0].fd = s->serial_fd; + fds[0].events = POLLIN; + + n = poll(fds, 1, 0); + if((n > 0) && ((fds[0].revents & POLLIN) == POLLIN)) { + len = read(s->serial_fd, buf, 1); + } else { + len = 0; + } + return len; +} + +int console_service(struct sim *s) +{ + /* fpga --> console */ + SERIAL_SOURCE_ACK = 1; + if(SERIAL_SOURCE_STB == 1) { + s->serial_tx_data = SERIAL_SOURCE_DATA; + console_write(s, &(s->serial_tx_data), 1); + } + + /* console --> fpga */ + SERIAL_SINK_STB = 0; + if(console_read(s, &(s->serial_rx_data))) + { + SERIAL_SINK_STB = 1; + SERIAL_SINK_DATA = s->serial_rx_data; + } + return 0; +} +#endif + +#ifdef WITH_ETH +int ethernet_service(struct sim *s) { + /* fpga --> tap */ + ETH_SOURCE_ACK = 1; + if(ETH_SOURCE_STB == 1) { + s->eth_txbuffer[s->eth_txbuffer_len] = ETH_SOURCE_DATA; + s->eth_txbuffer_len++; + } else { + if(s->eth_last_source_stb) { + eth_write(s, s->eth_txbuffer, s->eth_txbuffer_len); + s->eth_txbuffer_len = 0; + } + } + s->eth_last_source_stb = ETH_SOURCE_STB; + + /* tap --> fpga */ + if(s->eth_rxbuffer_len == 0) { + ETH_SINK_STB = 0; + s->eth_rxbuffer_pos = 0; + s->eth_rxbuffer_len = eth_read(s, s->eth_rxbuffer); + } else { + if(s->eth_rxbuffer_pos < MAX(s->eth_rxbuffer_len, 60)) { + ETH_SINK_STB = 1; + ETH_SINK_DATA = s->eth_rxbuffer[s->eth_rxbuffer_pos]; + s->eth_rxbuffer_pos++; + } else { + ETH_SINK_STB = 0; + s->eth_rxbuffer_len = 0; + memset(s->eth_rxbuffer, 0, 1532); + } + } +} +#endif + +void sim_tick(struct sim *s) +{ + SYS_CLK = s->tick%2; + dut->eval(); + if(trace) + tfp->dump(s->tick); + s->tick++; +} + +void sim_init(struct sim *s) +{ + int i; + s->tick = 0; +#ifdef SYS_RST + SYS_RST = 1; + SYS_CLK = 0; + for (i=0; i<8; i++) + sim_tick(s); + SYS_RST = 0; +#endif + s->start = clock(); +} + +int main(int argc, char **argv, char **env) +{ + float speed; + +#ifndef WITH_SERIAL_PTY + set_conio_terminal_mode(); +#endif + + Verilated::commandArgs(argc, argv); + dut = new Vdut; + + Verilated::traceEverOn(true); + tfp = new VerilatedVcdC; + dut->trace(tfp, 99); + tfp->open("dut.vcd"); + + struct sim s; + sim_init(&s); + +#ifdef WITH_SERIAL_PTY + console_init(&s); + console_open(&s); +#endif + +#ifdef WITH_ETH + eth_init(&s, "/dev/net/tap0", "tap0"); // XXX get this from /tmp/simethernet + eth_open(&s); +#endif + + s.run = true; + while(s.run) { + sim_tick(&s); + if(SYS_CLK) { +#ifdef WITH_SERIAL + if(console_service(&s) != 0) + s.run = false; +#endif +#ifdef WITH_ETH + ethernet_service(&s); +#endif + } + } + s.end = clock(); + + speed = (s.tick/2)/((s.end-s.start)/CLOCKS_PER_SEC); + + printf("average speed: %3.3f MHz\n\r", speed/1000000); + + tfp->close(); + + +#ifdef WITH_SERIAL_PTY + console_close(&s); +#endif +#ifdef WITH_ETH + eth_close(&s); +#endif + + exit(0); +} diff --git a/litex/gen/migen/build/sim/platform.py b/litex/gen/migen/build/sim/platform.py new file mode 100644 index 00000000..0c5e17d2 --- /dev/null +++ b/litex/gen/migen/build/sim/platform.py @@ -0,0 +1,20 @@ +from migen.build.generic_platform import GenericPlatform +from migen.build.sim import common, verilator + + +class SimPlatform(GenericPlatform): + def __init__(self, *args, toolchain="verilator", **kwargs): + GenericPlatform.__init__(self, *args, **kwargs) + if toolchain == "verilator": + self.toolchain = verilator.SimVerilatorToolchain() + else: + raise ValueError("Unknown toolchain") + + def get_verilog(self, *args, special_overrides=dict(), **kwargs): + so = dict(common.sim_special_overrides) + so.update(special_overrides) + return GenericPlatform.get_verilog(self, *args, special_overrides=so, **kwargs) + + def build(self, *args, **kwargs): + return self.toolchain.build(self, *args, **kwargs) + diff --git a/litex/gen/migen/build/sim/verilator.py b/litex/gen/migen/build/sim/verilator.py new file mode 100644 index 00000000..4e635528 --- /dev/null +++ b/litex/gen/migen/build/sim/verilator.py @@ -0,0 +1,148 @@ +# This file is Copyright (c) 2015 Florent Kermarrec +# License: BSD + +import os +import subprocess + +from migen.fhdl.structure import _Fragment +from migen.build import tools +from migen.build.generic_platform import * + + +def _build_tb(platform, vns, serial, template): + def io_name(resource, subsignal=None): + res = platform.lookup_request(resource) + if subsignal is not None: + res = getattr(res, subsignal) + return vns.get_name(res) + + ios = """ +#define SYS_CLK dut->{sys_clk} +""".format(sys_clk=io_name("sys_clk")) + + if serial == "pty": + ios += "#define WITH_SERIAL_PTY" + elif serial == "console": + pass + else: + raise ValueError + try: + ios += """ +#define SERIAL_SOURCE_STB dut->{serial_source_stb} +#define SERIAL_SOURCE_ACK dut->{serial_source_ack} +#define SERIAL_SOURCE_DATA dut->{serial_source_data} + +#define SERIAL_SINK_STB dut->{serial_sink_stb} +#define SERIAL_SINK_ACK dut->{serial_sink_ack} +#define SERIAL_SINK_DATA dut->{serial_sink_data} +""".format( + serial_source_stb=io_name("serial", "source_stb"), + serial_source_ack=io_name("serial", "source_ack"), + serial_source_data=io_name("serial", "source_data"), + + serial_sink_stb=io_name("serial", "sink_stb"), + serial_sink_ack=io_name("serial", "sink_ack"), + serial_sink_data=io_name("serial", "sink_data"), + ) + except: + pass + + try: + ios += """ +#define ETH_SOURCE_STB dut->{eth_source_stb} +#define ETH_SOURCE_ACK dut->{eth_source_ack} +#define ETH_SOURCE_DATA dut->{eth_source_data} + +#define ETH_SINK_STB dut->{eth_sink_stb} +#define ETH_SINK_ACK dut->{eth_sink_ack} +#define ETH_SINK_DATA dut->{eth_sink_data} +""".format( + eth_source_stb=io_name("eth", "source_stb"), + eth_source_ack=io_name("eth", "source_ack"), + eth_source_data=io_name("eth", "source_data"), + + eth_sink_stb=io_name("eth", "sink_stb"), + eth_sink_ack=io_name("eth", "sink_ack"), + eth_sink_data=io_name("eth", "sink_data"), + ) + except: + pass + + content = "" + f = open(template, "r") + done = False + for l in f: + content += l + if "/* ios */" in l and not done: + content += ios + done = True + + f.close() + tools.write_to_file("dut_tb.cpp", content) + + +def _build_sim(platform, vns, build_name, include_paths, sim_path, serial, verbose): + include = "" + for path in include_paths: + include += "-I"+path+" " + + build_script_contents = """# Autogenerated by Migen + rm -rf obj_dir/ +verilator {disable_warnings} -O3 --cc dut.v --exe dut_tb.cpp -LDFLAGS "-lpthread" -trace {include} +make -j -C obj_dir/ -f Vdut.mk Vdut + +""".format( + disable_warnings="-Wno-fatal", + include=include) + build_script_file = "build_" + build_name + ".sh" + tools.write_to_file(build_script_file, build_script_contents, force_unix=True) + + _build_tb(platform, vns, serial, os.path.join("..", sim_path, "dut_tb.cpp")) + if verbose: + r = subprocess.call(["bash", build_script_file]) + else: + r = subprocess.call(["bash", build_script_file], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) + if r != 0: + raise OSError("Subprocess failed") + + +def _run_sim(build_name): + run_script_contents = """obj_dir/Vdut +""" + run_script_file = "run_" + build_name + ".sh" + tools.write_to_file(run_script_file, run_script_contents, force_unix=True) + r = subprocess.call(["bash", run_script_file]) + if r != 0: + raise OSError("Subprocess failed") + + +class SimVerilatorToolchain: + # XXX fir sim_path + def build(self, platform, fragment, build_dir="build", build_name="top", + sim_path="../migen/migen/build/sim/", serial="console", + run=True, verbose=False): + tools.mkdir_noerror(build_dir) + os.chdir(build_dir) + + if not isinstance(fragment, _Fragment): + fragment = fragment.get_fragment() + platform.finalize(fragment) + + v_output = platform.get_verilog(fragment) + named_sc, named_pc = platform.resolve_signals(v_output.ns) + v_output.write("dut.v") + + include_paths = [] + for source in platform.sources: + path = os.path.dirname(source[0]).replace("\\", "\/") + if path not in include_paths: + include_paths.append(path) + include_paths += platform.verilog_include_paths + _build_sim(platform, v_output.ns, build_name, include_paths, sim_path, serial, verbose) + + if run: + _run_sim(build_name) + + os.chdir("..") + + return v_output.ns diff --git a/litex/gen/migen/build/tools.py b/litex/gen/migen/build/tools.py new file mode 100644 index 00000000..9e0880d5 --- /dev/null +++ b/litex/gen/migen/build/tools.py @@ -0,0 +1,42 @@ +import os +import struct +from distutils.version import StrictVersion + + +def mkdir_noerror(d): + try: + os.mkdir(d) + except OSError: + pass + + +def language_by_filename(name): + extension = name.rsplit(".")[-1] + if extension in ["v", "vh", "vo"]: + return "verilog" + if extension in ["vhd", "vhdl", "vho"]: + return "vhdl" + return None + + +def write_to_file(filename, contents, force_unix=False): + newline = None + if force_unix: + newline = "\n" + with open(filename, "w", newline=newline) as f: + f.write(contents) + + +def arch_bits(): + return struct.calcsize("P")*8 + + +def versions(path): + for n in os.listdir(path): + full = os.path.join(path, n) + if not os.path.isdir(full): + continue + try: + yield StrictVersion(n) + except ValueError: + continue diff --git a/litex/gen/migen/build/xilinx/__init__.py b/litex/gen/migen/build/xilinx/__init__.py new file mode 100644 index 00000000..4120abb8 --- /dev/null +++ b/litex/gen/migen/build/xilinx/__init__.py @@ -0,0 +1,2 @@ +from migen.build.xilinx.platform import XilinxPlatform +from migen.build.xilinx.programmer import UrJTAG, XC3SProg, FpgaProg, VivadoProgrammer, iMPACT, Adept diff --git a/litex/gen/migen/build/xilinx/common.py b/litex/gen/migen/build/xilinx/common.py new file mode 100644 index 00000000..52f8ca97 --- /dev/null +++ b/litex/gen/migen/build/xilinx/common.py @@ -0,0 +1,150 @@ +import os +import sys +from distutils.version import StrictVersion + +from migen.fhdl.structure import * +from migen.fhdl.specials import Instance +from migen.fhdl.module import Module +from migen.fhdl.specials import SynthesisDirective +from migen.genlib.cdc import * +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.genlib.io import * + +from migen.build import tools + + +def settings(path, ver=None, sub=None): + vers = list(tools.versions(path)) + if ver is None: + ver = max(vers) + else: + ver = StrictVersion(ver) + assert ver in vers + + full = os.path.join(path, str(ver)) + if sub: + full = os.path.join(full, sub) + + search = [64, 32] + if tools.arch_bits() == 32: + search.reverse() + + if sys.platform == "win32" or sys.platform == "cygwin": + script_ext = "bat" + else: + script_ext = "sh" + + for b in search: + settings = os.path.join(full, "settings{0}.{1}".format(b, script_ext)) + if os.path.exists(settings): + return settings + + raise OSError("no settings file found") + + +class XilinxNoRetimingImpl(Module): + def __init__(self, reg): + self.specials += SynthesisDirective("attribute register_balancing of {r} is no", r=reg) + + +class XilinxNoRetiming: + @staticmethod + def lower(dr): + return XilinxNoRetimingImpl(dr.reg) + + +class XilinxMultiRegImpl(MultiRegImpl): + def __init__(self, *args, **kwargs): + MultiRegImpl.__init__(self, *args, **kwargs) + self.specials += [SynthesisDirective("attribute shreg_extract of {r} is no", r=r) + for r in self.regs] + + +class XilinxMultiReg: + @staticmethod + def lower(dr): + return XilinxMultiRegImpl(dr.i, dr.o, dr.odomain, dr.n) + + +class XilinxAsyncResetSynchronizerImpl(Module): + def __init__(self, cd, async_reset): + rst1 = Signal() + self.specials += [ + Instance("FDPE", p_INIT=1, i_D=0, i_PRE=async_reset, + i_CE=1, i_C=cd.clk, o_Q=rst1), + Instance("FDPE", p_INIT=1, i_D=rst1, i_PRE=async_reset, + i_CE=1, i_C=cd.clk, o_Q=cd.rst) + ] + + +class XilinxAsyncResetSynchronizer: + @staticmethod + def lower(dr): + return XilinxAsyncResetSynchronizerImpl(dr.cd, dr.async_reset) + + +class XilinxDifferentialInputImpl(Module): + def __init__(self, i_p, i_n, o): + self.specials += Instance("IBUFDS", i_I=i_p, i_IB=i_n, o_O=o) + + +class XilinxDifferentialInput: + @staticmethod + def lower(dr): + return XilinxDifferentialInputImpl(dr.i_p, dr.i_n, dr.o) + + +class XilinxDifferentialOutputImpl(Module): + def __init__(self, i, o_p, o_n): + self.specials += Instance("OBUFDS", i_I=i, o_O=o_p, o_OB=o_n) + + +class XilinxDifferentialOutput: + @staticmethod + def lower(dr): + return XilinxDifferentialOutputImpl(dr.i, dr.o_p, dr.o_n) + + +class XilinxDDROutputImpl(Module): + def __init__(self, i1, i2, o, clk): + self.specials += Instance("ODDR2", + p_DDR_ALIGNMENT="NONE", p_INIT=0, p_SRTYPE="SYNC", + i_C0=clk, i_C1=~clk, i_CE=1, i_S=0, i_R=0, + i_D0=i1, i_D1=i2, o_Q=o, + ) + + +class XilinxDDROutput: + @staticmethod + def lower(dr): + return XilinxDDROutputImpl(dr.i1, dr.i2, dr.o, dr.clk) + + +xilinx_special_overrides = { + NoRetiming: XilinxNoRetiming, + MultiReg: XilinxMultiReg, + AsyncResetSynchronizer: XilinxAsyncResetSynchronizer, + DifferentialInput: XilinxDifferentialInput, + DifferentialOutput: XilinxDifferentialOutput, + DDROutput: XilinxDDROutput +} + + +class XilinxDDROutputImplS7(Module): + def __init__(self, i1, i2, o, clk): + self.specials += Instance("ODDR", + p_DDR_CLK_EDGE="SAME_EDGE", + i_C=clk, i_CE=1, i_S=0, i_R=0, + i_D1=i1, i_D2=i2, o_Q=o, + ) + + +class XilinxDDROutputS7: + @staticmethod + def lower(dr): + return XilinxDDROutputImplS7(dr.i1, dr.i2, dr.o, dr.clk) + + +xilinx_s7_special_overrides = { + DDROutput: XilinxDDROutputS7 +} diff --git a/litex/gen/migen/build/xilinx/ise.py b/litex/gen/migen/build/xilinx/ise.py new file mode 100644 index 00000000..72e82a90 --- /dev/null +++ b/litex/gen/migen/build/xilinx/ise.py @@ -0,0 +1,198 @@ +import os +import subprocess +import sys + +from migen.fhdl.structure import _Fragment +from migen.build.generic_platform import * +from migen.build import tools +from migen.build.xilinx import common + + +def _format_constraint(c): + if isinstance(c, Pins): + return "LOC=" + c.identifiers[0] + elif isinstance(c, IOStandard): + return "IOSTANDARD=" + c.name + elif isinstance(c, Drive): + return "DRIVE=" + str(c.strength) + elif isinstance(c, Misc): + return c.misc + + +def _format_ucf(signame, pin, others, resname): + fmt_c = [] + for c in [Pins(pin)] + others: + fc = _format_constraint(c) + if fc is not None: + fmt_c.append(fc) + fmt_r = resname[0] + ":" + str(resname[1]) + if resname[2] is not None: + fmt_r += "." + resname[2] + return "NET \"" + signame + "\" " + " | ".join(fmt_c) + "; # " + fmt_r + "\n" + + +def _build_ucf(named_sc, named_pc): + r = "" + for sig, pins, others, resname in named_sc: + if len(pins) > 1: + for i, p in enumerate(pins): + r += _format_ucf(sig + "(" + str(i) + ")", p, others, resname) + else: + r += _format_ucf(sig, pins[0], others, resname) + if named_pc: + r += "\n" + "\n\n".join(named_pc) + return r + + +def _build_xst_files(device, sources, vincpaths, build_name, xst_opt): + prj_contents = "" + for filename, language, library in sources: + prj_contents += language + " " + library + " " + filename + "\n" + tools.write_to_file(build_name + ".prj", prj_contents) + + xst_contents = """run +-ifn {build_name}.prj +-top top +{xst_opt} +-ofn {build_name}.ngc +-p {device} +""".format(build_name=build_name, xst_opt=xst_opt, device=device) + for path in vincpaths: + xst_contents += "-vlgincdir " + path + "\n" + tools.write_to_file(build_name + ".xst", xst_contents) + + +def _run_yosys(device, sources, vincpaths, build_name): + ys_contents = "" + incflags = "" + for path in vincpaths: + incflags += " -I" + path + for filename, language, library in sources: + ys_contents += "read_{}{} {}\n".format(language, incflags, filename) + + ys_contents += """hierarchy -check -top top +proc; memory; opt; fsm; opt +synth_xilinx -top top -edif {build_name}.edif""".format(build_name=build_name) + + ys_name = build_name + ".ys" + tools.write_to_file(ys_name, ys_contents) + r = subprocess.call(["yosys", ys_name]) + if r != 0: + raise OSError("Subprocess failed") + + +def _run_ise(build_name, ise_path, source, mode, ngdbuild_opt, + bitgen_opt, ise_commands, map_opt, par_opt, ver=None): + if sys.platform == "win32" or sys.platform == "cygwin": + source_cmd = "call " + script_ext = ".bat" + shell = ["cmd", "/c"] + build_script_contents = "@echo off\nrem Autogenerated by Migen\n" + else: + source_cmd = "source " + script_ext = ".sh" + shell = ["bash"] + build_script_contents = "# Autogenerated by Migen\nset -e\n" + if source: + settings = common.settings(ise_path, ver, "ISE_DS") + build_script_contents += source_cmd + settings + "\n" + if mode == "edif": + ext = "edif" + else: + ext = "ngc" + build_script_contents += """ +xst -ifn {build_name}.xst +""" + + build_script_contents += """ +ngdbuild {ngdbuild_opt} -uc {build_name}.ucf {build_name}.{ext} {build_name}.ngd +map {map_opt} -o {build_name}_map.ncd {build_name}.ngd {build_name}.pcf +par {par_opt} {build_name}_map.ncd {build_name}.ncd {build_name}.pcf +bitgen {bitgen_opt} {build_name}.ncd {build_name}.bit +""" + build_script_contents = build_script_contents.format(build_name=build_name, + ngdbuild_opt=ngdbuild_opt, bitgen_opt=bitgen_opt, ext=ext, + par_opt=par_opt, map_opt=map_opt) + build_script_contents += ise_commands.format(build_name=build_name) + build_script_file = "build_" + build_name + script_ext + tools.write_to_file(build_script_file, build_script_contents, force_unix=False) + command = shell + [build_script_file] + r = subprocess.call(command) + if r != 0: + raise OSError("Subprocess failed") + + +class XilinxISEToolchain: + def __init__(self): + self.xst_opt = """-ifmt MIXED +-use_new_parser yes +-opt_mode SPEED +-register_balancing yes""" + self.map_opt = "-ol high -w" + self.par_opt = "-ol high -w" + self.ngdbuild_opt = "" + self.bitgen_opt = "-g Binary:Yes -w" + self.ise_commands = "" + + def build(self, platform, fragment, build_dir="build", build_name="top", + toolchain_path=None, source=None, run=True, mode="xst"): + if not isinstance(fragment, _Fragment): + fragment = fragment.get_fragment() + if toolchain_path is None: + if sys.platform == "win32": + toolchain_path = "C:\\Xilinx" + elif sys.platform == "cygwin": + toolchain_path = "/cygdrive/c/Xilinx" + else: + toolchain_path = "/opt/Xilinx" + if source is None: + source = sys.platform != "win32" + + platform.finalize(fragment) + ngdbuild_opt = self.ngdbuild_opt + vns = None + + tools.mkdir_noerror(build_dir) + cwd = os.getcwd() + os.chdir(build_dir) + try: + if mode == "xst" or mode == "yosys": + v_output = platform.get_verilog(fragment) + vns = v_output.ns + named_sc, named_pc = platform.resolve_signals(vns) + v_file = build_name + ".v" + v_output.write(v_file) + sources = platform.sources | {(v_file, "verilog", "work")} + if mode == "xst": + _build_xst_files(platform.device, sources, platform.verilog_include_paths, build_name, self.xst_opt) + isemode = "xst" + else: + _run_yosys(platform.device, sources, platform.verilog_include_paths, build_name) + isemode = "edif" + ngdbuild_opt += "-p " + platform.device + + if mode == "mist": + from mist import synthesize + synthesize(fragment, platform.constraint_manager.get_io_signals()) + + if mode == "edif" or mode == "mist": + e_output = platform.get_edif(fragment) + vns = e_output.ns + named_sc, named_pc = platform.resolve_signals(vns) + e_file = build_name + ".edif" + e_output.write(e_file) + isemode = "edif" + + tools.write_to_file(build_name + ".ucf", _build_ucf(named_sc, named_pc)) + if run: + _run_ise(build_name, toolchain_path, source, isemode, + ngdbuild_opt, self.bitgen_opt, self.ise_commands, + self.map_opt, self.par_opt) + finally: + os.chdir(cwd) + + return vns + + def add_period_constraint(self, platform, clk, period): + platform.add_platform_command("""NET "{clk}" TNM_NET = "GRP{clk}"; +TIMESPEC "TS{clk}" = PERIOD "GRP{clk}" """+str(period)+""" ns HIGH 50%;""", clk=clk) diff --git a/litex/gen/migen/build/xilinx/platform.py b/litex/gen/migen/build/xilinx/platform.py new file mode 100644 index 00000000..d4ca4b18 --- /dev/null +++ b/litex/gen/migen/build/xilinx/platform.py @@ -0,0 +1,33 @@ +from migen.build.generic_platform import GenericPlatform +from migen.build.xilinx import common, vivado, ise + + +class XilinxPlatform(GenericPlatform): + bitstream_ext = ".bit" + + def __init__(self, *args, toolchain="ise", **kwargs): + GenericPlatform.__init__(self, *args, **kwargs) + if toolchain == "ise": + self.toolchain = ise.XilinxISEToolchain() + elif toolchain == "vivado": + self.toolchain = vivado.XilinxVivadoToolchain() + else: + raise ValueError("Unknown toolchain") + + def get_verilog(self, *args, special_overrides=dict(), **kwargs): + so = dict(common.xilinx_special_overrides) + if self.device[:3] == "xc7": + so.update(common.xilinx_s7_special_overrides) + so.update(special_overrides) + return GenericPlatform.get_verilog(self, *args, special_overrides=so, **kwargs) + + def get_edif(self, fragment, **kwargs): + return GenericPlatform.get_edif(self, fragment, "UNISIMS", "Xilinx", self.device, **kwargs) + + def build(self, *args, **kwargs): + return self.toolchain.build(self, *args, **kwargs) + + def add_period_constraint(self, clk, period): + if hasattr(clk, "p"): + clk = clk.p + self.toolchain.add_period_constraint(self, clk, period) diff --git a/litex/gen/migen/build/xilinx/programmer.py b/litex/gen/migen/build/xilinx/programmer.py new file mode 100644 index 00000000..8291465a --- /dev/null +++ b/litex/gen/migen/build/xilinx/programmer.py @@ -0,0 +1,201 @@ +import os +import sys +import subprocess + +from migen.build.generic_programmer import GenericProgrammer +from migen.build.xilinx import common + + +def _run_urjtag(cmds): + with subprocess.Popen("jtag", stdin=subprocess.PIPE) as process: + process.stdin.write(cmds.encode("ASCII")) + process.communicate() + + +class UrJTAG(GenericProgrammer): + needs_bitreverse = True + + def __init__(self, cable, flash_proxy_basename=None): + GenericProgrammer.__init__(self, flash_proxy_basename) + self.cable = cable + + def load_bitstream(self, bitstream_file): + cmds = """cable {cable} +detect +pld load {bitstream} +quit +""".format(bitstream=bitstream_file, cable=self.cable) + _run_urjtag(cmds) + + def flash(self, address, data_file): + flash_proxy = self.find_flash_proxy() + cmds = """cable {cable} +detect +pld load "{flash_proxy}" +initbus fjmem opcode=000010 +frequency 6000000 +detectflash 0 +endian big +flashmem "{address}" "{data_file}" noverify +""".format(flash_proxy=flash_proxy, address=address, data_file=data_file, + cable=self.cable) + _run_urjtag(cmds) + + +class XC3SProg(GenericProgrammer): + needs_bitreverse = False + + def __init__(self, cable, flash_proxy_basename=None): + GenericProgrammer.__init__(self, flash_proxy_basename) + self.cable = cable + + def load_bitstream(self, bitstream_file): + subprocess.call(["xc3sprog", "-v", "-c", self.cable, bitstream_file]) + + def flash(self, address, data_file): + flash_proxy = self.find_flash_proxy() + subprocess.call(["xc3sprog", "-v", "-c", self.cable, "-I"+flash_proxy, "{}:w:0x{:x}:BIN".format(data_file, address)]) + + + +class FpgaProg(GenericProgrammer): + needs_bitreverse = False + + def __init__(self, flash_proxy_basename=None): + GenericProgrammer.__init__(self, flash_proxy_basename) + + def load_bitstream(self, bitstream_file): + subprocess.call(["fpgaprog", "-v", "-f", bitstream_file]) + + def flash(self, address, data_file): + if address != 0: + raise ValueError("fpga prog needs a main bitstream at address 0") + flash_proxy = self.find_flash_proxy() + subprocess.call(["fpgaprog", "-v", "-sa", "-r", "-b", flash_proxy, + "-f", data_file]) + + +def _run_impact(cmds): + with subprocess.Popen("impact -batch", stdin=subprocess.PIPE, shell=True) as process: + process.stdin.write(cmds.encode("ASCII")) + process.communicate() + return process.returncode + + +def _create_xsvf(bitstream_file, xsvf_file): + assert os.path.exists(bitstream_file), bitstream_file + assert not os.path.exists(xsvf_file), xsvf_file + assert 0 == _run_impact(""" +setPreference -pref KeepSVF:True +setMode -bs +setCable -port xsvf -file {xsvf} +addDevice -p 1 -file {bitstream} +program -p 1 +quit +""".format(bitstream=bitstream_file, xsvf=xsvf_file)) + + +class iMPACT(GenericProgrammer): + needs_bitreverse = False + + def load_bitstream(self, bitstream_file): + cmds = """setMode -bs +setCable -p auto +addDevice -p 1 -file {bitstream} +program -p 1 +quit +""".format(bitstream=bitstream_file) + _run_impact(cmds) + + +def _run_vivado(path, ver, cmds): + if sys.platform == "win32" or sys.platform == "cygwin": + vivado_cmd = "vivado -mode tcl" + else: + settings = common.settings(path, ver) + vivado_cmd = "bash -c \"source " + settings + "&& vivado -mode tcl\"" + with subprocess.Popen(vivado_cmd, stdin=subprocess.PIPE, shell=True) as process: + process.stdin.write(cmds.encode("ASCII")) + process.communicate() + + +class VivadoProgrammer(GenericProgrammer): + needs_bitreverse = False + def __init__(self, vivado_path="/opt/Xilinx/Vivado", vivado_ver=None, + flash_part="n25q256-3.3v-spi-x1_x2_x4"): + GenericProgrammer.__init__(self) + self.vivado_path = vivado_path + self.vivado_ver = vivado_ver + self.flash_part = flash_part + + 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(self.vivado_path, self.vivado_ver, cmds) + + # XXX works to flash bitstream, adapt it to flash bios + def flash(self, address, data_file): + cmds = """open_hw +connect_hw_server +open_hw_target [lindex [get_hw_targets -of_objects [get_hw_servers localhost]] 0] +create_hw_cfgmem -hw_device [lindex [get_hw_devices] 0] -mem_dev [lindex [get_cfgmem_parts {{{flash_part}}}] 0] + +set_property PROGRAM.BLANK_CHECK 0 [ get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices] 0 ]] +set_property PROGRAM.ERASE 1 [ get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices] 0 ]] +set_property PROGRAM.CFG_PROGRAM 1 [ get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices] 0 ]] +set_property PROGRAM.VERIFY 1 [ get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices] 0 ]] +refresh_hw_device [lindex [get_hw_devices] 0] + +set_property PROGRAM.ADDRESS_RANGE {{use_file}} [ get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices] 0 ]] +set_property PROGRAM.FILES [list "{data}" ] [ get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices] 0]] +set_property PROGRAM.UNUSED_PIN_TERMINATION {{pull-none}} [ get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices] 0 ]] +set_property PROGRAM.BLANK_CHECK 0 [ get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices] 0 ]] +set_property PROGRAM.ERASE 1 [ get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices] 0 ]] +set_property PROGRAM.CFG_PROGRAM 1 [ get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices] 0 ]] +set_property PROGRAM.VERIFY 1 [ get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices] 0 ]] + +startgroup +if {{![string equal [get_property PROGRAM.HW_CFGMEM_TYPE [lindex [get_hw_devices] 0]] [get_property MEM_TYPE [get_property CFGMEM_PART [get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices] 0 ]]]]] }} {{ create_hw_bitstream -hw_device [lindex [get_hw_devices] 0] [get_property PROGRAM.HW_CFGMEM_BITFILE [ lindex [get_hw_devices] 0]]; program_hw_devices [lindex [get_hw_devices] 0]; }}; +program_hw_cfgmem -hw_cfgmem [get_property PROGRAM.HW_CFGMEM [lindex [get_hw_devices] 0 ]] +endgroup + +quit +""".format(data=data_file, flash_part=self.flash_part) + _run_vivado(self.vivado_path, self.vivado_ver, cmds) + + +class Adept(GenericProgrammer): + """Using the Adept tool with an onboard Digilent "USB JTAG" cable. + + You need to install Adept Utilities V2 from + http://www.digilentinc.com/Products/Detail.cfm?NavPath=2,66,828&Prod=ADEPT2 + """ + + needs_bitreverse = False + + def __init__(self, board, index, flash_proxy_basename=None): + GenericProgrammer.__init__(self, flash_proxy_basename) + self.board = board + self.index = index + + def load_bitstream(self, bitstream_file): + subprocess.call([ + "djtgcfg", + "--verbose", + "prog", "-d", self.board, + "-i", str(self.index), + "-f", bitstream_file, + ]) + + def flash(self, address, data_file): + raise ValueError("Flashing unsupported with DigilentAdept tools") diff --git a/litex/gen/migen/build/xilinx/vivado.py b/litex/gen/migen/build/xilinx/vivado.py new file mode 100644 index 00000000..c1a54a41 --- /dev/null +++ b/litex/gen/migen/build/xilinx/vivado.py @@ -0,0 +1,138 @@ +# This file is Copyright (c) 2014 Florent Kermarrec +# License: BSD + +import os +import subprocess +import sys + +from migen.fhdl.structure import _Fragment +from migen.build.generic_platform import * +from migen.build import tools +from migen.build.xilinx import common + + +def _format_constraint(c): + if isinstance(c, Pins): + return "set_property LOC " + c.identifiers[0] + elif isinstance(c, IOStandard): + return "set_property IOSTANDARD " + c.name + elif isinstance(c, Drive): + return "set_property DRIVE " + str(c.strength) + elif isinstance(c, Misc): + return "set_property " + c.misc.replace("=", " ") + else: + raise ValueError("unknown constraint {}".format(c)) + + +def _format_xdc(signame, resname, *constraints): + fmt_c = [_format_constraint(c) for c in constraints] + fmt_r = resname[0] + ":" + str(resname[1]) + if resname[2] is not None: + fmt_r += "." + resname[2] + r = " ## {}\n".format(fmt_r) + for c in fmt_c: + r += c + " [get_ports " + signame + "]\n" + return r + + +def _build_xdc(named_sc, named_pc): + r = "" + for sig, pins, others, resname in named_sc: + if len(pins) > 1: + for i, p in enumerate(pins): + r += _format_xdc(sig + "[" + str(i) + "]", resname, Pins(p), *others) + elif pins: + r += _format_xdc(sig, resname, Pins(pins[0]), *others) + else: + r += _format_xdc(sig, resname, *others) + if named_pc: + r += "\n" + "\n\n".join(named_pc) + return r + + +def _run_vivado(build_name, vivado_path, source, ver=None): + if sys.platform == "win32" or sys.platform == "cygwin": + build_script_contents = "REM Autogenerated by Migen\n" + build_script_contents += "vivado -mode batch -source " + build_name + ".tcl\n" + build_script_file = "build_" + build_name + ".bat" + tools.write_to_file(build_script_file, build_script_contents) + r = subprocess.call([build_script_file]) + else: + build_script_contents = "# Autogenerated by Migen\nset -e\n" + settings = common.settings(vivado_path, ver) + build_script_contents += "source " + settings + "\n" + build_script_contents += "vivado -mode batch -source " + build_name + ".tcl\n" + build_script_file = "build_" + build_name + ".sh" + tools.write_to_file(build_script_file, build_script_contents) + r = subprocess.call(["bash", build_script_file]) + + if r != 0: + raise OSError("Subprocess failed") + + +class XilinxVivadoToolchain: + def __init__(self): + self.bitstream_commands = [] + self.additional_commands = [] + self.pre_synthesis_commands = [] + self.with_phys_opt = False + + def _build_batch(self, platform, sources, build_name): + tcl = [] + for filename, language, library in sources: + filename_tcl = "{" + filename + "}" + tcl.append("add_files " + filename_tcl) + tcl.append("set_property library {} [get_files {}]" + .format(library, filename_tcl)) + + tcl.append("read_xdc {}.xdc".format(build_name)) + tcl.extend(c.format(build_name=build_name) for c in self.pre_synthesis_commands) + tcl.append("synth_design -top top -part {} -include_dirs {{{}}}".format(platform.device, " ".join(platform.verilog_include_paths))) + tcl.append("report_utilization -hierarchical -file {}_utilization_hierarchical_synth.rpt".format(build_name)) + tcl.append("report_utilization -file {}_utilization_synth.rpt".format(build_name)) + tcl.append("place_design") + if self.with_phys_opt: + tcl.append("phys_opt_design -directive AddRetime") + tcl.append("report_utilization -hierarchical -file {}_utilization_hierarchical_place.rpt".format(build_name)) + tcl.append("report_utilization -file {}_utilization_place.rpt".format(build_name)) + tcl.append("report_io -file {}_io.rpt".format(build_name)) + tcl.append("report_control_sets -verbose -file {}_control_sets.rpt".format(build_name)) + tcl.append("report_clock_utilization -file {}_clock_utilization.rpt".format(build_name)) + tcl.append("route_design") + tcl.append("report_route_status -file {}_route_status.rpt".format(build_name)) + tcl.append("report_drc -file {}_drc.rpt".format(build_name)) + tcl.append("report_timing_summary -max_paths 10 -file {}_timing.rpt".format(build_name)) + tcl.append("report_power -file {}_power.rpt".format(build_name)) + for bitstream_command in self.bitstream_commands: + tcl.append(bitstream_command.format(build_name=build_name)) + tcl.append("write_bitstream -force {}.bit ".format(build_name)) + for additional_command in self.additional_commands: + tcl.append(additional_command.format(build_name=build_name)) + tcl.append("quit") + tools.write_to_file(build_name + ".tcl", "\n".join(tcl)) + + def build(self, platform, fragment, build_dir="build", build_name="top", + toolchain_path="/opt/Xilinx/Vivado", source=True, run=True): + tools.mkdir_noerror(build_dir) + os.chdir(build_dir) + + if not isinstance(fragment, _Fragment): + fragment = fragment.get_fragment() + platform.finalize(fragment) + v_output = platform.get_verilog(fragment) + named_sc, named_pc = platform.resolve_signals(v_output.ns) + v_file = build_name + ".v" + v_output.write(v_file) + sources = platform.sources | {(v_file, "verilog", "work")} + self._build_batch(platform, sources, build_name) + tools.write_to_file(build_name + ".xdc", _build_xdc(named_sc, named_pc)) + if run: + _run_vivado(build_name, toolchain_path, source) + + os.chdir("..") + + return v_output.ns + + def add_period_constraint(self, platform, clk, period): + platform.add_platform_command("""create_clock -name {clk} -period """ + \ + str(period) + """ [get_ports {clk}]""", clk=clk) diff --git a/litex/gen/migen/fhdl/__init__.py b/litex/gen/migen/fhdl/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/litex/gen/migen/fhdl/bitcontainer.py b/litex/gen/migen/fhdl/bitcontainer.py new file mode 100644 index 00000000..15a68d0b --- /dev/null +++ b/litex/gen/migen/fhdl/bitcontainer.py @@ -0,0 +1,121 @@ +from migen.fhdl import structure as f + + +__all__ = ["log2_int", "bits_for", "value_bits_sign"] + + +def log2_int(n, need_pow2=True): + l = 1 + r = 0 + while l < n: + l *= 2 + r += 1 + if need_pow2 and l != n: + raise ValueError("Not a power of 2") + return r + + +def bits_for(n, require_sign_bit=False): + if n > 0: + r = log2_int(n + 1, False) + else: + require_sign_bit = True + r = log2_int(-n, False) + if require_sign_bit: + r += 1 + return r + + +def value_bits_sign(v): + """Bit length and signedness of a value. + + Parameters + ---------- + v : Value + + Returns + ------- + int, bool + Number of bits required to store `v` or available in `v`, followed by + whether `v` has a sign bit (included in the bit count). + + Examples + -------- + >>> value_bits_sign(f.Signal(8)) + 8, False + >>> value_bits_sign(C(0xaa)) + 8, False + """ + if isinstance(v, (f.Constant, f.Signal)): + return v.nbits, v.signed + elif isinstance(v, (f.ClockSignal, f.ResetSignal)): + return 1, False + elif isinstance(v, f._Operator): + obs = list(map(value_bits_sign, v.operands)) + if v.op == "+" or v.op == "-": + if not obs[0][1] and not obs[1][1]: + # both operands unsigned + return max(obs[0][0], obs[1][0]) + 1, False + elif obs[0][1] and obs[1][1]: + # both operands signed + return max(obs[0][0], obs[1][0]) + 1, True + elif not obs[0][1] and obs[1][1]: + # first operand unsigned (add sign bit), second operand signed + return max(obs[0][0] + 1, obs[1][0]) + 1, True + else: + # first signed, second operand unsigned (add sign bit) + return max(obs[0][0], obs[1][0] + 1) + 1, True + elif v.op == "*": + if not obs[0][1] and not obs[1][1]: + # both operands unsigned + return obs[0][0] + obs[1][0], False + elif obs[0][1] and obs[1][1]: + # both operands signed + return obs[0][0] + obs[1][0] - 1, True + else: + # one operand signed, the other unsigned (add sign bit) + return obs[0][0] + obs[1][0] + 1 - 1, True + elif v.op == "<<<": + if obs[1][1]: + extra = 2**(obs[1][0] - 1) - 1 + else: + extra = 2**obs[1][0] - 1 + return obs[0][0] + extra, obs[0][1] + elif v.op == ">>>": + if obs[1][1]: + extra = 2**(obs[1][0] - 1) + else: + extra = 0 + return obs[0][0] + extra, obs[0][1] + elif v.op == "&" or v.op == "^" or v.op == "|": + if not obs[0][1] and not obs[1][1]: + # both operands unsigned + return max(obs[0][0], obs[1][0]), False + elif obs[0][1] and obs[1][1]: + # both operands signed + return max(obs[0][0], obs[1][0]), True + elif not obs[0][1] and obs[1][1]: + # first operand unsigned (add sign bit), second operand signed + return max(obs[0][0] + 1, obs[1][0]), True + else: + # first signed, second operand unsigned (add sign bit) + return max(obs[0][0], obs[1][0] + 1), True + elif v.op == "<" or v.op == "<=" or v.op == "==" or v.op == "!=" \ + or v.op == ">" or v.op == ">=": + return 1, False + elif v.op == "~": + return obs[0] + else: + raise TypeError + elif isinstance(v, f._Slice): + return v.stop - v.start, value_bits_sign(v.value)[1] + elif isinstance(v, f.Cat): + return sum(value_bits_sign(sv)[0] for sv in v.l), False + elif isinstance(v, f.Replicate): + return (value_bits_sign(v.v)[0])*v.n, False + elif isinstance(v, f._ArrayProxy): + bsc = list(map(value_bits_sign, v.choices)) + return max(bs[0] for bs in bsc), any(bs[1] for bs in bsc) + else: + raise TypeError("Can not calculate bit length of {} {}".format( + type(v), v)) diff --git a/litex/gen/migen/fhdl/conv_output.py b/litex/gen/migen/fhdl/conv_output.py new file mode 100644 index 00000000..793fad20 --- /dev/null +++ b/litex/gen/migen/fhdl/conv_output.py @@ -0,0 +1,35 @@ +from operator import itemgetter + + +class ConvOutput: + def __init__(self): + self.main_source = "" + self.data_files = dict() + + def set_main_source(self, src): + self.main_source = src + + def add_data_file(self, filename_base, content): + filename = filename_base + i = 1 + while filename in self.data_files: + parts = filename_base.split(".", maxsplit=1) + parts[0] += "_" + str(i) + filename = ".".join(parts) + i += 1 + self.data_files[filename] = content + return filename + + def __str__(self): + r = self.main_source + "\n" + for filename, content in sorted(self.data_files.items(), + key=itemgetter(0)): + r += filename + ":\n" + content + return r + + def write(self, main_filename): + with open(main_filename, "w") as f: + f.write(self.main_source) + for filename, content in self.data_files.items(): + with open(filename, "w") as f: + f.write(content) diff --git a/litex/gen/migen/fhdl/decorators.py b/litex/gen/migen/fhdl/decorators.py new file mode 100644 index 00000000..694479c5 --- /dev/null +++ b/litex/gen/migen/fhdl/decorators.py @@ -0,0 +1,107 @@ +from migen.fhdl.structure import * +from migen.fhdl.module import Module +from migen.fhdl.tools import insert_reset, rename_clock_domain + + +__all__ = ["CEInserter", "ResetInserter", "ClockDomainsRenamer", + "ModuleTransformer"] + + +class ModuleTransformer: + # overload this in derived classes + def transform_instance(self, i): + pass + + # overload this in derived classes + def transform_fragment(self, i, f): + pass + + def wrap_class(self, victim): + class Wrapped(victim): + def __init__(i, *args, **kwargs): + victim.__init__(i, *args, **kwargs) + self.transform_instance(i) + + def get_fragment(i): + f = victim.get_fragment(i) + self.transform_fragment(i, f) + return f + + Wrapped.__name__ = victim.__name__ + # "{}_{}".format(self.__class__.__name__, victim.__name__) + return Wrapped + + def wrap_instance(self, victim): + self.transform_instance(victim) + orig_get_fragment = victim.get_fragment + + def get_fragment(): + f = orig_get_fragment() + self.transform_fragment(victim, f) + return f + + victim.get_fragment = get_fragment + return victim + + def __call__(self, victim): + if isinstance(victim, Module): + return self.wrap_instance(victim) + else: + return self.wrap_class(victim) + + +class ControlInserter(ModuleTransformer): + control_name = None # override this + + def __init__(self, clock_domains=None): + self.clock_domains = clock_domains + + def transform_instance(self, i): + if self.clock_domains is None: + ctl = Signal(name=self.control_name) + assert not hasattr(i, self.control_name) + setattr(i, self.control_name, ctl) + else: + for cd in self.clock_domains: + name = self.control_name + "_" + cd + ctl = Signal(name=name) + assert not hasattr(i, name) + setattr(i, name, ctl) + + def transform_fragment(self, i, f): + if self.clock_domains is None: + if len(f.sync) != 1: + raise ValueError("Control signal clock domains must be specified when module has more than one domain") + cdn = list(f.sync.keys())[0] + to_insert = [(getattr(i, self.control_name), cdn)] + else: + to_insert = [(getattr(i, self.control_name + "_" + cdn), cdn) + for cdn in self.clock_domains] + self.transform_fragment_insert(i, f, to_insert) + + +class CEInserter(ControlInserter): + control_name = "ce" + + def transform_fragment_insert(self, i, f, to_insert): + for ce, cdn in to_insert: + f.sync[cdn] = [If(ce, *f.sync[cdn])] + + +class ResetInserter(ControlInserter): + control_name = "reset" + + def transform_fragment_insert(self, i, f, to_insert): + for reset, cdn in to_insert: + f.sync[cdn] = insert_reset(reset, f.sync[cdn]) + + +class ClockDomainsRenamer(ModuleTransformer): + def __init__(self, cd_remapping): + if isinstance(cd_remapping, str): + cd_remapping = {"sys": cd_remapping} + self.cd_remapping = cd_remapping + + def transform_fragment(self, i, f): + for old, new in self.cd_remapping.items(): + rename_clock_domain(f, old, new) diff --git a/litex/gen/migen/fhdl/edif.py b/litex/gen/migen/fhdl/edif.py new file mode 100644 index 00000000..f6d7deb0 --- /dev/null +++ b/litex/gen/migen/fhdl/edif.py @@ -0,0 +1,213 @@ +from collections import OrderedDict, namedtuple + +from migen.fhdl.structure import * +from migen.fhdl.namer import build_namespace +from migen.fhdl.tools import list_special_ios +from migen.fhdl.structure import _Fragment +from migen.fhdl.conv_output import ConvOutput + + +_Port = namedtuple("_Port", "name direction") +_Cell = namedtuple("_Cell", "name ports") +_Property = namedtuple("_Property", "name value") +_Instance = namedtuple("_Instance", "name cell properties") +_NetBranch = namedtuple("_NetBranch", "portname instancename") + + +def _write_cells(cells): + r = "" + for cell in cells: + r += """ + (cell {0.name} + (cellType GENERIC) + (view view_1 + (viewType NETLIST) + (interface""".format(cell) + for port in cell.ports: + r += """ + (port {0.name} (direction {0.direction}))""".format(port) + r += """ + ) + ) + )""" + return r + + +def _write_io(ios): + r = "" + for s in ios: + r += """ + (port {0.name} (direction {0.direction}))""".format(s) + return r + + +def _write_instantiations(instances, cell_library): + instantiations = "" + for instance in instances: + instantiations += """ + (instance {0.name} + (viewRef view_1 (cellRef {0.cell} (libraryRef {1})))""".format(instance, cell_library) + for prop in instance.properties: + instantiations += """ + (property {0} (string "{1}"))""".format(prop.name, prop.value) + instantiations += """ + )""" + return instantiations + + +def _write_connections(connections): + r = "" + for netname, branches in connections.items(): + r += """ + (net {0} + (joined""".format(netname) + for branch in branches: + r += """ + (portRef {0}{1})""".format(branch.portname, "" if branch.instancename == "" else " (instanceRef {})".format(branch.instancename)) + r += """ + ) + )""" + return r + + +def _write_edif(cells, ios, instances, connections, cell_library, design_name, part, vendor): + r = """(edif {0} + (edifVersion 2 0 0) + (edifLevel 0) + (keywordMap (keywordLevel 0)) + (external {1} + (edifLevel 0) + (technology (numberDefinition))""".format(design_name, cell_library) + r += _write_cells(cells) + r += """ + ) + (library {0}_lib + (edifLevel 0) + (technology (numberDefinition)) + (cell {0} + (cellType GENERIC) + (view view_1 + (viewType NETLIST) + (interface""".format(design_name) + r += _write_io(ios) + r += """ + (designator "{0}") + ) + (contents""".format(part) + r += _write_instantiations(instances, cell_library) + r += _write_connections(connections) + r += """ + ) + ) + ) + ) + (design {0} + (cellRef {0} (libraryRef {0}_lib)) + (property PART (string "{1}") (owner "{2}")) + ) +)""".format(design_name, part, vendor) + + return r + + +def _generate_cells(f): + cell_dict = OrderedDict() + for special in f.specials: + if isinstance(special, Instance): + port_list = [] + for port in special.items: + if isinstance(port, Instance.Input): + port_list.append(_Port(port.name, "INPUT")) + elif isinstance(port, Instance.Output): + port_list.append(_Port(port.name, "OUTPUT")) + elif isinstance(port, Instance.InOut): + port_list.append(_Port(port.name, "INOUT")) + elif isinstance(port, Instance.Parameter): + pass + else: + raise NotImplementedError("Unsupported instance item") + if special.of in cell_dict: + if set(port_list) != set(cell_dict[special.of]): + raise ValueError("All instances must have the same ports for EDIF conversion") + else: + cell_dict[special.of] = port_list + else: + raise ValueError("EDIF conversion can only handle synthesized fragments") + return [_Cell(k, v) for k, v in cell_dict.items()] + + +def _generate_instances(f, ns): + instances = [] + for special in f.specials: + if isinstance(special, Instance): + props = [] + for prop in special.items: + if isinstance(prop, Instance.Input): + pass + elif isinstance(prop, Instance.Output): + pass + elif isinstance(prop, Instance.InOut): + pass + elif isinstance(prop, Instance.Parameter): + props.append(_Property(name=prop.name, value=prop.value)) + else: + raise NotImplementedError("Unsupported instance item") + instances.append(_Instance(name=ns.get_name(special), cell=special.of, properties=props)) + else: + raise ValueError("EDIF conversion can only handle synthesized fragments") + return instances + + +def _generate_ios(f, ios, ns): + outs = list_special_ios(f, False, True, False) + inouts = list_special_ios(f, False, False, True) + r = [] + for io in ios: + direction = "OUTPUT" if io in outs else "INOUT" if io in inouts else "INPUT" + r.append(_Port(name=ns.get_name(io), direction=direction)) + return r + + +def _generate_connections(f, ios, ns): + r = OrderedDict() + for special in f.specials: + if isinstance(special, Instance): + instname = ns.get_name(special) + for port in special.items: + if isinstance(port, Instance._IO): + s = ns.get_name(port.expr) + if s not in r: + r[s] = [] + r[s].append(_NetBranch(portname=port.name, instancename=instname)) + elif isinstance(port, Instance.Parameter): + pass + else: + raise NotImplementedError("Unsupported instance item") + else: + raise ValueError("EDIF conversion can only handle synthesized fragments") + for s in ios: + io = ns.get_name(s) + if io not in r: + r[io] = [] + r[io].append(_NetBranch(portname=io, instancename="")) + return r + + +def convert(f, ios, cell_library, vendor, device, name="top"): + if not isinstance(f, _Fragment): + f = f.get_fragment() + if f.comb != [] or f.sync != {}: + raise ValueError("EDIF conversion can only handle synthesized fragments") + if ios is None: + ios = set() + cells = _generate_cells(f) + ns = build_namespace(list_special_ios(f, True, True, True)) + instances = _generate_instances(f, ns) + inouts = _generate_ios(f, ios, ns) + connections = _generate_connections(f, ios, ns) + src = _write_edif(cells, inouts, instances, connections, cell_library, name, device, vendor) + + r = ConvOutput() + r.set_main_source(src) + r.ns = ns + return r diff --git a/litex/gen/migen/fhdl/module.py b/litex/gen/migen/fhdl/module.py new file mode 100644 index 00000000..7a209933 --- /dev/null +++ b/litex/gen/migen/fhdl/module.py @@ -0,0 +1,185 @@ +import collections +from itertools import combinations + +from migen.util.misc import flat_iteration +from migen.fhdl.structure import * +from migen.fhdl.structure import _Fragment +from migen.fhdl.tools import rename_clock_domain + + +__all__ = ["Module", "FinalizeError"] + + +class FinalizeError(Exception): + pass + + +def _flat_list(e): + if isinstance(e, collections.Iterable): + return flat_iteration(e) + else: + return [e] + + +class _ModuleProxy: + def __init__(self, fm): + object.__setattr__(self, "_fm", fm) + + +class _ModuleComb(_ModuleProxy): + def __iadd__(self, other): + self._fm._fragment.comb += _flat_list(other) + return self + + +def _cd_append(d, key, statements): + try: + l = d[key] + except KeyError: + l = [] + d[key] = l + l += _flat_list(statements) + + +class _ModuleSyncCD: + def __init__(self, fm, cd): + self._fm = fm + self._cd = cd + + def __iadd__(self, other): + _cd_append(self._fm._fragment.sync, self._cd, other) + return self + + +class _ModuleSync(_ModuleProxy): + def __iadd__(self, other): + _cd_append(self._fm._fragment.sync, "sys", other) + return self + + def __getattr__(self, name): + return _ModuleSyncCD(self._fm, name) + + def __setattr__(self, name, value): + if not isinstance(value, _ModuleSyncCD): + raise AttributeError("Attempted to assign sync property - use += instead") + + +# _ModuleForwardAttr enables user classes to do e.g.: +# self.subm.foobar = SomeModule() +# and then access the submodule with self.foobar. +class _ModuleForwardAttr: + def __setattr__(self, name, value): + self.__iadd__(value) + setattr(self._fm, name, value) + + +class _ModuleSpecials(_ModuleProxy, _ModuleForwardAttr): + def __iadd__(self, other): + self._fm._fragment.specials |= set(_flat_list(other)) + return self + + +class _ModuleSubmodules(_ModuleProxy): + def __setattr__(self, name, value): + self._fm._submodules += [(name, e) for e in _flat_list(value)] + setattr(self._fm, name, value) + + def __iadd__(self, other): + self._fm._submodules += [(None, e) for e in _flat_list(other)] + return self + + +class _ModuleClockDomains(_ModuleProxy, _ModuleForwardAttr): + def __iadd__(self, other): + self._fm._fragment.clock_domains += _flat_list(other) + return self + + +class Module: + def get_fragment(self): + assert(not self.get_fragment_called) + self.get_fragment_called = True + self.finalize() + return self._fragment + + def __getattr__(self, name): + if name == "comb": + return _ModuleComb(self) + elif name == "sync": + return _ModuleSync(self) + elif name == "specials": + return _ModuleSpecials(self) + elif name == "submodules": + return _ModuleSubmodules(self) + elif name == "clock_domains": + return _ModuleClockDomains(self) + + # hack to have initialized regular attributes without using __init__ + # (which would require derived classes to call it) + elif name == "finalized": + self.finalized = False + return self.finalized + elif name == "_fragment": + self._fragment = _Fragment() + return self._fragment + elif name == "_submodules": + self._submodules = [] + return self._submodules + elif name == "_clock_domains": + self._clock_domains = [] + return self._clock_domains + elif name == "get_fragment_called": + self.get_fragment_called = False + return self.get_fragment_called + + else: + raise AttributeError("'"+self.__class__.__name__+"' object has no attribute '"+name+"'") + + def __setattr__(self, name, value): + if name in ["comb", "sync", "specials", "submodules", "clock_domains"]: + if not isinstance(value, _ModuleProxy): + raise AttributeError("Attempted to assign special Module property - use += instead") + else: + object.__setattr__(self, name, value) + + def _collect_submodules(self): + r = [] + for name, submodule in self._submodules: + if not submodule.get_fragment_called: + r.append((name, submodule.get_fragment())) + return r + + def finalize(self, *args, **kwargs): + if not self.finalized: + self.finalized = True + # finalize existing submodules before finalizing us + subfragments = self._collect_submodules() + self.do_finalize(*args, **kwargs) + # finalize submodules created by do_finalize + subfragments += self._collect_submodules() + # resolve clock domain name conflicts + needs_renaming = set() + for (mod_name1, f1), (mod_name2, f2) in combinations(subfragments, 2): + f1_names = set(cd.name for cd in f1.clock_domains) + f2_names = set(cd.name for cd in f2.clock_domains) + common_names = f1_names & f2_names + if common_names: + if mod_name1 is None or mod_name2 is None: + raise ValueError("Multiple submodules with local clock domains cannot be anonymous") + if mod_name1 == mod_name2: + raise ValueError("Multiple submodules with local clock domains cannot have the same name") + needs_renaming |= common_names + for mod_name, f in subfragments: + for cd in f.clock_domains: + if cd.name in needs_renaming: + rename_clock_domain(f, cd.name, mod_name + "_" + cd.name) + # sum subfragments + for mod_name, f in subfragments: + self._fragment += f + + def do_finalize(self): + pass + + def do_exit(self, *args, **kwargs): + for name, submodule in self._submodules: + submodule.do_exit(*args, **kwargs) diff --git a/litex/gen/migen/fhdl/namer.py b/litex/gen/migen/fhdl/namer.py new file mode 100644 index 00000000..8e0f450d --- /dev/null +++ b/litex/gen/migen/fhdl/namer.py @@ -0,0 +1,258 @@ +from collections import OrderedDict +from itertools import combinations + +from migen.fhdl.structure import * + + +class _Node: + def __init__(self): + self.signal_count = 0 + self.numbers = set() + self.use_name = False + self.use_number = False + self.children = OrderedDict() + + +def _display_tree(filename, tree): + from migen.util.treeviz import RenderNode + + def _to_render_node(name, node): + children = [_to_render_node(k, v) for k, v in node.children.items()] + if node.use_name: + if node.use_number: + color = (0.5, 0.9, 0.8) + else: + color = (0.8, 0.5, 0.9) + else: + if node.use_number: + color = (0.9, 0.8, 0.5) + else: + color = (0.8, 0.8, 0.8) + label = "{0}\n{1} signals\n{2}".format(name, node.signal_count, node.numbers) + return RenderNode(label, children, color=color) + + top = _to_render_node("top", tree) + top.to_svg(filename) + + +def _build_tree(signals, basic_tree=None): + root = _Node() + for signal in signals: + current_b = basic_tree + current = root + current.signal_count += 1 + for name, number in signal.backtrace: + if basic_tree is None: + use_number = False + else: + current_b = current_b.children[name] + use_number = current_b.use_number + if use_number: + key = (name, number) + else: + key = name + try: + current = current.children[key] + except KeyError: + new = _Node() + current.children[key] = new + current = new + current.numbers.add(number) + if use_number: + current.all_numbers = sorted(current_b.numbers) + current.signal_count += 1 + return root + + +def _set_use_name(node, node_name=""): + cnames = [(k, _set_use_name(v, k)) for k, v in node.children.items()] + for (c1_prefix, c1_names), (c2_prefix, c2_names) in combinations(cnames, 2): + if not c1_names.isdisjoint(c2_names): + node.children[c1_prefix].use_name = True + node.children[c2_prefix].use_name = True + r = set() + for c_prefix, c_names in cnames: + if node.children[c_prefix].use_name: + for c_name in c_names: + r.add((c_prefix, ) + c_name) + else: + r |= c_names + + if node.signal_count > sum(c.signal_count for c in node.children.values()): + node.use_name = True + r.add((node_name, )) + + return r + + +def _name_signal(tree, signal): + elements = [] + treepos = tree + for step_name, step_n in signal.backtrace: + try: + treepos = treepos.children[(step_name, step_n)] + use_number = True + except KeyError: + treepos = treepos.children[step_name] + use_number = False + if treepos.use_name: + elname = step_name + if use_number: + elname += str(treepos.all_numbers.index(step_n)) + elements.append(elname) + return "_".join(elements) + + +def _build_pnd_from_tree(tree, signals): + return dict((signal, _name_signal(tree, signal)) for signal in signals) + + +def _invert_pnd(pnd): + inv_pnd = dict() + for k, v in pnd.items(): + inv_pnd[v] = inv_pnd.get(v, []) + inv_pnd[v].append(k) + return inv_pnd + + +def _list_conflicting_signals(pnd): + inv_pnd = _invert_pnd(pnd) + r = set() + for k, v in inv_pnd.items(): + if len(v) > 1: + r.update(v) + return r + + +def _set_use_number(tree, signals): + for signal in signals: + current = tree + for step_name, step_n in signal.backtrace: + current = current.children[step_name] + current.use_number = current.signal_count > len(current.numbers) and len(current.numbers) > 1 + +_debug = False + + +def _build_pnd_for_group(group_n, signals): + basic_tree = _build_tree(signals) + _set_use_name(basic_tree) + if _debug: + _display_tree("tree{0}_basic.svg".format(group_n), basic_tree) + pnd = _build_pnd_from_tree(basic_tree, signals) + + # If there are conflicts, try splitting the tree by numbers + # on paths taken by conflicting signals. + conflicting_signals = _list_conflicting_signals(pnd) + if conflicting_signals: + _set_use_number(basic_tree, conflicting_signals) + if _debug: + print("namer: using split-by-number strategy (group {0})".format(group_n)) + _display_tree("tree{0}_marked.svg".format(group_n), basic_tree) + numbered_tree = _build_tree(signals, basic_tree) + _set_use_name(numbered_tree) + if _debug: + _display_tree("tree{0}_numbered.svg".format(group_n), numbered_tree) + pnd = _build_pnd_from_tree(numbered_tree, signals) + else: + if _debug: + print("namer: using basic strategy (group {0})".format(group_n)) + + # ...then add number suffixes by DUID + inv_pnd = _invert_pnd(pnd) + duid_suffixed = False + for name, signals in inv_pnd.items(): + if len(signals) > 1: + duid_suffixed = True + for n, signal in enumerate(sorted(signals, key=lambda x: x.duid)): + pnd[signal] += str(n) + if _debug and duid_suffixed: + print("namer: using DUID suffixes (group {0})".format(group_n)) + + return pnd + + +def _build_signal_groups(signals): + r = [] + for signal in signals: + # build chain of related signals + related_list = [] + cur_signal = signal + while cur_signal is not None: + related_list.insert(0, cur_signal) + cur_signal = cur_signal.related + # add to groups + for _ in range(len(related_list) - len(r)): + r.append(set()) + for target_set, source_signal in zip(r, related_list): + target_set.add(source_signal) + # with the algorithm above and a list of all signals, + # a signal appears in all groups of a lower number than its. + # make signals appear only in their group of highest number. + for s1, s2 in zip(r, r[1:]): + s1 -= s2 + return r + + +def _build_pnd(signals): + groups = _build_signal_groups(signals) + gpnds = [_build_pnd_for_group(n, gsignals) for n, gsignals in enumerate(groups)] + + pnd = dict() + for gn, gpnd in enumerate(gpnds): + for signal, name in gpnd.items(): + result = name + cur_gn = gn + cur_signal = signal + while cur_signal.related is not None: + cur_signal = cur_signal.related + cur_gn -= 1 + result = gpnds[cur_gn][cur_signal] + "_" + result + pnd[signal] = result + + return pnd + + +def build_namespace(signals, reserved_keywords=set()): + pnd = _build_pnd(signals) + ns = Namespace(pnd, reserved_keywords) + # register signals with name_override + for signal in signals: + if signal.name_override is not None: + ns.get_name(signal) + return ns + + +class Namespace: + def __init__(self, pnd, reserved_keywords=set()): + self.counts = {k: 1 for k in reserved_keywords} + self.sigs = {} + self.pnd = pnd + self.clock_domains = dict() + + def get_name(self, sig): + if isinstance(sig, ClockSignal): + sig = self.clock_domains[sig.cd].clk + if isinstance(sig, ResetSignal): + sig = self.clock_domains[sig.cd].rst + if sig is None: + raise ValueError("Attempted to obtain name of non-existent " + "reset signal of domain "+sig.cd) + + if sig.name_override is not None: + sig_name = sig.name_override + else: + sig_name = self.pnd[sig] + try: + n = self.sigs[sig] + except KeyError: + try: + n = self.counts[sig_name] + except KeyError: + n = 0 + self.sigs[sig] = n + self.counts[sig_name] = n + 1 + if n: + return sig_name + "_" + str(n) + else: + return sig_name diff --git a/litex/gen/migen/fhdl/simplify.py b/litex/gen/migen/fhdl/simplify.py new file mode 100644 index 00000000..dba41beb --- /dev/null +++ b/litex/gen/migen/fhdl/simplify.py @@ -0,0 +1,114 @@ +from migen.fhdl.structure import * +from migen.fhdl.specials import Memory, _MemoryPort, WRITE_FIRST, NO_CHANGE +from migen.fhdl.decorators import ModuleTransformer +from migen.util.misc import gcd_multiple + + +class FullMemoryWE(ModuleTransformer): + def __init__(self): + self.replacments = dict() + + def transform_fragment(self, i, f): + newspecials = set() + + for orig in f.specials: + if not isinstance(orig, Memory): + newspecials.add(orig) + continue + global_granularity = gcd_multiple([p.we_granularity if p.we_granularity else orig.width for p in orig.ports]) + if global_granularity == orig.width: + newspecials.add(orig) # nothing to do + else: + newmems = [] + for i in range(orig.width//global_granularity): + if orig.init is None: + newinit = None + else: + newinit = [(v >> i*global_granularity) & (2**global_granularity - 1) for v in orig.init] + newmem = Memory(global_granularity, orig.depth, newinit, orig.name_override + "_grain" + str(i)) + newspecials.add(newmem) + newmems.append(newmem) + for port in orig.ports: + port_granularity = port.we_granularity if port.we_granularity else orig.width + newport = _MemoryPort( + adr=port.adr, + + dat_r=port.dat_r[i*global_granularity:(i+1)*global_granularity] if port.dat_r is not None else None, + we=port.we[i*global_granularity//port_granularity] if port.we is not None else None, + dat_w=port.dat_w[i*global_granularity:(i+1)*global_granularity] if port.dat_w is not None else None, + + async_read=port.async_read, + re=port.re, + we_granularity=0, + mode=port.mode, + clock_domain=port.clock.cd) + newmem.ports.append(newport) + newspecials.add(newport) + self.replacments[orig] = newmems + + f.specials = newspecials + + +class MemoryToArray(ModuleTransformer): + def __init__(self): + self.replacements = dict() + + def transform_fragment(self, i, f): + newspecials = set() + + for mem in f.specials: + if not isinstance(mem, Memory): + newspecials.add(mem) + continue + + storage = Array() + self.replacements[mem] = storage + init = [] + if mem.init is not None: + init = mem.init + for d in init: + mem_storage = Signal(mem.width, reset=d) + storage.append(mem_storage) + for _ in range(mem.depth-len(init)): + mem_storage = Signal(mem.width) + storage.append(mem_storage) + + for port in mem.ports: + if port.we_granularity: + raise NotImplementedError + try: + sync = f.sync[port.clock.cd] + except KeyError: + sync = f.sync[port.clock.cd] = [] + + # read + if port.async_read: + f.comb.append(port.dat_r.eq(storage[port.adr])) + else: + if port.mode == WRITE_FIRST and port.we is not None: + adr_reg = Signal.like(port.adr) + rd_stmt = adr_reg.eq(port.adr) + f.comb.append(port.dat_r.eq(storage[adr_reg])) + elif port.mode == NO_CHANGE and port.we is not None: + rd_stmt = If(~port.we, port.dat_r.eq(storage[port.adr])) + else: # READ_FIRST or port.we is None, simplest case + rd_stmt = port.dat_r.eq(storage[port.adr]) + if port.re is None: + sync.append(rd_stmt) + else: + sync.append(If(port.re, rd_stmt)) + + # write + if port.we is not None: + if port.we_granularity: + n = mem.width//port.we_granularity + for i in range(n): + m = i*port.we_granularity + M = (i+1)*port.we_granularity + sync.append(If(port.we[i], + storage[port.adr][m:M].eq(port.dat_w))) + else: + sync.append(If(port.we, + storage[port.adr].eq(port.dat_w))) + + f.specials = newspecials diff --git a/litex/gen/migen/fhdl/specials.py b/litex/gen/migen/fhdl/specials.py new file mode 100644 index 00000000..bd3e1bf5 --- /dev/null +++ b/litex/gen/migen/fhdl/specials.py @@ -0,0 +1,360 @@ +from operator import itemgetter + +from migen.fhdl.structure import * +from migen.fhdl.structure import _Value +from migen.fhdl.bitcontainer import bits_for, value_bits_sign +from migen.fhdl.tools import * +from migen.fhdl.tracer import get_obj_var_name +from migen.fhdl.verilog import _printexpr as verilog_printexpr + + +__all__ = ["TSTriple", "Instance", "Memory", + "READ_FIRST", "WRITE_FIRST", "NO_CHANGE"] + + +class Special(DUID): + def iter_expressions(self): + for x in []: + yield x + + def rename_clock_domain(self, old, new): + for obj, attr, direction in self.iter_expressions(): + rename_clock_domain_expr(getattr(obj, attr), old, new) + + def list_clock_domains(self): + r = set() + for obj, attr, direction in self.iter_expressions(): + r |= list_clock_domains_expr(getattr(obj, attr)) + return r + + def list_ios(self, ins, outs, inouts): + r = set() + for obj, attr, direction in self.iter_expressions(): + if (direction == SPECIAL_INPUT and ins) \ + or (direction == SPECIAL_OUTPUT and outs) \ + or (direction == SPECIAL_INOUT and inouts): + signals = list_signals(getattr(obj, attr)) + r.update(signals) + return r + + +class Tristate(Special): + def __init__(self, target, o, oe, i=None): + Special.__init__(self) + self.target = wrap(target) + self.o = wrap(o) + self.oe = wrap(oe) + self.i = wrap(i) if i is not None else None + + def iter_expressions(self): + for attr, target_context in [ + ("target", SPECIAL_INOUT), + ("o", SPECIAL_INPUT), + ("oe", SPECIAL_INPUT), + ("i", SPECIAL_OUTPUT)]: + if getattr(self, attr) is not None: + yield self, attr, target_context + + @staticmethod + def emit_verilog(tristate, ns, add_data_file): + def pe(e): + return verilog_printexpr(ns, e)[0] + w, s = value_bits_sign(tristate.target) + r = "assign " + pe(tristate.target) + " = " \ + + pe(tristate.oe) + " ? " + pe(tristate.o) \ + + " : " + str(w) + "'bz;\n" + if tristate.i is not None: + r += "assign " + pe(tristate.i) + " = " + pe(tristate.target) + ";\n" + r += "\n" + return r + + +class TSTriple: + def __init__(self, bits_sign=None, min=None, max=None, reset_o=0, reset_oe=0): + self.o = Signal(bits_sign, min=min, max=max, reset=reset_o) + self.oe = Signal(reset=reset_oe) + self.i = Signal(bits_sign, min=min, max=max) + + def get_tristate(self, target): + return Tristate(target, self.o, self.oe, self.i) + + +class Instance(Special): + class _IO: + def __init__(self, name, expr=None): + self.name = name + if expr is None: + expr = Signal() + self.expr = wrap(expr) + class Input(_IO): + pass + class Output(_IO): + pass + class InOut(_IO): + pass + class Parameter: + def __init__(self, name, value): + self.name = name + if isinstance(value, (int, bool)): + value = Constant(value) + self.value = value + class PreformattedParam(str): + pass + + def __init__(self, of, *items, name="", synthesis_directive=None, **kwargs): + Special.__init__(self) + self.of = of + if name: + self.name_override = name + else: + self.name_override = of + self.items = list(items) + self.synthesis_directive = synthesis_directive + for k, v in sorted(kwargs.items(), key=itemgetter(0)): + item_type, item_name = k.split("_", maxsplit=1) + item_class = { + "i": Instance.Input, + "o": Instance.Output, + "io": Instance.InOut, + "p": Instance.Parameter + }[item_type] + self.items.append(item_class(item_name, v)) + + def get_io(self, name): + for item in self.items: + if isinstance(item, Instance._IO) and item.name == name: + return item.expr + + def iter_expressions(self): + for item in self.items: + if isinstance(item, Instance.Input): + yield item, "expr", SPECIAL_INPUT + elif isinstance(item, Instance.Output): + yield item, "expr", SPECIAL_OUTPUT + elif isinstance(item, Instance.InOut): + yield item, "expr", SPECIAL_INOUT + + @staticmethod + def emit_verilog(instance, ns, add_data_file): + r = instance.of + " " + parameters = list(filter(lambda i: isinstance(i, Instance.Parameter), instance.items)) + if parameters: + r += "#(\n" + firstp = True + for p in parameters: + if not firstp: + r += ",\n" + firstp = False + r += "\t." + p.name + "(" + if isinstance(p.value, Constant): + r += verilog_printexpr(ns, p.value)[0] + elif isinstance(p.value, float): + r += str(p.value) + elif isinstance(p.value, Instance.PreformattedParam): + r += p.value + elif isinstance(p.value, str): + r += "\"" + p.value + "\"" + else: + raise TypeError + r += ")" + r += "\n) " + r += ns.get_name(instance) + if parameters: r += " " + r += "(\n" + firstp = True + for p in instance.items: + if isinstance(p, Instance._IO): + name_inst = p.name + name_design = verilog_printexpr(ns, p.expr)[0] + if not firstp: + r += ",\n" + firstp = False + r += "\t." + name_inst + "(" + name_design + ")" + if not firstp: + r += "\n" + if instance.synthesis_directive is not None: + synthesis_directive = "/* synthesis {} */".format(instance.synthesis_directive) + r += ")" + synthesis_directive + ";\n\n" + else: + r += ");\n\n" + return r + + +(READ_FIRST, WRITE_FIRST, NO_CHANGE) = range(3) + + +class _MemoryPort(Special): + def __init__(self, adr, dat_r, we=None, dat_w=None, + async_read=False, re=None, we_granularity=0, mode=WRITE_FIRST, + clock_domain="sys"): + Special.__init__(self) + self.adr = adr + self.dat_r = dat_r + self.we = we + self.dat_w = dat_w + self.async_read = async_read + self.re = re + self.we_granularity = we_granularity + self.mode = mode + self.clock = ClockSignal(clock_domain) + + def iter_expressions(self): + for attr, target_context in [ + ("adr", SPECIAL_INPUT), + ("we", SPECIAL_INPUT), + ("dat_w", SPECIAL_INPUT), + ("re", SPECIAL_INPUT), + ("dat_r", SPECIAL_OUTPUT), + ("clock", SPECIAL_INPUT)]: + yield self, attr, target_context + + @staticmethod + def emit_verilog(port, ns, add_data_file): + return "" # done by parent Memory object + + +class _MemoryLocation(_Value): + def __init__(self, memory, index): + _Value.__init__(self) + self.memory = memory + self.index = wrap(index) + + +class Memory(Special): + def __init__(self, width, depth, init=None, name=None): + Special.__init__(self) + self.width = width + self.depth = depth + self.ports = [] + self.init = init + self.name_override = get_obj_var_name(name, "mem") + + def __getitem__(self, index): + # simulation only + return _MemoryLocation(self, index) + + def get_port(self, write_capable=False, async_read=False, + has_re=False, we_granularity=0, mode=WRITE_FIRST, + clock_domain="sys"): + if we_granularity >= self.width: + we_granularity = 0 + adr = Signal(max=self.depth) + dat_r = Signal(self.width) + if write_capable: + if we_granularity: + we = Signal(self.width//we_granularity) + else: + we = Signal() + dat_w = Signal(self.width) + else: + we = None + dat_w = None + if has_re: + re = Signal() + else: + re = None + mp = _MemoryPort(adr, dat_r, we, dat_w, + async_read, re, we_granularity, mode, + clock_domain) + self.ports.append(mp) + return mp + + @staticmethod + def emit_verilog(memory, ns, add_data_file): + r = "" + def gn(e): + if isinstance(e, Memory): + return ns.get_name(e) + else: + return verilog_printexpr(ns, e)[0] + adrbits = bits_for(memory.depth-1) + + r += "reg [" + str(memory.width-1) + ":0] " \ + + gn(memory) \ + + "[0:" + str(memory.depth-1) + "];\n" + + adr_regs = {} + data_regs = {} + for port in memory.ports: + if not port.async_read: + if port.mode == WRITE_FIRST and port.we is not None: + adr_reg = Signal(name_override="memadr") + r += "reg [" + str(adrbits-1) + ":0] " \ + + gn(adr_reg) + ";\n" + adr_regs[id(port)] = adr_reg + else: + data_reg = Signal(name_override="memdat") + r += "reg [" + str(memory.width-1) + ":0] " \ + + gn(data_reg) + ";\n" + data_regs[id(port)] = data_reg + + for port in memory.ports: + r += "always @(posedge " + gn(port.clock) + ") begin\n" + if port.we is not None: + if port.we_granularity: + n = memory.width//port.we_granularity + for i in range(n): + m = i*port.we_granularity + M = (i+1)*port.we_granularity-1 + sl = "[" + str(M) + ":" + str(m) + "]" + r += "\tif (" + gn(port.we) + "[" + str(i) + "])\n" + r += "\t\t" + gn(memory) + "[" + gn(port.adr) + "]" + sl + " <= " + gn(port.dat_w) + sl + ";\n" + else: + r += "\tif (" + gn(port.we) + ")\n" + r += "\t\t" + gn(memory) + "[" + gn(port.adr) + "] <= " + gn(port.dat_w) + ";\n" + if not port.async_read: + if port.mode == WRITE_FIRST and port.we is not None: + rd = "\t" + gn(adr_regs[id(port)]) + " <= " + gn(port.adr) + ";\n" + else: + bassign = gn(data_regs[id(port)]) + " <= " + gn(memory) + "[" + gn(port.adr) + "];\n" + if port.mode == READ_FIRST or port.we is None: + rd = "\t" + bassign + elif port.mode == NO_CHANGE: + rd = "\tif (!" + gn(port.we) + ")\n" \ + + "\t\t" + bassign + if port.re is None: + r += rd + else: + r += "\tif (" + gn(port.re) + ")\n" + r += "\t" + rd.replace("\n\t", "\n\t\t") + r += "end\n\n" + + for port in memory.ports: + if port.async_read: + r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(port.adr) + "];\n" + else: + if port.mode == WRITE_FIRST and port.we is not None: + r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(adr_regs[id(port)]) + "];\n" + else: + r += "assign " + gn(port.dat_r) + " = " + gn(data_regs[id(port)]) + ";\n" + r += "\n" + + if memory.init is not None: + content = "" + for d in memory.init: + content += "{:x}\n".format(d) + memory_filename = add_data_file(gn(memory) + ".init", content) + + r += "initial begin\n" + r += "\t$readmemh(\"" + memory_filename + "\", " + gn(memory) + ");\n" + r += "end\n\n" + + return r + + +class SynthesisDirective(Special): + def __init__(self, template, **signals): + Special.__init__(self) + self.template = template + self.signals = signals + + @staticmethod + def emit_verilog(directive, ns, add_data_file): + name_dict = dict((k, ns.get_name(sig)) for k, sig in directive.signals.items()) + formatted = directive.template.format(**name_dict) + return "// synthesis " + formatted + "\n" + + +class Keep(SynthesisDirective): + def __init__(self, signal): + SynthesisDirective.__init__(self, "attribute keep of {s} is true", s=signal) diff --git a/litex/gen/migen/fhdl/structure.py b/litex/gen/migen/fhdl/structure.py new file mode 100644 index 00000000..f4250266 --- /dev/null +++ b/litex/gen/migen/fhdl/structure.py @@ -0,0 +1,713 @@ +import builtins as _builtins +import collections as _collections + +from migen.fhdl import tracer as _tracer +from migen.util.misc import flat_iteration as _flat_iteration + + +class DUID: + """Deterministic Unique IDentifier""" + __next_uid = 0 + def __init__(self): + self.duid = DUID.__next_uid + DUID.__next_uid += 1 + + +class _Value(DUID): + """Base class for operands + + Instances of `_Value` or its subclasses can be operands to + arithmetic, comparison, bitwise, and logic operators. + They can be assigned (:meth:`eq`) or indexed/sliced (using the usual + Python indexing and slicing notation). + + Values created from integers have the minimum bit width to necessary to + represent the integer. + """ + def __bool__(self): + # Special case: Constants and Signals are part of a set or used as + # dictionary keys, and Python needs to check for equality. + if isinstance(self, _Operator) and self.op == "==": + a, b = self.operands + if isinstance(a, Constant) and isinstance(b, Constant): + return a.value == b.value + if isinstance(a, Signal) and isinstance(b, Signal): + return a is b + if (isinstance(a, Constant) and isinstance(b, Signal) + or isinstance(a, Signal) and isinstance(a, Constant)): + return False + raise TypeError("Attempted to convert Migen value to boolean") + + def __invert__(self): + return _Operator("~", [self]) + def __neg__(self): + return _Operator("-", [self]) + + def __add__(self, other): + return _Operator("+", [self, other]) + def __radd__(self, other): + return _Operator("+", [other, self]) + def __sub__(self, other): + return _Operator("-", [self, other]) + def __rsub__(self, other): + return _Operator("-", [other, self]) + def __mul__(self, other): + return _Operator("*", [self, other]) + def __rmul__(self, other): + return _Operator("*", [other, self]) + def __lshift__(self, other): + return _Operator("<<<", [self, other]) + def __rlshift__(self, other): + return _Operator("<<<", [other, self]) + def __rshift__(self, other): + return _Operator(">>>", [self, other]) + def __rrshift__(self, other): + return _Operator(">>>", [other, self]) + def __and__(self, other): + return _Operator("&", [self, other]) + def __rand__(self, other): + return _Operator("&", [other, self]) + def __xor__(self, other): + return _Operator("^", [self, other]) + def __rxor__(self, other): + return _Operator("^", [other, self]) + def __or__(self, other): + return _Operator("|", [self, other]) + def __ror__(self, other): + return _Operator("|", [other, self]) + + def __lt__(self, other): + return _Operator("<", [self, other]) + def __le__(self, other): + return _Operator("<=", [self, other]) + def __eq__(self, other): + return _Operator("==", [self, other]) + def __ne__(self, other): + return _Operator("!=", [self, other]) + def __gt__(self, other): + return _Operator(">", [self, other]) + def __ge__(self, other): + return _Operator(">=", [self, other]) + + def __len__(self): + from migen.fhdl.bitcontainer import value_bits_sign + return value_bits_sign(self)[0] + + def __getitem__(self, key): + n = len(self) + if isinstance(key, int): + if key >= n: + raise IndexError + if key < 0: + key += n + return _Slice(self, key, key+1) + elif isinstance(key, slice): + start, stop, step = key.indices(n) + if step != 1: + return Cat(self[i] for i in range(start, stop, step)) + return _Slice(self, start, stop) + else: + raise TypeError + + def eq(self, r): + """Assignment + + Parameters + ---------- + r : _Value, in + Value to be assigned. + + Returns + ------- + _Assign + Assignment statement that can be used in combinatorial or + synchronous context. + """ + return _Assign(self, r) + + def __hash__(self): + raise TypeError("unhashable type: '{}'".format(type(self).__name__)) + + +def wrap(value): + """Ensures that the passed object is a Migen value. Booleans and integers + are automatically wrapped into ``Constant``.""" + if isinstance(value, (bool, int)): + value = Constant(value) + if not isinstance(value, _Value): + raise TypeError("Object is not a Migen value") + return value + + +class _Operator(_Value): + def __init__(self, op, operands): + _Value.__init__(self) + self.op = op + self.operands = [wrap(o) for o in operands] + + +def Mux(sel, val1, val0): + """Multiplex between two values + + Parameters + ---------- + sel : _Value(1), in + Selector. + val1 : _Value(N), in + val0 : _Value(N), in + Input values. + + Returns + ------- + _Value(N), out + Output `_Value`. If `sel` is asserted, the Mux returns + `val1`, else `val0`. + """ + return _Operator("m", [sel, val1, val0]) + + +class _Slice(_Value): + def __init__(self, value, start, stop): + _Value.__init__(self) + if not isinstance(start, int) or not isinstance(stop, int): + raise TypeError("Slice boundaries must be integers") + self.value = wrap(value) + self.start = start + self.stop = stop + + +class Cat(_Value): + """Concatenate values + + Form a compound `_Value` from several smaller ones by concatenation. + The first argument occupies the lower bits of the result. + The return value can be used on either side of an assignment, that + is, the concatenated value can be used as an argument on the RHS or + as a target on the LHS. If it is used on the LHS, it must solely + consist of `Signal` s, slices of `Signal` s, and other concatenations + meeting these properties. The bit length of the return value is the sum of + the bit lengths of the arguments:: + + len(Cat(args)) == sum(len(arg) for arg in args) + + Parameters + ---------- + *args : _Values or iterables of _Values, inout + `_Value` s to be concatenated. + + Returns + ------- + Cat, inout + Resulting `_Value` obtained by concatentation. + """ + def __init__(self, *args): + _Value.__init__(self) + self.l = [wrap(v) for v in _flat_iteration(args)] + + +class Replicate(_Value): + """Replicate a value + + An input value is replicated (repeated) several times + to be used on the RHS of assignments:: + + len(Replicate(s, n)) == len(s)*n + + Parameters + ---------- + v : _Value, in + Input value to be replicated. + n : int + Number of replications. + + Returns + ------- + Replicate, out + Replicated value. + """ + def __init__(self, v, n): + _Value.__init__(self) + if not isinstance(n, int) or n < 0: + raise TypeError("Replication count must be a positive integer") + self.v = wrap(v) + self.n = n + + +class Constant(_Value): + """A constant, HDL-literal integer `_Value` + + Parameters + ---------- + value : int + bits_sign : int or tuple or None + Either an integer `bits` or a tuple `(bits, signed)` + specifying the number of bits in this `Constant` and whether it is + signed (can represent negative values). `bits_sign` defaults + to the minimum width and signedness of `value`. + """ + def __init__(self, value, bits_sign=None): + from migen.fhdl.bitcontainer import bits_for + + _Value.__init__(self) + + self.value = int(value) + if bits_sign is None: + bits_sign = bits_for(self.value), self.value < 0 + elif isinstance(bits_sign, int): + bits_sign = bits_sign, self.value < 0 + self.nbits, self.signed = bits_sign + if not isinstance(self.nbits, int) or self.nbits <= 0: + raise TypeError("Width must be a strictly positive integer") + + def __hash__(self): + return self.value + + +C = Constant # shorthand + + +class Signal(_Value): + """A `_Value` that can change + + The `Signal` object represents a value that is expected to change + in the circuit. It does exactly what Verilog's `wire` and + `reg` and VHDL's `signal` do. + + A `Signal` can be indexed to access a subset of its bits. Negative + indices (`signal[-1]`) and the extended Python slicing notation + (`signal[start:stop:step]`) are supported. + The indices 0 and -1 are the least and most significant bits + respectively. + + Parameters + ---------- + bits_sign : int or tuple + Either an integer `bits` or a tuple `(bits, signed)` + specifying the number of bits in this `Signal` and whether it is + signed (can represent negative values). `signed` defaults to + `False`. + name : str or None + Name hint for this signal. If `None` (default) the name is + inferred from the variable name this `Signal` is assigned to. + Name collisions are automatically resolved by prepending + names of objects that contain this `Signal` and by + appending integer sequences. + variable : bool + Deprecated. + reset : int + Reset (synchronous) or default (combinatorial) value. + When this `Signal` is assigned to in synchronous context and the + corresponding clock domain is reset, the `Signal` assumes the + given value. When this `Signal` is unassigned in combinatorial + context (due to conditional assignments not being taken), + the `Signal` assumes its `reset` value. Defaults to 0. + name_override : str or None + Do not use the inferred name but the given one. + min : int or None + max : int or None + If `bits_sign` is `None`, the signal bit width and signedness are + determined by the integer range given by `min` (inclusive, + defaults to 0) and `max` (exclusive, defaults to 2). + related : Signal or None + """ + def __init__(self, bits_sign=None, name=None, variable=False, reset=0, name_override=None, min=None, max=None, related=None): + from migen.fhdl.bitcontainer import bits_for + + _Value.__init__(self) + + # determine number of bits and signedness + if bits_sign is None: + if min is None: + min = 0 + if max is None: + max = 2 + max -= 1 # make both bounds inclusive + assert(min < max) + self.signed = min < 0 or max < 0 + self.nbits = _builtins.max(bits_for(min, self.signed), bits_for(max, self.signed)) + else: + assert(min is None and max is None) + if isinstance(bits_sign, tuple): + self.nbits, self.signed = bits_sign + else: + self.nbits, self.signed = bits_sign, False + if not isinstance(self.nbits, int) or self.nbits <= 0: + raise ValueError("Signal width must be a strictly positive integer") + + self.variable = variable # deprecated + self.reset = reset + self.name_override = name_override + self.backtrace = _tracer.trace_back(name) + self.related = related + + def __setattr__(self, k, v): + if k == "reset": + v = wrap(v) + _Value.__setattr__(self, k, v) + + def __repr__(self): + return "" + + @classmethod + def like(cls, other, **kwargs): + """Create Signal based on another. + + Parameters + ---------- + other : _Value + Object to base this Signal on. + + See `migen.fhdl.bitcontainer.value_bits_sign` for details. + """ + from migen.fhdl.bitcontainer import value_bits_sign + return cls(bits_sign=value_bits_sign(other), **kwargs) + + def __hash__(self): + return self.duid + + +class ClockSignal(_Value): + """Clock signal for a given clock domain + + `ClockSignal` s for a given clock domain can be retrieved multiple + times. They all ultimately refer to the same signal. + + Parameters + ---------- + cd : str + Clock domain to obtain a clock signal for. Defaults to `"sys"`. + """ + def __init__(self, cd="sys"): + _Value.__init__(self) + if not isinstance(cd, str): + raise TypeError("Argument of ClockSignal must be a string") + self.cd = cd + + +class ResetSignal(_Value): + """Reset signal for a given clock domain + + `ResetSignal` s for a given clock domain can be retrieved multiple + times. They all ultimately refer to the same signal. + + Parameters + ---------- + cd : str + Clock domain to obtain a reset signal for. Defaults to `"sys"`. + allow_reset_less : bool + If the clock domain is resetless, return 0 instead of reporting an + error. + """ + def __init__(self, cd="sys", allow_reset_less=False): + _Value.__init__(self) + if not isinstance(cd, str): + raise TypeError("Argument of ResetSignal must be a string") + self.cd = cd + self.allow_reset_less = allow_reset_less + + +# statements + + +class _Statement: + pass + + +class _Assign(_Statement): + def __init__(self, l, r): + self.l = wrap(l) + self.r = wrap(r) + + +def _check_statement(s): + if isinstance(s, _collections.Iterable): + return all(_check_statement(ss) for ss in s) + else: + return isinstance(s, _Statement) + + +class If(_Statement): + """Conditional execution of statements + + Parameters + ---------- + cond : _Value(1), in + Condition + *t : Statements + Statements to execute if `cond` is asserted. + + Examples + -------- + >>> a = Signal() + >>> b = Signal() + >>> c = Signal() + >>> d = Signal() + >>> If(a, + ... b.eq(1) + ... ).Elif(c, + ... b.eq(0) + ... ).Else( + ... b.eq(d) + ... ) + """ + def __init__(self, cond, *t): + if not _check_statement(t): + raise TypeError("Not all test body objects are Migen statements") + self.cond = wrap(cond) + self.t = list(t) + self.f = [] + + def Else(self, *f): + """Add an `else` conditional block + + Parameters + ---------- + *f : Statements + Statements to execute if all previous conditions fail. + """ + if not _check_statement(f): + raise TypeError("Not all test body objects are Migen statements") + _insert_else(self, list(f)) + return self + + def Elif(self, cond, *t): + """Add an `else if` conditional block + + Parameters + ---------- + cond : _Value(1), in + Condition + *t : Statements + Statements to execute if previous conditions fail and `cond` + is asserted. + """ + _insert_else(self, [If(cond, *t)]) + return self + + +def _insert_else(obj, clause): + o = obj + while o.f: + assert(len(o.f) == 1) + assert(isinstance(o.f[0], If)) + o = o.f[0] + o.f = clause + + +class Case(_Statement): + """Case/Switch statement + + Parameters + ---------- + test : _Value, in + Selector value used to decide which block to execute + cases : dict + Dictionary of cases. The keys are numeric constants to compare + with `test`. The values are statements to be executed the + corresponding key matches `test`. The dictionary may contain a + string key `"default"` to mark a fall-through case that is + executed if no other key matches. + + Examples + -------- + >>> a = Signal() + >>> b = Signal() + >>> Case(a, { + ... 0: b.eq(1), + ... 1: b.eq(0), + ... "default": b.eq(0), + ... }) + """ + def __init__(self, test, cases): + self.test = wrap(test) + self.cases = dict() + for k, v in cases.items(): + if isinstance(k, (bool, int)): + k = Constant(k) + if (not isinstance(k, Constant) + and not (isinstance(k, str) and k == "default")): + raise TypeError("Case object is not a Migen constant") + if not isinstance(v, _collections.Iterable): + v = [v] + if not _check_statement(v): + raise TypeError("Not all objects for case {} " + "are Migen statements".format(k)) + self.cases[k] = v + + def makedefault(self, key=None): + """Mark a key as the default case + + Deletes/substitutes any previously existing default case. + + Parameters + ---------- + key : int or None + Key to use as default case if no other key matches. + By default, the largest key is the default key. + """ + if key is None: + for choice in self.cases.keys(): + if key is None or choice.value > key.value: + key = choice + self.cases["default"] = self.cases[key] + del self.cases[key] + return self + + +# arrays + + +class _ArrayProxy(_Value): + def __init__(self, choices, key): + _Value.__init__(self) + self.choices = [] + for c in choices: + if isinstance(c, (bool, int)): + c = Constant(c) + self.choices.append(c) + self.key = key + + def __getattr__(self, attr): + return _ArrayProxy([getattr(choice, attr) for choice in self.choices], + self.key) + + def __getitem__(self, key): + return _ArrayProxy([choice.__getitem__(key) for choice in self.choices], + self.key) + + +class Array(list): + """Addressable multiplexer + + An array is created from an iterable of values and indexed using the + usual Python simple indexing notation (no negative indices or + slices). It can be indexed by numeric constants, `_Value` s, or + `Signal` s. + + The result of indexing the array is a proxy for the entry at the + given index that can be used on either RHS or LHS of assignments. + + An array can be indexed multiple times. + + Multidimensional arrays are supported by packing inner arrays into + outer arrays. + + Parameters + ---------- + values : iterable of ints, _Values, Signals + Entries of the array. Each entry can be a numeric constant, a + `Signal` or a `Record`. + + Examples + -------- + >>> a = Array(range(10)) + >>> b = Signal(max=10) + >>> c = Signal(max=10) + >>> b.eq(a[9 - c]) + """ + def __getitem__(self, key): + if isinstance(key, Constant): + return list.__getitem__(self, key.value) + elif isinstance(key, _Value): + return _ArrayProxy(self, key) + else: + return list.__getitem__(self, key) + + +class ClockDomain: + """Synchronous domain + + Parameters + ---------- + name : str or None + Domain name. If None (the default) the name is inferred from the + variable name this `ClockDomain` is assigned to (stripping any + `"cd_"` prefix). + reset_less : bool + The domain does not use a reset signal. Registers within this + domain are still all initialized to their reset state once, e.g. + through Verilog `"initial"` statements. + + Attributes + ---------- + clk : Signal, inout + The clock for this domain. Can be driven or used to drive other + signals (preferably in combinatorial context). + rst : Signal or None, inout + Reset signal for this domain. Can be driven or used to drive. + """ + def __init__(self, name=None, reset_less=False): + self.name = _tracer.get_obj_var_name(name) + if self.name is None: + raise ValueError("Cannot extract clock domain name from code, need to specify.") + if self.name.startswith("cd_"): + self.name = self.name[3:] + if self.name[0].isdigit(): + raise ValueError("Clock domain name cannot start with a number.") + self.clk = Signal(name_override=self.name + "_clk") + if reset_less: + self.rst = None + else: + self.rst = Signal(name_override=self.name + "_rst") + + def rename(self, new_name): + """Rename the clock domain + + Parameters + ---------- + new_name : str + New name + """ + self.name = new_name + self.clk.name_override = new_name + "_clk" + if self.rst is not None: + self.rst.name_override = new_name + "_rst" + + +class _ClockDomainList(list): + def __getitem__(self, key): + if isinstance(key, str): + for cd in self: + if cd.name == key: + return cd + raise KeyError(key) + else: + return list.__getitem__(self, key) + + +(SPECIAL_INPUT, SPECIAL_OUTPUT, SPECIAL_INOUT) = range(3) + + +class _Fragment: + def __init__(self, comb=None, sync=None, specials=None, clock_domains=None): + if comb is None: comb = [] + if sync is None: sync = dict() + if specials is None: specials = set() + if clock_domains is None: clock_domains = _ClockDomainList() + + self.comb = comb + self.sync = sync + self.specials = specials + self.clock_domains = _ClockDomainList(clock_domains) + + def __add__(self, other): + newsync = _collections.defaultdict(list) + for k, v in self.sync.items(): + newsync[k] = v[:] + for k, v in other.sync.items(): + newsync[k].extend(v) + return _Fragment(self.comb + other.comb, newsync, + self.specials | other.specials, + self.clock_domains + other.clock_domains) + + def __iadd__(self, other): + newsync = _collections.defaultdict(list) + for k, v in self.sync.items(): + newsync[k] = v[:] + for k, v in other.sync.items(): + newsync[k].extend(v) + self.comb += other.comb + self.sync = newsync + self.specials |= other.specials + self.clock_domains += other.clock_domains + return self diff --git a/litex/gen/migen/fhdl/tools.py b/litex/gen/migen/fhdl/tools.py new file mode 100644 index 00000000..ddc135c2 --- /dev/null +++ b/litex/gen/migen/fhdl/tools.py @@ -0,0 +1,298 @@ +from migen.fhdl.structure import * +from migen.fhdl.structure import _Slice, _Assign +from migen.fhdl.visit import NodeVisitor, NodeTransformer +from migen.fhdl.bitcontainer import value_bits_sign +from migen.util.misc import flat_iteration + + +class _SignalLister(NodeVisitor): + def __init__(self): + self.output_list = set() + + def visit_Signal(self, node): + self.output_list.add(node) + + +class _TargetLister(NodeVisitor): + def __init__(self): + self.output_list = set() + self.target_context = False + + def visit_Signal(self, node): + if self.target_context: + self.output_list.add(node) + + def visit_Assign(self, node): + self.target_context = True + self.visit(node.l) + self.target_context = False + + def visit_ArrayProxy(self, node): + for choice in node.choices: + self.visit(choice) + + +class _InputLister(NodeVisitor): + def __init__(self): + self.output_list = set() + + def visit_Signal(self, node): + self.output_list.add(node) + + def visit_Assign(self, node): + self.visit(node.r) + + +def list_signals(node): + lister = _SignalLister() + lister.visit(node) + return lister.output_list + + +def list_targets(node): + lister = _TargetLister() + lister.visit(node) + return lister.output_list + + +def list_inputs(node): + lister = _InputLister() + lister.visit(node) + return lister.output_list + + +def _resort_statements(ol): + return [statement for i, statement in + sorted(ol, key=lambda x: x[0])] + + +def group_by_targets(sl): + groups = [] + seen = set() + for order, stmt in enumerate(flat_iteration(sl)): + targets = set(list_targets(stmt)) + group = [(order, stmt)] + disjoint = targets.isdisjoint(seen) + seen |= targets + if not disjoint: + groups, old_groups = [], groups + for old_targets, old_group in old_groups: + if targets.isdisjoint(old_targets): + groups.append((old_targets, old_group)) + else: + targets |= old_targets + group += old_group + groups.append((targets, group)) + return [(targets, _resort_statements(stmts)) + for targets, stmts in groups] + + +def list_special_ios(f, ins, outs, inouts): + r = set() + for special in f.specials: + r |= special.list_ios(ins, outs, inouts) + return r + + +class _ClockDomainLister(NodeVisitor): + def __init__(self): + self.clock_domains = set() + + def visit_ClockSignal(self, node): + self.clock_domains.add(node.cd) + + def visit_ResetSignal(self, node): + self.clock_domains.add(node.cd) + + def visit_clock_domains(self, node): + for clockname, statements in node.items(): + self.clock_domains.add(clockname) + self.visit(statements) + + +def list_clock_domains_expr(f): + cdl = _ClockDomainLister() + cdl.visit(f) + return cdl.clock_domains + + +def list_clock_domains(f): + r = list_clock_domains_expr(f) + for special in f.specials: + r |= special.list_clock_domains() + for cd in f.clock_domains: + r.add(cd.name) + return r + + +def is_variable(node): + if isinstance(node, Signal): + return node.variable + elif isinstance(node, _Slice): + return is_variable(node.value) + elif isinstance(node, Cat): + arevars = list(map(is_variable, node.l)) + r = arevars[0] + for x in arevars: + if x != r: + raise TypeError + return r + else: + raise TypeError + + +def generate_reset(rst, sl): + targets = list_targets(sl) + return [t.eq(t.reset) for t in sorted(targets, key=lambda x: x.duid)] + + +def insert_reset(rst, sl): + return [If(rst, *generate_reset(rst, sl)).Else(*sl)] + + +def insert_resets(f): + newsync = dict() + for k, v in f.sync.items(): + if f.clock_domains[k].rst is not None: + newsync[k] = insert_reset(ResetSignal(k), v) + else: + newsync[k] = v + f.sync = newsync + + +class _Lowerer(NodeTransformer): + def __init__(self): + self.target_context = False + self.extra_stmts = [] + self.comb = [] + + def visit_Assign(self, node): + old_target_context, old_extra_stmts = self.target_context, self.extra_stmts + self.extra_stmts = [] + + self.target_context = True + lhs = self.visit(node.l) + self.target_context = False + rhs = self.visit(node.r) + r = _Assign(lhs, rhs) + if self.extra_stmts: + r = [r] + self.extra_stmts + + self.target_context, self.extra_stmts = old_target_context, old_extra_stmts + return r + + +# Basics are FHDL structure elements that back-ends are not required to support +# but can be expressed in terms of other elements (lowered) before conversion. +class _BasicLowerer(_Lowerer): + def __init__(self, clock_domains): + self.clock_domains = clock_domains + _Lowerer.__init__(self) + + def visit_ArrayProxy(self, node): + # TODO: rewrite without variables + array_muxed = Signal(value_bits_sign(node), variable=True) + if self.target_context: + k = self.visit(node.key) + cases = {} + for n, choice in enumerate(node.choices): + cases[n] = [self.visit_Assign(_Assign(choice, array_muxed))] + self.extra_stmts.append(Case(k, cases).makedefault()) + else: + cases = dict((n, _Assign(array_muxed, self.visit(choice))) + for n, choice in enumerate(node.choices)) + self.comb.append(Case(self.visit(node.key), cases).makedefault()) + return array_muxed + + def visit_ClockSignal(self, node): + return self.clock_domains[node.cd].clk + + def visit_ResetSignal(self, node): + rst = self.clock_domains[node.cd].rst + if rst is None: + if node.allow_reset_less: + return 0 + else: + raise ValueError("Attempted to get reset signal of resetless" + " domain '{}'".format(node.cd)) + else: + return rst + + +class _ComplexSliceLowerer(_Lowerer): + def visit_Slice(self, node): + if not isinstance(node.value, Signal): + slice_proxy = Signal(value_bits_sign(node.value)) + if self.target_context: + a = _Assign(node.value, slice_proxy) + else: + a = _Assign(slice_proxy, node.value) + self.comb.append(self.visit_Assign(a)) + node = _Slice(slice_proxy, node.start, node.stop) + return NodeTransformer.visit_Slice(self, node) + + +def _apply_lowerer(l, f): + f = l.visit(f) + f.comb += l.comb + + for special in f.specials: + for obj, attr, direction in special.iter_expressions(): + if direction != SPECIAL_INOUT: + # inouts are only supported by Migen when connected directly to top-level + # in this case, they are Signal and never need lowering + l.comb = [] + l.target_context = direction != SPECIAL_INPUT + l.extra_stmts = [] + expr = getattr(obj, attr) + expr = l.visit(expr) + setattr(obj, attr, expr) + f.comb += l.comb + l.extra_stmts + + return f + + +def lower_basics(f): + return _apply_lowerer(_BasicLowerer(f.clock_domains), f) + + +def lower_complex_slices(f): + return _apply_lowerer(_ComplexSliceLowerer(), f) + + +class _ClockDomainRenamer(NodeVisitor): + def __init__(self, old, new): + self.old = old + self.new = new + + def visit_ClockSignal(self, node): + if node.cd == self.old: + node.cd = self.new + + def visit_ResetSignal(self, node): + if node.cd == self.old: + node.cd = self.new + + +def rename_clock_domain_expr(f, old, new): + cdr = _ClockDomainRenamer(old, new) + cdr.visit(f) + + +def rename_clock_domain(f, old, new): + rename_clock_domain_expr(f, old, new) + if new != old: + if old in f.sync: + if new in f.sync: + f.sync[new].extend(f.sync[old]) + else: + f.sync[new] = f.sync[old] + del f.sync[old] + for special in f.specials: + special.rename_clock_domain(old, new) + try: + cd = f.clock_domains[old] + except KeyError: + pass + else: + cd.rename(new) diff --git a/litex/gen/migen/fhdl/tracer.py b/litex/gen/migen/fhdl/tracer.py new file mode 100644 index 00000000..a394f93f --- /dev/null +++ b/litex/gen/migen/fhdl/tracer.py @@ -0,0 +1,115 @@ +import inspect +from opcode import opname +from collections import defaultdict + + +def get_var_name(frame): + code = frame.f_code + call_index = frame.f_lasti + call_opc = opname[code.co_code[call_index]] + if call_opc != "CALL_FUNCTION" and call_opc != "CALL_FUNCTION_VAR": + return None + index = call_index+3 + while True: + opc = opname[code.co_code[index]] + if opc == "STORE_NAME" or opc == "STORE_ATTR": + name_index = int(code.co_code[index+1]) + return code.co_names[name_index] + elif opc == "STORE_FAST": + name_index = int(code.co_code[index+1]) + return code.co_varnames[name_index] + elif opc == "STORE_DEREF": + name_index = int(code.co_code[index+1]) + return code.co_cellvars[name_index] + elif opc == "LOAD_GLOBAL" or opc == "LOAD_ATTR" or opc == "LOAD_FAST" or opc == "LOAD_DEREF": + index += 3 + elif opc == "DUP_TOP": + index += 1 + elif opc == "BUILD_LIST": + index += 3 + else: + return None + + +def remove_underscore(s): + if len(s) > 2 and s[0] == "_" and s[1] != "_": + s = s[1:] + return s + + +def get_obj_var_name(override=None, default=None): + if override: + return override + + frame = inspect.currentframe().f_back + # We can be called via derived classes. Go back the stack frames + # until we reach the first class that does not inherit from us. + ourclass = frame.f_locals["self"].__class__ + while "self" in frame.f_locals and isinstance(frame.f_locals["self"], ourclass): + frame = frame.f_back + + vn = get_var_name(frame) + if vn is None: + vn = default + else: + vn = remove_underscore(vn) + return vn + +name_to_idx = defaultdict(int) +classname_to_objs = dict() + + +def index_id(l, obj): + for n, e in enumerate(l): + if id(e) == id(obj): + return n + raise ValueError + + +def trace_back(varname=None): + l = [] + frame = inspect.currentframe().f_back.f_back + while frame is not None: + if varname is None: + varname = get_var_name(frame) + if varname is not None: + varname = remove_underscore(varname) + l.insert(0, (varname, name_to_idx[varname])) + name_to_idx[varname] += 1 + + try: + obj = frame.f_locals["self"] + except KeyError: + obj = None + if hasattr(obj, "__del__"): + obj = None + + if obj is None: + if varname is not None: + coname = frame.f_code.co_name + if coname == "": + modules = frame.f_globals["__name__"] + modules = modules.split(".") + coname = modules[len(modules)-1] + coname = remove_underscore(coname) + l.insert(0, (coname, name_to_idx[coname])) + name_to_idx[coname] += 1 + else: + classname = obj.__class__.__name__.lower() + try: + objs = classname_to_objs[classname] + except KeyError: + classname_to_objs[classname] = [obj] + idx = 0 + else: + try: + idx = index_id(objs, obj) + except ValueError: + idx = len(objs) + objs.append(obj) + classname = remove_underscore(classname) + l.insert(0, (classname, idx)) + + varname = None + frame = frame.f_back + return l diff --git a/litex/gen/migen/fhdl/verilog.py b/litex/gen/migen/fhdl/verilog.py new file mode 100644 index 00000000..19bba1fa --- /dev/null +++ b/litex/gen/migen/fhdl/verilog.py @@ -0,0 +1,361 @@ +from functools import partial +from operator import itemgetter +import collections + +from migen.fhdl.structure import * +from migen.fhdl.structure import _Operator, _Slice, _Assign, _Fragment +from migen.fhdl.tools import * +from migen.fhdl.bitcontainer import bits_for +from migen.fhdl.namer import build_namespace +from migen.fhdl.conv_output import ConvOutput + + +_reserved_keywords = { + "always", "and", "assign", "automatic", "begin", "buf", "bufif0", "bufif1", + "case", "casex", "casez", "cell", "cmos", "config", "deassign", "default", + "defparam", "design", "disable", "edge", "else", "end", "endcase", + "endconfig", "endfunction", "endgenerate", "endmodule", "endprimitive", + "endspecify", "endtable", "endtask", "event", "for", "force", "forever", + "fork", "function", "generate", "genvar", "highz0", "highz1", "if", + "ifnone", "incdir", "include", "initial", "inout", "input", + "instance", "integer", "join", "large", "liblist", "library", "localparam", + "macromodule", "medium", "module", "nand", "negedge", "nmos", "nor", + "noshowcancelled", "not", "notif0", "notif1", "or", "output", "parameter", + "pmos", "posedge", "primitive", "pull0", "pull1" "pulldown", + "pullup", "pulsestyle_onevent", "pulsestyle_ondetect", "remos", "real", + "realtime", "reg", "release", "repeat", "rnmos", "rpmos", "rtran", + "rtranif0", "rtranif1", "scalared", "showcancelled", "signed", "small", + "specify", "specparam", "strong0", "strong1", "supply0", "supply1", + "table", "task", "time", "tran", "tranif0", "tranif1", "tri", "tri0", + "tri1", "triand", "trior", "trireg", "unsigned", "use", "vectored", "wait", + "wand", "weak0", "weak1", "while", "wire", "wor","xnor", "xor" +} + + +def _printsig(ns, s): + if s.signed: + n = "signed " + else: + n = "" + if len(s) > 1: + n += "[" + str(len(s)-1) + ":0] " + n += ns.get_name(s) + return n + + +def _printconstant(node): + if node.signed: + return (str(node.nbits) + "'sd" + str(2**node.nbits + node.value), + True) + else: + return str(node.nbits) + "'d" + str(node.value), False + + +def _printexpr(ns, node): + if isinstance(node, Constant): + return _printconstant(node) + elif isinstance(node, Signal): + return ns.get_name(node), node.signed + elif isinstance(node, _Operator): + arity = len(node.operands) + r1, s1 = _printexpr(ns, node.operands[0]) + if arity == 1: + if node.op == "-": + if s1: + r = node.op + r1 + else: + r = "-$signed({1'd0, " + r1 + "})" + s = True + else: + r = node.op + r1 + s = s1 + elif arity == 2: + r2, s2 = _printexpr(ns, node.operands[1]) + if node.op not in ["<<<", ">>>"]: + if s2 and not s1: + r1 = "$signed({1'd0, " + r1 + "})" + if s1 and not s2: + r2 = "$signed({1'd0, " + r2 + "})" + r = r1 + " " + node.op + " " + r2 + s = s1 or s2 + elif arity == 3: + assert node.op == "m" + r2, s2 = _printexpr(ns, node.operands[1]) + r3, s3 = _printexpr(ns, node.operands[2]) + if s2 and not s3: + r3 = "$signed({1'd0, " + r3 + "})" + if s3 and not s2: + r2 = "$signed({1'd0, " + r2 + "})" + r = r1 + " ? " + r2 + " : " + r3 + s = s2 or s3 + else: + raise TypeError + return "(" + r + ")", s + elif isinstance(node, _Slice): + # Verilog does not like us slicing non-array signals... + if isinstance(node.value, Signal) \ + and len(node.value) == 1 \ + and node.start == 0 and node.stop == 1: + return _printexpr(ns, node.value) + + if node.start + 1 == node.stop: + sr = "[" + str(node.start) + "]" + else: + sr = "[" + str(node.stop-1) + ":" + str(node.start) + "]" + r, s = _printexpr(ns, node.value) + return r + sr, s + elif isinstance(node, Cat): + l = [_printexpr(ns, v)[0] for v in reversed(node.l)] + return "{" + ", ".join(l) + "}", False + elif isinstance(node, Replicate): + return "{" + str(node.n) + "{" + _printexpr(ns, node.v)[0] + "}}", False + else: + raise TypeError("Expression of unrecognized type: '{}'".format(type(node).__name__)) + + +(_AT_BLOCKING, _AT_NONBLOCKING, _AT_SIGNAL) = range(3) + + +def _printnode(ns, at, level, node): + if node is None: + return "" + elif isinstance(node, _Assign): + if at == _AT_BLOCKING: + assignment = " = " + elif at == _AT_NONBLOCKING: + assignment = " <= " + elif is_variable(node.l): + assignment = " = " + else: + assignment = " <= " + return "\t"*level + _printexpr(ns, node.l)[0] + assignment + _printexpr(ns, node.r)[0] + ";\n" + elif isinstance(node, collections.Iterable): + return "".join(list(map(partial(_printnode, ns, at, level), node))) + elif isinstance(node, If): + r = "\t"*level + "if (" + _printexpr(ns, node.cond)[0] + ") begin\n" + r += _printnode(ns, at, level + 1, node.t) + if node.f: + r += "\t"*level + "end else begin\n" + r += _printnode(ns, at, level + 1, node.f) + r += "\t"*level + "end\n" + return r + elif isinstance(node, Case): + if node.cases: + r = "\t"*level + "case (" + _printexpr(ns, node.test)[0] + ")\n" + css = [(k, v) for k, v in node.cases.items() if isinstance(k, Constant)] + css = sorted(css, key=lambda x: x[0].value) + for choice, statements in css: + r += "\t"*(level + 1) + _printexpr(ns, choice)[0] + ": begin\n" + r += _printnode(ns, at, level + 2, statements) + r += "\t"*(level + 1) + "end\n" + if "default" in node.cases: + r += "\t"*(level + 1) + "default: begin\n" + r += _printnode(ns, at, level + 2, node.cases["default"]) + r += "\t"*(level + 1) + "end\n" + r += "\t"*level + "endcase\n" + return r + else: + return "" + else: + raise TypeError("Node of unrecognized type: "+str(type(node))) + + +def _list_comb_wires(f): + r = set() + groups = group_by_targets(f.comb) + for g in groups: + if len(g[1]) == 1 and isinstance(g[1][0], _Assign): + r |= g[0] + return r + + +def _printheader(f, ios, name, ns, + reg_initialization): + sigs = list_signals(f) | list_special_ios(f, True, True, True) + special_outs = list_special_ios(f, False, True, True) + inouts = list_special_ios(f, False, False, True) + targets = list_targets(f) | special_outs + wires = _list_comb_wires(f) | special_outs + r = "module " + name + "(\n" + firstp = True + for sig in sorted(ios, key=lambda x: x.duid): + if not firstp: + r += ",\n" + firstp = False + if sig in inouts: + r += "\tinout " + _printsig(ns, sig) + elif sig in targets: + if sig in wires: + r += "\toutput " + _printsig(ns, sig) + else: + r += "\toutput reg " + _printsig(ns, sig) + else: + r += "\tinput " + _printsig(ns, sig) + r += "\n);\n\n" + for sig in sorted(sigs - ios, key=lambda x: x.duid): + if sig in wires: + r += "wire " + _printsig(ns, sig) + ";\n" + else: + if reg_initialization: + r += "reg " + _printsig(ns, sig) + " = " + _printexpr(ns, sig.reset)[0] + ";\n" + else: + r += "reg " + _printsig(ns, sig) + ";\n" + r += "\n" + return r + + +def _printcomb(f, ns, + display_run, + dummy_signal, + blocking_assign): + r = "" + if f.comb: + if dummy_signal: + # Generate a dummy event to get the simulator + # to run the combinatorial process once at the beginning. + syn_off = "// synthesis translate_off\n" + syn_on = "// synthesis translate_on\n" + dummy_s = Signal(name_override="dummy_s") + r += syn_off + r += "reg " + _printsig(ns, dummy_s) + ";\n" + r += "initial " + ns.get_name(dummy_s) + " <= 1'd0;\n" + r += syn_on + + groups = group_by_targets(f.comb) + + for n, g in enumerate(groups): + if len(g[1]) == 1 and isinstance(g[1][0], _Assign): + r += "assign " + _printnode(ns, _AT_BLOCKING, 0, g[1][0]) + else: + if dummy_signal: + dummy_d = Signal(name_override="dummy_d") + r += "\n" + syn_off + r += "reg " + _printsig(ns, dummy_d) + ";\n" + r += syn_on + + r += "always @(*) begin\n" + if display_run: + r += "\t$display(\"Running comb block #" + str(n) + "\");\n" + if blocking_assign: + for t in g[0]: + r += "\t" + ns.get_name(t) + " = " + _printexpr(ns, t.reset)[0] + ";\n" + r += _printnode(ns, _AT_BLOCKING, 1, g[1]) + else: + for t in g[0]: + r += "\t" + ns.get_name(t) + " <= " + _printexpr(ns, t.reset)[0] + ";\n" + r += _printnode(ns, _AT_NONBLOCKING, 1, g[1]) + if dummy_signal: + r += syn_off + r += "\t" + ns.get_name(dummy_d) + " <= " + ns.get_name(dummy_s) + ";\n" + r += syn_on + r += "end\n" + r += "\n" + return r + + +def _printsync(f, ns): + r = "" + for k, v in sorted(f.sync.items(), key=itemgetter(0)): + r += "always @(posedge " + ns.get_name(f.clock_domains[k].clk) + ") begin\n" + r += _printnode(ns, _AT_SIGNAL, 1, v) + r += "end\n\n" + return r + + +def _call_special_classmethod(overrides, obj, method, *args, **kwargs): + cl = obj.__class__ + if cl in overrides: + cl = overrides[cl] + if hasattr(cl, method): + return getattr(cl, method)(obj, *args, **kwargs) + else: + return None + + +def _lower_specials_step(overrides, specials): + f = _Fragment() + lowered_specials = set() + for special in sorted(specials, key=lambda x: x.duid): + impl = _call_special_classmethod(overrides, special, "lower") + if impl is not None: + f += impl.get_fragment() + lowered_specials.add(special) + return f, lowered_specials + + +def _can_lower(overrides, specials): + for special in specials: + cl = special.__class__ + if cl in overrides: + cl = overrides[cl] + if hasattr(cl, "lower"): + return True + return False + + +def _lower_specials(overrides, specials): + f, lowered_specials = _lower_specials_step(overrides, specials) + while _can_lower(overrides, f.specials): + f2, lowered_specials2 = _lower_specials_step(overrides, f.specials) + f += f2 + lowered_specials |= lowered_specials2 + f.specials -= lowered_specials2 + return f, lowered_specials + + +def _printspecials(overrides, specials, ns, add_data_file): + r = "" + for special in sorted(specials, key=lambda x: x.duid): + pr = _call_special_classmethod(overrides, special, "emit_verilog", ns, add_data_file) + if pr is None: + raise NotImplementedError("Special " + str(special) + " failed to implement emit_verilog") + r += pr + return r + + +def convert(f, ios=None, name="top", + special_overrides=dict(), + create_clock_domains=True, + display_run=False, asic_syntax=False): + r = ConvOutput() + if not isinstance(f, _Fragment): + f = f.get_fragment() + if ios is None: + ios = set() + + for cd_name in sorted(list_clock_domains(f)): + try: + f.clock_domains[cd_name] + except KeyError: + if create_clock_domains: + cd = ClockDomain(cd_name) + f.clock_domains.append(cd) + ios |= {cd.clk, cd.rst} + else: + raise KeyError("Unresolved clock domain: '"+cd_name+"'") + + f = lower_complex_slices(f) + insert_resets(f) + f = lower_basics(f) + fs, lowered_specials = _lower_specials(special_overrides, f.specials) + f += lower_basics(fs) + + ns = build_namespace(list_signals(f) \ + | list_special_ios(f, True, True, True) \ + | ios, _reserved_keywords) + ns.clock_domains = f.clock_domains + r.ns = ns + + src = "/* Machine-generated using Migen */\n" + src += _printheader(f, ios, name, ns, + reg_initialization=not asic_syntax) + src += _printcomb(f, ns, + display_run=display_run, + dummy_signal=not asic_syntax, + blocking_assign=asic_syntax) + src += _printsync(f, ns) + src += _printspecials(special_overrides, f.specials - lowered_specials, ns, r.add_data_file) + src += "endmodule\n" + r.set_main_source(src) + + return r diff --git a/litex/gen/migen/fhdl/visit.py b/litex/gen/migen/fhdl/visit.py new file mode 100644 index 00000000..dd3ebbd7 --- /dev/null +++ b/litex/gen/migen/fhdl/visit.py @@ -0,0 +1,202 @@ +from copy import copy + +from migen.fhdl.structure import * +from migen.fhdl.structure import (_Operator, _Slice, _Assign, _ArrayProxy, + _Fragment) + + +class NodeVisitor: + def visit(self, node): + if isinstance(node, Constant): + self.visit_Constant(node) + elif isinstance(node, Signal): + self.visit_Signal(node) + elif isinstance(node, ClockSignal): + self.visit_ClockSignal(node) + elif isinstance(node, ResetSignal): + self.visit_ResetSignal(node) + elif isinstance(node, _Operator): + self.visit_Operator(node) + elif isinstance(node, _Slice): + self.visit_Slice(node) + elif isinstance(node, Cat): + self.visit_Cat(node) + elif isinstance(node, Replicate): + self.visit_Replicate(node) + elif isinstance(node, _Assign): + self.visit_Assign(node) + elif isinstance(node, If): + self.visit_If(node) + elif isinstance(node, Case): + self.visit_Case(node) + elif isinstance(node, _Fragment): + self.visit_Fragment(node) + elif isinstance(node, (list, tuple)): + self.visit_statements(node) + elif isinstance(node, dict): + self.visit_clock_domains(node) + elif isinstance(node, _ArrayProxy): + self.visit_ArrayProxy(node) + elif node is not None: + self.visit_unknown(node) + + def visit_Constant(self, node): + pass + + def visit_Signal(self, node): + pass + + def visit_ClockSignal(self, node): + pass + + def visit_ResetSignal(self, node): + pass + + def visit_Operator(self, node): + for o in node.operands: + self.visit(o) + + def visit_Slice(self, node): + self.visit(node.value) + + def visit_Cat(self, node): + for e in node.l: + self.visit(e) + + def visit_Replicate(self, node): + self.visit(node.v) + + def visit_Assign(self, node): + self.visit(node.l) + self.visit(node.r) + + def visit_If(self, node): + self.visit(node.cond) + self.visit(node.t) + self.visit(node.f) + + def visit_Case(self, node): + self.visit(node.test) + for v, statements in node.cases.items(): + self.visit(statements) + + def visit_Fragment(self, node): + self.visit(node.comb) + self.visit(node.sync) + + def visit_statements(self, node): + for statement in node: + self.visit(statement) + + def visit_clock_domains(self, node): + for clockname, statements in node.items(): + self.visit(statements) + + def visit_ArrayProxy(self, node): + for choice in node.choices: + self.visit(choice) + self.visit(node.key) + + def visit_unknown(self, node): + pass + + +# Default methods always copy the node, except for: +# - Signals, ClockSignals and ResetSignals +# - Unknown objects +# - All fragment fields except comb and sync +# In those cases, the original node is returned unchanged. +class NodeTransformer: + def visit(self, node): + if isinstance(node, Constant): + return self.visit_Constant(node) + elif isinstance(node, Signal): + return self.visit_Signal(node) + elif isinstance(node, ClockSignal): + return self.visit_ClockSignal(node) + elif isinstance(node, ResetSignal): + return self.visit_ResetSignal(node) + elif isinstance(node, _Operator): + return self.visit_Operator(node) + elif isinstance(node, _Slice): + return self.visit_Slice(node) + elif isinstance(node, Cat): + return self.visit_Cat(node) + elif isinstance(node, Replicate): + return self.visit_Replicate(node) + elif isinstance(node, _Assign): + return self.visit_Assign(node) + elif isinstance(node, If): + return self.visit_If(node) + elif isinstance(node, Case): + return self.visit_Case(node) + elif isinstance(node, _Fragment): + return self.visit_Fragment(node) + elif isinstance(node, (list, tuple)): + return self.visit_statements(node) + elif isinstance(node, dict): + return self.visit_clock_domains(node) + elif isinstance(node, _ArrayProxy): + return self.visit_ArrayProxy(node) + elif node is not None: + return self.visit_unknown(node) + else: + return None + + def visit_Constant(self, node): + return node + + def visit_Signal(self, node): + return node + + def visit_ClockSignal(self, node): + return node + + def visit_ResetSignal(self, node): + return node + + def visit_Operator(self, node): + return _Operator(node.op, [self.visit(o) for o in node.operands]) + + def visit_Slice(self, node): + return _Slice(self.visit(node.value), node.start, node.stop) + + def visit_Cat(self, node): + return Cat(*[self.visit(e) for e in node.l]) + + def visit_Replicate(self, node): + return Replicate(self.visit(node.v), node.n) + + def visit_Assign(self, node): + return _Assign(self.visit(node.l), self.visit(node.r)) + + def visit_If(self, node): + r = If(self.visit(node.cond)) + r.t = self.visit(node.t) + r.f = self.visit(node.f) + return r + + def visit_Case(self, node): + cases = dict((v, self.visit(statements)) for v, statements in node.cases.items()) + r = Case(self.visit(node.test), cases) + return r + + def visit_Fragment(self, node): + r = copy(node) + r.comb = self.visit(node.comb) + r.sync = self.visit(node.sync) + return r + + # NOTE: this will always return a list, even if node is a tuple + def visit_statements(self, node): + return [self.visit(statement) for statement in node] + + def visit_clock_domains(self, node): + return dict((clockname, self.visit(statements)) for clockname, statements in node.items()) + + def visit_ArrayProxy(self, node): + return _ArrayProxy([self.visit(choice) for choice in node.choices], + self.visit(node.key)) + + def visit_unknown(self, node): + return node diff --git a/litex/gen/migen/genlib/__init__.py b/litex/gen/migen/genlib/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/litex/gen/migen/genlib/cdc.py b/litex/gen/migen/genlib/cdc.py new file mode 100644 index 00000000..c5342b7b --- /dev/null +++ b/litex/gen/migen/genlib/cdc.py @@ -0,0 +1,141 @@ +from migen.fhdl.structure import * +from migen.fhdl.module import Module +from migen.fhdl.specials import Special +from migen.fhdl.bitcontainer import value_bits_sign +from migen.genlib.misc import WaitTimer + + +class NoRetiming(Special): + def __init__(self, reg): + Special.__init__(self) + self.reg = reg + + # do nothing + @staticmethod + def lower(dr): + return Module() + + +class MultiRegImpl(Module): + def __init__(self, i, o, odomain, n): + self.i = i + self.o = o + self.odomain = odomain + + w, signed = value_bits_sign(self.i) + self.regs = [Signal((w, signed)) for i in range(n)] + + ### + + src = self.i + for reg in self.regs: + sd = getattr(self.sync, self.odomain) + sd += reg.eq(src) + src = reg + self.comb += self.o.eq(src) + self.specials += [NoRetiming(reg) for reg in self.regs] + + +class MultiReg(Special): + def __init__(self, i, o, odomain="sys", n=2): + Special.__init__(self) + self.i = wrap(i) + self.o = wrap(o) + self.odomain = odomain + self.n = n + + def iter_expressions(self): + yield self, "i", SPECIAL_INPUT + yield self, "o", SPECIAL_OUTPUT + + def rename_clock_domain(self, old, new): + Special.rename_clock_domain(self, old, new) + if self.odomain == old: + self.odomain = new + + def list_clock_domains(self): + r = Special.list_clock_domains(self) + r.add(self.odomain) + return r + + @staticmethod + def lower(dr): + return MultiRegImpl(dr.i, dr.o, dr.odomain, dr.n) + + +class PulseSynchronizer(Module): + def __init__(self, idomain, odomain): + self.i = Signal() + self.o = Signal() + + ### + + toggle_i = Signal() + toggle_o = Signal() + toggle_o_r = Signal() + + sync_i = getattr(self.sync, idomain) + sync_o = getattr(self.sync, odomain) + + sync_i += If(self.i, toggle_i.eq(~toggle_i)) + self.specials += MultiReg(toggle_i, toggle_o, odomain) + sync_o += toggle_o_r.eq(toggle_o) + self.comb += self.o.eq(toggle_o ^ toggle_o_r) + + +class BusSynchronizer(Module): + """Clock domain transfer of several bits at once. + + Ensures that all the bits form a single word that was present + synchronously in the input clock domain (unlike direct use of + ``MultiReg``).""" + def __init__(self, width, idomain, odomain, timeout=128): + self.i = Signal(width) + self.o = Signal(width) + + if width == 1: + self.specials += MultiReg(self.i, self.o, odomain) + else: + sync_i = getattr(self.sync, idomain) + sync_o = getattr(self.sync, odomain) + + starter = Signal(reset=1) + sync_i += starter.eq(0) + self.submodules._ping = PulseSynchronizer(idomain, odomain) + self.submodules._pong = PulseSynchronizer(odomain, idomain) + self.submodules._timeout = WaitTimer(timeout) + self.comb += [ + self._timeout.wait.eq(~self._ping.i), + self._ping.i.eq(starter | self._pong.o | self._timeout.done), + self._pong.i.eq(self._ping.i) + ] + + ibuffer = Signal(width) + obuffer = Signal(width) + sync_i += If(self._pong.o, ibuffer.eq(self.i)) + self.specials += MultiReg(ibuffer, obuffer, odomain) + sync_o += If(self._ping.o, self.o.eq(obuffer)) + + +class GrayCounter(Module): + def __init__(self, width): + self.ce = Signal() + self.q = Signal(width) + self.q_next = Signal(width) + self.q_binary = Signal(width) + self.q_next_binary = Signal(width) + + ### + + self.comb += [ + If(self.ce, + self.q_next_binary.eq(self.q_binary + 1) + ).Else( + self.q_next_binary.eq(self.q_binary) + ), + self.q_next.eq(self.q_next_binary ^ self.q_next_binary[1:]) + ] + self.sync += [ + self.q_binary.eq(self.q_next_binary), + self.q.eq(self.q_next) + ] diff --git a/litex/gen/migen/genlib/coding.py b/litex/gen/migen/genlib/coding.py new file mode 100644 index 00000000..6327b388 --- /dev/null +++ b/litex/gen/migen/genlib/coding.py @@ -0,0 +1,98 @@ +""" +Encoders and decoders between binary and one-hot representation +""" + +from migen.fhdl.structure import * +from migen.fhdl.module import Module + + +class Encoder(Module): + """Encode one-hot to binary + + If `n` is low, the `o` th bit in `i` is asserted, else none or + multiple bits are asserted. + + Parameters + ---------- + width : int + Bit width of the input + + Attributes + ---------- + i : Signal(width), in + One-hot input + o : Signal(max=width), out + Encoded binary + n : Signal(1), out + Invalid, either none or multiple input bits are asserted + """ + def __init__(self, width): + self.i = Signal(width) # one-hot + self.o = Signal(max=max(2, width)) # binary + self.n = Signal() # invalid: none or multiple + act = dict((1< 0: + state = name + for i in range(delay): + if i == delay - 1: + next_state = target + else: + next_state = AnonymousState() + self.act(state, NextState(next_state)) + state = next_state + else: + self.state_aliases[name] = target + + def ongoing(self, state): + is_ongoing = Signal() + self.act(state, is_ongoing.eq(1)) + return is_ongoing + + def _get_signal(self, d, state): + if state not in self.actions: + self.actions[state] = [] + try: + return d[state] + except KeyError: + is_el = Signal() + d[state] = is_el + return is_el + + def before_entering(self, state): + return self._get_signal(self.before_entering_signals, state) + + def before_leaving(self, state): + return self._get_signal(self.before_leaving_signals, state) + + def after_entering(self, state): + signal = self._get_signal(self.after_entering_signals, state) + self.sync += signal.eq(self.before_entering(state)) + return signal + + def after_leaving(self, state): + signal = self._get_signal(self.after_leaving_signals, state) + self.sync += signal.eq(self.before_leaving(state)) + return signal + + def do_finalize(self): + nstates = len(self.actions) + self.encoding = dict((s, n) for n, s in enumerate(self.actions.keys())) + self.state = Signal(max=nstates, reset=self.encoding[self.reset_state]) + self.next_state = Signal(max=nstates) + + ln = _LowerNext(self.next_state, self.encoding, self.state_aliases) + cases = dict((self.encoding[k], ln.visit(v)) for k, v in self.actions.items() if v) + self.comb += [ + self.next_state.eq(self.state), + Case(self.state, cases).makedefault(self.encoding[self.reset_state]) + ] + self.sync += self.state.eq(self.next_state) + for register, next_value_ce, next_value in ln.registers: + self.sync += If(next_value_ce, register.eq(next_value)) + + # drive entering/leaving signals + for state, signal in self.before_leaving_signals.items(): + encoded = self.encoding[state] + self.comb += signal.eq((self.state == encoded) & ~(self.next_state == encoded)) + if self.reset_state in self.after_entering_signals: + self.after_entering_signals[self.reset_state].reset = 1 + for state, signal in self.before_entering_signals.items(): + encoded = self.encoding[state] + self.comb += signal.eq(~(self.state == encoded) & (self.next_state == encoded)) diff --git a/litex/gen/migen/genlib/io.py b/litex/gen/migen/genlib/io.py new file mode 100644 index 00000000..fc930125 --- /dev/null +++ b/litex/gen/migen/genlib/io.py @@ -0,0 +1,96 @@ +from migen.fhdl.structure import * +from migen.fhdl.module import Module +from migen.fhdl.specials import Special + + +class DifferentialInput(Special): + def __init__(self, i_p, i_n, o): + Special.__init__(self) + self.i_p = wrap(i_p) + self.i_n = wrap(i_n) + self.o = wrap(o) + + def iter_expressions(self): + yield self, "i_p", SPECIAL_INPUT + yield self, "i_n", SPECIAL_INPUT + yield self, "o", SPECIAL_OUTPUT + + @staticmethod + def lower(dr): + raise NotImplementedError("Attempted to use a differential input, but platform does not support them") + + +class DifferentialOutput(Special): + def __init__(self, i, o_p, o_n): + Special.__init__(self) + self.i = wrap(i) + self.o_p = wrap(o_p) + self.o_n = wrap(o_n) + + def iter_expressions(self): + yield self, "i", SPECIAL_INPUT + yield self, "o_p", SPECIAL_OUTPUT + yield self, "o_n", SPECIAL_OUTPUT + + @staticmethod + def lower(dr): + raise NotImplementedError("Attempted to use a differential output, but platform does not support them") + + +class CRG(Module): + def __init__(self, clk, rst=0): + self.clock_domains.cd_sys = ClockDomain() + self.clock_domains.cd_por = ClockDomain(reset_less=True) + + if hasattr(clk, "p"): + clk_se = Signal() + self.specials += DifferentialInput(clk.p, clk.n, clk_se) + clk = clk_se + + # Power on Reset (vendor agnostic) + int_rst = Signal(reset=1) + self.sync.por += int_rst.eq(rst) + self.comb += [ + self.cd_sys.clk.eq(clk), + self.cd_por.clk.eq(clk), + self.cd_sys.rst.eq(int_rst) + ] + + +class DDRInput(Special): + def __init__(self, i, o1, o2, clk=ClockSignal()): + Special.__init__(self) + self.i = wrap(i) + self.o1 = wrap(o1) + self.o2 = wrap(o2) + self.clk = wrap(clk) + + def iter_expressions(self): + yield self, "i", SPECIAL_INPUT + yield self, "o1", SPECIAL_OUTPUT + yield self, "o2", SPECIAL_OUTPUT + yield self, "clk", SPECIAL_INPUT + + @staticmethod + def lower(dr): + raise NotImplementedError("Attempted to use a DDR input, but platform does not support them") + + +class DDROutput(Special): + def __init__(self, i1, i2, o, clk=ClockSignal()): + Special.__init__(self) + self.i1 = i1 + self.i2 = i2 + self.o = o + self.clk = clk + + def iter_expressions(self): + yield self, "i1", SPECIAL_INPUT + yield self, "i2", SPECIAL_INPUT + yield self, "o", SPECIAL_OUTPUT + yield self, "clk", SPECIAL_INPUT + + @staticmethod + def lower(dr): + raise NotImplementedError("Attempted to use a DDR output, but platform does not support them") + diff --git a/litex/gen/migen/genlib/misc.py b/litex/gen/migen/genlib/misc.py new file mode 100644 index 00000000..f8f4a7c4 --- /dev/null +++ b/litex/gen/migen/genlib/misc.py @@ -0,0 +1,88 @@ +from migen.fhdl.structure import * +from migen.fhdl.module import Module +from migen.fhdl.bitcontainer import bits_for + + +def split(v, *counts): + r = [] + offset = 0 + for n in counts: + if n != 0: + r.append(v[offset:offset+n]) + else: + r.append(None) + offset += n + return tuple(r) + + +def displacer(signal, shift, output, n=None, reverse=False): + if shift is None: + return output.eq(signal) + if n is None: + n = 2**len(shift) + w = len(signal) + if reverse: + r = reversed(range(n)) + else: + r = range(n) + l = [Replicate(shift == i, w) & signal for i in r] + return output.eq(Cat(*l)) + + +def chooser(signal, shift, output, n=None, reverse=False): + if shift is None: + return output.eq(signal) + if n is None: + n = 2**len(shift) + w = len(output) + cases = {} + for i in range(n): + if reverse: + s = n - i - 1 + else: + s = i + cases[i] = [output.eq(signal[s*w:(s+1)*w])] + return Case(shift, cases).makedefault() + + +def timeline(trigger, events): + lastevent = max([e[0] for e in events]) + counter = Signal(max=lastevent+1) + + counterlogic = If(counter != 0, + counter.eq(counter + 1) + ).Elif(trigger, + counter.eq(1) + ) + # insert counter reset if it doesn't naturally overflow + # (test if lastevent+1 is a power of 2) + if (lastevent & (lastevent + 1)) != 0: + counterlogic = If(counter == lastevent, + counter.eq(0) + ).Else( + counterlogic + ) + + def get_cond(e): + if e[0] == 0: + return trigger & (counter == 0) + else: + return counter == e[0] + sync = [If(get_cond(e), *e[1]) for e in events] + sync.append(counterlogic) + return sync + + +class WaitTimer(Module): + def __init__(self, t): + self.wait = Signal() + self.done = Signal() + + # # # + + count = Signal(bits_for(t), reset=t) + self.comb += self.done.eq(count == 0) + self.sync += \ + If(self.wait, + If(~self.done, count.eq(count - 1)) + ).Else(count.eq(count.reset)) diff --git a/litex/gen/migen/genlib/record.py b/litex/gen/migen/genlib/record.py new file mode 100644 index 00000000..bf6aa619 --- /dev/null +++ b/litex/gen/migen/genlib/record.py @@ -0,0 +1,179 @@ +from migen.fhdl.structure import * +from migen.fhdl.tracer import get_obj_var_name + +from functools import reduce +from operator import or_ + + +(DIR_NONE, DIR_S_TO_M, DIR_M_TO_S) = range(3) + +# Possible layout elements: +# 1. (name, size) +# 2. (name, size, direction) +# 3. (name, sublayout) +# size can be an int, or a (int, bool) tuple for signed numbers +# sublayout must be a list + + +def set_layout_parameters(layout, **layout_dict): + def resolve(p): + if isinstance(p, str): + try: + return layout_dict[p] + except KeyError: + return p + else: + return p + + r = [] + for f in layout: + if isinstance(f[1], (int, tuple, str)): # cases 1/2 + if len(f) == 3: + r.append((f[0], resolve(f[1]), f[2])) + else: + r.append((f[0], resolve(f[1]))) + elif isinstance(f[1], list): # case 3 + r.append((f[0], set_layout_parameters(f[1], **layout_dict))) + else: + raise TypeError + return r + + +def layout_len(layout): + r = 0 + for f in layout: + if isinstance(f[1], (int, tuple)): # cases 1/2 + if len(f) == 3: + fname, fsize, fdirection = f + else: + fname, fsize = f + elif isinstance(f[1], list): # case 3 + fname, fsublayout = f + fsize = layout_len(fsublayout) + else: + raise TypeError + if isinstance(fsize, tuple): + r += fsize[0] + else: + r += fsize + return r + + +def layout_get(layout, name): + for f in layout: + if f[0] == name: + return f + raise KeyError(name) + + +def layout_partial(layout, *elements): + r = [] + for path in elements: + path_s = path.split("/") + last = path_s.pop() + copy_ref = layout + insert_ref = r + for hop in path_s: + name, copy_ref = layout_get(copy_ref, hop) + try: + name, insert_ref = layout_get(insert_ref, hop) + except KeyError: + new_insert_ref = [] + insert_ref.append((hop, new_insert_ref)) + insert_ref = new_insert_ref + insert_ref.append(layout_get(copy_ref, last)) + return r + + +class Record: + def __init__(self, layout, name=None): + self.name = get_obj_var_name(name, "") + self.layout = layout + + if self.name: + prefix = self.name + "_" + else: + prefix = "" + for f in self.layout: + if isinstance(f[1], (int, tuple)): # cases 1/2 + if(len(f) == 3): + fname, fsize, fdirection = f + else: + fname, fsize = f + finst = Signal(fsize, name=prefix + fname) + elif isinstance(f[1], list): # case 3 + fname, fsublayout = f + finst = Record(fsublayout, prefix + fname) + else: + raise TypeError + setattr(self, fname, finst) + + def eq(self, other): + return [getattr(self, f[0]).eq(getattr(other, f[0])) + for f in self.layout if hasattr(other, f[0])] + + def iter_flat(self): + for f in self.layout: + e = getattr(self, f[0]) + if isinstance(e, Signal): + if len(f) == 3: + yield e, f[2] + else: + yield e, DIR_NONE + elif isinstance(e, Record): + yield from e.iter_flat() + else: + raise TypeError + + def flatten(self): + return [signal for signal, direction in self.iter_flat()] + + def raw_bits(self): + return Cat(*self.flatten()) + + def connect(self, *slaves, leave_out=set()): + if isinstance(leave_out, str): + leave_out = {leave_out} + r = [] + for f in self.layout: + field = f[0] + if field not in leave_out: + self_e = getattr(self, field) + if isinstance(self_e, Signal): + direction = f[2] + if direction == DIR_M_TO_S: + r += [getattr(slave, field).eq(self_e) for slave in slaves] + elif direction == DIR_S_TO_M: + r.append(self_e.eq(reduce(or_, [getattr(slave, field) for slave in slaves]))) + else: + raise TypeError + else: + for slave in slaves: + r += self_e.connect(getattr(slave, field), leave_out=leave_out) + return r + + def connect_flat(self, *slaves): + r = [] + iter_slaves = [slave.iter_flat() for slave in slaves] + for m_signal, m_direction in self.iter_flat(): + if m_direction == DIR_M_TO_S: + for iter_slave in iter_slaves: + s_signal, s_direction = next(iter_slave) + assert(s_direction == DIR_M_TO_S) + r.append(s_signal.eq(m_signal)) + elif m_direction == DIR_S_TO_M: + s_signals = [] + for iter_slave in iter_slaves: + s_signal, s_direction = next(iter_slave) + assert(s_direction == DIR_S_TO_M) + s_signals.append(s_signal) + r.append(m_signal.eq(reduce(or_, s_signals))) + else: + raise TypeError + return r + + def __len__(self): + return layout_len(self.layout) + + def __repr__(self): + return "" diff --git a/litex/gen/migen/genlib/resetsync.py b/litex/gen/migen/genlib/resetsync.py new file mode 100644 index 00000000..db936900 --- /dev/null +++ b/litex/gen/migen/genlib/resetsync.py @@ -0,0 +1,18 @@ +from migen.fhdl.structure import * +from migen.fhdl.specials import Special + + +class AsyncResetSynchronizer(Special): + def __init__(self, cd, async_reset): + Special.__init__(self) + self.cd = cd + self.async_reset = wrap(async_reset) + + def iter_expressions(self): + yield self.cd, "clk", SPECIAL_INPUT + yield self.cd, "rst", SPECIAL_OUTPUT + yield self, "async_reset", SPECIAL_INPUT + + @staticmethod + def lower(dr): + raise NotImplementedError("Attempted to use a reset synchronizer, but platform does not support them") diff --git a/litex/gen/migen/genlib/roundrobin.py b/litex/gen/migen/genlib/roundrobin.py new file mode 100644 index 00000000..87ac5bb6 --- /dev/null +++ b/litex/gen/migen/genlib/roundrobin.py @@ -0,0 +1,41 @@ +from migen.fhdl.structure import * +from migen.fhdl.module import Module + + +(SP_WITHDRAW, SP_CE) = range(2) + + +class RoundRobin(Module): + def __init__(self, n, switch_policy=SP_WITHDRAW): + self.request = Signal(n) + self.grant = Signal(max=max(2, n)) + self.switch_policy = switch_policy + if self.switch_policy == SP_CE: + self.ce = Signal() + + ### + + if n > 1: + cases = {} + for i in range(n): + switch = [] + for j in reversed(range(i+1, i+n)): + t = j % n + switch = [ + If(self.request[t], + self.grant.eq(t) + ).Else( + *switch + ) + ] + if self.switch_policy == SP_WITHDRAW: + case = [If(~self.request[i], *switch)] + else: + case = switch + cases[i] = case + statement = Case(self.grant, cases) + if self.switch_policy == SP_CE: + statement = If(self.ce, statement) + self.sync += statement + else: + self.comb += self.grant.eq(0) diff --git a/litex/gen/migen/genlib/sort.py b/litex/gen/migen/genlib/sort.py new file mode 100644 index 00000000..8f38e629 --- /dev/null +++ b/litex/gen/migen/genlib/sort.py @@ -0,0 +1,71 @@ +from migen.fhdl.structure import * +from migen.fhdl.module import Module + + +class BitonicSort(Module): + """Combinatorial sorting network + + The Bitonic sort is implemented as a combinatorial sort using + comparators and multiplexers. Its asymptotic complexity (in terms of + number of comparators/muxes) is O(n log(n)**2), like mergesort or + shellsort. + + http://www.dps.uibk.ac.at/~cosenza/teaching/gpu/sort-batcher.pdf + + http://www.inf.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/bitonicen.htm + + http://www.myhdl.org/doku.php/cookbook:bitonic + + Parameters + ---------- + n : int + Number of inputs and output signals. + m : int + Bit width of inputs and outputs. Or a tuple of `(m, signed)`. + ascending : bool + Sort direction. `True` if input is to be sorted ascending, + `False` for descending. Defaults to ascending. + + Attributes + ---------- + i : list of Signals, in + Input values, each `m` wide. + o : list of Signals, out + Output values, sorted, each `m` bits wide. + """ + def __init__(self, n, m, ascending=True): + self.i = [Signal(m) for i in range(n)] + self.o = [Signal(m) for i in range(n)] + self._sort(self.i, self.o, int(ascending), m) + + def _sort_two(self, i0, i1, o0, o1, dir): + self.comb += [ + o0.eq(i0), + o1.eq(i1), + If(dir == (i0 > i1), + o0.eq(i1), + o1.eq(i0), + )] + + def _merge(self, i, o, dir, m): + n = len(i) + k = n//2 + if n > 1: + t = [Signal(m) for j in range(n)] + for j in range(k): + self._sort_two(i[j], i[j + k], t[j], t[j + k], dir) + self._merge(t[:k], o[:k], dir, m) + self._merge(t[k:], o[k:], dir, m) + else: + self.comb += o[0].eq(i[0]) + + def _sort(self, i, o, dir, m): + n = len(i) + k = n//2 + if n > 1: + t = [Signal(m) for j in range(n)] + self._sort(i[:k], t[:k], 1, m) # ascending + self._sort(i[k:], t[k:], 0, m) # descending + self._merge(t, o, dir, m) + else: + self.comb += o[0].eq(i[0]) diff --git a/litex/gen/migen/sim/__init__.py b/litex/gen/migen/sim/__init__.py new file mode 100644 index 00000000..d99780b8 --- /dev/null +++ b/litex/gen/migen/sim/__init__.py @@ -0,0 +1 @@ +from migen.sim.core import Simulator, run_simulation diff --git a/litex/gen/migen/sim/core.py b/litex/gen/migen/sim/core.py new file mode 100644 index 00000000..f89cd02c --- /dev/null +++ b/litex/gen/migen/sim/core.py @@ -0,0 +1,335 @@ +import operator +import collections +import inspect + +from migen.fhdl.structure import * +from migen.fhdl.structure import (_Value, _Statement, + _Operator, _Slice, _ArrayProxy, + _Assign, _Fragment) +from migen.fhdl.bitcontainer import value_bits_sign +from migen.fhdl.tools import list_signals, list_targets, insert_resets +from migen.fhdl.simplify import MemoryToArray +from migen.fhdl.specials import _MemoryLocation +from migen.sim.vcd import VCDWriter, DummyVCDWriter + + +class ClockState: + def __init__(self, high, half_period, time_before_trans): + self.high = high + self.half_period = half_period + self.time_before_trans = time_before_trans + + +class TimeManager: + def __init__(self, description): + self.clocks = dict() + + for k, period_phase in description.items(): + if isinstance(period_phase, tuple): + period, phase = period_phase + else: + period = period_phase + phase = 0 + half_period = period//2 + if phase >= half_period: + phase -= half_period + high = True + else: + high = False + self.clocks[k] = ClockState(high, half_period, half_period - phase) + + def tick(self): + rising = set() + falling = set() + dt = min(cs.time_before_trans for cs in self.clocks.values()) + for k, cs in self.clocks.items(): + if cs.time_before_trans == dt: + cs.high = not cs.high + if cs.high: + rising.add(k) + else: + falling.add(k) + cs.time_before_trans -= dt + if not cs.time_before_trans: + cs.time_before_trans += cs.half_period + return dt, rising, falling + + +str2op = { + "~": operator.invert, + "+": operator.add, + "-": operator.sub, + "*": operator.mul, + + ">>>": operator.rshift, + "<<<": operator.lshift, + + "&": operator.and_, + "^": operator.xor, + "|": operator.or_, + + "<": operator.lt, + "<=": operator.le, + "==": operator.eq, + "!=": operator.ne, + ">": operator.gt, + ">=": operator.ge, +} + + +def _truncate(value, nbits, signed): + value = value & (2**nbits - 1) + if signed and (value & 2**(nbits - 1)): + value -= 2**nbits + return value + + +class Evaluator: + def __init__(self, clock_domains, replaced_memories): + self.clock_domains = clock_domains + self.replaced_memories = replaced_memories + self.signal_values = dict() + self.modifications = dict() + + def commit(self): + r = set() + for k, v in self.modifications.items(): + if k not in self.signal_values or self.signal_values[k] != v: + self.signal_values[k] = v + r.add(k) + self.modifications.clear() + return r + + def eval(self, node, postcommit=False): + if isinstance(node, Constant): + return node.value + elif isinstance(node, Signal): + if postcommit: + try: + return self.modifications[node] + except KeyError: + pass + try: + return self.signal_values[node] + except KeyError: + return node.reset.value + elif isinstance(node, _Operator): + operands = [self.eval(o, postcommit) for o in node.operands] + if node.op == "-": + if len(operands) == 1: + return -operands[0] + else: + return operands[0] - operands[1] + elif node.op == "m": + return operands[1] if operands[0] else operands[2] + else: + return str2op[node.op](*operands) + elif isinstance(node, _Slice): + v = self.eval(node.value, postcommit) + idx = range(node.start, node.stop) + return sum(((v >> i) & 1) << j for j, i in enumerate(idx)) + elif isinstance(node, Cat): + shift = 0 + r = 0 + for element in node.l: + nbits = len(element) + # make value always positive + r |= (self.eval(element, postcommit) & (2**nbits-1)) << shift + shift += nbits + return r + elif isinstance(node, _ArrayProxy): + return self.eval(node.choices[self.eval(node.key, postcommit)], + postcommit) + elif isinstance(node, _MemoryLocation): + array = self.replaced_memories[node.memory] + return self.eval(array[self.eval(node.index, postcommit)], postcommit) + elif isinstance(node, ClockSignal): + return self.eval(self.clock_domains[node.cd].clk, postcommit) + elif isinstance(node, ResetSignal): + rst = self.clock_domains[node.cd].rst + if rst is None: + if node.allow_reset_less: + return 0 + else: + raise ValueError("Attempted to get reset signal of resetless" + " domain '{}'".format(node.cd)) + else: + return self.eval(rst, postcommit) + else: + raise NotImplementedError + + def assign(self, node, value): + if isinstance(node, Signal): + assert not node.variable + self.modifications[node] = _truncate(value, + node.nbits, node.signed) + elif isinstance(node, Cat): + for element in node.l: + nbits = len(element) + self.assign(element, value & (2**nbits-1)) + value >>= nbits + elif isinstance(node, _Slice): + full_value = self.eval(node.value, True) + # clear bits assigned to by the slice + full_value &= ~((2**node.stop-1) - (2**node.start-1)) + # set them to the new value + value &= 2**(node.stop - node.start)-1 + full_value |= value << node.start + self.assign(node.value, full_value) + elif isinstance(node, _ArrayProxy): + self.assign(node.choices[self.eval(node.key)], value) + elif isinstance(node, _MemoryLocation): + array = self.replaced_memories[node.memory] + self.assign(array[self.eval(node.index)], value) + else: + raise NotImplementedError + + def execute(self, statements): + for s in statements: + if isinstance(s, _Assign): + self.assign(s.l, self.eval(s.r)) + elif isinstance(s, If): + if self.eval(s.cond) & (2**len(s.cond) - 1): + self.execute(s.t) + else: + self.execute(s.f) + elif isinstance(s, Case): + nbits, signed = value_bits_sign(s.test) + test = _truncate(self.eval(s.test), nbits, signed) + found = False + for k, v in s.cases.items(): + if isinstance(k, Constant) and k.value == test: + self.execute(v) + found = True + break + if not found and "default" in s.cases: + self.execute(s.cases["default"]) + elif isinstance(s, collections.Iterable): + self.execute(s) + else: + raise NotImplementedError + + +# TODO: instances via Iverilog/VPI +class Simulator: + def __init__(self, fragment_or_module, generators, clocks={"sys": 10}, vcd_name=None): + if isinstance(fragment_or_module, _Fragment): + self.fragment = fragment_or_module + else: + self.fragment = fragment_or_module.get_fragment() + if not isinstance(generators, dict): + generators = {"sys": generators} + self.generators = dict() + for k, v in generators.items(): + if (isinstance(v, collections.Iterable) + and not inspect.isgenerator(v)): + self.generators[k] = list(v) + else: + self.generators[k] = [v] + + self.time = TimeManager(clocks) + for clock in clocks.keys(): + if clock not in self.fragment.clock_domains: + cd = ClockDomain(name=clock, reset_less=True) + cd.clk.reset = C(self.time.clocks[clock].high) + self.fragment.clock_domains.append(cd) + + mta = MemoryToArray() + mta.transform_fragment(None, self.fragment) + insert_resets(self.fragment) + # comb signals return to their reset value if nothing assigns them + self.fragment.comb[0:0] = [s.eq(s.reset) + for s in list_targets(self.fragment.comb)] + self.evaluator = Evaluator(self.fragment.clock_domains, + mta.replacements) + + if vcd_name is None: + self.vcd = DummyVCDWriter() + else: + signals = list_signals(self.fragment) + for cd in self.fragment.clock_domains: + signals.add(cd.clk) + if cd.rst is not None: + signals.add(cd.rst) + for memory_array in mta.replacements.values(): + signals |= set(memory_array) + signals = sorted(signals, key=lambda x: x.duid) + self.vcd = VCDWriter(vcd_name, signals) + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + def close(self): + self.vcd.close() + + def _commit_and_comb_propagate(self): + # TODO: optimize + all_modified = set() + modified = self.evaluator.commit() + all_modified |= modified + while modified: + self.evaluator.execute(self.fragment.comb) + modified = self.evaluator.commit() + all_modified |= modified + for signal in all_modified: + self.vcd.set(signal, self.evaluator.signal_values[signal]) + + def _evalexec_nested_lists(self, x): + if isinstance(x, list): + return [self._evalexec_nested_lists(e) for e in x] + elif isinstance(x, _Value): + return self.evaluator.eval(x) + elif isinstance(x, _Statement): + self.evaluator.execute([x]) + return None + else: + raise ValueError + + def _process_generators(self, cd): + exhausted = [] + for generator in self.generators[cd]: + reply = None + while True: + try: + request = generator.send(reply) + if request is None: + break # next cycle + else: + reply = self._evalexec_nested_lists(request) + except StopIteration: + exhausted.append(generator) + break + for generator in exhausted: + self.generators[cd].remove(generator) + + def _continue_simulation(self): + # TODO: passive generators + return any(self.generators.values()) + + def run(self): + self.evaluator.execute(self.fragment.comb) + self._commit_and_comb_propagate() + + while True: + dt, rising, falling = self.time.tick() + self.vcd.delay(dt) + for cd in rising: + self.evaluator.assign(self.fragment.clock_domains[cd].clk, 1) + if cd in self.fragment.sync: + self.evaluator.execute(self.fragment.sync[cd]) + if cd in self.generators: + self._process_generators(cd) + for cd in falling: + self.evaluator.assign(self.fragment.clock_domains[cd].clk, 0) + self._commit_and_comb_propagate() + + if not self._continue_simulation(): + break + + +def run_simulation(*args, **kwargs): + with Simulator(*args, **kwargs) as s: + s.run() diff --git a/litex/gen/migen/sim/vcd.py b/litex/gen/migen/sim/vcd.py new file mode 100644 index 00000000..f3ea7aee --- /dev/null +++ b/litex/gen/migen/sim/vcd.py @@ -0,0 +1,75 @@ +from itertools import count + +from migen.fhdl.namer import build_namespace + + +def vcd_codes(): + codechars = [chr(i) for i in range(33, 127)] + for n in count(): + q, r = divmod(n, len(codechars)) + code = codechars[r] + while q > 0: + q, r = divmod(q, len(codechars)) + code = codechars[r] + code + yield code + + +class VCDWriter: + def __init__(self, filename, signals): + self.fo = open(filename, "w") + self.codes = dict() + self.signal_values = dict() + self.t = 0 + + try: + ns = build_namespace(signals) + codes = vcd_codes() + for signal in signals: + name = ns.get_name(signal) + code = next(codes) + self.codes[signal] = code + self.fo.write("$var wire {len} {code} {name} $end\n" + .format(name=name, code=code, len=len(signal))) + self.fo.write("$dumpvars\n") + for signal in signals: + value = signal.reset.value + self._write_value(signal, value) + self.signal_values[signal] = value + self.fo.write("$end\n") + self.fo.write("#0\n") + except: + self.close() + raise + + def _write_value(self, signal, value): + l = len(signal) + if value < 0: + value += 2**l + if l > 1: + fmtstr = "b{:0" + str(l) + "b} {}\n" + else: + fmtstr = "{}{}\n" + self.fo.write(fmtstr.format(value, self.codes[signal])) + + def set(self, signal, value): + if self.signal_values[signal] != value: + self._write_value(signal, value) + self.signal_values[signal] = value + + def delay(self, delay): + self.t += delay + self.fo.write("#{}\n".format(self.t)) + + def close(self): + self.fo.close() + + +class DummyVCDWriter: + def set(self, signal, value): + pass + + def delay(self, delay): + pass + + def close(self): + pass diff --git a/litex/gen/migen/test/__init__.py b/litex/gen/migen/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/litex/gen/migen/test/support.py b/litex/gen/migen/test/support.py new file mode 100644 index 00000000..c1bb4697 --- /dev/null +++ b/litex/gen/migen/test/support.py @@ -0,0 +1,13 @@ +from migen import * +from migen.fhdl import verilog + + +class SimCase: + def setUp(self, *args, **kwargs): + self.tb = self.TestBench(*args, **kwargs) + + def test_to_verilog(self): + verilog.convert(self.tb) + + def run_with(self, generator): + run_simulation(self.tb, generator) diff --git a/litex/gen/migen/test/test_coding.py b/litex/gen/migen/test/test_coding.py new file mode 100644 index 00000000..64cfb6fd --- /dev/null +++ b/litex/gen/migen/test/test_coding.py @@ -0,0 +1,113 @@ +import unittest + +from migen import * +from migen.genlib.coding import * + +from migen.test.support import SimCase + + +class EncCase(SimCase, unittest.TestCase): + class TestBench(Module): + def __init__(self): + self.submodules.dut = Encoder(8) + + def test_sizes(self): + self.assertEqual(len(self.tb.dut.i), 8) + self.assertEqual(len(self.tb.dut.o), 3) + self.assertEqual(len(self.tb.dut.n), 1) + + def test_run_sequence(self): + seq = list(range(1<<8)) + def gen(): + for _ in range(256): + if seq: + yield self.tb.dut.i.eq(seq.pop(0)) + if (yield self.tb.dut.n): + self.assertNotIn((yield self.tb.dut.i), [1< 0: + self.assertEqual(i & 1<<(o - 1), 0) + self.assertGreaterEqual(i, 1< 0: + self.assertEqual(i & 1<<(o - 1), 0) + self.assertGreaterEqual(i, 1< q, + lambda p, q: p >= q, + lambda p, q: p < q, + lambda p, q: p <= q, + lambda p, q: p == q, + lambda p, q: p != q, + ] + self.vals = [] + for asign in 1, -1: + for bsign in 1, -1: + for f in comps: + r = Signal() + r0 = f(asign*self.a, bsign*self.b) + self.comb += r.eq(r0) + self.vals.append((asign, bsign, f, r, r0.op)) + + def test_comparisons(self): + def gen(): + for i in range(-4, 4): + yield self.tb.a.eq(i) + yield self.tb.b.eq(i) + a = yield self.tb.a + b = yield self.tb.b + for asign, bsign, f, r, op in self.tb.vals: + r, r0 = (yield r), f(asign*a, bsign*b) + self.assertEqual(r, int(r0), + "got {}, want {}*{} {} {}*{} = {}".format( + r, asign, a, op, bsign, b, r0)) + yield + self.run_with(gen()) diff --git a/litex/gen/migen/test/test_size.py b/litex/gen/migen/test/test_size.py new file mode 100644 index 00000000..a44ae367 --- /dev/null +++ b/litex/gen/migen/test/test_size.py @@ -0,0 +1,19 @@ +import unittest + +from migen import * + + +def _same_slices(a, b): + return a.value is b.value and a.start == b.start and a.stop == b.stop + + +class SignalSizeCase(unittest.TestCase): + def setUp(self): + self.i = C(0xaa) + self.j = C(-127) + self.s = Signal((13, True)) + + def test_len(self): + self.assertEqual(len(self.s), 13) + self.assertEqual(len(self.i), 8) + self.assertEqual(len(self.j), 8) diff --git a/litex/gen/migen/test/test_sort.py b/litex/gen/migen/test/test_sort.py new file mode 100644 index 00000000..acb9fa23 --- /dev/null +++ b/litex/gen/migen/test/test_sort.py @@ -0,0 +1,30 @@ +import unittest +from random import randrange + +from migen import * +from migen.genlib.sort import * + +from migen.test.support import SimCase + + +class BitonicCase(SimCase, unittest.TestCase): + class TestBench(Module): + def __init__(self): + self.submodules.dut = BitonicSort(8, 4, ascending=True) + + def test_sizes(self): + self.assertEqual(len(self.tb.dut.i), 8) + self.assertEqual(len(self.tb.dut.o), 8) + for i in range(8): + self.assertEqual(len(self.tb.dut.i[i]), 4) + self.assertEqual(len(self.tb.dut.o[i]), 4) + + def test_sort(self): + def gen(): + for repeat in range(20): + for i in self.tb.dut.i: + yield i.eq(randrange(1< 2014,2015 + +# assuming your xilinx toolchain lives in /opt/Xilinx, +# run `strace_tailor.sh /opt/Xilinx/ [synthesis script] [options]`, +# e.g. for the pipistrello target of misoc: +# strace_tailor.sh /opt/Xilinx/ ./make.py -t pipistrello build-bitstream +# then in your current directory, `opt/Xilinx/*` is the +# minimal toolchain required for this synthesis script run. + +PREFIX=$1 +shift +strace -e trace=file,process -f -o strace.log $@ +sed -n 's|^.*"\('"$PREFIX"'[^"]*\)".*$|\1|p' strace.log \ + | sort | uniq | xargs -d '\n' \ + cp --parent --no-dereference --preserve=all -t .