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 
}
#---------プログラムはここまで-------------------------------