Check back for corrections and clarifications
1. If you do this project in a pair, have one partner submit and include a textfile named README with the other partner's login.
2. Feel free to add extra inputs and outputs to subcircuits besides those specified.
3. To make things clear, your CPU should be single cycle.
4. For the overflow LEDs, to simplify things, allow them to go on for any instruction that needs the ALU to add.
5. Added clarifications to how to run the assembler.
In this project you will be using Logisim to create an 16-bit single cycle CPU for an instruction set called CAL-16R. Please read this document CAREFULLY as there is a lot of new stuff to cover and a lot of your questions are no doubt answered within.
In this project you're allowed unrestricted access to Logisim's built-in libraries.
Though Logisim is generally stable it is recommended that you save and backup your .circ files early and often.
If you are having trouble with Logisim: REBOOT! Don't waste your time chasing a bug that is not your fault. However, if rebooting doesn't solve the problem, it is very likely that the bug is a flaw in your circuit.
Logisim offers some functionality for automating circuit implementation given a truth table, or vice versa. Though not disallowed (enforcing such a requirement is impractical), use of this feature is discouraged. Remember that you will not be allowed a laptop running Logisim on the final.
The lab computers have Logisim version 2.3.3, so we will use that version in grading. It's probably a good idea to make sure that your circuit works on it.
The CAL-16R is a load-store architecture like MIPS, but the
word width is only 16 bits. It has 16 general-purpose
registers. Register $0 always contains 0, just like in
MIPS. Memory is word-addressed (READ: 16-bits). We will not be
implementing the instructions with opcodes D and E; they can be ignored.
Immediates are sign-extended to 16 bits as indicated by the Sx
function in the RTL below. The instructions
are pretty easy to read in hex because each field is one or more hex
digits. The instructions are arranged to make them easy to decode.
15-12 | 11-8 |
7-4 |
3-0 |
op | Semantics | ||
0 | a | d | b | and | R[d] := R[a] & R[b] | PC := PC+1 | |
1 | a | d | b | or | R[d] := R[a] | R[b] | PC := PC+1 | |
2 | a | d | b | xor | R[d] := R[a] ^ R[b] | PC := PC+1 | |
3 | a | d | b | add | R[d] := R[a] + R[b] | PC := PC+1 | |
4 | a | d | off4 | addi | R[d] := R[a] + Sx(off4) | PC := PC+1 | |
5 | a | d | off4 | rotr | R[d] := R[a] rot zeroExtend(off4) | PC := PC+1 | |
6 | a | d | off4 | sw |
Mem(R[a] + Sx(off4)) := R[d] | PC := PC+1 | |
7 | a | d | off4 | lw |
R[d] := Mem(R[a] + Sx(off4)) |
PC := PC+1 | |
8 | a | off8 |
li | R[a] := zeroExtend(off8) | PC := PC+1 | ||
9 | a |
off8 |
disp | DISP[off8] := R[a] | PC := PC+1 | ||
A | a | off8 |
bneg | PC := (R[a] < 0) ? PC + Sx(off8) : PC+1 | |||
B | a | off8 |
bz | PC := (R[a] == 0) ? PC + Sx(off8) : PC+1 | |||
C | a | d | off4 | jalr | R[d] := PC + 1 | PC := R[a]+Sx(off4) | |
D | |||||||
E | |||||||
F | off12 |
j | PC := (PC & 0xF000) | zeroExtend(off12) |
In addition to the registers and PC, your machine contains a ROM module for instruction memory and a RAM module for data memory, both consisting of 64Kibi words.
This machine has very few instructions. Many of the ones you are used to need to be implemented in software using combinations of these simple ones. There is no subtract; that is implemented in software using XOR, ADDI, and ADD. There is no LUI; you need to LI and ROTR. There is no load or store byte. You need to load, merge and store one 16-bit word at a time.
Using the 16-bit Register under the Memory library, implement a subcircuit with this behavior -
Inputs
Outputs
Don't make the clock signal an input; wire the Register to a clock inside the subcircuit.
Using Reg16s, implement a 16x16-bit register file. Register 0 always reads as 0. You may find the Plexers library useful.
Inputs
Outputs
This ALU will be somewhat different from your hw7 because the TA is bad at thinking ahead, but he's sure that you'll do fine making another one.
Inputs
Outputs
The functional behavior is given by the following table.
op | operation | |
0 0 0 | and | R = A & B |
0 0 1 | or | R = A | B |
0 1 0 | xor | R = A ^ B |
0 1 1 | add | R = A + B |
1 0 0 | pass B | R = B |
1 0 1 | rotate right | R = A >> B[3:0] | A << 16-B[3:0] |
It will help you to build a simple subcircuit that splits the instruction into its six possible fields: op, ra, rb(off4), rd, off8, off12. This will simplify your wiring a lot later. Keep these signals in mind as you build up your datapath.
Logisim RAM modules can be found in the built-in memory library. Depending on the version of Logisim, you might have to manually add the library to your project. To do so, select "Project/Load Library/Built-in Library..." and select the Memory module.
The best way to learn how these work is simply to play with them. In any case, here's a little bit of info to help you get started. The Logisim help page on RAM modules can be found here, but is not terribly helpful. A chooses which address will be accessed (if any). sel essentially determines whether or not the RAM module is active (if sel is low, D is undefined). The clock input provides synchronization for memory writes. out determines whether or not memory is being read or written. If out is high, then D will be driven with the contents of memory at address A. clr will instantly set all contents of memory to 0 if high. D acts as both data in and data out for this module. This means that you must use a controlled buffer on the input of D to prevent conflicts between data being driven in and the contents of memory. To modify the contents of the module, use either the poke tool or "right-click/Edit Content..."
The newer Logisim versions offer a configuration option for the RAM module that allows you to change it to use separate pins for input and output data. You're allowed to use that instead of the type in the picture above if you can get the same functional behavior.
You will be using a Logisim ROM module for your instruction memory. It is a much simplified version of a RAM module Both RAM and ROM modules can also be loaded from files using "right-click/Load Image..."
If you check the tattoo on your left arm, you'll be reminded that every computer has five parts: control, datapath, memory, input, and output devices. Accordingly, your project must include at least 2 arrays of four seven-segment displays for output. It should look something like this:
The disp instruction assigns a register's value to the off8th array of four seven-segment displays. Even though you have 8 bits, you'll only need 1 of them to select between 2 arrays, although adding more would be more awesome.. kind of. The register value should be held until the next time a disp instruction replaces that display index. We've provided a converter library to make seven-segment displays easier to deal with. It can be downloaded here and included via the "Load Library/Logisim Library" menu option. See the examples section to get a better idea of how to incorporate these into your project.
You also need to have TWO LED units which light up to signify the two kinds of overflow. They should go on if and only if you are adding.
At this point in this document should have all the primary elements that make up your CPU. What's left is putting the datapath together and implementing control to make it have the correct behavior for the instructions that it will execute. Go wild but not too wild; your final circuit should look as clean as you can make it.
We've provided an assembler for CAL-16R to make writing your test programs easier.
The assembler can be downloaded here. Instructions are in comments at the top of the ruby script. Post bugs (or bugfixes) on the newsgroup...
First copy it to a local directory, the add executive permission withchmod u+x asm-cal16r.rb
change the #! path (by default /bin/ruby) to where the ruby interpreter is installed on your machine, which you can find by
which ruby
It can be invoked from your working directory like so:
./asm-cal16r.rb input.c16 > output.o
You must have your instruction ROM module and data RAM module visible from the main circuit of your Logisim project. Your displays and LEDs must also be visible there.
You must use subcircuits. They make it easier for you, they make it easier for us. Give your subcircuits appropriate labels as well. Keep in mind that this won't be autograded, and humans will have to look at your schematics (and grade them!). Excessively cluttered implementations will lose points!
Use multi-bit buses in Logisim! You will have to use splitters to get individual bits from the wire or to combine the bits to get a multi-bit bus! If you don't know how, this is the time to learn! If you're not sure how to use these, look back at the labs. If you're still confused, ask us or your peers!
Use the label tool to organize your CPU. In particular label the control, datapath, and display sections, but it can also be useful to label specific busses and wires. It could make debugging a lot easier!
you can optionally submit other files such as a README or subcircuit libraries.
From the directory containing your project files, submit using the following command: