ハードウェアシミュレータその4

DeadBeefROMについて。
とりあえず何か作ってみるかと言うことで16進数8桁でdeadbeefと言うデータを書き込んだROMを作って、アドレスを渡すとデータが出てくる、という回路を作ってみた。のでその話。

マルチプレクサ

よくわかっていないのだが、「コンピュータシステムの理論と実装」という本によると、2つの入力と選択信号を受け取ってどっちかの入力を片方だけ出力する回路をマルチプレクサというらしい。
図にするとこんなの。

sにLが入るとd0が、Hが入るとd1がoに出てくる。これはANDとORとNOTを組み合わせることで簡単に作れる。

# 1bitデータ2つを1bitで選択するマルチプレクサ
class Mux1_1
  def initialize
    @and1 = AND.new
    @and2 = AND.new
    @not = NOT.new
    @or = OR.new
    @and1.a = @not.o
    @or.a = @and1.o
    @or.b = @and2.o
  end

  def s=(v)
    @not.a = @and2.a = v
  end

  def d0=(v)
    @and1.b = v
  end

  def d1=(v)
    @and2.b = v
  end

  def o
    @or.o
  end

  def inspect
    "#{HLHASH[@or.o.get]}"
  end
end

スリーステートバッファ

スリーステートバッファは入力1つを選択信号で出すか出さないかを選ぶことができる。
図にするとこんなの。

これをこう繋ぐことでマルチプレクサが作れる。

シミュレータ上はスリーステートバッファは出力ピンを持っていないので、Busオブジェクトに繋いでそこから出力を出す。

# 1bitデータ2つを1bitで選択するマルチプレクサ(スリーステートバッファ版)
class Mux1_1
  def initialize
    @tsb1 = ThreeStateBuffer.new
    @tsb2 = ThreeStateBuffer.new
    @bus = Bus.new
    @not = NOT.new
    @bus.add(@tsb1)
    @bus.add(@tsb2)
    @tsb1.g = @not.o
  end
 
  def s=(v)
    @not.a = @tsb2.g = v
  end
 
  def d0=(v)
    @tsb1.d = v
  end
 
  def d1=(v)
    @tsb2.d = v
  end
 
  def o
    @bus.o
  end
 
  def inspect
    "#{HLHASH[@bus.o.get]}"
  end
end

どっちにしてもデジタル的な理論上は同じである。

4bit2入力マルチプレクサ

1bitデータを選択しても別に嬉しくないが、これを4個並べて1本の選択信号で制御すると、4bitまとめて入出力できるマルチプレクサが作れる。

# 4bitデータ2つを1bitで選択するマルチプレクサ
class Mux1_4
  def initialize
    @mux3 = Mux1_1.new
    @mux2 = Mux1_1.new
    @mux1 = Mux1_1.new
    @mux0 = Mux1_1.new
  end
 
  def s=(v)
    @mux3.s = v
    @mux2.s = v
    @mux1.s = v
    @mux0.s = v
  end
 
  def d0=(v)
    @mux3.d0 = v[0]
    @mux2.d0 = v[1]
    @mux1.d0 = v[2]
    @mux0.d0 = v[3]
  end
 
  def d1=(v)
    @mux3.d1 = v[0]
    @mux2.d1 = v[1]
    @mux1.d1 = v[2]
    @mux0.d1 = v[3]
  end
 
  def o
    [
      @mux3.o,
      @mux2.o,
      @mux1.o,
      @mux0.o
    ]
  end
 
  def inspect
    "#{HLHASH[@mux3.o.get]}"+
    "#{HLHASH[@mux2.o.get]}"+
    "#{HLHASH[@mux1.o.get]}"+
    "#{HLHASH[@mux0.o.get]}"
  end
end

4bitを別々に入出力するのが面倒だったのでOutputPinオブジェクトの配列で渡すようにしてある。配列の順番とマルチプレクサの番号が逆になっているが、配列の頭が上位bitになっていたほうがわかりやすいかな〜?と思ってそうしただけで、深い意味はなく、どっちがいいのかはよくわからない。

4bit4入力マルチプレクサ

更にそこから選択信号を2本に増やしてやると4bitデータ4個のうち1個を選択できるようになる。これの作り方は色々あると思うが、Mux1_4を3個使って2段構成にしてみた。

