---
title: "valid를 지워도 일이 남는다: 파이프라인 flush의 책임"
author: "브레이브브라우저쓰자"
published_at: "2026-10-08T06:31:04.983Z"
updated_at: "2026-10-05T14:09:31.850Z"
tags: ["파이프라인 flush"]
canonical_url: "https://ariser.io/@writer_a9817d5a8de0/valid%EB%A5%BC-%EC%A7%80%EC%9B%8C%EB%8F%84-%EC%9D%BC%EC%9D%B4-%EB%82%A8%EB%8A%94%EB%8B%A4-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-flush%EC%9D%98-%EC%B1%85%EC%9E%84"
ai_assisted: true
---

# valid를 지워도 일이 남는다: 파이프라인 flush의 책임

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

[원문](https://ariser.io/@writer_a9817d5a8de0/valid%EB%A5%BC-%EC%A7%80%EC%9B%8C%EB%8F%84-%EC%9D%BC%EC%9D%B4-%EB%82%A8%EB%8A%94%EB%8B%A4-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-flush%EC%9D%98-%EC%B1%85%EC%9E%84)

파이프라인의 valid 비트를 모두 0으로 만들었는데 다음 요청이 들어오지 않는다. 예약한 자원은 여전히 사용 중으로 표시되어 있고, 늦게 돌아온 완료 신호는 새 요청의 카운터를 줄인다. **파이프라인 flush는 결과를 보이지 않게 만드는 것뿐 아니라, 취소한 거래가 남긴 책임을 정리하는 동작**이다. 비트를 지웠다는 사실과 일을 취소했다는 사실은 같지 않다.

flush는 잘못된 경로의 작업이나 더 이상 필요 없는 작업을 파이프라인에서 제거할 때 사용하는 제어다. 여기서는 내부에서 취소 가능한 거래만 다루고, flush가 오는 에지에는 입력 수락과 출력 전송을 막는 규칙을 선택한다. 외부 장치가 이미 수락한 쓰기를 이 신호 하나로 되돌릴 수 있다고 가정하지 않는다.

## 같은 에지에 무엇이 우선하는지 정하기

한 칸 버퍼에 항목 A가 있고, 상류는 B를 제시하며 하류는 ready=1이라고 하자. 동시에 flush가 왔다. A를 내보내고 B를 받는지, 둘 다 버리는지, A만 버리고 B를 받는지는 인터페이스가 정해야 한다. “flush니까 지운다”는 설명만으로는 충분하지 않다.

다음은 flush가 우선하며 같은 에지의 거래를 막는 설명용 구조다. 연결된 내부 생산자와 소비자도 flush의 취소 의미를 공유한다고 가정한다.

```systemverilog
assign out_valid = valid_q && !flush;
assign in_ready  = !flush && (!valid_q || out_ready);

always_ff @(posedge clk or negedge rst_n) begin
  if (!rst_n)
    valid_q <= 1'b0;
  else if (flush)
    valid_q <= 1'b0;
  else if (in_ready)
    valid_q <= in_valid;
end

always_ff @(posedge clk) begin
  if (rst_n && in_valid && in_ready)
    data_q <= in_data;
end
```

flush=1이면 out_valid=0, in_ready=0이다. A는 그 에지에 전송되지 않고 valid가 지워진다. B는 수락되지 않았으므로 생산자가 책임을 가진다. 다음 에지에 flush가 해제되면 빈 버퍼에서 B를 받을 수 있다. flush는 샘플링 에지 전에 안정되어 있다는 동기 제어 조건이다.

이 예제는 일반적인 “제시한 valid는 수락까지 유지한다”는 규칙에 취소 예외를 추가한다. 예외를 모르는 외부 소비자에게 out_valid를 갑자기 내리면 프로토콜 위반이 될 수 있다. 따라서 실제 모듈에 이 코드를 넣기 전에 flush가 어느 경계까지 공유되는지 확인해야 한다.

## 보이지 않는 예약도 취소해야 한다

요청을 수락할 때 결과 저장 슬롯 하나를 예약하고, 완료할 때 예약을 풀었다고 하자. accepted와 completed만 세면 flush로 사라진 거래는 영원히 미완료로 남는다. 수량 관계에는 취소도 포함되어야 한다.

**미완료 수 = 수락 수 − 정상 완료 수 − 취소 수**

가령 설명용 실행에서 세 요청을 수락하고 하나를 완료한 뒤 나머지 두 개를 취소했다면 미완료 수는 3−1−2=0이다. valid를 지웠는데 예약 수가 2로 남아 있으면, 빈 파이프라인이 새 요청을 막는다. 거래마다 잡았던 credit, 목적지 busy 비트, 반환 슬롯 등 관련 상태가 같은 취소 집합을 따라야 한다.

모든 pending 작업을 취소한다면 카운터를 0으로 만드는 방식이 가능할 수 있다. 일부만 취소한다면 어떤 항목이 남는지 식별해야 한다. 복수 단계의 valid 개수를 단순히 더하는 것도 조심해야 한다. 한 거래의 데이터와 제어가 다른 곳에 중복 표현되어 있다면 같은 거래를 두 번 취소한 것으로 셀 수 있다.

## 계산은 끝나지 않았는데 책임은 끝났다면

취소한 작업이 멈출 수 없는 연산기에 들어가 있으면 flush 뒤에도 결과가 도착할 수 있다. 현재 파이프라인에는 새 요청 C가 있고 pending=1인데, 이전 A의 완료가 돌아와 pending을 0으로 만들면 새 요청의 책임을 잘못 지운다. 결과를 무시하는 것과 자원 회계에서 무시하는 것 모두 필요하다.

거래 식별자나 세대 표시를 함께 보내면 결과가 어느 작업에 속하는지 구분할 수 있다. flush 뒤의 새 세대와 이전 세대가 다르면 이전 완료를 출력과 카운터 갱신에서 제외하는 방식이다. 단순히 완료 신호가 왔다는 이유로 모든 pending을 감소시키면 안 된다.

세대 표시는 유한한 비트 수를 가진다. 오래된 결과가 남아 있는 동안 번호를 다시 쓰면 새 작업과 충돌할 수 있다. 따라서 사용한 비트 수뿐 아니라 최대 잔존 시간과 번호 재사용 규칙도 필요하다. 태그를 추가했다는 사실만으로 무한히 늦는 응답까지 해결했다고 주장할 수는 없다.

## 이미 바깥에서 수락한 동작은 다른 종류의 문제다

출력 요청을 외부 인터페이스가 수락했다면 내부 valid를 내리는 것으로 그 요청의 존재를 없앨 수 없다. 외부 장치에 쓰기가 이미 전달되었거나 응답이 나중에 돌아오는 상황이다. 취소 가능한 별도 프로토콜이 없다면 응답을 받아 정리하거나, 부작용이 확정되기 전까지 요청의 발행을 늦추는 구조가 필요하다.

[BOOM의 ROB 문서](https://docs.boom-core.org/en/latest/sections/reorder-buffer.html)는 실행 완료와 commit을 구분하고, 예외 및 flush에 따라 진행 중인 상태를 정리하는 방식을 설명한다. 그 구분을 일반적인 파이프라인에 가져오면 “계산했음”, “외부에서 관측 가능해짐”, “더 이상 취소할 수 없음”을 한 완료 비트에 섞지 않게 된다.

이 글의 한 칸 예제는 아직 외부에 전송하지 않은 항목만 취소한다. 외부에 도달한 작업의 rollback까지 구현한 코드가 아니다. 시스템에서 취소 경계를 어디에 둘지 정하는 일이 먼저다.

## 다른 구현의 flush 규칙은 그대로 가져와야 한다

공개 모듈을 사용할 때에도 flush와 새 valid의 동시성을 살펴봐야 한다. [PULP의 flush 가능한 spill register](https://github.com/pulp-platform/common_cells/blob/master/src/cc_spill_register_flushable.sv)는 flush와 입력 valid가 동시에 높은 조건을 금지하는 assertion을 둔다. 이 글의 입력 ready 차단 방식과 동일한 인터페이스 규칙은 아니다.

같은 flush라는 이름이어도 정상 거래로 배출하는지, 즉시 점유 상태를 없애는지, clear와 다르게 쓰는지 구현마다 의미가 달라질 수 있다. 어댑터는 타이밍만 맞출 것이 아니라 이런 제어 계약도 맞춰야 한다.

검증에서는 flush와 stall, 새 요청, 완료가 같은 에지에 겹치는 상황을 따져 볼 수 있다. 각 거래가 정상 완료되었는지 취소되었는지 정확히 한 번 설명되어야 한다. 아무 설명 없이 사라지거나 두 번 정리되는 거래가 없다면 수량과 소유권의 계약이 맞아 가고 있는 것이다.

처음의 빈 파이프라인은 실제로 비어 있지 않았다. payload는 없어졌지만 예약과 늦은 완료의 의미가 남아 있었다. **파이프라인 flush를 완성하려면 출력 억제, 취소한 자원의 반환, 이후 응답의 구분, 외부 부작용의 경계를 함께 정의해야 한다.** 그때 valid를 지우는 작은 동작이 시스템의 일관된 취소로 연결된다.

