UNC miniRISC-V Architecture Simulator V 0.1


 

1.0 UNC miniRISCV Processor Architecture

The UNC miniRISCV is a simple 32-bit processor with 32 registers, and a with main memory size of 2 gigabytes (231), or 536,870,912 (229) 32-bit words. It RV32I compliant and includes a subset of RV32M instructions suffcient for supporting the C programing language. Memory is byte-addressable, and all instructions occupy one 32-bit word (4-bytes) in memory. Byte addresses of are in little-endian order.

The 32 registers are specified as x0-x31. Each register holds a 32-bit value, which can be interpreted a number, a character, or the address of some of a some variable or object in memory. Register, x0, is special. When used as a source operand, the contents of x0 are always 0, and all updates of x0, when used as a destination, are ignored.

Program execution begins at location 0x80000000 in kernel mode. However, the program must first be assembled (converted from its symbolic representation to a sequence of 32-bit words in memory) before it can be executed. If there are errors in the assembly process, a dialog box will pop up explaining the error.

2.0 Introduction to UNC miniRISCV Assembly Language

A typical line of assembly code specifies a single primitive operation, called an instruction, and its operands. The list of all UNC miniRISCV operations (its instruction set) is enumerated in section 3. In assembly language instructions are specified via short mneumonics of the operation specified (usually either abbreviations of acronymns). A list of comma-separated operands follows each instruction's mneumonic. The assembler uses this mneumonic and operand list to generate a binary encoding of the instruction, which is stored as a word in memory. Thus, an assembly language program is merely a way of generating a sequence of binary words, that the computer interprets as either (or both) program or data. To make this discussion concrete, consider the following example line of assembly code:

add x3,x4,x4 # "add" is the instruction mneumonic
# "x3,x4,x4" is a list of 3 operands

Generally, he first operand specifies the destination (output) of the operation, and subsequent operands specify source (input) operands. This instruction given tells miniRISCV to add the contents register 4 to itself and store the result in register 3.

Empty lines are ignored by the assembler (they generate no output to memory), but they are often used to enhance readability. Any text following a "#" is considered a comment and the remainder of the line is ignored by the assembler. Comments are used to document the intent of the code beyond what is evident from the instruction specification alone.

Optionally, any line of assembly code can start with a label. A label is a sequence of characters followed by a colon (":"), which it is usually a meaningful name. A label provides a means for referencing the memory location that a particular instruction or variable is stored at. Instruction labels are often used to specify targets for branch instuctions. In the case of data, a label might represent a variable name. An example using label is shown below:

loop: addi $3,$3,-1 # loop is a label
bne $0,$3,loop # here the label is referenced

The UNC miniRISCV assembler provides a small set of assembler directives, that are used like instructions. All assembler directives are prefixed with a "." (period) and they are generally used for allocating space for data and initializing variables. A complete list of assembler directives is given in section 4. Examples are given below.

Fib: .word 1,1,2,3,5,8,13,21 # first 8 Fibonacci numbers
masks: .word 0x000000ff,0x0000ff00,0x00ff0000,0xff000000 # byte masks
Name: .asciiz "Leonard" # 0-terminated string (8 bytes)
array: .space 20 # 20 uninitialized words

The UNC miniRISCV assembler also provides special support for simulation. Any line of assembly code beginning with an "*" (asterix) will act as a breakpoint causing the simulator to halt prior to the execution of the instruction following the mark, or after marked data is accessed via either a load or store instruction. One or more breakpoints can be set, and at least one is required to use the "Run" button.

2.0 UNC miniRISCV Instruction Formats

All RISC-V RV32I instructions are encoded in one of 4 formats:

    Format     31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
R-type func7 rt/shamt rs func3 rd opcode
I-type imm12 rs func3 rd opcode
B/S-type imm7 rt rs func3 imm5 opcode
U-type imm20 rd opcode

3.0 UNC miniRISCV Instruction Set

Math Instructions

ADD: add registers
Syntax: add rd,rs,rt
Encoding: 0000 000t tttt ssss s000 dddd d011 0011
Description:
Reg[d] ← Reg[s] + Reg[t]

