0%
0%

# BREDSAC

Similar projects worth following
1.3k views
This is an attempt to reimplement the historic EDSAC computer on breadboards using 74 series TTL and parts of a similar era.

## What possessed me to do this?

Inspiration for this project came from two sources.

One is the effort underway at the National Museum of Computing in
Bletchley Park to build a replica of the EDSAC, one of the earliest
stored-program computers, built at Cambridge University in the late
1940s.

EDSAC Replica Project

The other is a series of videos by Ben Eater about the construction of
a simple computer on breadboards from 74 series logic chips.

Ben Eater's 8-bit Computer

Put these two things together, add in a hankering to do something with
hardware and a pile of parts burning a hole in the bottom of my junk
box, and the conclusion is obvious. I need to build a breadboard
implementation of the EDSAC!

### bit_counter.png

Logisim - Bit counter

Portable Network Graphics (PNG) - 15.76 kB - 06/13/2018 at 06:37

### state_counter.png

Logisim - State counter

Portable Network Graphics (PNG) - 6.75 kB - 06/13/2018 at 06:37

### run_control_2.png

Logisim - Run control logic

Portable Network Graphics (PNG) - 15.12 kB - 06/13/2018 at 06:34

### BlockDiagram.png

Overall organisation of the computational logic.

Portable Network Graphics (PNG) - 48.38 kB - 06/07/2018 at 10:53

### BasicCycle.png

Diagram showing the basic short-word timing cycle of BREDSAC.

Portable Network Graphics (PNG) - 18.06 kB - 06/07/2018 at 10:51

• ### Automated Input Testing

I added tests to the combined test suite for the Input instruction and the telephone dial input facility.

So that dial input could be tested automatically, I added a trigger circuit that watches the teletype output looking for the sequence of characters D#n, where n is a digit, and dials that digit.

### Additions to test program for input testing

```#
#   INPUT instruction
#   Enter with AC = 0
#   Exit with AC = 0
#   Expected output: INP
#
I_test:     O cI F
I temp F
A temp F
L ^12
T temp F
O temp F
I temp F
A temp F
L ^12
T temp F
O temp F
O sp F

#
#   Dial Input test
#   Enter with AC = 0
#   Exit with AC = 0
#   Expected output: D#5P
#
dial_test:  O cD F
O figs F
O chash F
O c5 F
Z F
O lets F
A temp F
S k_5 F
F fail D
O cP F
O sp F
```

• ### Telephone Dial

The telephone dial is a somewhat unusual input device, as there is no way for the program to explicitly read it. When dial input is required, the program executes a Halt instruction. Dialling a digit n causes n to be added to the upper word of the accumulator (with the digit 0 giving n=10). Execution is then automatically resumed.

I expanded the IOSEL field to 3 bits so that I could add two more I/O sense inputs:

DIAL - high when the dial is turned away from its home position.

DIALP - goes high for each dial pulse.

Inside the State Control subcircuit, DIAL is inverted so that the branch condition is true when dialling has finished.

DIAL is also connected to the Run Control circuit to trigger a NIMS to handle dial input.

### Dial Simulator

The dial simulator runs on a slow internal clock so that the computer simulation can keep up with it. It has 4 states. In State 0, it is paused waiting for a button to be pressed. When that happens, the digit dialled is loaded into the pulse counter and the state counter is allowed to advance. State 1 provides a delay. In state 2, pulses are generated at the DIALP output and the pulse counter counts down. When it reaches zero, the state counter advances again, through state 3 which provides another delay, and back to state 0.

### Dial Input Microcode

