BÁO CÁO LAB 3 ĐỒ ÁN VI XỬ LÝ VÀ CẤU TRÚC MÁY TÍNH
Nhóm 22A
1. Yêu cầu:
- Thiết kế chip Mips Single -Cycle 32bit bao gồm các lệnh LW, SW, J, JR, BNE, XORI, ADD, SUB và SLT. - Các lệnh đều được xử lý trong
1 chu kỳ lệnh (Single Cycle).
- Thiết kế dựa trên các module có sẵn regfife, alu, instrmem và datamem
2. Tập lệnh: Các lệnh chia làm 3 nhóm: -
R-format: ADD, SUB, SLT, JR.
-
I-format: LW, SW, BNE, XORI.
-
J-format: J.
2.1. R-format: - Ý nghĩa lệnh: + ADD rd, rs, rt: Reg[rd] = Reg[rs] + Reg[rt]. + SLT rd, rs, rt: If (Reg[rs] < Reg[rt]) Reg[rd] = 1 else Reg[rd] = 0. + SUB rd, rs, rt: Reg[rd] = Reg[rs] – Reg[rt]. + JR rs: PC = Reg[rs].
- Khuôn dạng:
opcode
rs
rt
rd
shamt
function
6 bit
5 bit
5 bit
5 bit
5 bit
6 bit
2.2. I-format: - Ý nghĩa lệnh: + BNE rs, rt, imm16: if (Reg[rs] != Reg[rt]) PC = PC + 4 + Sign_ext(Imm16)<<2 else PC = PC + 4. + LW rt, imm16(rs): Reg[rt] = Mem[Reg[rs] + Sign_ext(Imm16)]. + SW rt, imm16(rs): Mem[Reg[rs] + Sign_ext(Imm16)] = Reg[rt]. + XORI rt, rs, imm16: Reg[rt] = Reg[rs] XOR Zero_ext(Imm16).
- Khuôn dạng:
opcode
rs
rt
address/immediate
6 bit
5 bit
5 bit
16 bit
2.3. J-format: - Ý nghĩa lệnh: + J target: PC = { PC[31:28], target, 00 }.
- Khuôn dạng:
opcode
target address
6 bit
26 bit
3. Thiết kế:
3.1. Sơ đồ khối tổng quát:
3.2. Thiết kế các module : 3.2.1. Module Control: - Khối điều khiển trung tâm, có nhiệm vụ giải mã opcode của câu lệnh
thành
các
tín
hiệu
điều
khiển
đến
các
khối
khác
trong
chip.
RegDst Jump Branch MemRead
Main Control
Instruction [31:26]
MemReg ALUop Memwrite Aluscr Regwrite
-
Các tín hiệu điều khiển và giá trị tương ứng trong từng câu lệnh được
cho trong bảng sau: R-format
Jump
BNE
LW
SW
XORI
Opcode
000000
000010
000101
100011
101011
001110
RegDst
1
X
X
0
X
0
ALUSrc
0
X
0
1
1
1
MemtoReg
0
X
X
1
X
0
RegWrite
1
0
0
1
0
1
MemWrite
0
0
0
0
1
0
Branch
0
0
1
0
0
0
Jump
0
1
0
0
0
0
SignEx
X
X
1
1
1
0
ALUOp 0
1
X
0
0
0
1
ALUOp 1
1
X
1
0
0
0
-
Từ bảng trạng thái, sử dụng các cổng logic AND và OR, ta lập được các tín
hiệu ra từ 6 bit Opcode - Code: module Control(ALUOp, RegDst, Branch, MemtoReg, MemWrite, ALUSrc, RegWrite, Jump, SignEx, InsOp); output [1:0] ALUOp; output RegDst, Branch, MemtoReg, MemWrite, ALUSrc, ALUSrc, RegWrite, Jump, SignEx; input [5:0] InsOp; wire wire wire wire wire
[5:0] nInsOp; ALUSrc1, ALUSrc2, ALUSrc3; RegWrite1, RegWrite2, RegWrite3; SignEx1, SignEx2, SignEx3; [1:0] ALUOp1, ALUOp2;
not
#50
and
#100 (RegDst, nInsOp[0], nInsOp[1], nInsOp[2], nInsOp[3], nInsOp[4], nInsOp[5]);
and
or
#100 (ALUSrc1, InsOp[0], InsOp[1], nInsOp[2], nInsOp[3], nInsOp[4], InsOp[5]), (ALUSrc2, InsOp[0], InsOp[1], nInsOp[2], InsOp[3], nInsOp[4], InsOp[5]), (ALUSrc3, nInsOp[0], InsOp[1], InsOp[2], InsOp[3], nInsOp[4], nInsOp[5]); #50 (ALUSrc, ALUSrc1, ALUSrc2, ALUSrc3);
and
#100 (MemtoReg, InsOp[0], InsOp[1], nInsOp[2], nInsOp[3], nInsOp[4], InsOp[5]);
and nInsOp[5]),
#100
(nInsOp[0], InsOp[0]), (nInsOp[1], InsOp[1]), (nInsOp[2], InsOp[2]), (nInsOp[3], InsOp[3]), (nInsOp[4], InsOp[4]), (nInsOp[5], InsOp[5]);
(RegWrite1,
nInsOp[0],
(RegWrite2,
InsOp[0],
(RegWrite3,
nInsOp[0],
nInsOp[1],
nInsOp[2],
nInsOp[3],
nInsOp[4],
InsOp[1],
nInsOp[2],
nInsOp[3],
nInsOp[4],
InsOp[3],
nInsOp[4],
InsOp[5]), nInsOp[5]); or
#50
InsOp[1],
InsOp[2],
(RegWrite, RegWrite1, RegWrite2, RegWrite3);
and
#100 (MemWrite, InsOp[0], InsOp[1], nInsOp[2], InsOp[3], nInsOp[4], InsOp[5]);
and
#100 (Branch, InsOp[0], nInsOp[1], InsOp[2], nInsOp[3], nInsOp[4], nInsOp[5]);
and
#100 (Jump, nInsOp[0], InsOp[1], nInsOp[2], nInsOp[3], nInsOp[4], nInsOp[5]);
and
#100 (SignEx1, InsOp[0], nInsOp[1], InsOp[2], nInsOp[3], nInsOp[4], nInsOp[5]), (SignEx2, InsOp[0], InsOp[1], nInsOp[2], nInsOp[3], nInsOp[4], InsOp[5]), (SignEx3, InsOp[0], InsOp[1], nInsOp[2], InsOp[3], nInsOp[4], InsOp[5]); #50 (SignEx, SignEx1, SignEx2, SignEx3);
or and nInsOp[5]),
#100 (ALUOp1[1], nInsOp[0], nInsOp[1], nInsOp[2], nInsOp[3], nInsOp[4],
(ALUOp2[1], InsOp[0], nInsOp[1], InsOp[2], nInsOp[3], nInsOp[4], nInsOp[5]); or and nInsOp[5]),
#50
(ALUOp[1], ALUOp1[1], ALUOp2[1]);
#100 (ALUOp1[0], nInsOp[0], nInsOp[1], nInsOp[2], nInsOp[3], nInsOp[4], (ALUOp2[0],
nInsOp[5]); or
#50
nInsOp[0],
InsOp[1],
InsOp[2],
InsOp[3],
nInsOp[4],
(ALUOp[0], ALUOp1[0], ALUOp2[0]);
endmodule
3.2.2. Module ALUControl: -
Giải mã tín hiệu điều khiển ALUOp từ khối Control, kết hợp với 6 bit
Function để đưa ra tín hiệu điều khiển cho khối ALU thực hiện một trong bốn phép toán ADD, SUB, SLT và XOR - Tín hiệu điều khiển cho ALU được cho trong bảng sau:
ALUOp
Function
ALUCtr
00
(Load hoặc Store)
X
00 (ADD)
01
(XOR)
X
01 (XOR)
10
(BNE)
X
10 (SUB)
100000
00 (ADD)
100010
10 (SUB)
101010
11 (SLT)
11
(Lệnh R)
-
Ngoài ra khối ALUControl còn đưa ra một tín hiệu điều khiển
JumpReg cho câu lệnh JR với ALUOp = 11 và Function = 001000. - Code: module ALUControl(ALUCtr, JumpReg, ALUOp, Function); output [1:0] ALUCtr; output JumpReg; input [1:0] ALUOp; input [5:0] Function; wire wire wire wire
[1:0] nALUOp; [5:0] nFunction; [1:0] R, nR; R1, R2, ALUCtr01, ALUCtr02, ALUCtr11, ALUCtr12, ALUCtr13;
not
#50
(nALUOp[0], ALUOp[0]), (nALUOp[1], ALUOp[1]), (nFunction[0], Function[0]),
(nFunction[1], Function[1]), (nFunction[2], Function[2]), (nFunction[3], Function[3]), (nFunction[4], Function[4]), (nFunction[5], Function[5]), (nR[0], R[0]), (nR[1], R[1]); and Function[5]);
#100 (R[0], nFunction[0], Function[1], nFunction[2], Function[3], nFunction[4],
and Function[5]),
#100 (R1, nFunction[0], Function[1], nFunction[2], Function[3], nFunction[4], (R2, nFunction[0], Function[1], nFunction[2], nFunction[3], nFunction[4],
Function[5]); or and
#50
(R[1], R1, R2);
or
#50 (ALUCtr11, nALUOp[0], ALUOp[1]), (ALUCtr12, nR[0], R[1], ALUOp[0], ALUOp[1]), (ALUCtr13, R[0], R[1], ALUOp[0], ALUOp[1]); #50 (ALUCtr[1], ALUCtr11, ALUCtr12, ALUCtr13);
and
#50
or
#50
(ALUCtr01, ALUOp[0], nALUOp[1]), (ALUCtr02, ALUOp[0], ALUOp[1], R[0], R[1]); (ALUCtr[0], ALUCtr01, ALUCtr02);
and #100 (JumpReg, ALUOp[0], ALUOp[1], nFunction[0], nFunction[1], nFunction[2], Function[3], nFunction[4], nFunction[5]); endmodule
3.2.3. Khối dịch trái 2 (nhân 4):
-
Do bộ nhớ chương trình được đánh địa chỉ byte và cấu trúc lệnh được
lưu trong 1 word, do đó đối với các lệnh rẽ nhánh, giá trị trước khi đưa vào thanh ghi PC phải nhân với 4, hay nói cách khác là dịch trái giá trị nhị phân đi 2 đơn vị. - Code: module ShiftLeft2_1(data_out, data_in); output [31:0] data_out; input [31:0] data_in; genvar i; generate for (i=2; i<32; i=i+1) begin: SL2 assign data_out[i] = data_in[i-2]; end endgenerate assign data_out[0] = 0; assign data_out[1] = 0; endmodule module ShiftLeft2_2(data_out, data_in); output [27:0] data_out; input [25:0] data_in; genvar i; generate for (i=2; i<28; i=i+1) begin: SL2 assign data_out[i] = data_in[i-2]; end endgenerate assign data_out[0] = 0; assign data_out[1] = 0; endmodule
3.2.4. Khối mở rông (Extend): - Phần address/immediate của các câu lệnh I -format gồm 16bit, trong khi
đối số của các lệnh hoặc địa chỉ bao gồm 32bit, do đó cần có bước mở rông trước khi dùng để tính toán. -
Tùy theo lệnh mà mở rộng dấu hay mở rông zero, ta sử dụng 1 bit điều
khiển từ khối Control để lựa chọn kiểu mở rộng (SignEx). - Code:
module Extend(data_out, data_in, SignEx); output [31:0] data_out; input [15:0] data_in; input SignEx; assign data_out[15:0] = data_in; genvar i; generate for (i=16; i<32; i=i+1) begin: Ex and #50 (data_out[i], data_in[15], SignEx); end endgenerate endmodule
3.2.5. Các bộ chọn kênh - Các bộ chọn kênh được sử dụng để chọn 1 tín hiệu ra từ 2 tín hiệu vào -
Các tín hiệu điều khiển phụ thuộc vào từng câu lệnh và được đưa ra từ
khối Control - Code: module Muxer32(data_out, data_in1, data_in2, sel); output [31:0] data_out; input [31:0] data_in1, data_in2; input sel; wire wire
[31:0] f1, f2; nsel, dsel;
not buf
#50 #50
(nsel, sel); (dsel, sel);
genvar i; generate for (i=0; i<32; i=i+1) begin: Muxer and #50 (f1[i], nsel, data_in1[i]); and #50 (f2[i], dsel, data_in2[i]); or #50 (data_out[i], f1[i], f2[i]); end endgenerate endmodule
module Muxer5(data_out, data_in1, data_in2, sel); output [4:0] data_out; input [4:0] data_in1, data_in2; input sel;
wire wire
[4:0] f1, f2; nsel, dsel;
not buf
#50 #50
(nsel, sel); (dsel, sel);
genvar i; generate for (i=0; i<5; i=i+1) begin: Muxer and #50 (f1[i], nsel, data_in1[i]); and #50 (f2[i], dsel, data_in2[i]); or #50 (data_out[i], f1[i], f2[i]); end endgenerate endmodule
3.2.6. Bộ nhớ chương trình và bộ nhớ dữ liệu: -
Bộ nhớ chương trình dùng để lưu mã máy của các câu lệnh. Bộ nhớ
chương trình có 210 word và được lưu trong file instr.dat. - Bộ nhớ dữ liệu dùng để lưu các giá trị trong quá trình tính toán. Bộ nhớ
dữ liệu có 2 8 word và được lưu trong file data.dat. - cả 2 bộ nhớ đều được đánh địa chỉ byte.
3.3. Module chính: 3.3.1. Thanh ghi PC:
-
Thanh ghi PC được sử dụng để chỉ đến vị trí của câu lệnh tiếp theo sẽ
được thực hiện. -
Khi không có các câu lệnh rẽ nhánh, giá trị của thanh ghi PC sau mỗi
chu kỳ lệnh sẽ tự động tăng lên 4. Các câu lệnh rẽ nhánh J, JR, BNE sẽ có tác động làm thay đổi giá trị của thanh ghi PC. -
Ta sử dụng 1 thanh ghi PC để lưu giá trị hiện thời và 1 thanh ghi PCIn
để lưu giá trị tiếp theo của PC khi có xung clk. -
Mỗi khi có xung clk, giá trị của thanh ghi PCIn sẽ được đặt vào th ành
ghi PC, bộ nhớ chương trình sẽ sử dụng giá trị của thanh ghi PC để lấy ra câu lệnh tiếp theo. Giá trị mới của PC sau khi thực hiện xong câu lệnh sẽ được lưu vào thanh ghi PCIn để đợi đưa vào PC khi có xung x ung clk tiếp theo. 3.3.2. Tính toán xung clk: - Có 4 khối trong chip cần có xung clk theo thứ tự sau: + Thanh ghi PC
+ Bộ nhớ dữ liệu (data memory) + Bộ thanh ghi (Registers) + Thanh ghi PCIn -
Từ lúc có xung clk đến thanh ghi PC, tín hiệu đi qua bộ nhớ chương
trình (1000ps), bộ thanh ghi (khoảng 500ps), khối ALU (khoảng 6000ps). Do đó, ta chọn độ trễ của xung clk cho bộ nhớ dữ liệu so với xung clk của PC là 10000ps để đảm bảo giá trị nhận được là chính xác. - Tương tự ta chọn độ trễ của xung clk cho thanh ghi PCIn là 10000ps. - Tín hiệu đi qua bộ nhớ dữ liệu mất 1000ps, ta chọn độ trễ của xung clk
cho bộ thanh ghi so với bộ nhớ dữ liệu là 2000ps. + Việc thực hiện 1 câu lệnh phải hoàn tất trong vòng 1 chu kỳ xung clk, do vậy ta phải chọn độ rộng của xung clk đủ lớn. Dựa vào tính toán ở trên, ta chọn độ rộng của xung clk là 15000ps, hay chu kỳ của xung clk là 30000ps. 3.3.3. Code: module CPU(clk); input clk; reg reset; wire [31:0] PC, PCIn, PCIn1, PCIn2, PCIn3, PCplus4, Instruction, ExImm, SL2Imm, JumpAdr, BranchAdr; wire [31:0] ReadData1, ReadData2, RegWriteData, ALU2ndIn, ALU2ndIn, ALUResult, MemOutput;
wire [4:0] WriteRegister; wire [1:0] ALUOp, ALUCtr; wire CarryOut, zero, overflow, negative, nzero; wire RegDst, Branch, MemtoReg, MemWrite, ALUSrc, ALUSrc, RegWrite, Jump, SignEx, JumpReg, BranchSel; wire Memclk, Regclk, PCInclk, PCclk; initial begin reset = 0; #50 reset = 1; #50 reset = 0; end assign PCclk = clk; buf #10000 (Memclk, PCclk); buf #1000 (Regclk, Memclk); buf #10000 (PCInclk, PCclk); SinReg PCInReg(PCIn, PCInReg(PCIn, PCIn3, reset, PCInclk, 1'b1); SinReg PCReg(PC, PCIn, 1'b0, PCclk, 1'b1); InstructionMem InsMem(Instruction, InsMem(Instruction, PC); Regfile Registers(ReadData1, ReadData2, RegWriteData, Instruction[25:21] , Instruction[20:16], WriteRegister, RegWrite, Regclk, reset); alu ALU(ALUResult, CarryOut, zero, overflow, negative, ReadData1, ALU2ndIn, ALUCtr); DataMem DataMem(MemOutput, ALUResult, ReadData2, MemWrite, Memclk); Control Ctrl(ALUOp, RegDst, Branch, MemtoReg, MemWrite, ALUSrc, RegWrite, Jump, SignEx, Instruction[31:26]); ALUControl ALUCtrl(ALUCtr, JumpReg, ALUOp, Instruction[5:0]); Extend Ext(ExImm, Instruction[15:0], SignEx); ShiftLeft2_1 SL1(SL2Imm, ExImm); ShiftLeft2_2 SL2(JumpAdr[27:0], Instruction[25:0]); AddCPU Add1(PCplus4, PC, 32'h00000004); AddCPU Add2(BranchAdr, PCplus4, SL2Imm); not #50 (nzero, zero); and #50 (BranchSel, Branch, nzero); assign JumpAdr[31:28] = PCplus4[31:28]; Muxer5 Muxer32 Muxer32 MemtoReg); Muxer32 Muxer32 Muxer32 Endmodule
RegDstMux(WriteRegister, Instruction[20:16], Instruction[15:11], RegDst); ALUSrcMux(ALU2ndIn, ALUSrcMux(ALU2ndIn, ReadData2, ExImm, ALUSrc); MemtoRegMux(RegWriteData, MemtoRegMux(RegWriteD ata, ALUResult, MemOutput, BraSelMux(PCIn1, BraSelMux(PCIn1, PCplus4, BranchAdr, BranchSel); JumpMux(PCIn2, JumpMux(PCIn2, PCIn1, JumpAdr, Jump); JumpRegMux(PCIn3, JumpRegMux(PCIn3, PCIn2, ReadData1, JumpReg);