Add the contents of registers Reg[s] and Reg[t], and place the result in Reg[d]. Overflows are ignored.

Example: add x6,x2,x0 # Encoded as: 0x00010333


ADDI: add immediate
Syntax: addi rd,rs,imm12
Encoding: iiii iiii iiii ssss s000 dddd d001 0011
Description:
Reg[d] ← Reg[s] + sign_extend(imm12)

Add a 16-bit sign-extended constant to the contents of Reg[s], and place the result in Reg[d]. Overflows are ignored.

Example: addi x6,x6,100 # Encoded as: 0x06430313


SUB: subtract registers
Syntax: sub rd,rs,rt
Encoding: 0100 000t tttt ssss s000 dddd d011 0011
Description:
Reg[d] ← Reg[s] - Reg[t]

Subtract the contents of Reg[t] from Reg[s], and place the result in Reg[d]. Overflows are ignored.

Example:sub x16,x0,x12 # Encoded as: 0x40C00833


MUL: multiply registers
Syntax: mul rd,rs,rt
Encoding: 0000 001t tttt ssss s000 dddd d011 0011
Description:
Reg[d] ← Reg[s] * Reg[t]

Multiply the contents of Reg[s] and Reg[t], and place the lower 32-bits of the product in Reg[d]. Overflows are ignored. MUL is binary and semantically compliant with the RV32M mul instruction.

Example:mul x5,x6,x7 # Encoded as: 0x027302B3


DIV: divide registers
Syntax: div rd,rs,rt
Encoding: 0000 001t tttt ssss s100 dddd d011 0011
Description:
Reg[d] ← Reg[s] / Reg[t]

Divide the contents of Reg[s] by Reg[t], and place the quotient in Reg[d]. A divisor of 0 does not generate an overflow, and the destination register rd is set to all ones. DIV is binary and semantically compliant with the RV32M div instruction.

Example:div x7,x5,x6 # Encoded as: 0x0262C3B3


REM: remainder of a register quotient
Syntax: rem rd,rs,rt
Encoding: 0000 001t tttt ssss s110 dddd d011 0011
Description:
Reg[d] ← Reg[s] % Reg[t]

Divide the contents of Reg[s] by Reg[t], and place the remainder in Reg[d]. A divisor of 0 does not generate an overflow and the contents of the destination register rd is set to the dividend, Reg[s]. REM is binary and semantically compliant with the RV32M rem instruction.

Example:rem x7,x5,x6 # Encoded as: 0x0262E3B3


SLT: set if less than
Syntax: slt rd,rs,rt
Encoding: 0000 000t tttt ssss s010 dddd d011 0011
Description:
Reg[d] ← 1 if (Reg[s] < Reg[t]); 0 otherwise

Set Reg[d] to '1' if the contents of Reg[s] is less than the contents of Reg[t].

Example:slt x17,x18,x19 # Encoded as: 0x013928B3


SLTU: set if less than unsigned
Syntax: sltu rd,rs,rt
Encoding: 0000 000t tttt ssss s011 dddd d011 0011
Description:
Reg[d] ← 1 if (Reg[s] < Reg[t]); 0 otherwise

Set Reg[d] to '1' if the contents of Reg[s] is less than the contents of Reg[t]. Both operands (Reg[s] and Reg[t]) are compared as unsigned integers.

Example:sltu x20,x21,x22 # Encoded as: 0x016ABA33


SLTI: set if less than immediate
Syntax: slti rd,rs,imm12
Encoding: iiii iiii iiii ssss s010 dddd d001 0011
Description:
Reg[t] ← 1 if (Reg[s] < sign_extend(imm12)); 0 otherwise

Set Reg[d] to '1' if the contents of Reg[s] is less than the supplied sign-extended immediate value.

Example:slti x6,x2,10 # Encoded as: 0x00A12313


SLTIU: set if less than immediate unsigned
Syntax: sltiu rt,rs,imm12
Encoding: iiii iiii iiii ssss s011 dddd d001 0011
Description:
Reg[t] ← 1 if (Reg[s] < sign_extend(imm16)); 0 otherwise

