BSV 소스는 세 시점에 걸쳐 의미가 정해집니다. 컴파일할 때 타입과 구조가 확정되고,
scheduling 분석이 같은 사이클에 함께 실행할 수 있는 rule을 정하며, 실행할 때 현재
상태가 어떤 rule이 실행될지를 결정합니다. 이 구분을 알아야 컴파일 오류, scheduling
경고, 실행 중 멈춤을 각각 정확히 진단할 수 있습니다.
앞 장들에서는 이 세 시점을 하나씩 따로 만났습니다.
guard: 유지와 정지 장에서는 명시 조건과 호출한
method의 준비 조건을 결합해 rule의 활성 조건을 만들었고,
상태를 공유하는 rule 장에서는 원자적 갱신, rule
사이의 충돌, 경고 G0010과 G0021을 보았으며,
rule로 만드는 상태 기계 장에서는 정해진 순서를 phase
상태로 표현했습니다. 이 장에서는 그 동작들이 각각 어느 단계에 속하는지 이름을 붙이고,
기본 모듈이 method 사이에 선언하는 scheduling 관계를 설명합니다. 충돌 경고의 출발점이
바로 이 관계입니다.
1. 설계를 해석하는 세 단계
1.1 단계별로 정해지는 것
BSV 설계는 정적 처리, scheduling 분석, 실행의 순서로 해석됩니다. 설계가 예상과 다르게 동작하면 이 순서대로 원인을 찾습니다.
| 단계 | 언제 | 정해지는 것 | 이 단계에서 보이는 문제 |
|---|---|---|---|
정적 검사와 elaboration | 컴파일할 때 | 타입, proviso, 폭, 모듈 인스턴스, 매개변수, 구조적 반복 | 타입 오류, 해결되지 않은 proviso |
| scheduling | 컴파일할 때 | 같은 사이클에 함께 실행할 수 있는 rule, method 순서, 우선순위 | 충돌 경고, 실행될 수 없는 rule 경고 |
| 실행 | 매 사이클 | 현재 상태에서 참인 조건, 실행되는 rule, 다음 상태 | 비활성 rule, 예상과 다른 상태 전이 |
확인 문제
bsc가 두 rule이 충돌하며 그중 하나는 결코 실행될 수 없다는 경고를 출력했습니다. 이 경고를 만든 단계는 무엇입니까?
1.2 정적 검사와 elaboration
elaboration은 최상위 모듈부터 하위 모듈을 차례로 인스턴스화하면서 하드웨어 구조를
펼치는 과정입니다. 매개변수가 전달되고, 구조를 만드는 반복문과 함수가 모두 펼쳐집니다.
Vector#(4, Reg#(Bit#(8))) regs <- replicateM(mkReg(0));
for (Integer i = 0; i < 4; i = i + 1)
rule clear_one (clearing);
regs[i] <= 0;
endrule| 코드 | elaboration이 하는 일 |
|---|---|
replicateM(mkReg(0)) | 레지스터 인스턴스 4개를 만듭니다 |
for (Integer i = 0; ...) | 실행 중에 도는 반복이 아닙니다. rule 4개를 펼쳐 만드는 구조적 반복입니다 |
elaboration이 끝나면 인스턴스 계층과 rule 집합은 더 이상 바뀌지 않습니다. 타입
오류는 실행 중에 선택되는 분기가 아니라 하드웨어가 생성되기 전에 검출되는 문제입니다.
확인 문제
위 코드의 elaboration이 끝나면 모듈에는 rule이 몇 개 있습니까?
1.3 scheduling 분석
scheduling 분석은 고정된 설계에서 각 rule이 호출하는 method를 끝까지 따라갑니다.
다른 모듈의 method나 중첩된 호출도 모두 분석 대상입니다. 분석은 컴파일할 때 한 번
수행되고, 그 결과로 생성된 scheduler 회로가 매 사이클 실행할 rule을 고릅니다.
1.4 실행
실행 단계에서는 현재 레지스터 값으로 각 rule의 조건을 평가합니다. 선택된 rule이
다음 상태 값을 계산하고, 저장 상태는 클록 에지에서 한꺼번에 바뀝니다. 시뮬레이션과
합성된 하드웨어는 같은 상태 전이 규칙을 따릅니다.
한 사이클에 함께 실행되는 rule들의 결과는, 스케줄러가 정한 순서대로 하나씩
실행하면서 각 rule이 앞의 rule이 남긴 상태를 본 결과와 같아야 합니다. 그래서
한 사이클을 예측할 때는 먼저 순서를 정하고, 그 순서대로 rule을 하나씩 머릿속으로
실행해 봅니다.
1.5 예제: 한 번에 하나씩 실행한 결과
OneRuleAtATime.bsv에서 x는 0, y는 9에서 시작합니다. 사이클 0에는
copy(y <= x)와 incr(x <= x + 1)가 모두 실행 가능하고, 사이클 1에는 show가
두 레지스터를 출력합니다. copy는 x를 읽고 incr는 x에 씁니다. 레지스터의
읽기는 쓰기보다 SB이므로 가능한 순서는 copy 다음 incr 하나뿐입니다.
실행하기 전에 예측해 보세요. 그 순서대로 두 rule을 하나씩 실행해 보고 x와
y를 적어 보세요.
확인 문제
show는 무엇을 출력할까요?
package OneRuleAtATime;
(* synthesize *)
module mkTop(Empty);
Reg#(Bit#(8)) x <- mkReg(0);
Reg#(Bit#(8)) y <- mkReg(9);
Reg#(Bit#(2)) cycle <- mkReg(0);
// 두 rule은 사이클 0에 함께 실행된다. copy는 x를 읽고 incr는 x에 쓴다.
rule copy (cycle == 0);
y <= x;
endrule
rule incr (cycle == 0);
x <= x + 1;
endrule
rule advance;
cycle <= cycle + 1;
endrule
rule show (cycle == 1);
$display("x=%0d y=%0d", x, y);
$finish(0);
endrule
endmodule
endpackage결과 확인. 스케줄링 경고 없이 x=1 y=0이 출력됩니다. 두 rule이 모두 사이클
0에 실행되었고, 결과는 copy 다음에 incr를 실행한 것과 같습니다.
직접 바꾸어 보세요. incr를 x <= y + 1;로 바꾼 뒤, 실행하기 전에 다시
예측해 보세요.
확인 문제
incr를 x <= y + 1;로 바꾸면 어떻게 될까요?
2. method scheduling 관계
2.1 관계의 종류
기본 모듈은 method 호출을 같은 사이클에 함께 수행할 수 있는지를 scheduling 관계로
bsc에 알립니다.
| 관계 | 의미 |
|---|---|
C | 두 호출이 충돌합니다. 같은 사이클에 함께 실행할 수 없습니다 |
CF | 두 호출이 충돌하지 않습니다. 어떤 순서로 보아도 결과가 같습니다 |
SB | 함께 실행할 수 있지만, 첫 호출이 둘째 호출보다 앞선 것처럼 동작해야 합니다 |
SBR | 첫 호출이 앞서며, 두 호출을 한 rule 안에서 함께 쓸 수 없습니다 |
SA와 SAR은 scheduling 문서에서 SB와 SBR의 반대 방향을 나타내는 이름입니다. BSV
소스에 쓰는 어트리뷰트가 아닙니다.
확인 문제
두 method 호출이 정해진 순서로 같은 사이클을 공유할 수는 있지만 한 rule 안에서는 함께 쓸 수 없게 하는 관계는 무엇입니까?
2.2 레지스터의 관계
| 호출 쌍 | 관계 | 결과 |
|---|---|---|
_read와 _read | CF | 여러 rule이 같은 사이클에 자유롭게 읽을 수 있습니다 |
_read와 _write | SB | 읽기가 쓰기보다 앞선 것으로 배열됩니다. 그래서 읽기는 이전 값을 봅니다 |
_write와 _write | SBR | 서로 다른 rule의 두 쓰기는 중재가 필요하고, 한 rule 안에서는 오류입니다 |
상태를 공유하는 rule 장의 충돌 예제에서 경고
G0010에 나온 phase.write와 phase.read도 이 관계로 설명됩니다. chooseOne이
chooseTwo보다 앞서려면 chooseOne의 쓰기가 chooseTwo의 읽기보다 앞서야 하는데,
읽기는 쓰기보다 앞서야 하므로 어느 순서로도 배열할 수 없습니다.
SBR 행은 그 장의 두 결과도 설명합니다. 한 rule 안에서 같은 레지스터를 두 번 쓰면
컴파일 오류 G0004입니다. SBR이 두 호출을 한 rule에 함께 두는 것을 막기
때문입니다. 반면 같은 레지스터를 쓰는 두 rule은 같은 사이클에 함께 실행될 수
있습니다. bsc가 두 rule의 순서를 정하고, 뒤에 놓인 쓰기가 반영되며, 경고 G0117로
그 쓰기가 앞의 쓰기를 가린다고 알립니다.
확인 문제
서로 다른 두 rule이 같은 레지스터 r을 쓰고, 같은 사이클에 둘 다 활성화되었습니다. _write와 _write 사이의 SBR 관계는 두 rule에 어떤 의미입니까?
참고. 저장 동작이 비슷한 모듈도 scheduling 관계는 다를 수 있습니다. 예를 들어
mkReg와 mkConfigReg는 scheduling 모델이 다릅니다. 기본 모듈을 고르면 면적과 초기화
방식뿐 아니라 한 사이클에 함께 실행할 수 있는 호출자의 범위도 바뀝니다. 다른 관계가
꼭 필요하고 검증했을 때만 대안 모듈을 사용합니다.
3. 정리
| 주제 | 핵심 |
|---|---|
| 세 단계 | elaboration은 구조를, scheduling은 함께 실행할 수 있는 rule을, 실행은 다음 상태를 정합니다 |
| method 관계 | C, CF, SB, SBR이 같은 사이클에 함께 호출할 수 있는지를 정합니다 |
| 레지스터의 관계 | 읽기끼리는 CF, 읽기는 쓰기보다 앞서는 SB, 쓰기끼리는 SBR입니다. 그래서 한 rule 안의 이중 쓰기는 오류이고, 쓰는 rule이 둘이면 중재가 필요합니다 |
4. 확인 문제
-
elaboration이 고정하는 것과 매 사이클 다시 평가되는 것을 하나씩 들어 보세요.정답 보기
elaboration은 모듈 인스턴스 계층과rule집합을 고정합니다.Integer로 도는for반복도 이때 펼쳐집니다. 매 사이클 다시 평가되는 것은rule의 명시 조건과 method의 준비 조건입니다. -
여러
rule이 한 사이클에 같은 레지스터를 읽는 것은 자유로운데, 같은 레지스터를 쓰는 두rule에는 중재가 필요한 이유는 무엇일까요?정답 보기
_read와_read는CF여서 두 읽기의 순서가 결과를 바꾸지 않습니다._write와_write는SBR이므로 두 쓰기에 순서를 정해야 하고,bsc가 어느rule을 뒤에 둘지 결정하며, 그 쓰기가 반영됩니다.