제어 장치와 제어 신호 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
# 주 제어와 ALU 제어를 게이트 식으로 만들고 표와 같은지 전수 검사한다 (실제 하드웨어가 아니라 파이썬 흉내다)
# 실행: python3 25_control-unit_verify.py
from itertools import product
X = None
SIGNALS = ["RegDst", "ALUSrc", "MemtoReg", "RegWrite", "MemRead", "MemWrite", "Branch", "ALUOp1", "ALUOp0"]
# 공개 강의 자료에서 확인한 표 (X = 아무 값이나 괜찮다)
TABLE = {
"R형": [1, 0, 0, 1, 0, 0, 0, 1, 0],
"lw": [0, 1, 1, 1, 1, 0, 0, 0, 0],
"sw": [X, 1, X, 0, 0, 1, 0, 0, 0],
"beq": [X, 0, X, 0, 0, 0, 1, 0, 1],
}
OPCODE = {"R형": 0b000000, "lw": 0b100011, "sw": 0b101011, "beq": 0b000100, "j": 0b000010}
FUNCT = {"add": 0b100000, "sub": 0b100010, "and": 0b100100, "or": 0b100101, "slt": 0b101010}
ALU_CODE = {"and": 0b0000, "or": 0b0001, "add": 0b0010, "sub": 0b0110, "slt": 0b0111}
def bits(x, n):
return [(x >> i) & 1 for i in range(n)] # [0번 비트, 1번 비트, ...]
def main_control_gates(opcode):
"""AND 게이트 넷이 명령어 종류를 알아보고, OR 게이트가 신호를 만든다."""
o = bits(opcode, 6)
n = [1 - b for b in o]
rformat = n[5] & n[4] & n[3] & n[2] & n[1] & n[0]
lw = o[5] & n[4] & n[3] & n[2] & o[1] & o[0]
sw = o[5] & n[4] & o[3] & n[2] & o[1] & o[0]
beq = n[5] & n[4] & n[3] & o[2] & n[1] & n[0]
return dict(RegDst=rformat, ALUSrc=lw | sw, MemtoReg=lw, RegWrite=rformat | lw, MemRead=lw,
MemWrite=sw, Branch=beq, ALUOp1=rformat, ALUOp0=beq)
def alu_control_table(aluop, funct):
if aluop == 0b00:
return ALU_CODE["add"]
if aluop == 0b01:
return ALU_CODE["sub"]
return {FUNCT[k]: ALU_CODE[k] for k in FUNCT}[funct]
def alu_control_gates(aluop1, aluop0, funct):
f = bits(funct, 6)
op3 = 0
op2 = aluop0 | (aluop1 & f[1])
op1 = (1 - aluop1) | (1 - f[2])
op0 = aluop1 & (f[3] | f[0])
return (op3 << 3) | (op2 << 2) | (op1 << 1) | op0
if __name__ == "__main__":
# 1. opcode 값: R형 0, lw 35, sw 43, beq 4, j 2
assert [OPCODE[k] for k in ("R형", "lw", "sw", "beq", "j")] == [0, 35, 43, 4, 2]
assert [hex(OPCODE[k]) for k in ("lw", "sw")] == ["0x23", "0x2b"]
# 2. 게이트 식이 표와 같다 (X 칸은 견주지 않는다)
for name, row in TABLE.items():
g = main_control_gates(OPCODE[name])
for sig, want in zip(SIGNALS, row):
assert want is X or g[sig] == want, (name, sig)
# 네 AND 게이트는 64가지 opcode 가운데 자기 것 하나에만 1을 낸다
for op in range(64):
g = main_control_gates(op)
hit = [k for k in ("R형", "lw", "sw", "beq") if OPCODE[k] == op]
if not hit:
assert g["RegWrite"] == 0 and g["MemWrite"] == 0 and g["Branch"] == 0 # 모르는 명령어는 아무것도 고치지 않는다
# X 칸에 게이트 식이 실제로 내는 값: sw와 beq에서 RegDst = 0, MemtoReg = 0
assert main_control_gates(OPCODE["sw"])["RegDst"] == 0 and main_control_gates(OPCODE["beq"])["MemtoReg"] == 0
# 3. ALU 제어 표
assert alu_control_table(0b00, 0) == 0b0010 and alu_control_table(0b01, 0) == 0b0110
assert [alu_control_table(0b10, FUNCT[k]) for k in ("add", "sub", "and", "or", "slt")] == [0b0010, 0b0110, 0b0000, 0b0001, 0b0111]
# 4. ALU 제어의 게이트 식이 표와 같다: ALUOp 00·01에서는 funct 64가지 모두, ALUOp 10에서는 다섯 funct
for funct in range(64):
assert alu_control_gates(0, 0, funct) == 0b0010
assert alu_control_gates(0, 1, funct) == 0b0110
for k in FUNCT:
assert alu_control_gates(1, 0, FUNCT[k]) == ALU_CODE[k], k
# 5. PCSrc = Branch AND Zero: 넷 가운데 하나만 1
assert [(b, z, b & z) for b, z in product((0, 1), repeat=2)] == [(0, 0, 0), (0, 1, 0), (1, 0, 0), (1, 1, 1)]
# 6. 카드: 신호 9비트를 표의 순서로 이어 쓴 값
row = lambda name: "".join("X" if v is X else str(v) for v in TABLE[name])
assert row("R형") == "100100010" and row("lw") == "011110000" and row("sw") == "X1X001000" and row("beq") == "X0X000101"
# X가 아닌 칸만 세면: 레지스터나 메모리를 고치는 신호(RegWrite, MemWrite)는 X가 될 수 없다
for name in TABLE:
assert TABLE[name][3] is not X and TABLE[name][5] is not X
print("ALL CHECKS PASSED")