RV32I基本命令実装
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_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