Crossbars in CuFlow

CuFlow ("Copper Flow") is an experiment that I've been playing with for about a year now. It is a procedural PCB layout package. Instead of spending hours working away at a PCB CAD system, you can spend hours on a Python program that draws the PCB instead!

The first board that I made in CuFlow was the Dazzler. There's no autorouter — every signal is laid out explicitly in the Python code. The labor-saving trick is river routing. By gathering signals into bundles ("rivers") that can then be routed together, split, joined and merged, even this quite complex layout becomes a matter of arranging about 8 "rivers".

This all works quite well when the signals at one end of the river can be assigned at random. This is the case for many kinds of devices - the GPIOs of an MCU, or the IO pins of an FPGA. When one end is free, it's straightforward to arrange the signals 1:1 so there are no wire crossings.

What about a situation where the signal orders in the river are fixed and don't match up? This is the case with the new Dazzler interface boards for Pico, Teensy, and Feather. The Dazzler's signals are in a fixed order, and unsurprisingly the order that that's best for each MCU is different.

The traditional solutions here are (1) a clever person spends time manually routing signals between the endpoints so they arrive the correct order and (2) use the autorouter, which quickly converges on a routing solution at the expense of making a shocking mess.

But in general, reordering signals isn't hard. What's required is a crossbar. It's a 2D grid, connecting two sets of signals in an arbitrary way. Here X2 and Y0 are connected, etc. By moving the junction points within the matrix, any reordering is possible.

So now CuFlow's rivers have a shuffle method that arbitrarily reorders signals. Here's its first use in the new Dazzler-Teensy board:

There are two rivers. In the top layer (yellow) the collected signals from the Dazzler, river_0. In the green layer the signals from the Teensy, river_1.

The crossbar at the top of the Teensy is where they meet and get reordered.

The via placement determines the connections. The whole grid is controlled by a block of code that simply lists the assignments. The shuffle method rearranges the signals of river_0, so it's ready to join up with river_1. This pattern is generated by a simple table in the Python code that lists signal correspondences by name:

river_0.shuffle(river_1, {
                    # Teensy       Dazzler
    "14": "1"  ,    # TX3          CONSOLE IN
    "15": "2"  ,    # RX3          CONOLE OUT
    "12": "22" ,    # MISO         MISO
    "8" : "25" ,    # 8            GPU SEL
    "9" : "26" ,    # 9            SD SEL
    "10": "27" ,    # 10           DAZZLER SEL
    "11": "28" ,    # MOSI         MOSI
    "13": "29" ,    # SCK          SCK
    "16":"PGM" ,    # 16           PGM
    "1" : "23" ,    # UART0 TX     UART

And for comparison, here's the same crossbar for the Pico board; just a different list of signal assignments.

Now CuFlow can do river routing with arbitrary reordering. The area required for this 9-signal crossbar is about 8x8 mm, carefully rotated so that there is no violation of the via-to-via design rules for this cheap 2-layer process. So much neater than the autorouter, and so much quicker than the old grind of schematic entry and hand-routing.