Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/nmigen
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: c42c3a096f89
Choose a base ref
...
head repository: m-labs/nmigen
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 7245b1e4c2c8
Choose a head ref
  • 16 commits
  • 17 files changed
  • 5 contributors

Commits on Jan 27, 2020

  1. Update README.

    whitequark authored Jan 27, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c280c7c View commit details

Commits on Jan 31, 2020

  1. Copy the full SHA
    7792a6c View commit details
  2. build.dsl: allow strings to be used as connector numbers.

    Fixes #311.
    anuejn authored and whitequark committed Jan 31, 2020
    Copy the full SHA
    ec3a219 View commit details
  3. vendor.lattice_ecp5: support internal oscillator (OSCG).

    miek authored and whitequark committed Jan 31, 2020
    Copy the full SHA
    b72c3fc View commit details
  4. back.rtlil: don't emit wires for empty signals.

    Fixes #312.
    whitequark committed Jan 31, 2020
    Copy the full SHA
    3ac13eb View commit details
  5. hdl.dsl: make if m.{If,Elif,Else}(...) a syntax error.

    A common typo, and hard to notice when it's silently ignored.
    
    Fixes #284.
    whitequark committed Jan 31, 2020
    Copy the full SHA
    9964fc6 View commit details
  6. README: clarify relationship to Migen.

    whitequark committed Jan 31, 2020
    Copy the full SHA
    a9da9ef View commit details
  7. hdl.dsl: add missing case width check for Enum values.

    Fixes #305.
    whitequark committed Jan 31, 2020
    Copy the full SHA
    687d3a3 View commit details

