控制单元详解:CPU中的组件与设计
控制单元是计算机中央处理器(CPU)的主要组件,能在处理器/计算机执行程序时指导操作。控制单元的主要功能是从计算机存储器中取指并执行指令。它接收用户输入指令/信息并转换为控制信号,再送CPU进一步执行。它作为冯·诺依曼架构一部分(约翰·诺依曼开发)。负责提供时序信号、控制信号并指导CPU执行程序。现代计算机中作为CPU内部组件。本文完整描述控制单元。
什么是控制单元?
接收用户输入信号/信息/指令并转换为CPU执行控制信号的组件。它控制和指导主存储器、算术逻辑单元(ALU)、输入输出设备,也负责发送给计算机CPU的指令。它从处理器主存取指令送处理器指令寄存器(含寄存器内容)。
控制单元将输入转控制信号送处理器,指导程序执行。处理器在计算机上指导要执行的操作。主要中央处理器(CPU)和图形处理器(GPU)需要控制作为内部组件。上图展示控制单元框图。
控制单元组件
组件包括指令寄存器、CPU内部控制信号、总线控制信号、控制总线、输入标志、时钟信号。
硬连线控制单元组件:指令寄存器(含操作码和地址字段)、时序单元、控制状态发生器、控制信号生成矩阵、指令解码器。
微程序控制单元组件:下一地址发生器、控制地址寄存器、控制存储器、控制数据寄存器。
功能
控制单元功能包括:
- 指导处理器与其他设备间数据流序列。
- 解释指令并控制处理器数据流。
- 从指令寄存器接收指令/命令生成控制信号序列。
- 负责控制CPU中执行单元如ALU、数据缓冲器、寄存器。
- 具备取指、译码、执行、存结果能力。
- 不处理和存储数据
- 为传输数据,与输入输出设备通信并控制计算机所有单元。
控制单元设计
设计可用两种控制单元类型:
- 硬连线型
- 微程序型(单级和两级)
硬连线控制单元
上图展示硬连线控制单元基本设计。此类型中,控制信号由专用硬件逻辑电路产生,电路结构无变化。产生的信号不可为处理器执行修改。
操作码基本数据(指令操作码)送指令解码器解码。指令解码器是解码操作码中不同数据类型的解码器集合。输出信号含有效信号值,作为矩阵发生器输入生成程序执行控制信号。
矩阵发生器提供控制单元状态和处理器输出信号(中断信号)。矩阵构建为可编程逻辑阵列。矩阵发生器生成的控制信号作为下一发生器矩阵输入,与时序单元矩形波时序信号结合。
为取新指令,控制单元转入初始阶段执行新指令。只要时序信号、输入信号、计算机指令状态不变,控制单元保持初始/第一阶段。任何产生信号变化均可改变控制单元状态。
外部信号或中断发生时,控制单元转入下一状态处理中断信号。标志和状态用于选择执行指令周期的期望状态。
最后状态下,控制单元取下一指令送程序计数器,再送存储器地址寄存器、缓冲寄存器、指令寄存器读指令。最后,若控制单元取到的最后指令是结束指令,则转处理器操作状态等待用户指示下一程序。
微程序控制单元
此类型中,控制存储器存储程序执行期间编码的控制信号。控制信号不立即产生和解码,因微程序在控制存储器存地址字段。整个过程为单级。
微操作用于执行程序中微指令。上图展示微程序控制单元框图。从图中,微指令地址从控制存储器地址寄存器获得。控制单元所有信息永久存控制存储器即ROM中。
控制存储器中的微指令由控制寄存器保持。因微指令为控制字形式(含二进制控制值),需1或多微操作完成数据处理。
微指令执行期间,下一地址发生器计算下一微指令地址送控制地址寄存器读取下一微指令。
微程序微操作序列由下一地址发生器执行,充当微程序定序器获取序列地址即从控制存储器读取。
控制单元Verilog代码
控制单元Verilog代码如下。
`include "prj_definition.v"
module CONTROL_UNIT(MEM_DATA, RF_DATA_W, RF_ADDR_W, RF_ADDR_R1, RF_ADDR_R2, RF_READ, RF_WRITE, ALU_OP1, ALU_OP2, ALU_OPRN, MEM_ADDR, MEM_READ, MEM_WRITE, RF_DATA_R1, RF_DATA_R2, ALU_RESULT, ZERO, CLK, RST)
// Output signals
// Outputs for register file
output [`DATA_INDEX_LIMIT:0] RF_DATA_W;
output [`ADDRESS_INDEX_LIMIT:0] RF_ADDR_W, RF_ADDR_R1, RF_ADDR_R2;
output RF_READ, RF_WRITE;
// Outputs for ALU
output [`DATA_INDEX_LIMIT:0] ALU_OP1, ALU_OP2;
output [`ALU_OPRN_INDEX_LIMIT:0] ALU_OPRN;
// Outputs for memory
output [`ADDRESS_INDEX_LIMIT:0] MEM_ADDR;
output MEM_READ, MEM_WRITE;
// Input signals
input [`DATA_INDEX_LIMIT:0] RF_DATA_R1, RF_DATA_R2, ALU_RESULT;
input ZERO, CLK, RST;
// Inout signal
inout [`DATA_INDEX_LIMIT:0] MEM_DATA;
// State nets
wire [2:0] proc_state;
//holds program counter value, stores the current instruction, stack pointer register
reg MEM_READ, MEM_WRITE;
reg MEM_ADDR;
reg ALU_OP1, ALU_OP2;
reg ALU_OPRN;
reg RF_ADDR_W, RF_ADDR_R1, RF_ADDR_R2;
reg RF_DATA_W;
reg [1:0] state, next_state;
PROC_SM state_machine(.STATE(proc_state),.CLK(CLK),.RST(RST));
always @ (posedge CLK)
begin
if (RST)
state <= RST;
else
state <= next_state;
end
always @ (state)
begin
MEM_READ = 1'b0; MEM_WRITE = 1'b0; MEM_ADDR = 1'b0;
ALU_OP1 = 1'b0; ALU_OP2 = 1'b0; ALU_OPRN = 1'b0;
RF_ADDR_R1 = 1'b0; RF_ADDR_R2 = 1'b0; RF_ADDR_W = 1'b0; RF_DATA_W = 1'b0;
case( state )
`PROC_FETCH : begin
next_state = `PROC_DECODE;
MEM_READ = 1'b1;
RF_ADDR_R1 = 1'b0; RF_ADDR_R2 = 1'b0;
RF_ADDR_W = 1'b1;
end
`PROC_DECODE : begin
next_state = `PROC_EXE;
MEM_ADDR = 1'b1;
ALU_OP1 = 1'b1; ALU_OP2 = 1'b1; ALU_OPRN = 1'b1;
MEM_WRITE = 1'b1;
RF_ADDR_R1 = 1'b1; RF_ADDR_R2 = 1'b1;
end
`PROC_EXE : begin
next_state = `PROC_MEM;
ALU_OP1 = 1'b1; ALU_OP2 = 1'b1; ALU_OPRN = 1'b1;
RF_ADDR_R1 = 1'b0;
end
`PROC_MEM: begin
next_state = `PROC_WB;
MEM_READ = 1'b1; MEM_WRITE = 1'b0;
end
`PROC_WB: begin
next_state = `PROC_FETCH;
MEM_READ = 1'b1; MEM_WRITE = 1'b0;
end
endcase
end
endmodule;
module PROC_SM(STATE,CLK,RST);
// list of inputs
input CLK, RST;
// list of outputs
output [2:0] STATE;
// input list
input CLK, RST;
// output list
output STATE;
reg [2:0] STATE;
reg [1:0] state;
reg [1:0] next_state;
reg PC_REG, INST_REG, SP_REF;
`define PROC_FETCH 3'h0
`define PROC_DECODE 3'h1
`define PROC_EXE 3'h2
`define PROC_MEM 3'h3
`define PROC_WB 3'h4
// initiation of state
initial
begin
state = 2'bxx;
next_state = `PROC_FETCH;
end
// reset signal handling
always @ (posedge RST)
begin
state = `PROC_FETCH;
next_state = `PROC_FETCH;
end
always @ (posedge CLK)
begin
state = next_state;
end
always @(state)
begin
if (state === `PROC_FETCH)
begin
next_state = `PROC_DECODE;
print_instruction(INST_REG);
end
if (state === `PROC_DECODE)
begin
next_state = `PROC_EXE;
end
if (state === `PROC_EXE)
begin
next_state = `PROC_MEM;
print_instruction(SP_REF);
end
if (state === `PROC_MEM)
begin
next_state = `PROC_WB;
end
if (state === `PROC_WB)
begin
next_state = `PROC_FETCH;
print_instruction(PC_REG);
end
end
task print_instruction;
input [`DATA_INDEX_LIMIT:0] inst;
reg [5:0] opcode;
reg [4:0] rs;
reg [4:0] rt;
reg [4:0] rd;
reg [4:0] shamt; reg [5:0] funct; reg [15:0] immediate; reg [25:0] address;
begin
// parse the instruction
// R-type
{opcode, rs, rt, rd, shamt, funct} = inst;
// I-type
{opcode, rs, rt, immediate } = inst;
// J-type
{opcode, address} = inst;
$write("@ %6dns -> [0X%08h] ", $time, inst);
case(opcode) // R-Type
6'h00 : begin
case(funct)
6'h20: $write("add r[%02d], r[%02d], r[%02d];", rs, rt, rd);
6'h22: $write("sub r[%02d], r[%02d], r[%02d];", rs, rt, rd);
6'h2c: $write("mul r[%02d], r[%02d], r[%02d];", rs, rt, rd);
6'h24: $write("and r[%02d], r[%02d], r[%02d];", rs, rt, rd);
6'h25: $write("or r[%02d], r[%02d], r[%02d];", rs, rt, rd);
6'h27: $write("nor r[%02d], r[%02d], r[%02d];", rs, rt, rd);
6'h2a: $write("slt r[%02d], r[%02d], r[%02d];", rs, rt, rd);
6'h00: $write("sll r[%02d], %2d, r[%02d];", rs, shamt, rd);
6'h02: $write("srl r[%02d], 0X%02h, r[%02d];", rs, shamt, rd);
6'h08: $write("jr r[%02d];", rs);
default: $write("");
endcase
end
// I-type
6'h08 : $write("addi r[%02d], r[%02d], 0X%04h;", rs, rt, immediate);
6'h1d : $write("muli r[%02d], r[%02d], 0X%04h;", rs, rt, immediate);
6'h0c : $write("andi r[%02d], r[%02d], 0X%04h;", rs, rt, immediate);
6'h0d : $write("ori r[%02d], r[%02d], 0X%04h;", rs, rt, immediate);
6'h0f : $write("lui r[%02d], 0X%04h;", rt, immediate);
6'h0a : $write("slti r[%02d], r[%02d], 0X%04h;", rs, rt, immediate);
6'h04 : $write("beq r[%02d], r[%02d], 0X%04h;", rs, rt, immediate);
6'h05 : $write("bne r[%02d], r[%02d], 0X%04h;", rs, rt, immediate);
6'h23 : $write("lw r[%02d], r[%02d], 0X%04h;", rs, rt, immediate);
6'h2b : $write("sw r[%02d], r[%02d], 0X%04h;", rs, rt, immediate);
// J-Type
6'h02 : $write("jmp 0X%07h;", address);
6'h03 : $write("jal 0X%07h;", address);
6'h1b : $write("push;");
6'h1c : $write("pop;");
default: $write("");
endcase
$write ("\n");
end
endtask
end module;
常见问题
1. 控制单元的工作是什么?
控制单元工作是指导计算机处理器执行数据或指令流。它控制、管理、协调主存、ALU、寄存器、输入输出单元。取指并生成执行控制信号。
2. 什么是控制存储器?
控制存储器通常为RAM或ROM,存储控制寄存器地址和数据。
3. 什么是Wilkes控制单元?
硬连线控制单元的时序和组合电路被Wilkes控制单元替代。它用存储单元存储微程序指令序列。
4. 什么是硬连线控制单元?
硬连线控制单元每个时钟脉冲从一状态变另一状态产生控制信号,电路无物理变化。控制信号生成依赖指令寄存器、解码器、中断信号。
5. 什么是控制存储器?
控制单元信息或数据临时或永久存储在控制存储器中。控制存储器两类:随机存取存储器(RAM)和只读存储器(ROM)。
以上涵盖控制单元定义、组件、设计、图解、功能、类型。留个问题给你:"控制地址寄存器的作用是什么?"
嵌入式