Last active
December 11, 2021 15:02
-
-
Save thata/1e1ffbceb604353fdea0ec493a1d47eb to your computer and use it in GitHub Desktop.
rv32sim: RISC-V(RV32) subset 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
require "./rv32sim" | |
def _add(rd, rs1, rs2) | |
0b0110011 | | |
(rd << 7) | | |
(0x0 << 12) | | |
(rs1 << 15) | | |
(rs2 << 20) | | |
(0x00 << 25) | |
end | |
def _sub(rd, rs1, rs2) | |
0b0110011 | | |
(rd << 7) | | |
(0x0 << 12) | | |
(rs1 << 15) | | |
(rs2 << 20) | | |
(0x20 << 25) | |
end | |
def _or(rd, rs1, rs2) | |
0b0110011 | | |
(rd << 7) | | |
(0x6 << 12) | | |
(rs1 << 15) | | |
(rs2 << 20) | | |
(0x00 << 25) | |
end | |
def _and(rd, rs1, rs2) | |
0b0110011 | | |
(rd << 7) | | |
(0x7 << 12) | | |
(rs1 << 15) | | |
(rs2 << 20) | | |
(0x00 << 25) | |
end | |
def _addi(rd, rs1, imm) | |
0b0010011 | | |
(rd << 7) | | |
(0x0 << 12) | | |
(rs1 << 15) | | |
(imm << 20) | |
end | |
def _nop | |
_addi(0, 0, 0) | |
end | |
def _beq(rs1, rs2, imm) | |
imm1_4 = (imm & 0b0_0000_0001_1110) >> 1 | |
imm5_10 = (imm & 0b0_0111_1110_0000) >> 5 | |
imm11 = (imm & 0b0_1000_0000_0000) >> 11 | |
imm12 = (imm & 0b1_0000_0000_0000) >> 12 | |
0b1100011 | | |
(imm11 << 7) | | |
(imm1_4 << 8) | | |
(0x0 << 12) | | |
(rs1 << 15) | | |
(rs2 << 20) | | |
(imm5_10 << 25) | | |
(imm12 << 31) | |
end | |
cpu = Cpu.new | |
cpu.x_registers[1] = 10 | |
cpu.x_registers[2] = 20 | |
cpu.x_registers[3] = 30 | |
# 命令メモリをセット | |
rom = [ | |
_add(4, 1, 2), # x4 = x1 + x2 | |
_add(5, 4, 3), # x5 = x4 + x3 | |
].pack("V*") | |
cpu.init_inst_memory(rom) | |
# 実行前 | |
puts cpu.pc | |
#=> 0 | |
puts cpu.x_registers[4] | |
#=> 0 | |
puts cpu.x_registers[5] | |
#=> 0 | |
cpu.run # 1つめの命令を実行 | |
cpu.run # 2つめの命令を実行 | |
# 実行後 | |
puts cpu.pc | |
#=> 8 | |
puts cpu.x_registers[4] | |
#=> 30 | |
puts cpu.x_registers[5] | |
#=> 60 |
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
require "./rv32sim" | |
decoder = Decoder.new | |
decoder.decode(0b1000001_10111_10011_101_10001_0110011) | |
printf "%07b\n", decoder.opcode | |
#=> 0110011 | |
printf "%05b\n", decoder.rd | |
#=> 10001 | |
printf "%03b\n", decoder.funct3 | |
#=> 101 | |
printf "%05b\n", decoder.rs1 | |
#=> 10011 | |
printf "%05b\n", decoder.rs2 | |
#=> 10111 | |
printf "%07b\n", decoder.funct7 | |
#=> 1000001 |
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
require "./rv32sim" | |
memory = Memory.new("\x93\x00\x01\x01\x94\x00\x01\x01") | |
printf "%08x\n", memory.read(0) | |
#=> 01010093 | |
printf "%08x\n", memory.read(4) | |
#=> 01010094 | |
p memory.read(8) |
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) | |
@data = data | |
end | |
def read(addr) | |
word = @data.slice(addr, WORD_SIZE) | |
# RISC-Vはリトルエンディアンなので、「V = little endian unsigned 32bit」でメモリの内容を読み込む | |
word.unpack("V").first | |
end | |
end | |
class Decoder | |
attr_reader :opcode, :rd, :funct3, :rs1, :rs2, :funct7, :i_imm, :s_imm, :b_imm | |
def initialize | |
@opcode = @rd = @funct3 = @rs1 = @rs2 = @funct7 = nil | |
@i_imm = @s_imm = @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 = (opcode == 0b0110011) ? ((inst & 0xfe000000) >> 25) : nil | |
@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 | |
end | |
class Cpu | |
class EmptyInstractionError < RuntimeError; end | |
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 | |
} | |
attr_accessor :pc | |
attr_reader :x_registers | |
def initialize | |
@pc = 0 # プログラムカウンタ | |
@x_registers = [0] * 32 # レジスタ | |
@inst_memory = Memory.new # 命令メモリ | |
@decoder = Decoder.new | |
end | |
def init_inst_memory(data) | |
@inst_memory.data = data | |
end | |
def run | |
inst = fetch | |
# 命令メモリの範囲外に来たら false を返して処理を終える | |
return false unless inst | |
decode(inst) | |
execute | |
true | |
end | |
def fetch | |
@inst_memory.read(@pc) | |
end | |
def decode(inst) | |
@decoder.decode(inst) | |
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 | |
end | |
class Simulator | |
def initialize | |
@cpu = Cpu.new | |
end | |
def init_inst_memory(data) | |
@cpu.init_inst_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 |
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
require "./rv32sim" | |
def _add(rd, rs1, rs2) | |
0b0110011 | | |
(rd << 7) | | |
(0x0 << 12) | | |
(rs1 << 15) | | |
(rs2 << 20) | | |
(0x00 << 25) | |
end | |
def _sub(rd, rs1, rs2) | |
0b0110011 | | |
(rd << 7) | | |
(0x0 << 12) | | |
(rs1 << 15) | | |
(rs2 << 20) | | |
(0x20 << 25) | |
end | |
def _or(rd, rs1, rs2) | |
0b0110011 | | |
(rd << 7) | | |
(0x6 << 12) | | |
(rs1 << 15) | | |
(rs2 << 20) | | |
(0x00 << 25) | |
end | |
def _and(rd, rs1, rs2) | |
0b0110011 | | |
(rd << 7) | | |
(0x7 << 12) | | |
(rs1 << 15) | | |
(rs2 << 20) | | |
(0x00 << 25) | |
end | |
def _addi(rd, rs1, imm) | |
0b0010011 | | |
(rd << 7) | | |
(0x0 << 12) | | |
(rs1 << 15) | | |
(imm << 20) | |
end | |
def _nop | |
_addi(0, 0, 0) | |
end | |
def _beq(rs1, rs2, imm) | |
imm1_4 = (imm & 0b0_0000_0001_1110) >> 1 | |
imm5_10 = (imm & 0b0_0111_1110_0000) >> 5 | |
imm11 = (imm & 0b0_1000_0000_0000) >> 11 | |
imm12 = (imm & 0b1_0000_0000_0000) >> 12 | |
0b1100011 | | |
(imm11 << 7) | | |
(imm1_4 << 8) | | |
(0x0 << 12) | | |
(rs1 << 15) | | |
(rs2 << 20) | | |
(imm5_10 << 25) | | |
(imm12 << 31) | |
end | |
# 命令メモリ | |
rom = [ | |
_addi(1, 0, 10), | |
_addi(2, 0, 20), | |
_addi(3, 0, 30), | |
_add(4, 1, 2), # x4 = x1 + x2 | |
_add(5, 4, 3), # x5 = x4 + x3 | |
].pack("V*") | |
sim = Simulator.new | |
sim.init_inst_memory(rom) | |
sim.start | |
sim.dump_registers |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment