LogicSimソースコード
# logicsim.rb
=begin
クラス Node
Logic素子(AND, OR,NOTなど)の共通部分の処理を行う
インスタンスのデータとして、入力ピン配列@inPin,出力ピン配列@outPin,
出力ピンに対応する出力値@outを持つ
メソッド outConnect
出力ピンに線分のfrom側端子を接続する
メソッド inConnect
入力ピンに線分のto側端子を接続する
メソッド outConnectNode
出力ピンに接続しているノードを求め、結果を@outNodeArrayに格納する。
なお、分岐ノードの場合は分岐先のノードを求める。
求めたノードをイベント発生としてプライオリティ・キューに入れる。
メソッド bunkiShori
出力ピンから出ている線分のT分岐処理を行う
メソッド inConnectNode
入力ピンに接続している前段ノードを求める
メソッド bunkiShori2
T分岐の前段ノードを求める
メソッド event
時刻と処理nodeをプライオリティ・キューに格納する
=end
class Node
@@pq = nil
def Node.pqset
@@pq = PQue.new
@@outNodeArray = Array.new
@@inNodeArray = Array.new
@@count = 0
end
def Node.pq
@@pq
end
def Node.count
@@count
end
def Node.countClear
@@count = 0
end
def Node.outNodeArray
@@outNodeArray
end
def Node.inNodeArray
@@inNodeArray
end
def initialize
@out = Array.new
@inPin = Array.new
@outPin = Array.new
end
def outConnect(pinNo, edge)
@outPin[pinNo - 1] = edge
end
def inConnect(pinNo, edge)
@inPin[pinNo - 1] = edge
end
def outConnectNode(pinNo)
@@outNodeArray.clear
outnode = @outPin[pinNo - 1].toNode
if outnode.class == Bunki
outnode = bunkiShori(outnode)
else
@@outNodeArray.push(outnode)
end
@@outNodeArray.each {|node|
puts " event enque " + (LogicSim.time).to_s + " " + node.class.to_s
event(LogicSim.time, node)
}
end
def bunkiShori(bunkiNode)
bunkiNode.outPin.each {|edge|
if edge.toNode == Bunki
bunkiShori(edge.toNode)
else
@outNodeArray.push(edge.toNode)
end
}
end
def inConnectNode
@@inNodeArray.clear
@inPin.each {|edge|
innode = edge.fromNode
if innode.class == Bunki
innode = bunkiShori2(innode)
end
@@inNodeArray.push(innode)
}
end
def bunkiShori2(node)
innode = node.inPin[0].fromNode
if innode.class == Bunki
bunkiShori2(innode)
end
innode
end
def event(eventTime, eventNode)
@@count += 1
item = EventItem.new(eventTime*1000 + @@count, eventNode)
@@pq.insert(item)
end
attr_accessor :out,:inPin,:outPin
protected :out,:inPin,:outPin
end # Nodeクラスの終了
=begin
クラス AND
二入力ANDゲートの処理を行う。
メソッド connect(input1, input2, output1)
出力の初期値は不定状態を示す2とする。
入力ピン1にWireのインスタンスinput1を接続
input1のto側にANDノードのピン1が接続
入力ピン2にWireのインスタンスinput2を接続
input2のto側にANDノードのピン2が接続
出力ピン1にWireのインスタンスoutput1を接続
output1のfrom側にANDノードの出力ピン1が接続
メソッド exec
入力ピンに接続しているノードを見つけ出す
そのノードの値に一つでも2を含めば出力は2
そのノードの値に一つでも0を含めば出力は0
それ以外なら出力は1
以前の出力と比較して、出力変化があればoutConnectNode(1)をコールする
=end
class AND < Node
def connect(input1, input2, output1)
self.out[0] = 2
self.inConnect(1, input1)
input1.connectTo(self, 1)
self.inConnect(2, input2)
input2.connectTo(self, 2)
self.outConnect(1, output1)
output1.connectFrom(self, 1)
end
def exec
inConnectNode
temp = Node.inNodeArray.length
Node.inNodeArray.each {|node|
if node.out[0] == 2
if self.out[0] != 2 # 出力変化があれば
self.out[0] = 2
outConnectNode(1)
break
end
elsif node.out[0] == 0
if self.out[0] != 0 # 出力変化があれば
self.out[0] = 0
puts " AND output 0" # debug
outConnectNode(1)
break
end
else
temp -= 1
end
}
if temp == 0 # すべての入力ノードが1の場合
if self.out[0] != 1 # 出力変化があれば
self.out[0] = 1
puts " AND output 1"
outConnectNode(1)
end
end
end
end
=begin
クラス OR
二入力ORゲートの処理を行う。
メソッド connect(input1, input2, output1)
出力の初期値は不定状態を示す2とする。
入力ピン1にWireのインスタンスinput1を接続
input1のto側にORノードのピン1が接続
入力ピン2にWireのインスタンスinput2を接続
input2のto側にORノードのピン2が接続
出力ピン1にWireのインスタンスoutput1を接続
output1のfrom側にORノードの出力ピン1が接続
メソッド exec
入力ピンに接続しているノードを見つけ出す
そのノードの値に一つでも2を含めば出力は2
そのノードの値に一つでも1を含めば出力は1
それ以外なら出力は0
以前の出力と比較して、出力変化があればoutConnectNode(1)をコールする
=end
class OR < Node # 二入力OR素子
def connect(input1, input2, output1)
self.out[0] = 2 # 2: 初期値は不定状態
self.inConnect(1, input1)
input1.connectTo(self, 1)
self.inConnect(2, input2)
input2.connectTo(self, 2)
self.outConnect(1, output1)
output1.connectFrom(self, 1)
end
def exec
inConnectNode
temp = Node.inNodeArray.length
Node.inNodeArray.each {|node|
if node.out[0] == 2
if self.out[0] != 2 # 出力変化があれば
self.out[0] = 2
outConnectNode(1)
break
end
elsif node.out[0] == 1
if self.out[0] != 1 # 出力変化があれば
self.out[0] = 1
puts " OR output 1" # debug
outConnectNode(1)
break
end
else
temp -= 1
end
}
if temp == 0 # すべての入力ノードが0の場合
if self.out[0] != 0 # 出力変化があれば
self.out[0] = 0
puts " OR output 0" # debug
outConnectNode(1)
end
end
end
end
=begin
クラス NOT
一入力NOTゲートの処理を行う。
メソッド connect(input1, output1)
出力の初期値は不定状態を示す2とする。
入力ピン1にWireのインスタンスinput1を接続
input1のto側にNOTノードのピン1が接続
出力ピン1にWireのインスタンスoutput1を接続
output1のfrom側にNOTノードの出力ピン1が接続
メソッド exec
入力を反転する
ただし入力が2(不定)の場合は出力は2となる
以前の出力と比較して、出力変化があればoutConnectNode(1)をコールする
=end
class NOT < Node # 一入力NOT素子
def connect(input1, output1)
self.out[0] = 2 # 2: 初期値は不定状態
self.inConnect(1, input1)
input1.connectTo(self, 1)
self.outConnect(1, output1)
output1.connectFrom(self, 1)
end
def exec
inConnectNode
node = Node.inNodeArray[0]
if node.out[0] == 2
if self.out[0] != 2 # 出力変化があれば
self.out[0] = 2
outConnectNode(1)
end
elsif node.out[0] == 1
if self.out[0] != 0 # 出力変化があれば
self.out[0] = 0
puts " NOT output 0"
outConnectNode(1)
end
else
if self.out[0] != 1 # 出力変化があれば
self.out[0] = 1
puts " NOT output 1"
outConnectNode(1)
end
end
end
end
=begin
クラス INPUT
INPUT素子 入力信号を発生する処理を行う。
メソッド connect(output1)
出力の初期値は不定状態を示す2とする。
出力ピン1にWireのインスタンスoutput1を接続
output1のfrom側にINPUTノードの出力ピン1が接続
メソッド exec(vale)
入力信号valueを出力端子に出す
以前の出力と比較して、出力変化があればoutConnectNode(1)をコールする
=end
class INPUT < Node
def connect(output1)
self.out[0] = 2 # 2: 初期値は不定状態
self.outConnect(1, output1)
output1.connectFrom(self, 1)
end
def exec(value)
self.out[0] = value
puts " INPUT value " + value.to_s
outConnectNode(1)
end
end
=begin
クラス OUTPUT
OUTPUT素子 この素子に接続している信号を記録するために用いる。
メソッド connect(input1, name)
入力ピン1にWireのインスタンスinput1を接続
input1のto側にOUTPUTノードの入力ピン1が接続
記録時に用いる名称nameをセットする
メソッド exec
入力ピン1に接続された信号の値を時刻と名称とともに記録する。
(OutputEventを生成し、Report.aryへpushする)
=end
class OUTPUT < Node
def connect(input1, name)
@name = name
self.out[0] = 2
self.inConnect(1, input1)
input1.connectTo(self, 1)
end
def exec
inConnectNode
self.out[0] = Node.inNodeArray[0].out[0]
output = OutputEvent.new(LogicSim.time, self.out[0], @name)
Report.ary.push(output)
end
end
=begin
クラス OutputEvent
時刻、名称、値を持つ記録用の項目
=end
class OutputEvent
def initialize(time, value, name)
@time = time
@name = name
@value = value
end
def time
@time
end
def name
@name
end
def value
@value
end
end
=begin
クラス Report
記録用の行列Report.aryを持つ
=end
class Report
def Report.initial
@@ary = Array.new
end
def Report.ary
@@ary
end
end
=begin
クラス Wire
一本の線である。from側端子とto側端子がある。
メソッド connectFrom(node, pinNo)
from側端子とnode素子(例えばAND素子)の出力ピンpinNoを接続する
メソッド connectTo(node, pinNo)
to側端子とnode素子(例えばAND素子)の入力ピンpinNoを接続する
=end
class Wire
def initialize
@fromNode = nil
@fromPin = nil
@toNode = nil
@toPin = nil
end
def connectFrom(node, pinNo)
@fromNode = node
@fromPin = pinNo
end
def connectTo(node, pinNo)
@toNode = node
@toPin = pinNo
end
def toNode
@toNode
end
def fromNode
@fromNode
end
end
=begin
クラス Bunki
T分岐の処理を行う。 T分岐は一入力を二出力へ分岐する。
メソッド outConnect(pinNo, edge)
T分岐の出力ピンpinNoに線edgeのfrom側端子を接続する
メソッド inConnect(pinNo, edge)
T分岐の入力ピンpinNoに線edgeのto側端子を接続する
=end
class Bunki # T分岐 1入力 2出力
def initialize
@inPin = Array.new
@outPin = Array.new
end
def outConnect(pinNo, edge)
if (pinNo == 1)or(pinNo == 2)
@outPin[pinNo - 1] = edge
else
puts "Bunki outConnect pinNo Error "
end
end
def inConnect(pinNo, edge)
if pinNo == 1
@inPin[pinNo - 1] = edge
else
puts "Bunki inConnect pinNo Error "
end
end
end
=begin
クラス EventItem
Pqueにエンキューするイベントであり、indexとcontentからなる
=end
class EventItem
def initialize(index, content)
@index = index
@content = content
end
attr_accessor :index, :content
end
=begin
クラス InputEvent
INPUT素子用のイベントであり、index, value, inputnodeを持つ
Indexは時刻、valueは入力信号の値、inputnodeは対応するINPUT素子であり、このイベントを受けて
INPUT素子が信号を発生することになる。
=end
class InputEvent
def initialize(time, value, input)
@index = time
@value = value
@inputnode = input
end
attr_accessor :index, :value, :inputnode
end
=begin
クラス Pque
プライオリティ・キューをヒープ構造を使って実現している
insertメソッド: 挿入
ヒープにデータを追加するときは、まず木構造の最後に追加する
このままではヒープ構造の条件を満たさないので、親と比較して
この方が小さければ入れ替える。この操作を根にたどり着くか、
または親の方が小さくなるまで繰り返す。
メソッド lookmin
先頭の要素(最小値)を見る。見るだけでデキューはしない。
deleteminメソッド:
先頭の要素(最小値)を取り除き、返す。
ヒープから最小のデータを取り出す場合は、その根のデータを
取り出せばよい。しかし、これだけでは根がなくなってヒープ
構造がなくなって壊れてしまう。そこで、ヒープの最後のデータ
を根に異動させる。さらに、ヒープ構造の条件を満たすために、
子の小さい方のデータと比較して親の方が大きければ入れ替える。
これを最後まで繰り返す。
=end
class PQue
def initialize
@heap = Array.new
@size = 0
end
def insert(a)
@size += 1
puts " que size up " + @size.to_s # debug
@heap[@size-1] = a
i = @size
j = (@size / 2).to_i
while((i > 1)and(@heap[i-1].index < @heap[j-1].index))
t = @heap[i-1]
@heap[i-1] = @heap[j-1]
@heap[j-1] = t
i = j
j = (i / 2).to_i
end
end
def size
@size
end
def lookmin
@heap[0]
end
def deletemin
if @size <= 0
return nil
else
r = @heap[0]
@size -= 1
puts " que size down " + @size.to_s # debug
@heap[0] = @heap[@size]
i = 1
j = (i * 2).to_i
while(j <= @size)
if ((j+1 <= @size)and(@heap[j-1].index > @heap[j].index))
j += 1
end
if (@heap[i-1].index > @heap[j-1].index)
t = @heap[i-1]
@heap[i-1] = @heap[j-1]
@heap[j-1] = t
end
i = j
j = (i * 2).to_i
end
return r
end
end
end
=begin
クラス LogicSim
プライオリティ・キューよりイベントを取り出す
INPUT素子用のイベントならば、クラスINPUTのexec(value)を実行する
その他のイベントならば、対応する各クラス(AND, OR, NOTnado)のexecを実行する
=end
class LogicSim
def LogicSim.time
@@time
end
def initialize
@pq = Node.pq
@@time = 0
end
def set(time, value, inputNode)
inputEvent = InputEvent.new(time*1000, value, inputNode)
@pq.insert(inputEvent)
end
def run(endtime)
while (@@time < endtime)and(@pq.size > 0)
event = @pq.lookmin
if event != nil
puts "event time: " + (event.index / 1000).to_s + " sim time: " + @@time.to_s
if (event.index / 1000) <= @@time
puts " deque"
@pq.deletemin
if event.class == InputEvent
puts " time: " + @@time.to_s + " " + event.class.to_s
event.inputnode.exec(event.value)
else
if event.content != nil # karishochi
puts " time: " + @@time.to_s + " node: " + event.content.class.to_s
event.content.exec
end # karishochi
end
else
@@time += 1
Node.countClear
end
end
end
end
end
Node.pqset
Report.initial
sim = LogicSim.new
o1 = Wire.new
o2 = Wire.new
o3 = Wire.new
o4 = Wire.new
o5 = Wire.new
o6 = Wire.new
in1 = INPUT.new # 入力素子
in2 = INPUT.new
in3 = INPUT.new
and1 = AND.new
or1 = OR.new
not1 = NOT.new
out1 = OUTPUT.new
in1.connect(o1)
in2.connect(o2)
in3.connect(o3)
and1.connect(o1, o2, o4)
or1.connect(o3, o4, o5)
not1.connect(o5, o6)
out1.connect(o6, "OUTPUT1")
p sim
sim.set(0,0,in1) # 時刻0, 値0, 入力in1
sim.set(0,0,in2)
sim.set(0,0,in3)
sim.set(10,1,in3)
sim.set(25,0,in3)
sim.set(30,1,in1)
sim.set(35,1,in2)
puts " "
puts "-----------run-----------------------------"
puts " "
sim.run(100)
# 出力結果の表示
puts "---------------REPORT-------------------------"
Report.ary.each {|item|
puts item.time.to_s + " " + item.name + " " + item.value.to_s
}
#---------プログラムはここまで-------------------------------