29구현 문제
PWM duty를 주기 경계에서 래치하기
1. 요구 동작
PwmLatch는 주기가 8 사이클인 PWM 신호를 출력합니다.
| 신호 | 동작 |
|---|---|
count | 0부터 7까지 세고 0으로 순환합니다 |
pwm | count < level인 동안 1입니다 |
level | 0~8에서는 한 주기에서 pwm이 1인 사이클 수입니다. 8보다 커도 주기 전체 8 사이클이 1입니다 |
level은 duty를 그대로 따라가지 않습니다. 주기가 끝나는 에지, 곧 count가 7에서
0으로 돌아가는 에지에서만 duty를 읽습니다. 따라서 주기 중간에 duty가 바뀌면 다음
주기부터 반영됩니다. 리셋은 count와 level을 모두 0으로 만듭니다.
2. 관찰되는 증상
시작 코드는 level을 갱신하지 않습니다. 그래서 pwm이 계속 0입니다.
TODO 줄을 level 갱신 로직으로 바꾸세요. 포트, count, last, pwm 줄, 공개
테스트벤치는 그대로 둡니다.
3. 실행 전 예측
공개 테스트벤치는 파형을 기록합니다. duty를 3으로 poke하고 네 주기를 실행하면서
주기마다 pwm이 1인 사이클을 셉니다. 주기 3의 중간에는 duty를 6으로 바꿉니다.
주기마다 PERIOD 줄을 출력한 뒤 VCD를 dump.vcd로 복사하고, lab은 출력 아래에
파형을 표시합니다.
실행하기 전에 올바른 설계가 출력할 PERIOD 줄 네 개를 예측해 보세요.
주의. 주기 1에서는 리셋 직후 level이 어떤 값인지 먼저 판단해야 합니다.
그다음 시작 코드를 실행하고 파형을 열어 보세요.
4. 파형 읽기
파형에서 count, last, level, io_duty, io_pwm을 찾아보세요. VCD에는 포트와
최적화 뒤에도 남은 내부 신호가 기록됩니다. Scala val로 이름을 정했다고 별도 신호가
반드시 남는 것은 아닙니다. 다음 세 지점을 확인해 보세요.
| 신호 | 확인할 지점 |
|---|---|
io_duty | 주기 3의 중간, poke한 시각에 바뀝니다. 상승 에지 사이의 시각입니다 |
level | count가 0으로 돌아가는 상승 에지에서만 바뀔 수 있습니다. last가 1인 사이클이 끝나는 에지입니다 |
io_pwm | level이 1~7이면 count가 level에 도달하는 에지에서 0이 됩니다. 0이면 계속 0이고, 8 이상이면 주기 전체가 1입니다 |
시작 코드의 level은 0에 머문 평평한 선입니다. 수정한 뒤에는 level이 바뀌는 지점과
io_duty가 바뀌는 지점을 비교해 보세요.
5. 완료 조건
- 0부터 8까지의 모든 duty에서
pwm은count = 0부터 정확히level사이클 동안 1입니다. 8보다 크면 주기 전체가 1입니다. - 주기 안 어느 시점에 duty가 바뀌어도 다음 주기에만 반영됩니다. 한 주기 동안
duty가 여러 번 바뀌면 마지막 사이클의 값만 반영됩니다. - 리셋 직후 첫 주기는
level0으로 동작하고, 리셋이 다시 걸리면level도 다시 0이 됩니다.
6. 힌트
힌트 1
파형에는 주기마다 정확히 한 사이클 동안 1인 last가 이미 있습니다. 그 사이클이 끝나는
에지가 어느 에지인지 생각해 보세요.
힌트 2
level 갱신을 last 조건으로 감싸세요. when 안에서만 대입되는 레지스터는 나머지
에지에서 값을 유지합니다.
개념 설명
경계에서만 값을 읽는 shadow register를 두면 입력이 주기 중간에 어떻게 바뀌든 한 주기의 동작이 일정합니다. 파형에서는 이 성질이 바로 보입니다. 입력은 주기 중간에 바뀌고, 레지스터는 주기 경계에서만 바뀝니다.
7. 추가 실험
count === 0.U일 때 level을 갱신하도록 바꿔 보세요. PERIOD 줄 네 개와 파형에서
level이 바뀌는 에지를 예측하고, 이제 모든 주기의 첫 사이클이 이전 duty를 쓰는
이유를 설명해 보세요.