Schematic Feature Discussion : Repeated Instances and Multi-Bit Wires

This is to discuss features that would enable concise drawing of schematics that have (highly) repetitive circuits and/or complex net connectivity. This does not address layout replication / re-use as that is a separate problem.

Complex designs can have devices with high repetition in parallel, series, or both. You can have wide bus signals which are made up of connections to several devices with their own (wide) bus signals. You can have signals (or a vector of signals) with identical connections to a large number of repeated circuits.
Examples: daisy-chained 100x smart leds, memory arrays, multi-channel audio circuits.

Drawing repeated, identical devices or hierarchical instances require one symbol placed for each element. If each element is identical, then why can’t they all be stacked into a single drawn symbol representing all elements? The schematic could be significantly more concise, especially when all connectivity follows a regular pattern.

A challenge to supporting this is that the pins of said stacked devices are like an array. There needs to be a way to differentiate a single-net wire connecting to each pin in this array versus creating individual nets for each pin.

Altium has a solution via the “REPEAT()” function that applies to hierarchical blocks. As part of this functionality, there is minimal support for routing a regular labeled wire to a repeated instance, and “repeating” the net accordingly. However its implementation is limited and in my opinion a real pain to use.

KiCad doesn’t have equivalent functionality at the moment. At least on the wiring side, KiCad has had some work going in this direction in the form of bus vectors, bus groups, and hierarchical bus pins. They work, but there are several limitations.

  • KiCad’s bus implementation can only describe sets of unique nets. Additionally, bus vectors are always sorted by ascending bit index in the background for determining connectivity.
    • You cannot intentionally reverse the bit order of a bus at a bus vector pin, should that be necessary.
    • You cannot create some arbitrary array of signals and connect it to a bus pin (ex: {A B C D} to pin FOO[3..0]).
  • For bus vector wires specifically, you can take continuous slices of a bus vector and route it to a hierarchical instance’s bus vector pin (ex: you can do bus wire FOO[7..4] to instance bus pin BAR[3..0]). Great, this is useful.
  • Discontinuous slices of a bus vector, such as an interleaving scheme, are not possible. (ex: for a bus vector FOO[7..0] and some hierarchical block instances A,B with bus pin BAR[3..0], what happens if you must route FOO[0,2,4,6] to A.BAR[3..0] and FOO[1,3,5,7] to B.BAR[3..0] ?). As an aside, this use case could be enabled by adding an “index step” feature to the vector syntax.
  • Bus groups are even more restrictive; element names and index ranges must match between bus group wires and bus group pins.

All of this to say that there is room for improvement.

There are CAD tools in the IC design space that solved these problems decades ago. The solutions have some similarities to what Altium has, but are more flexible. Referencing the solution from from one popular tool, here’s a visual example of what this would look like in KiCad:


Explanation of the features:

  • Device instances and hierarchical block instances in schematic accept a vector syntax in the reference designator describing an index range with optional step size. prefix<start[:end[:step]]>. These repeated instances will be elaborated in the background to individual instances named prefix<idx>. Examples:
    • U0<0> → U0<0> (Single instance)
    • U1<0:2> → U1<0>, U1<1>, U1<2> (ascending range)
    • U2<3:0:2> → U2<3>, U2<1> (descending range with step)
  • Hierarchical block pins (regular, not bus vector or bus group pins) are upgraded to accept the same range & step vector syntax. These are elaborated in the background into arrays of individual pins. A key feature is that you are allowed to have repeated instances of a sheet using vectored pins.
  • Wires are upgraded to operate on arrays of nets. Wire labels accept an even more powerful syntax to describe net vectors, repeats, and concatenations of lists of nets. Examples:
    • AD<1:0> → AD<1>, AD<0> (basic net vector)
    • AD<0:3:2> → AD<0>, AD<2> (basic net vector with index step)
    • BC<0:1*2> → BC<0>,BC<0>,BC<1>,BC<1> (net vector with inner index repeat)
    • BC<(0:1)*2> → BC<0>,BC<1>,BC<0>,BC<1> (net vector with outer index repeat)
    • BC<3:2,4,0:1> → BC<3>,BC<2>,BC<4>,BC<0>,BC<1> (net vector with a list of index ranges)
    • BC<(0,1*2)*2,5> → BC<0>,BC<1>,BC<1>,BC<0>,BC<1>,BC<1>,BC<5> (complex net vector)
    • <*2>A → A, A (prefix repeat operator)
    • A,B<1:0>,C → A,B<1>,B<0>,C (a list of nets and vectors)
    • <*2>(A,<*2>B,A,C<1:0>) → A,B,B,A,C<1>,C<0>,A,B,B,A,C<1>,C<0> (a mix of features)
  • Instances, pins, and net expressions get expand into arrays in the background. Connectivity is determined by the arrays at each index, so order matters. If there is a mismatch in signal width between a wire and some (repeated instance) pin, there are rules in certain cases to allow one side to be repeated so the widths match. Examples:
    • A single-bit wire named X and a repeated instance U1<0:3> with pin CLK. U1<i>.CLK == X for each i.
    • A multi-bit wire named X<0:3> and a repeated instance U1<0:3> with pin CLK. U1<i>.CLK == X<i> for each i.
    • A multi-bit wire named X<0:3> and a single instance U1 with pin CLK<0:3> . X<i> == U1.CLK<i> for each i.
    • A multi-bit wire named IN<3:0> and a repeated instance U1<1:0> with pin D<3:0>. IN<0:3> == U1<i>.D<3:0> for each i.
    • A multi-bit wire named OUT<0:7> and a repeated instance U1<1:0> with pin Q<3:0>. OUT<0:3> == U1<1>.Q<3:0> and OUT<4:7> == U1<0>.Q<3:0>

