ハードウェアシミュレータその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