Skip to content

Instantly share code, notes, and snippets.

@mirichi

mirichi/risc-v_lb.rb Secret

Created Jan 1, 2018
Embed
What would you like to do?
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