The Spikeputor

I am building a computer featuring a 16-bit RISC CPU made of discrete transistors for learning, fun and art. It will be pretty large.

Similar projects worth following
After taking the MITx course "Computation Structures", I realized how relatively easy it would be to build a CPU. Armed with a beginners knowledge of electronics and the concepts introduced in the course, I designed and started implementation of an NMOS logic-based RISC CPU. Wall-mounted, it will take up an entire wall. For extra retro-computing flair, the I/O will be handled by an old Apple II plus I had sitting in my closet. I'm physically building it out of pegboards, protoboards, 2N7000 transistors, resistors (mainly SIPs, but a few discretes here and there), LEDs, a few HP-5082 display chips, some electroluminescent wire, and lots of lovely colored jumper wire. My goal will be to be able to execute simple programs and visualize the computation as it happens.

The Spikeputor will be a fully functional computer with a CPU made exclusively from NMOS logic using about 5,000 MOSFETs (2N7000), resistors, and LEDs to visualize the logic. HP-5082-7340 Hex Display chips will be strategically added to display the numeric contents of the outputs of the major CPU components, although if you read binary, the LEDs will provide the same information. Electroluminescent wire will be added to visualize the logic pathways between the major components. The clock will be adjustable from maximum speed (predicted to be in the tens of thousands of hertz range) down to single step. Since the primary driver of the Spikeputor is to visualize computation, speed was not a concern, although some steps will be taken (a clock tree, e.g.) to enhance the overall performance.

The initial CPU design was inspired by the Beta CPU, introduced in the MITx course, "Computation Structures" (highly recommended!) The Beta has a load-store, 32-bit RISC architecture. To save space, transistors, and the creator's sanity, the Spikeputor CPU was reduced to 16 bits. This necessitated several other major design changes, mainly centering on the fact that 16-bit opcodes could no longer support the inclusion of constants within the opcode.

Spikeputor CPU Design Features and Major Components:

  • 16-Bit Address space
  • Register Memory: Seven 16-bit registers, plus one register hard-coded to zero.
  • Multi-function ALU
    • Addition and Subtraction, supporting 2s Complement negative numbers
    • Comparison (Equal, Less Than, Less Than or Equal)
    • Boolean Logic (AND, OR, XOR, Identity)
    • Variable Bitwise Shift Right or Left with or without Sign Bit Propagation
  • Control Logic implemented with simple logic gates (no microcode or ROMs)
  • Additional registers for Program Counter, Instruction, Constant, CPU Phase, and a few required status flags
  • One-word opcodes for operations between registers
  • Two-Word opcodes for operations between registers and 16-bit constants
    • ALU Functions (see above)
    • Memory functions: Load, Load Relative to PC, and Store
    • Conditional (BEQ, BNE) and non-conditional branching with branch point storage
  • IRQ and RESET handling (including automatically using register R6 as the Exception Pointer)
  • Direct Memory Access (DMA) for all I/O


To be able to execute more than just trivial programs, the Spikeputor CPU will interface with high speed static memory RAM (2 x 32K AS6C62256) and ROM (2 x 32K AT27C256R) chips. In addition, a mirrored write-only "screen memory," also made from (roughly 5,000) transistors, will be created, providing a 48 by 18 array of addressable LEDs. Memory addresses will be restricted to word-boundaries. Attempts to address odd-numbered memory locations return the corresponding even-numbered 16 bit word. Since there weren't any 16K SRAM and ROM chips available, I used 32K chips, and will design a memory banking system to select 16K banks from the RAM and ROM for read operations. The full 32K words (64 kbytes) of RAM can be written to at any time.


I/O functions will be facilitated via custom software and hardware running on an Apple II Plus computer, vintage 1986. A custom peripheral card called BIAS (Board to Interface Apple to Spikeputor) will be designed as part of the project, as well as the software on the Apple to provide keyboard input, screen output, and long-term data storage and retrieval. The I/O controller will have direct access to the Spikeputor memory and will halt the Spikeputor CPU during all read and write operations.

16 Bits of General Purpose Input and Output signals will also be implemented, mirrored to fixed high memory locations.


