lb命令実装
require_relative './hsim' | |
require_relative './riscvasm' | |
# 外付けRAM | |
class RAM | |
attr_reader :memory | |
def initialize(a, d) | |
@memory = Array.new(80*30){0} | |
@o = Array.new(d){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 update | |
# クロックがHになったら処理する | |
if @clk.get | |
address = ("0b" + @addr.map{|o|o.get ? "1" : "0"}.join).to_i(2) | |
if @w.get # RAMへの書き込み | |
data = ("0b" + @data.map{|o|o.get ? "1" : "0"}.join).to_i(2) | |
@memory[address] = data | |
end | |
if @r.get # RAMからの読み込み | |
memory = @memory[address].to_s(2).rjust(@o.size, "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, 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(5, 32) # アドレス線5本、データ線32本 | |
# 32アドレスぶんの命令 | |
rom.d = RISCVASM.asm do | |
addi r2, r0, 72 | |
sb r2, 0, r0 | |
addi r2, r0, 101 | |
sb r2, 1, r0 | |
addi r2, r0, 108 | |
sb r2, 2, r0 | |
addi r2, r0, 108 | |
sb r2, 3, r0 | |
addi r2, r0, 111 | |
sb r2, 4, r0 | |
addi r2, r0, 32 | |
sb r2, 5, r0 | |
addi r2, r0, 119 | |
sb r2, 6, r0 | |
addi r2, r0, 111 | |
sb r2, 7, r0 | |
addi r2, r0, 114 | |
sb r2, 8, r0 | |
addi r2, r0, 108 | |
sb r2, 9, r0 | |
addi r2, r0, 100 | |
sb r2, 10, r0 | |
addi r2, r0, 33 | |
sb r2, 11, r0 | |
lb r2, 0, r0 | |
sb r2, 80, r0 | |
lb r2, 1, r0 | |
sb r2, 81, r0 | |
lb r2, 2, r0 | |
sb r2, 82, r0 | |
lb r2, 3, r0 | |
sb r2, 83, r0 | |
end.map{|t|_to_pin(t, vcc, gnd)}.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_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_J_Type = [insn_bit_imm20_1[0]]*11 + insn_bit_imm20_1 + [gnd] | |
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 | |
# デコード用ビットパターン | |
opcode_addi = _to_pin("0010011", vcc, gnd) | |
opcode_add = _to_pin("0110011", vcc, gnd) | |
opcode_jalr = _to_pin("1100111", vcc, gnd) | |
opcode_jal = _to_pin("1101111", vcc, gnd) | |
opcode_sb = _to_pin("0100011", vcc, gnd) | |
opcode_lb = _to_pin("0000011", vcc, gnd) | |
funct3_addi = _to_pin("000" , vcc, gnd) | |
funct3_add = _to_pin("000" , vcc, gnd) | |
funct3_jalr = _to_pin("000" , vcc, gnd) | |
funct3_sb = _to_pin("000" , vcc, gnd) | |
funct3_lb = _to_pin("000" , vcc, gnd) | |
funct7_add = _to_pin("0000000", vcc, gnd) | |
# addi命令でH | |
insn_addi = _equal(insn_bit_opcode, opcode_addi) & _equal(insn_bit_funct3, funct3_addi) | |
# add命令でH | |
insn_add = _equal(insn_bit_opcode, opcode_add) & _equal(insn_bit_funct3, funct3_add) & _equal(insn_bit_funct7, funct7_add) | |
# 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) | |
# sb命令でH | |
insn_sb = _equal(insn_bit_opcode, opcode_sb) | |
# lb命令でH | |
insn_lb = _equal(insn_bit_opcode, opcode_lb) | |
# レジスタ書き込み条件 | |
write_reg = insn_addi | insn_add | insn_jalr | insn_jal | insn_lb | |
## 各種回路ブロック生成 | |
# 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 | |
# 32bit加算器 | |
adder = Adder.new(32) | |
# 回路停止用1bitレジスタ(Hの時に停止) | |
stall = Register.new(1) | |
stall.clk = clk.o | |
stall.clrb = clrb.o | |
stall.w = vcc | |
st = ~stall.o[0] & insn_lb # 実行状態かつlb命令のときにHになる(次のクロックですぐLに戻る) | |
stall.d = [st] | |
# でっちあげRAM | |
ram = RAM.new(8, 8) | |
ram.a = adder.o[24..31] | |
ram.d = [gnd]*24 + rs2[24..31] | |
ram.clk = clk.o | |
ram.w = insn_sb | |
ram.r = st | |
## 回路接続 | |
# ROMの出力はThreeStateBufferなのでBusオブジェクトにaddする | |
bus.add rom.o | |
# ROMの出力の値はBusオブジェクトから取り出す | |
# pcとインクリメンタ接続 | |
pc.d = _if(insn_jalr | insn_jal, adder.o, inc.o + [gnd, gnd]) | |
inc.d = pc.o[0, 30] | |
# pcの書き込み条件 | |
pc.w = ~st | |
# ROMにPCを接続 | |
rom.a = pc.o[25, 5] | |
rom.csb = gnd | |
# レジスタファイルの入力ポート接続 | |
regf.d = _if(insn_jalr | insn_jal, inc.o + [gnd, gnd], # jalr or jalの場合はrdに戻りアドレスを格納する | |
_if(insn_lb, [gnd]*24 + ram.o, # lb命令の場合はramの出力 | |
adder.o # それ以外の命令は加算器の結果を格納する | |
)) | |
regf.sin = insn_bit_rd # 書き込むレジスタはRISC-Vの場合は必ずrdになる | |
regf.w = write_reg & ~st # レジスタ書き込み条件 | |
# レジスタファイルの出力ポート選択信号の接続 | |
regf.sout0 = insn_bit_rs1 | |
regf.sout1 = insn_bit_rs2 | |
# 32bit全加算器 | |
adder.d = [_if(insn_jal, pc.o, # jal命令 | |
rs1 # それ以外 | |
), | |
_if(insn_jal, imm_J_Type, # jal命令 | |
_if(insn_add, rs2, # add命令 | |
_if(insn_sb, imm_S_Type, # sb命令 | |
imm_I_Type # それ以外 | |
))) | |
] | |
adder.x = gnd | |
# 電源接続 | |
_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