Homework 4: Introducing ULNAv2-A
This homework is due on Thursday, April 23, at 11:59:59 PM
(Eastern daylight time). You must use submit to
turn in your homework like so:
submit cs411_jtang hw4 hw4.c multu16.S hw4.circ
The grader will use the supplied Makefile to compile your
work. In addition, each submitted source code file must have a file
header comment, as described on the coding
conventions page. For the Logisim file, place your name and
assignment number in a text label on the main circuit.
This assignment builds upon the logic components you created in the third homework, and thus you must have completed that homework before attempting this assignment. Furthermore, you must have a working unsigned multiplication algorithm written in assembly.
In this assignment, you will implement a disassembler, in C, for the ULNAv2-A instruction set. You will then use that disassembler to partially decode a ULNAv2-A program, displaying control signals that would be generated for each instruction. Finally, you will add a decoder into your Logisim file to decode those control signals.
Part 0.0: ULNAv2-A Instruction Set
ULNAv2-A (UMBC's Limited Nifty Architecture, version 2 bugfix A) is the 16-bit RISC architecture that you will implement for this class's final project. In ULNAv2-A, all instructions are 16 bits and all data registers hold 16 bits. The data format is big-endian.
There are 8 general purpose registers, each 16-bit wide, referred to
as R0
through R7
. There is also a 16-bit
program counter (PC). All registers are initialized to 0 after
reset.
In ULNAv2-A, there are two memory systems: Instruction Memory and Data Memory. Both memories have 16-bit addresses and 16-bit data widths. The data memory is initialized at zero at processor startup and is writable, while instruction memory is preloaded with the program to execute and is considered read-only.
All instructions are 16 bits, where the top 5 bits (towards MSB) give the instruction number. The top 2 bits (bits 15 and 14) specify the instruction class, the next three bits are instruction subtype, and remaining bits are operands:
Instruction Class | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
A | 0 | 0 | subtype | Reg W | Reg A | Reg B | X | X | ||||||||
B | 0 | 1 | subtype | Reg W | Reg A | Imm 5 | ||||||||||
C | 1 | 0 | subtype | Reg W | Imm 8 | |||||||||||
D | 1 | 1 | subtype | Imm 11 |
There are 29 instructions defined in ULNAv2-A. Instructions numbers 6, 17, and 19 are undefined. Use the following links to obtain the full instruction set:
In ULNAv2-A RTN, the notation R[n]
means general
purpose register n. MemWord[n]
means the
16-bit value within data memory at address n. All
additions and subtractions treat operands as two's complement signed
integer values. When executed, only some of these instructions
update the ALU's condition codes Z, C, N,
and V, as specified by the column Updates Cond
Codes; these instructions' names also end with a dot.
Here are further details for some of the instructions:
-
and., andi., or., ori.
- These instructions update the condition codes Z and N based upon the result of the logic operation. The condition codes C and V are set to Don't Cares; Z and N are based upon the result of the operation.
-
ash
- This normally performs an arithmetic shift right, by a number of bit positions equal to the immediate, towards the LSB. If the immediate is negative, then shift left instead.
-
br
-
The
B
andW
register values are Don't Cares. -
brl
-
This "branch to register and link" first saves the
program counter to a register, before jumping to the address
within a register.
W
andA
can refer to the same register, in which case the PC is first saved prior to the jump. -
cmp., cmpi.
- These instruction do not modify any registers nor data memory; they simply set the ALU's condition codes.
-
halt
-
This instruction explicitly sets the PC to itself, preventing
the program from executing further. The
imm11
value is a Don't Care. -
lsh
- This normally performs a logical shift right, by a number of bit positions equal to the immediate, towards the LSB. If the immediate is negative, then shift left instead.
-
movis
- This replaces the upper eight bits of a register with the 8-bit immediate value. (This instruction moves an immediate that has been shifted to the left by eight bit positions.)
-
rot
- This normally rolls a register's value to the right, by a number of bit positions equal to the immediate, towards the LSB. If the immediate is negative, then rotate left instead.
Study ULNAv2-A carefully. Post on the Blackboard forum questions you have about this instruction set.
Part 0.1: ULNAv2-A Assembler and Emulator
Your friendly instruction has written several useful ULNAv2-A tools. Create in your hame directory a subdirectory named ulnav2-a, add then fetch the following files via wget:
- http://www.csee.umbc.edu/~jtang/cs411.s20/homework/ulnav2-a/ulnav2a-as.c
- Assembler for ULNAv2-A.
- http://www.csee.umbc.edu/~jtang/cs411.s20/homework/ulnav2-a/ulnav2a-as.h
- Common header file used by the assembler.
- http://www.csee.umbc.edu/~jtang/cs411.s20/homework/ulnav2-a/ulnav2a-lexer.l
- Flex file that "tokenizes" an input stream.
- http://www.csee.umbc.edu/~jtang/cs411.s20/homework/ulnav2-a/ulnav2a-parser.y
- Bison file that parses tokens into an "abstract syntax tree".
- http://www.csee.umbc.edu/~jtang/cs411.s20/homework/ulnav2-a/ulnav2a-emu.cpp
- Software emulator for the ULNAv2-A instruction set, controllable using a GDB-like interface.
- http://www.csee.umbc.edu/~jtang/cs411.s20/homework/ulnav2-a/Makefile
- Builds the above tools, by simply running make. Also included is a clean target to remove all built objects.
In addition, you need to install the flex and bison utilities:
sudo apt-get install flex bison libreadline-dev
After you have downloaded all of the files, run make. You will use the resulting tools ulnav2a-as and ulnav2a-emu for both this assignment and the final project. You may find these files interesting to read through, if you are interested in compiler theory.
Part 1: ULNAv2-A Disassembler
Create another directory for this assignment. Download the following additional files via wget:
- http://www.csee.umbc.edu/~jtang/cs411.s20/homework/hw4/hw4.c
- Skeleton C code for this assignment.
- http://www.csee.umbc.edu/~jtang/cs411.s20/homework/hw4/multu16.S
- Skeleton assembly code for this assignment.
- http://www.csee.umbc.edu/~jtang/cs411.s20/homework/hw4/adder.S
- Demonstration ULNAv2-A program, that adds two values and stores the result into main memory. Do not submit this file with your work.
- http://www.csee.umbc.edu/~jtang/cs411.s20/homework/hw4/bsort.S
- Another demonstration ULNAv2-A program, that bubble sorts an array of 4 values. Do not submit this file with your work.
- http://www.csee.umbc.edu/~jtang/cs411.s20/homework/hw4/Makefile
- Builds all of the code for this assignment, by simply running make. Also included is a clean target to remove all built objects.
Now run make to build the disassembler and demonstration ULNAv2-A programs.
Examine the files adder.S and bsort.S. The ULNAv2-A assembly syntax is similar to ARMv8-A syntax. Minor differences between ULNAv2-A and ARMv8-A syntax are:
-
Like ARMv8-A, immediate values must be preceded
by
#
, but in ULNAv2-A it is optional. The value after the#
may be prefixed by0x
to indicate a hexadecimal value. Otherwise it is treated as a decimal. - Unlike ARMv8-A, ULNAv2-A does not support pre-indexed nor post-indexed addressing modes.
-
Whereas ARMv8-A can refer to the same register using
an
X
orW
notation, ULNAv2-A treats all register always as 16-bit values.
Now examine the generated files adder.img and bsort.img. These are Logisim memory image files. Every line after the first represents a 16-bit value to be stored in instructional memory. When ulnav2a-as is run with the -g flag, additional debugging information are added as comments.
Returning to this homework, the first part of this assignment
involves building your ULNAv2-A
Disassembler. Run hw4, passing in an image filename on
the command line. For each instruction within the image file, the
disassembler displays the raw 16-bit value and its instruction
class. You are to further decode the instruction. Based upon the
instruction number, display the instruction name. For example, given
the instruction c7ee
, which is a branch instruction,
display this:
c7ee: instruction class "D" this is: b
Part 2: Control Signals Decoder
In the final project, you will use Logisim to implement ULNAv2-A as a single-cycle datapath. This will take a while to do. In preparation, your next task is to analyze the ULNAv2-A instruction set and determine how to set control signals for each decoded instruction. For this part, concentrate on these signals:
-
ALUOp
(5 bits) - Specifies which operation the ALU should perform. Not all combination of bits are valid.
-
CondUpdate
(1 bit) - This signal is 1 if the Condition Codes register should be modified.
-
MemRead
(1 bit) - This signal is 1 when reading from data memory.
-
MemWrite
(1 bit) - This signal is 1 when writing to data memory.
-
RegWrite
(1 bit) - This signal is 1 when the register file should be written with the contents of the W Bus.
Then update your hw4.c. After displaying the instruction's name, display its control signal values.
Hint: You should build yourself a spreadsheet to analyze ULNAv2-A. Using the supplied spreadsheet as a basis, insert columns for each control signal. For each instruction, determine what value needs to be written to each control signal. Use an X if the control signal is set to a Don't Care.
Note: Your final implementation of ULNAv2-A will require many more signals. For this homework, you only need to generate the above signals.
Part 3: Implement Unsigned Multiplication
In this next part, you will write your own ULNAv2-A assembly
code. Examine the skeleton file multu16.S. This assembly
code is to implement unsigned 16-bit integer multiplication, based
upon the ARMv8-A code you wrote in
HW3. Like the previous assignment, four registers will be set
prior to executing your algorithm: register R0
has the
multiplicand, R1
the multiplier, R2
the
address in memory to store the upper half of the product,
and R3
the address in memory to store lower half.
Run make to assemble your code into the image file multu16.img. In the final project, you will execute that image within Logisim. For this assignment, you will instead execute the image using the ULNAv2-A emulator ulta2a-emu. Start the emulator like so:
$ ~/ulnav2-a/ulnav2a-emu multu16.imgThe emulator takes you to a command prompt. Enter ? to view a list of commands. You can simulate one CPU cycle at a time by entering s, or run continuously until a
halt
instruction with the command c.
Upon program completion, defined as execution of a halt
instruction, your algorithm is to have written the 32-bit product
into instruction memory. Warning: the grader will
use different values
for R0
, R1
, R2
,
and R3
during grading.
Part 4: Instruction Decoder
In this part of the assignment, you will expand your Logisim file from the third homework.
Create a new subcircuit, Decoder. It has exactly one input, the 16-bit instruction Inst. Its outputs are the five control signals from the Part 2, plus these three:
-
ASel
(3 bits) - Selects a register to read from the register file.
-
BSel
(3 bits) - Selects a register to read from the register file.
-
WSel
(3 bits) - Selects a register to read/write from the register file.
Using the input Inst, generate all eight output signals. To
get you started, here is one part of the decoder:
Next, update your Main circuit. Insert the Instruction Memory (a RAM device), configured with one asynchronous load/store port. Then connect the PC's output to the Instruction Memory's address port. Route the Instruction Memory's output into a 16-bit tunnel Inst. Then rewire the clock, so that the Program Counter, Register File, and Condition Codes are updated on the clock's falling edge.
Add your Instruction Decoder to Main. Route Inst into it,
then route its outputs to the rest of your circuit.
Note: Control signals MemRead and MemWrite do not go anywhere in this assignment, but you will need them for the final project.
Test your circuit by right-clicking Instruction Memory, select "Load Image...", then choose an ULNAv2-A image file. Manually poke the clock line. Ensure that all control signals generated by your Instruction Decoder match your ULNAv2-A disassembler.
Part 5: Required Documentation
Add a comment block to the top of hw4.c file that answers these questions:
-
ARMv8-A has the
mov
instruction to copy the value from one register to another register, but ULNAv2-A lacks that instruction. Give four different ways thatmov
can be simulated, using only a single ULNAv2-A instruction. -
ARMv8-A has the
sub
(immediate) instruction, to subtract a constant from a register and store the difference in a register. How cansub
be simulated in ULNAv2-A, using only a single instruction?
Sample Output
Here is a sample output from running the instruction decoder. The grader will use different image files when testing your decoder. This sample does not show all of the required control signals, and it only shows the disassembly of the last three instructions from bsort.img
$ ./hw4 bsort.img <snip> c7f6: instruction class "D" this is: b CondUpdate: 0 MemRead: 0 RegWrite: 0 4921: instruction class "B" this is: addi. CondUpdate: 1 MemRead: 0 RegWrite: 1 c7ef: instruction class "D" this is: b CondUpdate: 0 MemRead: 0 RegWrite: 0
Here is a sample output from running the multiplication algorithm through the ULNAv2-A emulator:
$ ~/ulnav2-a/ulnav2a-emu multu16.img ULNAv2-A emulator. Enter '?' to display list of commands. PC 0x0000: MOVI r0, 0x10 > (User enters c) VM halted! -- Cycle = 328, PC = 0x0028 -- Register File: 0: 0x010c 1: 0xa310 2: 0x0020 3: 0x0021 4: 0x4110 5: 0x0000 6: 0x8000 7: 0x0000 Condition Codes: C: t N: f V: f Z: t Data Memory: 0x0000: 0000 0000 0000 0000 0000 0000 0000 0000 ... 0x0020: 010c a310 0000 0000 0000 0000 0000 0000 0x0028: 0000 0000 0000 0000 0000 0000 0000 0000 ... 0xfff8: 0000 0000 0000 0000 0000 0000 0000 ffff
Other Hints and Notes
- Ask plenty of questions on the Blackboard discussion board.
- At the top of your submitted files, list any help you received as well as web pages you consulted. Please do not use any URL shorteners, such as goo.gl or TinyURL. Also, do not cite shared data services, such as Pastebin, Dropbox, or Google Drive.
- You are not writing any ARMv8-A assembly for this assignment, but rather ULNAv2-A assembly.
- Construct Karnaugh maps for control signals CondUpdate, MemRead, MemWrite, and RegWrite. Use the sum of products to implement your Instruction Decoder. You do not need to submit your K-Maps. Although instruction 6 is undefined, output 0 for all of its signals.
- You have your choice of multiplication algorithm for Part 3. The instructor has successfully implemented both shift-add and Booth's algorithm using ULNAv2-A, though admittedly Booth's algorithm is bit harder to write.
- For Part 3, you may need to store temporary values into data memory. You may assume that the values for R2 and R3 are at least 32.
Extra Credit
Sorry, there is no extra credit available for this assignment.