We use CircuitSim version 1.9.1
to
inplement the datapath and simulate the assembly. For Windows user, go to
CircuitSim/Windows/ to
download and install the .exe
file.
For MacOS, the installation is a little bit tricky. First, download the Jar file
from CircuitSim/Jar/.
CircuitSim requires Java 14+. Additionally, not all versions of the JDK come
with JavaFX which CircuitSim uses.
- If you have never installed Java 14 or higher before, simply install the
latest JDK that already comes with JavaFX, i.e.,
Azul Zulu.
To open CircuitSim,
java -jar "CircuitSim1.9.1.jar"
- If you already have a recent Java version installed, download the JavaFX SDK
separately from Gluon JavaFX that
matches your Java version. To open CircuitSim,
java -jar --module-path "javafx-sdk-21.0.4/lib" --add-modules javafx.base,javafx.controls,javafx.fxml "CircuitSim1.9.1.jar"
Note that you need to replace javafx-sdk-21.0.4
and CircuitSim1.9.1.jar
with the correct path to the JavaFX SDK and the CircuitSim jar. Then, click
File -> Load to check our datapath.sim file.
The instructions we implemented are summarized below.
ADD: DR = SR1 + SR2;
TheADD
instruction obtains the first source operand from theSR1
register. The second source operand is obtained from theSR2
register. The second operand is added to the first source operand, and the result is stored inDR
.NAND: DR = ~(SR1 & SR2);
TheNAND
instruction performs a logicalNAND
on the source operands obtained fromSR1
andSR2
. The result is stored inDR
.ADDI: DR = SR1 + SEXT(immval20);
TheADDI
instruction obtains the first source operand from theSR1
register. The second source operand is obtained by sign-extending the immval20 field to 32 bits. The resulting operand is added to the first source operand, and the result is stored inDR
.LW: DR = MEM[BaseR + SEXT(offset20)];
An address is computed by sign-extending bits [19:0] to 32 bits and then adding this result to the contents of the register specified by bits [23:20]. The 32-bit word at this address is loaded intoDR
.SW: MEM[BaseR + SEXT(offset20)] = SR;
An address is computed by sign-extending bits [19:0] to 32 bits and then adding this result to the contents of the register specified by bits [23:20]. The 32-bit word obtained from registerSR
is then stored at this address.BEQ: if (SR1 == SR2) {PC = incrementedPC + SEXT(offset20)}
A branch is taken ifSR1
is equal toSR2
. If this is the case, thePC
will be set to the sum of the incrementedPC
(since we have already undergone fetch) and the sign-extended offset[19:0].JALR: RA = PC; PC = AT;
First, the incrementedPC
(address of the instruction + 1) is stored into registerRA
. Next, thePC
is loaded with the value of registerAT
, and the computer resumes execution at the newPC
.HALT
The machine is brought to a halt and executes no further instructions.BLT: if (SR1 < SR2) {PC = incrementedPC + SEXT(offset20)}
A branch is taken ifSR1
is less thanSR2
. If this is the case, thePC
will be set to the sum of the incrementedPC
(since we have already undergone fetch) and the sign-extended offset[19:0].LEA: DR = PC + SEXT(PCoffset20);
An address is computed by sign-extending bits [19:0] to 32 bits and adding this result to the incrementedPC
(address of instruction + 1). It then stores the computed address into registerDR
.BGT: if (SR1 > SR2) {PC = incrementedPC + SEXT(offset20)}
A branch is taken ifSR1
is greater thanSR2
. If this is the case, thePC
will be set to the sum of the incrementedPC
(since we have already undergone fetch) and the sign-extended offset[19:0].OR: DR = SR1 | SR2;
TheOR
instruction obtains the first source operand from theSR1
register. The second source operand is obtained from theSR2
register. Preform theOR
operation on the two operands, and the result is stored inDR
.XOR: DR = SR1 (XOR) SR2;
TheXOR
instruction obtains the first source operand from theSR1
register. The second source operand is obtained from theSR2
register. Preform theXOR
operation on the two operands, and the result is stored inDR
.EI: IE = 1;
The Interrupts EnabledIE
register inDatapath
is set to1
, enabling interrupts.DI: IE = 0;
The Interrupts EnabledIE
register inDatapath
is set to0
, disabling interrupts.RETI: PC = $k0; IE = 1;
ThePC
is restored to the return address stored in$k0
register inRegister
. The Interrupts EnabledIE
register inDatapath
is set to1
, enabling interrupts.IN: DAR = SEXT(addr20); DR = DeviceData; DAR = 0;
The value in addr20 is sign-extended to determine the 32-bit device address. This address is then loaded into the Device Address RegisterDAR
register inDatapath
. The processor then reads a single word value off the device data bus, and writes this value to theDR
register. TheDAR
is then reset to zero, ending the device bus cycle.
A simpler datapth without supporting interrupts can be find here that you can see the overall structure and use it to deduce behavior of each instruction.
We have two ALUs with 2-bit and 1-bit control signal respectively. The 2-bit
ALU
can perform 00: ADD
, 01: SUB
, 10: NAND
,
11: A+1
operations, control by signal func
. The 1-bit
ALU2
can perform 0: OR
, 1: XOR
operations, control
by signal IR[4]
, i.e., the fifth bit of instruction (refer to OR
and XOR
instruction details). As a notes, we use the term 'operation'
for ALU, and it's different from the term 'instruction' that we use for the ISA.
The control gate DrALU
in Datapath
will be set if
we want ALU
output and DrALU2
will be set if we want
ALU2
output.
The LC-2222a has 16 general-purpose registers:
Register Number | Name | Use | Callee Save? |
---|---|---|---|
0 | $zero | Always Zero | NA |
1 | $at | Assembler/Target Address | NA |
2 | $v0 | Return Value | No |
3 | $a0 | Argument 1 | No |
4 | $a1 | Argument 2 | No |
5 | $a2 | Argument 3 | No |
6 | $t0 | Temporary Variable | No |
7 | $t1 | Temporary Variable | No |
8 | $t2 | Temporary Variable | No |
9 | $s0 | Saved Register | Yes |
10 | $s1 | Saved Register | Yes |
11 | $s2 | Saved Register | Yes |
12 | $k0 | Reserved for OS and Traps | NA |
13 | $sp | Stack Pointer | No |
14 | $fp | Frame Pointer | Yes |
15 | $ra | Return Address | No |
- Register 0 is always read as zero. Any values written to it are discarded.
- Register 1 is used to hold the target address of a jump. It may also be used by pseudo-instructions generated by the assembler.
- Register 2 is where you should store any returned value from a subroutine call.
- Register 3-5 are used to store function/subroutine arguments.
- Register6-8 are designated for temporary variables. The caller must save these registers if they want these values to be retained.
- Register 9-11 are saved registers. The caller may assume that these registers are never tampered with by the subroutine. If the subroutine needs these registers, then it should place them on the stack and restore them before they jump back to the caller.
- Register 12 is reserved for handling interrupts. While it should be implemented, it otherwise will not have any special use on this assignment.
- Register 13 is the everchanging top of the stack; it keeps track of the top of the activation record for a subroutine.
- Register 14 is the anchor point of the activation frame. It is used to point to the first address on the activation record for the currently executing process.
- Register 15 is used to store the address a subroutine should return to when it is finished executing.
At implementation level, Registers
has a 32-bit
input data Din
, a 32-bit output data Dout
, and three control signals:
WrREG
, regno
, Clock
. Since we only have 16 registers, we use a 4-bit
regno
to select which register to read or write.
- Write Register. When write data into a register, set
WrREG
to1
andregno
to the register that we want to write to. Note that in our design we use a decode with 5-bit select signal where the first 4 bits are theregno
and the last bit is not ofWrREG
and . WhenWrREG
is0
, Decoder will always select[16-19]
, i.e., no register will be enabled to write. WhenWrREG
is1
, Decoder will select by theregno
value. - Read Register. We use two levels of Mux with 2-bit select signal two
select which register to read. The first level Mux selects using
regno[0-1]
and the second level Mux selects usingregno[2-3]
. The control gateDrREG
inDatapath
will be used to control whether to output the register data to bus or not.
The CmpLogic
is responsible for performing the
comparison logic associated with the BLT
, BGT
, and BEQ
instructions.
When executing BLT
, BGT
, and BEQ
, A - B will be computed using 01: SUB
operation of the 2-bit ALU
. While this result of the ALU
is being driven on the bus, the comparison logic will read the result and output
a single true
or false
bit for either the condition A > B
, A < B
, or
A == B
, depending on the instruction being executed that control by the signal
func
. The control gate LdCmp
in Datapath
will be
set so that bool output of the comparison logic will be temporarily stored in
the register CmpReg
in Datapath
. This bool
register will be used in next cycle for CC
ROM in
Microcontroller
to decide whether to jump or
not.
This microcontrol unit can be implemented in different ways, such as using combinational logic and flip-flops or a single ROM to hardwire the signals. However, using a single ROM can be highly inefficient, as it would waste a significant amount of space. This is because most microstates do not depend on the opcode or conditional tests to determine which signals to assert. For instance, if the condition line is an input for the address, every microstate would require an address for both condition = 0 and condition = 1, even though this only matters for one specific microstate.
To address this inefficiency, a four-ROM microcontroller can be utilized, which also handles interrupts. In this design, four ROMs are used:
- Main
MAIN
ROM: Outputs control signals. - Sequencer
SEQ
ROM: Helps determine which microstate to transition to at the end of theFETCH
state. - Condition
CC
ROM: Assists in deciding whether to skip an instruction duringBLT
(Branch Less Than) operations. - Interrupt
INT
ROM: Determines whether the next state is fetch2 or the start of theINT
macrostate.
In this arrangement, the next state can originate from four different sources:
part of the output from the previous state (MAIN
ROM), SEQ
ROM, CC
ROM, or
INT
ROM. A multiplexer (MUX) controls which of these sources is passed through
to the state register. If the "next state" field from the previous state
dictates where to go, neither the OPTest
nor ChkCmp
signals are asserted.
If the Opcode
from the instruction register IR
determines the next state
(such as at the end of the FETCH
state), the OPTest
signal is asserted.
If the comparison circuitry decides the next state (as in a BLT
instruction),
the ChkCmp
signal is asserted. When dealing with an interrupt (entering the
INT
macrostate), both the OPTest
and ChkCmp
signals are asserted.
A simpler microcontrol unit can be find here that you can see the overall structure and use it to deduce behavior of each instruction.
I would like to thank Hanyun (Hannah) Huang for her invaluable help in understanding the many details of this project. Her support and clarity were crucial in guiding me through the process.
- Project 1 - LC-2222 Datapath, CS2200 Introduction to Systems and Networking by Prof. Daniel Forsyth, Georgia Institute of Technology.
- Project 2 - Interrupts, CS2200 Introduction to Systems and Networking by Prof. Daniel Forsyth, Georgia Institute of Technology.
- Computer Systems: An Integrated Approach to Architecture and Operating Systems by Umakishore Ramachandran and William D. Leahy.