Hello world!
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} | |
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 | |
end | |
def o | |
@o | |
end | |
def update | |
# クロックがHになったら書き込む | |
if @clk.get and @w.get | |
address = ("0b" + @addr.map{|o|o.get ? "1" : "0"}.join).to_i(2) | |
data = ("0b" + @data.map{|o|o.get ? "1" : "0"}.join).to_i(2) | |
@memory[address] = data | |
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 | |
label :test | |
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 | |
jal r0, 0 | |
nop | |
nop | |
nop | |
nop | |
nop | |
nop | |
nop | |
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) | |
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) | |
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) | |
# レジスタ書き込み条件 | |
write_reg = insn_addi | insn_add | insn_jalr | insn_jal | |
## 各種回路ブロック生成 | |
# 32bitプログラムカウンタ用レジスタ | |
pc = Register.new(32) | |
pc.clk = clk.o | |
pc.clrb = clrb.o | |
pc.w = vcc | |
# 30bitインクリメンタ | |
inc = Incrementer.new(30) | |
# レジスタファイル(32bitが32個) | |
regf = RegisterFile.new(5, 32) | |
regf.clk = clk.o | |
regf.clrb = clrb.o | |
# 32bit加算器 | |
adder = Adder.new(32) | |
# でっちあげRAM | |
ram = RAM.new(8, 8) | |
ram.a = adder.o[24..31] | |
ram.d = [gnd]*24 + regf.o1[24..31] | |
ram.clk = clk.o | |
ram.w = insn_sb | |
## 回路接続 | |
# 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] | |
# ROMにPCを接続 | |
rom.a = pc.o[25, 5] | |
rom.csb = gnd | |
# レジスタファイルの入力ポート接続 | |
regf.d = _if(insn_jalr | insn_jal, inc.o + [gnd, gnd], adder.o) | |
regf.sin = insn_bit_rd | |
regf.w = write_reg # レジスタ書き込み条件 | |
# レジスタファイルの出力ポート選択信号の接続 | |
regf.sout0 = insn_bit_rs1 | |
regf.sout1 = insn_bit_rs2 | |
# 32bit全加算器 | |
adder.d = [_if(insn_jal, pc.o, # jal命令 | |
regf.o0 # それ以外 | |
), | |
_if(insn_jal, imm_J_Type, # jal命令 | |
_if(insn_add, regf.o1, # 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 | |
60.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