Hello world!
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' | |
# 外付け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