Skip to content

Instantly share code, notes, and snippets.

@mirichi
Created June 19, 2015 12:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mirichi/41f73dab95db1541cb9e to your computer and use it in GitHub Desktop.
Save mirichi/41f73dab95db1541cb9e to your computer and use it in GitHub Desktop.
コード整理したフィボナッチ数列
require './hsim'
# 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|
reg.w = @w.o & sel # &演算子でANDゲートが生成される。戻りはANDゲートの出力ピン
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
# 電源とグランド
_vcc = Line.new
_gnd = Line.new
vcc = _vcc.o
gnd = _gnd.o
# クロックとクリアの入力線
clk = Line.new
clrb = Line.new
# 8bitデータバス
bus = Bus.new(8)
# ROM
rom = ROM.new(3, 8) # アドレス線3本、データ線8本
rom.d = [ # 8アドレスぶんのデータ
[gnd, gnd, gnd, gnd, gnd, gnd, gnd, gnd], # 0 : mov a, 0x0
[gnd, gnd, gnd, gnd, gnd, vcc, gnd, vcc], # 1 : mov b, 0x1
[vcc, gnd, gnd, gnd, gnd, gnd, vcc, gnd], # 2 : mov c, a
[vcc, gnd, gnd, vcc, gnd, vcc, vcc, gnd], # 3 : add c, b
[vcc, gnd, gnd, gnd, gnd, vcc, gnd, gnd], # 4 : mov a, b
[vcc, gnd, gnd, gnd, vcc, gnd, gnd, vcc], # 5 : mov b, c
[gnd, vcc, gnd, gnd, gnd, gnd, gnd, gnd], # 6 : add a, 0
[vcc, vcc, gnd, gnd, gnd, gnd, vcc, gnd], # 7 : jnc 2
].reverse
#0 0 imm3 imm2 imm1 imm0 reg1 reg0 : mov reg, imm
#0 1 imm3 imm2 imm1 imm0 reg1 reg0 : add reg, imm
#1 0 X 0 rs1 rs0 rd1 rd0 : mov rd, rs
#1 0 X 1 rs1 rs0 rd1 rd0 : add rd, rs
#1 1 adr5 adr4 adr3 adr2 adr1 adr0 : jnc adr
## 命令デコーダ
# mov命令時にH
insn_mov = (~bus.o[0] & ~bus.o[1]) | (bus.o[0] & ~bus.o[1] & ~bus.o[3])
# add命令時にH
insn_add = (~bus.o[0] & bus.o[1]) | (bus.o[0] & ~bus.o[1] & bus.o[3])
# ジャンプ命令時にH
insn_jnc = bus.o[0] & bus.o[1]
# レジスタ書き込み条件
write_reg = ~insn_jnc
# ソースがイミディエート時にH
source_imm = ~bus.o[0]
# ソースがレジスタ時にH
source_reg = bus.o[0] & ~bus.o[1]
## 各種回路ブロック生成
# キャリーフラグ用1bitレジスタ
cf = Register.new(1)
cf.clk = clk.o
cf.clrb = clrb.o
cf.w = vcc
# 4bitプログラムカウンタ用レジスタ
pc = Register.new(4)
pc.clk = clk.o
pc.clrb = clrb.o
pc.w = vcc
# 4bitインクリメンタ
inc = Incrementer.new(4)
# ジャンプ命令用のマルチプレクサ
muxj = Mux1_n.new(4) # 命令中には7ビットのアドレスがあるが、この回路のアドレス線は4本しかない
# レジスタファイル
regf = RegisterFile.new(2, 4)
regf.clk = clk.o
regf.clrb = clrb.o
# 加算器
adder = Adder.new(4)
# 書き込みデータ選択用マルチプレクサ
muxd = Mux1_n.new(4)
# 出力ポート1とバスの入力を選択するマルチプレクサ
muxa = Mux1_n.new(4)
## 回路接続
# ROMの出力はThreeStateBufferなのでBusオブジェクトにaddする
bus.add rom.o
# ROMの出力の値はBusオブジェクトから取り出す
# pcとインクリメンタ接続
pc.d = muxj.o
inc.d = pc.o
# ジャンプ命令でキャリーフラグがLの場合に命令内のアドレスを使う(jncなので)
muxj.d = [bus.o[4..7], inc.o]
muxj.s = insn_jnc & ~cf.o[0]
# ROMにカウンタの値を接続
rom.a = pc.o[1..3]
rom.csb = pc.o[0]
# 入力ポートの接続
regf.d = muxd.o
regf.sin = bus.o[6..7] # 命令的に出力と同じレジスタが選択される
regf.w = write_reg # レジスタ書き込み条件
# 出力ポート選択信号の接続
regf.sout0 = bus.o[6..7]
regf.sout1 = bus.o[4..5]
muxa.d = [bus.o[2..5], regf.o1]
muxa.s = source_imm # ソースがイミディエートの場合、命令内の値を使う
# 4bit全加算器
adder.d = [regf.o0, muxa.o] # レジスタファイルの出力ポート0とmuxaのデータを加算する
adder.x = gnd # キャリー入力は今のところ使わないのでL固定としておく
# movとaddの命令によってmuxaか加算器を通した値かを選択するマルチプレクサ
muxd.d = [adder.o, muxa.o]
muxd.s = insn_add
# 全加算器のキャリー出力をキャリーフラグに入力する
cf.d = [adder.c]
# 電源接続
_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("d b0", bus.o[7])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("d b1", bus.o[6])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("d b2", bus.o[5])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("d b3", bus.o[4])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("d b4", bus.o[3])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("d b5", bus.o[2])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("d b6", bus.o[1])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("d b7", bus.o[0])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("adr b0", pc.o[3])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("adr b1", pc.o[2])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("adr b2", pc.o[1])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("adr b3", pc.o[0])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("areg b0", regf.o[3][3])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("areg b1", regf.o[3][2])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("areg b2", regf.o[3][1])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("areg b3", regf.o[3][0])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("breg b0", regf.o[2][3])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("breg b1", regf.o[2][2])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("breg b2", regf.o[2][1])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("breg b3", regf.o[2][0])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("creg b0", regf.o[1][3])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("creg b1", regf.o[1][2])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("creg b2", regf.o[1][1])
LogicAnalyzer::probes << LogicAnalyzer::Probe.new("creg b3", regf.o[1][0])
# アナライズ
LogicAnalyzer.analyze do
clk.d = L
clrb.d = H
clrb.d = L
clrb.d = H
Fiber.yield
40.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