```# IOSEL values for IOTST
TDFIN = 001  # Telephone dial finished
TDPUL = 101  # Telephone dial pulse

# Dial Input
1 00011 0 0000 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   IOTST   -   ---    TDFIN -  # If dialling finished
1 00011 0 0001 :  -  EOI  --  ---   -  --    -     -   --   -   -   -   -   -    -   ----    -   BNIOT  0100     #   then continue program
1 00011 0 0010 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   IOTST   -   ---    TDPUL -  # If no dial pulse
1 00011 0 0011 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   ----    -   BNIOT  0000     #   then wait for pulse
1 00011 0 0100 :  -   -   11  XAC   -  Y0   CY1    -   WAC  -   -  MSW LSW  -    -   ----    -   ---    ----     # Add 1 to MSW of accumulator
1 00011 0 0101 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   IOTST   -   ---    TDPUL -  # If no dial pulse
1 00011 0 0110 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   ----    -   BNIOT  0000     #   then wait for pulse
1 00011 0 0111 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   IOTST   -   ---    TDFIN -  # If dialling finished then continue
1 00011 0 1000 :  -  EOI  --  ---   -  --    -     -   --   -   -   -   -   -    -   ----    -   BNIOT  1010     #   program else wait for no pulse
```

• ### Bit Timing Change

I found another problem. When entering a NIMS, the first RUNCLK pulse, which is used to load the starting microcode address into the opcode register, was prematurely advancing the bit counter, skipping T0 of the first microinstruction.

To fix this, I modified the Bit Counter subcircuit so that the bit number is incremented only when RUN is true.

• ### Bootstrapping

I created a boot device simulation and hooked it into the main circuit. To support booting, I added a PIBD value (boot device to PIB) for the IOSEL field,  a CLRPC (clear program counter) operation and a BDSTA (boot device start) I/O signal.

I also changed the sense of the BDRUN input and renamed it to BDFIN (boot device finished), because it turned out I wanted to branch the other way on it in the microcode.

## Boot Device Simulator

The boot device simulator is very similar to the paper tape simulator, with the addition of a flip flop to keep track of whether the boot device is running.  The memory is 18 bits wide; 17 of them are used for boot data, and one is used to flag the last word of the data.

## Boot Microcode

The boot sequence is entered when the Start button on the front panel is pressed. It first clears the PC, starts the boot device and then enters a loop reading words from the boot device and writing them to memory. Along with reading each word, BDFIN is tested; when it is true, the PC is cleared again and execution of the program begins.

```# MISC values
CLRPC = 0011  # Clear PC

# PISEL values
PIBD = 01  # Boot device data to PIB

# IOSEL values for IOSIG
BDSTA = 00  # Start boot device
BDACK = 11  # Acknowledge boot device input

# IOSEL values for IOTST
BDFIN = 00  # Boot device is finished
BDRDY = 11  # Data available from boot device

# Boot
1 00010 0 0000 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   CLRPC   -   ---    ----     # Clear PC
1 00010 0 0001 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   IOSIG   -   ---    BDSTA -- # Start boot device
1 00010 0 0010 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   IOTST   -   ---    BDRDY -- # Test for boot data
1 00010 0 0011 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   ----    -   BNIOT  0100     # Loop until ready
1 00010 0 0100 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   PLS     -   ---    PIBD  -- # Load boot data into S
1 00010 0 0101 : SHS  -   --  XSR   -  --    -    WMEM --   -   -   -   -   -    -   MAPC    -   ---    ----     # Write S to memory at PC
1 00010 0 0110 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   IOTST   -   ---    BDFIN -- # Test for boot finished
1 00010 0 0111 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   IOSIG   -   ---    BDACK -- # Ack boot data
1 00010 0 1000 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   INCPC   -   BNIOT  0100     # Inc PC and loop if boot not finished
1 00010 0 1001 :  -  EOI  --  ---   -  --    -     -   --   -   -   -   -   -    -   CLRPC   -   ---    ----     # Clear PC and enter program

```

## Paper tape input timing

• ### Redesigning I/O

The next thing I want to tackle is bootstrapping, or as the EDSAC team would have called it, "loading the initial orders".

I haven't decided exactly what form the bootstrap device will take yet, but I'm assuming it will be some kind of parallel input device that interfaces in a similar way to the paper tape reader. So before going any further I decided to make some changes to the way I/O devices are handled so I can treat them in a more uniform way.

## I/O condition testing

I didn't have any more branch condition inputs available, so I added an IOTST operation that samples one of four input conditions and latches it for branching on by a subsequent microinstruction. The conditions are assigned as follows:

