TABLE OF CONTENTS (HIDE)

Rivasm - A RISC-V RV32IM Assembly Program Assembler and Simulator, and a RISC-V RV32IM CPU Implementation on FPGA

Download rivasm.jar

Download rivasm.jar. Bug reports are welcome (Send comments to yamin@hosei.ac.jp).

Screen Shots

Below is the Rivasm main window. An Editor window is in the behand (there is also a Console window).

Editor window:

Console window:

RISC-V RV32IM instructions implemented in Rivasm:

RISC-V RV32IM CPU Implementation on FPGA

The image below shows a RISC-V RV32IM system that displays keyboard scancodes (make codes + break codes) on VGA monitor. The system consists of an RV32IM CPU, instruction memory, data memory, VRAM, a keyboard interface, and a VGA controller.

Below is the assembly code which will be translated into binary RV32IM machine code and executed on the RISC-V RV32IM CPU for displaying scancodes. The cyan caret was implemented with hardware. The instruction "sw a4, 0(s3)" writes the caret background color and current caret position into a caret register.

By clicking Verilog button in the Rivasm main window, a Verilog HDL file for implementing instruction memory (ROM) is created, as shown as below.

Xilinx COE or Altera MIF can be also created by clicking Xilinx or Altera button.

The image above shows the execution results of mul, mulh, mulhsu, mulhu, div, divu, rem, and remu instructions. Below is the test codes.

riscv_rv32im_mul_div_rem_inst_ram.s

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/*
 * test mul, mulh, mulhsu, mulhu, div, divu, rem, remu hardware
 * check new version via https://yamin.cis.k.hosei.ac.jp/rivasm/
 */
.text
main:
    li     sp, 0x10000        # 64KB stack and data memory
    lui    s2, 0xc0000        # vram space: c0000000 - dfffffff

signed_x_signed:
    li     a1, 0x7fffffff     # a
    li     a2, -15            # b
    mulh   a5,  a1, a2        # product high
    mul    a4,  a1, a2        # product low, fused with mulh
    li     a3,  0x2a          # '*'
    call   print_a1_a2_a4_a5  # s = s*s: 7fffffff * fffffff1 = fffffff88000000f
    la     a0,  sxs           # data memory, "   Signed*  signed"
    li     a1,  18            # string size = 18
    call   putchars           # display
    # oooooooo*oooooooo=oooooooo,oooooooo   Signed*  signed
    # 8*4+3=35
    # 35+18=53
    # 53+27=80
    # 27*4=108
    addi   s2,  s2, 108       # next row
unsigned_x_unsigned:
    li     a1, 0x7fffffff     # a
    li     a2, -15            # b
    mulhu  a5,  a1, a2        # product high
    mul    a4,  a1, a2        # product low, fused with mulhu
    li     a3,  0x2a          # '*'
    call   print_a1_a2_a4_a5  # u = u*u: 7fffffff * fffffff1 = 7ffffff78000000f
    la     a0,  uxu           # data memory, " Unsigned*unsigned"
    li     a1,  18            # string size = 18
    call   putchars           # display
    addi   s2,  s2, 108       # next row
signed_x_unsigned:
    li     a1, 0x7fffffff     # a
    li     a2, -15            # b
    mulhsu a5,  a1, a2        # product high
    mul    a4,  a1, a2        # product low, fused with mulhsu
    li     a3,  0x2a          # '*'
    call   print_a1_a2_a4_a5  # s = s*u: 7fffffff * fffffff1 = 7ffffff78000000f
    la     a0,  sxu           # data memory, "   Signed*unsigned"
    li     a1,  18            # string size = 18
    call   putchars           # display
    addi   s2,  s2, 108       # next row
signed32_0:
    li     a1, 0x7fffffff
    li     a2,   2
    div    a5,  a1, a2        # div signed
    rem    a4,  a1, a2        # rem signed, fused with div
    li     a3,  0x2f          # '/'
    call   print_a1_a2_a4_a5  # signed32: 7fffffff / 2 = 3fffffff; 7fffffff % 2 = 1
    la     a0,  sdr           # data memory, "   Signed quotient,remainder"
    li     a1,  28            # string size = 28
    call   putchars           # display
    addi   s2,  s2, 68        # next row
