Examples of Code Patterns and Structure in RISC-V - Coding Style

Coding Style

This page presents suggestions for good style when writing a RISC-V assembly program. The code file linked at the bottom demonstrates some of the suggestions in practice. This is general guidance; style is subjective and practices should be adapted to best suit a group of developers.

Function/Routine Headers

Function headers should provide a general overview of a subroutine/function to someone reading the code. Function headers should always be included in programming assignment submissions. They are usually placed at the top of a function, and first contain a general description of what the function does. Next, they contain a list of arguments received by the function, a list of values returned by the function, a list of side effects, and a list of the register usage. The header should have the entries for arguments, return balues, and side effects even when there is none to make it clear when there is none. Listing the register usage is valuable to make debugging easier. Below is an example of a function header.

#-------------------------------------------------------------------------------
# NumberOfBits
# A function that counts the number of 1 bits in a bit vector
#
# Arguments:
#       a0: bit-vector memory address
#       a1: bit-vector length
# Return Values:
#       a0: number of 1 bits in vector
# Side Effects:
#       None
# Register Usage:
#       t0: Intermediary for bit-wise operations.
#       t1: Used to store word loaded from vector.
#       t2: Running counter for 1 bits.
#
#-------------------------------------------------------------------------------

Commenting

Commenting is another important aspect of good style. A comment begins with the character #. Commenting is more important in assembly than in higher-level languages for code readablility. However, be careful not to over comment and clutter the assembly code.

For example, below is the code to calculate (A + B) * (A + C) where A, B, C are integers stored in registers t0, t1, t2 respectively. Below is an example of what good commenting might look like:

# Calculate (A + B) * (A + C).
add t1, t0, t1    # t1 <- A + B
add t2, t0, t2    # t2 <- A + C
mul t0, t1, t2    # t0 <- t1 * t2

Modularity

When writing assembly programs, take care to modularize logical blocks into either their own sections of the code or their own functions depending on what the situation calls for. A modular program makes reading and debugging easier. Also, creating functions for individual tasks prevents code duplication and increases reuse by simply calling these functions.

Modular programs also enable unit testing: a practice where each individual component of a program is tested separately. For instance, if the calculation of the log of a number -- a small step of a much larger routine -- contains a mistake. If the the log-calculating code is spread throughout the program, it is difficult to discover that a bug stems from the log calculation. If, instead, the the log calculation is in a separate function, the mistake could be easily discovered through unit testing. Employing good modularity can massively reduce the time spent debugging a RISC-V program.

Below is a file demonstrating the above style suggestions for your reference.

Link to program file