Last active
June 3, 2022 08:51
-
-
Save thata/1d0aa0dc3d177035f7f973d5f12e6a80 to your computer and use it in GitHub Desktop.
rv32sim - One file RISC-V simulator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# fibonacci for RV32 | |
# | |
# $ riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -Wl,-Ttext=0x00 -nostdlib -o fibonacci fibonacci.S | |
# $ riscv64-unknown-elf-objcopy -O binary fibonacci fibonacci.rom | |
# $ ruby rv32sim.rb fibonacci.rom | |
.text | |
.globl _start | |
.type _start, @function | |
_start: | |
addi x5, x0, 10 # n = 10 | |
addi x4, x0, 0 # i = 0 | |
addi x1, x0, 0 # f(i) = 0 | |
addi x2, x0, 1 # f(i+1) = 1 | |
loop: | |
beq x4, x5, break # goto break if i == n | |
add x3, x1, x2 # temp = f(i) + f(i+1) | |
add x1, x2, x0 # f(i) = f(i+1) | |
add x2, x3, x0 # f(i+1) = temp | |
addi x4, x4, 1 # i = i + 1 | |
beq x0, x0, loop # goto loop | |
break: | |
addi x6, x0, 0x100 | |
sw x1, 0(x6) # メモリの0x100番地へfib(10)の結果を格納 | |
nop | |
nop | |
nop | |
nop | |
nop |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Memory | |
WORD_SIZE = 4 | |
attr_accessor :data | |
def initialize(data = nil) | |
# バイナリデータとして扱いたいので ASCII-8BIT エンコーディングへ変換 | |
@data = data.b | |
end | |
# 指定したアドレスから1ワード(4バイト)のデータを読み込む | |
def read(addr) | |
word = @data.slice(addr, WORD_SIZE) | |
# 「signed int32」でメモリの内容を読み込む | |
# see: https://docs.ruby-lang.org/ja/latest/doc/pack_template.html | |
word.unpack1("l") | |
end | |
# 指定したアドレスへ1ワード(4バイト)のデータを書き込む | |
def write(addr, word) | |
# 「signed int32」でメモリへ書き込む | |
@data[addr, 4] = [word].pack("l") | |
end | |
end | |
class Decoder | |
attr_reader :opcode, :rd, :funct3, :rs1, :rs2, :funct7, :i_imm, :s_imm, :b_imm | |
def initialize | |
@opcode = nil | |
@rd = nil | |
@funct3 = nil | |
@rs1 = nil | |
@rs2 = nil | |
@funct7 = nil | |
@i_imm = nil | |
@s_imm = nil | |
@b_imm = nil | |
end | |
def decode(inst) | |
@opcode = (inst & 0x0000007f) | |
@rd = (inst & 0x00000f80) >> 7 | |
@funct3 = (inst & 0x00007000) >> 12 | |
@rs1 = (inst & 0x000f8000) >> 15 | |
@rs2 = (inst & 0x01f00000) >> 20 | |
@funct7 = if opcode == 0b0110011 | |
(inst & 0xfe000000) >> 25 | |
else | |
nil | |
end | |
@i_imm = (inst & 0xfff00000) >> 20 | |
@s_imm = ((inst & 0xfe000000) >> 20) | ((inst & 0x00000f80) >> 7) | |
@b_imm = ((inst & 0x80000000) >> 19) | | |
((inst & 0x00000080) << 4) | | |
((inst & 0x7e000000) >> 20) | | |
((inst & 0x00000f00) >> 7) | |
end | |
# nop命令(no operation 何も行わない命令)かどうかを判定(あとで使う) | |
def nop? | |
@opcode == 0b0010011 && | |
@funct3 == 0 && | |
@rd == 0 && | |
@rs1 == 0 && | |
@i_imm == 0 | |
end | |
end | |
class Cpu | |
INST_TABLE = { | |
[0b0110011, 0x0, 0x00] => :_add, | |
[0b0110011, 0x0, 0x20] => :_sub, | |
[0b0110011, 0x6, 0x00] => :_or, | |
[0b0110011, 0x7, 0x00] => :_and, | |
[0b0010011, 0x0, nil] => :_addi, | |
[0b1100011, 0x0, nil] => :_beq, | |
[0b0000011, 0x2, nil] => :_lw, | |
[0b0100011, 0x2, nil] => :_sw | |
} | |
attr_accessor :pc | |
attr_reader :x_registers, :memory | |
def initialize | |
@pc = 0 # プログラムカウンタ | |
@x_registers = [0] * 32 # レジスタ | |
@decoder = Decoder.new | |
@memory = Memory.new( # メモリ | |
("\x00" * 512).b | |
) | |
@nop_count = 0 | |
end | |
def init_memory(data) | |
@memory.data[0, data.size] = data | |
end | |
def run | |
inst = fetch | |
decode(inst) | |
# NOPが5回来たら処理を終える | |
return false if @nop_count >= 5 | |
execute | |
true | |
end | |
def fetch | |
@memory.read(@pc) | |
end | |
def decode(inst) | |
@decoder.decode(inst) | |
if @decoder.nop? | |
@nop_count += 1 | |
else | |
@nop_count = 0 | |
end | |
end | |
def execute | |
key = [@decoder.opcode, @decoder.funct3, @decoder.funct7] | |
inst_symbol = INST_TABLE[key] | |
send inst_symbol | |
end | |
### Instructions | |
def _add | |
rd = @decoder.rd | |
rs1 = @decoder.rs1 | |
rs2 = @decoder.rs2 | |
@x_registers[rd] = @x_registers[rs1] + @x_registers[rs2] | |
@pc = @pc + 4 | |
end | |
def _sub | |
rd = @decoder.rd | |
rs1 = @decoder.rs1 | |
rs2 = @decoder.rs2 | |
@x_registers[rd] = @x_registers[rs1] - @x_registers[rs2] | |
@pc = @pc + 4 | |
end | |
def _or | |
rd = @decoder.rd | |
rs1 = @decoder.rs1 | |
rs2 = @decoder.rs2 | |
@x_registers[rd] = @x_registers[rs1] | @x_registers[rs2] | |
@pc = @pc + 4 | |
@x_registers[@rd] = @x_registers[@rs1] | @x_registers[@rs2] | |
@pc = @pc + 4 | |
end | |
def _and | |
rd = @decoder.rd | |
rs1 = @decoder.rs1 | |
rs2 = @decoder.rs2 | |
@x_registers[rd] = @x_registers[rs1] & @x_registers[rs2] | |
@pc = @pc + 4 | |
end | |
def _addi | |
rd = @decoder.rd | |
rs1 = @decoder.rs1 | |
i_imm = @decoder.i_imm | |
minus_flg = (i_imm & 0b100000000000) >> 11 | |
imm = if minus_flg == 1 | |
# TODO もっといい感じに書きたい | |
imm = (0b1000000000000 - i_imm) * -1 | |
else | |
imm = i_imm | |
end | |
@x_registers[rd] = @x_registers[rs1] + imm | |
@pc = @pc + 4 | |
end | |
def _beq | |
rd = @decoder.rd | |
rs1 = @decoder.rs1 | |
rs2 = @decoder.rs2 | |
b_imm = @decoder.b_imm | |
minus_flg = (b_imm & 0b1000000000000) >> 12 | |
imm = if minus_flg == 1 | |
# TODO もっといい感じに書きたい | |
imm = (0b10000000000000 - b_imm) * -1 | |
else | |
imm = b_imm | |
end | |
@pc = if @x_registers[rs1] == @x_registers[rs2] | |
@pc + imm | |
else | |
@pc + 4 | |
end | |
end | |
def _lw | |
rd = @decoder.rd | |
rs1 = @decoder.rs1 | |
imm = @decoder.i_imm | |
@x_registers[rd] = @memory.read(@x_registers[rs1] + imm) | |
@pc = @pc + 4 | |
end | |
def _sw | |
rs1 = @decoder.rs1 | |
rs2 = @decoder.rs2 | |
imm = @decoder.s_imm | |
@memory.write(@x_registers[rs1] + imm, @x_registers[rs2]) | |
@pc = @pc + 4 | |
end | |
end | |
class Simulator | |
def initialize | |
@cpu = Cpu.new | |
end | |
def init_memory(data) | |
@cpu.init_memory(data) | |
end | |
def start | |
loop do | |
@cpu.run || break | |
end | |
end | |
def dump_registers | |
puts "-" * 80 | |
for i in 0..7 | |
for j in 0..3 | |
print "\t" unless j == 0 | |
n = (i * 4) + j | |
print sprintf "x%02d = 0x%x (%d)", n, @cpu.x_registers[n], @cpu.x_registers[n] | |
end | |
print "\n" | |
end | |
puts "-" * 80 | |
puts sprintf "pc = 0x%x (%d)", @cpu.pc, @cpu.pc | |
end | |
end | |
if $0 == __FILE__ | |
sim = Simulator.new | |
mem = File.binread(ARGV.shift) | |
sim.init_memory(mem) | |
sim.start | |
sim.dump_registers | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment