Ben Eater style CPU

Based on Ben Eater's YouTube series.

It's not an exact replica, for example, I have a EEPROM with programs preloaded on it that can be accessed by selecting the Program Select inputs (3-bit binary), and selecting Load Program and letting it run for 20 cycles.

Some pages take a long time to load (for example, the CPU takes about 15 seconds on my M1 Macbook Pro), I'm calculating the routing on the fly, the components are just defined in JSON files, as a list of child components and wire connections. For example, the clock module looks like:-

Long JSON file - click to show
{
  "kind": "composite",
  "id": "EATER_CLOCK",
  "name": "Counter Module",
  "minCanvasWidth": 1200,
  "minCanvasHeight": 400,
  "inputs": [
    {
      "id": "CLK_STEP",
      "name": "Clock Step"
    },
    {
      "id": "HLT",
      "name": "Halt"
    }
  ],
  "outputs": [
    {
      "id": "CLK",
      "name": "CLK"
    },
    {
      "id": "CLK_n",
      "name": "'CLK"
    }
  ],
  "children": [
    {
      "instanceId": "clock",
      "definitionId": "CLOCK",
      "column": 0
    },
    {
      "instanceId": "c_or",
      "definitionId": "OR2",
      "column": 1
    },
    {
      "instanceId": "h_not",
      "definitionId": "NOT",
      "column": 1
    },
    {
      "instanceId": "h_and",
      "definitionId": "AND2",
      "column": 2
    },
    {
      "instanceId": "c_not",
      "definitionId": "NOT",
      "column": 3
    },
    {
      "instanceId": "hlt_latch",
      "definitionId": "D_LATCH",
      "column": 3
    },
    {
      "instanceId": "raw_n",
      "definitionId": "NOT",
      "column": 2
    }
  ],
  "internalWires": [
    {
      "from": {
        "component": "self",
        "pinId": "CLK_STEP"
      },
      "to": [
        {
          "component": "c_or",
          "pinId": "A"
        }
      ]
    },
    {
      "from": {
        "component": "self",
        "pinId": "HLT"
      },
      "to": [
        {
          "component": "h_not",
          "pinId": "A"
        }
      ]
    },
    {
      "from": {
        "component": "h_not",
        "pinId": "OUT"
      },
      "to": [
        {
          "component": "hlt_latch",
          "pinId": "D"
        }
      ]
    },
    {
      "from": {
        "component": "clock",
        "pinId": "CLK"
      },
      "to": [
        {
          "component": "c_or",
          "pinId": "B"
        }
      ],
      "colour": "#408000"
    },
    {
      "from": {
        "component": "c_or",
        "pinId": "OUT"
      },
      "to": [
        {
          "component": "h_and",
          "pinId": "A"
        },
        {
          "component": "raw_n",
          "pinId": "A"
        }
      ]
    },
    {
      "from": {
        "component": "h_and",
        "pinId": "OUT"
      },
      "to": [
        {
          "component": "self",
          "pinId": "CLK"
        }
      ],
      "colour": "#408000"
    },
    {
      "from": {
        "component": "h_and",
        "pinId": "OUT"
      },
      "to": [
        {
          "component": "c_not",
          "pinId": "A"
        }
      ]
    },
    {
      "from": {
        "component": "c_not",
        "pinId": "OUT"
      },
      "to": [
        {
          "component": "self",
          "pinId": "CLK_n"
        }
      ]
    },
    {
      "from": {
        "component": "raw_n",
        "pinId": "OUT"
      },
      "to": [
        {
          "component": "hlt_latch",
          "pinId": "EN"
        }
      ]
    },
    {
      "from": {
        "component": "hlt_latch",
        "pinId": "Q"
      },
      "to": [
        {
          "component": "h_and",
          "pinId": "B"
        }
      ]
    }
  ]
}

The routing algorithm definitely isn't the best (It's based on A*), and there's a couple of minor visual issues (some wires loop back over themselves). But for the most part, it gets the job done.

As for how to use it? You can either click on the inputs to the left of the circuit, or on the circuit itself. Any pins can also be clicked on to add a probe at that point for historical view.

For any components with a clock, we automatically add a clock timer below the canvas (for example, the CPU has two) that you can adjust the timing on, so you can see what's going on.

Seven segment displays have a slight fade to them, mainly because when we multiplex them, it reduces the flashing and makes them easier to read.