ネストしたmodule (nested modules)

概要

SVでは、module 宣言のなかに、module宣言が置けます。


以下の回路では、baseモジュールの下には、sub_moduleのインスタンス,sub_moduleの下には、sub_sub_moduleがインスタンス化されます。



module base(input d);
	wire w=d;
	wire q1,q2;
	

	initial begin
			#1;
			$display("%m %d");
	 end

	module sub_module;
		wire s=d;
		assign q1=s;

		initial begin
			#1;
			$display("%m");
		
		
	  	end

		module sub_sub_module;
			wire ss=s;
			assign q2=ss;

				
		
			initial begin
				#1;
				$display("%m d=%b w=%b q1=%b q2=%b s=%b ss=%b",d,w,q1,q2,s,ss);
				#100;
				$finish;
	  		end

		endmodule

	endmodule



endmodule

module top;
	reg ck=1;
	
	always #10 ck=~ck;

	base dut(ck);

endmodule

実行結果は次のようになります。


***** Veritak SV32 Engine Version 433 Build Jan  5 2013*****

top.dut 
top.dut.sub_module
top.dut.sub_module.sub_sub_module d=1 w=1 q1=1 q2=1 s=1 ss=1
Info: $finishコマンドを実行します。time=101ns

これをScopeTreeViewで見ると、以下のようになります。


これは、通常の記述でも、書こうと思えば書けますね。では、一体ないがよいかというと、

nest moduleを使うと、共通部/非共通部をパーティショニングできる。

上の回路で2,3行目の信号は、その下のsub_module/ sub_sub_moduleからダイレクトに見えます。また、12行目の信号は、その下のsub_sub_moduleからダイレクトに見えます。
30行目のように、一番下の sub_sub_moduleは、あたかも、自分のmoduleで宣言したかのように自由にアクセスできます。

一方、上のモジュールから、下のmoduleの信号は、見えません。共通な信号は、上の方でまとめて、共通でない信号は、ローカルモジュールとしてまとめることが出来ます。
ポートを介すると、なにかと分かりづらくなってしまいますが、設計意図を反映しやすい記述ができるようになります。

ローカライズしたmoduleは、他のモジュールからは見えませんし、それだけを取り出してインスタンス化することは、できません。

-必要でない部分を、できるだけ見えなくすることは、プログラムのカプセル化に通じる部分があると思います。


暗黙インスタンス化を使わない記述

上では、nested moduleだけに許されている”暗黙のインスタンス化”で、インスタンス化しました。暗黙インスタンスが適用されるのは、明示的インスタンス化がない場合かつ、ポートがない場合です。"暗黙のインスタンス化”では、インスタンス名=モジュール名でインスタンス化されます。なお、ポートのある記述で明示的インスタンスがない場合は、無視されてしまいます。(インスタンス化されません。)

以下は、適当に書いたサンプルです。回路としての意味はありません。(ネストモジュールにおけるスコープ(可視部分)の説明用です。)

module base  #(parameter int W=3,WORDS=4 )  (input  d);

        wire [W-1:0] q1[WORDS];
        int address_reg;//各階層で同じ名前のレジスタがある
        task base_task;
                $display("base_task  $bits(address_reg)=%3d",$bits(address_reg));
                $display("q1=%p",q1);
        endtask

        function automatic int clog2(input      longint x);
                longint val;
                if (!x) return 0;
                else begin
                        val = 1;
                        while (val<=x) begin
                          clog2++;  val <<=1;
                        end
                        return clog2;
                end
        endfunction

        initial begin
                        #3;
                        base_task;//可視task郡
         end

        module sub_module #(parameter int ADDRESS=0)();
                wire [W-1:0] s;
                assign q1[ADDRESS]=s;
                reg [ clog2(WORDS)-1:0] address_reg;//各階層で同じ名前のレジスタがある

                task sub_task;
                        $display("sub_task  $bits(address_reg)=%3d",$bits(address_reg));
                endtask

                initial begin
                        #2;
                        base_task;//可視task郡
                        sub_task;//可視task郡
                end

                module sub_sub_module #(parameter int SEL=0)();
                        wire [W-1:0] ss=$signed(d);
                        assign s[SEL]=ss[SEL];
                        byte address_reg;//各階層で同じ名前のレジスタがある                     

                        task sub_sub_task;
                                $display("sub_sub_task  $bits(address_reg)=%3d",$bits(address_reg));
                        endtask
                
                        initial begin
                                #1;
                                base_task;//可視task郡
                                sub_task;//可視task郡
                                sub_sub_task;//可視task郡                               
                        
                                #100;
                                $finish;
                        end

                endmodule
                
                for (genvar i=0; i< W; i++ ) begin
                        parameter int SEL=i;
                        sub_sub_module #(.SEL(SEL)) B();
                end
                
        endmodule

        
        for (genvar i=0;i < WORDS;i++) begin
                parameter int ADDRESS=i;
                sub_module #(.ADDRESS(ADDRESS)) B();
        end
        
endmodule

module top;
        reg ck=1;
        
        always #10 ck=~ck;

        base #(.WORDS(8),.W(4)) dut(ck);

endmodule

上の記述の階層構造は、以下のようになります。

これ例では、暗黙のインスタンス化は用いずに、明示的にインスタンス化しています。まず、task 郡ですが、子どもの階層では、自分以上親の階層のモジュールのタスクを自由に呼び出すことができます。53-55行目、38-39行目。つまり、ネストモジュールの範囲内においては、親moduleは、子どもmoduleにとって可視範囲にある訳です。

30行目で親のコンスタントファンクション(コンパイル時に使用され、実行中は、得られた値が定数として使用される)を使っていますが、これが使用可能なのも同じ理屈です。

4,30,45行目で、address_reg という同じ名前のレジスタがありますがリーガルな記述です。 
-このような同じ名前のつけかたは、推奨されないスタイルだと思います。

これらは、すべてそのスコープでのレジスタになることに注意してください。名前の探索は、まず自分のスコープ、そこで見つからなければ、その上のスコープ、そこで見つからなければ、また次の上のスコープ..という風に行われます。ベースモジュールまで探索して見つからなければ、コンパイルエラーになります。下の図のように、ネストモジュール中に他のモジュールが存在した場合は、それらは、module baseは勿論、module sub_module/sub_sub_module からも見えません。

-ただし、パッケージや、$unit スコープが定義されている場合は、さらにそれらも探索が行われます。SVの場合、かなり広大な名前空間を探索することになるので、うっかり名前をミスっても、コンパイルエラーにならず、名前が意図しないスコープでヒットしてしまうことが多いと思います。




実行結果です。address_reg は、各々違うレジスタとして、各SCOPEで認識されています。
Veritak SV32 Engine Version 433 Build Jan 6 2013*****
base_task $bits(address_reg)="32" q1='{15,15,15,15,15,15,15,15}
sub_task  $bits(address_reg)=  4
sub_sub_task  $bits(address_reg)=  8

...