# 4bitデータ4つを2bitで選択するマルチプレクサ
class Mux2_4
  def initialize
    @mux0 = Mux1_4.new
    @mux1 = Mux1_4.new
    @mux2 = Mux1_4.new
    @mux2.d0 = @mux0.o
    @mux2.d1 = @mux1.o
  end
 
  def s=(v)
    @mux0.s = v[1]
    @mux1.s = v[1]
    @mux2.s = v[0]
  end
 
  def d0=(v)
    @mux0.d0 = v
  end
 
  def d1=(v)
    @mux0.d1 = v
  end
 
  def d2=(v)
    @mux1.d0 = v
  end
 
  def d3=(v)
    @mux1.d1 = v
  end
 
  def o
    @mux2.o
  end
 
  def inspect
    "#{HLHASH[@mux2.o[0].get]}"+
    "#{HLHASH[@mux2.o[1].get]}"+
    "#{HLHASH[@mux2.o[2].get]}"+
    "#{HLHASH[@mux2.o[3].get]}"
  end
end

ROM

この4bit4入力マルチプレクサは入力データを固定にしてやることで、アドレス線2本とデータ線4本の、4bit*4の16bitデータのうち4bitを出力するROMとして扱うことができる。deadbeefは32bitなのでこのROMを2個使う。ROMを複数扱うために、ROMにはチップセレクト信号も入力してやり、この信号がLになっているROMだけがデータを出力するように構成する。

# 4bit*4の容量を持つROM
class ROM16bit
  def initialize
    @mux = Mux2_4.new
    @tsb3 = ThreeStateBuffer.new
    @tsb2 = ThreeStateBuffer.new
    @tsb1 = ThreeStateBuffer.new
    @tsb0 = ThreeStateBuffer.new
    @not = NOT.new
    @tsb3.d = @mux.o[0]
    @tsb2.d = @mux.o[1]
    @tsb1.d = @mux.o[2]
    @tsb0.d = @mux.o[3]
    @tsb3.g = @not.o
    @tsb2.g = @not.o
    @tsb1.g = @not.o
    @tsb0.g = @not.o
  end
 
  # アドレス線(2本)
  def a=(v)
    @mux.s = [v[0], v[1]]
  end
 
  # チップセレクト
  def csb=(v)
    @not.a = v
  end
 
  # 保持データの接続
  def d=(v)
    @mux.d0 = [v[0][0], v[0][1], v[0][2], v[0][3]]
    @mux.d1 = [v[1][0], v[1][1], v[1][2], v[1][3]]
    @mux.d2 = [v[2][0], v[2][1], v[2][2], v[2][3]]
    @mux.d3 = [v[3][0], v[3][1], v[3][2], v[3][3]]
  end
 
  # 出力(スリーステートバッファ)
  def o
    [
      @tsb3,
      @tsb2,
      @tsb1,
      @tsb0
    ]
  end
 
  def inspect
    "#{HLHASH[@tsb3.o.get]}"+
    "#{HLHASH[@tsb2.o.get]}"+
    "#{HLHASH[@tsb1.o.get]}"+
    "#{HLHASH[@tsb0.o.get]}"
  end
end

この例ではROMの出力はスリーステートバッファオブジェクト4個の配列であり、チップセレクト信号はそれらのG入力となっている。ROMのデータ線はROMを使う側で用意したBusオブジェクトに接続されて、その出力ピンからは選択されたROMのアドレス指定された4bitが出てくる、という感じになる。
あとはまあ、ROMのデータにdeadbeefを設定して、チップセレクトとアドレスあわせて3bitをバイナリカウンタで作って接続し、Busオブジェクトから出てくるデータを見るとdeadbeefが順番に出てくる、という話だ。
先日のロジックアナライザも含めたROM全体のソースはこちら。
https://gist.github.com/mirichi/74fc181d68736a36d62b
んで、DXRubyとこのシミュレータと一緒に使うと出力が目で見える。
https://gist.github.com/mirichi/cbf87b3626389f4e3c66

おしまい。

以上である。
ROMが作れたらRAMも簡単に作れるんじゃね?とか思ったけども、データ線を双方向にしてやらないといけないのでちょっと難しい。つってもICの場合は物理的なピン数の制約があるから双方向バスになるだけで、シミュレータなら入力線と出力線を分けてしまっても問題無いのでそのようにすれば簡単になるかもしれない。
しかしRAMだけ作っても使い道は無いのでその前にレジスタ作ってROMに簡単な命令を格納して値を設定できるとかそんな程度のものをまず作ってから、レジスタとRAMの転送ができるようにする、みたいな感じがよさげである。