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: c52cd72d3e19
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: c9879c795bf7
Choose a head ref
  • 2 commits
  • 7 files changed
  • 1 contributor

Commits on Jun 5, 2019

  1. build.dsl: replace extras= with Attrs().

    This change proved more tricky than expected due to downstream
    dependencies, so it also includes some secondary refactoring.
    whitequark committed Jun 5, 2019
    Copy the full SHA
    ab3f103 View commit details
  2. build.{dsl,res,plat}: apply clock constraints to signals, not resources.

    This adds the Clock() build DSL element, and adds a resource manager
    function add_clock_constraint() that takes a Pin or a Signal.
    Note that not all platforms, in particular not any nextpnr platforms
    at the moment, can add constraints on arbitrary signals.
    
    Fixes #86.
    whitequark committed Jun 5, 2019
    Copy the full SHA
    c9879c7 View commit details
Showing with 381 additions and 326 deletions.
  1. +3 −3 nmigen/build/__init__.py
  2. +86 −51 nmigen/build/dsl.py
  3. +40 −41 nmigen/build/plat.py
  4. +98 −84 nmigen/build/res.py
  5. +68 −37 nmigen/test/test_build_dsl.py
  6. +43 −47 nmigen/test/test_build_res.py
  7. +43 −63 nmigen/vendor/lattice_ice40.py
6 changes: 3 additions & 3 deletions nmigen/build/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .dsl import Pins, DiffPairs, Subsignal, Resource, Connector
from .res import ConstraintError
from .plat import Platform, TemplatedPlatform
from .dsl import *
from .res import ResourceError
from .plat import *
137 changes: 86 additions & 51 deletions nmigen/build/dsl.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from collections import OrderedDict


__all__ = ["Pins", "DiffPairs", "Subsignal", "Resource", "Connector"]
__all__ = ["Pins", "DiffPairs", "Attrs", "Clock", "Subsignal", "Resource", "Connector"]


class Pins:
@@ -32,13 +32,15 @@ def __iter__(self):
return iter(self.names)

def map_names(self, mapping, resource):
mapped_names = []
for name in self.names:
while ":" in name:
if name not in mapping:
raise NameError("Resource {!r} refers to nonexistent connector pin {}"
.format(resource, name))
name = mapping[name]
yield name
mapped_names.append(name)
return mapped_names

def __repr__(self):
return "(pins {} {})".format(self.dir, " ".join(self.names))
@@ -67,65 +69,98 @@ def __repr__(self):
self.dir, " ".join(self.p.names), " ".join(self.n.names))


class Attrs(OrderedDict):
def __init__(self, **attrs):
for attr_key, attr_value in attrs.items():
if not isinstance(attr_value, str):
raise TypeError("Attribute value must be a string, not {!r}"
.format(attr_value))

super().__init__(**attrs)

def __repr__(self):
return "(attrs {})".format(" ".join("{}={}".format(k, v)
for k, v in self.items()))


class Clock:
def __init__(self, frequency):
if not isinstance(frequency, (float, int)):
raise TypeError("Clock frequency must be a number")

self.frequency = float(frequency)

@property
def period(self):
return 1 / self.frequency

def __repr__(self):
return "(clock {})".format(self.frequency)


class Subsignal:
def __init__(self, name, *io, extras=None):
self.name = name

if not io:
raise TypeError("Missing I/O constraints")
for c in io:
if not isinstance(c, (Pins, DiffPairs, Subsignal)):
raise TypeError("I/O constraint must be one of Pins, DiffPairs or Subsignal, "
"not {!r}"
.format(c))
if isinstance(io[0], (Pins, DiffPairs)) and len(io) > 1:
raise TypeError("Pins and DiffPairs cannot be followed by more I/O constraints, but "
"{!r} is followed by {!r}"
.format(io[0], io[1]))
if isinstance(io[0], Subsignal):
for c in io[1:]:
if not isinstance(c, Subsignal):
raise TypeError("A Subsignal can only be followed by more Subsignals, but "
"{!r} is followed by {!r}"
.format(io[0], c))
self.io = io
self.extras = {}

if extras is not None:
if not isinstance(extras, dict):
raise TypeError("Extra constraints must be a dict, not {!r}"
.format(extras))
for extra_key, extra_value in extras.items():
if not isinstance(extra_key, str):
raise TypeError("Extra constraint key must be a string, not {!r}"
.format(extra_key))
if not isinstance(extra_value, str):
raise TypeError("Extra constraint value must be a string, not {!r}"
.format(extra_value))
self.extras[extra_key] = extra_value

if isinstance(self.io[0], Subsignal):
for sub in self.io:
sub.extras.update(self.extras)
def __init__(self, name, *args):
self.name = name
self.ios = []
self.attrs = Attrs()
self.clock = None