Set Reg[d] to '1' if the contents of Reg[s] is less than the supplied sign-extended immediate value. Both arguments are treated as unsigned integers (despite the sign extension... go figure?).

Example:sltiu x2,x6,0xfff # Encoded as: 0xFFF33113


Logic Instructions

AND: bitwise-and registers
Syntax: and rd,rs,rt
Encoding: 0000 000t tttt ssss s111 dddd d011 0011
Description:
Reg[d] ← Reg[s] & Reg[t]

Performs a "bitwise" anding of the contents of registers Reg[s] and Reg[t] and places the result in Reg[d]. The and instruction is commonly used to mask or clear bits.

Example: and x6,x2,x3 # Encoded as: 0x00317333


ANDI: bitwise-and with immediate
Syntax: andi rd,rs,imm12
Encoding: iiii iiii iiii ssss s111 dddd d001 0011
Description:
Reg[t] ← Reg[s] & sign_extend(imm12)

Performs a "bitwise" anding of the contents of Reg[s] with the given sign-extended 12-bit constant and places the result in Reg[d]. The andi instruction is commonly used to mask or clear bits.

Example: andi x6,x2,255 # Encoded as: 0x0FF17313


OR: bitwise or registers
Syntax: or rd,rs,rt
Encoding: 0000 000t tttt ssss s110 dddd d011 0011
Description:
Reg[d] ← Reg[s] | Reg[t]

Performs a "bitwise" oring of the contents of Reg[s] with Reg[t], and place the result in Reg[d]. The or instruction is commonly used to set bits.

Example:or x4,x4,x5 # Encoded as: 0x00526233


ORI: bitwise or with immediate
Syntax: ori rd,rs,imm12
Encoding: iiii iiii iiii ssss s110 dddd d001 0011
Description:
Reg[t] ← Reg[s] | sign_extend(imm12)

Performs a "bitwise" oring of the contents of Reg[s] with the given sign-extended 12-bit constant and place the result in Reg[t]. The ori instruction is commonly used to set bits.

Example:ori x12,x11,0xbcd # Encoded as: 0xBCD5E613


XOR: bitwise exclusive or registers
Syntax: xor rd,rs,rt
Encoding: 0000 000t tttt ssss s100 dddd d011 0011
Description:
Reg[d] ← Reg[s] ^ Reg[t]

Perform a "bitwise" exclusive-or of the contents of Reg[s] with Reg[t]), and place the result in Reg[d]. The xor instruction is commonly used to complement bits.

Example:xor x6,x9,x10 # Encoded as: 0x00A4C333


XORI: bitwise exclusive-or with immediate
Syntax: xori rd,rs,imm12
Encoding: iiii iiii iiii ssss s100 dddd d001 0011
Description:
Reg[t] ← Reg[s] ^ imm16

Performs a "bitwise" exclusive-oring of the contents of Reg[s] with the sign-extended 12-bit constant and places the result in Reg[t]. The xori instruction is commonly used to complement bits.

Example:xori x6,x12,0xfff # Encoded as: 0xFFF64313


Shift Instructions

LUI: load upper immediate while filling lower bits with zeros
Syntax: lui rd,imm20
Encoding: iiii iiii iiii iiii iiii dddd d011 0111
Description:
Reg[t] ← imm20 << 12

Load Reg[t] with the given 20-bit constant shifted left by 12 bits. Vacated bits are filled with 0's. This instruction is frequently followed by an addi rd,x0,imm12 to initialize a register with a 32-bit constant. If a label is specified, the upper 20 bits of the absolute address are used as the constant.

Example: lui sp,0xc0000 # Encoded as: 0xC0000137


SLL: shift left logical by filling lower bits with zeros
Syntax: slli rd,rs,imm5
Encoding: 0000 000i iiii ssss s001 dddd d001 0011
Description:
Reg[d] ← Reg[t] << imm5

