본문으로 건너뛰기
code0.HDL

23구현 문제

범용 hold 레지스터 만들기

1. 요구 동작

Hold[T <: Data](gen)은 어떤 payload 타입에도 사용할 수 있는 레지스터입니다.

조건동작
en이 1인 클록 에지in을 저장합니다
en이 0인 클록 에지저장된 값을 유지합니다
out가장 최근에 저장한 값을 출력합니다

저장한 값은 저장이 일어난 에지 직후 out에 반영됩니다. 첫 저장 전의 출력은 정의하지 않습니다. PixelHold는 같은 생성기를 필드 세 개의 Pixel로 고정한 모듈입니다.

시작 코드는 in을 out에 직접 연결합니다. 출력이 매 사이클 입력을 따라가므로 아무 값도 유지되지 않습니다. Hold에 필요한 레지스터를 추가하세요. 공개 실행은 Hold(UInt(8.W))와 PixelHold를 모두 만들므로 본문은 모든 T에서 동작해야 합니다. 클래스 이름, 타입 매개변수, 포트는 그대로 둡니다.

2. 시작 코드 읽기

gen이 쓰이는 곳을 찾아보세요. gen은 in과 out의 형태를 정하고, en은 항상 Bool입니다. 템플릿 자체는 어디에도 연결되지 않습니다. 본문은 T의 폭이나 필드 이름을 알지 못하며, gen이 기술하는 형태만 사용할 수 있습니다.

3. 실행 결과 예측

공개 실행은 입력을 인가하고 클록을 한 번 진행한 뒤 out을 출력합니다. UInt 테스트의 입력은 7, 8, 9, 10, 11이며 en은 7과 10에서만 1입니다. 다섯 out 값과 Pixel의 세 줄을 먼저 적어 보세요. 시작 코드는 모든 줄에서 입력을 그대로 출력합니다.

4. 실행 결과 해석

수정한 설계를 실행해 보세요. UInt 줄의 out은 en이 참인 줄에서만 바뀌어야 하고, Pixel 줄은 세 필드를 함께 유지해야 합니다. 같은 본문이 타입 이름을 하나도 쓰지 않고 한 인스턴스에는 8비트 레지스터를, 다른 인스턴스에는 필드 세 개의 레지스터를 만든 과정을 설명해 보세요.

5. 완료 조건

  • out은 en이 1이던 에지 뒤에만 바뀝니다.
  • en이 0이면 in이 바뀌어도 몇 사이클이든 out이 유지됩니다.
  • Bundle payload의 모든 필드가 함께 저장됩니다.
  • 같은 본문이 UInt와 Pixel에서 모두 컴파일되고 올바르게 동작합니다.

6. 단계형 힌트

힌트 1

여러 사이클에 걸쳐 값을 유지하려면 상태가 필요합니다. 순차 회로 장에서 값을 저장하고 :=로 갱신하는 구성 요소를 떠올려 보세요.

힌트 2

레지스터의 타입은 T여야 하고, T를 기술하는 값은 gen뿐입니다. UInt(8.W) 같은 고정 타입을 쓰면 Pixel 인스턴스에서 컴파일 오류가 발생합니다.

힌트 3

레지스터는 when(io.en) 안에서 갱신하고, out은 입력이 아니라 레지스터에서 연결하세요.

개념 설명

gen은 Chisel 타입, 즉 하드웨어의 형태를 정하는 템플릿입니다. Input(gen), Output(gen), Reg(gen)은 각각 같은 형태의 별도 하드웨어 값을 만듭니다. 범용 본문은 :=와 when처럼 모든 Data가 지원하는 연산만 사용합니다. 그래서 같은 코드가 한 인스턴스에서는 8비트 레지스터를, 다른 인스턴스에서는 24비트 Bundle 레지스터를 생성합니다.

7. 추가 실험

gen 대신 Reg(io.in)을 쓰면 무엇이 달라질지 예측해 보세요. 이어서 new Hold(0.U(8.W))의 elaboration 결과와 실패하는 단계를 예측해 보세요.

개념 다시 보기
준비

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

Main.scala