if not args:
raise ValueError("Missing I/O constraints")
for arg in args:
if isinstance(arg, (Pins, DiffPairs)):
if not self.ios:
self.ios.append(arg)
else:
raise TypeError("Pins and DiffPairs are incompatible with other location or "
"subsignal constraints, but {!r} appears after {!r}"
.format(arg, self.ios[-1]))
elif isinstance(arg, Subsignal):
if not self.ios or isinstance(self.ios[-1], Subsignal):
self.ios.append(arg)
else:
raise TypeError("Subsignal is incompatible with location constraints, but "
"{!r} appears after {!r}"
.format(arg, self.ios[-1]))
elif isinstance(arg, Attrs):
self.attrs.update(arg)
elif isinstance(arg, Clock):
if self.ios and isinstance(self.ios[-1], (Pins, DiffPairs)):
if self.clock is None:
self.clock = arg
else:
raise ValueError("Clock constraint can be applied only once")
else:
raise TypeError("Clock constraint can only be applied to Pins or DiffPairs, "
"not {!r}"
.format(self.ios[-1]))
else:
raise TypeError("Constraint must be one of Pins, DiffPairs, Subsignal, Attrs, "
"or Clock, not {!r}"
.format(arg))

def _content_repr(self):
parts = []
for io in self.ios:
parts.append(repr(io))
if self.clock is not None:
parts.append(repr(self.clock))
if self.attrs:
parts.append(repr(self.attrs))
return " ".join(parts)

def __repr__(self):
return "(subsignal {} {} {})".format(self.name,
" ".join(map(repr, self.io)),
" ".join("{}={}".format(k, v)
for k, v in self.extras.items()))
return "(subsignal {} {})".format(self.name, self._content_repr())


class Resource(Subsignal):
def __init__(self, name, number, *io, extras=None):
super().__init__(name, *io, extras=extras)
def __init__(self, name, number, *args):
super().__init__(name, *args)

self.number = number

def __repr__(self):
return "(resource {} {} {} {})".format(self.name, self.number,
" ".join(map(repr, self.io)),
" ".join("{}={}".format(k, v)
for k, v in self.extras.items()))
return "(resource {} {} {})".format(self.name, self.number, self._content_repr())


class Connector:
81 changes: 40 additions & 41 deletions nmigen/build/plat.py
Original file line number Diff line number Diff line change
@@ -17,13 +17,12 @@
__all__ = ["Platform", "TemplatedPlatform"]


class Platform(ConstraintManager, metaclass=ABCMeta):
class Platform(ResourceManager, metaclass=ABCMeta):
resources = abstractproperty()
connectors = abstractproperty()
clocks = abstractproperty()

def __init__(self):
super().__init__(self.resources, self.connectors, self.clocks)
super().__init__(self.resources, self.connectors)

self.extra_files = OrderedDict()

@@ -67,25 +66,25 @@ def add_pin_fragment(pin, pin_fragment):
pin_fragment.flatten = True
fragment.add_subfragment(pin_fragment, name="pin_{}".format(pin.name))

for pin, port, extras in self.iter_single_ended_pins():
for pin, port, attrs in self.iter_single_ended_pins():
if pin.dir == "i":
add_pin_fragment(pin, self.get_input(pin, port, extras))
add_pin_fragment(pin, self.get_input(pin, port, attrs))
if pin.dir == "o":
add_pin_fragment(pin, self.get_output(pin, port, extras))
add_pin_fragment(pin, self.get_output(pin, port, attrs))
if pin.dir == "oe":
add_pin_fragment(pin, self.get_tristate(pin, port, extras))
add_pin_fragment(pin, self.get_tristate(pin, port, attrs))
if pin.dir == "io":
add_pin_fragment(pin, self.get_input_output(pin, port, extras))
add_pin_fragment(pin, self.get_input_output(pin, port, attrs))

for pin, p_port, n_port, extras in self.iter_differential_pins():
for pin, p_port, n_port, attrs in self.iter_differential_pins():
if pin.dir == "i":
add_pin_fragment(pin, self.get_diff_input(pin, p_port, n_port, extras))
add_pin_fragment(pin, self.get_diff_input(pin, p_port, n_port, attrs))
if pin.dir == "o":
add_pin_fragment(pin, self.get_diff_output(pin, p_port, n_port, extras))
add_pin_fragment(pin, self.get_diff_output(pin, p_port, n_port, attrs))
if pin.dir == "oe":
add_pin_fragment(pin, self.get_diff_tristate(pin, p_port, n_port, extras))
add_pin_fragment(pin, self.get_diff_tristate(pin, p_port, n_port, attrs))
if pin.dir == "io":
add_pin_fragment(pin, self.get_diff_input_output(pin, p_port, n_port, extras))
add_pin_fragment(pin, self.get_diff_input_output(pin, p_port, n_port, attrs))

