Skip to content

Instantly share code, notes, and snippets.

@mirichi

mirichi/riscv-hsim.rb Secret

Created Dec 13, 2017
Embed
What would you like to do?
RISC-Vを目指す回路
require_relative './hsim'
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
# 8bitデータバス
bus = Bus.new(32)
# ROM
rom = ROM.new(3, 32) # アドレス線3本、データ線8本
rom.d = [ # 8アドレスぶんのデータ
_to_pin("00000000000000000000000010110011", vcc, gnd), # add r1, r0, r0
_to_pin("00000000001100001000000010010011", vcc, gnd), # addi r1, r1, 3
_to_pin("11111111111100001000000010010011", vcc, gnd), # addi r1, r1, -1
_to_pin("00000000001100001000000010010011", vcc, gnd), # addi r1, r1, 3
_to_pin("11111111111100001000000010010011", vcc, gnd), # addi r1, r1, -1
_to_pin("00000000001100001000000010010011", vcc, gnd), # addi r1, r1, 3
_to_pin("00000000000100001000000010110011", vcc, gnd), # add r1, r1, r1
_to_pin("00000000001100001000000010010011", vcc, gnd), # addi r1, r1, 3
].reverse
## 命令デコーダ(RISC-V/RV32I)
insn = bus.o
insn_bit_funct7 = insn[0..6]
insn_bit_imm11_5 = insn[0..6]
insn_bit_imm11_0 = insn[0..11]
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]
# デコード用ビットパターン
opcode_addi = _to_pin("0010011", vcc, gnd)
opcode_add = _to_pin("0110011", vcc, gnd)
opcode_jalr = _to_pin("1100111", vcc, gnd)
funct3_addi = _to_pin("000" , vcc, gnd)
funct3_add = _to_pin("000" , vcc, gnd)
funct3_jalr = _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)
# レジスタ書き込み条件
write_reg = insn_addi | insn_add | insn_jalr
## 各種回路ブロック生成
# 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)
## 回路接続
# ROMの出力はThreeStateBufferなのでBusオブジェクトにaddする
bus.add rom.o
# ROMの出力の値はBusオブジェクトから取り出す
# pcとインクリメンタ接続
pc.d = _if(insn_jalr, adder.o, inc.o + [gnd, gnd])
inc.d = pc.o[0, 30]
# ROMにPCを接続
rom.a = pc.o[27, 3]
rom.csb = gnd
# レジスタファイルの入力ポート接続
regf.d = _if(insn_jalr, 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 = [regf.o0, _if(insn_add, regf.o1, [insn_bit_imm11_0[0]]*20 + insn_bit_imm11_0)]
adder.x = gnd
# 電源接続
_vcc.d = H
_gnd.d = L
# 簡易ロジックアナライザ
require 'dxruby'
module LogicAnalyzer
@@probes = []
def self.probes
@@probes
end
class Probe < Struct.new(:name, :outpin)
end
def self.analyze(&b)
f = Fiber.new(&b)
buf = []
# 実行
while f.resume(:la) != :la
tmp = []
@@probes.each do |pr|
tmp.push(pr.outpin.get)
end
buf.push(tmp)
end
image = Image.new(640, 480)
size = 480 / @@probes.size
font = Font.new(size)
x = @@probes.map{|v|font.get_width(v.name)}.max + 15
w = (640 - x) / buf.size
# 画像作成
@@probes.each.with_index do |pr, i|
image.draw_font(10, i * size, pr.name, font)
tmp = false
buf.each.with_index do |b, j|
if tmp != b[i]
image.line(j * w + x, i * size + 8, j * w + x, i * size + size - 1, C_WHITE)
end
image.line(j * w + x, i * size + (b[i] ? 8 : size - 1), j * w + x + w, i * size + (b[i] ? 8 : size - 1), C_WHITE)
tmp = b[i]
end
end
Window.loop do
Window.draw(0, 0, image)
break if Input.key_push?(K_ESCAPE)
end
end
end
# プローブ接続
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("clk", clk.o)
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("insn addi", insn_addi)
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("insn add", insn_add)
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("insn jalr", insn_jalr)
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("opcode b0", bus.o[31])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("opcode b1", bus.o[30])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("opcode b2", bus.o[29])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("opcode b3", bus.o[28])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("opcode b4", bus.o[27])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("opcode b5", bus.o[26])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("opcode b6", bus.o[25])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("adr b0", pc.o[31])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("adr b1", pc.o[30])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("adr b2", pc.o[29])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("adr b3", pc.o[28])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("adr b4", pc.o[27])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("r1 b0", regf.o[30][31])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("r1 b1", regf.o[30][30])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("r1 b2", regf.o[30][29])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("r1 b3", regf.o[30][28])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("r1 b4", regf.o[30][27])
# アナライズ
LogicAnalyzer.analyze do
clk.d = L
clrb.d = H
clrb.d = L
clrb.d = H
Fiber.yield
20.times do
clk.d = L
Fiber.yield
clk.d = H
Fiber.yield
end
:la
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment