-
-
Save mirichi/2c12f858217f9fd250bc3cd1e603870a to your computer and use it in GitHub Desktop.
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