PTRDY - data is available from the paper tape reader
TTRDY - teletype is ready to accept data
BDRDY - data is available from the boot device
BDRUN - boot device is running

TTRDY and PTRDY were previously connected directly to inputs of the branch condition multiplexer. One of those inputs is now called IORDY and is used to sense the condition latched by IOTST; the other is currently spare.

The condition to be tested by IOTST is selected by the IOSEL field, which again re-uses two bits from the UBRANCH field.

## I/O control signals

On the outwards side, there is an IOSIG operation that produces one of four output pulses during the second half of T17, selected by IOSEL.

PTACK - acknowledge data from paper tape reader
TTOUT - signal teletype that data is available
BDACK - acknowledge data from boot device
BDSTART - start the boot device

## Paper tape input

Input data from the paper tape is now read into the S register via the PIB, instead of having its own dedicated shift register.

## Logisim Changes

### Paper tape simulation

For testing purposes, I put together a circuit that simulates a paper tape reader feeding in a few characters using the input device protocol. It incorporates a timer to slow down the input so that I can test whether the microcode correctly waits for the device to become ready.

## New microcode for Input and Output instructions

```# UBCOND values
BNIOT = 011  # I/O test false

# MISC values
IOSIG = 1010  # Generate I/O signal selected by IOSEL
IOTST = 0110  # Test I/O sense line selected by IOSEL

# PISEL values
PIPT = 10

# IOSEL values for IOSIG
PTACK = 10
TTOUT = 01
BDACK = 11

# IOSEL values for IOTST
BDRUN = 00
PTRDY = 10
TTRDY = 01
BDRDY = 11

# O - Output
0 01001 0 0001 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   IOTST   -   ---    TTRDY --  # Test TTY
0 01001 0 0010 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   ----    -   BNIOT  1000      # Loop until ready
0 01001 0 0011 : SHS EOI  --  ---   -  --    -     -   --   -   -   -   -   -    -   IOSIG   -   ---    TTOUT --  # Read memory into S and signal TTY

# I - Input
0 01000 0 0001 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   IOTST   -   ---    PTRDY --  # Test paper tape
0 01000 0 0010 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   ----    -   BNIOT  1000      # Loop until ready
0 01000 0 0011 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   PLS     -   ---    PIPT  --  # Load paper tape data into S
0 01000 0 0100 : SHS EOI  --  XSR   -  --    -    WMEM --   -   -   -   -   -    -   IOSIG   -   ---    PTACK --  # Write S to memory and ack paper tape
```

• ### Fixing Instruction Register Timing

It turns out that clocking the instruction register with BITCLK wasn't such a good idea, because its transitions occur slightly ahead of RUNCLK, and this was leading to glitchy behaviour.

So I came up with another solution to the problem that the change to the IR clocking was designed to solve. The IR is clocked with BITCLK as before, and the Run Control subcircuit lets through a RUNCLK pulse during a Continue pulse.

## Changes to Run Control

The way the data paths are set up, the easiest way to get a value from the switches to the PC is to go through the instruction register. However, I can't load the whole instruction register in the middle of a microcode sequence, because that would mess up the microcode address. So, I have split out the address part of the instruction register into a Memory Address register that I can load separately.

A quirk of this approach is that the value to be loaded into the PC needs to be set into switches 2 to 11, not 1 to 10 as might have been expected. This is due to the fact that IR/MA is loaded with an instruction during T17 of a fetch cycle, at which time the final shift of the value in the S register has not yet been performed; to compensate for this, inputs 0 to 16 of IR/MA are wired to outputs 1 to 17 of the S register.

This is not really a problem, it's just a matter of labelling the switches appropriately. I created a bezel for the switches on the main schematic showing where the hex digits go for PC values and data.

```# MISC values
LDMA  = 1101  # Load MA register from S register

1 00001 0 0000 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   PLS     -   ---    PISW -- # Load switches into S
1 00001 0 0001 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   LDMA    -   ---    ----    # Load MA from S
1 00001 0 0010 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   LDPC    -   ---    ----    # Load PC from MA
1 00001 0 0011 :  -  EOI  --  ---   -  --    -     -   --   -   -   -   -   -    -   HALT    -   ---    ----    # Stop
```

