CS 202 - Project 02: ARM Processor
2026-04-24T11:59
Goals
- Combine what you know about digital logic and assembly code to tie together the two abstraction layers
- Demonstrate that you know how to take a complex problem, break it down into smaller pieces, implement solutions to those and then assemble them into a final solution
Objective
Your objective for this project is to implement a slightly simplified simulation of an ARM32 processor. You processor should be able to handle both the register and immediate versions (where applicable) of the following instructions:
ANDADDBCMPEORMOVMVNORRRSBSUB
Memory
LDR and STR are not on this list because our primary simplification will be to make our memory read only.
To create your read only memory, you can use the ROM element from the Memory library. We aren’t going to write super long programs, so it is okay to set the address bit width to 8 and the data bit width to 32. This will give you 256 bytes, or 64 instructions, which is plenty. I also suggest that you use “Classic Logisim” for the appearance, which gives you the simplest looking interface.
While you can type values into the ROM like you can the pins, a faster way to load a whole program is to use image files. These are just text files with a list of values (similar to the test vectors). If you right click on the ROM you will see menu options for loading and saving these files.
The get you started, here is a simple program: mult.txt. You can use it directly, but you should not use it as your sole test of the functionality. Use it as a template for making other test programs.
You are expected to make use of the ALU and register file that you created for problem 09 and problem 10.
Condition codes
In your ALU, you are outputting the condition codes N, Z, C, and V. In an ARM processor, these are stored in a register. This allows us to control when they are set, which should be on the rising edge of the clock on instructions that set the codes.
Timing
For our simplified process, we will be doing all of the computation in a single clock cycle. Most of the actual operation of the processor is combinational. When the rising edge of the clock comes, you should think about saving the result of the computation rather than doing the computation. The calculation of the new PC is included in that. So after the rising clock edge, the PC will have the new value, and the computation of the next instruction and the next PC will occur.
Requirements
- There should be a pin for each of the 16 registers. The processor is basically a closed system (an a real computer we are at the level of adding keyboard, mouse, monitor, network cable, etc… for I/O). The pins will make debugging and checking easier.
- The primary circuit should be in
main. We should be able to see the clock, the ROM and the register pins at minimum at this level. - You should use the
clockcomponent from the Wiring library for the clock. - On the rising edge of the clock, the instruction pointed to by the PC (r15) should be executed, updating the appropriate register as required by the instruction. Simultaneously, the PC should be updated with the address of the next instruction.
Advice
- Make good use of subcircuits. With complex systems, abstraction is our best tool for managing complexity.
- We have not used them yet, but in Logisim, there is a component called a “tunnel” that you will find in the Wiring library. If you make two tunnels and give them the same name, it will create a connection between them without the need to connect them with wires. You can use these to connect components on opposite sides of your circuit without having to create a web of wiring everywhere.
- Don’t make things because you think they will be useful. Only add things when you know that you need them.
- If you are going to make a big change, make a copy of your circuit first. If you know how to use version control, that would be ideal. Failing that, I advise making a folder with the date and time as its name. Put a copy of your circuit inside along with a text file describing the current state of the work. That will make it much easier to return to earlier work if your modifications don’t work out.
General approach:
- Start with a checklist of the instructions that you need to implement (separate out the immediate and register versions)
- Order the list by perceived complexity
- The the first instruction on the list and write out a specific instance of it. Assemble it into machine code and wrote down what you expect when the instruction is run.
- Add the instruction as the current instruction in the ROM. Look at the inputs to the register file to see if it will do what you think it should. Adjust as necessary.
- Test some other versions.
- Move on to the next instruction on the list and repeat.
Many of the data processing operations will come together pretty easily once you have figured out how to deconstruct the instructions.
Submission
Upload your .circ file to Gradescope.