Shift the contents of Reg[t] left the number of positions specified by the given unsigned constant (0-31) and place the result in Reg[d]. Vacated bits are filled with 0's. This instruction is frequently used to multiply by powers of 2.

Example: slli x6,x10,2 # Encoded as: 0x00251313


SLL: shift left logical variable; zero-fill lower bits
Syntax: sll rd,rs,rt
Encoding: 0000 000t tttt ssss s001 dddd d011 0011
Description:
Reg[d] ← Reg[s] << Reg[t]

Shift the contents of Reg[s] left the number of positions specified by the specified by lower 5-bits of Reg[t] and place the result in Reg[d]. Vacated bits are filled with 0's and the upper 27 bits of Reg[t] are ignored. This instruction is frequently used to multiply by powers of 2.

Example:sll x10,x11,x12 # Encoded as: 0x00C59533


SRLI: shift right logical filling upper bits with zeros
Syntax: srli rd,rs,imm5
Encoding: 0000 000i iiii ssss s101 dddd d001 0011
Description:
Reg[d] ← Reg[t] >>> imm5

Shift the contents of Reg[t] right the number of positions specified by the given unsigned constant (0-31). Vacated register bits are filled with 0's. This instruction can be used to perform unsigned integer divides by a power of 2.

Example:srli x21,x21,8 # Encoded as: 0x008ADA93


SRL: shift right logical variable; zero-fill upper bits
Syntax: srl rd,rs,rt
Encoding: 0000 000t tttt ssss s101 dddd d011 0011
Description:
Reg[d] ← Reg[t] >>> Reg[s]

Shift the contents of Reg[t] right the number of positions specified by the lower 5-bits of Reg[s] and place the result in Reg[d]. Vacated bits are filled with 0's. This instruction is frequently used to divide unsigned integers by powers of 2.

Example:srl x6,x8,x24 # Encoded as: 0x01845333


SRAI: shift right arithmetic filling upper-bits with the sign
Syntax: srai rd,rs,imm5
Encoding: 0100 000i iiii ssss s101 dddd d001 0011
Description:
Reg[d] ← Reg[t] >> imm5

Shift the contents of Reg[t] right the number of positions specified by the given unsigned constant (0-31). Vacated register bits are filled with copies of the sign bit. An alternate intepretation of is:

Reg[d] ← Reg[t] / 2imm5

Where the contents of Reg[t] are treated as a signed integer.

Example:srai x10,x10,24 # Encoded as: 0x41855513


SRA: shift right variable filling upper-bits with the sign
Syntax: sra rd,rs,rt
Encoding: 0100 000t tttt ssss s101 dddd d011 0011
Description:
Reg[d] ← Reg[t] >> Reg[s]

Shift the contents of Reg[t] right the number of positions specified by the lower 5-bits of Reg[s]. The vacated register bits are filled with copies of the sign bit.

Example:sra x10,x11,x12 # Encoded as: 0x40C5D533


Memory Access Instructions

SW: store word
Syntax: sw rt,imm12(rs)
sw rt,imm12      # implicit rs = x0
sw rt,(rs)       # implicit imm12 = 0
Encoding: iiii iiit tttt ssss s010 iiii i010 0011
Description:
Memory[Reg[s]+sign_extended(imm12)] ← Reg[t]

Save the contents of Reg[t] into the memory address given by the contents of Reg[s] plus a sign-extended 12-bit constant.

Example:sw x1,12(x2) # Encoded as: 0x00112623


LW: load word
Syntax: lw rd,imm12(rs)
lw rd,imm12      # implicit rs = x0
lw rd,(rs)       # implicit imm12 = 0
Encoding: iiii iiii iiii ssss s010 dddd d000 0011
Description:
Reg[d] ← Memory[Reg[s]+sign_extended(imm16)]

Load Reg[d] with the contents of memory at the address given by the contents of Reg[s] plus a sign-extended 16-bit constant.

Example:lw x11,8(x2) # Encoded as: 0x00812583


Branch and Jump Instructions

BEQ: branch if equal
Syntax: beq rs,rt,imm12
Encoding: iiii iiit tttt ssss s000 iiii i110 0011
Description:
if (Reg[s] == Reg[t]) {
    PC ← PC + sign_extended(imm12)
}

