单字节指令:
操作码 OP 4位 | 目的寄存器 Rd 2位 | 源寄存器 Rs 2位 |
双字节指令:
操作码 OP 4位 | 目的寄存器 Rd 2位 | 源寄存器 Rs 2位 |
立即数字段 8位 |
本CPU模型有13条指令,其中0-9为单字节指令,10-13为双字节指令。
使用Verilog实现的时候,会在内存之中预先设置好执行的指令,然后模拟仿真,观察结果。
序号 | 指令助记符 | 功能 | 操作码 | 举例 | 机器码 |
0 | IN Rd | 输入 IN←Rd | 0000 | IN R2 | 0000 10 00 |
1 | OUT Rs | 输出 OUT←Rs | 0001 | OUT R1 | 0001 00 01 |
2 | MOV Rd, Rs | 寄存器传输 Rd←Rs | 0010 | MOV R1,R2 | 0010 10 01 |
3 | ADD Rd, Rs | 加运算 Rd←Rs+Rd 并设置Cy,Zero标志 | 0011 | ADD R3,R0 | 0011 11 00 |
4 | AND Rd, Rs | 与运算 Rd←Rs & Rd 并设置Zero标志 | 0100 | AND R1,R0 | 0100 01 00 |
5 | MUL | 原码一位乘法运算 {HIGH,LOW}←RD*RS | 0101 | MUL RD RS | 0101 01 10 |
6 | STI | 开中断 | 0110 | STI | 0110xxxx |
7 | CLI | 关中断 | 0111 | CLI | 0111xxxx |
8 | IRET | 中断返回 | 1000 | IRET | 1000xxxx |
9 | HLT | 停机 | 1001 | HLT | 1001xxxx |
10 | LDI | Rd←立即数 | 1010 | LDI R1 59 | 101001xx 01011001 |
11 | LAD | 读内存Rd←MEM | 1011 | LAD R1 | 10110100 |
12 | STA | 写内存MEM←Rs | 1100 | STA R2 | 11001000 |
13 | JMP target | 无条件转移 PC←地址 | 1101 | JMP 59 | 110101xx 01011001 |
14 | JC target | 条件转移,有进位时转移 如果 FC= =1’b1, PC←立即数,程序实现转移。 否则 不修改PC,程序顺序执行。 | 1110 | JC 50 | 11100000 01010000 |
ALU运算器:
当ALU_B为1时,ALU输出,否则处于高阻态
S1、S0控制ALU的运算种类
FC进位标志寄存器:
当做加法指令时,进位保存在FC中
用于条件跳转指令的判断条件。
MAR地址寄存器、A、B运算暂存器:
输出没有三态控制,即只要输入到寄存器,输出就有值了。
程序计数器PC:
当LDPC为1时,在时钟上升沿,接收数据。
当INC_PC为1时,在时钟上升沿,实现PC+1。
当PC_B为1时,输出数据。否则高阻态。
内存:
/CE=1 /WE=x,不操作。
/CE=0 /WE=0 写内存;/CE=0 /WE=1 读内存。
读内存,由内存到MDR,再由MDR到总线。
寄存器IR:
寄存器R3~R0:
以R0为例:当R_B为1时,R输出(根据指令判断),
否则处于高阻态。
当LDR0为1时,在时钟上升沿,接收数据。
分为两个节拍
T1:在T1上升沿,微程序控制器工作,设置微指令各字段的值。根据各字段的值,设置微控制信号;各微控制信号,控制各寄存器传输到总线BUS。
T2:在T2的上升沿,当LDXXX的信号有效时,将数据从总线输入到寄存器中
运算器 2位 | 向总线输出 3位 | 从总线输入 3位 | 下地址 6位 |
S1 S0 | XXX_B | LDXXX | uMA |
XXX_B为1时,XXX部件输出到总线上。
LDXXX为1时,当T2上升沿到来时,将总线上的数据输入到XXX部件。
LDXXX字段
|
|
|
|
0 | 0 | 0 | NOP |
0 | 0 | 1 | LDA |
0 | 1 | 0 | LDB |
0 | 1 | 1 | LDR |
1 | 0 | 0 | LDOUT |
1 | 0 | 1 | LDMAR |
1 | 1 | 0 | LDIR |
1 | 1 | 1 | LDPC |
XXX_B字段
|
|
| |
0 | 0 | 0 | NOP |
0 | 0 | 1 | ALU_B |
0 | 1 | 0 | R_B |
0 | 1 | 1 | PC_B |
1 | 0 | 0 | STI |
1 | 0 | 1 | CLI |
1 | 1 | 0 | MEM_B |
1 | 1 | 1 | IN_B |
C字段
|
|
|
|
0 | 0 | 0 | NOP |
0 | 0 | 1 | P<1> |
0 | 1 | 0 | P<2> |
0 | 1 | 1 | P<3> |
1 | 0 | 0 | P<4> |
1 | 0 | 1 | P<5> |
1 | 1 | 0 | 保留 |
1 | 1 | 1 | 保留 |
module CPU(clk,reset,interrupt,T1,T2,PC,MAR,IR,uMA,A,B,ALU,R0,R1,R2,R3,LDR,LDIR,BUS,FC); input clk,reset,interrupt; output T1,T2,PC,MAR,IR,uMA,A,B,ALU,R0,R1,R2,R3,LDR,LDIR,BUS,FC; reg[7:0] MEM0,MEM1,MEM2,MEM3,MEM4; //内存中的普通程序 reg[7:0] R0,R1,R2,R3,ALU,A,B,PC,BUS,MAR,IR; reg[1:0] S; // 2位ALU控制字段 reg[2:0] LDXXX,XXX_B; // 三个控制字段 reg[5:0] uMA; // 6位微地址字段 //T1时刻直接XXX_B、设置LDXXX控制信号, T2时刻根据LDXXX信号 从BUS传数据 reg LDA,LDB,LDR,LDPC,LDOUT,LDMAR,LDIR,INC_PC,FC; // 微控制信号 reg T1; wire T2; //产生时序T1 T2;初始内存中的机器指令 always @(posedge clk) begin if(reset) begin T1 <= 1'b0; //内存初始赋值(输入机器指令)MEM MEM0 <= 8'b10100000; //立即数传值->R0 MEM1 <= 8'b10000000; MEM2 <= 8'b10100100; //立即数传值->R1 MEM3 <= 8'b10000000; MEM4 <= 8'b00110001; //R0+R1 ->R0 end else //设置时序 T1 <= ~T1; end //设置时序 assign T2=~T1; //T1 设置微代码各字段 always @(posedge T1) begin if(reset) uMA <= 6'b000000; else begin case(uMA) 6'h00: begin S <= 2'b00; XXX_B <= 3'b101; LDXXX <= 3'b000; INC_PC <= 1'b0; uMA <= 6'h01; end 6'h01: begin S <= 2'b00; XXX_B <= 3'b000; LDXXX <= 3'b000; INC_PC <= 1'b0; uMA <= 6'h02; end 6'h02: begin S <= 2'b00; XXX_B <= 3'b011; LDXXX <= 3'b101; INC_PC <= 1'b1; uMA <= 6'h03; end 6'h03: begin S <= 2'b00; XXX_B <= 3'b110; LDXXX <= 3'b110; INC_PC <= 1'b0; uMA <= 6'h04; end 6'h04: begin S <= 2'b00; XXX_B <= 3'b000; LDXXX <= 3'b000; INC_PC <= 1'b0; case({IR[7],IR[6],IR[5],IR[4]}) 4'b0011: uMA <= 6'h08; //ADD 4'b1010: uMA <= 6'h16; //LDI endcase end 6'h08: //RD->A begin S <= 2'b00; XXX_B <= 3'b010; LDXXX <= 3'b001; INC_PC <= 1'b0; uMA <= 6'h09; end 6'h09://RS->B begin S <= 2'b00; XXX_B <= 3'b010; LDXXX <= 3'b010; INC_PC <= 1'b0; uMA <= 6'h0A; end 6'h0A: //A+B->RD begin S <= 2'b01; XXX_B <= 3'b001; LDXXX <= 3'b011; INC_PC <= 1'b0; uMA <= 6'h01; end 6'h16: //LDI begin S <= 2'b00; XXX_B <= 3'b011; LDXXX <= 3'b101; INC_PC <= 1'b1; uMA <= 6'h17; end 6'h17: begin S <= 2'b00; XXX_B <= 3'b110; LDXXX <= 3'b011; INC_PC <= 1'b0; uMA <= 6'h01; end endcase end end //设置每字段的控制信号 always @(S or LDXXX or XXX_B) begin //ALU运算控制 case(S) 2'b00: begin ALU <= ALU; end 2'b01: begin {FC,ALU} <= A + B; end endcase // A字段控制 LDXX case(LDXXX) 3'b000: begin {LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0000000; end 3'b001: begin {LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b1000000; end 3'b010: begin {LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0100000; end 3'b011: begin {LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0010000; end 3'b100: begin {LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0001000; end 3'b101: begin {LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0000100; end 3'b110: begin {LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0000010; end 3'b111: begin {LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0000001; end endcase // B字段控制 XX_B case(XXX_B) 3'b000: begin BUS <= BUS; end 3'b001: begin BUS <= ALU; end 3'b010: begin case({IR[1],IR[0]}) 2'b00: BUS <= R0; 2'b01: BUS <= R1; 2'b10: BUS <= R2; 2'b11: BUS <= R3; endcase end 3'b011: begin BUS <= PC; end 3'b100: begin STI <= 1'b1; CLI <= 1'b0; end 3'b101: begin STI <= 1'b0; CLI <= 1'b1; end 3'b110: begin case(MAR) 8'h00: BUS <= MEM0; 8'h01: BUS <= MEM1; 8'h02: BUS <= MEM2; 8'h03: BUS <= MEM3; 8'h04: BUS <= MEM4; endcase end 3'b111: BUS <= IN; endcase endendmodul七、仿真
内存指令:MEM0 <= 8'b10100000; //立即数传值->R0 MEM1 <= 8'b10000000; MEM2 <= 8'b10100100; //立即数传值->R1 MEM3 <= 8'b10000000; MEM4 <= 8'b00110001; //R0+R1 ->R0八、结语
本文只实现了简单的功能加法、立即数传值指令,介绍如何使用Verilog 模拟微程序控制器,进而实现一个简单CPU的功能。实现微程序流程图中的所有指令的源代码,在我的GitHub 之中,欢迎大家参考。
新闻热点
疑难解答