0 回顾
之前,我们完成了17条R型指令的设计,接下来,我们逐步完成I型指令的设计。
1 核心思想:增量思维 & 复用思维 & 学会选择 & 分治思想
增量思维
我们从无到有,构建了支持R型指令的CPU,接下来还会完成I型指令,J型指令,这样,单周期CPU就完成了,之后,我们会将其改进为多周期CPU,也会改进为五级流水线CPU,之后可能需要提升性能,增加串口;将内存移出去,增加内存控制器;增加指令Cache和数据Cache……让我们慢慢启程吧!
复用思维 & 学会选择
人生皆选择,CPU也一样,同样的一个功能,复用同一个器件,输入却可能不同,而控制器就帮其做出选择,配合多路选择器完成这个选择!
分治思想
我们每一次,都是完成一类指令,我们将一类具备共同特征的指令抽出来,一起来完成它,提高效率!
2 第一类I型指令
这些指令,是R型指令的立即数操作。
I-类型 | op | rs | rt | immediate |
---|---|---|---|---|
addi | 001000 | rs | rt | immediate |
addiu | 001001 | rs | rt | immediate |
andi | 001100 | rs | rt | immediate |
ori | 001101 | rs | rt | immediate |
xori | 001110 | rs | rt | immediate |
lui | 001111 | 00000 | rt | immediate |
slti | 001010 | rs | rt | immediate |
sltiu | 001011 | rs | rt | immediate |
它们共同的操作都是 rt <- rs op imm
特别的,lui
比较特殊,直接将imm移动到32位高16位,低16位补0.
另外,16位的imm需要扩展,可能是0扩展也可能是符号扩展,根据不同的指令进行不同判断即可。
然后,我们看看这些操作需要增加的器件和控制信号,再进行设计即可,这个过程已经做了好多遍了,这里可以直接加速完成,不再细说了。
这里,还需要一个额外的控制信号,表明是0扩展还是符号扩展,我们使用Zero_sign_ex
- 0:0扩展
- 1:符号扩展
还有Lui信号,表明是lui指令,输入到ALU。
这是新的数据通路,我们在设计的时候
- 将imm,ALUSrc,Zero_sign_ex集成到ALU中
- 将RegDst集成到Reg Files中
3 设计
3.1 控制器
特别注意RegDst
信号
- 0:rt I类指令
- 1:rd R类指令
instruction | op | ALUop | RegWrite | Sftmd | Jrn | Lui | RegDst | ALUSrc | Zero_sign_ex |
---|---|---|---|---|---|---|---|---|---|
addi | 001000 | 0000 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
addiu | 001001 | 0001 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
andi | 001100 | 0100 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
ori | 001101 | 0101 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
xori | 001110 | 0110 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
lui | 001111 | 1101 | 1 | 0 | 0 | 1 | 0 | 1 | 0 |
slti | 001010 | 1000 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
sltiu | 001011 | 1001 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/11/14 22:30:48
// Design Name:
// Module Name: control_1
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//module control_1(input [5:0] op,input [5:0] func,output reg RegWrite,output reg Sftmd, // indicate the instruction is sll/srl/sraoutput reg [3:0] ALUop,output reg Jrn, // jr instructionoutput reg Lui, // lui instructionoutput reg RegDst,output reg ALUSrc,output reg Zero_sign_ex);always @(*)
begincase(op)6'b000000: R-type begincase (func)6'b100000: // addbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0000;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b100001: // addubeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0001;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b100010: // subbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0010;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b100011: // sububeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0011;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b100100: // andbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0100;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b100101: // orbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0101;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b100110: // xorbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0110;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b100111: // norbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0111;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b101010: // sltbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b1000;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b101011: // sltubeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b1001;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b000100: // sllvbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b1010;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b000110: // srlvbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b1011;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b000111: // sravbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b1100;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b000000: // sllbeginRegWrite <= 1;Sftmd <= 1;ALUop <= 4'b1010;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b000010: // srlbeginRegWrite <= 1;Sftmd <= 1;ALUop <= 4'b1011;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b000011: // srabeginRegWrite <= 1;Sftmd <= 1;ALUop <= 4'b1100;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;end6'b001000: // jrbeginRegWrite <= 0;Sftmd <= 0;ALUop <= 4'b1111;Jrn <= 1;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;enddefault:beginRegWrite <= 0;Sftmd <= 0;ALUop <= 4'b1111;Jrn <= 0;Lui <= 0;RegDst <= 1;ALUSrc <= 0;Zero_sign_ex <= 0;endendcaseend/*************** I-type ***************/6'b001000: // addibeginALUop <= 4'b0000;RegWrite <= 1;Sftmd <= 0;Jrn <= 0;Lui <= 0;RegDst <= 0;ALUSrc <= 1;Zero_sign_ex <= 1;end6'b001001: // addiubeginALUop <= 4'b0001;RegWrite <= 1;Sftmd <= 0;Jrn <= 0;Lui <= 0;RegDst <= 0;ALUSrc <= 1;Zero_sign_ex <= 1;end6'b001100: // andibeginALUop <= 4'b0100;RegWrite <= 1;Sftmd <= 0;Jrn <= 0;Lui <= 0;RegDst <= 0;ALUSrc <= 1;Zero_sign_ex <= 0;end6'b001101: // oribeginALUop <= 4'b0101;RegWrite <= 1;Sftmd <= 0;Jrn <= 0;Lui <= 0;RegDst <= 0;ALUSrc <= 1;Zero_sign_ex <= 0;end6'b001110: // xoribeginALUop <= 4'b0110;RegWrite <= 1;Sftmd <= 0;Jrn <= 0;Lui <= 0;RegDst <= 0;ALUSrc <= 1;Zero_sign_ex <= 0;end6'b001111: // lui note beginALUop <= 4'b1101;RegWrite <= 1;Sftmd <= 0;Jrn <= 0;Lui <= 1;RegDst <= 0;ALUSrc <= 1;Zero_sign_ex <= 0;end6'b001010: // sltibeginALUop <= 4'b1000;RegWrite <= 1;Sftmd <= 0;Jrn <= 0;Lui <= 0;RegDst <= 0;ALUSrc <= 1;Zero_sign_ex <= 1;end6'b001011: // sltiubeginALUop <= 4'b1001;RegWrite <= 1;Sftmd <= 0;Jrn <= 0;Lui <= 0;RegDst <= 0;ALUSrc <= 1;Zero_sign_ex <= 0;enddefault:beginRegWrite <= 0;Sftmd <= 0;ALUop <= 4'b1111;Jrn <= 0;Lui <= 0;RegDst <= 0;ALUSrc <= 0;Zero_sign_ex <= 0;endendcase
endendmodule
3.2 ALU
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/11/14 22:30:23
// Design Name:
// Module Name: ALU_1
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//module ALU_1(// datainput [31:0] A,input [31:0] B,input [4:0] shamt,// controlinput [3:0] ALUop,input Sftmd, // shift instruction control/*** I-type ***/input [15:0] imm, // data// controlinput Lui, // 1:lui instructioninput ALUSrc, // 1:imm calculateinput Zero_sign_ex, // 0:zero extension; 1:sign extensionoutput reg [31:0] ALUresult);// convert A and B to signed numbers
wire signed [31:0] A_signed = A;
wire signed [31:0] B_signed;
wire signed [31:0] B_signed_origin = B; // for sra instruction// for shift instructions
// select data: if (Sftmd == 1) input shamt else input rs
wire [31:0] A_or_Shift = (Sftmd == 0) ? A : {27'b0,shamt};/*** I-type: data select ***/// immediate data extension and select
wire [31:0] zero_imm_ex = {16'b0,imm};
wire [31:0] sign_imm_ex = (imm[15] == 1)? {16'hffff,imm}: {16'b0,imm}; // NOTE: 16'b1 is incorrect
wire [31:0] imm_input = (Zero_sign_ex == 0)? zero_imm_ex: sign_imm_ex;// R[rt] or imm extension
wire [31:0] B_select = (ALUSrc == 0)? B: imm_input;
assign B_signed = B_select;/* calculate */
always @(*)
begincase (ALUop)4'b0000: // add addibeginALUresult <= A + B_select;end4'b0001: // addu addiubeginALUresult <= A + B_select;end4'b0010: // subbeginALUresult <= A - B;end4'b0011: // sububeginALUresult <= A - B;end4'b0100: // and andibeginALUresult <= A & B_select;end4'b0101: // or oribeginALUresult <= A | B_select;end4'b0110: // xor xoribeginALUresult <= A ^ B_select;end4'b0111: // nor noribeginALUresult <= ~(A | B_select);end4'b1000: // slt slti // note:********signed********//beginif(A_signed < B_signed)ALUresult <= 1;elseALUresult <= 0;end4'b1001: // sltu sltiubeginif(A < B_select)ALUresult <= 1;elseALUresult <= 0;end4'b1010: // sllv 10 /*** note: not B_select ***/beginALUresult <= B << A_or_Shift; // NOTE: not A << B!end4'b1011: // srlvbeginALUresult <= B >> A_or_Shift; // NOTE: not A >> B!end4'b1100: // srav // note: ******signed*******//beginALUresult <= B_signed_origin >>> A_or_Shift; // NOTE: not A_signed >> B!end4'b1101: // luibeginALUresult <= (Lui == 1)? {imm,16'b0}: 0;enddefault:beginALUresult <= 0;endendcase
endendmodule
3.3 Reg Files
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/11/14 22:31:09
// Design Name:
// Module Name: reg_files_1
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//module reg_files_1(input clk,input rst_n,/*** read port 1 ***/input [4:0] rA, // rs fieldoutput reg [31:0] A,/*** read port 2 ***/input [4:0] rB, // rtoutput reg [31:0] B,/*** write port ***/input [4:0] rW, // rdinput [31:0] writeData, // datainput RegWrite, // if RegWrite == 1,you can write data to reg files/*** I-type input control ***/input RegDst // 1: R-type destination is rd; 0: I-type dst is rt);// reg files
reg [31:0] register [0:31];
integer i;
initial
beginfor (i = 0;i < 32;i = i + 1)begin// 为了方便初步测试 ///register[i] <= i;// register[i] <= 0;end
end/******* write operation *******/
wire [4:0] rW_select;
assign rW_select = (RegDst == 1)? rW: rB;always @(posedge clk) // sequential logic
beginif(rst_n == 0) // reset is invalidbeginif((RegWrite == 1'b1) && (rW_select != 5'b0)) // write is valid and address is not equal zerobeginregister[rW_select] <= writeData;endelse;endelse;
end/******* rA read operation *******/
always @(*) // combinational logic
beginif(rst_n == 1)beginA <= 32'b0;endelse if(rA == 5'b0)beginA <= 32'b0;endelsebeginA <= register[rA];end
end/******* rB read operation *******/
always @(*) // combinational logic
beginif(rst_n == 1)beginB <= 32'b0;endelse if(rB == 5'b0) // $zerobeginB <= 32'b0;endelsebeginB <= register[rB];end
endendmodule
注意事项:需要16位1的写法是16'h_FFFF
,不是16'b1
,缺少的项会被自动补0!如果需要16位0,只需要16'b0
即可。
3.4 datapath
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/11/27 11:41:34
// Design Name:
// Module Name: datapath_1
// Project Name:
// Target Devices:
// Tool Versions:
// Description: 仅仅实现了几个简单的R类指令的最简单的数据通路,不与外界交互
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//module datapath_1(input clk,input rst_n,output [31:0] result // 测试syntheses,没有输出的模块是恐怖的);/******** PC ********/// pc_1 Inputs
wire Jrn;
wire [31:0] JrPC;// pc_1 Outputs
wire [31:0] pcOld;pc_1 u_pc_1 (.clk ( clk ),.rst_n ( rst_n ),.pcOrigin ( pcOld ),.JrPC ( JrPC ),.Jrn ( Jrn ),.pcOld ( pcOld ));///
/******** Instruction ROM ********/
///// blk_mem_gen_0 Inputs
// wire [13:0] addra = pcOld[15:2];// blk_mem_gen_0 Outputs // instructions
wire [31:0] instruction;blk_mem_gen_0 u_blk_mem_gen_0 (.clka ( clk ),.addra ( pcOld[15:2] ),.douta ( instruction ));/
/******** Reg Files ********/
/// reg_files_1 Inputs
wire [31:0] ALUresult;/// wire [4:0] rA = instruction[25:21];
/// wire [4:0] rB = instruction[20:16];
/// wire [4:0] rW = instruction[15:11];
/// wire [31:0] writeData = ALUresult;
wire RegWrite;
wire RegDst_in;// reg_files_1 Outputs
wire [31:0] A; // rs
wire [31:0] B; // rt
assign JrPC = A;reg_files_1 u_reg_files_1 (.clk ( clk ),.rst_n ( rst_n ),.rA ( instruction[25:21] ),.rB ( instruction[20:16] ),.rW ( instruction[15:11] ),.writeData ( ALUresult ),.RegWrite ( RegWrite ),.RegDst ( RegDst_in ),.A ( A ),.B ( B ));///
/******** ALU ********/
///// ALU_1 Inputs
// wire [31:0] A;
// wire [31:0] B;
wire [3:0] ALUop;
wire Sftmd;wire [15:0] imm = instruction[15:0];
wire Lui_in;
wire ALUSrc_in;
wire Zero_sign_ex_in;// ALU_1 Outputs
// wire [31:0] ALUresult = writeData; // Note:Error!ALU_1 u_ALU_1 (.A ( A ),.B ( B ),.shamt ( instruction[10:6]),.ALUop ( ALUop ),.Sftmd ( Sftmd ),/** I-type **/.imm ( imm ),.Lui ( Lui_in ),.ALUSrc ( ALUSrc_in ),.Zero_sign_ex ( Zero_sign_ex_in ),.ALUresult ( ALUresult ));/
/******** controler ********/
/// control_1 Inputs
// wire [5:0] op = instruction[31:26];
// wire [5:0] func = instruction[5:0];// control_1 Outputs
// wire RegWrite
// wire [3:0] ALUop;
wire Lui;
wire RegDst;
wire ALUSrc;
wire Zero_sign_ex;assign RegDst_in = RegDst; // Send to Reg Files
// send to ALU
assign Lui_in = Lui;
assign ALUSrc_in = ALUSrc;
assign Zero_sign_ex_in = Zero_sign_ex;control_1 u_control_1 (.op ( instruction[31:26] ),.func ( instruction[5:0] ),.RegWrite ( RegWrite ),.Sftmd ( Sftmd ),.ALUop ( ALUop ),.Jrn ( Jrn ),// I type.Lui ( Lui ),.RegDst ( RegDst ),.ALUSrc ( ALUSrc ),.Zero_sign_ex ( Zero_sign_ex ));assign result = ALUresult;endmodule
4 测试
注意,以前完成的指令依然要测试,因为你不知道你改完之后,是不是把以前对的改错了。
nop
add $1,$2,$3 # $1 = 2 + 3 = 5
addu $2,$4,$1 # $2 = 4 + 5 = 9
sub $4,$2,$1 # $4 = 9 - 5 = 4
subu $5,$4,$3 # $5 = 4 - 3 = 1and $6,$7,$8 # $6 = 0111 and 1000 = 0
or $7,$6,$8 # $7 = 0 or 1000 = 8
xor $7,$6,$8 # $7 = 0000 xor 1000 = 1000 = 8
nor $8,$7,$6 # $8 = not (1000 or 0) = 11111111111110111slt $10,$11,$12 # $10 = 11 < 12 = 1 # 应该用负数验证,以后再说
sltu $10,$12,$11 # $10 = 12 > 11 = 0# sllv $12,$5,$13 # $12 = 1101 << 1 = 1101_0 = 1A 【注意此处的倒置问题! sllv rd,rt,rs】
# srlv $12,$5,$13 # $12 = 1101 >> 1 = 110 = 6
# srav $14,$5,$15 # $14 = 1111 >>> 1 = 111 = 7 应该用负数验证,以后再说# 上面3条是错误的!我们应该改的不是使用,而是内部运算逻辑
# 对于使用者来说,逻辑就是 $13 << $5
# 而实际的编码是 rt = $13,rs = $5,这与一般的指令不一样
# 因此,我们在ALU运算中 rt--B,rs--A,应该是 【B << A】,而不是 A >> B。
sllv $12,$13,$5 # $12 = 1101 << 1 = 1101_0 = 1A
srlv $12,$13,$5 # $12 = 1101 >> 1 = 110 = 6
srav $14,$15,$5 # $14 = 1111 >>> 1 = 111 = 7 应该用负数验证,以后再说sll $16,$17,2 # $16 = 1_0001 << 2 = 100_0100 = 44
srl $16,$18,2 # $16 = 1_0010 >> 2 = 0100 = 4
sra $16,$19,2 # 应该用负数验证,以后再说 $16 = 4################ I type test #################
addi $17,$7,-1 # $17 = 8 - 1 = 7 测试符号扩展
addiu $17,$7,-2 # $17 = 8 - 2 = 6
andi $17,$8,1 # $17 = 1 测试zero extension
ori $17,$8,0 # $17 = ffff_fff7 = ffff_fff7
xori $17,$8,15 # $17 = ffff_fff7 xor 0000_000f = ffff_fff8 lui $17,100 # $17 = 前16位是64_后16位是0jr $16 # PC = 4
编码
memory_initialization_radix = 16;
memory_initialization_vector =
00000000,
00430820,
00811021,
00412022,
00832823,
00e83024,
00c83825,
00c83826,
00e64027,
016c502a,
018b502b,
00ad6004,
00ad6006,
00af7007,
00118080,
00128082,
00138083,
20f1ffff,
24f1fffe,
31110001,
35110000,
3911000f,
3c110064,
02000008;
行为仿真测试成功。