I think it would be great to implement something like what is described above. With some adaptations, I think it could be possible to integrate it with existing functionality.

  • Leave bus vectors and bus groups as-is. If they are connected to a repeated instance, then simply repeat them as-is to each element. Maybe consider adding an index step feature.
  • The current bus wire and pin implementation drops brackets when generating net names. IE: AD[3] will be net AD3. The new features should do the same. Similar argument for sheet pins.
  • For instance names, it might be helpful to keep the brackets in the name to avoid name conflicts like U4<0> → U40.
  • All elements of a repeated instance must be identical in all attributes (parameters, footprint, saved layout, etc).

Let me know what you think and please share any ideas for improvement!

Maybe when Python bindings to the schematic editor are implemented all this can be done programmatically.

Thirty years ago I was frequently designing boards with data busses all over. In recent years not at all.

1 Like

Possibly because so much functionality has been sucked into the ICs: no more external RAM, ROM, coprocessor, etc. And high-speed serial instead of parallel buses.

My first thought was pretty similar to the other responses here. How useful is this in the 21 century? Over the last 50 years electronics has changed a lot. Big buses and multiple parallel IC’s with many connections has mostly disappeared. Back in the '80-ies it was also common to hide Power and GND for all the logic IC’s. It made sense back then with PCB’s of 100+ IC’s all connected to the same power supply, but in this age, with far fewer IC’s, and (often) a bunch of different power supply voltages and mixed logic, hiding power connections does not make much sense anymore.

But maybe it still makes sense to do this. But instead of starting with a demonstration of how this could be implemented, start with giving some examples which show is still relevant now. If you can get a few people together who think they can benefit from this, then it may be worth a feature request. When there is little interest from others, you can still make a feature request of course (It’s the nature of Open Source software) but do keep in mind that developer resources still is a scarce resource for the KiCad project, and many feature requests are competing to get implemented.

It was partly the integration into SOCs and also pressure from EMC regulations - fat busses with a clock radiate strongly unless done with great care like in PC motherboards. There is no chance of passing without a ground plane, so the two layer boards we used back then are out.

Hey, thanks for the feedback.

I agree with you on the hiding power pins part; it’s better to use a strict hierarchical approach to power nets. I forgot to add power pins to the sheet.

Looks like my intent was not properly conveyed, so I’ll try to re-phrase.

I wish to improve hierarchical and multi-channel design capabilities in KiCad. I want 1.) more flexibility with creating connectivity, and 2.) more concise schematics, especially with repeated circuits (much faster to draw your intent, less to read, less copy-paste errors, etc).

Based on what I’ve experienced in other CAD tools, achieving this in KiCad boils down to the following upgrades:

  • make the bus vectors in KiCad more flexible (think of them as arbitrary arrays of nets instead of strictly a data bus). Add features like order-dependent vectors, stepped index ranges, index repeats, etc.
  • vectorize sheet instances and possibly device instances.
  • vectorize wires if busses can’t be upgraded.

I’m not the first to present some of these ideas for KiCad. I found this thread within an open gitlab issue.
https://gitlab.com/kicad/code/kicad/-/issues/1998#note_254394724
They bring up the idea of “vectorizing” a design, showing an example of 8x daisy-chained smart LED’s, and an example of 8x parallel connectors wired up with the same pattern (ie multi-channel).

Maybe the full solution as I originally presented is unnecessary (and that seems to be what you guys are feeling). Maybe this could be stripped back to vectorizing only sheet instances, and some bus upgrades as stated above. Following this train of thought, the Altium approach with the REPEAT() function could be used, but I’d like to see some adjustments such as making bus vectors work with it ( ie: similar to how REPEAT() works on a single pin… a bare bus pin AD[3:0] would duplicate the connection to the same bus vector for each repeated instance, while REPEAT(AD[3:0]) pin would make a flat size-(MxN) array for connectivity)

This is something I’d be open to working on myself, but only if the community likes these ideas.