else:
                 assert False
 
+    def iter_port_constraints_bits(self):
+        for port_name, pin_names, extras in self.iter_port_constraints():
+            if len(pin_names) == 1:
+                yield port_name, pin_names[0], extras
+            else:
+                for bit, pin_name in enumerate(pin_names):
+                    yield "{}[{}]".format(port_name, bit), pin_name, extras
+
     def iter_clock_constraints(self):
         for name, number in self.clocks.keys() & self._requested.keys():
             resource = self.resources[name, number]
 
         * ``synth_opts``: adds options for ``synth_ice40`` Yosys command.
         * ``script_after_read``: inserts commands after ``read_ilang`` in Yosys script.
         * ``script_after_synth``: inserts commands after ``synth_ice40`` in Yosys script.
-        * ``yosys_opts``: overrides default options (``-q``) for Yosys.
-        * ``nextpnr_opts``: overrides default options (``-q --placer heap``).
+        * ``yosys_opts``: adds extra options for Yosys.
+        * ``nextpnr_opts``: adds extra and overrides default options (``--placer heap``)
+          for nextpnr.
 
     Build products:
         * ``{{name}}.rpt``: Yosys log.
         """,
         "{{name}}.pcf": r"""
             # {{autogenerated}}
-            {% for port, pins, extra in platform.iter_port_constraints() %}
-                {% if pins|count > 1 %}
-                    {% for bit in range -%}
-                        set_io {{port}}[{{bit}}] {{pins[bit]}}
-                    {% endfor %}
-                {% else -%}
-                    set_io {{port}} {{pins[0]}}
-                {% endif %}
+            {% for port_name, pin_name, extras in platform.iter_port_constraints_bits() -%}
+                set_io {{port_name}} {{pin_name}}
             {% endfor %}
         """,
         "{{name}}_pre_pack.py": r"""