Skip to content

Instantly share code, notes, and snippets.

@mirichi

mirichi/hw.rb Secret

Created Dec 30, 2017
Embed
What would you like to do?
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