Lab1: Assembler I

Objectives

This is not a list of tasks for you to do. It is a list of skills you will have or things you will know after you complete the lab.

Following completion of this lab you should be able to:

Guidelines

Your Tasks

1 Install Python

Install Python 3.x, if you already have Python installed from another class or project you should be good to go. Otherwise, you can download an installer here.

2 Install VSCode

  1. We'll be using VSCode as the IDE for this class, you can download and install it from here. (Read the next step before you click that link.)

  2. During install you may be prompted for what languaged you want the IDE to support. If so, select Python and it should install the basic Python extensions.

    If that does not happen you will need to manually install extensions. Once VSCode opens select the Extensions button on left (it looks like 4 squares with one of them detatched). In the search bar type "python", select and install the Python extension from Microsoft and the Python Debugger extension from Microsoft

  3. Next, in the search bar at the top of the VSCode window type >terminal and select "Python: Create Terminal" It will open a terminal in the lower part of the screen.

  4. In that terminal type python to launch python and make sure it works. Check the version number to ensure you're running python 3 or newer, you may need to specify a version of python (e.g. type python3.12) to get the right version running in the terminal. Type exit() to close the python interpreter.

  5. In the terminal (outside of python) run the following command:

    python -m pip install gradescope-utils

    Replace the python with the appropriate version/path for the python interpreter you want to use. This installs a testing library that we'll use for the test cases.

3 Get your repository

To get your git repo, you'll need the git tools installed. If you are using Windows and need to install git, see the git scm website. If you use a different OS, you should use the install method best for your operating system.

  1. We'll be using Github For Education to manage repositories for this course. Read all of these instructions before you begin.

    1. You'll need to create a github account, your account name should be recognizable as your RHIT username.
      1. Use the pattern: "rhit-username", for example Robert's account name would be: "rhit-williarj".
      2. Even if you already have your own personal Github account please make a course-work specific account. We don't want to have to figure out who XxXSuperLuigi2022XxX is when we're grading. If we can't figure out who you are from the username we'll assume you didn't submit the assignment.
      3. You should set up SSH keys for github access. Scroll to the bottom of this page and follow the directions in "Fixing GitHub Authentication Issues". You can read details about that here.
    2. Go to this url to create your repository.
    3. You may be prompted to grant Github Education access to your account, grant access if asked.
    4. Select your Rose username from the list of students when you create your account.
      1. If your username is not listed select the "Skip to next step" link above the list of names. Tell the instructor you did this!
    5. Select "Accept this assignment" on the following page. This will create your repository copy the link on the nextpage it will look something like this: https://github.com/rhit-csse232/csse232-2324c-labs-joestudent
      • you can click your custom link to view your repository on github.com. The github.com web page will display a green "Code" button with instructions on how to clone your repo.
  2. Check out a copy of your repository.

    1. Open a terminal window. For Windows, the program Git Bash will serve as your terminal. Right click in any folder and select Git Bash Here to start up the terminal.
    2. Navigate to where you want your repo. For example:
      cd Desktop
    3. Use this command to get your repo, adjusting for your url from above.
      git clone git@github.com:rhit-csse232/rhit-csse232-2324c-labs-joestudent.git

4 Running code and tests in VSCode

Running code and editing arguments

  1. To run your assembler you need to set up the debugger. Your repo should contain a launch.json file which specifies the arguments to run the program with.

  2. Select the Run and Debug button on the left side of the screen (it looks like a play button with a bug on top of it). Then near the top you should see a green play button with "Python Debugger: Current File with Arguments". If you have your chosen python file open when you press this button it will start running.

  3. You can change the arguments by opening launch.json and editing the "args" list. Try adding "--help" as an item to this list when you run the assembler.py file below to see its effect. You can add and edit this list to change the behavior of the program. This is really just for debugging though, if you leave it with the options provided you can simply edit the contents of test.asm to test different instructions for this assembler. By default it will output the assembled machine code into the file out.txt which will appear in the folder.

Debugging in VSCode

  1. You should get familiar with and use the VSCode debugger. You can set breakpoints by clicking to the left of the line number in a file.

  2. Use the buttons that appear at the top of the window to navigate through the code as you're debugging.

  3. The pane on the left will show you variables in the code as you debug.