If the contents of Reg[s] equals the contents of Reg[t] branch to the instruction that is the offset from currect PC by the specified signed immediate value, which is usually specified by a label.

Example:halt: beq x0,x0,halt # Encoded as: 0x00000063


BNE: branch if not equal
Syntax: bne rs,rt,imm12
Encoding: iiii iiit tttt ssss s001 iiii i110 0011
Description:
if (Reg[s] ≠ Reg[t]) {
    PC ← PC + sign_extended(imm12)
}

If the contents of Reg[s] does not equal the contents of Reg[t] branch to the instruction that is the offset from currect PC by the specified signed immediate value, which is usually specified by a label.

Example:bne  x10,x0,skip # Encoded as: 0x00051463
            srai x10,x10,16
    skip:



BLT: branch if less than
Syntax: blt rs,rt,imm12
Encoding: iiii iiit tttt ssss s100 iiii i110 0011
Description:
if (Reg[s] < Reg[t]) {
    PC ← PC + sign_extended(imm12)
}

If the signed contents of Reg[s] is less than the signed contents of Reg[t] branch to the instruction that is the offset from currect PC by the specified signed immediate value, which is usually specified by a label.

Example:loop: addi  x5,x5,-1
                  blt   x5,x0,loop # Encoded as: 0xFE02CEE3



BGE: branch if greater than or equal
Syntax: bge rs,rt,imm12
Encoding: iiii iiit tttt ssss s101 iiii i110 0011
Description:
if (Reg[s] ≥ Reg[t]) {
    PC ← PC + sign_extended(imm12)
}

If the signed contents of Reg[s] is greater than of equal to the signed contents of Reg[t] branch to the instruction that is the offset from currect PC by the specified signed immediate value, which is usually specified by a label.

Example:loop: addi  x5,x5,-1
                  bge   x5,x0,loop # Encoded as: 0xFE02CEE3



BLTU: branch if less than unsigned
Syntax: bltu rs,rt,imm12
Encoding: iiii iiit tttt ssss s110 iiii i110 0011
Description:
if (Reg[s] < Reg[t]) {
    PC ← PC + sign_extended(imm12)
}

If the unsigned contents of Reg[s] is less than the unsigned contents of Reg[t] branch to the instruction that is the offset from currect PC by the specified signed immediate value, which is usually specified by a label.

Example:loop: addi  x5,x5,-1
                  bltu  x0,x5,loop # Encoded as: 0xFE504EE3



BGEU: branch if greater or equal unsigned
Syntax: bgeu rs,rt,imm12
Encoding: iiii iiit tttt ssss s111 iiii i110 0011
Description:
if (Reg[s] ≥ Reg[t]) {
    PC ← PC + sign_extended(imm12)
}

If the unsigned contents of Reg[s] is less than the unsigned contents of Reg[t] branch to the instruction that is the offset from currect PC by the specified signed immediate value, which is usually specified by a label.

Example:loop: addi  x5,x5,-1
                  bltu  x0,x5,loop # Encoded as: 0xFE504EE3



JAL: jump and link
Syntax: jal rd,imm20
Encoding: iiii iiii iiii iiii iiii dddd d110 1111
Description:
Reg[d] ← PC + 4
PC ← PC + sign_extended(imm20)

Write the address of the following instruction, PC + 4, into Reg[d], then jump to the instruction that is found by adding the current PC by.

Example:jr $31 # Encoded as: 0x03e00008


JALR: jump and link register
Syntax: jalr rd,imm12(rs)
               jalr rd,(rs)

Encoding: iiii iiii iiii ssss s000 dddd d110 0111
Description:
Reg[d] ← PC + 4
PC ← Reg[s] + sign_extended(imm12)

Jump to the instruction given by the contents of Reg[s], and save the location of the next instruction in Reg[s].

Example:jalr x1,20(x31) # Encoded as: 0x014F80E7


