マルチプレクサは、複数の入力のどれか一つを選択する論理回路です。
わたしはマルチプレクサが必要なときは、FPGAベンダの供給する出来合いのモジュールを使用しています。これは、FPGAに論理を組む際にはマルチプレクサを多用することから、少しでも効率の良いものを使用したいと考えてのことです。
Altera社の提供するQualtus IIを使用する場合には、以下のような手順でマルチプレクサのモジュールを形成します。
まず、画面上部のプルダウンメニューで「Tools」の「MegaWizard Plug-In Manager」を選択します。
次に、“Create a new custom megafunction variation”を選択し、新しいメガファンクションモジュールの作成を指定します。マルチプレクサを形成する場合は、Nextボタンを押して現れるポップアップウインドウで、下図に示すように、“Gates”の+記号をクリックしてその内容を表示させて“LPM_MUX”を選択します。形成されるモジュールのタイプにVerilogを選択し、モジュール名として任意の名前(ここでは“mux8_16”としました)を記入してNextボタンを押します。
Nextボタンを押すと、下に示す機能選択ウィンドウが開きます。ここで、入力信号の数とビット幅を指定します。ウィンドウの左側には形成されるモジュールのブロック図が表示されますので、これが意図したものであることを確認しておきましょう。
あとは、Next、Next、Finishとボタンを押していけばモジュールが自動的に出来上がります。
メガファンクションウィザードを用いて作成されたモジュールはテキストファイルですので、内容をみることができます。上のようにした場合に形成されるファイル名は“mux8_16.v”となり、その内容は以下のようになります。
// megafunction wizard: %LPM_MUX%
// GENERATION: STANDARD
// VERSION: WM1.0
// MODULE: lpm_mux
// ============================================================
// File Name: mux8_16.v
// Megafunction Name(s):
// lpm_mux
……途中省略……
module mux8_16 (
data0x,
data1x,
data2x,
data3x,
data4x,
data5x,
data6x,
data7x,
sel,
result);
input [15:0] data0x;
input [15:0] data1x;
input [15:0] data2x;
input [15:0] data3x;
input [15:0] data4x;
input [15:0] data5x;
input [15:0] data6x;
input [15:0] data7x;
input [2:0] sel;
output [15:0] result;
……以下省略……
このファイルから、module文以下を呼び出し側のソースファイルにコピー・ペーストしてこの部分を編集することで、ミスの少ない、迅速な作業が可能となります。
メガファンクションモジュールを使用した、ビット幅16の8入力マルチプレクサのトップモジュールは以下のようになります。この場合も、遅延時間を計測するために、マルチプレクサの入力側と出力側にレジスタを挿入しています。
module multiplex_ip(
input [15:0] d0_p, d1_p, d2_p, d3_p, d4_p, d5_p, d6_p, d7_p,
input [2:0] sel_p,
output reg [15:0] result_p,
input clock);
reg [15:0] d0, d1, d2, d3, d4, d5, d6, d7;
reg [2:0] sel;
wire [15:0] result;
mux8_16 u1(
.data0x(d0), .data1x(d1), .data2x(d2), .data3x(d3), .data4x(d4),
.data5x(d5), .data6x(d6), .data7x(d7), .sel(sel), .result(result));
always@(posedge clock) begin
d0 <= d0_p; d1 <= d1_p; d2 <= d2_p; d3 <= d3_p;
d4 <= d4_p; d5 <= d5_p; d6 <= d6_p; d7 <= d7_p;
sel <= sel_p; result_p <= result;
end
endmodule
生成された論理ブロックは次のようになります。
上図中央部の薄緑色の四角がウィザードで形成されたマルチプレクサモジュールですが、この内部はみることができません。トップモジュールの最大クロック周波数は167.31 MHz、使用されたロジックエレメントの総数は147となりました。シミュレーションの結果を下図に示します。
通常、マルチプレクサはファンクション文を用いて記述されます。この場合のVerilogソースコードを以下に示します。
module multiplex_func(
input [15:0] d0_p, d1_p, d2_p, d3_p, d4_p, d5_p, d6_p, d7_p,
input [2:0] sel_p,
output reg [15:0] result_p,
input clock);
reg [15:0] d0, d1, d2, d3, d4, d5, d6, d7;
reg [2:0] sel;
wire [15:0] result = mux8_16(sel, d0, d1, d2, d3, d4, d5, d6, d7);
always@(posedge clock) begin
d0 <= d0_p; d1 <= d1_p; d2 <= d2_p; d3 <= d3_p;
d4 <= d4_p; d5 <= d5_p; d6 <= d6_p; d7 <= d7_p;
sel <= sel_p; result_p <= result;
end
function [15:0] mux8_16;
input [2:0] sel;
input [15:0] d0, d1, d2, d3, d4, d5, d6, d7;
case (sel)
3'h0: mux8_16 = d0;
3'h1: mux8_16 = d1;
3'h2: mux8_16 = d2;
3'h3: mux8_16 = d3;
3'h4: mux8_16 = d4;
3'h5: mux8_16 = d5;
3'h6: mux8_16 = d6;
3'h7: mux8_16 = d7;
endcase
endfunction
endmodule
上のリストで、ファンクション“mux8_16”は“function”から“endfunction”までの部分で定義されています。ファンクション名mux8_16の前に書かれた“[15:0]”はこの関数が出力する信号線を定義したものであり、この行に続く“input”文で入力信号を定義しています。ここで注意すべき点は、入力信号を定義する順序で、ファンクションを呼び出す際の順序と一致する必要があります。
ファンクション文を使う理由は、その内部でcase文が使用できるからです。ここでは、case文を用いてselの値に応じて、関数の出力に入力信号のd0からd7をそれぞれ割り当てています。
トップモジュールは“wire [15:0] result = mux8_16(sel, d0, d1, d2, d3, d4, d5, d6, d7);”の部分でファンクション“mux8_16”を呼び出しています。
生成された論理ブロックは次のようになります。
上図では下半分を省略しましたが、ビット幅1の8入力マルチプレクサを16個並べてビット幅16の8入力マルチプレクサを形成した形となっています。トップモジュールの最大クロック周波数は167.31 MHz、使用されたロジックエレメントの総数は147と、メガファンクションを用いた場合と同じになりました。
最も簡単なマルチプレクサである2入力のマルチプレクサはif文で表現することができます。そしてこれをn段直列に接続することで2n入力のマルチプレクサを構成することができます。この場合のVerilogソースコードを以下に示します。
module multiplex_if(
input [15:0] d0_p, d1_p, d2_p, d3_p, d4_p, d5_p, d6_p, d7_p,
input [2:0] sel_p,
output reg [15:0] result_p,
input clock);
reg [15:0] d0, d1, d2, d3, d4, d5, d6, d7;
reg [2:0] sel;
wire [63:0] work2 = sel[2] ? {d7, d6, d5, d4} : {d3, d2, d1, d0};
wire [31:0] work1 = sel[1] ? work2[63:32] : work2[31:0];
wire [15:0] result = sel[0] ? work1[31:16] : work1[15:0];
always@(posedge clock) begin
d0 <= d0_p; d1 <= d1_p; d2 <= d2_p; d3 <= d3_p;
d4 <= d4_p; d5 <= d5_p; d6 <= d6_p; d7 <= d7_p;
sel <= sel_p; result_p <= result;
end
endmodule
生成された論理ブロックは次のようになります。
この場合の最大クロック周波数は167.31 MHz、使用されたロジックエレメントの総数は147と、メガファンクションを用いた場合およびファンクション文を用いた場合と同じになりました。
以上の結果ではいずれの書き方も同等の結果となりましたが、性能を追求する場合はベンダーの提供するメガファンクションの利用が、環境依存性をなくすためにはファンクション文を用いた記述が良いのではないかと思います。また、速度が問題となる場合には、if式を用いた例で示したように多段構成としてパイプライン化するのが効果的であると思われます。