상태기계와 메타스테이빌리티
한 줄 요약 — 측정 시퀀스(채널 선택 → 세틀링 → 변환 → 저장)는 상태기계입니다. 그리고 바깥에서 들어오는 신호는 반드시 두 번 걸러야 합니다 — 안 그러면 아주 가끔 회로가 미쳐 버립니다.
왜 상태기계로 쓰는가
측정 시퀀스를 조건문으로 흩뿌리면 지금 어느 단계인지가 코드 여러 곳에 흩어집니다. 상태기계는 그것을 변수 하나로 모읍니다.
상태가 다섯 개, 전이가 여섯 개. 이것을 표로 적어 두면 빠뜨린 경우가 눈에 보입니다 — 조건문으로 흩어 놓으면 안 보입니다.
| 지금 | 조건 | 다음 | 출력 |
|---|---|---|---|
| IDLE | start | SELECT | — |
| SELECT | 항상 | SETTLE | addr 갱신, en=0 |
| SETTLE | timer done | CONVERT | en=1 |
| CONVERT | eoc | STORE | soc=1 |
| STORE | ch < N | SELECT | we=1 |
| STORE | ch == N | IDLE | done=1 |
무어와 밀리
| 출력이 | 특징 | |
|---|---|---|
| 무어 | 상태만으로 정해짐 | 출력이 글리치 없음, 상태가 한 개 더 필요할 때가 있음 |
| 밀리 | 상태 + 입력 | 상태가 적음, 출력에 글리치 가능 |
측정 회로에서는 무어를 씁니다. 출력이 아날로그 스위치아날로그 스위치 analog switch신호를 그대로 통과시키거나 끊는 반도체 스위치로, 다채널 측정의 채널 선택이 여기서 이뤄집니다.자세히 보기 → 나 ADC 변환 시작으로 가는데, 밀리의 출력 글리치가 그대로 사고입니다 (07 카르노맵에서 본 그것).
더 확실한 방법: 출력을 플립플롭 한 단 더 거쳐 내보냅니다(등록 출력). 한 클럭 늦어지는 대신 글리치가 원천적으로 불가능합니다.
상태 인코딩
| 방식 | 3상태 예 | 플립플롭 | 다음 상태 논리 |
|---|---|---|---|
| 이진 | 00, 01, 10 | 복잡 | |
| 원핫 | 001, 010, 100 | 아주 단순 | |
| 그레이 | 00, 01, 11 | 중간, 전이 글리치↓ |
FPGA/CPLD 에서는 원핫이 표준입니다. 플립플롭은 남아도는데 조합 논리가 비싸기 때문입니다. 이산 로직으로 만들면 이진이 낫습니다 — 플립플롭이 곧 부품 값입니다.
⚠️ 원핫에는 함정이 있습니다. 정의되지 않은 상태(예 011)에 빠지면
스스로 못 나옵니다. default: → IDLE 를 반드시 넣습니다.
★ 메타스테이빌리티 — 아주 가끔 미치는 이유
플립플롭은 클럭 에지 앞뒤로 데이터가 가만히 있어야 합니다 (셋업·홀드셋업·홀드 시간 setup / hold time클럭 가장자리 앞뒤로 데이터가 흔들리지 않고 서 있어야 하는 시간으로, 어기면 값이 깨집니다.자세히 보기 →). 이것을 어기면 출력이 0 도 1 도 아닌 중간 전압에서 한동안 머뭅니다.
언제 풀릴지 정해져 있지 않습니다. 확률적으로 지수적으로 줄어들 뿐입니다.
tau시정수time constantse잡음 전압 밀도noise voltage densityV/√Hzf_clk주파수 (clk)frequency (clk)Hzf_data주파수 (data)frequency (data)Hzt_r시간 (r)time (r)sT_00번 온도 또는 주기temperature or period 0| 기호 | 뜻 |
|---|---|
| 다음 단이 읽기까지 주어진 여유 시간 | |
| 플립플롭의 회복 시상수 (수십~수백 ps) | |
| 클럭과 데이터 변화 빈도 |
이 지수에 있습니다. 여유를 조금만 더 줘도 MTBF 가 자릿수로 좋아집니다 — 그것이 동기화기입니다.
두 단 동기화기
비동기 신호를 상태기계에 넣기 전에 플립플롭 두 개를 직렬로 통과시킵니다.
FF1 이 메타스테이블이 되어도, FF2 가 읽기까지 한 클럭(예 10 ns)이 있습니다. 가 100 ps 라면 — 천문학적으로 안전해집니다.
| 단수 | 100 MHz 클럭에서 MTBF (대략) |
|---|---|
| 없음 | 밀리초 — 하루에도 여러 번 |
| 1단 | 초 |
| 2단 | 수백만 년 |
| 3단 | 우주 나이 이상 (극한 환경용) |
핵심: 두 단이면 충분하고, 두 단은 반드시 있어야 합니다. “지금까지 잘 됐는데요” 는 근거가 아닙니다 — 메타스테이빌리티는 확률이고, 오래 돌릴수록 반드시 만납니다.
⚠️ 다중 비트는 두 단으로 안 됩니다. 각 비트가 따로 동기화되어 서로 다른 클럭에 넘어가면 없던 값이 나옵니다. 다중 비트는 그레이 코드를 쓰거나 (카운터), 핸드셰이크 또는 FIFO 를 씁니다.
계산 예제 — 8채널 스캔의 타이밍
요구: 8채널, 채널당 1 kSPS → 스캔 주기 125 µs, 채널당 15.6 µs.
FSM 클럭 10 MHz(100 ns), 상태별 소요:
| 상태 | 클럭 수 | 시간 | 근거 |
|---|---|---|---|
| SELECT | 1 | 0.1 µs | 주소 놓기 |
| SETTLE | 80 | 8 µs | MUX+앰프 세틀링정착 시간 settling time출력이 목표값의 허용 오차 안으로 들어와 머무를 때까지 걸리는 시간으로, 다채널 측정의 속도 상한을 정합니다.자세히 보기 → |
| CONVERT | 40 | 4 µs | ADC 변환 (SAR 250 kSPS) |
| STORE | 2 | 0.2 µs | |
| 합 | 123 | 12.3 µs |
12.3 < 15.6 µs — 통과. 여유 21%.
여유가 어디로 가는지 확인: 온도가 오르면 앰프 세틀링이 느려지고, ADC 변환 시간도 늘어납니다. 20% 여유는 그것을 덮을 만합니다.
⚠️ SETTLE 이 전체의 65% 입니다. 속도를 올리려면 여기를 봐야 합니다 — ADC 를 빠른 것으로 바꿔 봐야 4 µs 밖에 못 줄입니다.
tau시정수time constantsn비트 수 또는 개수number of bits or countt_settle정착 시간settling times8 µs 라면 = 0.89 µs. 이걸 반으로 줄이려면 MUX 뒤 앰프의 대역을 2배로 — 전류가 2배입니다 (6-08 소신호).
시뮬레이션으로 확인
# examples/09-digital/fsm-settle-timing.yaml
meta:
name: 채널 전환과 세틀링
lesson: docs/09-digital/08-fsm.md
note: EN 을 끄고 주소를 바꾸는 구간이 세틀링에 어떻게 더해지는지 본다
components:
- {ref: VEN, type: vpulse, params: {v1: 0, v2: 5, td: 2u, tr: 20n, tf: 20n, pw: 10u, per: 15.6u}}
- {ref: SW1, type: sw, model: SW_IDEAL}
- {ref: VCH, type: vdc, value: 2, label: 채널 전압}
- {ref: RMUX, type: r, value: 200, label: MUX 도통 저항}
- {ref: CIN, type: c, value: 4.5n, label: 앰프 입력 + 배선}
- {ref: RIN, type: r, value: 1MEG}
nets:
CH: [VCH.p, SW1.1]
MUXOUT: [SW1.2, RMUX.1]
AMPIN: [RMUX.2, CIN.1, RIN.1]
EN: [VEN.p, SW1.cp]
GND: [VCH.n, CIN.2, RIN.2, VEN.n, SW1.cn]
analyses:
- {type: tran, tstep: 50n, tstop: 50u} µs 이므로 12비트 세틀링에 8.1 µs.
AMPIN 이 목표값의 ±0.012% 안에 들어오는 시각을 재면 위 계산과 맞습니다.
⚖️ 트레이드오프
| 선택 | ✅ | ⚠️ |
|---|---|---|
| 무어 + 등록 출력 | 글리치 원천 차단 | 한 클럭 지연 |
| 밀리 | 상태·지연↓ | 출력 글리치 |
| 원핫 | 논리 단순, 빠름 | 플립플롭 개, 불법 상태 |
| 이진 | 플립플롭 최소 | 디코딩 논리 |
| 2단 동기화기 | 안전 | 2클럭 지연 |
| 동기화 생략 | 지연 0 | 아주 가끔 미침 — 최악의 버그 |
| FSM 을 MCU 소프트웨어로 | 유연, 디버깅 쉬움 | 타이밍 지터, 인터럽트 방해 |
채널 스캔처럼 타이밍이 정확해야 하는 것은 하드웨어 FSM(CPLD)이나 MCU 타이머 페리페럴로 만듭니다. 소프트웨어 루프로 만들면 인터럽트 하나에 샘플 간격이 흔들리고, 그것이 그대로 지터지터 jitter샘플링 시각이 흔들리는 양으로, 신호 주파수가 높을수록 해롭고 저주파에서는 거의 무해합니다.자세히 보기 →가 됩니다.
🔌 실제 쓰임새
- 다채널 ADC 스캔 — 이 문서의 예제
- SPI/I2C 마스터를 직접 구현 — 비트 단위 FSM
- 버튼 입력 — 디바운싱디바운싱 debouncing기계 접점이 붙을 때 수 ms 동안 튀는 것을 걸러 한 번의 눌림으로 읽는 처리입니다.자세히 보기 → + 2단 동기화기
- 외부 트리거 — 다른 장비에서 오는 신호는 반드시 동기화
- 클럭 도메인 교차 — MCU 와 ADC 클럭이 다르면 그 경계가 전부
⚠️ 이상과 실제의 차이
| 이상 | 실제 |
|---|---|
| 상태가 정의된 것만 | 전원 기동·글리치로 불법 상태에 빠질 수 있습니다 — default 필수 |
| 비동기 입력을 그냥 읽어도 됨 | 메타스테이빌리티 — 두 단 동기화기 필수 |
| 2단이면 다중 비트도 안전 | 아닙니다. 비트마다 다른 클럭에 넘어갑니다 |
| 시뮬에서 동작하면 됨 | 시뮬은 셋업/홀드 위반을 보통 무시합니다 |
| FSM 은 디지털 문제 | 출력이 아날로그 스위치로 가면 아날로그 문제입니다 |
연습문제
문제 1. 외부 장비의 트리거 신호를 FSM 의 start 로 직접 연결했습니다.
평소엔 잘 되는데 하루에 한두 번 스캔이 엉킵니다. 왜입니까?
풀이
메타스테이빌리티입니다.
외부 트리거는 우리 클럭과 아무 관계가 없으므로, 언젠가는 반드시 셋업/홀드 창 안에서 바뀝니다. 그때 첫 플립플롭이 중간 전압에 머물고, 그 값을 읽는 논리들이 서로 다르게 판단해 정의되지 않은 상태로 갑니다.
“하루에 한두 번”이 딱 동기화기 없는 MTBF 입니다.
해결: 두 단 동기화기를 넣습니다. 지연 2클럭(10 MHz 면 200 ns)이 늘지만 MTBF 가 수백만 년이 됩니다.
trigger → [FF] → [FF] → start (FSM 으로)
⚠️ 그리고 트리거가 클럭보다 짧을 수 있으면 에지 검출기도 같이 넣습니다 — 짧은 펄스는 동기화기가 통째로 놓칠 수 있습니다.
문제 2. 원핫으로 만든 5상태 FSM 이 가끔 멈춥니다. 무엇을 확인합니까?
exercises/09-08-onehot-stuck.yaml풀이
불법 상태에 빠졌는지 봅니다.
원핫 5상태는 플립플롭 5개 = 32가지 조합인데, 합법은 5가지뿐입니다. 나머지 27가지 중 하나에 빠지면 어느 전이 조건도 안 맞아 영원히 멈춥니다.
어떻게 빠지는가:
- 전원 기동 시 초기값이 안 정해짐
- 노이즈로 비트 하나가 뒤집힘
- 메타스테이빌리티
해결:
default를 IDLE 로 — 합법이 아닌 모든 조합에서 IDLE 로 가게- 비동기 리셋을 확실히 걸어 IDLE 로 시작
- 워치독 — 일정 시간 이상 같은 상태면 리셋
1번이 핵심입니다. HDL 로 쓸 때 case 의 default 를 빠뜨리면
합성기가 “그 경우는 안 온다”고 보고 아무것도 안 만듭니다.
문제 3. 스캔 속도를 2배로 올리라는 요구를 받았습니다. 위 계산 (SETTLE 8 µs, CONVERT 4 µs)에서 무엇부터 봅니까?
exercises/09-08-double-scan-speed.yaml풀이
SETTLE 부터입니다. 전체 12.3 µs 중 8 µs(65%)를 차지합니다.
목표는 채널당 6.15 µs. CONVERT 4 µs 를 그대로 두면 SETTLE 에 쓸 수 있는 것은 2 µs 뿐입니다.
mu이동도mobilitym²/V·stau시정수time constantst_settle정착 시간settling times지금 = 0.89 µs 이므로 4배 빠르게 해야 합니다.
| 방법 | 어떻게 | 대가 |
|---|---|---|
| MUX 도통 저항↓ | 더 좋은 MUX | 값, 전하 주입↑ |
| 뒤쪽 용량↓ | 배선 짧게, 앰프 입력 작게 | 레이아웃 |
| 분해능↓ | 12비트 → 10비트 | , 15% 만 이득 |
| 구조 변경 | 채널마다 앰프 | 부품 8배 |
⚠️ 분해능을 낮추는 것은 거의 도움이 안 됩니다 — 세틀링이 비트 수에 로그로 붙기 때문입니다 (18-00의 연습문제 3 과 같은 계산).
현실적인 답: CONVERT 를 더 빠른 ADC 로 줄이고(4 → 2 µs), SETTLE 을 4 µs 로 (τ 를 반으로 = MUX 와 배선 개선). 그래도 안 되면 채널마다 ADC 를 두는 구조로 갑니다.
이전 ← 07 카르노맵과 해저드 | 다음 → 10장. MCU 인터페이싱