The Spikeputor will be assembled on a series of mounted solderless breadboards. Each major component (ALU, Register Memory, Control Logic/Program Counter/Status Registers, and Screen Memory) will be laid out on a 4 foot by 2 foot pegboard. Each pegboard can contain...

Read more »

  • 10000 × 2N7000 MOSFET Discrete Semiconductors / Diode-Transistor Modules
  • 200 × 830 Hole Prototype Board Prototype Board
  • 200 × L091S472LF, 4.7kΩ 9-pin Resistor SIP Array Resistor Networks / Thick Film Networks
  • 2 × AS6C62256 32K x 8 bit SRAM Memory ICs / Static RAM (SRAM)
  • 2 × AT28C256R 32K x 8 bit EPROM Memory ICs / PROMs, OTP PROMs

View all 9 components

  • Timing Refinements and a Discrete 555

    spudfishScott02/03/2020 at 22:40 0 comments

    Now that the Spikeputor CPU is up and running, it was time to make some refinements. First up, something that I had been toying with for awhile: replacing the 555s used to generate the clock signal (bistable) and to generate a long RESET signal (monostable). I replaced the monostable 555 circuit with a simple RC circuit. This was incorporated into the start-up schematic that was shown in a previous log. The new version looks like this:

    While replacing the bistable 555 circuit for clock signal generation, I reviewed how the Spikeputor writes to memory, as I had been seeing a few glitches in that department. The original design was to trigger the Write Enable signal on the memory chips during the second half of the clock cycle by simply ANDing the inverted clock with the Spikeputor Memory Write Enable (MWE) signal. There were several problems with this approach. First, it gave the Spikeputor only a half a clock cycle to compute the address to write to, effectively limiting the maximum clock cycle frequency. Second, and a bit more concerning, was the fact that there was a possibility that the Write Enable signal might persist while the address bus was changing on the rising edge of the next clock pulse. This could data to get inadvertently written to the wrong location. To avoid cutting it close like that, and to give the Spikeputor more time to compute the address to write to, the following timing was envisioned:

    Instead of the Write pulse starting halfway through the clock cycle and ending at the very end, I wanted a pulse that started later and ended before the end of the clock cycle. One relatively easy way to achieve this would be to divide the clock by four and use simple logic gates to generate the desired pulse that starts 3/4 of the way through the cycle and ends 7/8 of the way through. To divide the clock, a discrete Toggle Flip-Flop was designed that toggles its output on every input clock cycle and could also take a SET input, which would be used to reset the new scheme whenever IOFLAG stops the Spikeputor clock:

    The discrete 555 circuit was taken from this great kit:

    Putting it all together, the new clock schematic is as follows:

    The discrete 555 passes through two T Flip-Flops to divide the signal by 4. The original clock signal and each of the T Flip-Flip outputs are combined via a NOR gate to create the desired proto-signal. This is then OR'd (as created by a NAND with inverted inputs) with a sub-circuit to produce the write pulse a short time after a manual clock pulse, creating the WRCLK signal. That signal in turn replaces the CLK signal input to the memory module, and with a few other small changes to get the phase of the signal right, the new memory schematic is:

    Whew! Here's a video of the new clock generator. You can see the clock signal get divided by two twice.

    The discrete 555 circuit is on the left half of the board. Measuring 72mm by 48 mm, it is exactly 64 times larger by area than the classic 555 IC package. Cool!

    So now the Spikeputor has a new clock, no more 555 chips, and new write signal timing, allowing me to increase the maximum clock frequency by 25%. Next up, time to finish and mount the third pegboard.

  • And Just Like That, We Have A CPU!

    spudfishScott11/18/2019 at 04:45 4 comments

    🚨Phase One of the Spikeputor Project has been completed! 🚨

    Integrating all of the modules was just a matter of making cables to connect everything, and writing some Arduino code to load data into the Spikeputor's memory through the DMA model. Here's a video describing the maiden voyage:

    Many more things to do, including a bunch of clean-up, making some memory from discrete transistors, adding some more I/O features, and optimizing the top speed.

    But for now, I'm just happy the thing actually works!

  • Adding the Program Counter Completes the Core CPU!

    spudfishScott11/05/2019 at 17:20 0 comments

    The final module to add to the core Spikeputor CPU is the program counter. The PC inputs are a JT (Jump to...) Address, a two bit PC Select (PCSEL) signal, and a signal to distinguish between a RESET or IRQ (IRQ-notRESET). Outputs are the current PC address, used to read the next instruction from memory, and the current PC address incremented by two bytes (one word), used to calculate branch offsets or to store a return address to a selected register.

    The schematic of the program counter includes a MUX4, a D-Register to store the PC output value on the next clock pulse, and a half adder with the initial input carry set to 1 to increment the current PC output value. Since the Spikeputor design restricts all memory access to even boundary addresses, the MUX4, register, and half adder circuits are only 15 bits wide with the lowest significant bit of the outputs hard coded to zero. The PCSEL signal input to the MUX4 selects which of four values is latched into the PC Register on the next clock pulse.

    When PCSEL is 0, the current value of the PC is retained on the next clock pulse. 

    A PCSEL signal of 1 selects the output of the half adder, incrementing the current PC address. 

    With a value of 2, the next PC value is set to either the RESET ($FFD0) vector or the IRQ vector ($FFE0). 

    Finally, a PCSEL value of 3 indicates that the JT input should be the next PC value, enabling branching.

    The entire Program Counter module is shown below. The clock board for the CPU is also pictured, since there was one open space for it to conveniently be placed.

    Here's a close-up of three MUX4 bits. The two blue LEDs reflect the PCSEL signal:

    And three bits of the PC Register:

    Finally, three bits of the half adder:

    To test the PC module, I applied the clock signal and manipulated the PCSEL signal to simulate the CPU under different conditions. 

    Initially, the PCSEL signal switches between 1 and 0 to simulate CPU phases 0 and 2, where an instruction is read, the PC is held constant, and the instruction is executed on the next clock pulse as the PC is incremented to fetch the next instruction.

    Later (at 0:16), I simulated a RESET condition. The PCSEL signal goes to 2, which sets the next PC value to the RESET vector, $FD00. From there, the cycle above continues from that address.

    Finally (at 0:34), by manipulating the control signals to simulate a JMP instruction, PCSEL goes to 3, which sets the program counter to a (hard-coded for this test) new address to continue the fetch-execute cycles.

    With the PC module complete, it remains to integrate all of the modules and start testing out actual programs! There are still a few major additions to complete as well (screen memory, logic path illumination), but those will be additions to the functional CPU. Stay tuned! 

  • Special Registers Completed

    spudfishScott10/12/2019 at 22:54 0 comments

    Since I've been away from the Spikeputor Manufacturing Center in Texas, it's been awhile since I've been able to make any progress on the project. I finally got there and spent some time completing the last two items referenced in the Memory Module. These are two special 16-bit registers for storing the current instruction and current constant, if required. You'll recall that the instruction is read from memory in CPU Phase 0. During that phase, ISEL is set high so the INSTRUCTION register is updated. Meanwhile, the CONSTANT register is updated on every clock cycle, meaning that if a constant is needed for the current instruction, it will be loaded in CONSTANT during CPU phase 1. INSTRUCTION still holds the current instruction, since ISEL is only high during CPU Phase 0. Then, during CPU Phase 2, the operation is executed, using CONSTANT as an operand if needed.

    To recap:

    Both registers are positive edge triggered D-Registers. The difference between the two is that INSTRUCTION has a Write Enable (EN) input so it can be selective about which memory outputs get stored.

    CONSTANT Schematic:

    INSTRUCTION Schematic:

    Here's a photo of the finished registers. CONSTANT is on top. Notice the blue LED in the INSTRUCTION register to indicate the state of the EN input.

    The last CPU module is the Program Counter circuitry. Once that's done, I can start integrating everything and get to an actual working stand-alone CPU! Everything else (and there's a bunch!) will be bells and whistles.

  • Making Memories

    spudfishScott06/30/2019 at 15:55 0 comments

    The Spikeputor memory module consists of two channels to address and read/write memory. One channel allows the CPU to access memory, and the other is used for DMA functions. The CPU can address memory from the Program Counter or from the ALU ouput, depending on the value of the MASEL (Memory Address SELect) signal. The DMA functions are performed by the BIAS card on the Apple II. Bringing the IOFLAG signal high pauses the CPU clock and redirects all memory access to BIAS. The memory is written to when either the MWE signal is high (set during the CPU STore command) or when the BIAS card sets IOW high. Memory data is available to be read by the CPU or BIAS as long as MWE (Memory Write Enable) is low. When high, MRDATA is undefined (high-Z). Memory data is stored in INSTRUCTION and CONSTANT registers as appropriate (discussed in the previous log entry). Additionally, bit 10 of the Memory data output is wired directly to the CN (Constant Needed) signal of the CPU Control Logic.

    In addition to DMA, there are 16 bits of General Purpose Input and 16 bits of General Purpose Output. These are hard coded to memory location $FFFE and can be written to in order to set the GPO lines and read from in order to read the GPI lines regardless of the current settings of the RAM/ROM Bank Select (see below).

    Memory storage is implemented in two ways. First, there are 32K words each of RAM and ROM, implemented by two each of 32K x 8 bit Static RAM (AS6C62256) and PROM (AT27C256R) chips. While RAM is always written to, whether RAM or ROM is read from depends on a bank-selection scheme, controlled by a two-bit specialty register, BANK_SEL. The value of BANK_SEL is set by writing the desired lower two bits to memory location $FFAE. RAM and ROM banks are accessed as shown in the table below:

    BANK_SEL[0] = 0BANK_SEL[0] = 1
    BANK_SEL[1] = 0RAM: $0000-$FFFFROM: $0000-$7FFF
    RAM: $8000-$FFFF
    BANK_SEL[1] = 1(default)
    RAM: $0000-$7FFF
    ROM: $8000-$FFFF
    ROM: $0000-$FFFF

    Second, there will eventually be 54 words of memory made via discrete transistors, resistors and LEDs, laid out to produce a 48 x 18 array of "screen memory" (this will be the last thing completed for the main Spikeputor boards). This memory will be "write-only," and will mirror chip RAM in the range $FF00-$FFFF. 

    The schematic of the memory modules is as follows:

    Finally, the power-up/reset logic is implemented via the following schematic. A RESET signal is simulated upon power-on and can be set manually via a debounced pushbutton or set via the BIAS card IORESET signal. RESET restores the BANK_SEL values to their defaults (0b10). The implementation of the simple memory elements for BANK_SEL was discussed in the "Building Blocks" section of the logs.

    Here's a photo of the actual Memory Module, followed by a block diagram of everything that's going on.

    The top row handles the selection of address and data input based on the logic described above, implemented by a pair of MUX2's for address input and a single MUX2 for data input. The next row contains the actual RAM and ROM chips, the general purpose input logic and LED display, and the data bus. Finally, the third row contains the screen memory addressing logic (a 3 to 8 decoder and a 4 to 16 decoder, partially implemented) and display, the power-up/reset/bank_sel logic, and a numeric display and output ports for the memory data output.

    Detailed photos of individual breadboards follow.

    Six bits of the address select logic:

    Eight bits of the data_in select logic:

    RAM Chip Memory:

    ROM Chip Memory (including an old Atari ROM chip for testing):

    Eight bits of the Data Bus:

    The 4 to 16 decoder for screen memory addressing, decoding the third nybble of the address. Only 11 lines are implemented since there are only nine rows of screen memory ($FF[0...9]x), plus one each to address BANK_SEL ($FF[A]E) and general purpose inputs and outputs ($FF[F]E).

    The power-up/reset/bank_sel...

    Read more »

  • Spikeputor ISA and Control Logic

    spudfishScott05/02/2019 at 20:00 0 comments

    Each Spikeputor instruction is one word (16 bits) long, with a subset of the instructions requiring a second 16-bit integer. Operations are divided into four groups: ALU Functions, Branching, Load, and Store.

    ALU Functions

    All ALU operations require two source operands, and one destination register. The two source operands are either two registers (Ra and Rb), or a register (Ra) and a 16-bit signed integer literal. The result of the ALU function is then stored in the destination register (Rc). Thus, ALU functions may either be one word or two words long, depending on the need for the 16-bit constant. Two-word instructions end in C.

    ALU Instructions:

    • Arithmetic
      • ADD(Ra, Rb, Rc)
      • SUB(Ra, Rb, Rc)
      • ADDC(Ra, const, Rc)
      • SUBC(Ra, const, Rc)
    • Bitwise Logic
      • AND(Ra, Rb, Rc)
      • OR(Ra, Rb, Rc)
      • XOR(Ra, Rb, Rc)
      • ANDC(Ra, const, Rc)
      • ORC(Ra, const, Rc)
      • XORC(Ra, const, Rc) 
    • Bitwise Shift: The bottom four bits of the second operand represents the number of bits to shift. SRA is a right shift with sign extension.
      • SHL(Ra, Rb, Rc)
      • SHR(Ra, Rb, Rc)
      • SRA(Ra, Rb, Rc)
      • SHLC(Ra, const, Rc)
      • SHRC(Ra, const, Rc)
      • SRAC(Ra, const, Rc)
    • Compare: Rc is set to 1 if the comparison is true, 0 if not. Comparisons are based on 2's complement signed integers except for CMPUL, which is an unsigned compare less than.
      • CMPEQ(Ra, Rb, Rc)
      • CMPLT(Ra, Rb, Rc)
      • CMPLE(Ra, Rb, Rc)
      • CMPUL(Ra, Rb, Rc)
      • CMPEQC(Ra, constant, Rc)
      • CMPLTC(Ra, constant, Rc)
      • CMPLEC(Ra, constant, Rc)
      • CMPULC(Ra, constant, Rc)


    Conditional branch instructions test the value of register Ra. The branch is taken if Ra is zero (for BEQ) or not zero (for BNE). The second word of the instruction is used to specify a 16-bit signed Program Counter offset. Before the branch, register Rc is loaded with the next calculated Program Counter value so it may be used as a return vector. If the branch will be followed, the target branch address is calculated by adding the given offset to that new PC value.

    For immediate branching, there are two options: JMPC calculates the target branch address by adding the value of Ra to the given 16-bit signed literal. JMP simply takes the target branch address from the value of register Ra. As with the conditional branches, Rc is set to the address of the subsequent instruction. Branch instructions are two words long except for JMP. Also, if Ra is register 6, the JMP(R6, Rc) instruction will clear the IRQ flag, indicating the end of interrupt processing.

    Branching Instructions:

    • BEQ(Ra, PC offset, Rc)
    • BNE(Ra, PC offset, Rc)
    • JMPC(Ra, mem offset, Rc)
    • JMP(Ra, Rc)


    There are two load instructions to move values from memory into registers. Both instructions use two words, the second of which is used as a signed memory offset. For the LD instruction, the target memory address is calculated by adding the value of register Ra to the given offset . The LDR (LoaD Relative) instruction adds the offset to the memory address of the subsequent instruction. In both cases, the data in the calculated memory address is stored in register Rc.

    Load Instructions:

    • LD(Ra, mem offset, Rc)
    • LDR(PC offset, Rc)


    The only way to write to main memory is through the two-word Store instruction. The value of register Rc is stored in the memory address calculated by adding the value of register Ra to the value of the second word.

    Store Instruction: ST(Rc, Ra, mem offset)

    The format of the opcodes are as follows:

    The top five bits of the opcode are simply the ALU function (described in the ALU log entry) and are passed directly to the ALU. Bit 10 (Constant Needed) indicates whether or not the opcode is made up of a one word or two words. Bit 9 differentiates...

    Read more »

  • It's About Time!

    spudfishScott04/27/2019 at 22:56 0 comments

    Until now, I've been generating clock signals via an Arduino. With the Register Memory and ALU boards complete, and work beginning on Control Logic, it was time to think about building a clock for the Spikeputor. Like many other homebrew computers, I wanted the clock rate to be variable based on user control of some kind of knob or slider. I also wanted the ability to switch to a mode where the user can control the clock pulses on a step-by-step basis for a fine-grained look at the CPU in action.

    Because everyone loves a 555, I decided that it would be an appropriate timekeeper for the Spikeputor. I considered creating a discrete version (see this great kit, for example), but since space on the pegboards was filling up fast, I decided to start with ICs, and then go discrete later on if space is available.

    Here's the schematic for the clock circuit:

    I'm actually using two 555's - one to generate the variable-rate clock signal, and another to generate a clean 200 ms debounced pulse for the manual step pushbutton. Using a 10 MΩ variable resistor and a 200 nF capacitor, clock rates between 0.35 and about 17,000 Hz are possible. I might want a little more dynamic range, so I'm considering switching to a control knob where I can substitute a larger range of resistors, but for now, the 10 MΩ slider will do.

    The 555 clock signal and the single step pulses go into a 2-channel MUX, the output of which is controlled by a Clock/Step Select switch, itself debounced via an SR latch. Finally, one final gate allows for setting IOFLAG high to stop the clock so the DMA I/O can occur.

    Here's how it all fits together on a board:

    IOFLAG is  hard-wired to 0 right now, but that will eventually connect up to the BIAS board of the Apple II for I/O.

    And here's a little demo of the board in action. We start in clock mode, adjust the rate, then switch over to single-step mode, step a bit, then switch back to the clock. This board drives the proto-Spikeputor perfectly!

    UPDATE: I decided to eschew one of the 555 chips in anticipation of eventually replacing the single remaining 555 with discrete components. Instead, I used a DPST momentary pushbutton switch for the single step pulse, debounced via another SR Latch as shown here:

  • I'm a Big Fan of Sharp Clock Signals

    spudfishScott04/06/2019 at 18:03 0 comments

    As work on the Spikeputor continues, I wanted to check out the shape of the clock pulses, now that a single signal feeds into 112 inputs (7 registers * 16 bits per register). In the entire design, there will be upwards of 165 clock inputs. The result of such a high fan-out number was not entirely unexpected:

    At 37 µS for full rise, that starts rivaling the top speed I want to be able to run the processor (~40 µS, preferably faster if I can speed up the ALU). All of those tiny capacitances of each of the input transistors are starting to add up, slowing down the rise time! Luckily, there's a simple solution to this expected problem: build a clock tree. I inserted a buffer (two transistors) in front of every group of 16-bit inputs, and now the rise time is seven times faster. Math and physics works! Yay!

    None of this is rocket science, but it's a very helpful and educational illustration of these principles. Onward!

  • ALU and RegMem Completed - Demo Time!

    spudfishScott03/02/2019 at 22:15 5 comments

    Two of the four major pegboards of the Spikeputor are now complete and have been mounted. They come down pretty easily in case repairs or modifications need to be made that can't be performed right on the wall.

    Now that we have seven functioning registers and an ALU, we can put it through its paces by simulating the rest of the CPU. Wiring together the components along with an Arduino, the nascent CPU can be "programmed" by simply setting up the correct input signals after every rising clock pulse (also provided by the Arduino). 

    Since there is no RAM yet, the only storage is in the registers. Nevertheless, you can easily set up the Arduino to do something like this:  

    • Place the correct values in each of the registers:
      • Select the register with Ra and Rc to output to Channel A and to allow the input to overwrite it (Rb is ignored)
      • Turn Write Enable on
      • Output the correct bit pattern to the CONSTANT input. 
      • Set ASEL to 0 (input from Register Channel A goes to ALU A)
      • Set BSEL to 1 (input from CONSTANT goes to ALU B)
      • Set the ALU to the ADDC function to add Input A to Input B.
    • Rotate Left each of the registers:
      • Select the register with Ra and Rc to output to Channel A and to allow the input to overwrite it (Rb is ignored)
      • Turn Write Enable on
      • Output 0x0001 to the CONSTANT input. 
      • Set ASEL to 0 (input from Register Channel A goes to ALU A)
      • Set BSEL to 1 (input from CONSTANT goes to ALU B)
      • Set the ALU to the Rotate Left function to rotate Input A by Input B bits.
    • After doing this for all seven registers, pause briefly and loop back to another rotate cycle

    And voilá! Ghost In The Machine!

    You can also set up a similar type of loop, except instead of shifting bits, you can add a constant. This video starts with adding 1 by setting the ALU A Input DIP switch to 0x0001, outputting the selected register to Channel B with write-back, and cycling for a number of clock cycles before moving on to the next register. It shows the whole CPU, then focuses on the ALU output, and then Register Output Channel B. The DIP switch is then changed to 0x0010 to increment the second digit (by adding 16), leaving the first digit intact.

    Finally, for something a bit more advanced, we can manipulate the control signals to simulate a program to calculate the first 24 Fibonacci numbers (all of them that can fit in 16 bits). After each number, the control signals are set to place the step number on RegMem Output Channel A and the corresponding Fibonacci number on RegMem Output Channel B, then pause before calculating the next one. The "program" uses four registers: 

    • R0 = N
    • R 1 = Fib(N-2)
    • R2 = Fib(N-1)
    • R3 = Fib(N)

    (Video in the dark for dramatic effect!)

  • Adding an Unsigned Compare Function to Enable Wide Math

    spudfishScott02/18/2019 at 21:49 0 comments

    As currently designed, the Spiekputor's ALU Compare functions worked with signed integers only. For whatever reason, the "Beta" processor from MITx, upon which the Spikeputor CPU was based, only included signed compares (so, for example, the number 0xC000 would be reported as less than 0x7000 because 0xC000 represents the signed integer -16384, rather than the unsigned integer 49152). This presents a problem if one desires to do math on numbers of a higher bit depth than the bit depth of the CPU registers and the ALU. To add two 32-bit numbers with a 16-bit ALU, for example, you need a way to calculate the carry after summing the lower 16 bits of the 32-bit numbers, then add that carry to the sum of the higher 16 bits. Calculating the carry can be done a variety of ways in code, but it comes directly by checking if the low order sum is less than either of the addends. Thus, an Unsigned Compare Less Than function would be a great addition to the ALU commands. Luckily, it was fairly easy to add the new compare function by expanding the Compare module, which simply subtracts one operand from the other and reports a result based on logic with (Z)zero, (N)egative, and o(V)erflow flags. Adding one more compare function (CMPUL for CoMPare Unsigned Less-than), and bringing in the Carry Out from the Bit 15 adder, as shown below, does the trick.

    Previous Compare Module:

    New Compare Module:

    This change adds just four transistors to the circuit (converting a MUX3 to a MUX4 and inverting Cout[15]), and now, the Spikeputor will be able to easily do math on numbers arbitrarily larger than 16 bits. A very small price to pay, indeed!

