---
title: "세 주기를 주는 multicycle 제약에서 hold도 다시 읽기"
author: "브레이브브라우저쓰자"
published_at: "2026-10-22T11:07:00.409Z"
updated_at: "2026-10-06T01:43:37.747Z"
tags: ["multicycle path"]
canonical_url: "https://ariser.io/@writer_a9817d5a8de0/%EC%84%B8-%EC%A3%BC%EA%B8%B0%EB%A5%BC-%EC%A3%BC%EB%8A%94-multicycle-%EC%A0%9C%EC%95%BD%EC%97%90%EC%84%9C-hold%EB%8F%84-%EB%8B%A4%EC%8B%9C-%EC%9D%BD%EA%B8%B0"
ai_assisted: true
---

# 세 주기를 주는 multicycle 제약에서 hold도 다시 읽기

이 글은 AI가 활용됐습니다.

[원문](https://ariser.io/@writer_a9817d5a8de0/%EC%84%B8-%EC%A3%BC%EA%B8%B0%EB%A5%BC-%EC%A3%BC%EB%8A%94-multicycle-%EC%A0%9C%EC%95%BD%EC%97%90%EC%84%9C-hold%EB%8F%84-%EB%8B%A4%EC%8B%9C-%EC%9D%BD%EA%B8%B0)

연산 결과를 세 번째 클록에서만 받기로 했다면, 조합 경로에도 세 주기를 줄 수 있다. 그런데 setup만 완화한 뒤 hold 위반이 갑자기 크게 늘어나는 경우가 있다. multicycle path는 시간축의 검사 에지를 바꾸므로, **늦게 받을 데이터와 먼저 보존할 데이터를 함께 따져야 한다.**

## 계산이 오래 걸려도 되는 실제 프로토콜

주기 10ns인 같은 클록으로 출발·목적 레지스터를 구동하는 가상 회로를 보자. 두 레지스터의 enable은 같은 위상으로 세 주기마다 켜진다. 0ns에서 출발 레지스터가 새 입력을 저장하고, 목적 레지스터는 그 입력의 결과를 30ns에서 받는다. 10ns와 20ns에서는 목적 레지스터가 결과를 캡처하지 않는다. 출발 레지스터도 그동안 입력을 유지하고, 30ns에서 다음 입력으로 갱신한다.

30ns의 공통 에지에서는 목적 레지스터가 에지 직전의 계산 결과를 받고 출발 레지스터는 다음 입력을 저장한다. 이 관계라면 0→30ns가 결과의 setup 검사 구간이다. enable이 가끔 꺼질 뿐 최소 간격이 보장되지 않는 회로와는 다르다. 예외를 주기 전에 두 enable의 위상, 최소 간격과 리셋 후 첫 거래를 확인해야 한다.

OpenSTA의 `set_multicycle_path`는 일반적인 한 주기의 레지스터 경로 검사 대신 여러 주기의 관계를 지정한다. source 기준인 `-start`와 destination 기준인 `-end`를 선택할 수 있다. 명령이 enable이나 저장 회로를 만들어 주지는 않는다. [OpenSTA의 다중 주기 제약](https://opensta.readthedocs.io/en/latest/Commands/#set_multicycle_path)

최대 clk-to-Q가 0.2ns, 조합 지연이 24ns, 목적 setup이 0.5ns라고 가정하자. 클록 차이와 불확실성을 생략하면 데이터는 출발 뒤 24.2ns에 도착한다. 한 주기의 필요한 시각 9.5ns와 비교하면 slack은 −14.7ns다. 세 주기의 필요한 시각 29.5ns와 비교하면 +5.3ns다. 회로가 빨라진 것이 아니라, 실제로 기다리는 캡처 시점에 맞춰 검사가 바뀐 것이다.

## setup을 옮기면 hold의 기준도 따라간다

같은 위상의 클록에서 setup 캡처를 10ns에서 30ns로 옮기고 hold 예외를 따로 주지 않았다고 하자. 일반적인 이 다중 주기 관계에서는 대응 hold 캡처가 setup 바로 앞의 20ns로 이동한다. 0ns에 출발한 새 데이터가 20ns 근처까지 바뀌지 말라는 강한 요구가 생긴다. AMD의 원 문서는 enable로 중간 캡처가 막힌 예제에서 setup 변경에 따라 hold가 이동하며, 필요한 경우 원래 hold 관계로 돌려야 함을 설명한다. [setup 완화와 hold 복원](https://docs.amd.com/r/2023.2-English/ug903-vivado-using-constraints/Relaxing-Setup-While-Maintaining-Hold)

가상 최소 clk-to-Q와 조합 지연의 합이 0.8ns, hold 요구가 0.1ns라면 이동한 hold 기준에서는 `0.8 − 20.1 = −19.3ns`가 된다. 이것만 보고 경로에 20ns 가까운 버퍼 지연을 넣으면, 실제로는 캡처하지 않는 중간 에지 때문에 회로를 바꾸는 셈이다.

위 프로토콜에서 보존해야 할 것은 0ns 에지에 목적 레지스터가 받던 이전 거래의 결과다. 새 거래의 데이터는 그 에지 뒤 hold 시간만큼은 이전 결과를 덮어쓰지 않아야 한다. hold를 원래 같은 에지 관계로 복원하면 필요한 최소 도착 시간은 0.1ns이고 slack은 `0.8 − 0.1 = +0.7ns`다.

세 주기 setup과 원래 hold 관계를 목적 클록 기준으로 지정하는 형태는 다음과 같다. 여기서 u_src와 u_dst는 해당 레지스터 인스턴스의 가상 이름이다.

```tcl
set_multicycle_path -setup -end 3 \
    -from [get_cells u_src] -to [get_cells u_dst]
set_multicycle_path -hold -end 2 \
    -from [get_cells u_src] -to [get_cells u_dst]
```

이 동일 주기·동일 위상 예에서 hold의 2는 setup의 3에 대응해 두 주기를 되돌린 값이다. 일반 회로의 hold를 임의로 두 주기 느슨하게 해도 된다는 뜻은 아니다. 두 클록의 주기나 위상이 다르면 어느 쪽 주기를 이동할지까지 직접 확인해야 한다.

## 숫자를 복사하기 전에 enable을 반례로 검사하기

목적 enable이 버그로 20ns에도 켜진다고 가정해 보자. 최대 도착 24.2ns인 결과는 그때 준비되지 않았다. 제약의 setup 3은 여전히 타이밍을 통과시킬 수 있지만 실제 동작의 계약은 깨진다. 출발 레지스터가 10ns에서 새 입력으로 바뀌어도 0ns 거래의 결과를 30ns에 받을 보장이 사라진다. 타이밍 예외가 프로토콜 오류를 검증해 주는 것은 아니다.

예외의 대상도 특정 enable 관계가 적용되는 레지스터로 한정해야 한다. 출발 레지스터를 공유하는 다른 목적 레지스터가 매 주기 캡처한다면 그 경로에는 동일 예외를 줄 수 없다. 최소 지연 검사는 이전 데이터 보존 때문에 남아 있으며, 정상적인 hold 요구까지 없애는 선택도 맞지 않는다.

다중 주기 경로를 읽을 때는 0ns 출발, 10·20ns의 비캡처, 30ns 캡처를 먼저 그려 보면 된다. 그다음 setup과 hold 보고서의 실제 에지가 같은 의도를 표현하는지 맞춘다. **세 주기라는 숫자는 계산의 희망 시간이 아니라, 회로가 보장하는 데이터 유지와 캡처 간격을 표현해야 한다.**

