Build a Datapath, part II

Overview

Now that you’re able to control the datapath with a microprogram, we’re ready to implement support for these additional instructions. Your work will augment and update your existing datapath.circ, microprogram.hex, and rules.py files from the previous lab.

Before you can test these instructions out on your datapath you’ll need to assemble them. The next section will walk you through what you need to know to create new instructions for the assembler to transform to the machine language that would then be processed by your datapath..

Background: The Assembler

Adding instructions to the PIPS instruction set architecture (ISA) will require that you write new assembler rules in the rules.py file. This file is written in the Python programming language, although you won’t need to know much about Python to add rules.

Let’s look at the addi instruction’s rule in detail as an example:

@assembler.instruction('addi #, #, #', 1)
def addi_inst(dest, op1, immediate):
  return pips.iformat(opcode='add', r0=dest, r1=op1, imm=immediate)

The first line of this rule declares that we are creating an assembler instruction. In Python terminology, this is known as a decorator. This runs a function to register the addi rule with the assembler so it will match lines in the input program. The decorator assembler.instruction is a function that takes two parameters: an instruction format and the number of machine instructions this assembly instruction will be converted to. The “#” character in the format indicates that some value will appear here; we don’t care what it is yet, but the way we use the value will determine whether it has to be a register, immediate, or something else.

The second line begins a Python function definition. The name of the function is not important, although giving it a descriptive name makes it easy to re-use this function when implementing pseudo-instructions like li. The Python functionaddi_inst takes three parameters: dest, op1, and immediate. The assembler will call this function to convert an instruction (matching the decorator) to machine code, and it will pass in the values that matched the three “#” wildcards in the pattern. This function (addi_instr) is expected to return a string of 32 zeros and ones—yes, these are stored in a Python string rather than an integer—that represent the instruction in machine code. You generally aren’t responsible for producing the sequence of bits because the pips module (discussed more below) has some helpful utilities.

The third line returns a value from this function. The value returned is produced by the pips.iformat function, which takes in the fields of an iformat instruction and combines them into a bit string. Each field is passed in using Python keyword arguments; the value before each “=” character is the name of a parameter to the iformat function, and the value after the “=” is being passed in as a parameter. The keywords (i.e., opcode) aren’t strictly necessary, but they make it clearer what values are going into which fields of the instruction.

The call to pips.iformat says that we want to encode an iformat instruction with the ‘add’ opcode, using the first wildcard value in the instruction as the destination register, the second wildcard as a source register, and the third as an immediate value. The pips.iformat function will convert these parameters from character strings (extracted from the assembly file) to integers (bit strings, if you like) for you. You should look closely at the documentation for the iformat function in pips.py to see how these fields are laid out and what they each do. There are also two tables in pips.py: one that converts opcode names into their corresponding numeric values, and another that converts register names to numbers. You’ll need to refer to the opcode table to see where you should add your microcode entries when setting up a new opcode’s control signals.

Preparation

  1. Open pips.py
  2. Read the documentation for the iformat function.
  3. Read the table of opcodes (search for opcode_table) to familiarize yourself with the instructions whose raw opcodes are available. By the time you complete your datapath, you will ultimately implement all of these instructions in your microprogram controller with additional supporting hardware.

Part A: Subtraction

  1. Add the following code to your rules.py file to start translating a subi instruction:

    You will need to fill in the parameters to pips.iformat to do the conversion. Refer to the opcode table and iformat documentation to as you work out how to encode this instruction.

    One important detail to mention at this point is that Python is a whitespace-sensitive language. Instead of using curly braces or parentheses, Python uses indentation levels to determine scope and control flow. It is important that you indent Python code consistently within one source file; do not mix tabs and spaces or you’ll get some strange errors. (You will note that in the file we have provided to you, we use two spaces to increase the indentation level; you should do the same.)

  2. Once you have encoded your PIPS subi instruction, write another rule in rules.py to convert a sub instruction to PIPS machine code.

  3. Now that you can assemble programs that use sub and subi, update the line in your control unit’s microprogram for the subtraction opcode. Include the comments documenting the specific values of the bit fields for each control line (adding any new ones you may have introduced to this instruction), as well as the recommended binary regrouping and translation to hexadecimal. Don’t forget to re-load the microprogram from microprogram.hex in your Logisim window before testing. (You only need to reload it when you change the microprogram.)
  4. Write a simple test program, assemble it, and use it to test all five of the instructions you can currently translate. Your test program must include comments with the expected results (i.e., register values).

Once you have this working, have the instructor or mentor sign off on this part of the lab. You should be prepared to show your well-documented microprogram, your test program (whose comments also indicate the expected behavior), and assembler rules. You will demonstrate the assembly of your program, load it, and run it.

Part B: More ALU Operations

Now that you know how to add assembler rules and microprograms for basic ALU operations, add support for the following instructions:

The table below shows all of the available ALU operations. You will need to use these operations in combination with the AinvA_{inv}, BinvB_{inv}, and CinC_{in} controls to implement each of the opcodes supported by PIPS.

op number ALU Operation Result
0 addition A+BA + B
1 and A&BA \& B
2 or ABA \| B
3 xor ABA \oplus B
4 slt for signed AA and BB: A<BA < B
5 sltu for unsigned AA and BB: A<BA < B

{:.table.table-lines}

Caution: The slt and sltu operations work a bit differently than they did in the ALU you built earlier this semester. You should not invert either input when running an slt operation; just select the slt or sltu operation using the ALU’s opop input.

After completing the implementation of each instruction, add lines to a simple test program that verifies that these instructions work as expected. Once you finish this part of the lab you should have a single test program that shows all of these instructions working. You’ll need to add comments to your test program to explain what values should appear in each register so you can audit the results of your execution.

Have the instructor or a mentor sign off on your implementation once you have completed this part. You should be prepared to show your well-documented microprogram, your test program(s) (whose comments also indicate the expected behavior), and assembler rules. You will demonstrate the assembly of any test program, load it, and run it.


Copyright © 2018, 2019, 2020 Charlie Curtsinger and Jerod Weinman

CC-BY-NC-SA This work is licensed under a Creative Commons Attribution NonCommercial ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.