Skip to content

Instantly share code, notes, and snippets.

@mirichi

mirichi/riscv-hsim.rb Secret

Created Jan 5, 2018
Embed
What would you like to do?
RV32I基本命令実装
require_relative './hsim'
require_relative './riscvasm'
# ALU
class ALU
def initialize(dsize)
h = OutputPin.new(H)
l = OutputPin.new(L)
@o = Array.new(dsize){OutputPin.new}
@buf_add = Buffer1.new
@buf_sub = Buffer1.new
@buf_sll = Buffer1.new
@buf_slt = Buffer1.new
@buf_sltu = Buffer1.new
@buf_xor = Buffer1.new
@buf_srl = Buffer1.new
@buf_sra = Buffer1.new
@buf_or = Buffer1.new
@buf_and = Buffer1.new
# 入力データ
@buf_d1 = Buffer.new(dsize)
@buf_d0 = Buffer.new(dsize)
# 加算器
@adder = Adder.new(dsize)
@adder.d = [@buf_d1.o, @buf_d0.o]
@adder.x = l
# 減算器(加算器と共有できると思うが面倒だったので)
@not = NOTn.new(dsize)
@subtractor = Adder.new(dsize)
@not.a = @buf_d1.o
@subtractor.d = [@not.o, @buf_d0.o]
@subtractor.x = OutputPin.new(H)
# and
@and = ANDn.new(dsize)
@and.a = @buf_d1.o
@and.b = @buf_d0.o
# or
@or = ORn.new(dsize)
@or.a = @buf_d1.o
@or.b = @buf_d0.o
# xor
@xor = XORn.new(dsize)
@xor.a = @buf_d1.o
@xor.b = @buf_d0.o
# バレルシフタ
ssize = Math.log2(dsize).to_i
@shifter = BarrelShifter.new(ssize, dsize)
@shifter.d = @buf_d0.o
@shifter.shift = @buf_d1.o[-ssize..-1]
@shifter.sign = _if(@buf_sra.o, [@buf_d0.o[0]], [l])[0]
@shifter.direction = ~@buf_sll.o
# 出力
@o = _if(@buf_add.o, @adder.o,
_if(@buf_sub.o, @subtractor.o,
_if(@buf_sltu.o, [l]*(dsize-1) + [~@subtractor.c],
_if(@buf_slt.o, [l]*(dsize-1) + _if(@buf_d0.o[0] & ~@buf_d1.o[0], [h],
_if(~@buf_d0.o[0] & @buf_d1.o[0], [l],
[~@subtractor.c]
)),
_if(@buf_and.o, @and.o,
_if(@buf_or.o, @or.o,
_if(@buf_xor.o, @xor.o,
_if(@buf_sll.o | @buf_srl.o | @buf_sra.o, @shifter.o,
[l]*dsize
))))))))
end
def d=(v)
@buf_d1.d = v[0]
@buf_d0.d = v[1]
end
def o
@o
end
def add=(v)
@buf_add.d = v
end
def sub=(v)
@buf_sub.d = v
end
def sll=(v)
@buf_sll.d = v
end
def slt=(v)
@buf_slt.d = v
end
def sltu=(v)
@buf_sltu.d = v
end
def xor=(v)
@buf_xor.d = v
end
def srl=(v)
@buf_srl.d = v
end
def sra=(v)
@buf_sra.d = v
end
def or=(v)
@buf_or.d = v
end
def and=(v)
@buf_and.d = v
end
end
# 外付けRAM
class RAM
attr_reader :memory
def initialize(a)
@memory = Array.new(80*30){0}
@o = Array.new(32){OutputPin.new}
@o.each{|t|t.set(false)}
end
def clk=(v)
v.output_objects << self
@clk = v
end
def a=(v)
@addr = v
end
def d=(v)
@data = v
end
def w=(v)
@w = v # HがWrite有効
end
def r=(v)
@r = v # HがRead有効
end
def o
@o
end
def size1=(v)
@size1 = v
end
def size2=(v)
@size2 = v
end
def size4=(v)
@size4 = v
end
def update
# クロックがHになったら処理する
if @clk.get
address = ("0b" + @addr.map{|o|o.get ? "1" : "0"}.join).to_i(2)
if @w.get # RAMへの書き込み
if @size4.get
@memory[address ] = ("0b" + @data[24..31].map{|o|o.get ? "1" : "0"}.join).to_i(2)
@memory[address+1] = ("0b" + @data[16..23].map{|o|o.get ? "1" : "0"}.join).to_i(2)
@memory[address+2] = ("0b" + @data[ 8..15].map{|o|o.get ? "1" : "0"}.join).to_i(2)
@memory[address+3] = ("0b" + @data[ 0.. 7].map{|o|o.get ? "1" : "0"}.join).to_i(2)
elsif @size2.get
@memory[address ] = ("0b" + @data[24..31].map{|o|o.get ? "1" : "0"}.join).to_i(2)
@memory[address+1] = ("0b" + @data[16..23].map{|o|o.get ? "1" : "0"}.join).to_i(2)
elsif @size1.get
@memory[address ] = ("0b" + @data[24..31].map{|o|o.get ? "1" : "0"}.join).to_i(2)
end
end
if @r.get # RAMからの読み込み
memory = @memory[address+3].to_s(2).rjust(8, "0") +
@memory[address+2].to_s(2).rjust(8, "0") +
@memory[address+1].to_s(2).rjust(8, "0") +
@memory[address ].to_s(2).rjust(8, "0")
@o.each.with_index do |t, i|
t.set(memory[i] == "1")
end
end
end
end
end
def _if(cond, t, f)
raise "size error" if t.size != f.size
mux = Mux1_n.new(t.size)
mux.s = cond
mux.d = [t, f]
mux.o
end
def _equal(a, b)
raize "size error" if a.size != b.size
ary = []
a.zip(b) do |p1, p2|
ary << (p1 ^ p2)
end
return ~ary.inject(ary.pop){|memo, item|memo | item}
end
def _to_pin(i)
h = OutputPin.new(H)
l = OutputPin.new(L)
i.to_s.each_byte.map{|s|(s-0x30) == 1 ? h : l}
end
# 電源とグランド
_vcc = Line.new
_gnd = Line.new
vcc = _vcc.o
gnd = _gnd.o
# クロックとクリアの入力線
clk = Line.new
clrb = Line.new
# 32bitデータバス
bus = Bus.new(32)
# ROM
rom = ROM.new(6, 32) # アドレス線6本、データ線32本
# 命令列
rom.d = RISCVASM.asm(64) do
addi x4, x0, 65
auipc x5, 0
add x3, x4, x5
sb x3, 0, x0
end.map{|t|_to_pin(t)}.reverse
## 命令デコーダ(RISC-V/RV32I)
insn = bus.o
insn_bit_funct7 = insn[0..6]
insn_bit_imm11_5 = insn[0..6]
insn_bit_imm4_0 = insn[20..24]
insn_bit_imm11_0 = insn[0..11]
insn_bit_imm20_1 = [insn[0]] + insn[12..19] + [insn[11]] + insn[1..10]
insn_bit_imm12_1 = [insn[0]] + [insn[24]] + insn[1..6] + insn[20..23]
insn_bit_imm31_12 = insn[0..19]
insn_bit_rs2 = insn[7..11]
insn_bit_rs1 = insn[12..16]
insn_bit_funct3 = insn[17..19]
insn_bit_rd = insn[20..24]
insn_bit_opcode = insn[25..31]
# 即値
imm_I_Type = [insn_bit_imm11_0[0]]*20 + insn_bit_imm11_0
imm_S_Type = [insn_bit_imm11_5[0]]*20 + insn_bit_imm11_5 + insn_bit_imm4_0
imm_B_Type = [insn_bit_imm12_1[0]]*19 + insn_bit_imm12_1 + [gnd]
imm_U_Type = insn_bit_imm31_12 + [gnd]*12
imm_J_Type = [insn_bit_imm20_1[0]]*11 + insn_bit_imm20_1 + [gnd]
# デコード用ビットパターン
opcode_jalr = _to_pin("1100111")
opcode_jal = _to_pin("1101111")
opcode_load = _to_pin("0000011")
opcode_store = _to_pin("0100011")
opcode_opimm = _to_pin("0010011")
opcode_op = _to_pin("0110011")
opcode_lui = _to_pin("0110111")
opcode_auipc = _to_pin("0010111")
opcode_branch = _to_pin("1100011")
funct3_jalr = _to_pin("000")
funct3_lb = _to_pin("000")
funct3_lh = _to_pin("001")
funct3_lw = _to_pin("010")
funct3_lbu = _to_pin("100")
funct3_lhu = _to_pin("101")
funct3_sb = _to_pin("000")
funct3_sh = _to_pin("001")
funct3_sw = _to_pin("010")
# 演算系funct3(sub以外はi付きと共通)
funct3_add = _to_pin("000")
funct3_sub = _to_pin("000")
funct3_sll = _to_pin("001")
funct3_slt = _to_pin("010")
funct3_sltu = _to_pin("011")
funct3_xor = _to_pin("100")
funct3_srl = _to_pin("101")
funct3_sra = _to_pin("101")
funct3_or = _to_pin("110")
funct3_and = _to_pin("111")
# 条件分岐系funct3
funct3_beq = _to_pin("000")
funct3_bne = _to_pin("001")
funct3_blt = _to_pin("100")
funct3_bge = _to_pin("101")
funct3_bltu = _to_pin("110")
funct3_bgeu = _to_pin("111")
funct7_add = _to_pin("0000000")
funct7_srl = _to_pin("0000000")
funct7_sub = _to_pin("0100000")
funct7_sra = _to_pin("0100000")
# OP_IMM系命令でH
insn_opimm = _equal(insn_bit_opcode, opcode_opimm)
insn_addi = insn_opimm & _equal(insn_bit_funct3, funct3_add)
insn_slli = insn_opimm & _equal(insn_bit_funct3, funct3_sll)
insn_slti = insn_opimm & _equal(insn_bit_funct3, funct3_slt)
insn_sltui = insn_opimm & _equal(insn_bit_funct3, funct3_sltu)
insn_xori = insn_opimm & _equal(insn_bit_funct3, funct3_xor)
insn_srli = insn_opimm & _equal(insn_bit_funct3, funct3_srl) & _equal(insn_bit_funct7, funct7_srl)
insn_srai = insn_opimm & _equal(insn_bit_funct3, funct3_sra) & _equal(insn_bit_funct7, funct7_sra)
insn_ori = insn_opimm & _equal(insn_bit_funct3, funct3_or)
insn_andi = insn_opimm & _equal(insn_bit_funct3, funct3_and)
# OP系命令でH
insn_op = _equal(insn_bit_opcode, opcode_op)
insn_add = insn_op & _equal(insn_bit_funct3, funct3_add) & _equal(insn_bit_funct7, funct7_add)
insn_sub = insn_op & _equal(insn_bit_funct3, funct3_sub) & _equal(insn_bit_funct7, funct7_sub)
insn_sll = insn_op & _equal(insn_bit_funct3, funct3_sll)
insn_slt = insn_op & _equal(insn_bit_funct3, funct3_slt)
insn_sltu = insn_op & _equal(insn_bit_funct3, funct3_sltu)
insn_xor = insn_op & _equal(insn_bit_funct3, funct3_xor)
insn_srl = insn_op & _equal(insn_bit_funct3, funct3_srl) & _equal(insn_bit_funct7, funct7_srl)
insn_sra = insn_op & _equal(insn_bit_funct3, funct3_sra) & _equal(insn_bit_funct7, funct7_sra)
insn_or = insn_op & _equal(insn_bit_funct3, funct3_or)
insn_and = insn_op & _equal(insn_bit_funct3, funct3_and)
# jalr命令でH
insn_jalr = _equal(insn_bit_opcode, opcode_jalr) & _equal(insn_bit_funct3, funct3_jalr)
# jal命令でH
insn_jal = _equal(insn_bit_opcode, opcode_jal)
# 各ロード命令でH
insn_load = _equal(insn_bit_opcode, opcode_load)
insn_lb = insn_load & _equal(insn_bit_funct3, funct3_lb)
insn_lh = insn_load & _equal(insn_bit_funct3, funct3_lh)
insn_lw = insn_load & _equal(insn_bit_funct3, funct3_lw)
insn_lbu = insn_load & _equal(insn_bit_funct3, funct3_lbu)
insn_lhu = insn_load & _equal(insn_bit_funct3, funct3_lhu)
# 各ストア命令でH
insn_store = _equal(insn_bit_opcode, opcode_store)
insn_sb = insn_store & _equal(insn_bit_funct3, funct3_sb)
insn_sh = insn_store & _equal(insn_bit_funct3, funct3_sh)
insn_sw = insn_store & _equal(insn_bit_funct3, funct3_sw)
# lui命令でH
insn_lui = _equal(insn_bit_opcode, opcode_lui)
# auipc命令でH
insn_auipc = _equal(insn_bit_opcode, opcode_auipc)
# 各条件分岐命令でH
insn_branch = _equal(insn_bit_opcode, opcode_branch)
insn_beq = insn_branch & _equal(insn_bit_funct3, funct3_beq)
insn_bne = insn_branch & _equal(insn_bit_funct3, funct3_bne)
insn_blt = insn_branch & _equal(insn_bit_funct3, funct3_blt)
insn_bge = insn_branch & _equal(insn_bit_funct3, funct3_bge)
insn_bltu = insn_branch & _equal(insn_bit_funct3, funct3_bltu)
insn_bgeu = insn_branch & _equal(insn_bit_funct3, funct3_bgeu)
# レジスタ書き込み条件
write_reg = insn_opimm | insn_op | insn_jalr | insn_jal | insn_load | insn_lui | insn_auipc
## 各種回路ブロック生成
# 32bitプログラムカウンタ用レジスタ
pc = Register.new(32)
pc.clk = clk.o
pc.clrb = clrb.o
# 30bitインクリメンタ
inc = Incrementer.new(30)
# レジスタファイル(32bitが32個)
regf = RegisterFile.new(5, 32)
regf.clk = clk.o
regf.clrb = clrb.o
rs1 = regf.o0
rs2 = regf.o1
# 32bitALU
alu = ALU.new(32)
# 回路停止用1bitレジスタ(Hの時に停止)
stall = Register.new(1)
stall.clk = clk.o
stall.clrb = clrb.o
stall.w = vcc
st = ~stall.o[0] & insn_load # 実行状態かつロード命令のときにHになる(次のクロックですぐLに戻る)
stall.d = [st]
# でっちあげRAM
ram = RAM.new(16)
ram.a = alu.o[16..31]
ram.d = rs2
ram.clk = clk.o
ram.w = insn_store
ram.r = st
ram.size1 = insn_sb
ram.size2 = insn_sh
ram.size4 = insn_sw
ram_read_data = _if(insn_lb, [ram.o[24]]*24 + ram.o[24..31],
_if(insn_lh, [ram.o[16]]*16 + ram.o[16..31],
_if(insn_lbu, [gnd]*24 + ram.o[24..31],
_if(insn_lhu, [gnd]*16 + ram.o[16..31],
ram.o
))))
# 条件分岐用の減算器
_not = NOTn.new(32)
subtractor = Adder.new(32)
_not.a = rs2
subtractor.d = [_not.o, rs1]
subtractor.x = vcc
# 条件分岐用の比較器
eq = _equal(rs1, rs2)
lt = _if(rs1[0] & ~rs2[0], [vcc],
_if(~rs1[0] & rs2[0], [gnd],
[~subtractor.c]
))[0]
# 分岐する場合にH
branch = _if(insn_beq, [eq],
_if(insn_bne, [~eq],
_if(insn_blt, [lt],
_if(insn_bge, [~lt],
_if(insn_bltu, [~subtractor.c],
_if(insn_bgeu, [subtractor.c],
[gnd]
))))))[0]
## 回路接続
# ROMの出力はThreeStateBufferなのでBusオブジェクトにaddする
bus.add rom.o
# ROMの出力の値はBusオブジェクトから取り出す
# pcとインクリメンタ接続
pc.d = _if(insn_jalr | insn_jal | branch, alu.o, inc.o + [gnd, gnd])
inc.d = pc.o[0, 30]
# pcの書き込み条件
pc.w = ~st
# ROMにPCを接続
rom.a = pc.o[24, 6]
rom.csb = gnd
# レジスタファイルの入力ポート接続
regf.d = _if(insn_jalr | insn_jal, inc.o + [gnd, gnd], # jalr or jalの場合はrdに戻りアドレスを格納する
_if(insn_load, ram_read_data, # ロード命令の場合はramの出力
_if(insn_lui, imm_U_Type, # lui命令の場合は即値
alu.o # それ以外の命令はALUの結果を格納する
)))
regf.sin = insn_bit_rd # 書き込むレジスタはRISC-Vの場合は必ずrdになる
regf.w = write_reg & ~st # レジスタ書き込み条件
# レジスタファイルの出力ポート選択信号の接続
regf.sout0 = insn_bit_rs1
regf.sout1 = insn_bit_rs2
# 32bitALU接続
alu.d = [_if(insn_jal, imm_J_Type, # jal命令
_if(insn_op, rs2, # op系命令
_if(insn_slli | insn_srli | insn_srai, [gnd]*27 + insn_bit_rs2, # shamt使う系命令
_if(insn_branch, imm_B_Type, # 条件分岐系命令
_if(insn_store, imm_S_Type, # ストア命令
_if(insn_auipc, imm_U_Type, # auipc命令
imm_I_Type # それ以外
)))))),
_if(insn_jal | insn_auipc | insn_branch, pc.o, # jal/auipc/条件分岐命令
rs1 # それ以外
)
]
alu.add = insn_jal | insn_jalr | insn_add | insn_addi | insn_load | insn_store | insn_auipc | insn_branch
alu.sub = insn_sub
alu.and = insn_and | insn_andi
alu.or = insn_or | insn_ori
alu.xor = insn_xor | insn_xori
alu.sll = insn_sll | insn_slli
alu.srl = insn_srl | insn_srli
alu.sra = insn_sra | insn_srai
alu.slt = insn_slt | insn_slti
alu.sltu = insn_sltu | insn_sltui
# 電源接続
_vcc.d = H
_gnd.d = L
# 簡易テキストVRAM
require 'dxruby'
clk.d = L
clrb.d = H
clrb.d = L
clrb.d = H
36.times do
clk.d = L
clk.d = H
end
font = Font.new(16, "MS ゴシック")
Window.loop do
30.times do |y|
80.times do |x|
Window.draw_font(x*8, y*16, ram.memory[x+y*80].chr(Encoding::WINDOWS_31J), font)
end
end
break if Input.key_push?(K_ESCAPE)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment