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: 185abb492d4d
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: 1670800c7f8d
Choose a head ref
  • 3 commits
  • 7 files changed
  • 1 contributor

Commits on Jun 3, 2019

  1. Copy the full SHA
    3116d4a View commit details
  2. Copy the full SHA
    41adcc3 View commit details
  3. Copy the full SHA
    1670800 View commit details
Showing with 127 additions and 45 deletions.
  1. +4 −4 nmigen/build/plat.py
  2. +3 −7 nmigen/build/res.py
  3. +0 −6 nmigen/test/test_build_res.py
  4. +114 −25 nmigen/vendor/fpga/lattice_ice40.py
  5. +2 −1 nmigen/vendor/ice40_hx1k_blink_evn.py
  6. +2 −1 nmigen/vendor/icestick.py
  7. +2 −1 nmigen/vendor/tinyfpga_bx.py
8 changes: 4 additions & 4 deletions nmigen/build/plat.py
Original file line number Diff line number Diff line change
@@ -134,13 +134,13 @@ def add_pin_fragment(pin, pin_fragment):

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

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

10 changes: 3 additions & 7 deletions nmigen/build/res.py
Original file line number Diff line number Diff line change
@@ -156,17 +156,13 @@ def iter_ports(self):
else:
assert False

def iter_port_constraints(self, diff_pins="pn"):
def iter_port_constraints(self):
for resource, pin, port in self._ports:
if isinstance(resource.io[0], Pins):
yield port.io.name, resource.io[0].names, resource.extras
elif isinstance(resource.io[0], DiffPairs):
# On some FPGAs like iCE40, only one pin out of two in a differential pair may be
# constrained. The other has to be completely disconnected.
if "p" in diff_pins:
yield port.p.name, resource.io[0].p.names, resource.extras
if "n" in diff_pins:
yield port.n.name, resource.io[0].n.names, resource.extras
yield port.p.name, resource.io[0].p.names, resource.extras
yield port.n.name, resource.io[0].n.names, resource.extras
else:
assert False

6 changes: 0 additions & 6 deletions nmigen/test/test_build_res.py
Original file line number Diff line number Diff line change
@@ -115,12 +115,6 @@ def test_request_diffpairs(self):
("clk100_0__p", ["H1"], {}),
("clk100_0__n", ["H2"], {}),
])
self.assertEqual(list(self.cm.iter_port_constraints(diff_pins="p")), [
("clk100_0__p", ["H1"], {}),
])
self.assertEqual(list(self.cm.iter_port_constraints(diff_pins="n")), [
("clk100_0__n", ["H2"], {}),
])