• ### Corrections

I found a couple of problems. The first is that XUSTART was being asserted for as long as a button was held down, instead of just a single BITCLK cycle as intended. A small change to the Run Control subcircuit fixed this.

The second is that, on reaching the end of a NIMS, bit 17 of the instruction register was left as 1. This meant that subsequently pressing Continue without an intervening Reset would not start the program running, but instead just repeat the last executed NIMS.

To fix this, I arranged for the instruction register to be reloaded at the end of the NIMS. Since XUSTART is inactive by then, this sets IR17 to 0. The rest of the instruction register is loaded with whatever data happens to be in the S register at the time, but this does not matter, because every microinstruction sequence addressed by IR17=0 begins with a Fetch microinstruction.

• ### Front Panel Deposit Operation

The Run Control subcircuit now has provision for initiating up to four different non-instruction microcode sequences (NIMS). The inputs for triggering these are first latched to synchronise them with BITCLK, and then fed to a priority encoder which generates a microcode address. At the same time, the Continue FF chain is activated, producing a one-BITCLK pulse at XUSTART and then setting the Run FF.

### Run Control

I found a problem with the logic for loading the NIMS entry point address into the instruction register. It doesn't work if the instruction register is clocked with RUNCLK, because the XUSTART pulse occurs before RUNCLK is activated. So the instuction register is now continuously clocked with BITCLK, and functions that should only occur while the machine is running are gated with RUN.

### Input Switches and Parallel Input Bus

I added a set of switches for manual data entry1. These are gated onto a Parallel Input Bus (PIB) which connects to new parallel inputs on the S register.
There is currently only one data source for the PIB, but there will be others later. I needed a way of selecting the PIB source in the microcode, but there are no more bits available, so I have re-used part of the UBADDR field. This means I won't be able to perform a microcode branch and use the PIB at the same time, but I don't think that will be a problem. The function of the Deposit switch will be to write the value from the input switches into memory at the address currently in the PC, and then increment the PC. To do this, I need to be able to select the PC as the source for the memory address, and increment the PC, independently of other functions.

I also need to be able to select the serial output of the S register as an X source in the ALU input selector.

### Deposit microcode

```# XSEL values
XSR  = 011  # S Register

# MISC values
PLS   = 1110  # Parallel load S register
LDPC  = 0001  # Load PC from IR
INCPC = 1001  # Increment PC
MAPC  = 0101  # Memory address = PC

# Deposit
1 00000 0 0000 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   PLS     -   ---    PISW -- # Load switches into S
1 00000 0 0001 : SHS  -   --  XSR   -  --    -    WMEM --   -   -   -   -   -    -   MAPC    -   ---    ----    # Write S to memory
1 00000 0 0010 :  -   -   --  ---   -  --    -     -   --   -   -   -   -   -    -   INCPC   -   ---    ----    # Increment PC
1 00000 0 0011 :  -  EOI  --  ---   -  --    -     -   --   -   -   -   -   -    -   HALT    -   ---    ----    # Stop
```

#### Footnotes

1. This is not quite historically accurate. The EDSAC designers deliberately did not provide a way for users to enter programs manually, because they didn't want people taking up machine time debugging at the console. It did have a set of "engineering switches" that were used for maintenance, but I don't know exactly what they did or how they were used.

Share

## Discussions

Dr. Cockroach wrote 08/13/2020 at 15:56 point

I must say that your BREDSAC design is giving me a lot of food for thought for my LLTP. Your build will be fantastic to see in action.

Are you sure? yes | no

Ken Yap wrote 07/10/2020 at 12:57 point

Very well documented project you have created. 👍 I'm old-school, I much rather read a pile of documents than watch a video.

If you need more vintage TTL chips (mostly LS and ALS) send me a message. I have a fair collection of pulls and it shouldn't cost that much to post you a bag, seeing as you're just across the Tasman ditch. Also have lots of DRAM chips.

Are you sure? yes | no

# Does this project spark your interest?

Become a member to follow this project and never miss any updates