signed32_1:
    li     a1, 0xfffffff1
    li     a2,   2
    div    a5,  a1, a2        # div signed
    rem    a4,  a1, a2        # rem signed, fused with div
    li     a3,  0x2f          # '/'
    call   print_a1_a2_a4_a5  # signed32: fffffff1 / 2 = fffffff9; fffffff1 % 2 = -1
    la     a0,  sdr           # data memory, "   Signed quotient,remainder"
    li     a1,  28            # string size = 28
    call   putchars           # display
    addi   s2,  s2, 68        # next row
unsigned32:
    li     a1, 0xfffffff1
    li     a2,   2
    divu   a5,  a1, a2        # div unsigned
    remu   a4,  a1, a2        # rem unsigned, fused with divu
    li     a3,  0x2f          # '/'
    call   print_a1_a2_a4_a5  # unsigned32: fffffff1 / 2 = 7ffffff8; fffffff1 % 2 = 1
    la     a0,  udr           # data memory, " Unsigned quotient,remainder"
    li     a1,  28            # string size = 28
    call   putchars           # display
    addi   s2,  s2, 68        # next row
output_caret:
    lui   s3,  0xa0000        # i/o  space: a0000000 - bfffffff
    li    t3,  0x600          # line 6
caret:
    lui   a4,  0xff0          # background color
    or    a4,  a4,  t3        # back,row,col
    sw    a4,  0(s3)          # to caret register
read_kbd:
    lw    a0,  0(s3)          # read kbd: {0,ready,byte}
    andi  a1,  a0,   0x100    # check if ready
    beqz  a1,  read_kbd       # if no key pressed, wait
    addi  t3,  t3,  0x100
    j     caret
#######################################################################################
print_a1_a2_a4_a5:
    addi   sp,  sp, -16
    sw     ra,  12(sp)

    mv     a0,  a1            # print a1
    call   print_a0
    mv     a0,  a3
    call   printchar_a0       # * or /
    mv     a0,  a2            # print a2
    call   print_a0
    li     a0,  0x3d
    call   printchar_a0       # =
    mv     a0,  a5            # print a5
    call   print_a0
    li     a0,  0x2c
    call   printchar_a0       # ,
    mv     a0,  a4            # print a4
    call   print_a0

    lw     ra,  12(sp)
    addi   sp,  sp,  16
    ret
#######################################################################################
printchar_a0:
    addi   sp,  sp, -16
    sw     ra,  12(sp)

    sw     a0,  0(s2)         # to vram
    addi   s2,  s2,  4        # vram address ++

    lw     ra,  12(sp)
    addi   sp,  sp,  16
    ret
#######################################################################################
print_a0:  # 8 chars
    addi   sp,  sp, -16
    sw     ra,  12(sp)

    li     t0,  28            # 31,30,29,28th 4-bit
shift:
    srl    t1,  a0,  t0       # 4-bit
    andi   t1,  t1,  0xf      # 4-bit
    addi   t2,  t1,  -9
    bgtz   t2,  abcdef
    addi   t1,  t1,  0x30     # ascii 0-9
    j      putchar2vram
abcdef:
    addi   t1,  t1,  0x37     # ascii a-f
putchar2vram:
    sw     t1,  0(s2)         # to vram
    addi   s2,  s2,  4        # vram address ++
    addi   t0,  t0, -4
    bgez   t0,  shift

    lw     ra,  12(sp)
    addi   sp,  sp,  16
    ret
#######################################################################################
putchars:  # a0: address of string; a1: number of chars
    addi   sp,  sp, -16
    sw     ra,  12(sp)

    li     a2,  0             # counter
put_next_char:
    lbu    a3,  0(a0)         # load a byte (a char) from data memory
    addi   a0,  a0,  1        # dram address ++, Little Endian
    sw     a3,  0(s2)         # send it to vram
    addi   s2,  s2,  4        # vram address ++
    addi   a2,  a2,  1
    bne    a2,  a1, put_next_char

    lw     ra,  12(sp)
    addi   sp,  sp,  16
    ret
#######################################################################################
.data 0
sxs: .string "   Signed*  signed"
uxu: .string " Unsigned*unsigned"
sxu: .string "   Signed*unsigned"
sdr: .string "   Signed quotient,remainder"
udr: .string " Unsigned quotient,remainder"
.end

Download riscv_rv32i_cpu.v

Download riscv_rv32i_cpu.v. Bug reports are welcome (Send comments to yamin@hosei.ac.jp).

Exercise

Design an rv32m.v and add it to riscv_rv32i_cpu.v so that you can have a riscv_rv32im_cpu.v.