지노랩 /JinoLab
[SystemVerilog] 4.14 동적 객체 이해하기 (Understanding Dynamic Objects) 본문
[SystemVerilog] 4.14 동적 객체 이해하기 (Understanding Dynamic Objects)
지노랩/JinoLab 2025. 2. 28. 09:24
SystemVerilog에서 동적 객체를 이해하는 것은 매우 중요하다. 기존 Verilog와 달리, 객체지향 프로그래밍(OOP)에서는 객체와 핸들이 분리되어 있어, 하나의 핸들이 여러 객체를 참조할 수 있다.
Verilog에서는 모든 데이터가 고정된 변수와 연결되지만, OOP에서는 객체의 수와 핸들의 수가 다를 수 있다. 예를 들어, 테스트벤치에서 수천 개의 트랜잭션 객체를 생성하더라도, 조작하는 핸들은 몇 개만 사용할 수도 있다.
이 개념은 기존 Verilog 사용자에게 익숙하지 않을 수 있으며, 객체를 다루는 새로운 방식에 적응하는 과정이 필요하다.
SystemVerilog에서는 객체를 조작하기 위해 핸들을 사용한다. 핸들은 객체를 가리키는 포인터와 유사하며, 객체는 배열, 큐, 링크드 리스트 등의 자료구조 내에 저장될 수 있다.
이러한 핸들은 메일박스(mailbox) 같은 SystemVerilog 내부 구조에 저장되기도 한다. 메일박스에 대한 자세한 내용은 7.6절에서 다룬다.
4.14.1 루틴에서 객체 전달하기 (Passing Objects to Routines)
객체를 루틴(함수, 태스크)에 전달할 때 핸들을 사용한다.
루틴은 전달된 객체를 읽거나 수정할 수 있다. 예를 들어, transmit() 태스크는 BusTran 객체의 데이터를 읽거나 변경할 수 있다.
핸들을 루틴에 전달하는 예제
task transmit(BusTran bt);
CBus.rx_data <= bt.data;
bt.timestamp = $time;
endtask
BusTran b;
initial begin
b = new(); // 객체 생성
b.addr = 42; // 값 설정
transmit(b); // 객체 전달
end
위 코드에서 transmit(b)를 호출하면 b의 핸들이 transmit() 함수로 전달된다.
그러나, transmit()이 핸들 자체를 변경하려면 ref 키워드를 사용해야 한다.
ref를 사용하지 않으면 값이 복사되므로 원본 객체의 데이터가 바뀌지 않는다.
4.14.2 태스크에서 핸들 수정하기 (Modifying a Handle in a Task)
루틴 내에서 객체 핸들을 변경하려면 ref 키워드를 사용해야 한다.
이를 생략하면, 핸들이 새 객체를 가리키더라도 호출자에게 영향을 주지 않는다.
잘못된 코드 (ref를 사용하지 않음)
task create_packet(BusTran bt);
bt = new(); // bt를 새로운 객체로 변경
bt.addr = 42;
endtask
BusTran b;
initial begin
create_packet(b); // bt를 새로운 객체로 변경하지만 호출자에는 영향 없음
$display(b.addr); // b는 여전히 null
end
위 코드에서 create_packet(b)가 호출된 후에도 b는 null 상태를 유지한다.
즉, bt = new();는 새로운 객체를 생성하지만 b에는 영향을 주지 않는다.
올바른 코드 (ref 사용)
task create_packet(ref BusTran bt);
bt = new();
bt.addr = 42;
endtask
이렇게 하면, bt = new();가 b에 영향을 주고, 새로운 객체를 할당할 수 있다.
4.14.3 실행 중 객체 수정하기 (Modifying Objects in Flight)
테스트벤치에서 반복적인 트랜잭션을 생성할 때, 객체를 올바르게 생성해야 한다.
잘못하면 하나의 객체를 여러 번 재사용하게 되어, 각 트랜잭션이 같은 데이터를 공유하게 된다.
잘못된 코드 (객체를 반복 사용)
task generator_bad(int n);
BusTran b;
b = new(); // 한 번만 객체 생성
repeat (n) begin
b.addr = $random(); // 주소 변경
$display("Sending addr=%h", b.addr);
transmit(b);
end
endtask
위 코드에서는 b 객체가 한 번만 생성되고, repeat 루프에서 계속해서 같은 객체를 재사용한다.
따라서, transmit(b)를 호출할 때마다 addr 값이 바뀌어, 마지막으로 설정된 값만 전송될 위험이 있다.
올바른 코드 (객체를 매번 새로 생성)
task generator_good(int n);
BusTran b;
repeat (n) begin
b = new(); // 객체를 매번 새로 생성
b.addr = $random();
$display("Sending addr=%h", b.addr);
transmit(b);
end
endtask
이제 루프를 돌 때마다 새로운 객체가 생성되므로, 각 트랜잭션이 독립적인 데이터를 갖게 된다.
4.14.4 핸들 배열 사용하기 (Arrays of Handles)
SystemVerilog에서 여러 개의 객체를 저장하려면 핸들 배열을 사용해야 한다.
핸들 배열을 선언하면, 각 요소는 객체를 가리키는 핸들이지만, 초기화되지 않은 상태이다.
즉, 명시적으로 new()를 호출해야 객체가 생성된다.
핸들 배열을 사용하는 예제
task generator();
BusTran barray[10]; // 핸들 배열 선언
foreach (barray[i]) begin
barray[i] = new(); // 각 객체 생성
transmit(barray[i]);
end
endtask
핸들 배열은 객체 자체를 저장하는 것이 아니라 객체를 참조하는 핸들만 저장한다.
따라서, 배열을 선언해도 각 요소가 자동으로 객체를 갖지는 않는다.
각 요소마다 new()를 호출해야 실제 객체가 생성된다.
정리
이번 글에서는 SystemVerilog에서 동적 객체를 사용하는 방법에 대해 정리했다.
핵심 개념을 정리하면 다음과 같다.
- 객체와 핸들은 다르다: 객체를 생성해도 핸들이 없으면 접근할 수 없다.
- 객체를 루틴에 전달할 때는 핸들을 사용한다: 루틴에서 객체를 수정하려면 ref 키워드를 사용해야 한다.
- 객체를 반복적으로 생성할 때는 주의해야 한다: 같은 객체를 여러 번 사용하면 값이 덮어씌워질 위험이 있다.
- 객체 배열을 사용할 때는 초기화가 필요하다: 핸들 배열을 선언하면 new()를 호출하여 개별 객체를 생성해야 한다.
SystemVerilog에서 동적 객체를 효과적으로 활용하면, 더 유연하고 유지보수가 쉬운 테스트벤치를 작성할 수 있다.
앞으로 더 심화된 개념을 학습하여 SystemVerilog의 객체지향 프로그래밍을 익히도록 하자.
Chris Spear 저자님의
SystemVerilog For Verification
A Guide to Learning the Testbench Language Features
내용을 기본으로 작성되었습니다.
'SystemVerilog검증 > 4. 객체지향 OOP 기초' 카테고리의 다른 글
[SystemVerilog] 4.16 Public vs. Private (0) | 2025.03.01 |
---|---|
[SystemVerilog] 4.15 객체 복사(Copying Objects) (2) | 2025.03.01 |
[SystemVerilog] 4.13 클래스 내 클래스 사용 (Using One Class Inside Another) (0) | 2025.02.28 |
[SystemVerilog] 4.12 스코핑 규칙 (Scoping Rules) (0) | 2025.02.27 |
[SystemVerilog] 4.11 클래스 외부에 루틴 정의(Defining Routines Outside of the Class)하는 방법 (0) | 2025.02.27 |