View all 18 project logs

Enjoy this project?



roelh wrote 09/12/2019 at 13:19 point

Hi spudfish, thank you for liking and following #Kobold 2 RISC computer !

  Are you sure? yes | no

Lee Wilkins wrote 01/07/2019 at 18:33 point

Very cool! Can't wait to see how ti progresses. Have you got any clearer pictures up close? 

  Are you sure? yes | no

spudfishScott wrote 01/07/2019 at 18:47 point

Thanks! I'll upload more close-up pictures as I "catch up" in the project log with where I am now.

  Are you sure? yes | no

Yann Guidon / YGDES wrote 01/03/2019 at 21:49 point

Oh, a MegaProcessor on Hackaday :-D
For MUX, you could use a "ladder/binary tree" system, which requires complementary control inputs but uses fewer transistors.
NMOS has some inherent drawbacks (power and speed). I've tried discrete CMOS for #Yet Another (Discrete) Clock :-)

  Are you sure? yes | no

spudfishScott wrote 01/05/2019 at 19:23 point

Thanks. I do know about the Megaprocessor. Although I found it after I started designing the Spikeputor, I decided to go forward anyway. The designs are different enough, plus I want one of my own! I'm pretty happy with NMOS, because I don't need the speed and the power requirements are reasonable (about 25 Watts total). Also, p-Channel MOSFETs are 8-10 times as expensive as n-Channel and using CMOS would double the number of transistors required.

  Are you sure? yes | no

Yann Guidon / YGDES wrote 01/09/2019 at 03:18 point

it's not as if you could have too many transistors ;-)

  Are you sure? yes | no

Peabody1929 wrote 12/18/2018 at 18:11 point

It looks like you are using EasyEDA.  Instead of breadboards, how about creating simple PCBs for different modules?  If one wire comes loose on a breadboard somewhere, it could take a LOOOOOOONG time to find it.

  Are you sure? yes | no

spudfishScott wrote 12/18/2018 at 18:26 point

Your point is well taken, and many of my friends think I'm nuts to not use PCBs. But I'm pretty committed to breadboards. I really like the way they look and they fit my artistic vision for the project. Interestingly, since things fail at the bit level, it has been simpler than you might think to track down wiring problems.

  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