Commits on Feb 1, 2020

  1. _unused: extract must-use logic from hdl.ir.

    whitequark committed Feb 1, 2020
    Copy the full SHA
    9fb4a4f View commit details
  2. hdl.ast: warn on unused property statements (Assert, Assume, etc).

    A property statement that is created but not added to a module is
    virtually always a serious bug, since it can make formal verification
    pass when it should not. Therefore, add a warning to it, similar to
    UnusedElaboratable.
    
    Doing this to all statements is possible, but many temporary ones are
    created internally by nMigen, and the extensive changes required to
    remove false positives are likely not worth the true positives.
    We can revisit this in the future.
    
    Fixes #303.
    whitequark committed Feb 1, 2020
    Copy the full SHA
    afece15 View commit details
  3. hdl.dsl: don't allow inheriting from Module.

    `Module` is an object with a lot of complex and sometimes fragile
    behavior that overrides Python attribute accessors and so on.
    To prevent user designs from breaking when it is changed, it is not
    supposed to be inherited from (unlike in Migen), but rather returned
    from the elaborate() method. This commit makes sure it will not be
    inherited from by accident (most likely by users familiar with
    Migen).
    
    Fixes #286.
    whitequark committed Feb 1, 2020
    Copy the full SHA
    6fd7cba View commit details
  4. build.plat: align pipeline with Fragment.prepare().

    Since commit 7257c20, platform code calls create_missing_domains()
    before _propagate_domains_up() (as a part of prepare() call). Since
    commit a7be3b4, without a platform, create_missing_domains() is
    calle after _propagate_domains_up(); because of that, it adds
    the missing domain to the fragment. When platform code then calls
    prepare() again, this causes an assertion failure.
    
    The true intent behind the platform code being written this way is
    that it *overrides* a part of prepare()'s mechanism. Because it was
    not changed when prepare() was modified in 7257c20, the override,
    which happened to work by coincidence, stopped working. This is
    now fixed by inlining the relevant parts of Fragment.prepare() into
    Platform.prepare().
    
    This is not a great solution, but given the amount of breakage this
    causes (no platform-using code works), it is acceptable for now.
    
    Fixes #307.
    whitequark committed Feb 1, 2020
    Copy the full SHA
    cce6b86 View commit details
  5. hdl.ast: prohibit shifts by signed value.

    These are not desirable in a HDL, and currently elaborate to broken
    RTLIL (after YosysHQ/yosys#1551); prohibit them completely, like
    we already do for division and modulo.
    
    Fixes #302.
    whitequark committed Feb 1, 2020
    Copy the full SHA
    49758a3 View commit details
  6. hdl.ast: update documentation for Signal.

    Fixes #288.
    whitequark committed Feb 1, 2020
    Copy the full SHA
    a295e35 View commit details

Commits on Feb 2, 2020

  1. Copy the full SHA
    60447a0 View commit details
  2. Update README.

    Symbiotic EDA can get credit after they clarify the sitation with me; contact attempt on 29-JAN-2020 received no reply.
    sbourdeauducq committed Feb 2, 2020
    Copy the full SHA
    7245b1e View commit details
3 changes: 2 additions & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Copyright (C) 2011-2019 M-Labs Limited
Copyright (C) 2011-2020 M-Labs Limited
Copyright (C) 2020 whitequark

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -12,14 +12,15 @@ Other nMigen libraries are built on FHDL and provide various tools and logic cor

See the [doc/](doc/) folder for more technical information.

nMigen is a direct descendant of [Migen][] rewritten from scratch to address many issues that became clear in the many years Migen has been used in production. nMigen provides an extensive [compatibility layer](#migration-from-migen) that makes it possible to build and simulate most Migen designs unmodified, as well as integrate modules written for Migen and nMigen.
nMigen is based on [Migen][], a hardware description language developed by [M-Labs][]. Although Migen works very well in production, its design could be improved in many fundamental ways, and nMigen reimplements Migen concepts from scratch to do so. nMigen also provides an extensive [compatibility layer](#migration-from-migen) that makes it possible to build and simulate most Migen designs unmodified, as well as integrate modules written for Migen and nMigen.

nMigen is designed for Python 3.6 and newer. nMigen's Verilog backend requires [Yosys][] 0.9 or a newer version.

Thanks [LambdaConcept][] for being a sponsor of this project! Contact sb [at] m-labs.hk if you also wish to support this work.
The development of nMigen has been supported by [M-Labs][] and [LambdaConcept][].

[migen]: https://m-labs.hk/migen
[yosys]: http://www.clifford.at/yosys/
[m-labs]: https://m-labs.hk
[lambdaconcept]: http://lambdaconcept.com/

### HLS?
@@ -70,7 +71,7 @@ nMigen is released under the very permissive two-clause BSD license. Under the t

Even though we do not require you to do so, these things are awesome, so please do them if possible:
* tell us that you are using nMigen
* put the [nMigen logo](doc/nmigen_logo.svg) on the page of a product using it, with a link to https://m-labs.hk
* put the [nMigen logo](doc/nmigen_logo.svg) on the page of a product using it, with a link to https://nmigen.org
* cite nMigen in publications related to research it has helped
* send us feedback and suggestions for improvements
* send us bug reports when something goes wrong
45 changes: 45 additions & 0 deletions nmigen/_unused.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import sys
import warnings

from ._utils import get_linter_option


__all__ = ["UnusedMustUse", "MustUse"]


class UnusedMustUse(Warning):
pass


class MustUse:
_MustUse__silence = False
_MustUse__warning = UnusedMustUse

def __new__(cls, *args, src_loc_at=0, **kwargs):
frame = sys._getframe(1 + src_loc_at)
self = super().__new__(cls)
self._MustUse__used = False
self._MustUse__context = dict(
filename=frame.f_code.co_filename,
lineno=frame.f_lineno,
source=self)
return self

def __del__(self):
if self._MustUse__silence:
return
if hasattr(self, "_MustUse__used") and not self._MustUse__used:
if get_linter_option(self._MustUse__context["filename"],
self._MustUse__warning.__name__, bool, True):
warnings.warn_explicit(
"{!r} created but never used".format(self), self._MustUse__warning,
**self._MustUse__context)


_old_excepthook = sys.excepthook
def _silence_elaboratable(type, value, traceback):
# Don't show anything if the interpreter crashed; that'd just obscure the exception
# traceback instead of helping.
MustUse._MustUse__silence = True
_old_excepthook(type, value, traceback)
sys.excepthook = _silence_elaboratable
6 changes: 2 additions & 4 deletions nmigen/back/pysim.py
Original file line number Diff line number Diff line change
@@ -347,8 +347,6 @@ class _ValueCompiler(ValueVisitor, _Compiler):
helpers = {
"sign": lambda value, sign: value | sign if value & sign else value,
"zdiv": lambda lhs, rhs: 0 if rhs == 0 else lhs // rhs,
"sshl": lambda lhs, rhs: lhs << rhs if rhs >= 0 else lhs >> -rhs,
"sshr": lambda lhs, rhs: lhs >> rhs if rhs >= 0 else lhs << -rhs,
}

def on_ClockSignal(self, value):
@@ -438,9 +436,9 @@ def sign(value):
if value.operator == "^":
return f"({self(lhs)} ^ {self(rhs)})"
if value.operator == "<<":
return f"sshl({sign(lhs)}, {sign(rhs)})"
return f"({sign(lhs)} << {sign(rhs)})"
if value.operator == ">>":
return f"sshr({sign(lhs)}, {sign(rhs)})"
return f"({sign(lhs)} >> {sign(rhs)})"
if value.operator == "==":
return f"({sign(lhs)} == {sign(rhs)})"
if value.operator == "!=":
3 changes: 3 additions & 0 deletions nmigen/back/rtlil.py
Original file line number Diff line number Diff line change
@@ -278,6 +278,9 @@ def add_port(self, signal, kind):
self.ports[signal] = (len(self.ports), kind)

def resolve(self, signal, prefix=None):
if len(signal) == 0:
return "{ }", "{ }"

if signal in self.wires:
return self.wires[signal]

10 changes: 6 additions & 4 deletions nmigen/build/dsl.py
Original file line number Diff line number Diff line change
@@ -14,8 +14,9 @@ def __init__(self, names, *, dir="io", invert=False, conn=None, assert_width=Non

if conn is not None:
conn_name, conn_number = conn
if not (isinstance(conn_name, str) and isinstance(conn_number, int)):
raise TypeError("Connector must be None or a pair of string and integer, not {!r}"
if not (isinstance(conn_name, str) and isinstance(conn_number, (int, str))):
raise TypeError("Connector must be None or a pair of string (connector name) and "
"integer/string (connector number), not {!r}"
.format(conn))
names = ["{}_{}:{}".format(conn_name, conn_number, name) for name in names]

@@ -236,8 +237,9 @@ def __init__(self, name, number, io, *, conn=None):

if conn is not None:
conn_name, conn_number = conn
if not (isinstance(conn_name, str) and isinstance(conn_number, int)):
raise TypeError("Connector must be None or a pair of string and integer, not {!r}"
if not (isinstance(conn_name, str) and isinstance(conn_number, (int, str))):
raise TypeError("Connector must be None or a pair of string (connector name) and "
"integer/string (connector number), not {!r}"
.format(conn))

for conn_pin, plat_pin in mapping.items():
7 changes: 5 additions & 2 deletions nmigen/build/plat.py
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
from .. import __version__
from .._toolchain import *
from ..hdl import *
from ..hdl.xfrm import SampleLowerer, DomainLowerer
from ..lib.cdc import ResetSynchronizer
from ..back import rtlil, verilog
from .res import *
@@ -115,7 +116,9 @@ def prepare(self, elaboratable, name="top", **kwargs):
self._prepared = True

fragment = Fragment.get(elaboratable, self)
fragment.create_missing_domains(self.create_missing_domain, platform=self)
fragment = SampleLowerer()(fragment)
fragment._propagate_domains(self.create_missing_domain, platform=self)
fragment = DomainLowerer()(fragment)

def add_pin_fragment(pin, pin_fragment):
pin_fragment = Fragment.get(pin_fragment, self)
@@ -144,7 +147,7 @@ def add_pin_fragment(pin, pin_fragment):
add_pin_fragment(pin,
self.get_diff_input_output(pin, p_port, n_port, attrs, invert))

fragment = fragment.prepare(ports=self.iter_ports(), missing_domain=lambda name: None)
fragment._propagate_ports(ports=self.iter_ports(), all_undef_as_ports=False)
return self.toolchain_prepare(fragment, name, **kwargs)

@abstractmethod
2 changes: 1 addition & 1 deletion nmigen/compat/fhdl/module.py
Original file line number Diff line number Diff line change
@@ -95,7 +95,7 @@ def __iadd__(self, other):


class CompatModule(ir.Elaboratable):
_Elaboratable__silence = True
_MustUse__silence = True

# Actually returns another nMigen Elaboratable (nmigen.dsl.Module), not a Fragment.
def get_fragment(self):
51 changes: 33 additions & 18 deletions nmigen/hdl/ast.py
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@

from .. import tracer
from .._utils import *
from .._unused import *


__all__ = [
@@ -17,9 +18,9 @@
"Signal", "ClockSignal", "ResetSignal",
"UserValue",
"Sample", "Past", "Stable", "Rose", "Fell", "Initial",
"Statement", "Assign", "Assert", "Assume", "Cover", "Switch",
"ValueKey", "ValueDict", "ValueSet", "SignalKey", "SignalDict",
"SignalSet",
"Statement", "Switch",
"Property", "Assign", "Assert", "Assume", "Cover",
"ValueKey", "ValueDict", "ValueSet", "SignalKey", "SignalDict", "SignalSet",
]


@@ -171,14 +172,28 @@ def __rfloordiv__(self, other):
self.__check_divisor()
return Operator("//", [other, self])

def __check_shamt(self):
width, signed = self.shape()
if signed:
# Neither Python nor HDLs implement shifts by negative values; prohibit any shifts
# by a signed value to make sure the shift amount can always be interpreted as
# an unsigned value.
raise NotImplementedError("Shift by a signed value is not supported")
def __lshift__(self, other):
other = Value.cast(other)
other.__check_shamt()
return Operator("<<", [self, other])
def __rlshift__(self, other):
self.__check_shamt()
return Operator("<<", [other, self])
def __rshift__(self, other):
other = Value.cast(other)
other.__check_shamt()
return Operator(">>", [self, other])
def __rrshift__(self, other):
self.__check_shamt()
return Operator(">>", [other, self])

def __and__(self, other):
return Operator("&", [self, other])
def __rand__(self, other):
@@ -493,13 +508,13 @@ def _rhs_signals(self):
@final
class AnyConst(AnyValue):
def __repr__(self):
return "(anyconst {}'{})".format(self.nbits, "s" if self.signed else "")
return "(anyconst {}'{})".format(self.width, "s" if self.signed else "")


@final
class AnySeq(AnyValue):
def __repr__(self):
return "(anyseq {}'{})".format(self.nbits, "s" if self.signed else "")
return "(anyseq {}'{})".format(self.width, "s" if self.signed else "")


@final
@@ -758,15 +773,13 @@ class Signal(Value, DUID):
Parameters
----------
shape : int or tuple or None
Either an integer ``width`` or a tuple ``(width, signed)`` specifying the number of bits
in this ``Signal`` and whether it is signed (can represent negative values).
``shape`` defaults to 1-bit and non-signed.
shape : ``Shape``-castable object or None
Specification for the number of bits in this ``Signal`` and its signedness (whether it
can represent negative values). See ``Shape.cast`` for details.
If not specified, ``shape`` defaults to 1-bit and non-signed.
name : str
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.
name this ``Signal`` is assigned to.
reset : int or integral Enum
Reset (synchronous) or default (combinatorial) value.
When this ``Signal`` is assigned to in synchronous context and the corresponding clock
@@ -777,11 +790,6 @@ class Signal(Value, DUID):
If ``True``, do not generate reset logic for this ``Signal`` in synchronous statements.
The ``reset`` value is only used as a combinatorial default or as the initial value.
Defaults to ``False``.
min : int or None
max : int or None
If ``shape`` 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).
attrs : dict
Dictionary of synthesis attributes.
decoder : function or Enum
@@ -798,6 +806,7 @@ class Signal(Value, DUID):
reset : int
reset_less : bool
attrs : dict
decoder : function
"""

def __init__(self, shape=None, *, name=None, reset=0, reset_less=False,
@@ -1221,7 +1230,13 @@ def __repr__(self):
return "(eq {!r} {!r})".format(self.lhs, self.rhs)


class Property(Statement):
class UnusedProperty(UnusedMustUse):
pass


class Property(Statement, MustUse):
_MustUse__warning = UnusedProperty

def __init__(self, test, *, _check=None, _en=None, src_loc_at=0):
super().__init__(src_loc_at=src_loc_at)
self.test = Value.cast(test)
Loading