六、8位二进制CPU的设计和实现3
1、取指令微程序
任务描述:将RAM中0地址 1地址 2地址数据分别取出放入IR DST SRC寄存器中。
pin.PC_OUT | pin.MAR_IN 程序计数器PC的数送到存储器地址寄存器MAR pin.RAM_OUT | pin.IR_IN | pin.PC_INC 存储器地址寄存器MAR指向RAM地址的数据送到指令寄存器IR,程序计数器PC+1 pin.PC_OUT | pin.MAR_IN 程序计数器PC的数送到存储器地址寄存器MAR pin.RAM_OUT | pin.DST_IN | pin.PC_INC存储器地址寄存器MAR指向RAM地址的数据送到目的数寄存器DST,程序计数器PC+1 pin.PC_OUT | pin.MAR_IN 程序计数器PC的数送到存储器地址寄存器MAR pin.RAM_OUT | pin.SRC_IN | pin.PC_INC存储器地址寄存器MAR指向RAM地址的数据送到目的数寄存器SRC,程序计数器PC+1
七条指令 00008040(H):0000 0000 0000 0000 1000 0000 0100 0000(B) 0001C0A4(H):0000 0000 0000 0001 1100 0000 1010 0100(B) 00008040(H):0000 0000 0000 0000 1000 0000 0100 0000(B) 0001C0C4(H):0000 0000 0000 0001 1100 0000 1100 0100(B) 00008040(H):0000 0000 0000 0000 1000 0000 0100 0000(B) 0001C0E4(H):0000 0000 0000 0001 1100 0000 1110 0100(B) 80000000(H):1000 0000 0000 0000 0000 0000 0000 0000(B) 第32位是1,时钟关闭,不输出一个周期的时钟,程序执行结束
指令解析
分析指令:00008040(H):0000 0000 0000 0000 1000 0000 0100 0000(B) 32 的0是打开时钟输出一个周期的时钟 31的0是内存控制器的PC的EN等于111(B)就是开启计数器的当前计数值,处于关闭读写模式,计数器的加1计算器开启,即加1计算器可以用时钟触发加1,control unit的A端输出:指令寄存器的38位输出+恒为0位,溢出位,奇偶校验位,奇偶标志位+程序计数器的低4位到内存 26~30暂时没有控制任何东西,保留 21~24的0000是不用控制–接收算术逻辑单元的与,或,异或,非的四选一的结果 18~20的000是不用控制–进行选择,算术逻辑单元用于与,或,异或,非的四选一 15~17的010是磁盘的PCEN等于010(B)就是开启计数器的当前计数值,处于读模式,计数器的加1计算器开启,即加1计算器可以用时钟触发加1,计数器的当前计数值输出到总线 11~14的00000是指令的1 ~ 5 位控制写什么寄存器,指令的6 ~ 10 位控制读什么寄存器, 6~10的00010是用于输入寄存器读写控制器的R端,用五三十二译码器等等处理后,可知寄存器读写控制位的低位只有MAR为1,MAR可写 1~5的000000是用于输入寄存器读写控制器的W端,用五三十二译码器等等处理后,可知寄存器读写控制位的高位都为0,都不可读
1~5 控制--写寄存器CS,DS,SS,ES,VEC,T1,T2,A,B,C,D,DI,SI,SP,BP,MSR,MAR,MDR,MC,IR,DST,SRC
6~10 控制--读寄存器CS,DS,SS,ES,VEC,T1,T2,A,B,C,D,DI,SI,SP,BP,MSR,MAR,MDR,MC,IR,DST,SRC
11,13控制--选择用目的操作数寄存器,源操作数寄存器,指令的1~5 中的其一控制写寄存器CS,DS,SS,ES,VEC,T1,T2,A,B,C,D,DI,SI,SP,BP,MSR,MAR,MDR,MC,IR,DST,SRC
12,14控制--选择用目的操作数寄存器,源操作数寄存器,指令的1~5 中的其一控制读寄存器CS,DS,SS,ES,VEC,T1,T2,A,B,C,D,DI,SI,SP,BP,MSR,MAR,MDR,MC,IR,DST,SRC
15~17控制--控制--磁盘RAM的程序计数器--是开关用程序计数器,还是**读写**程序计数器
18~20控制--进行选择,算术逻辑单元用于与,或,异或,非的四选一
21~24控制--接收算术逻辑单元的与,或,异或,非的四选一的结果
26~30暂时没有控制任何东西,保留
31 控制--内存ROM的程序计数器--是开关用程序计数器,还是**读写**程序计数器
32 控制--开关,是否输出一个周期的时钟
两位控制寄存器读写原理 两位控制读写原理:寄存器中CS,DS,SS,ES,VEC,T1,T2,A,B,C,D,DI,SI,SP,BP,MSR,MAR,MDR,MC,IR,DST,SRC这些的两位读写控制,低位是写端值,高位时读与写异或后的值。 这两位读写控制位输送给一字节的寄存器读写模式控制端IO 一字节的寄存器读写模式控制端IO将2位数据,低位输送给WE,高位输送给CS WE:一字节的寄存器读写模式的选择(信号)按钮 CS:cs信号等于1时,一字节的寄存器能被触发;cs信号等于0时,一字节的寄存器不能被触发
指令设计pin.py
MSR = 1
MAR = 2
MDR = 3
RAM = 4
IR = 5
DST = 6
SRC = 7
A = 8
B = 9
C = 10
D = 11
DI = 12
SI = 13
SP = 14
BP = 15
CS = 16
DS = 17
SS = 18
ES = 19
VEC = 20
T1 = 21
T2 = 22
MSR_OUT = MSR
MAR_OUT = MAR
MDR_OUT = MDR
RAM_OUT = RAM
IR_OUT = IR
DST_OUT = DST
SRC_OUT = SRC
A_OUT = A
B_OUT = B
C_OUT = C
D_OUT = D
DI_OUT = DI
SI_OUT = SI
SP_OUT = SP
BP_OUT = BP
CS_OUT = CS
DS_OUT = DS
SS_OUT = SS
ES_OUT = ES
VEC_OUT = VEC
T1_OUT = T1
T2_OUT = T2
_DST_SHIFT = 5
MSR_IN = MSR << _DST_SHIFT
MAR_IN = MAR << _DST_SHIFT
MDR_IN = MDR << _DST_SHIFT
RAM_IN = RAM << _DST_SHIFT
IR_IN = IR << _DST_SHIFT
DST_IN = DST << _DST_SHIFT
SRC_IN = SRC << _DST_SHIFT
A_IN = A << _DST_SHIFT
B_IN = B << _DST_SHIFT
C_IN = C << _DST_SHIFT
D_IN = D << _DST_SHIFT
DI_IN = DI << _DST_SHIFT
SI_IN = SI << _DST_SHIFT
SP_IN = SP << _DST_SHIFT
BP_IN = BP << _DST_SHIFT
CS_IN = CS << _DST_SHIFT
DS_IN = DS << _DST_SHIFT
SS_IN = SS << _DST_SHIFT
ES_IN = ES << _DST_SHIFT
VEC_IN = VEC << _DST_SHIFT
T1_IN = T1 << _DST_SHIFT
T2_IN = T2 << _DST_SHIFT
SRC_R = 2 ** 10
SRC_W = 2 ** 11
DST_R = 2 ** 12
DST_W = 2 ** 13
PC_WE = 2 ** 14
PC_CS = 2 ** 15
PC_EN = 2 ** 16
PC_OUT = PC_CS
PC_IN = PC_CS | PC_WE
PC_INC = PC_CS | PC_WE | PC_EN
HLT = 2 ** 31
程序设计
import pin
FETCH = [
pin.PC_OUT | pin.MAR_IN,
pin.RAM_OUT | pin.IR_IN | pin.PC_INC,
pin.PC_OUT | pin.MAR_IN,
pin.RAM_OUT | pin.DST_IN | pin.PC_INC,
pin.PC_OUT | pin.MAR_IN,
pin.RAM_OUT | pin.SRC_IN | pin.PC_INC,
]
编译下载
import os
import pin
import assembly as ASM
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'micro.bin')
micro = [pin.HLT for _ in range(0x10000)]
for addr in range(0x10000):
ir = addr >> 8
psw = (addr >> 4) & 0xf
cyc = addr & 0xf
if cyc < len(ASM.FETCH):
micro[addr] = ASM.FETCH[cyc]
with open(filename, 'wb') as file:
for var in micro:
value = var.to_bytes(4, byteorder='little')
file.write(value)
print('Compile micro instruction finish!!!')
2、MOV指令
1
强调一下几点概念 我们做的事是在对指令进行编码,如MOV,我们编码为1000 xxxx, MOV 寄存器,立即数 我们编码为1000 0100,这样的编码是为了方便将指令放在RAM中(同时在ROM指令集里寻找到相应的微指令), 我们将汇编语言如:MOV 寄存器,立即数翻译成1000 0100称作编译,但是编译出来的1000 0100这样的机器语言仍然只是编码。 真正的指令是存放在CPU内部的ROM指令集里的,我们需要用这个机器语言去找到对应的微指令。而这个微指令才是真正控制CPU里的所有资源的指令。
2
MSR = 1
MAR = 2
MDR = 3
RAM = 4
IR = 5
DST = 6
SRC = 7
A = 8
B = 9
C = 10
D = 11
DI = 12
SI = 13
SP = 14
BP = 15
CS = 16
DS = 17
SS = 18
ES = 19
VEC = 20
T1 = 21
T2 = 22
MSR_OUT = MSR
MAR_OUT = MAR
MDR_OUT = MDR
RAM_OUT = RAM
IR_OUT = IR
DST_OUT = DST
SRC_OUT = SRC
A_OUT = A
B_OUT = B
C_OUT = C
D_OUT = D
DI_OUT = DI
SI_OUT = SI
SP_OUT = SP
BP_OUT = BP
CS_OUT = CS
DS_OUT = DS
SS_OUT = SS
ES_OUT = ES
VEC_OUT = VEC
T1_OUT = T1
T2_OUT = T2
_DST_SHIFT = 5
MSR_IN = MSR << _DST_SHIFT
MAR_IN = MAR << _DST_SHIFT
MDR_IN = MDR << _DST_SHIFT
RAM_IN = RAM << _DST_SHIFT
IR_IN = IR << _DST_SHIFT
DST_IN = DST << _DST_SHIFT
SRC_IN = SRC << _DST_SHIFT
A_IN = A << _DST_SHIFT
B_IN = B << _DST_SHIFT
C_IN = C << _DST_SHIFT
D_IN = D << _DST_SHIFT
DI_IN = DI << _DST_SHIFT
SI_IN = SI << _DST_SHIFT
SP_IN = SP << _DST_SHIFT
BP_IN = BP << _DST_SHIFT
CS_IN = CS << _DST_SHIFT
DS_IN = DS << _DST_SHIFT
SS_IN = SS << _DST_SHIFT
ES_IN = ES << _DST_SHIFT
VEC_IN = VEC << _DST_SHIFT
T1_IN = T1 << _DST_SHIFT
T2_IN = T2 << _DST_SHIFT
SRC_R = 2 ** 10
SRC_W = 2 ** 11
DST_R = 2 ** 12
DST_W = 2 ** 13
PC_WE = 2 ** 14
PC_CS = 2 ** 15
PC_EN = 2 ** 16
PC_OUT = PC_CS
PC_IN = PC_CS | PC_WE
PC_INC = PC_CS | PC_WE | PC_EN
CYC = 2 ** 30
HLT = 2 ** 31
ADDR2 = 1 << 7
ADDR1 = 1 << 6
ADDR2_SHIFT = 4
ADDR1_SHIFT = 2
AM_INS = 0
AM_REG = 1
AM_DIR = 2
AM_RAM = 3
import pin
FETCH = [
pin.PC_OUT | pin.MAR_IN,
pin.RAM_OUT | pin.IR_IN | pin.PC_INC,
pin.PC_OUT | pin.MAR_IN,
pin.RAM_OUT | pin.DST_IN | pin.PC_INC,
pin.PC_OUT | pin.MAR_IN,
pin.RAM_OUT | pin.SRC_IN | pin.PC_INC,
]
MOV = 0 | pin.ADDR2
ADD = (1 << pin.ADDR2_SHIFT) | pin.ADDR2
NOP = 0
HLT = 0x3f
INSTRUCTIONS = {
2: {
MOV: {
(pin.AM_REG, pin.AM_INS): [
pin.DST_W | pin.SRC_OUT,
]
}
},
1: {},
0: {
NOP: [
pin.CYC,
],
HLT: [
pin.HLT,
]
}
}
print(INSTRUCTIONS)
print(bin(MOV))
import os
import pin
import assembly as ASM
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'micro.bin')
micro = [pin.HLT for _ in range(0x10000)]
def compile_addr2(addr, ir, psw, index):
global micro
op = ir & 0xf0
amd = (ir >> 2) & 3
ams = ir & 3
INST = ASM.INSTRUCTIONS[2]
if op not in INST:
micro[addr] = pin.CYC
return
am = (amd, ams)
if am not in INST[op]:
micro[addr] = pin.CYC
return
EXEC = INST[op][am]
if index < len(EXEC):
micro[addr] = EXEC[index]
else:
micro[addr] = pin.CYC
def compile_addr1(addr, ir, psw, index):
pass
def compile_addr0(addr, ir, psw, index):
global micro
op = ir
INST = ASM.INSTRUCTIONS[0]
if op not in INST:
micro[addr] = pin.CYC
return
EXEC = INST[op]
if index < len(EXEC):
micro[addr] = EXEC[index]
else:
micro[addr] = pin.CYC
for addr in range(0x10000):
ir = addr >> 8
psw = (addr >> 4) & 0xf
cyc = addr & 0xf
if cyc < len(ASM.FETCH):
micro[addr] = ASM.FETCH[cyc]
continue
addr2 = ir & (1 << 7)
addr1 = ir & (1 << 6)
index = cyc - len(ASM.FETCH)
if addr2:
compile_addr2(addr, ir, psw, index)
elif addr1:
compile_addr1(addr, ir, psw, index)
else:
compile_addr0(addr, ir, psw, index)
with open(filename, 'wb') as file:
for var in micro:
value = var.to_bytes(4, byteorder='little')
file.write(value)
print('Compile micro instruction finish!!!')
3
3、汇编编译器
1 python中class的定义及使用
2
3
import os
import re
import pin
import assembly as ASM
dirname = os.path.dirname(__file__)
inputfile = os.path.join(dirname, 'program.asm')
outputfile = os.path.join(dirname, 'program.bin')
annotation = re.compile(r"(.*?);.*")
codes = []
OP2 = {
'MOV': ASM.MOV
}
OP1 = {
}
OP0 = {
'NOP': ASM.NOP,
'HLT': ASM.HLT,
}
OP2SET = set(OP2.values())
OP1SET = set(OP1.values())
OP0SET = set(OP0.values())
REGISTERS = {
"A": pin.A,
"B": pin.B,
"C": pin.C,
"D": pin.D,
}
class Code(object):
def __init__(self, number, source):
self.numer = number
self.source = source.upper()
self.op = None
self.dst = None
self.src = None
self.prepare_source()
def get_op(self):
if self.op in OP2:
return OP2[self.op]
if self.op in OP1:
return OP1[self.op]
if self.op in OP0:
return OP0[self.op]
raise SyntaxError(self)
def get_am(self, addr):
if not addr:
return 0, 0
if addr in REGISTERS:
return pin.AM_REG, REGISTERS[addr]
if re.match(r'^[0-9]+$', addr):
return pin.AM_INS, int(addr)
if re.match(r'^0X[0-9A-F]+$', addr):
return pin.AM_INS, int(addr, 16)
raise SyntaxError(self)
def prepare_source(self):
tup = self.source.split(',')
if len(tup) > 2:
raise SyntaxError(self)
if len(tup) == 2:
self.src = tup[1].strip()
tup = re.split(r" +", tup[0])
if len(tup) > 2:
raise SyntaxError(self)
if len(tup) == 2:
self.dst = tup[1].strip()
self.op = tup[0].strip()
def compile_code(self):
op = self.get_op()
amd, dst = self.get_am(self.dst)
ams, src = self.get_am(self.src)
if op in OP2SET:
ir = op | (amd << 2) | ams
elif op in OP1SET:
ir = op | amd
else:
ir = op
return [ir, dst, src]
def __repr__(self):
return f'[{self.numer}] - {self.source}'
class SyntaxError(Exception):
def __init__(self, code: Code, *args, **kwargs):
super().__init__(*args, **kwargs)
self.code = code
def compile_program():
with open(inputfile, encoding='utf8') as file:
lines = file.readlines()
for index, line in enumerate(lines):
source = line.strip()
if ';' in source:
match = annotation.match(source)
source = match.group(1)
if not source:
continue
code = Code(index + 1, source)
codes.append(code)
with open(outputfile, 'wb') as file:
for code in codes:
values = code.compile_code()
for value in values:
result = value.to_bytes(1, byteorder='little')
file.write(result)
def main():
compile_program()
print('compile program.asm finished!!!')
if __name__ == '__main__':
main()
七、笔记
引脚标识的一般含义 A: 8位二进制加法器的8位输入 B: 8位二进制加法器的8位输入 O:8位二进制加法器的8位结果输出端(需要使能位使能) S:8位二进制加法器的8位结果输出端(不需要使能位使能) CO:8位二进制加法器的输出进位值 CI:8位二进制加法器的输入进位值
OP:取反器的使能(信号)按钮 CP:D边沿触发器的上升沿触发(信号)按钮
DI:一字节存储器的数据输入端 DO:一字节存储器的数据输出端 Clear:一字节存储器的清零(信号)按钮 Pre:一字节存储器的预设输入端 DO:一字节存储器的的数据输出端 EN:一字节存储器的允许触发(信号)按钮
CL:一字节的寄存器的清零(信号)按钮 W:一字节的寄存器的读模式设置(信号)按钮 R:一字节的寄存器的数据输出使能(信号)按钮(前提已经按动了寄存器的读模式(信号)按钮) WE:一字节的寄存器读写模式的选择(信号)按钮 CS:cs信号等于1时,一字节的寄存器能被触发;cs信号等于0时,一字节的寄存器不能被触发 IO:一字节的寄存器读写模式控制端,将2位数据,一位输送给WE,另一位输送给CS, -----最终低位为1时,寄存器为写模式;此时高位必须为1,以允许时钟触发写入数据 -----------------为0时,寄存器为读模式;此时高位必须为1,以允许输出数据 即11写,10读
W:16位的高位交叉编址存储器的数据输入使能(信号)按钮 R:16位的高位交叉编址存储器的数据输出使能(信号)按钮 A:16位的高位交叉编址存储器的地址总线
ALU:算术逻辑单元的与,或,异或,非的四选一的结果输出端 PWS:算术逻辑单元计算前后溢出位,奇偶校验位,奇偶标志位的值的输出端,共4位最高位恒为0 OP:算术逻辑单元用于与,或,异或,非的四选一的输入数据端 CL:清零算术逻辑单元计算前后溢出位,奇偶校验位,奇偶标志位的值 CP:触发更新算术逻辑单元计算前后溢出位,奇偶校验位,奇偶标志位的值
PC或EN:程序计数器的3位输入,这3位输入可以决定读写模式选择和开启关闭循环累加: -----------1)是否启动用程序计数器,程序计数器会用DI端的值更新计数器的当前计数值,再每个时钟加1(2位) -----------2)控制对当前计数值的读写(0~1位) 例子:a、PC或EN=111(B)就是开启计数器的当前计数值,处于写模式,计数器的加1计算器开启,即加1计算器可以用时钟触发加1
b、
磁盘RAM计数器EN端的三位输入位为010,高位0代表计数器关闭循环累加,低2位10代表计数器读写模式选择读,低2位是PC的寄存器的两位读写控制位(11写,10读),高1位是选择PC输入数据到PC寄存器,还是选择PC加法器输出数据到PC寄存器(1输出,0输入)。
磁盘RAM计数器EN端的三位输入位为011,高位0代表计数器关闭循环累加,低2位11代表计数器读写模式选择写
磁盘RAM计数器EN端的三位输入位为111,高位1代表计数器开启循环累加,低2位11代表计数器读写模式选择写
I1:CPU控制器的–输入:指令寄存器的8位输出 I2:CPU控制器的–输入:目的操作寄存器的8位输出 I3:CPU控制器的–输入:源操作寄存器的8位输出 SYC:CPU控制器的32位微指令中的4位地址位 A:CPU控制器的–输出:输出:指令寄存器的8位输出+恒为0位,溢出位,奇偶校验位,奇偶标志位+程序计数器的低4位 D:CPU控制器的–输入:内存的某个32位存储单元 HLT:CPU控制器的–输出:控制时钟信号的打开和关闭
8位二进制CPU的设计和实现CPU基本电路的实现1 8位二进制CPU的设计和实现CPU微机架构的实现2 8位二进制CPU的设计和实现3
|