言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

prolog + Verilog HDL で 3 + 4


図書館で HDL の教科書を読むと、ハードウェア記述言語というのはある意味マルチパラダイム言語だという事がわかった。と言うのも、ハードから近い順に次のような書き方を全部サポートしているのだ。

  • 構造(Structure)記述 : 回路図をそのまま文字にしただけ
  • データフロー(Dataflow)記述 : 数式のレベルで書ける
  • 機能(Behavior)記述 : 副作用のある代入や、制御構造が使える。

こ、これは面白い!どこが面白いかと言うと、この Structure, Dataflow, Behavior というのは、プログラミング言語で言うと、論理型言語、関数型言語手続き型言語に対応している。しかもこの順序は、実装から遠いと思っていた順だ。つまり、普通手続き型言語が一番機械語に近くて、論理型言語が一番遠くて実装がややこしい。しかし、ハードの世界になると逆転して、実は論理型が一番メカに近くて、手続き型が一番人工的なのだ!うーん目から鱗

さて、本も読んだし何かやらなくては。やはり Smalltalker としては 3 + 4 が欠かせない。3 + 4 を計算する回路を考えてみた。と、言っても。ハードウェア記述言語というのは相当高級で、3 + 4 を計算するには 3 + 4 と書けば済んでしまう!じゃあこうしよう。実際に回路を手で組み立てるつもりで、and, or, not の論理演算だけを使ってやれば面白いんじゃないか。しかも構造記述は論理型言語だ!という自説を確かめる為に、prolog でシミュレーションしてから Verilog で書くというのはどうだ。XILINX ISE Web Pack は遅いので、デバッグのイライラも避けられて一石二鳥だ。と言うわけで prolog で書いたソースはこんなの。

% A Logic Gate for 3 + 4
%
% ?- add3bit(Sum, [0, 1, 1], [1, 0, 0]).

% Primitive Gates (Output, Input, ...)
and(0, 0, 0).
and(0, 1, 0).
and(0, 0, 1).
and(1, 1, 1).

or(0, 0, 0).
or(1, 1, 0).
or(1, 0, 1).
or(1, 1, 1).

not(1, 0).
not(0, 1).

% Half Adder
halfAdder(Carry, Sum, A, B) :-
    and(Carry, A, B),
    or(X, A, B),
    not(Y, Carry),
    and(Sum, X, Y).

% Full Adder
fullAdder(Carry, Sum, A, B, C) :-
    halfAdder(C1, X, A, B),
    halfAdder(C2, Sum, X, C),
    or(Carry, C1, C2).

% 3 Bit Addr
add3bit([Sum2, Sum1, Sum0], [A2, A1, A0], [B2, B1, B0]) :-
    halfAdder(C0, Sum0, A0, B0),
    fullAdder(C1, Sum1, A1, B1, C0),
    fullAdder(_, Sum2, A2, B2, C1).

で、verilog に書き直したのがこんなの。

module halfAdder (output Carry, output Sum, input A, input B);
    and(Carry, A, B);
    or(X, A, B);
    not(Y, Carry);
    and(Sum, X, Y);
endmodule

module fullAdder (output Carry, output Sum, input A, input B, input C);
    halfAdder hadd0 (C1, X, A, B);
    halfAdder hadd1 (C2, Sum, X, C);
    or(Carry, C1, C2);
endmodule

module threePlusFour(
    input CLOCK,
    input [2:0] A,
    input [2:0] B,
    output [2:0] Sum
    );

    halfAdder hadd0 (C0, Sum[0], A[0], B[0]);
    fullAdder fadd0 (C1, Sum[1], A[1], B[1], C0);
    fullAdder fadd1 (_, Sum[2], A[2], B[2], C1);
endmodule

案の定、ソースコードは殆ど同じになった。実行結果が上の図です。