Verilog 계층적 참조 범위 마스터하기: 모듈과 블록 간 이름 충돌 방지
대부분의 프로그래밍 언어에는 범위(scope)라는 특성이 있어, 코드의 특정 섹션이 변수와 메서드에 대해 가시성을 정의합니다. 범위는 같은 네임스페이스 내 다른 객체 이름 간의 충돌을 피하기 위한 네임스페이스를 정의합니다.
Verilog는 모듈, 함수, 태스크, 명명된 블록, generate 블록에 대해 새로운 범위를 정의합니다.
module tb;
reg signal;
// 같은 범위에서 이미 존재하는 이름으로 다른 변수 선언 불가
reg signal; // 에러
// 하지만 'signal' 이름은 태스크 안에서 재사용 가능 (다른 범위이므로)
task display();
reg signal = 1;
$display("signal = %0b", signal);
endtask
endmodule
식별자(신호 이름 등)는 주어진 범위에서 한 종류의 항목만 선언하는 데 사용될 수 있습니다. 즉, 같은 범위에서 다른 데이터 타입의 두 변수가 같은 이름을 가질 수 없으며, 태스크와 변수가 같은 이름을 가질 수 없고, 넷과 게이트 인스턴스도 같은 이름을 가질 수 없습니다.
Verilog의 모든 식별자는 고유한 계층적 경로 이름을 가지며, 각 모듈 인스턴스, 태스크, 함수, 명명된 begin end 또는 fork join 블록이 새로운 레벨 또는 범위를 정의합니다.
계층적 참조 예제
module tb;
// 서로 다른 두 모듈의 인스턴스 생성
A uA();
B uB();
// 신호를 선언하고 시뮬레이션 시작 10ns에 값 출력하는 명명된 블록
initial begin : TB_INITIAL
reg signal;
#10 $display("signal=%0d", signal);
end
// 이 initial 블록에서 계층적 참조를 사용하여 다른 범위에 접근 시도
initial begin
TB_INITIAL.signal = 0;
uA.display();
uB.B_INITIAL.B_INITIAL_BLOCK1.b_signal_1 = 1;
uB.B_INITIAL.B_INITIAL_BLOCK2.b_signal_2 = 0;
end
endmodule
module A;
task display();
$display("Hello, this is A");
endtask
endmodule
module B;
initial begin : B_INITIAL
#50;
begin : B_INITIAL_BLOCK1
reg b_signal_1;
#10 $display("signal_1=%0d", b_signal_1);
end
#50;
begin : B_INITIAL_BLOCK2
reg b_signal_2;
#10 $display("signal_2=%0d", b_signal_2);
end
end
endmodule
시뮬레이션 로그
xcelium> run
Hello, this is A
TB signal=0
signal_1=1
signal_2=0
xmsim: *W,RNQUIE: Simulation is complete.
상향 이름 참조 (Upwards Name Referencing)
하위 레벨 모듈은 계층 구조에서 그 위의 모듈에 있는 항목을 참조할 수 있습니다. 예를 들어, TB_INITIAL 블록의 signal이 모듈 A의 display 태스크에서 보일 수 있습니다.
module A;
task display();
$display("Hello, this is A");
// 상향 참조, TB_INITIAL이 이 모듈에서 보임
#5 TB_INITIAL.signal = 1;
endtask
endmodule
모듈 A에 의한 상향 참조 변경으로 TB signal이 0 대신 1이 됨을 알 수 있습니다.
시뮬레이션 로그
xcelium> run
Hello, this is A
TB signal=1
signal_1=1
signal_2=0
xmsim: *W,RNQUIE: Simulation is complete.
여러 중첩 모듈이 있는 또 다른 예제로, 리프 노드가 상향 계층적 참조를 통해 상위 노드의 멤버에 직접 접근할 수 있습니다.
module tb;
A a();
function display();
$display("Hello, this is TB");
endfunction
endmodule
module A;
B b();
function display();
$display("Hello, this is A");
endfunction
endmodule
module B;
C c();
function display();
$display("Hello, this is B");
endfunction
endmodule
module C;
D d();
function display();
$display("Hello, this is C");
endfunction
endmodule
module D;
initial begin
a.display(); // 또는 A.display()
b.display(); // 또는 B.display()
c.display(); // 또는 C.display()
a.b.c.display();
end
endmodule
시뮬레이션 로그
xcelium> run
Hello, this is A
Hello, this is B
Hello, this is C
Hello, this is C
xmsim: *W,RNQUIE: Simulation is complete.
컴파일러가 b.display()를 찾으면,
- 현재 범위(모듈 D 내부)에서 b가 정의되어 있는지 확인. 없으면 둘러싼 범위에서 이름을 찾고 모듈 범위까지 위로 이동. 여전히 없으면 다음 단계로.
- 부모 모듈의 최외곽 범위에서 찾고, 없으면 계층을 계속 위로 올라감.