Skip to content

Instantly share code, notes, and snippets.

@thata
Last active December 11, 2021 15:02
Show Gist options
  • Save thata/1e1ffbceb604353fdea0ec493a1d47eb to your computer and use it in GitHub Desktop.
Save thata/1e1ffbceb604353fdea0ec493a1d47eb to your computer and use it in GitHub Desktop.
rv32sim: RISC-V(RV32) subset simulator
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
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
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)
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
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