return self.toolchain_prepare(fragment, name, **kwargs)

@@ -104,37 +103,37 @@ def toolchain_program(self, products, name, **kwargs):
raise NotImplementedError("Platform {} does not support programming"
.format(self.__class__.__name__))

def _check_feature(self, feature, pin, extras, valid_xdrs, valid_extras):
def _check_feature(self, feature, pin, attrs, valid_xdrs, valid_attrs):
if not valid_xdrs:
raise NotImplementedError("Platform {} does not support {}"
.format(self.__class__.__name__, feature))
elif pin.xdr not in valid_xdrs:
raise NotImplementedError("Platform {} does not support {} for XDR {}"
.format(self.__class__.__name__, feature, pin.xdr))

if not valid_extras and extras:
raise NotImplementedError("Platform {} does not support extras for {}"
if not valid_attrs and attrs:
raise NotImplementedError("Platform {} does not support attributes for {}"
.format(self.__class__.__name__, feature))

def get_input(self, pin, port, extras):
self._check_feature("single-ended input", pin, extras,
valid_xdrs=(0,), valid_extras=None)
def get_input(self, pin, port, attrs):
self._check_feature("single-ended input", pin, attrs,
valid_xdrs=(0,), valid_attrs=None)

m = Module()
m.d.comb += pin.i.eq(port)
return m

def get_output(self, pin, port, extras):
self._check_feature("single-ended output", pin, extras,
valid_xdrs=(0,), valid_extras=None)
def get_output(self, pin, port, attrs):
self._check_feature("single-ended output", pin, attrs,
valid_xdrs=(0,), valid_attrs=None)

m = Module()
m.d.comb += port.eq(pin.o)
return m

def get_tristate(self, pin, port, extras):
self._check_feature("single-ended tristate", pin, extras,
valid_xdrs=(0,), valid_extras=None)
def get_tristate(self, pin, port, attrs):
self._check_feature("single-ended tristate", pin, attrs,
valid_xdrs=(0,), valid_attrs=None)

m = Module()
m.submodules += Instance("$tribuf",
@@ -145,9 +144,9 @@ def get_tristate(self, pin, port, extras):
)
return m

def get_input_output(self, pin, port, extras):
self._check_feature("single-ended input/output", pin, extras,
valid_xdrs=(0,), valid_extras=None)
def get_input_output(self, pin, port, attrs):
self._check_feature("single-ended input/output", pin, attrs,
valid_xdrs=(0,), valid_attrs=None)

m = Module()
m.submodules += Instance("$tribuf",
@@ -159,21 +158,21 @@ def get_input_output(self, pin, port, extras):
m.d.comb += pin.i.eq(port)
return m

def get_diff_input(self, pin, p_port, n_port, extras):
self._check_feature("differential input", pin, extras,
valid_xdrs=(), valid_extras=None)
def get_diff_input(self, pin, p_port, n_port, attrs):
self._check_feature("differential input", pin, attrs,
valid_xdrs=(), valid_attrs=None)

def get_diff_output(self, pin, p_port, n_port, extras):
self._check_feature("differential output", pin, extras,
valid_xdrs=(), valid_extras=None)
def get_diff_output(self, pin, p_port, n_port, attrs):
self._check_feature("differential output", pin, attrs,
valid_xdrs=(), valid_attrs=None)

def get_diff_tristate(self, pin, p_port, n_port, extras):
self._check_feature("differential tristate", pin, extras,
valid_xdrs=(), valid_extras=None)
def get_diff_tristate(self, pin, p_port, n_port, attrs):
self._check_feature("differential tristate", pin, attrs,
valid_xdrs=(), valid_attrs=None)

def get_diff_input_output(self, pin, p_port, n_port, extras):
self._check_feature("differential input/output", pin, extras,
valid_xdrs=(), valid_extras=None)
def get_diff_input_output(self, pin, p_port, n_port, attrs):
self._check_feature("differential input/output", pin, attrs,
valid_xdrs=(), valid_attrs=None)


class TemplatedPlatform(Platform):
@@ -267,7 +266,7 @@ def render(source, origin):
plan = BuildPlan(script="build_{}".format(name))
for filename_tpl, content_tpl in self.file_templates.items():
plan.add_file(render(filename_tpl, origin=filename_tpl),
render(content_tpl, origin=filename_tpl))
render(content_tpl, origin=content_tpl))
for filename, content in self.extra_files.items():
plan.add_file(filename, content)
return plan
Loading