Running Unit Tests

  1. We're using the Python unittest library for our test cases. Each test is preceded by a decorator that looks like: @weight(N) where N is the number of points the test is worth during grading.

  2. The provided settings.json file should have the testing framework ready for you. Click the flask icon on the left to open the Testing window. (Note, sometimes I have to click or open a file before the button will appear.)

  3. The window will list all the testing files and individual tests. It will look something like this:

TODO: Fix these images sizes

When you hover over an individual test, or header for a group of tests, three buttons appear to the right of the name. The first one lets you run the test(s).

  1. The second button is the debugger, you can put breakpoints in the tests or your code to use the debugger while the test cases are running to help you find what causes tests to fail.

  2. You should run the tests as soon as you get your repo to make sure that everything is hooked up correctly. You should see output that looks something like this:

Each test is listed and then the status is printed. Yours will say "ERROR" instead of "ok" to start with.

However, VSCode sometimes defaults to a different python testing framework. If the output mentions pytest you should close VSCode and reopen it and it will likely use the correct tests. The tests will still give output, the incorrect output will look something like this:

Notice the pytest-8.2.2 under the first header, and the fact that the individual tests are not mentioned, just the test file name.

Reading test errors

When a test fails it can be very intimidating, here are the two main types of failures you'll see. First, when your code translates an instruction incorrectly you'll see something like this:

This error box is pointing to the test that failed (test_R_types_add). In the box it shows the expected and actual result, just left of the orange circle number 1. The top binary string is always the expected (e.g. "right") answer, the bottom is the result from calling your assembler.

Each test group runs several inputs through your assembler, to figure out which exact input caused the failure you need to look at the traceback. Look for the item that refers to the test name that failed. Here it is just left of orange circle number 2. We can see that the input at line 34 caused the failure, so I can go look at it.

Next I would set a breakpoint at that line and press the test debugger button to trace the code.

The second kind of error you'll see will occur when your code does not raise an exception when it should. Here is an example:

This box similarly points to the test that failed (test_R_types_arguments). At the top it tells us an exception was not raised when the test expected one, here at orange circle 1 we can see that the test was expecting a BadArguments exception but did not get one. At orange circle 2 we can see the line numer of the offending input so we can debug the problem.

This lab provides several different kinds of exceptions for different cases. Some of them may be ambiguous, e.g. when is it a BadArgument vs a BadImmediate. Go with your gut and match the test cases later. The test cases are set up to minimize the number of places in the code you need to check for errors and raise exceptions, rather than detecting errors as soon as possible.

Build an Assembler

You've been given a partial implementation of a 32-bit RISC-V assembler. Your job for this lab is to implement the parts of the assembler needed to make R-, I-, and S- types work. You only need to edit methods in the lab that are listed below and that have a line like: TODO: Lab 1 in their body. You can ignore the Lab 2 TODOs for now, and do not make changes to any other methods or classes. You are free to add your own new helper methods and classes as you see fit.

Before you start writing code you should look at the lab worksheet and read the Grading Rubric section below.

1 Helps and Hints

You should open up assembler.py and review the general code structure. You can look through the docs/assembler.html file in your repo for an easy to read list of functions and classes. I recommend you keep this open so you can look at any helper methods at a glance.

Some helper methods are implemented for you which you are free to use, these are not tested so you are free to change them as you see fit.

Python implements a few ways to convert integers between bases. First, the int() method takes a second argument that defines the base:

int("101", 2) -> 5

int("11", 2) -> 3

int("101", 16) -> 257

int("11", 16) -> 17

Additionally the bin() method converts a decimal integer into a binary string:

bin(5) -> '0b101'

bin(3) -> '0b11'

bin(-3) -> '-0b11'

Note that it does not use twos-compliment for negative numbers and the strings always start with a '0b'.

1 Assemble()

The heart of the assembler is the Assemble() method. This takes a single instruction and returns its binary representation. At its core though, this method simply figures out the type of the instruction and then calls the appropriate helper function (which you will will write below). Try and make this function do as little work as possible, it will make your code easier to edit in the future.

