CS 232: Project 7

Title image Spring 2026

Project 7: CPU

Due Monday 27 April 2026

Purpose: The purpose of this project is to build a simple CPU that integrates all the necessary aspects of a general-purpose computer. This is a two-week project. You will need to demonstrate your functional alu with the alu test program during the labs on November 9-10.

Note: you may design your own CPU however you like, so long as it implements the instruction set specified in the design document. You may, after talking with the instructor, even choose to discard the specific machine language and design your own, so long as the computer supports at least the set of instructions specified. Either of these options will be considered major extensions and your project will be graded out of 30.


Tasks

The overall task is to create a functional CPU, using a ROM for the program memory, a RAM for the data memory, and a separate ALU circuit.

Examine the CPU Design before starting.

  1. Setup

    Make a new project for lab 7 in a new folder. I'll refer to the project and top-level circuit as cpu, so if you are using Quartus, please name your project cpu.

  2. Create Memory Files

    In Project 6, we used the RAM for a stack and it started out as empty. In Project 5, we implemented ROM as a conditional assignment statement that returned data based on the address inputted. In this project, we use both RAM and ROM as generated by the MegaWizard Plugin Manager and both will be given values by importing memory initialization (.mif) files. (Mif files are just ASCII text files, so you can read them with the text editor and see that their format is a pretty simple address, then data format.)

    First, create the ROM as a entity named ProgramRom.

    • If you are using Quartus, use the Tools:MegaWizard Plugin Manager to create a 1-port ROM that has 256 words of 16 bits each. Tell it to initialize the ROM using program.mif. Be sure to add ProgramRom.vhd to your project if is isn't automatically added.
    • If you aren't using Quartus, then download the file Stephanie made (ProgramROM.vhd) along with program.mif.

    Second, create the RAM as an entity named DataRAM.

    • If you are using Quartus, use the Tools:MegaWizard Plug0in Manager, create a 1-port RAM that has 256 words of 16 bits each. Tell it to initialize the RAM using data.mif. Be sure to add DataRAM.vhd to your project if that isn't done automatically.
    • If you aren't using Quartus, then download the file Stephanie made (DataRAM.vhd) along with data.mif.
  3. Define the CPU entity

    Create a top-level VHDL file called cpu.vhd with an entity called cpu. Your cpu entity should have a clock (std_logic), reset (std_logic), input port (std_logic_vector(7 downto 0)), and an output port (std_logic_vector(15 downto 0)). You can use the 4-state Moore machine template, if you wish.

    To use the cpu test bench for simulation, you will need to include the following signals to your cpu port statement. The port statement that matches the cpubench usage is below.

      port (
        clk   : in  std_logic;                       -- main clock
        reset : in  std_logic;                       -- reset button
    
        PCview : out std_logic_vector( 7 downto 0);  -- debugging outputs
        IRview : out std_logic_vector(15 downto 0);
        RAview : out std_logic_vector(15 downto 0);
        RBview : out std_logic_vector(15 downto 0);
        RCview : out std_logic_vector(15 downto 0);
        RDview : out std_logic_vector(15 downto 0);
        REview : out std_logic_vector(15 downto 0);
    
        iport : in  std_logic_vector(7 downto 0);    -- input port
        oport : out std_logic_vector(15 downto 0));  -- output port
    
    								

    Create component statements for both the ProgramROM and DataRAM in the architecture header.

  4. Write the ALU

    Create an arithmetic logic unit VHDL design file, alu.vhd. Add the file to your project. You can use this template as the basis for the ALU circuit design. The circuit is completely asynchronous, and it does not require any additioanl signals beyond what is in the template in order to build the circuit.

    There are four condition bits that need to be set, based on the ALU operation.

    • Zero: cr(0) should be set to '1' if the ALU operation resulted in all zeros for bits 15 downto 0 of the result.
    • Arithmetic overflow: cr(1) should be set to '1' if an addition or subtraction operation resulted in an overflow.
      • 2's complement overflow for addition occurs when the two operands are the same sign and the result is a different sign.
      • 2's complement overflow for substraction occurs when the result of subtracting a positive number from a negative number is positive and when the result of subtracting a negative number from a positive number is negative (i.e. when the subtraction results in a number further from zero and it goes far enough that it flips the sign.)
    • Negative: cr(2) should be set to '1' if the sign bit of the result (i.e. tdest(15)) is '1' (negative).
    • Carry out: cr(3) should be set to '1' if a '1' was shifted out of the left or right side of the input, or if an arithmetic operation resulted in a carry. tdest(16) contains the carry bit (see comments in ALU.vhd).

    When you have your alu design complete, test it using this testbench program. It should produce the output below using ghdl/gtkwave.

    ghdl -a alutestbench.vhd alu.vhd
    ghdl -e alutestbench
    ghdl -r alutestbench --vcd=alutestbench.vcd
    gtkwave alutestbench.vcd &

    Debugging Note: If your CR bits are incorrect (i.e.. 0xC when should be 0x4), then that is likely because the instructions weren't clear enough about how to complete the logic operation in the ALU. For the arithmetic operations (addition, subtraction, shifting, rotating), you need to sign-extend. For the logic operations (AND, OR, XOR), you should extend the results of athe operation with '0' (e.g. '0' & (SRCA and SRCB))

  5. Create the CPU state machine

    As noted in the CPU design, you probably want to use a 9-state state machine for your CPU. The first state is a startup state, and the final state is a halt state. After eight clock cycles, the state should move to the fetch state from the satrt-up state. You will need a small internal counter (3 bits) to implement the pause in the start state. The state machine should never return to the start state unless it is reset.

    The remaining states are fetch, execute-setup, execute-process, and execute-write. Set up your overall state machine structure to follow this model. Each state always leads to the next state in the process, with the execute-write state going back to the fetch state.

    Set up your skeleton nine state state machine, putting in only the state transitions for now. No state should go to the halt state (yet).

  6. Define the internal signals

    Create the remaining internal signals you need for your CPU. There are a lot. Following the cpu diagram, there should be a signal for every register, including the condition register. In addition, you will need a signal to represent the two ALU input buses, the ALU output bus, and the condition flags coming from the ALU.

    You will also want signals for the output port register (OUTREG), the ROM data-out wire, the RAM data-out wire, the RAM write-enable wire, the ALU opcode, and the ALU condition argument.

    Now is a good time to assign the various signals to their matching outputs. The PCview should be assigned the value of the PC, the IRview should be assigned the value of the IR, and so on. You will also need to be sure to assign your internal output register (OUTREG) signal to oport.

    Note about the condition register: You need a 4-bit condition register (CR) and an additional 4-bit signal (let's call it aluCR) that port maps to the ALU's condition output. Why not just port map the CR to the ALU's condition output? Because we want the CR to reflect the conditions after the most recent ALU operation at the same scope. If we make a "CALL", then we need to store the CR on the stack (with the PC). and when we RETURN, we restore the CR from the stack. that way, the next conditional branch depends on the last ALU operation done in the main program, rather than in the function call. Since CR needs to be set by either the ALU or the stack, it can't be port-mapped to the ALU. So it needs to be its own signal.

  7. Port map the associated elements

    Port map the ProgramROM, DataRAM, and ALU circuits into your cpu. The clock, PC and ROM data wire should map to the ROM clock, address, and output.

    The MAR and MBR should map to the RAM address and data-in parameters. The RAM data wire, RAM write enable wire, and clock should map to the RAM output, RAM write enable (wren) and clock.

    The srcA, srcB, and dest parameters should map to the ALU input buses and the output bus, respectively. Map the ALU opcode and condition outputs to their respective signals.

  8. Implement the CPU
    1. Reset case

      If the reset button is activated, set the registers PC, IR, OUTREG, MAR, MBR, RA, RB, RC, RD, RE, SP, and CR to zeros. The state should be reset to the startup state and the small counter should be reset to zeros.

    2. Start state

      The startup state should increment the small counter and stay there until it reaches 7. Then it should move to the fetch state. (this is not a for loop)

    3. Fetch state

      The fetch state should copy the ROM data wire contents to the IR, increment the PC, and move to the execute-setup state.

    4. Execute-Setup state

      The execute-setup state should set up each of the instructions.

      • For the load and store instructions, move the correct RAM address into the MAR. If IR(11) is set, use the 8 low bits of the IR plus register E (RE). If IR(11) is not set, just use the low bits of the IR. You will also need to put the data to write into the RAM into the MBR.
      • For the unconditional branch, set the PC to the low 8 bits of the IR.
      • For the conditional branch, check the condition flag specified in the instruction and move the low 8 bits of the IR to the PC if the condition is true.
      • For the CALL instruction, set the PC to the low 8 bits of the IR, set the MAR to the SP, set the MBR to the concatenation of four zeros, the CR, and the PC. Then increment the stack pointer.
      • For the RETURN instruction, set the MAR to the SP-1 and decrement the SP.
      • For the push operation, put the current vaue of the SP into the MAR, increment the SP, and put the value specified in the source bits into the MBR.
      • For the pop operation, put the value (SP-1) into the MAR and decrement the SP.
      • For the binary ALU operations (add, sub, and, or xor) set up srcA and srcB.
      • For the unary ALU operations (shift, rotate) set up srcA and put the direction bit in the low bit of srcB.
      • For the move operation, set up srcA, which will come either from a register or be a sign extended value from the immediate value bits of the IR (bits 10 downto 3).
    5. Execute-ALU state The execute-ALU state should set the RAM write enable signal to high if the operation is a store (opcode 0001, or integer 1), a push, or a CALL.
    6. Execute-MemWait state The execute-wait state should do nothing. All instructions can pass through this state, but it is required only for instructions that are reading from the RAM (POP, LOAD, and RETURN).
    7. Execute-Write state The execute-write state should handle the final stage of the various operations. At the beginning of the state, it should set the write enable flag to '0'.
      • For the load operation, it should write the contents of the RAM data wire to the specified destination register.
      • The store, unconditional branch, conditional branch, call, and push operations require no action.
      • The return operation requires that the proper parts of the RAM data wire be written to the PC and CR.
      • For the pop operation, it should write the value of the RAM data wire to the destination specified in the instruction.
      • For the write to the output port, it should set the output port register OUTREG to the specified value.
      • For the load from the input port, it should write the input port value to the specified register.
      • For the binary and unary arithmetic operations (add, sub, and, or xor, shift, rotate), it should write the destination value to the proper register. It should also assign the ALU condition flags to the condition register [CR].
      • For the move operation it should write the destination value to the proper register and set the condition register [CR] from the ALU condition flags.
    8. Execute-return states The execute-return-pause states, there are two of them, are equired for RETURN instructions so that the next instruction can be accessed from the program ROM before heading back to the fetch state. On the RETURN instruction needs to use these two states.
  9. Test the CPU

    Test your CPU using the program given in the programROM.mif file above and using the cpubench.vhd test file. You should get a simulation like the following (4 images capturing about 100 ns each). Note that on the second command below you need to make sure the filenames match your own.

    You can use the following single step command to compile and simulation using ghdl

    ghdl -c --ieee=synopsys -fexplicit --work=altera_mf /opt/quartus/quartus/eda/sim_lib/altera*.vhd cpubench.vhd cpu.vhd alu.vhd programROM.vhd DataRAM.vhd -r cpubench --vcd=cpubench.vcd
    gtkwave cpubench.vcd &

  10. Test the stack operations and a function call

    Here are some more test programs you can try out. Read the .mif files to understand what they are doing and what you should expect for outputs. To use the mif file, download it and change the name from "program.mif" in ProgramRom.vhd.

    • testpush.mif an MIF file for testing push and pop.
    • testcall.mif an MIF file for testing a function call (check out the com,ents indicating how to interpret the file.).
  11. Have your CPU generate the Fibonnaci sequence

    Write a program for your computer that creates the first 10 values of the fibonnaci sequence. Try to do it with a for loop. Simulate your circuit and demonstrate that it works.


Extensions


Report

Create a Word/Pages/etc document for your report.

Please note that all these report requirements are reflected on your project 7 feedback spreadsheet. Remember to include all required elements in your report and grant access to "Anyone with the link" if you provide any video links.


Handin

Put your vhd and write-up files in a folder called Project7_<username>, zip it up (so its name will be Project7_<username>), and upload the zip file to the Google classroom assignment for this project.