$29
The purpose of the checkpoints is to help you stay on track to com-plete the project in time. There will be some credit associated with submitting the checkpoints on time. If the TAs notice glaring issues with your design, they will point these out to you, but otherwise individual feedback or comments will not be provided. of course, if you are unsure about your design, we encourage you to ask the TAs for feedback during lab sessions.
Final Demonstration Required.
Introduction
This lab is a large-scale project intended to bring together all of your knowl-edge on RTL design, datapath construction, and modular testing. This project is signi cantly more involved than previous labs, so it’s best to get started early. Note that there are weekly due dates for portions of the lab, although the only demonstration is for the nal product.
In this lab, you will design and build the Princeton University Computer (PUnC), a 16-bit processor with a simple but versatile instruction set. This processor is Turing complete and is a full- edged stored program computer, so you will be able to compile code and run it on your design!
As always, you will test your circuit thoroughly via simulation.
1
2
To begin the lab, download and unzip punc.zip from Blackboard, and follow the instructions in this document. All of the source code for this lab, including testbenches, is stored in the /punc folder. We have also provided the Verilog codes for a Six-instruction Programmable Processor (SIPP), which is explained in chapter 8 of your textbook. It may be helpful to start by looking at how SIPP is implemented in Verilog. You may download the corresponding les (SIPP.zip) from Blackboard.
Introduction to PUnC
PUnC is a 16-bit stored-program computer. In PUnC, all data and instruc-tions are aligned 16-bit words, and both programs and data reside in the same memory unit. PUnC has several required hardware modules, which are described in detail in the following section.
Hardware Modules
Memory
PUnC memory addresses are 16 bits long, and each unique address points to a full 16-bit data word. For example, the address 0x0000 points to the rst 16-bit entry in memory, while the address 0x0001 points to the next 16-bit entry in memory.
Ideally, PUnC’s memory would contain 216 16-bit entries, but simulation with that many entries takes a long time. For e ciency, our version only implements the rst 128 entries, leaving the rest disconnected. If you nd that you need more memory elements, please ask a lab TA for help expanding the memory.
Memory.v contains an implementation of PUnC’s memory module. It has already been wired into your datapath for use in simulation.
Here’s an overview of the Memory module’s ports:
3
Port
I/O
Width
Use
clk
I
1
Clock
rst
I
1
Synchronous Reset
r
addr
0
I
16
Async Read Address
r
addr
1
I
16
External Debug Read Address
w
addr
I
16
Synchronous Write Address
w
data
I
16
Synchronous Write Data
w
en
I
1
Synchronous Write Enable
r
data
0
O
16
Async Read Data
r
data
1
O
16
External Debug Read Data
This memory has asynchronous reads and synchronous writes. As soon as an address appears on one of the read address ports, the corresponding data will appear on the matching read data port. On the other hand, the data on w data will be written to memory on positive clock edges and only whenever w en is high. Resetting the memory will restore whatever program and data was initially present in the memory.
The second read port on the memory is used to allow external inspection of memory. It is already wired up for you, and it will be useful for debugging in simulations.
Register File
PUnC has eight general-purpose 16-bit registers, addressed from 0x0 to 0x7. These registers are used as scratch space for arithmetic operations and are used in nearly every instruction. None of the registers have a special meaning, but register 7 (0x7) is automatically used to save the program counter during Jump to Subroutine calls and is used to load a new program counter during the Return instruction.
RegisterFile.v contains an implementation of PUnC’s register le. It has already been wired into your datapath for use in simulation.
Here’s an overview of the register le’s ports:
4
Port
I/O
Width
Use
clk
I
1
Clock
rst
I
1
Synchronous Reset
r
addr
0
I
3
Async Read Address
r
addr
1
I
3
Async Read Address
r
addr
2
I
3
External Debug Read Address
w
addr
I
3
Synchronous Write Address
w
data
I
16
Synchronous Write Data
w
en
I
1
Synchronous Write Enable
r
data
0
O
16
Async Read Data
r
data
1
O
16
Async Read Data
r
data
2
O
16
External Debug Read Data
Much like the memory, this register le has asynchronous reads and syn-chronous writes. Resetting the register le sets all registers to zero. The rst two read ports on the register le are for you to use when constructing your processor { the third is used for external inspection of the register le. It is already wired up for you, and it will be useful for debugging in simulations.
Condition Codes
PUnC also has three 1-bit condition code registers: N (Negative), Z (Zero), and P (Positive). These condition codes are set by arithmetic and load oper-ations based on the value of the data being saved to the register le. PUnC treats that data as a two’s complement number, and sets N to 1 if the number is negative, Z to 1 if the number is exactly zero, and P to 1 if the number is positive. All codes that are not set to 1 are set to zero, so only one of N, Z, and P can be 1 at any given time.
Resetting the processor should set all condition code registers to zero.
Program Counter
In PUnC, all programs are stored in memory as a series of 16-bit binary instructions. To keep track of which instruction we should currently be ex-ecuting, PUnC has a 16-bit program counter (PC) register that is used to address memory.
In ordinary operation, the PC increments by 1 immediately after fetching and decoding an instruction, so the program counter actually points to the next instruction to be fetched while the current instruction executes. The
5
program counter can also be modi ed by control ow instructions, which will be described in detail later.
Note that the program counter’s value is tied to a signal (pc debug data) that goes all the way to the top of the PUnC hierarchy in the given source code. This signal enables external inspection of the program counter, so keep it in place for simulation. Resetting the processor should set the PC to zero.
Instruction Register
Finally, while not architecturally required, it will be very useful to have one 16-bit instruction register (IR) that stores the currently-executing instruction. Saving fetched instructions in the IR will make it much easier to set control signals during execution.
Other
There may be other hardware modules you need in your datapath. Feel free to include any as you see t.
Ports
PUnC has only seven external ports. They are listed in the table below:
Port
I/O
Width
Use
clk
I
1
Clock
rst
I
1
Synchronous Reset
mem
debug
addr
I
16
Memory Inspection Address
rf
debug
addr
I
16
RegFile Inspection Address
mem
debug
data
O
16
Memory Inspection Data
rf
debug
data
O
16
RegFile Inspection Address
pc
debug
data
O
16
Current Program Counter
The memory and register le inspection addresses can be set to view the contents of those modules, and the PC value is always available for viewing on the pc debug data line.
The LC3 Instruction Set
PUnC uses the LC3 instruction set, an educational instruction-set architec-ture (ISA) that was created by Professors Yale N. Patt and Sanjay J. Patel.
6
The assignment les contain LC3ISA.pdf, a full speci cation of the LC3 ar-chitecture.
For this project, we will only be implementing a subset of the LC3 ISA. Speci cally, you will implement the instructions shown in Figure 6.1. Each instruction is 16 bits long and begins with an opcode, which is a 4-bit code that indicates what type of instruction it is. Some instructions share an opcode { speci cally, there are two types of ADD, two types of AND, and two types of JSR (and RET is just a special version of the JMP instruction). In the event that two instructions share an opcode, some other bit is present that distinguishes the two instructions { for example, the two ADD instructions are di erentiated by the bit in position 5.
In addition to the opcode, there are several other pieces of information in each instruction. Here’s a list of each named eld and what it means:
DR { The 3-bit address of the destination register. The value generated by the instruction (whether through an arithmetic operation or through
• a load from memory) is written to regfile[DR].
SR/SR1/SR2/BaseR { The 3-bit address of a source register. Source registers are used as operands for arithmetic operations, targets for
• branching instructions, or sources for stores to memory.
imm5/o set6/PCo set9/PCo set11 { Immediate values. These elds are bit sequences that are stored as part of the instruction and denote a constant value. They are used in arithmetic operations, such as calculating memory addresses for the PC or for loads and stores. Be-fore they are used, they are sign-extended to 16 bits. This means that the highest-order bit in the immediate is copied to pad the immediate to 16 bits. For example, an immediate of 5’b10001 would be extended to 16’b1111111111110001, while an immediate of 5’b00011 would be-come 16’b0000000000000011.
All of the instructions listed are described in detail in LC3ISA.pdf with the exception of HALT. HALT should simply stop the processor { the program counter will be left pointing to the instruction after HALT, and the only way to get the processor to continue executing is to reset it.
Refer to LC3ISA.pdf (pages 524-540) as you implement your PUnC design. This document can be found on Blackboard. There are a couple of key things to be aware of as you read.
First, all arithmetic operations that involve the PC are performed with the incremented PC. For example, when calculating the target address for the
7
BR instruction, we add the sign-extended o set to the PC. This PC should be pointing to the instruction directly after the BR instruction during the com-putation. This should actually require almost no work to implement, as the PC should be incremented immediately after decoding but before executing an instruction.
Secondly, all instructions that are marked with a \+" sign in Figure 6.1 set the condition code registers based on whatever value is being stored into DR. No other instructions should modify the condition codes.
Finally, do not worry about any topics covered in LC3ISA.pdf that do not involve the instructions shown in Figure 6.1. We’re just implementing a simple subset of LC3’s functionality, so don’t worry about interrupts or LC3’s other available instructions.
8
Figure 6.1: LC3 Instruction Set
9
Instruction Stages
PUnC works in a continuous cycle of three main stages, each of which is described in detail below. PUnC is an unpipelined processor, which means that only one instruction is in ight at any time { an instruction must fully complete its execution before the next instruction can be fetched. Here are the three main phases in the life of an instruction:
1. Fetch { An instruction is loaded from memory into the IR (IR <= Mem[PC]). This phase takes one cycle.
2. Decode { The instruction is now in the IR. In this stage, we simply increment the PC. On a pipelined processor, we would also prepare the control signals for executing the instruction, but since PUnC is un-pipelined, we’ll actually do that in the execute phase. This phase takes one cycle.
3. Execute { The control unit sets control signals to manipulate the data-path into executing the decoded instruction. This could involve reading from memory, executing an arithmetic operation, or even modifying the PC. This phase may take as many cycles as you need, although most instructions can be executed in a single cycle. Some instructions, such as LDI, may require at least two cycles.
As PUnC processes the program stored in memory, it will cycle through these three stages for every executed instruction. The Fetch-Decode-Execute paradigm will serve as an essential starting point for your PUnC design.
10
Week 1: Datapath Design
Checkpoint 1 Due Date: Nov. 18th, 2020, 11:59 pm
The rst step in tackling PUnC is creating a datapath. This datapath should contain all of the hardware modules listed in the description of PUnC, including:
A Memory Unit
A Register File
A Program Counter
Condition Codes
• An Instruction Register
Any other items you need, like ALU’s, multiplexers, and extra registers.
Designing Your Datapath
Sitting down to design your datapath may seem like a daunting task, but it can be broken down into a simple series of tasks:
1. Draw your memory unit, register le, program counter, Condition Codes, and instruction register. Attach write-enable signals to all the registers, but leave the other connections blank.
2. Pick an unconnected port on one of your datapath modules (remember that the PC, condition codes, and IR all have an implicit input port that holds a new value to be loaded). Then, iterate through all of the LC3 instructions (in addition to the universal fetch and decode stages) and note the possible values that port might need to take on (for input ports) or the possible places the port’s value might be used (for output ports). For example, the register le’s w data port can take on values from memory’s r data 0 port, from the PC, or from the result of an arithmetic operation.
3. Once you’ve compiled a list of the possible inputs to each port and the places the outputs must be routed to, add hardware modules as needed to enable the requisite behavior. For example, I would add a multiplexer that selects between the three possible input values to the register le’s w data port.
11
4. If you nd that you need additional local registers, add them.
5. Once you’ve iterated through all ports and all instructions and are sure you have the elements required to support all possible operations, your datapath is ready to go!
Drawing Your Datapath
Draw your datapath as you see t, but please follow these guidelines:
1. Feel free to use high-level modules like adders, comparators, multi-function registers, sign-extenders, and the like. We do not want a gate-level description of all of these components { just make assumptions about the control signals each module needs and mark your modules so that it’s clear what they do. When in doubt, keep it simple!
2. Clearly label the bit-width of every wire in your design.
3. Clearly indicate the inputs and the outputs of your datapath, and pro-vide names for each. Signal names should be simple but descriptive enough to explain what they do. For example, a control signal that resets a counting register to zero could be labelled with count rst.
Once you have a datapath design that you believe is complete, save a copy of it. If you drew it by hand, take a picture or scan it in.
Control Signal Table
As a nal step, you will set up a table that has a column for every possible phase in your processor { speci cally, one column for the fetch phase, one column for the decode phase, and a column for every possible execute phase. You should have an execute phase column for every possible instruction type. If an instruction’s execute phase takes multiple cycles, add an execute column for each cycle.
The rows of this table should be the datapath control signals you speci ed in your diagram. For each phase, ll in the slots for the control signals that matter in that phase. Leaving a cell blank for a phase indicates that the corresponding control signal is zero (for write-enable signals) or that we don’t care what its value is for that phase.
For example, here’s part of an example output table:
12
CTRL SIG
Fetch
Decode
ADDR
...
ir
w
en
1
...
status
w
en
1
...
...
...
...
...
...
Feel free to use named values for multiplexer select signals instead of simple numerical values. For example, if I’m designing a control signal for a multi-plexer that selects between the possible inputs to the register le’s w data port, then I might name the possible values of that signal MEM DATA, PC DATA, or ALU DATA (instead of just 0, 1, or 2). These names for the multiplexer select signal paint a clearer picture of exactly what each value does to the datapath, and you can incorporate them using de ne macros in your code for readability.
Demonstration
Demonstration for this checkpoint is not required. However, we encourage you to visit one of the lab help sessions and show your design. If the TA notices a glaring error with your design, they will point it out to you. But otherwise, individual feedback or comments will not be provided as it gets very hard to dive into everyone’s design.
Writeup and Submission
Save your control signal table in an Excel le. Make sure to enable the option \All Borders" in Excel. Then save your le as a PDF.
Also, save your datapath design as a single PDF le { if you hand-drew it, take a picture of it and save it as a PDF using a document editor of your choice.
Finally, merge the two PDF les you have created (your datapath and the control signal table) and name it ELE206 PUnC c1 netid.pdf. Feel free to use any software of your choice to merge the two PDF les together. There are many free online tools available, e.g., https://combinepdf.com/.
Congratulations! You’ve nished work for week one. It’s a good idea to start the next part early, as it may take longer to complete!
13
Week 2: Datapath and Controller Implementation
Checkpoint 2 Due Date: Nov. 25th, 2020, 11:59 pm
Now that you have a design hammered out for your datapath and the con-trol signals necessary for each instruction, it’s time to implement the datapath and controller for PUnC.
Datapath Implementation
Let’s begin with the datapath { open up PUnCDatapath.v, which has been started already for you. You’ll notice that the register le and memory have already been instantiated. Feel free to connect wires to their ports as needed, but don’t modify the signals that are already connected to their debug ports.
Several datapath module ports have already been declared as well. Do not modify these, as they’ll be needed for debugging later. However, you may (and should) add your own ports to the datapath module declaration.
Finally, note that the PC and IR are already instantiated for your con-venience. If you modify their names, be sure to ensure that the PC remains connected to the pc debug data port via an assign statement.
Write your full datapath using synthesizable Verilog. If you wish to name the possible values for multiplexer select signals, use de ne statements and place them in De nes.v so that they can be used in the controller as well. For example, I might have this set of de nitions for the multiplexer select that controls which value is written to the register le:
1
define
RF_W_DATA_SEL_ALU
2
b00
2
define
RF_W_DATA_SEL_PC
2
b01
3
`define
RF_W_DATA_SEL_MEM
2'b10
Controller Implementation
Once your datapath is completed, you should implement your controller.
In this case, your nite state machine should be very simple, proceeding from Fetch to Decode to a variable number of Execute stages before cycling back to the Fetch stage. The complex part is setting the datapath control signals correctly for each phase based on the instruction being executed. Your signal table from week 1 should be helpful in this regard.
The code for the controller should be written in PUnCControl.v. Once again, some code has been supplied for you to help you get started. Feel free
14
to modify it as you wish, adding ports to produce the control signals needed by the datapath and to pull in the data predicates generated by the datapath.
Connection and Testing
Once both your datapath and controller have been completed, it’s time to test them out.
Open PUnC.v, the top-level module for PUnC. This module simply serves to tie the datapath and controller together, and the provided le already has instantiated them.
Add your ports to the datapath and controller instances, wiring them together. Do not modify the port signals provided by default in PUnC.v { they will be used for debugging.
Once you’ve connected the datapath and controller, it’s time to test out your design. We’ve included a full- edged suite that tests individual instruc-tions one-by-one. Additionally, the suite includes a program that uses a variety of instructions to calculate the greatest common denominator of two values (see next week’s notes for more information on this test). Our advice is to get the instructions working in the order that they appear in the testing output, as there are some dependencies between test cases. For example, almost all of the test cases depend on the ADDI instruction working, as it is used to load values into the register le. Finally, note that your HALT instruction must work for the testbench to function. The tests run a simple program to completion, and if the program doesn’t complete by halting, then neither will the simulation.
Compile this testbench with the following command:
1 iverilog -g2005 -Wall -Wno-timescale -DSIM=1 -o PUnCTest PUnC.t.v
Fix any compiler errors or warnings, and then execute the suite with the following command:
1 vvp PUnCTest
Ensure that there are no failures. You’ll see some warnings that look like this when you compile the datapath tests:
1 VCD warning: array word PUnCTest.punc.dpath.mem.mem[0] will conflict with an
escaped identifier.
Just ignore those type of warnings throughout the rest of the lab { they’re caused by dumping out array contents (in this case, your memory and register le contents) to a VCD waveform le.
15
If you need waveforms for debugging, they will be dumped out as PUnCTest.vcd.
You can open them with this command:
1 gtkwave PUnCTATest.vcd
A Note About the Testbench
During your debugging, it may be useful to understand what the testbench is doing and how it works. A test case looks roughly like this:
1 START_TEST("addi");
2 WAIT_PC_FREEZE;
3 `ASSERT_REG_EQ(0, 16'd3);
The rst macro, START TEST, takes as an argument the name of a memory image le from the /images folder. For example, the provided code will use /images/addi.vmh. These memory image les are simply a list of 128 sixteen-bit values that are speci ed in hexadecimal. They may contain comments, and each value must be on its own line. The START TEST macro loads these les into memory, with the rst value in the image le corresponding to the rst value in memory (mem[0]) and so on. Then, it resets the processor.
Next, the WAIT PC FREEZE macro waits for the PC to remain unchanged for at least 10 clock cycles. Once this happens, we know that the processor has hit a HALT instruction. Every memory image le included in our test cases ends with a HALT instruction to terminate the program.
Finally, once the program runs to completion, we examine memory and the register le to ensure that the program ran successfully. The ASSERT REG EQ and ASSERT MEM EQ macros take two arguments { an address and a value { and ensure that the corresponding memory element or register holds the given value at the provided address.
All of our memory images are commented with assembly code, which is the low-level language that maps one-to-one to binary machine instructions. The LC3ISA.pdf document provides assembly-code examples for each instruction
{ read through these to understand what each assembly instruction means. Then, you can use the assembly comments to gure out what each program is doing if you nd yourself mysteriously failing a test case.
Demonstration
Demonstration for this checkpoint is not required. However, we encourage you to visit one of the lab help sessions and show your design. If the TA notices a glaring error with your design, they will point it out to you. But otherwise,
16
individual feedback or comments will not be provided as it gets very hard to dive into everyone’s design.
Write-up and Submission
For this week’s writeup, you’ll simply be submitting an updated version of your datapath and signal table, plus your current code.
Similar to the procedure for Week 1, merge your new PDF les for your signal table and the datapath and name it ELE206 PUnC c2 netid.pdf, with netid replaced with your Princeton NetID.
Additionally, you should submit your code as well. It’s okay if your code is not yet fully debugged { we just expect some progress on it, but you won’t lose points if it doesn’t fully work.
Create a directory named ELE206 PUnC c2 netid, with netid replaced with your Princeton NetID. Then place the following les in that directory:
ELE206 PUnC c2 netid.pdf
De nes.v
PUnCControl.v
• PUnCDatapath.v
PUnC.v
Finally, create a .zip of that directory (i.e., ELE206 PUnC c2 netid.zip)
and submit it to Gradescope.
Congratulations! You’ve nished the work for week 2. It’s a good idea to start the next part early.
17
Week 3: Creating Your Own Program
Final Due Date: Dec. 8th, 2020 at 5 pm
Now that your code passes our basic functional tests, you will create your own test for PUnC. For this part, you will create your own LC3 program that uses at least ten of the provided instructions to perform some sort of interesting computation.
Our Example Program
For example, our provided program (saved in /images/gcd.vmh) computes the greatest common denominator of two integers.
In C, the code for this program would look something like this:
1
int main(void) {
2
int
a
=
15;
3
int
b
=
13;
4
int
gcd
= 0;
5
6
while
(a != b) {
7
if (a
> b) {
8
a
=
a - b;
9
}
else {
10
b
=
b - a;
11
}
12
}
13
14
gcd
=
a;
15
}
Once the program runs to completion, the GCD of A and B will be stored in both a and b, as well as in gcd.
We can translate this to LC3 assembly fairly easily, which produces this program:
1
// INITIAL BLOCK:
2
/*0:*/
LD R0, #23
// Load A into R0
from
mem[24]
3
/*1:*/
LD R1, #23
// Load B into R1
from
mem[25]
4
/*2:*/
NOT R2, R1
// Load -B into R2
5
/*3:*/
ADD R2, R2, #1
6
/*4:*/
NOT R3, R0
// Load -A into R3
7
/*5:*/
ADD R3, R3, #1
8
/*6:*/
ADD R4, R0, R2
// Load A - B into R4
9
/*7:*/
BRz #13
// If A - B == 0,
goto
A_EQUAL_TO_B
10
/*8:*/
BRn #6
//IFA-B<0,
goto
A_LESS_THAN_B
18
11
12
// A_GREATER_THAN_B:
13
/*9: */
ADD R0, R0, R2
// Load A - B into R0 (A = A - B)
14
/*10:*/
NOT R3, R0
// Load -A into R3
15
/*11:*/
ADD R3, R3, #1
16
/*12:*/
ADD R4, R0, R2
// Load A - B into R4
17
/*13:*/
BRp #-5
// If A - B > 0, goto A_GREATER_THAN_B
18
/*14:*/
BRz #6
// If A - B == 0, goto A_EQUAL_TO_B
19
20
// A_LESS_THAN_B:
21
/*15:*/
ADD R1, R1, R3
// Store B - A into R1 (B = B - A)
22
/*16:*/
NOT R2, R1
// Store -B into R2
23
/*17:*/
ADD R2, R2, #1
24
/*18:*/
ADD R5, R1, R3
// Store B - A into R5
25
/*19:*/
BRn #-11
// If B - A < 0, goto A_GREATER_THAN_B
26
/*20:*/
BRp #-6
// If B - A > 0, goto A_LESS_THAN_B
27
28
// A_EQUAL_TO_B:
29
/*21:*/
ST R0, #1
// Store A into mem[23] (GCD)
30
/*22:*/
HALT
// HALT -- GCD of A and B will be in R0 and R1
31
32
// DATA:
33
/*23:*/
0000
// GCD
34
/*24:*/
000C
// A
35
/*25:*/
000F
// B
Note that the numbers at the beginning of each line are the memory ad-dresses for each instruction and are just there for your convenience. They are not part of the assembly code, which is why they’re commented out.
The initial numbers that we wish to take the GCD of are stored in memory at addresses 24 and 25 respectively. In this case, A is 12 and B is 15, and their GCD is 3. This program generally keeps the current value of A in R0, B in R1, -A in R2, and -B in R3. Once the HALT instruction has been hit, the GCD of A and B is stored in R0, in R1, and in memory at address 23.
Note that data can be written in assembly simply as a 16-bit hexadecimal number instead of an instruction. Hard-coding initial values in memory is a useful way to make a program easy to edit and test for di erent datasets.
This assembly code produces the following memory image le:
1
2017
// LD R0, #23
2
2217
// LD R1, #23
3
947F
// NOT R2, R1
4
14A1
// ADD R2, R2, #1
5
963F
// NOT R3, R0
6
16E1
// ADD R3, R3, #1
7
1802
// ADD R4, R0, R2
8
040D
// BRz #13
19
9
0806
// BRn #6
10
1002
// ADD R0, R0, R2
11
963F
// NOT R3, R0
12
16E1
// ADD R3, R3, #1
13
1802
// ADD R4, R0, R2
14
03FB
// BRp #-5
15
0406
// BRz #6
16
1243
// ADD R1, R1, R3
17
947F
// NOT R2, R1
18
14A1
// ADD R2, R2, #1
19
1A43
// ADD R5, R1, R3
20
09F5
// BRn #-11
21
03FA
// BRp #-6
22
3001
// ST R0, #1
23
F000
// HALT
24
0000
// 0000
25
000C
// 000C
26
000F
// 000F
27
0000
// Zeros from here on
28
//.....
This le is padded with zeros until memory address 127 is reached. The memory image for the GCD program has been saved as images/gcd.vmh, and it was included in the TA testbench from last week’s section.
Using the Assembler
Write your own assembly program using ELE 206’s custom LC3 assembler, which can be found on Blackboard. In the left-hand window, simply write your LC3 assembly code (without any comments). Once you’re done, click on the \To Mem Image" button to produce a commented memory image le. Type a lename into the \Output Filename" box and click \Download Result" to download the memory image le. For consistency, save the le with a .vmh extension.
Once you’re sure your program works as you wish, download your assembly code using the \Download ASM" button. For consistency, save your le with an .asm extension.
Remember that your program must use ten unique instructions from the provided set, and it must produce some sort of meaningful result. It doesn’t need to be anything fancy, but don’t just place random instructions!
20
Running Your Program
Move your .vmh le into /images, and then open up PUnC.t.v. Find the end of the provided tests (the last one is our GCD program), and add your own using the provided macros. These macros are described in detail in last week’s notes on the TA testbench, but speci cally, you can run your test by inserting the following sequence of commands:
1 START_TEST("mytest");
2 `WAIT_PC_FREEZE;
This code will load your program into memory and then run it to com-pletion. Ensure that your program produces the correct results by checking the end state of memory and the register le. Use the ASSERT REG EQ and ASSERT MEM EQ macros at your convenience to do this.
Once your program works correctly, save your assembly code { you’ll be required to turn in a commented copy of your program’s assembly code as part of your nal write-up.
Write-Up
For your nal write-up, create a PDF le that includes your revised datapath drawing, an annotated, commented copy of your program’s assembly code, and an explanation of what your assembly program does. Additionally, please leave a bit of feedback regarding this lab. How much time did it take, how di cult was it, did you get stuck anywhere? There are no wrong answers here { we’ll be using the feedback to adjust this lab for the future.
In addition, save your revised Excel spreadsheet that contains your control signal table and prepare it for nal submission. Make sure you have enabled the option \Border All" in Excel. Then save your Excel spreadsheet as a PDF le.
Finally, similar to the procedure for Week 1 and Week 2, merge the two PDF les togother and name it ELE206 PUnC netid.pdf, with netid replaced with your Princeton NetID.
Submission
Delete PUnCTest and any .vcd waveform les from the /punc directory. You can do this via your le browser or with one of the following commands.
On OS X and Linux:
1 rm -f *.vcd PUnCTest
21
On Windows:
1 del *.vcd PUnCTest
Move your nal PDF into the /punc directory, and then create a .zip archive of that directory.
On OS X and Linux, simply change directories in your terminal to the directory that contains the /punc directory and then execute this command:
1 zip -r ELE206_PUnC_netid.zip ./punc
On Windows, simply open up File Explorer and locate the /punc folder. From Command Prompt, you can open your current directory’s parent in File Explorer by issuing this command:
1 explorer.exe ..
Right-click on the /punc folder and select \Send to", then \Compressed (zipped) folder". This will create a .zip le { rename it as ELE206 PUnC netid.zip, with netid replaced with your Princeton NetID.
For reference, here is a checklist of les that should be in your .zip:
De nes.v
Memory.v
PUnCDatapath.v
PUnCControl.v
PUnC.v
PUnC.t.v
• /images, with your program .vmh le inside. ELE206 PUnC netid.pdf
Finally, submit the following to Gradescope:
ELE206 PUnC netid.zip