본문으로 건너뛰기
code0.HDL

24검증 문제

한계값에 맞는 카운터 폭 정하기

1. 요구 동작

WrapCounter(cfg)는 en이 1인 사이클마다 0, 1, ..., cfg.limit까지 센 뒤 0으로 돌아갑니다. atLimit은 카운트가 한계값과 같은 동안에만 1입니다. WrapConfig는 한계값을 보관하고 그 값에서 레지스터 폭을 계산합니다. 폭을 case class에서 계산하므로 설정을 사용하는 모든 모듈이 같은 폭을 씁니다. CounterBank는 한계값이 5, 8, 16인 카운터 세 개를 만들고 en 하나를 공유합니다.

시작 코드는 컴파일되고 한계값 5인 카운터는 올바르게 동작하지만, 나머지 두 카운터는 한계값에 도달하지 못합니다. 잘못 계산된 매개변수를 찾아 고치세요. case class, require, WrapCounter, CounterBank의 나머지 부분은 그대로 둡니다.

2. 실행 결과 비교

시작 코드를 실행해 보세요. 공개 실행은 en을 1로 유지하며 11사이클 동안 세 카운트와 atLimit 플래그를 출력합니다. 한계값 8인 카운터가 명세와 처음 달라지는 사이클과 그때의 동작을 적어 보세요. 한계값 16인 카운터의 증상은 출력된 사이클보다 뒤에 나타납니다. 그 시점의 동작을 예측해 보세요.

3. 원인 진단

WrapCounter의 모든 폭은 cfg.width에서 결정됩니다. 세 한계값마다 다음 표를 직접 채워 보세요.

한계값cfg.width저장 가능한 최댓값도달해야 하는 값
55
88
1616

한계값 5에서는 문제가 드러나지 않고 8과 16에서 드러나는 이유를 설명해 보세요.

4. 수정 후 확인

폭 계산을 고치고 다시 실행하세요. 한계값 8인 카운터는 이제 8과 atLimit 플래그를 함께 출력하고, 다음 줄에서 0으로 돌아가야 합니다. 한계값 16인 카운터는 11사이클 안에 한계값에 도달하지 않으므로 공개 실행으로는 수정 결과를 확인할 수 없습니다. 3절의 표를 근거로 이 카운터도 올바르게 동작하는 이유를 설명해 보세요.

5. 완료 조건

  • 각 카운터가 자기 한계값에 도달해 그 사이클에 atLimit을 1로 출력하고, 다음 활성 사이클에 0으로 돌아갑니다.
  • en이 0인 동안 카운트가 유지됩니다.
  • 리셋하면 세 카운터가 모두 0이 됩니다.
  • 세 한계값에 우연히 맞는 고정 폭을 고르지 말고, 설정의 한계값에서 필요한 폭을 계산해야 합니다.

다른 설정을 실험할 때는 limit이 양수이고 limit + 1이 Scala의 Int 표현 범위 안에 있는지 확인하세요. 내부 레지스터 폭과 별개로, 기존 CounterBank의 8비트 카운트 출력도 선택한 한계값을 표현할 수 있어야 합니다.

6. 단계형 힌트

힌트 1

카운터는 0부터 limit까지 limit + 1개의 서로 다른 값을 저장해야 합니다.

힌트 2

log2Ceil(n)은 서로 다른 값 n개를 구별하는 데 필요한 비트 수입니다. 따라서 표현할 수 있는 최댓값은 2의 거듭제곱보다 1 작습니다. 어떤 수를 인자로 전달하는지 확인해 보세요.

개념 설명

case class 안의 파생 매개변수는 한 번 계산되고, 그 값을 읽는 모든 모듈이 그대로 사용합니다. 이 계산에 off-by-one 오류가 있으면 elaboration은 정상적으로 끝나고 일부 설정에서만 회로가 잘못 동작합니다. 이 문제에서는 한계값이 2의 거듭제곱인 경우입니다. count === cfg.limit.U 비교는 폭을 자동으로 맞추므로 폭 오류도 발생하지 않습니다.

7. 추가 실험

CounterBank에 한계값 1을 추가한다고 가정해 보세요. 시작 코드의 계산과 수정한 계산으로 각각 폭을 구하고, 시작 코드가 그 카운터에 어떤 회로를 만드는지 예측해 보세요.

개념 다시 보기
준비

CheerpJ로 브라우저에서 실행합니다. 첫 실행에는 Java 약 14.5 MB와 JAR 약 47 MB를 내려받습니다. 컴파일에 1분 정도 걸릴 수 있습니다. 라이선스

Main.scala