J: jump
Syntax: j imm26
Encoding: 0000 10ii iiii iiii iiii iiii iiii iiii
Description:
PC ← (PC & 0xf0000000) | (4*imm26)

Jump to the instruction given by 4 times the immediate 26-bit constant, which is usually references a label.

Example:j 0x00400000 # Encoded as: 0x08100000


JAL: jump and link
Syntax: jal imm26
Encoding: 0000 11ii iiii iiii iiii iiii iiii iiii
Description:
Reg[31] ← PC + 4
PC ← (PC & 0xf0000000) | (4*imm26)

Jump to the instruction given by 4 times the immediate 26-bit constant, which is usually references a label, and save the location of the next instruction in Reg[31].

Example:jal main # Encoded as: 0x0c100000


4.0 Pseudoinstructions

NEG: negate register
Syntax: neg $d,$s
Encoding: pseudoinstruction
Description:
Reg[d] ← -Reg[s]

Negates the contents of register Reg[s] and places the result in Reg[d]. Generates an overflow if the sign of the result is the same as the original contents of the source register.

Example: neg $6,$2


NEGU: negate register and ingore overflows.
Syntax: negu $d,$s
Encoding: pseudoinstruction
Description:
Reg[d] ← -Reg[s]

Negates the contents of register Reg[s] and places the result in Reg[d]. Overflows are ignored.

Example: negu $6,$2


NOT: bitwise complement
Syntax: not $d,$s
Encoding: pseudoinstruction
Description:
Reg[d] ← ~Reg[s]

Performs a "bitwise" complement of the contents of Reg[s] and places the result in Reg[d]. Each '0' is replaced by a '1', and each '1' with a '0';

Example:not $4,$4


MOVE: copy a register's contents of
Syntax: move $d,$s
Encoding: pseudoinstruction
Description:
Reg[d] ← Reg[s]

Copies the contents of Reg[s] to Reg[d].

Example:move $1,$4


LA: load address
Syntax: la $d,imm16($s)
la $d,imm16      # implicit $s = $0
la $d,($s)       # implicit $imm16 = 0
Encoding: pseudoinstruction
Description:
Reg[d] ← Reg[s]+sign_extended(imm16)

Load Reg[d] with the address given by the adding the contents of Reg[s] to a sign-extended 16-bit constant. Using $1 as an operand may give unexpected results.

Example:la $1,8($29)


SGT: set if greater than
Syntax: sgt $d,$s,$t
Encoding: pseudoinstruction
Description:
Reg[d] ← 1 if (Reg[s] > Reg[t]); 0 otherwise

Set Reg[d] to '1' if the contents of Reg[s] is greater than the contents of Reg[t].

Example:sgt $17,$18,$19


B: unconditional branch
Syntax: b imm16
Encoding: pseudoinstruction
Description:
PC ← PC + 4*sign_extended(imm16)

Branch to the instruction given by the specified constant, which is usually specified by a label.

Example:halt: b halt


5.0 Assembler Directives

.WORD: Specify initialized data
Syntax: .word value1, value2, ..., valuen
Description:
Successive words in memory are initialized with constant values from the list. Constants can be decimal numbers, octal numbers prefixed with '0', hexadecimal numbers prefixed with '0x', or labels, which specify an address.

Example: .word 10,010,0x10 # Encoded as: 0x0000000a, 0x00000008, 0x00000010


.SPACE: Specify a block of uninitialized space
Syntax: .space value1,value2, ..., valuen
Description:
Blocks of sizes specified in the list of constant values are reserved. Constants can be decimal numbers, octal numbers prefixed with '0', hexadecimal numbers prefixed with '0x', or an address label.

Example: .space 50 # reserves 50 uninitialized words


.ASCIIZ: Initialize memory with strings
Syntax: .asciiz "string1","string2", ..., "stringn"
Description:
Fills successive memory locations with characters from the given list of quoted strings, each string is termnated with one or more "0"s. The address of the first letter of each string is word aligned.

Example: .asciiz "Tarheels" # Encoded as: 0x54617268, 0x65656C73, 0x00000000
 

Back to course web page  



© 2022 Leonard McMillan