단일 사이클의 클록 주기 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
# 단일 사이클의 클록 주기와 파이프라인의 클록 주기 계산
# 실행: python3 26_single-cycle-timing_verify.py
# 부품 지연 시간은 이 문서에서 직접 정한 값이다(교재의 수치가 아니다). 단위는 ps.
DELAY = dict(IM=250, RegRead=150, ALU=200, DM=250, RegWrite=100)
PATH = { # 명령어 종류마다 차례로 지나는 부품
"lw": ["IM", "RegRead", "ALU", "DM", "RegWrite"],
"sw": ["IM", "RegRead", "ALU", "DM"],
"R형": ["IM", "RegRead", "ALU", "RegWrite"],
"beq": ["IM", "RegRead", "ALU"],
"j": ["IM"],
}
def path_time(kind, delay=DELAY):
return sum(delay[p] for p in PATH[kind])
def clock_single(delay=DELAY):
return max(path_time(k, delay) for k in PATH)
def clock_pipeline(delay=DELAY, reg_overhead=0):
"""단계 다섯 개 가운데 가장 긴 것 + 파이프라인 레지스터가 쓰는 시간."""
return max(delay.values()) + reg_overhead
if __name__ == "__main__":
t = {k: path_time(k) for k in PATH}
assert t == {"lw": 950, "sw": 850, "R형": 700, "beq": 600, "j": 250}
assert clock_single() == 950 and max(t, key=t.get) == "lw"
assert round(1e12 / 950 / 1e9, 2) == 1.05 # 약 1.05 GHz
# 명령어마다 제 시간만 쓸 수 있다면 (가상의 가변 클록)
mix = {"lw": 0.25, "sw": 0.10, "R형": 0.45, "beq": 0.15, "j": 0.05}
assert abs(sum(mix.values()) - 1) < 1e-12
avg = sum(mix[k] * t[k] for k in mix)
assert abs(avg - 740) < 1e-9 and round(950 / avg, 2) == 1.28
# 카드: 다른 지연값
d2 = dict(IM=300, RegRead=100, ALU=250, DM=300, RegWrite=100)
assert {k: path_time(k, d2) for k in PATH} == {"lw": 1050, "sw": 950, "R형": 750, "beq": 650, "j": 300}
assert clock_single(d2) == 1050
# ALU만 두 배 빨라져도 주기는 조금만 준다 (가장 긴 길의 일부일 뿐)
d3 = dict(DELAY, ALU=100)
assert clock_single(d3) == 850 and round(950 / 850, 2) == 1.12
# 카드: 데이터 메모리를 150ps로 줄이면 850ps, j를 줄여도 그대로
assert clock_single(dict(DELAY, DM=150)) == 850 and clock_single(dict(DELAY, IM=250)) == 950
# 곱셈기처럼 느린 부품(900ps)이 EX에 들어오면 모든 명령어가 그만큼 느려진다
d4 = dict(DELAY, ALU=900)
assert clock_single(d4) == 1650
# 파이프라인 (27, 33 문서)
assert clock_pipeline() == 250 and sum(DELAY.values()) == 950
assert 5 * 250 == 1250 > 950 # 명령어 하나가 끝나는 데 드는 시간은 오히려 는다
assert round(950 / 250, 2) == 3.8 # 5배가 아니다: 단계가 고르지 않다
assert sum(DELAY.values()) / 5 == 190 # 다섯 단계가 똑같다면 190ps
for n, single, pipe in [(5, 4750, 2250), (100, 95000, 26000), (1000, 950000, 251000)]:
assert n * 950 == single and (n + 4) * 250 == pipe
assert round(4750 / 2250, 2) == 2.11 and round(95000 / 26000, 2) == 3.65 and round(950000 / 251000, 2) == 3.78
# 27 문서 카드: 300, 100, 250, 300, 100
assert clock_single(d2) == 1050 and clock_pipeline(d2) == 300 and 1050 / 300 == 3.5
# 파이프라인 레지스터가 20ps를 쓰면
assert clock_pipeline(reg_overhead=20) == 270 and round(950 / 270, 2) == 3.52
# 33 문서 카드: 데이터 메모리만 600ps로 느려지면
d5 = dict(DELAY, DM=600)
assert clock_single(d5) == 1300 and clock_pipeline(d5) == 600 and round(1300 / 950, 2) == 1.37 and 600 / 250 == 2.4
assert abs(3.8 * 250 - 950) < 1e-9
# 다중 사이클(33 문서): 주기 250ps, 명령어마다 3~5사이클
cyc = {"lw": 5, "sw": 4, "R형": 4, "beq": 3, "j": 3}
cpi = sum(mix[k] * cyc[k] for k in mix)
assert abs(cpi - 4.05) < 1e-9 and abs(cpi * 250 - 1012.5) < 1e-9
# 해저드까지 넣은 파이프라인의 CPI (33 문서): 쉬는 사이클이 명령어당 평균 0.2라면
assert abs((1 + 0.2) * 250 - 300) < 1e-9 and round(950 / 300, 2) == 3.17
print("ALL CHECKS PASSED")