Close
0%
0%

BREDSAC

Electronic Dynamic Storage Breadboard Computer

Public Chat
Similar projects worth following
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

Preview
Download

state_counter.png

Logisim - State counter

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

Preview
Download

run_control_2.png

Logisim - Run control logic

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

Preview
Download

BlockDiagram.png

Overall organisation of the computational logic.

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

Preview
Download

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

Preview
Download

  • Starting on Phase Two

    greg.ewing01/07/2021 at 03:31 0 comments

    Down the Rabbit Hole and Back Again

    Well, I got a bit distracted.

    After the last log entry, I felt I was ready to embark on the second major phase of the project, in which I plan to build a second Logisim model based on real-world parts rather than generic Logisim ones.

    I also want to be able to print out schematics from Logisim and use them to build from, which means showing pin numbers on schematic symbols, assigning designation numbers to components, etc. While these things can be done, Logisim doesn't give you much help with them. And there are complications – for example, a 7400 consists of four gates with different pin numbers, which means I would need to create four different library components for it, and be careful to use the right ones in the right places and not use the same gate twice or miss using a gate that I could have used. All very tedious and error-prone.

    So, since Logisim is open source, I decided to take a couple of weeks to see if I could add a few things to it to make my life easier.

    A few months later...

    I now have a fork of Logisim-evolution that I'm calling Logisim-CAD. It allows a single library component to represent a part with multiple units. It has an auto-numbering feature that understands the multi-unit components. It also has a few tweaks to the rendering so that printed schematics come out looking nicer. For full details see the Readme on the Logisim-CAD Github page

    Main Memory, Memory Timing and Refresh Timing

    This is the first chunk of the Phase 2 model. It contains the DRAMs, timing DRAM cycles, and the timing chain that both refreshes the DRAM and generates data for the oscilloscope display.

    I've divided the schematic into three parts here, although it's currently a single subcircuit in the Logisim model.

    There are two layers of multiplexers. U103, U105, U108 and U111 select either the CPU or display/refresh address. There are 15 address bits, leaving one input spare that is used to multiplex the write signal. U104 and U109 select the row and column addresses for the RAMs.

    U102a latches data read from the RAM during CPU memory cycles.

    This is the timing chain for memory cycles. A 16MHz clock input is divided by U113a to give two 8MHz clocks of opposite phases. One of them drives U114a and b, which form a 4-state ring counter that generates the RAS (Row Address Strobe) and CAS (Column Address Strobe) for the RAMs. U113b is clocked from the other 8MHz phase and produces a signal that changes mid way between the falling edges of nRAS and nCAS; this is used to drive the row/column address multiplexers.

    U115a is toggled for each memory cycle and partitions them into CPU cycles (TCPU) and display/refresh cycles (TREF). U115b further partitions the CPU cycles into read cycles (TCPURD) and potential write cycles (TCPUWR).

    U102b synchronises the input from the reset switch so that the system-wide reset signal (nRESET) is released between active edges of the 8MHz clock, to ensure that everything starts off cleanly following a reset. I'm not sure if this is strictly necessary, but it seems like a good idea.

    U115b also performs another function. U115b and U122 form a 5-bit counter configured to count from 0 to 17; this generates the display/refresh bit address (REFBIT). U116 and U123 form a 6-bit counter ranging from 0 to 43, from which the display/refresh word address (REFWRD) is derived. Each line of the display contains two words, so the upper 5 bits of REFWRD represent the display line number, ranging from 0 to 21. The first 16 lines show the page of memory selected by the TANK inputs, and the rest show data from the registers.

    Also generated here are the display register file address (DSPRFA) and two synchronising signals for the display raster generator (DSPHSYNC and DSPVSYNC).

    U120 combines the various sources of display data and produces a display video signal (DSPDAT). It consists of a short pulse at the end of each TREF cycle where a 1 is to be displayed. This will...

    Read more »

  • Automated Input Testing

    greg.ewing08/16/2020 at 11:21 0 comments

    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.

    Changes to main circuit

    Dial test trigger subcircuit

    Changes to dial simulator subcircuit

    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

    greg.ewing08/15/2020 at 13:54 0 comments

    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.

    Main circuit changes

    State Control changes

    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 Timing

    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

    greg.ewing08/13/2020 at 23:27 0 comments

    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

    greg.ewing08/13/2020 at 09:41 0 comments

    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.

    Changes to main circuit

    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 Timing

    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
    
    

  • I/O Timing Diagrams

    greg.ewing08/13/2020 at 09:37 0 comments

    Teletype output timing

    Paper tape input timing

  • Redesigning I/O

    greg.ewing08/11/2020 at 06:40 0 comments

    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

    Main circuit

    State Control

    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

    greg.ewing08/08/2020 at 01:53 0 comments

    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 main circuit

    Changes to Run Control

  • Front Panel Load Address Operation

    greg.ewing07/28/2020 at 15:16 0 comments

    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.


    Load Address Microcode

    # MISC values
    LDMA  = 1101  # Load MA register from S register
    
    # Load Address
       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

    greg.ewing07/28/2020 at 15:14 0 comments

    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.

View all 49 project logs

Enjoy this project?

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

Similar Projects

Does this project spark your interest?

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