시리즈 마무리: 아이템 · 인터페이스 · 드라이버 · 모니터가 어떻게 맞물리나
axi4_seq_item (아이템)
"AXI 버스트 하나"의 공통 표현
┌──────────────────────────────────────┐
│ Sequence가 randomize → Driver가 구동 │
│ Monitor가 재조립 → Scoreboard가 비교 │
└──────────────────────────────────────┘
▲ ▲
같은 객체 타입을 │ │ 공유
│ │
┌────────────────────────────────────────────────────────┐
│ axi4_if.sv │
│ master_cb (드라이브) monitor_cb (샘플) │
│ #1step / #1 #1step only │
└──────────┬────────────────────────┬────────────────────┘
│ vif.master_cb │ vif.monitor_cb
┌──────▼──────┐ ┌──────▼─────┐
│ axi4_driver │ │axi4_monitor│
│ │ │ │
│ 큐: 보낼 것 │ │ 큐: 본 것 │
│ aw_q,w_q, │ │ aw_q, │
│ ar_q │ │ w_done_q, │
│ │ │ ar_q │
│ pop → 구동 │ │ [0]참조→채움│
│ item_done() │ │ ap.write() │
│ 즉시! │ │ │
└──────┬──────┘ └─────┬──────┘
│ │
│ axi4_seq_item ▼
└───────┬─────────→ Scoreboard
(do_compare로 판정)
flowchart LR
ITEM["axi4_seq_item<br/>(아이템)<br/>버스트 1개 표현"]
SEQ["Sequence"] -->|randomize| ITEM
ITEM -->|beat_addr 공유| REF["Reference Model"]
ITEM --> DRV["axi4_driver"]
IF["axi4_if<br/>(clocking block)"] -.master_cb.-> DRV
IF -.monitor_cb.-> MON["axi4_monitor"]
DRV -->|핀 구동| BUS["AXI 버스"]
BUS -->|핀 관찰| MON
MON -->|재조립한 아이템| SB["Scoreboard"]
REF -->|예측한 아이템| SB
SB -->|do_compare| RESULT["pass / fail"]
| axi4_seq_item | axi4_if | axi4_driver | axi4_monitor | |
|---|---|---|---|---|
| 역할 | 버스트 1개 표현 + 합법성 규칙 | 신호 + 타이밍 규칙 정의 | 큐 → 버스 (구동) | 버스 → 큐 (관찰) |
| 핵심 자산 | constraint, beat_addr() | master_cb, monitor_cb | 6개 스레드 | 5개 스레드 |
| Clocking Block | — | 둘 다 제공 | master_cb 사용 | monitor_cb 사용 |
| 큐/데이터 | 동적 배열 data[], strb[] | — | pop_front() 후 구동 | [0] 참조하며 채움 |
| 핵심 트릭 | post_randomize 마스킹 | #1step race 방지 | item_done() 즉시 | [0]을 pop 없이 참조 |
| 검증 관여 | 비교 기준 제공 (do_compare) | 없음 | 없음 (구동만) | 발행만 (판정은 SB) |
아이템: AXI_DATA_WIDTH, AXI_ADDR_WIDTH, AXI_STRB_WIDTH (패키지)
인터페이스: DATA_WIDTH=32, ADDR_WIDTH=16, STRB_WIDTH=4
이 둘이 다르면:
seq_item.data는 64비트인데 vif.wdata는 32비트
→ 상위 비트 잘림 → 조용한 데이터 손실!
해결: package의 파라미터를 인터페이스 default로 사용해 단일 소스화
아이템 규칙: WRAP은 len {1,3,7,15}, 4KB 경계 준수 (constraint)
드라이버: beat_addr()로 WRAP 주소 계산해 구동
+
드라이버 이슈: W가 AW보다 먼저 나갈 수 있음 (채널 독립)
+
모니터 가정: AW를 먼저 봤다고 가정 (aw_q[0]에 채움)
↓
특정 backpressure 패턴에서 모니터가 W beat를 놓침!
→ 둘 중 하나를 고쳐야 함
(드라이버: AW 핸드셰이크 후 w_q push / 모니터: W 버퍼링)
Item (seq_item):
Encoded AXI4 protocol legality (4KB boundary, size/address alignment, WRAP beat-count
rules) directly into SystemVerilog constraints, and implemented burst-accurate address
prediction shared by driver and reference model — exposing a DUT bug where WRAP bursts
incremented linearly.
Interface:
Designed a parameterized AXI4 SystemVerilog interface with separate driver/monitor
clocking blocks (sample/drive skews) and direction-enforcing modports, eliminating
clock-edge race conditions at compile time.
Driver:
Built a pipelined AXI4 master driver with per-channel threads and configurable
outstanding depth, instrumented with peak-concurrency counters that quantitatively
prove multi-outstanding traffic reached the DUT — the same driver degrades to
strictly serialized behavior against single-outstanding slaves without code changes.
Monitor:
Architected an outstanding-aware, channel-parallel AXI4 monitor using per-phase FIFO
correlation, preventing transaction loss under multi-outstanding traffic.
왜 clocking block을 두 개 만들었나?
마스터는 드라이브+샘플이 섞여있고 모니터는 순수 샘플만 해야 하는데,
하나의 CB로는 두 역할의 방향을 동시에 표현할 수 없어서.
blocking 드라이버와 pipelined 드라이버의 차이는?
item_done()을 언제 부르냐. 즉시 부르면 시퀀스가 버스보다 앞서 달려
Outstanding이 생김.
wr_peak 카운터는 왜 필요한가?
Outstanding 미지원 슬레이브는 이 값을 1 이상 못 올림.
따라서 이 카운터가 "파이프라인이 실제로 동작했다"는 증거.
모니터가 aw_q[0]을 pop하지 않고 참조만 하는 이유는?
Handle 복사로 큐 안 객체를 직접 채우기 위해. WLAST에서야 pop.
W가 AW보다 먼저 나가면 무슨 일이 생기나?
AXI상 합법이지만 이 모니터는 AW를 먼저 봤다고 가정 → beat 유실 가능.
시퀀스가 seq_item을 재사용하면 왜 위험한가?
파이프라인 드라이버는 큐에 Handle을 저장 → 원본 재랜덤화 시 큐 내용도 변경.
Deep copy 또는 매번 create로 방지.
do_compare에서 == 대신 ===를 쓰는 이유는?
==는 2-state라 X/Z가 있으면 결과가 X(불확실). ===는 4-state로 X까지 정확히
비교 → DUT가 X를 뱉는 버그를 명확히 불일치로 잡음.
WRAP 버스트를 어떻게 검증했나?
beat_addr()가 정확한 WRAP window 계산을 하고, DUT는 WRAP을 INCR처럼 선형
증가시키는 버그가 있어 스코어보드에서 미스매치로 포착됨.