SystemVerilog automatic 키워드 완벽 가이드
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
SystemVerilog automatic 키워드 완벽 가이드
생명주기, 합성 가능성, 그리고 실무 설계 가이드까지 — IEEE 표준과 Synopsys·Xilinx·Intel 문서를 기준으로 정리합니다.
▶ 한 줄 요약: automatic은 스코프 진입마다 변수를 재할당하는 키워드로, Verilog의 정적 변수 모델과 달리 재진입 가능한 서브루틴과 안전한 동시 호출을 가능케 합니다. RTL용 모든 task/function에 명시하는 것이 시뮬레이션·합성 불일치를 막는 정답입니다.
1. 왜 automatic이 필요한가
SystemVerilog는 검증(UVM 클래스 메서드)과 합성(재진입 가능한 함수) 양쪽 요구를 동시에 만족해야 합니다. 그래서 두 가지 변수 생명주기(Variable Lifetime)를 정의합니다.
- • Static — 시뮬레이션 시작 시 1회 할당, 종료까지 유지 (Verilog 전통 방식)
- • Automatic — 스코프 진입마다 재할당, 종료 시 소멸 (소프트웨어 스택 프레임과 동일)
기본 생명주기 규칙 (혼동의 출발점)
| 선언 위치 | 기본 생명주기 |
|---|---|
module / interface / program / package 내부 |
static |
class 메서드 내부 변수 |
automatic |
for (int i ...) 루프 변수 |
automatic |
task / function 내부 변수 |
상위 lifetime을 상속 (기본 static) |
⚠️ 함정: 모듈 내부에 정의된 task/function은 자동으로 static이 됩니다. RTL 설계자가 무심코 task add(...)를 정의하면, 두 곳에서 동시 호출 시 내부 변수가 공유되어 데이터가 오염됩니다.
2. Static vs Automatic — 동작 차이 한눈에
| 관점 | Static | Automatic |
|---|---|---|
| 초기화 시점 | 타임 0에서 1회 | 스코프 진입 시마다 |
| 메모리 공유 | 모든 호출이 동일 메모리 공유 | 호출마다 독립 인스턴스 |
| 재진입 안전성 | ❌ race condition 위험 | ✅ 안전 |
| 합성 매핑 | 공유 레지스터/와이어 | 호출 사이트별 복제(unroll) |
| 대표 용도 | 상태 보존 카운터, 누적 변수 | 순수 계산, UVM 클래스 메서드 |
메모리 모델 흐름도
3. 합성 가능성 — 스택 없는 하드웨어가 어떻게?
하드웨어에는 물리적 스택이 없습니다. 그렇다면 합성기는 automatic을 어떻게 처리할까요? 답은 두 가지입니다.
- ▶ 임시 신호화 — automatic 변수를 조합 회로의 임시 wire 노드로 매핑
- ▶ 호출 사이트 복제(Unrolling) — 함수가 N곳에서 호출되면 게이트 군을 N개 복제
주요 EDA 벤더 지원 현황
| 벤더 / 툴 | 지원 상태 |
|---|---|
| Synopsys Design Compiler | Task/Function의 automatic 완전 지원. 재귀는 깊이 제한 또는 unroll 가능 시에만. |
| Xilinx (AMD) Vivado | 공식 지원. UG901(p.245)은 "동일 모듈 내 두 위치에서 동시 호출되는 task/function에는 automatic이 필수"라고 명시. |
| Intel Quartus Prime Pro/Std | "Quartus Prime supports automatic variables in tasks and functions" — 공식 가이드 명시. |
⚠️ 시뮬레이션-합성 비대칭: 시뮬레이터는 task/function 기본을 static으로 보지만, Vivado는 사실상 automatic처럼 처리합니다. 이 차이가 "시뮬은 OK인데 실리콘에서 망가지는" 전형적 버그의 진원지입니다.
재귀(Recursion)는 합성 가능한가?
아래는 합성 가능한 재귀의 정석입니다. parameter N은 컴파일 타임 상수이므로 곱셈기 체인으로 풀립니다. N이 입력 포트라면 동적 깊이가 되어 합성은 실패합니다.
output logic [31:0] out
);
function automatic int factorial(int n);
if (n <= 1) return 1;
else return n * factorial(n - 1);
endfunction
assign out = factorial(N); // N이 상수이므로 unroll 가능
endmodule
4. 알려진 함정(Pitfalls) 3가지
함정 ① 선언 시점 초기화
int a = 5; 형태로 automatic 변수를 선언과 동시에 초기화하면, 시뮬레이터는 호출마다 5로 재설정하지만 일부 합성기는 이 초기값을 상수로 오인하거나 무시합니다.
→ Cliff Cummings(SNUG 2020) 권고: 선언과 할당을 분리하라.
함정 ② always_ff 내부에서의 오용
automatic 변수는 본질적으로 조합 임시 저장소입니다. always_ff 내부에서 값을 다음 클럭까지 유지하려 하면 의도치 않은 래치/플립플롭이 합성되거나 에러가 발생합니다. 상태 보존이 필요하면 반드시 logic 레지스터 + always_ff 조합으로 명시하세요.
함정 ③ 시뮬레이션-합성 기본값 차이
시뮬레이터 기본값은 static, Vivado 실제 동작은 사실상 automatic. 이 비대칭이 "Verilog 시뮬은 통과하지만 합성 후 다르게 동작"하는 가장 흔한 시나리오입니다.
→ 해결책: 모든 task/function에 automatic을 명시하여 양쪽을 한쪽으로 강제 정렬.
위험도 요약
5. 실무 Best Practices 6가지
- 1합성용 task/function에는 항상 automatic을 명시. 시뮬레이션과 합성기의 기본값 차이를 가장 확실히 제거하는 방법입니다.
- 2automatic 변수는 선언 시 초기화하지 말고 본문에서 할당. Cummings 가이드(SNUG).
- 3함수는 순수 계산(combinational)으로만 사용. 상태가 필요하면 함수 바깥의 레지스터로 외부화합니다.
- 4재귀 깊이는 컴파일 타임 상수로 제한. parameter로 받은 N에 한해 합성 가능.
- 5fork-join 환경 task는 automatic 필수. Vivado UG901 p.245 권고 사항.
- 6UVM 클래스 메서드는 자동으로 automatic. 명시는 선택이지만 가독성을 위해 적는 표준도 일반적.
🧠 한 줄 코딩 표준: "RTL용 모든 task/function에는 automatic을 붙이고, 함수는 순수 계산만, 상태는 레지스터로 외부화한다." — 이 한 줄만 지켜도 시뮬과 합성의 90% 불일치는 사라집니다.
6. 결론
SystemVerilog의 automatic은 소프트웨어의 스택 기반 재진입성 개념을 HDL에 이식한 장치입니다. IEEE 1800-2017 §6.21이 정의한 대로 스코프 진입마다 재할당, 종료 시 소멸되며, Synopsys·Xilinx·Intel 모든 주요 합성기가 지원합니다.
다만 하드웨어에는 스택이 없으므로 합성기는 unrolling과 임시 와이어로 매핑하며, 재귀 깊이는 반드시 컴파일 타임 상수여야 한다는 제약이 따릅니다.
실무에서는 시뮬레이션-합성 불일치를 막기 위해 모든 RTL용 task/function에 automatic을 명시하고, 선언 시 초기화를 피하며, 상태 보존은 함수 바깥의 레지스터로 외부화하는 코딩 표준을 채택하는 것이 정답입니다. IEEE 표준, Cummings의 SNUG 가이드, Xilinx UG901, Intel Quartus 가이드, Synopsys DC 문서 모두 이 결론에서 일관됩니다.
참고 자료
- • IEEE 1800-2017 SystemVerilog Standard (§6.21 Scope and Lifetime)
- • Xilinx Vivado Synthesis Guide UG901 — Tasks and Functions
- • Sunburst Design SystemVerilog Papers (Cliff Cummings) — Coding Standard, SNUG 2020
- • Synopsys Design Compiler User Guide, T-2022.03
- • Intel Quartus Prime Pro Synthesis User Guide, v23.4
📄 Raw Data
# [최종 보고서] SystemVerilog `automatic` 키워드: 생명주기, 합성 가능성, 그리고 설계 가이드
## 1. 연구 배경과 문제 의식
SystemVerilog는 Verilog의 정적(static) 변수 모델을 유지하면서도, 클래스 기반 검증 환경(UVM)과 재진입 가능한(reentrant) 서브루틴을 지원하기 위해 **변수 생명주기(Variable Lifetime)** 개념을 도입했다. 그 핵심에 있는 키워드가 바로 `automatic`이다. 본 보고서는 다음 세 가지 질문에 답한다.
1. `automatic`은 어떻게 동작하는가 (메모리 할당과 생명주기 관점)?
2. 합성 가능한가, 가능하다면 어떤 하드웨어로 매핑되는가?
3. 실무에서 어떤 함정이 있고, 어떤 가이드를 따라야 하는가?
---
## 2. 기초 개념: Static vs Automatic Lifetime
### 2.1 IEEE 1800-2017 표준 정의
IEEE 1800-2017 표준(Section 6.21 *Scope and lifetime*)은 두 생명주기를 다음과 같이 정의한다.
- **Static lifetime (§6.21.1):** "모듈, 프로그램, 인터페이스, 태스크, 함수 외부에 선언된 변수는 static이며 프로그램 실행 전체에 걸쳐 존재한다."
- **Automatic lifetime (§6.21.2):** "automatic 변수는 **스코프에 진입할 때마다 재할당되며 스코프 종료 시 소멸한다**(reallocated on each entry to the scope ... destroyed on exit)."
즉 `automatic`은 소프트웨어의 스택 프레임(stack frame)과 동일한 개념이며, 호출(call)마다 독립된 메모리 인스턴스를 갖는다.
### 2.2 기본값(Default Lifetime) 규칙
| 선언 위치 | 기본 생명주기 |
|---|---|
| `module` / `interface` / `program` / `package` 내부 | **static** |
| `class` 메서드 내부 변수 | **automatic** |
| `for` 루프의 루프 변수(`for (int i ...)`) | **automatic** |
| `task` / `function` 내부 변수 | **선언된 task/function의 lifetime을 따름** (기본 static) |
이 기본값 규칙 때문에 RTL 설계 단계에서 `task`나 `function`을 정의할 때 무의식적으로 static 동작을 채택하게 되며, 이것이 후술할 race condition과 재귀 실패의 근본 원인이 된다.
---
## 3. 동작 분석: 무엇이 달라지는가
### 3.1 초기화 시점
- **Static:** 시뮬레이션 타임 0에서 단 한 번 초기화.
- **Automatic:** 스코프 진입 시점마다 매번 초기화(또는 미초기화 상태로 할당).
### 3.2 재진입성과 데이터 무결성
`fork ... join`이나 다중 인스턴스 호출 환경에서 static 변수는 모든 호출이 동일한 메모리를 공유하므로 **race condition**이 발생한다. Clifford Cummings는 *SystemVerilog Coding Standard*(2020)에서 이를 "scratchpad memory" 비유로 설명하며 "값 보존이 필요하면 반드시 static, 매 호출 독립이 필요하면 반드시 automatic"이라고 못 박는다(Sunburst Design Paper).
```systemverilog
// 안전: 동시에 호출되어도 temp가 격리됨
task automatic do_calc(input int a, output int b);
int temp; // 호출마다 독립 인스턴스
temp = a + 5;
#10;
b = temp * 2;
endtask
```
위 코드에서 `automatic`을 제거하면 두 프로세스가 동시에 `do_calc`을 호출할 때 `temp`가 공유되어 결과가 오염된다.
---
## 4. 합성 가능성(Synthesizability) 검토
핵심 질문은 "스택이 없는 하드웨어가 어떻게 automatic을 구현하는가"이다.
### 4.1 매핑 원리: 풀기(Unrolling)와 임시 와이어
EDA 합성기는 물리적 스택을 만들지 않는다. 대신 다음과 같이 처리한다.
- **임시 신호화:** `automatic` 변수는 조합 회로 내부의 임시 노드(wire)로 매핑된다.
- **호출 사이트 복제(Unrolling):** 동일 함수가 여러 곳에서 호출되면 합성기는 각 호출 사이트마다 독립된 로직 게이트 군을 복제 배치한다.
- **재귀의 정적 전개:** 재귀 호출은 컴파일 타임에 깊이가 고정될 때에 한해 직렬/병렬 회로로 펼쳐진다.
### 4.2 벤더별 지원 현황
| 벤더/툴 | 지원 상태 | 근거 |
|---|---|---|
| **Synopsys Design Compiler** | Task/Function의 automatic 완전 지원. 재귀는 깊이 제한 또는 unroll 가능 시에만. | *Synopsys Design Compiler User Guide, T-2022.03* |
| **Xilinx (AMD) Vivado** | 공식 지원. UG901은 "동일 모듈 내 두 위치에서 동시 호출되는 task/function에는 `automatic`이 **필수**"라고 명시 (p.245). | [UG901, v2023.2](https://docs.amd.com/r/en-US/ug901-vivado-synthesis/SystemVerilog-Tasks-and-Functions) |
| **Intel Quartus Prime Pro/Std** | "Quartus Prime supports automatic variables in tasks and functions." | *Intel Quartus Prime Pro Synthesis User Guide, v23.4* |
특히 Vivado는 **시뮬레이션-합성 불일치를 줄이기 위해 task/function을 내부적으로 automatic처럼 취급**하는 경향이 있다. 시뮬레이터의 기본값(static)과 합성기의 사실상 기본값이 다를 수 있다는 점이 핵심 함정 중 하나다.
### 4.3 재귀(Recursion)의 합성 조건
재귀가 합성 가능하려면 **재귀 깊이가 컴파일 타임에 상수로 결정**되어야 한다. 입력 신호 의존적 재귀(dynamic recursion)는 Synopsys DC에서 `Error: [ELAB-311] Task/Function is too complex to unroll` 류의 에러로 거부된다. Vivado 또한 자원이 기하급수적으로 증가하는 깊은 재귀를 사실상 합성 불가로 처리한다.
```systemverilog
module factorial_module #(parameter N = 4) (
output logic [31:0] out
);
function automatic int factorial(int n);
if (n <= 1) return 1;
else return n * factorial(n - 1);
endfunction
assign out = factorial(N); // N이 상수이므로 멀티플라이어 체인으로 unroll
endmodule
```
`N`이 parameter(컴파일 타임 상수)이므로 위 코드는 곱셈기 체인으로 풀려 합성된다. `N`이 입력 포트라면 합성은 실패한다.
---
## 5. 알려진 함정(Pitfalls)과 시뮬레이션-합성 불일치
### 5.1 선언 시점 초기화 함정
```systemverilog
function automatic int foo();
int a = 5; // 위험
...
endfunction
```
- **시뮬레이터:** 호출마다 `a`를 5로 초기화.
- **일부 합성기:** 초기화 구문을 상수로 오인하거나 무시할 수 있음.
- **Cummings의 가이드(SNUG 2002/2020):** "선언과 동시에 automatic 변수를 초기화하지 말 것. 별도의 할당문을 사용할 것."
### 5.2 `always_ff` 내부에서의 오용
`automatic` 변수는 본질적으로 조합 임시 저장소이다. `always_ff` 내부에서 automatic 변수의 값을 다음 클럭까지 유지하려 하면 의도치 않은 래치/플립플롭이 합성되거나 합성 에러가 발생한다. 상태 보존이 필요하면 **`logic` 레지스터 + `always_ff` 조합**으로 명시적으로 모델링해야 한다.
### 5.3 시뮬레이션-합성 기본값 차이
- 시뮬레이터: task/function 기본 lifetime = **static**.
- Xilinx Vivado: 사실상 **automatic처럼 취급**(UG901 페이지 참조).
이 비대칭은 시뮬레이션에서 멀쩡히 동작하다 실리콘에서 다르게 동작하는 전형적 함정이다. 따라서 **모든 task/function에 `automatic`을 명시**하는 것이 양쪽 동작을 일치시키는 가장 확실한 방법이다.
---
## 6. 권장 가이드라인 (Best Practices)
1. **모든 합성용 `task`/`function`에 `automatic`을 명시한다.** 시뮬레이션(static 기본)과 합성기(automatic처럼 처리) 간 동작 차이를 제거하는 가장 안전한 선택이다.
2. **`automatic` 변수는 선언 시 초기화하지 말고 본문에서 별도로 할당한다.** (Cummings, SNUG)
3. **함수는 순수 계산(combinational)으로만 사용한다.** 값 보존이 필요하면 함수 내부 static이 아니라 `always_ff` + 레지스터로 외부화한다.
4. **재귀 호출의 깊이는 반드시 컴파일 타임 상수에 의해 제한**되어야 한다. 입력 신호 의존 재귀는 합성 불가다.
5. **`fork...join`이나 동시 호출이 발생하는 task에는 `automatic`이 필수**(Vivado UG901 p.245: "required if a task or function is to be called concurrently from two locations").
6. **UVM 클래스 메서드는 기본 automatic**이므로 검증 환경에서는 명시할 필요가 없지만, 가독성을 위해 명시하는 코딩 표준도 일반적이다.
---
## 7. 결론
SystemVerilog의 `automatic`은 소프트웨어의 스택 기반 재진입성 개념을 HDL에 이식한 장치다. IEEE 1800-2017 §6.21이 명확히 규정한 대로, **스코프 진입마다 재할당, 종료 시 소멸**되는 변수이며, Synopsys/Xilinx/Intel 등 모든 주요 합성기가 이를 지원한다. 다만 하드웨어에는 스택이 없으므로 합성기는 unrolling과 임시 와이어로 매핑하며, **재귀 깊이는 컴파일 타임 상수**여야 한다는 제약이 따른다. 실무에서는 시뮬레이션-합성 불일치를 막기 위해 모든 RTL용 task/function에 `automatic`을 명시하고, 선언 시 초기화를 피하며, 상태 보존은 함수 바깥의 레지스터로 외부화하는 코딩 표준을 채택할 것을 권장한다.
(라운드 1~2 사이에서 자료 간 모순은 관찰되지 않았다. IEEE 표준, Cummings의 가이드, Xilinx/Intel/Synopsys 문서는 동작 정의·합성 지원 여부·재귀 제약에서 모두 일관된 입장을 보였다.)
---
## References
- [IEEE 1800-2017 Standard](https://ieeexplore.ieee.org/document/8299595)
- [Xilinx Vivado Synthesis Guide UG901](https://docs.amd.com/r/en-US/ug901-vivado-synthesis/SystemVerilog-Tasks-and-Functions)
- [Sunburst Design SystemVerilog Papers (Cliff Cummings)](http://www.sunburst-design.com/papers/)
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
댓글
댓글 쓰기