Skip to content

Instantly share code, notes, and snippets.

@mirichi

mirichi/hsim.rb Secret

Created Dec 13, 2017
Embed
What would you like to do?
RISC-Vを目指すシミュレータ
HLHASH = {true=>"H", false=>"L", nil=>"nil"}
H = true
L = false
QUEUE = []
# 出力ピン
class OutputPin
attr_accessor :output_objects
def initialize(v=nil)
@value = v
@output_objects = []
end
# 出力をセットする
def set(v)
# 出力が変わったら通知先をキューに格納する
if @value != v
@value = v
@output_objects.each do |i|
QUEUE << i
end
end
self
end
# 出力の取得
def get
@value
end
# &演算子
def &(dst)
tmp = AND.new
tmp.a = self
tmp.b = dst
tmp.o
end
# |演算子
def |(dst)
tmp = OR.new
tmp.a = self
tmp.b = dst
tmp.o
end
# ~演算子
def ~
tmp = NOT.new
tmp.a = self
tmp.o
end
# ^演算子
def ^(dst)
tmp = XOR.new
tmp.a = self
tmp.b = dst
tmp.o
end
end
# 外部からの入力用配線
class Line
attr_accessor :output_objects
def initialize
@o = OutputPin.new
@output_objects = []
end
def d=(v)
@d = OutputPin.new(v)
QUEUE.push(self)
# キューが無くなるまでシミュレーション実行
while QUEUE.size > 0
QUEUE.shift.update
end
end
def o
@o
end
def update
@o.set(@d.get)
end
end
class Bus1
def initialize
@o = OutputPin.new
end
def o
@o
end
def add(v)
v.connect_bus(self)
end
end
class Bus
def initialize(bit_width)
@bus = Array.new(bit_width){Bus1.new}
end
def o
@bus.map{|b|b.o}
end
def add(v)
v.connect_bus(@bus)
end
def bus
@bus
end
end
class ThreeStateBuffer1
def d=(v)
v.output_objects << self
@d = v
QUEUE.push self if v.get != nil
end
def g=(v)
v.output_objects << self
@g = v
QUEUE.push self if v.get != nil
end
def update
@bus.o.set(@d.get) if @g and @g.get
end
def connect_bus(b)
@bus = b
end
end
class ThreeStateBuffer
def initialize(bit_width)
@tsb = Array.new(bit_width){ThreeStateBuffer1.new}
end
def d=(v)
@tsb.zip(v) do |tsb, o|
tsb.d = o
end
end
def g=(v)
@tsb.each do |tsb|
tsb.g = v
end
end
def connect_bus(b)
@tsb.zip(b).each do |t, bus|
t.connect_bus bus
end
end
end
class ThreeStateBufferB1
def d=(v)
v.output_objects << self
@d = v
QUEUE.push self if v.get != nil
end
def g=(v)
v.output_objects << self
@g = v
QUEUE.push self if v.get != nil
end
def update
@bus.o.set(@d.get) if @g and !@g.get
end
def connect_bus(b)
@bus = b
end
end
class ThreeStateBufferB
def initialize(bit_width)
@tsb = Array.new(bit_width){ThreeStateBufferB1.new}
end
def d=(v)
@tsb.zip(v) do |tsb, o|
tsb.d = o
end
end
def g=(v)
@tsb.each do |tsb|
tsb.g = v
end
end
def connect_bus(b)
@tsb.zip(b).each do |t, bus|
t.connect_bus bus
end
end
end
# NANDゲート試作
class NAND
def initialize
@o = OutputPin.new
@a = @b = OutputPin.new
end
def a=(v)
v.output_objects << self
@a = v
QUEUE.push self if v.get != nil
end
def b=(v)
v.output_objects << self
@b = v
QUEUE.push self if v.get != nil
end
def o
@o
end
def update
@o.set(!(@a.get and @b.get))
end
def inspect
"#{HLHASH[o.get]}"
end
end
# NOTゲート試作
class NOT
def initialize
@nand = NAND.new
end
def a=(v)
@nand.a = v
@nand.b = v
end
def o
@nand.o
end
def inspect
"#{HLHASH[o.get]}"
end
end
class AND
def initialize
@nand = NAND.new
@not = NOT.new
@not.a = @nand.o
end
def a=(v)
@nand.a = v
end
def b=(v)
@nand.b = v
end
def o
@not.o
end
def inspect
"#{HLHASH[o.get]}"
end
end
class OR
def initialize
@nand = NAND.new
@not1 = NOT.new
@not2 = NOT.new
@nand.a = @not1.o
@nand.b = @not2.o
end
def a=(v)
@not1.a = v
end
def b=(v)
@not2.a = v
end
def o
@nand.o
end
def inspect
"#{HLHASH[o.get]}"
end
end
class XOR
def initialize
@and1 = AND.new
@and2 = AND.new
@not1 = NOT.new
@not2 = NOT.new
@or = OR.new
@and1.a = @not1.o
@and2.b = @not2.o
@or.a = @and1.o
@or.b = @and2.o
end
def a=(v)
@not1.a = v
@and2.a = v
end
def b=(v)
@not2.a = v
@and1.b = v
end
def o
@or.o
end
def inspect
"#{HLHASH[o.get]}"
end
end
class NAND3
def initialize
@nand = NAND.new
@and = AND.new
@nand.a = @and.o
end
def a=(v)
@nand.b = v
end
def b=(v)
@and.a = v
end
def c=(v)
@and.b = v
end
def o
@nand.o
end
def inspect
"#{HLHASH[o.get]}"
end
end
class HarfAdder
def initialize
@and = AND.new
@xor = XOR.new
end
def a=(v)
@and.a = v
@xor.a = v
end
def b=(v)
@and.b = v
@xor.b = v
end
def s
@xor.o
end
def c
@and.o
end
def inspect
"#{HLHASH[c.get]}#{HLHASH[s.get]}"
end
end
class FullAdder
def initialize
@hadr1 = HarfAdder.new
@hadr2 = HarfAdder.new
@or = OR.new
@hadr2.a = @hadr1.s
@or.a = @hadr1.c
@or.b = @hadr2.c
end
def a=(v)
@hadr1.a = v
end
def b=(v)
@hadr1.b = v
end
def x=(v)
@hadr2.b = v
end
def s
@hadr2.s
end
def c
@or.o
end
def inspect
"#{HLHASH[c.get]}#{HLHASH[s.get]}"
end
end
class RSFF
def initialize
@not1 = NOT.new
@not2 = NOT.new
@nand1 = NAND.new
@nand2 = NAND.new
@nand1.a = @not1.o
@nand1.b = @nand2.o
@nand2.a = @not2.o
@nand2.b = @nand1.o
end
def s=(v)
@not1.a = v
end
def r=(v)
@not2.a = v
end
def q
@nand1.o
end
def qb
@nand2.o
end
def inspect
"q=#{HLHASH[q.get]}, qb=#{HLHASH[qb.get]}"
end
end
class DFF1
def initialize
@not1 = NOT.new
@not2 = NOT.new
@nand1 = NAND.new
@nand2 = NAND.new
@nand3 = NAND.new
@nand4 = NAND.new
@nand5 = NAND.new
@nand6 = NAND.new
@nand7 = NAND.new
@nand8 = NAND.new
@reset1_and = AND.new
@reset1_not = NOT.new
@reset1_nand = NAND.new
@reset1_and.a = @nand2.o
@reset1_not.a = @nand1.o
@reset1_nand.a = @reset1_not.o
@reset2_and = AND.new
@reset2_not = NOT.new
@reset2_nand = NAND.new
@reset2_and.a = @nand6.o
@reset2_not.a = @nand5.o
@reset2_nand.a = @reset2_not.o
@not2.a = @not1.o
@nand1.b = @not1.o
@nand2.a = @nand1.o
@nand2.b = @not1.o
@nand3.a = @reset1_nand.o
@nand3.b = @nand4.o
@nand4.a = @nand3.o
@nand4.b = @reset1_and.o
@nand5.a = @nand3.o
@nand5.b = @not2.o
@nand6.a = @nand5.o
@nand6.b = @not2.o
@nand7.a = @reset2_nand.o
@nand7.b = @nand8.o
@nand8.a = @nand7.o
@nand8.b = @reset2_and.o
end
def d=(v)
@nand1.a = v
end
def clk=(v)
@not1.a = v
end
def clrb=(v)
@reset1_and.b = @reset1_nand.b = v
@reset2_and.b = @reset2_nand.b = v
end
def q
@nand7.o
end
def qb
@nand8.o
end
def inspect
"q=#{HLHASH[q.get]}, qb=#{HLHASH[qb.get]}"
end
end
class DFF
def initialize(bit_width)
@dff = Array.new(bit_width){DFF1.new}
end
def d=(v)
@dff.zip(v) do |dff, o|
dff.d = o
end
end
def clk=(v)
@dff.each do |dff|
dff.clk = v
end
end
def clrb=(v)
@dff.each do |dff|
dff.clrb = v
end
end
def q
@dff.map{|dff|dff.q}
end
def qb
@dff.map{|dff|dff.qb}
end
end
class JKFF
def initialize
@nand1 = NAND.new
@nand2 = NAND.new
@nand3 = NAND.new
@nand4 = NAND3.new
@nand5 = NAND3.new
@nand6 = NAND.new
@nand7 = NAND.new
@nand8 = NAND.new
@reset_and = AND.new
@reset_not = NOT.new
@reset_nand = NAND.new
@reset_and.a = @nand5.o
@reset_not.a = @nand4.o
@reset_nand.a = @reset_not.o
@nand1.a = @nand8.o
@nand2.b = @nand7.o
@nand3.a = @nand1.o
@nand3.b = @nand4.o
@nand4.a = @nand3.o
@nand4.c = @nand5.o
@nand5.a = @nand4.o
@nand5.c = @nand6.o
@nand6.a = @nand5.o
@nand6.b = @nand2.o
@nand7.a = @reset_nand.o # @nand.o
@nand7.b = @nand8.o
@nand8.a = @nand7.o
@nand8.b = @reset_and.o #@nand5.o
end
def j=(v)
@nand1.b = v
end
def k=(v)
@nand2.a = v
end
def clk=(v)
@nand4.b = v
@nand5.b = v
end
def clrb=(v)
@reset_and.b = @reset_nand.b = v
end
def q
@nand7.o
end
def qb
@nand8.o
end
def inspect
"#{HLHASH[q.get]}#{HLHASH[qb.get]}"
end
end
class TFF
def initialize
@jkff = JKFF.new
@jkff.j = @jkff.k = OutputPin.new(H)
end
def t=(v)
@jkff.clk = v
end
def clrb=(v)
@jkff.clrb = v
end
def q
@jkff.q
end
def qb
@jkff.qb
end
def inspect
"#{HLHASH[q.get]}#{HLHASH[qb.get]}"
end
end
class Incrementer
def initialize(bit_width)
@ha = Array.new(bit_width){HarfAdder.new}
@ha.last.b = OutputPin.new(H)
if bit_width > 1
(bit_width-1).times do |i|
@ha[i].b = @ha[i+1].c
end
end
end
def d=(v)
@ha.zip(v).each do |ha, o|
ha.a = o
end
end
def o
@ha.map{|ha|ha.s}
end
end
class Counter
def initialize(bit_width)
@dff = Array.new(bit_width){
tmp = DFF1.new
tmp.d = tmp.qb
tmp
}
if bit_width > 1
(bit_width-1).times do |i|
@dff[i].clk = @dff[i+1].qb
end
end
end
def clk=(v)
@dff.last.clk = v
end
def clrb=(v)
@dff.each do |dff|
dff.clrb = v
end
end
def o
@dff.map{|dff|dff.q}
end
end
# 単純なバッファ
class Buffer1
def initialize
@not1 = NOT.new
@not2 = NOT.new
@not1.a = @not2.o
end
def d=(v)
@not2.a = v
end
def o
@not1.o
end
end
# 単純なバッファの塊
class Buffer
def initialize(bit_width)
@buf = Array.new(bit_width){Buffer1.new}
end
def d=(v)
@buf.zip(v) do |buf, o|
buf.d = o
end
end
def o
@buf.map{|b|b.o}
end
end
# 1bitデータ2つを1bitで選択するマルチプレクサ(スリーステートバッファ版)
class Mux1_1
def initialize
@tsb0 = ThreeStateBufferB1.new
@tsb1 = ThreeStateBuffer1.new
@bus = Bus1.new
@bus.add(@tsb0)
@bus.add(@tsb1)
end
def s=(v)
@tsb0.g = @tsb1.g = v
end
def d0=(v)
@tsb0.d = v
end
def d1=(v)
@tsb1.d = v
end
def o
@bus.o
end
end
# n bitデータ2つを1bitで選択するマルチプレクサ
class Mux1_n
def initialize(bit_width)
@mux = Array.new(bit_width){Mux1_1.new}
end
def s=(v)
@mux.each do |m|
m.s = v
end
end
def d=(v)
@mux.zip(*v) do |m, d1, d0|
m.d1 = d1
m.d0 = d0
end
end
def o
@mux.map{|m|m.o}
end
end
# n bitデータをn bitで選択するマルチプレクサ
class Mux
def initialize(select_bit_width, data_bit_width)
# 作成
@mux = Array.new(select_bit_width){|i| Array.new(2**i){Mux1_n.new(data_bit_width)}}
@buf = Buffer.new(select_bit_width)
# 選択線接続
@mux.zip(@buf.o).each do |ary, b|
ary.each do |m|
m.s = b
end
end
# データ線接続
if select_bit_width > 1
(1...select_bit_width).each do |i|
@mux[i-1].each.with_index do |m, j|
m.d = [@mux[i][j * 2].o, @mux[i][j * 2 + 1].o]
end
end
end
end
def s=(v)
@buf.d = v
end
def d=(v)
@mux.last.each.with_index do |m, i|
m.d = [v[i * 2], v[i * 2 + 1]]
end
end
def o
@mux[0][0].o
end
end
# n bitの入力から2**n bitのどれかを選択してHにするセレクタ
class Selector
def initialize(bit_width)
@buf = Buffer.new(bit_width)
tmp = NOT.new
tmp.a = @buf.o[0]
@o = [@buf.o[0], tmp.o]
# 複数bit時の接続
if bit_width > 1
(bit_width-1).times do |i|
tmp = NOT.new
tmp.a = @buf.o[i+1]
bary = [@buf.o[i+1]]*(@o.size) + [tmp.o]*(@o.size)
@o = @o * 2
@o = @o.zip(bary).map do |o, b|
a = AND.new
a.a = o
a.b = b
a.o
end
end
end
end
def s=(v)
# 苦労して作ったら順番が逆だったのでreverseしてごまかす
@buf.d = v.reverse
end
def o
@o
end
end
# 可変長加算器
class Adder
def initialize(bit_width)
@adder = Array.new(bit_width){FullAdder.new}
# 2bit以上の加算の場合、加算器同士を接続する
if bit_width > 1
@adder[0..-2].each.with_index do |a, i|
a.x = @adder[i+1].c
end
end
end
# データ入力
def d=(v)
@adder.zip(*v).each do |a, d1, d0|
a.a = d1
a.b = d0
end
end
# キャリー入力
def x=(v)
@adder.last.x = v
end
# キャリー出力
def c
@adder[0].c
end
# 出力
def o
@adder.map{|a|a.s}
end
def inspect
o.map{|o|"#{HLHASH[o.get]}"}.join
end
end
# 指定ビットシフトする(論理、算術共有)(-が左、+が右)
class Shifter
def initialize(bit_width, n)
@buf = Buffer.new(bit_width)
@n = n
@sign = Buffer1.new
end
def d=(v)
@buf.d = Array.new(@buf.o.size) do |i|
if i - @n < 0
@sign.o
elsif i - @n >= @buf.o.size
OutputPin.new(L)
else
v[i - @n]
end
end
end
# 右シフトの場合、ここに最上位ビットの出力ピンを接続すると算術シフト、L固定にすると論理シフト。
# 左シフトの場合は使われないのでどっちでもいい。
def sign=(v)
@sign.d = v
end
def o
@buf.o
end
end
# バレルシフタ
class BarrelShifter
def initialize(shift_bit_width, data_bit_width)
# 左シフタ
@lshifter = Array.new(shift_bit_width){|i| Shifter.new(data_bit_width, -(2**i))}
@lmux = Array.new(shift_bit_width){Mux1_n.new(data_bit_width)}
@lbuf = Buffer.new(shift_bit_width)
# 右シフタ
@rshifter = Array.new(shift_bit_width){|i| Shifter.new(data_bit_width, 2**i)}
@rmux = Array.new(shift_bit_width){Mux1_n.new(data_bit_width)}
@rbuf = Buffer.new(shift_bit_width)
# 接続
shift_bit_width.times do |i|
@lmux[i].s = @lbuf.o[i]
@rmux[i].s = @rbuf.o[i]
if i > 0
@lshifter[i].d = @lmux[i-1].o
@lmux[i].d = [@lshifter[i].o, @lmux[i-1].o]
@rshifter[i].d = @rmux[i-1].o
@rmux[i].d = [@rshifter[i].o, @rmux[i-1].o]
end
end
# 左右選択マルチプレクサ
@mux = Mux1_n.new(data_bit_width)
@mux.d = [@rmux.last.o, @lmux.last.o]
end
# データ入力
def d=(v)
@lshifter[0].d = @rshifter[0].d = v
@lmux[0].d = @rmux[0].d = [@rshifter[0].o, v]
end
# シフトビット数
def shift=(v)
@lbuf.d = @rbuf.d = v.reverse
end
# 算術シフトの場合に最上位に埋められる符号ビット
def sign=(v)
@lshifter.each do |s|
s.sign = v
end
@rshifter.each do |s|
s.sign = v
end
end
# Lが左、Hが右
def direction=(v)
@mux.s = v
end
def o
@mux.o
end
def inspect
o.map{|o|"#{HLHASH[o.get]}"}.join
end
end
# 符号なし乗算器
class Multiplier
def initialize(bit_width)
# 入力線
@buf0 = Buffer.new(bit_width)
@buf1 = Buffer.new(bit_width)
# L固定の出力ピン
opl = OutputPin.new(L)
# シフトした配列を作る
buf = Array.new(bit_width) do |i|
tmp1 = [opl] * (bit_width * 2) # Lの出力ピンをbit_width*2だけ並べた配列
tmp2 = @buf0.o.map{|b|b & @buf1.o[bit_width - i - 1]} # buf0の各bitとbuf1の1bitのandを取る
tmp1[bit_width - i, bit_width] = tmp2 # tmp1配列の一部をtmp2に差し替える
tmp1
end
# シフトした値を加算するための加算器
@adder = Array.new(Math.log(bit_width, 2)){|i|Array.new(2**i){Adder.new(bit_width*2)}}
@adder.last.each.with_index do |adder, i|
adder.d = [buf[i * 2], buf[i * 2 + 1]]
adder.x = opl
end
(1...(@adder.size)).each do |i|
@adder[i-1].each.with_index do |adder, j|
adder.d = [@adder[i][j * 2].o, @adder[i][j * 2 + 1].o]
adder.x = opl
end
end
end
def d=(v)
@buf0.d = v[0]
@buf1.d = v[1]
end
def o
@adder[0][0].o
end
end
# 符号あり乗算器
class SignedMultiplier
def initialize(bit_width)
# 入力線
@buf0 = Buffer.new(bit_width)
@buf1 = Buffer.new(bit_width)
# HL固定の出力ピン
oph = OutputPin.new(H)
opl = OutputPin.new(L)
# シフトした配列を作る
@buf = Array.new(bit_width) do |i|
tmp1 = [opl] * (bit_width * 2) # Lの出力ピンをbit_width*2だけ並べた配列
tmp2 = @buf1.o[bit_width - i - 1]
if i == (bit_width-1) # 最後のみ
# 先頭以外反転
tmp2 = [@buf0.o[0] & tmp2] + @buf0.o[1..-1].map{|b|~(b & tmp2)}
else
# 先頭だけ反転
tmp2 = [~(@buf0.o[0] & tmp2)] + @buf0.o[1..-1].map{|b|b & tmp2}
end
tmp1[bit_width - i, bit_width] = tmp2 # tmp1配列の一部をtmp2に差し替える
if i == (bit_width-1) or i == 0 # 最初と最後
tmp1[bit_width - i - 1] = oph
end
tmp1
end
# シフトした値を加算するための加算器
@adder = Array.new(Math.log(bit_width, 2)){|i|Array.new(2**i){Adder.new(bit_width*2)}}
@adder.last.each.with_index do |adder, i|
adder.d = [@buf[i * 2], @buf[i * 2 + 1]]
adder.x = opl
end
(1...(@adder.size)).each do |i|
@adder[i-1].each.with_index do |adder, j|
adder.d = [@adder[i][j*2].o, @adder[i][j*2+1].o]
adder.x = opl
end
end
end
def d=(v)
@buf0.d = v[0]
@buf1.d = v[1]
end
def o
@adder[0][0].o
end
end
# ROM
class ROM
def initialize(select_bit_width, data_bit_width)
@mux = Mux.new(select_bit_width, data_bit_width)
@tsb = ThreeStateBufferB.new(data_bit_width)
@tsb.d = @mux.o
end
# アドレス線
def a=(v)
@mux.s = v
end
# チップセレクト
def csb=(v)
@tsb.g = v
end
# 保持データの接続
def d=(v)
@mux.d = v
end
# 出力(スリーステートバッファ)
def o
@tsb
end
end
# レジスタ
class Register
def initialize(bit_width)
@dff = DFF.new(bit_width)
@mux = Mux1_n.new(bit_width)
@dff.d = @mux.o
end
def clk=(v)
@dff.clk = v
end
def d=(v)
@mux.d = [v, self.o]
end
# 書き込む場合にH
def w=(v)
@mux.s = v
end
def clrb=(v)
@dff.clrb = v
end
def o
@dff.q
end
end
# レジスタファイル(出力2ポート、入力1ポート)。ゼロ番はゼロレジスタになる
class RegisterFile
def initialize(select_bit_width, data_bit_width)
# レジスタの配列
@reg = Array.new(2**select_bit_width){Register.new(data_bit_width)}
# 書き込みレジスタを選択するためのセレクタ
# セレクタの出力はレジスタファイルのW信号入力とANDを取って各レジスタのWに入る
@sel = Selector.new(select_bit_width)
@w = Buffer1.new # W入力用バッファ
@reg.zip(@sel.o).each do |reg, sel|
if @sel.o[-1] != sel # 最後の1個じゃない場合
reg.w = @w.o & sel # &演算子でANDゲートが生成される。戻りはANDゲートの出力ピン
else # 最後の1個(ゼロレジスタ)の場合
reg.w = OutputPin.new(L) # つねにL。つまり書き込みは行われない
end
end
# 出力ポート
@mux0 = Mux.new(select_bit_width, data_bit_width)
@mux0.d = self.o
@mux1 = Mux.new(select_bit_width, data_bit_width)
@mux1.d = self.o
end
# クロック入力
# 書き込みタイミングはクロックの立ち上がり
def clk=(v)
@reg.each do |reg|
reg.clk = v
end
end
# select_bit_width本の書き込み選択信号
# 内部のセレクタに入力される
def sin=(v)
@sel.s = v
end
# 出力ポート0用選択信号
def sout0=(v)
@mux0.s = v
end
# 出力ポート1用選択信号
def sout1=(v)
@mux1.s = v
end
# 入力データ
# wがHの場合、クロックの立ち上がりでsで選択されたレジスタにデータが書き込まれる
def d=(v)
@reg.each do |reg|
reg.d = v
end
end
# 入力ポート有効がH
def w=(v)
@w.d = v
end
# クリア信号
def clrb=(v)
@reg.each do |reg|
reg.clrb = v
end
end
# 出力ポート0の出力
def o0
@mux0.o
end
# 出力ポート1の出力
def o1
@mux1.o
end
# 出力(全レジスタ)
def o
@reg.map{|r|r.o}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment