Created
December 11, 2021 14:58
-
-
Save thata/91a959fbf1086c6c03255e0a8efd6a68 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