파이프라인 레지스터와 제어 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.

# 파이프라인 레지스터가 실어 나르는 것과 제어 신호가 쓰이는 단계 확인
# 실행: python3 28_pipeline-registers_verify.py
# 칸의 구성과 비트 수는 이 문서의 설계(27_mips-pipeline_impl.py의 흉내기)를 기준으로 센 것이다.
STAGES = ["IF", "ID", "EX", "MEM", "WB"]
# 제어 신호가 쓰이는 단계: 그 신호가 움직이는 부품이 있는 곳
USED_IN = {"RegDst": "EX", "ALUOp1": "EX", "ALUOp0": "EX", "ALUSrc": "EX",
           "Branch": "MEM", "MemRead": "MEM", "MemWrite": "MEM",
           "RegWrite": "WB", "MemtoReg": "WB"}
FIELDS = {
    "IF/ID":  {"PC+4": 32, "명령어": 32},
    "ID/EX":  {"PC+4": 32, "읽은 값 1": 32, "읽은 값 2": 32, "부호 확장한 값": 32, "rs": 5, "rt": 5, "rd": 5},
    "EX/MEM": {"분기 목표 주소": 32, "Zero": 1, "ALU 결과": 32, "읽은 값 2": 32, "쓸 레지스터 번호": 5},
    "MEM/WB": {"메모리에서 읽은 값": 32, "ALU 결과": 32, "쓸 레지스터 번호": 5},
}


def control_bits_in(reg):
    """그 레지스터를 지난 뒤에도 쓰일 신호만 싣는다."""
    after = {"IF/ID": 0, "ID/EX": 2, "EX/MEM": 3, "MEM/WB": 4}[reg]
    if reg == "IF/ID":
        return []                 # 아직 해독 전이라 신호가 없다
    return [s for s, st in USED_IN.items() if STAGES.index(st) >= after]


def stage_at(i, cycle):
    """쉬거나 버리는 일이 없을 때, i번째(1부터) 명령어가 cycle번째 사이클에 있는 단계."""
    k = cycle - i
    return STAGES[k] if 0 <= k < 5 else None


if __name__ == "__main__":
    assert len(control_bits_in("ID/EX")) == 9 and len(control_bits_in("EX/MEM")) == 5 and len(control_bits_in("MEM/WB")) == 2
    assert control_bits_in("EX/MEM") == ["Branch", "MemRead", "MemWrite", "RegWrite", "MemtoReg"]
    assert control_bits_in("MEM/WB") == ["RegWrite", "MemtoReg"]
    data = {k: sum(v.values()) for k, v in FIELDS.items()}
    assert data == {"IF/ID": 64, "ID/EX": 143, "EX/MEM": 102, "MEM/WB": 69}
    total = {k: data[k] + len(control_bits_in(k)) for k in FIELDS}
    assert total == {"IF/ID": 64, "ID/EX": 152, "EX/MEM": 107, "MEM/WB": 71}
    # 1번 명령어가 WB에 있는 5번째 사이클에 ID에는 4번 명령어가 있다
    assert stage_at(1, 5) == "WB" and stage_at(4, 5) == "ID" and [stage_at(i, 5) for i in range(1, 6)] == ["WB", "MEM", "EX", "ID", "IF"]
    # 그래서 쓸 레지스터 번호를 IF/ID에서 바로 가져오면 3칸 뒤 명령어의 번호가 된다
    assert 4 - 1 == 3
    # 예: lw $t0 / add $t1 / sub $t2 / and $t3 -> 5번째 사이클의 IF/ID에는 and가 있고, 그 rt 칸은 lw의 $t0가 아니다
    prog = [("lw", dict(rt=8)), ("add", dict(rt=18)), ("sub", dict(rt=20)), ("and", dict(rt=22))]
    in_id = [p for i, p in enumerate(prog, 1) if stage_at(i, 5) == "ID"][0]
    assert in_id[0] == "and" and in_id[1]["rt"] != prog[0][1]["rt"]
    print("ALL CHECKS PASSED")