digital circuit simulator (6)
とりあえず、昨晩とりあえず動いた微妙な実装を以下にサラしてみる。
とても恥ずかしい。がしかし (以下略
class Segment attr_accessor :t, :q def initialize(time, queue) @t = time @q = queue end end class Agenda attr_accessor :currentTime, :segments def initialize @currentTime = 0 @segments = Array.new end def segments=(seg) @segments.push seg end def empty? @segments.size == 0 end def firstAgenda seg = @segments.first @currentTime = seg.t seg.q.first end def removeFirst @segments.first.q.shift if @segments.first.q.size == 0 @segments.shift end end def add2agenda(time, p) if empty? || @segments.first.t > time seg = Segment.new(time, Array[p]) @segments.unshift(seg) else @segments.each_index do |i| if @segments[i].t == time # @segments[i].q.unshift(p) @segments[i].q.push(p) return elsif @segments[i].t > time # elsif @segments[i].t < time seg = Segment.new(time, Array[p]) # seg = Segment.new(time, Array.new) # seg.q.shift(p) # @segments.insert(i - 1, seg) @segments.insert(i, seg) return end end seg = Segment.new(time, Array[p]) @segments.push(seg) end end end class Wire def initialize @value = 0 @action = Array.new end def getValue @value end def setValue(v) if @value != v @value = v @action.each do |i| i.call end end end def addAction(p) @action.unshift(p) p.call end end class Inverter attr_accessor :i, :o def initialize(input, output, agenda) @i = input; @o = output; @ag = agenda @d = $inverterDelay p = Proc.new { newVal = logicalNot(@i.getValue); # Sim.afterDelay(@d, lambda{@o.setValue(newVal)}) } afterDelay(@d, lambda{@o.setValue(newVal)}, @ag) } # @s.afterDelay(@d, lambda{@o.setValue(newVal)}) } @i.addAction p end def logicalNot(signal) if signal == 0 1 else 0 end end end class AndGate # attr_accessor :i1, :i2, :o def initialize(input1, input2, output, agenda) @i1 = input1; @i2 = input2; @o = output; @d = $andDelay; @ag = agenda # p = Proc.new { newVal = ((@i1.getValue == 1 && @i2.getValue == 1) ? 1 : 0); p = Proc.new { newVal = logicalAnd(@i1.getValue, @i2.getValue); # Sim.afterDelay(@d, lambda{@o.setValue(newVal)}) } afterDelay(@d, lambda{@o.setValue(newVal)}, @ag) } # @s.afterDelay(@d, lambda{@o.setValue(newVal)}) } @i1.addAction p @i2.addAction p end def logicalAnd(s1, s2) if s1 == 1 && s2 == 1 1 else 0 end end end class OrGate # attr_accessor :i1, :i2, :o def initialize(input1, input2, output, agenda) @i1 = input1; @i2 = input2; @o = output; @d = $orDelay; @ag = agenda # p = Proc.new { newVal = ((@i1.getValue == 0 && @i2.getValue == 0) ? 0 : 1); p = Proc.new { newVal = logicalOr(@i1.getValue, @i2.getValue); # Sim.afterDelay(@d, lambda{@o.setValue(newVal)}) } afterDelay(@d, lambda{@o.setValue(newVal)}, @ag) } # @s.afterDelay(@d, lambda{@o.setValue(newVal)}) } @i1.addAction p @i2.addAction p end def logicalOr(s1, s2) if s1 == 1 || s2 == 1 1 else 0 end end end class HalfAdder # attr_accessor :a, :b, :s, :c attr_accessor :d, :e def initialize(agenda, a, b, s, c) @a = a; @b = b; @s = s; @c = c; @ag = agenda @d = Wire.new @e = Wire.new @g1 = OrGate.new(@a, @b, @d, @ag) @g2 = AndGate.new(@a, @b, @c, @ag) @g3 = Inverter.new(@c, @e, @ag) @g4 = AndGate.new(@d, @e, @s, @ag) # @gates = Array[OrGate.new(@a, @b, @d), # AndGate.new(@a, @b, @c), # Inverter.new(@c, @e), # AndGate.new(@d, @e, @s)] end end class FullAdder attr_accessor :a, :b, :cIn, :sum, :cOut def initialize(a, b, cIn, sum, cOut) @a = a; @b = b; @cIn = cIn; @sum = sum; @cOut = cOut; @s = Wire.new @c1 = Wire.new @c2 = Wire.new @gates = Array[HalfAdder.new(@b, @cIn, @s, @c1), HalfAdder.new(@a, @s, @sum, @c2), OrGate.new(@c1, @c2, @cOut)] end end class Sim attr_accessor :ag def initialize $inverterDelay = 2 $andDelay = 3 $orDelay = 5 @ag = Agenda.new end def simulate @input1 = Wire.new @input2 = Wire.new @sum = Wire.new @carry = Wire.new probe("carry", @carry) probe("sum", @sum) @haobj = HalfAdder.new(@ag, @input1, @input2, @sum, @carry) @input1.setValue(1) propagate @input2.setValue(1) propagate end def probe(name, wire) wire.addAction(lambda {print name; print " "; print @ag.currentTime; print " New-value = "; print wire.getValue; puts ""}) end def propagate if !@ag.empty? @ag.firstAgenda.call @ag.removeFirst propagate end end end def afterDelay(delay, p, ag) ag.add2agenda(delay + ag.currentTime, p) end
コメントアウトした部分もそのままサラしてやれ。不具合が出てたのは基本的に add-to-agenda! (上記実装では Agenda#add2agenda ) です。同一時刻単位に手続き追加する時に enqueue してなかったり、末端に次第書きが追加されなかったり、等とゆー微妙なバグが多くてヘコんどります。