You should not try and write this function in its entirety now. I recommend you add to it as you expand your assembler to support more types, just add support for one instruction type at a time.

If the method is invoked like this: Assemble("add t0 t0 t0") then Assemble would call Assemble_R_Type("add", ["t0", "t0", "t0"]) and return the result.

You may find this helper method useful: is_core_inst()

The line_num argument is only used for debugging for this lab.

As you write this method and the others in the lab you should raise exceptions when you encounter instructions that can't be assembled. There are several example exceptions raised throughout the template, take a look at dec_to_bin for a couple examples (and consider why these are raised here).

This program provides a suite of custom exceptions: BadImmediate, BadArguments, BadInstruction, BadRegister, BadField, BadFormat, BadLabel

You can check the code (or the assembler.html file) for a general description of each. Your assembler should probably raise every one of these in at least one place when it is complete. Not all of these are tested, but they are very useful as you debug. For this lab you will not need the BadLabel exception.

2 Assemble Individual Types

You will implement the methods for each individual type. Note that the I-types are broken up into separate methods for each slightly different format. Write the code to implement each of these:

  1. Assemble_R_Type
  2. Assemble_I_Type
  3. Assemble_I_Type_shift
  4. Assemble_I_Type_base_offset
  5. Assemble_S_Type

You should edit the Assemble() function to call the correct one of these for a given instruction.

The helper get_register_bin() will be useful for these methods.

I types are complicated you may want to consider using is_shift_immediate(), parse_base_offset(), reverse_string(), and dec_to_bin() in these.

Grading Rubric

All the labs for CSSE232 have these general requirements:

General Requirements for all Labs

  1. The solution fits the need
  2. Aspects of performance are discussed
  3. The solution is tested for correctness
  4. The submission shows iteration and documentation

Some labs will hit some of these requirements more than others. But you should always be thinking about them.

Fill out the Lab Worksheet

In the worksheet, explain how you satisfy each of these items. Some guidelines:

  1. Submit your completed worksheet to gradescope, only 1 per team (make sure all team member's names are included). In gradescope you are able add your team members names to the submission, make sure you do so. You can find the gradescope link on the course moodle page.

  2. Lab code will be submitted to EVERY team member's git repository. You must include your name in a comment at the top of all files you submit. BOTH PARTNERS MUST SUBMIT CODE FOR ALL THE LABS. TODO: GET THESE INSTRUCTIONS UPDATED

    1. Be sure to include your name and your partner's name in all of your files. TODO: FIX THIS
    2. Open a terminal window and to navigate to your lab01/p3 folder.
    3. Add the changed file with:
      git add p3.asm
    4. Commit the changes with a commit comment:
      git commit -m "YOUR COMMIT MESSAGE"
    5. Send the changes to the server:
      git push
  3. Verify that your changes were pushed correctly:

    1. Something about looking on the github website ...
    2. ...

Fixing GitHub Authentication Issues

Here is a quick and dirty guide to setting up SSH keys if you have been having trouble accessing repos.

  1. Make an SSH Key
    1. Open "Git Bash"
    2. Change to the ~/.ssh directory (cd ~/.ssh)
    3. Generate an SSH key by typing ssh-keygen
      1. Hit "enter" any time it asks you a question (default RSA type, blank passphrase)
    4. display the contents of the new public key (cat id_rsa.pub)
    5. Copy all the contents it displayed (right click to copy in GitBash, ctrl+c won't work)
  2. Add the SSH key to your github account
    1. In your web browser, go to the github website.
    2. Open your account Settings on the github website (click the user icon in upper-right corner of the web page and choose "settings")
    3. Select "SSH and GPG Keys" on the left
    4. Click "New SSH key" green button
    5. Give it a name like "232 ssh key"
    6. Paste the key you copied into the large text box
    7. Click "Add SSH key" button
  3. Use the key to clone your repo
    1. In your web browser, open your new repository in the github web page (the URL it gave you when you first created it)
    2. Click the green "Code" button
    3. Note that it says "Clone with SSH": copy that URL (looks like git@github.com/....)
    4. Go back to Git Bash and git clone that copied URL