def test_request_raw(self):
clk50 = self.cm.request("clk50", 0, dir="-")
139 changes: 114 additions & 25 deletions nmigen/vendor/fpga/lattice_ice40.py
Original file line number Diff line number Diff line change
@@ -108,25 +108,77 @@ class LatticeICE40Platform(TemplatedPlatform):
"""
]

def _get_dff(self, clk, d, q):
return Instance("$dff",
p_CLK_POLARITY=0,
p_WIDTH=len(d),
i_CLK=clk,
i_D=d,
o_Q=q)

def _get_io_buffer(self, pin, port, extras):
m = Module()
def iter_ports(self):
for resource, pin, port in self._ports:
if isinstance(resource.io[0], Pins):
yield port.io
elif isinstance(resource.io[0], DiffPairs):
if resource.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
yield port.p
else:
yield port.p
yield port.n
else:
assert False

def iter_port_constraints(self):
for resource, pin, port in self._ports:
if isinstance(resource.io[0], Pins):
yield port.io.name, resource.io[0].names, resource.extras
elif isinstance(resource.io[0], DiffPairs):
if resource.extras.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT":
yield port.p.name, resource.io[0].p.names, resource.extras
else:
yield port.p.name, resource.io[0].p.names, resource.extras
yield port.n.name, resource.io[0].n.names, resource.extras
else:
assert False

def _get_io_buffer(self, m, pin, port, extras, o_invert=None):
def _get_dff(clk, d, q):
m.submodules += Instance("$dff",
p_CLK_POLARITY=0,
p_WIDTH=len(d),
i_CLK=clk,
i_D=d,
o_Q=q)

def _get_inverter(a, invert):
if invert is None:
return a
else:
y = Signal.like(a, name="{}_x{}".format(a.name, 1 if invert else 0))
for bit in range(len(a)):
m.submodules += Instance("SB_LUT4",
p_LUT_INIT=0b01 if invert else 0b10,
i_I0=a[bit],
i_I1=Const(0),
i_I2=Const(0),
i_I3=Const(0),
o_O=y[bit])
return y

if "GLOBAL" in extras:
is_global_input = bool(extras["GLOBAL"])
del extras["GLOBAL"]
else:
is_global_input = False

if "o" in pin.dir:
if pin.xdr < 2:
pin_o = _get_inverter(pin.o, o_invert)
elif pin.xdr == 2:
pin_o0 = _get_inverter(pin.o0, o_invert)
pin_o1 = _get_inverter(pin.o1, o_invert)

if "i" in pin.dir and pin.xdr == 2:
i0_ff = Signal.like(pin.i0, name="{}_ff".format(pin.i0.name))
i1_ff = Signal.like(pin.i1, name="{}_ff".format(pin.i1.name))
m.submodules += self._get_dff(pin.i_clk, i0_ff, pin.i0)
m.submodules += self._get_dff(pin.i_clk, i1_ff, pin.i1)
_get_dff(pin.i_clk, i0_ff, pin.i0)
_get_dff(pin.i_clk, i1_ff, pin.i1)
if "o" in pin.dir and pin.xdr == 2:
o1_ff = Signal.like(pin.o1, name="{}_ff".format(pin.o1.name))
m.submodules += self._get_dff(pin.o_clk, pin.o1, o1_ff)
_get_dff(pin.o_clk, pin_o1, o1_ff)

for bit in range(len(port)):
io_args = [
@@ -162,49 +214,86 @@ def _get_io_buffer(self, pin, port, extras):
io_args.append(("i", "OUTPUT_CLK", pin.o_clk))

if "i" in pin.dir:
if pin.xdr < 2:
if pin.xdr == 0 and is_global_input:
io_args.append(("o", "GLOBAL_BUFFER_OUTPUT", pin.i[bit]))
elif pin.xdr < 2:
io_args.append(("o", "D_IN_0", pin.i[bit]))
if pin.xdr == 2:
elif pin.xdr == 2:
# Re-register both inputs before they enter fabric. This increases hold time
# to an entire cycle, and adds one cycle of latency.
io_args.append(("o", "D_IN_0", i0_ff))
io_args.append(("o", "D_IN_1", i1_ff))
if "o" in pin.dir:
if pin.xdr < 2:
io_args.append(("i", "D_OUT_0", pin.o[bit]))
if pin.xdr == 2:
io_args.append(("i", "D_OUT_0", pin_o[bit]))
elif pin.xdr == 2:
# Re-register negedge output after it leaves fabric. This increases setup time
# to an entire cycle, and doesn't add latency.
io_args.append(("i", "D_OUT_0", pin.o0[bit]))
io_args.append(("i", "D_OUT_0", pin_o0[bit]))
io_args.append(("i", "D_OUT_1", o1_ff))

if pin.dir in ("oe", "io"):
io_args.append(("i", "OUTPUT_ENABLE", pin.oe))

m.submodules += Instance("SB_IO", *io_args)

return m
if is_global_input:
m.submodules += Instance("SB_GB_IO", *io_args)
else:
m.submodules += Instance("SB_IO", *io_args)

def get_input(self, pin, port, extras):
self._check_feature("single-ended input", pin, extras,
valid_xdrs=(0, 1, 2), valid_extras=True)
return self._get_io_buffer(pin, port, extras)
m = Module()
self._get_io_buffer(m, pin, port, extras)
return m

def get_output(self, pin, port, extras):
self._check_feature("single-ended output", pin, extras,
valid_xdrs=(0, 1, 2), valid_extras=True)
return self._get_io_buffer(pin, port, extras)
m = Module()
self._get_io_buffer(m, pin, port, extras)
return m

def get_tristate(self, pin, port, extras):
self._check_feature("single-ended tristate", pin, extras,
valid_xdrs=(0, 1, 2), valid_extras=True)
return self._get_io_buffer(pin, port, extras)
m = Module()
self._get_io_buffer(m, 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, 1, 2), valid_extras=True)
return self._get_io_buffer(pin, port, extras)
m = Module()
self._get_io_buffer(m, pin, port, extras)
return m

def get_diff_input(self, pin, p_port, n_port, extras):
self._check_feature("differential input", pin, extras,
valid_xdrs=(0, 1, 2), valid_extras=True)
# On iCE40, a differential input is placed by only instantiating an SB_IO primitive for
# the pin with z=0, which is the non-inverting pin. The pinout unfortunately differs
# between LP/HX and UP series:
# * for LP/HX, z=0 is DPxxB (B is non-inverting, A is inverting)
# * for UP, z=0 is IOB_xxA (A is non-inverting, B is inverting)
m = Module()
self._get_io_buffer(m, pin, p_port, extras)
return m

def get_diff_output(self, pin, p_port, n_port, extras):
self._check_feature("differential output", pin, extras,
valid_xdrs=(0, 1, 2), valid_extras=True)
m = Module()
# Note that the non-inverting output pin is not driven the same way as a regular
# output pin. The inverter introduces a delay, so for a non-inverting output pin,
# an identical delay is introduced by instantiating a LUT. This makes the waveform
# perfectly symmetric in the xdr=0 case.
self._get_io_buffer(m, pin, p_port, extras, o_invert=False)
self._get_io_buffer(m, pin, n_port, extras, o_invert=True)
return m

# Tristate and bidirectional buffers are not supported on iCE40 because it requires external
# termination, which is incompatible for input and output differential I/Os.

class IceStormProgrammerMixin:
def toolchain_program(self, products, name, *, mode=None):
3 changes: 2 additions & 1 deletion nmigen/vendor/ice40_hx1k_blink_evn.py
Original file line number Diff line number Diff line change
@@ -12,7 +12,8 @@ class ICE40HX1KBlinkEVNPlatform(IceBurnProgrammerMixin, LatticeICE40Platform):
("clk3p3", 3.3e6),
]
resources = [
Resource("clk3p3", 0, Pins("13", dir="i"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
Resource("clk3p3", 0, Pins("13", dir="i"),
extras={"GLOBAL": "1", "IO_STANDARD": "SB_LVCMOS33"}),

Resource("user_led", 0, Pins("59", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
Resource("user_led", 1, Pins("56", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
3 changes: 2 additions & 1 deletion nmigen/vendor/icestick.py
Original file line number Diff line number Diff line change
@@ -12,7 +12,8 @@ class ICEStickPlatform(IceStormProgrammerMixin, LatticeICE40Platform):
("clk12", 12e6),
]
resources = [
Resource("clk12", 0, Pins("21", dir="i"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
Resource("clk12", 0, Pins("21", dir="i"),
extras={"GLOBAL": "1", "IO_STANDARD": "SB_LVCMOS33"}),

Resource("user_led", 0, Pins("99", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
Resource("user_led", 1, Pins("98", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
3 changes: 2 additions & 1 deletion nmigen/vendor/tinyfpga_bx.py
Original file line number Diff line number Diff line change
@@ -12,7 +12,8 @@ class TinyFPGABXPlatform(TinyProgrammerMixin, LatticeICE40Platform):
("clk16", 16e6),
]
resources = [
Resource("clk16", 0, Pins("B2", dir="i"), extras={"IO_STANDARD": "SB_LVCMOS33"}),
Resource("clk16", 0, Pins("B2", dir="i"),
extras={"GLOBAL": 1, "IO_STANDARD": "SB_LVCMOS33"}),

Resource("user_led", 0, Pins("B3", dir="o"), extras={"IO_STANDARD": "SB_LVCMOS33"}),