#计组实验#单周期CPU设计
来源:互联网 发布:西部数码 域名 编辑:程序博客网 时间:2024/05/16 12:05
一.实验目的
1.掌握单周期CPU数据通路图的构成、原理及其设计方法
2.掌握单周期CPU的实现方法,代码实现方法
3.认识和掌握指令与CPU的关系
4.掌握测试单周期CPU的方法
二.实验内容
设计一个单周期CPU,该CPU至少能实现以下指令功能操作。需设计的指令与格式如下:
三.实验原理
单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用一个时钟周期完成。电平从低到高变化的瞬间称为时钟上升沿,两个相邻时钟上升沿之间的时间间隔称为一个时钟周期。时钟周期一般也称振荡周期。
CPU在处理指令时,一般需要经过以下几个步骤:
(1) 取指令(IF):根据程序计数器PC中的指令地址,从存储器中取出一条指令,同时,PC根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令时,则控制器把“转移地址”送入PC,当然得到的“地址”需要做些变换才送入PC。(2) 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。(3) 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。(4) 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。(5) 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。单周期CPU,是在一个时钟周期内完成这五个阶段的处理。
图2是一个简单的基本上能够在单周期CPU上完成所要求设计的指令功能的数据通路和必要的控制线路图。其中指令和数据各存储在不同存储器中,即有指令存储器和数据存储器。访问存储器时,先给出地址,然后由读或写信号控制操作。对于寄存器组,读操作时,先给出地址,输出端就直接输出相应数据;而在写操作时,在 WE使能信号为1时,在时钟边沿触发写入。图中控制信号作用如表1所示,表2是ALU运算功能表。
1.InstructionMemory
该模块实现了根据RW信号读Iaddr地址的指令的功能,写指令功能因为本实验没有用到,注释掉了。在初始化的时候,本模块先用系统调用$readmemb装载指令。
module InstructionMemory( input [31:0] Iaddr, // 指令存储器地址输入端口 // input [31:0] IDataIn, // 指令存储器数据输入端口(指令代码输入端口) input RW, // 指令存储器读写控制信号,为1写,为0读 output reg[31:0] IDataOut // 指令存储器数据输出端口(指令代码输出端口) ); reg[7:0] storage [127:0]; always @(RW or Iaddr ) begin if(RW == 1) begin //write /* 本次实验不需要用到写指令功能 storage[Iaddr] <= IDataIn[7:0]; storage[Iaddr + 1] <= IDataIn[15:8]; storage[Iaddr + 2] <= IDataIn[23:16]; storage[Iaddr + 3] <= IDataIn[31:24]; */ end else begin // read IDataOut[7:0] <= storage[Iaddr + 3]; IDataOut[15:8] <= storage[Iaddr + 2]; IDataOut[23:16] <= storage[Iaddr + 1]; IDataOut[31:24] <= storage[Iaddr]; end end initial begin $readmemb("F:/ECOP_Experiment/CPU/CPU.srcs/sources_1/new/ins.txt",storage); endendmodule
2.ALU32
ALU实现不复杂,根据表2,对每个opCode实现对应的运算,输出便可。
module ALU32( input [3:0] ALUopcode, input [31:0] rega, input [31:0] regb, output reg [31:0] result, output zero ); assign zero = (result==0)?1:0; always @( ALUopcode or rega or regb ) begin case (ALUopcode) 4'b0000 : result = rega + regb; 4'b0001 : result = rega - regb; 4'b0010 : result = (rega == regb)?1:0; 4'b0011 : result = regb << rega; 4'b0100 : result = rega & regb; 4'b0101 : result = (!rega) & regb; 4'b0110 : result = rega ^ regb; 4'b0111 : result = rega ^~ regb 4'b1000 : result = rega | regb; // 4'b1001 : result = (rega < regb)?1:0; default : begin result = 32'h00000000; $display (" no match"); end endcase endendmodule
3.CU
CU的设计较为复杂,实现的功能主要是根据输入的操作码opCode和ALU的zero信号来控制各个信号的输出。根据表1和表2,我们可以得出表3的ALU输出信号真值表。根据该表,实现每种输入对应的使出便可,如图6、图7、图8所示。
module CU( // input input [5:0] opCode, input zero, // output output RegDst, output InsMemRW, output PCWre, output ExtSel, output DBDataSrc, output WR, output RD, output ALUSrcB, output ALUSrcA, output PCSrc, output reg[3:0] ALUop, output RegWre ); // assign Reset=(opCode == 6'b110000)? 0 : 1; assign RegDst=(opCode == 6'b000000 || opCode == 6'b000010 || opCode == 6'b010001 || opCode == 6'b010010 || opCode == 6'b011000)? 1 : 0; assign PCWre=(opCode == 6'b111111)? 0 : 1; assign ALUSrcB=(opCode == 6'b000001 || opCode==6'b010000 || opCode == 6'b100110 || opCode == 6'b100111 ) ? 1 : 0; assign DBDataSrc=(opCode == 6'b100111)? 1 : 0; assign InsMemRw = 1; assign RD=(opCode == 6'b100111)? 1 : 0; assign WR=(opCode == 6'b100110)? 1 : 0; assign ExtSel=(opCode == 6'b010000 || opCode == 6'b010000 || opCode == 6'b100111)? 0 : 1; assign PCSrc=(zero == 0 && opCode == 6'b110000)? 1:0; assign ALUSrcA =(opCode == 6'b011000)?1: 0; assign RegWre=(opCode==6'b100110 || opCode==6'b110000 || opCode==6'b111111 )? 0 : 1; always @(opCode) begin case(opCode) // add 6'b000000 : ALUop = 4'b0000; // addi 6'b000001 : ALUop = 4'b0000; // sub 6'b000010 :ALUop = 4'b0001; // ori 6'b010000 :ALUop = 4'b1000; // and 6'b010001 :ALUop = 4'b0100; // or 6'b010010 :ALUop = 4'b1000; // sll 6'b011000 :ALUop = 4'b0011; // sw 6'b100110 : begin end // lw 6'b100111 : begin end // beq 6'b110000 : ALUop = 4'b0010; // halt 6'b111111 : begin end default: begin $display (" no match"); end endcase endendmodule
4.DataMemory
DataMemory主要实验数据的读写功能,如图9所示。
module DataMemory( // input input [31:0] Daddr, input [31:0] DataIn, input CLK, input WR, input RD, // output, output reg[31:0] DataOut );reg[7:0] storage [3:0];// RD为真的时候读数always @(Daddr or WR or RD) begin if (RD == 1) begin DataOut[7:0] <= storage[Daddr]; DataOut[15:8] <= storage[Daddr+1]; DataOut[23:16] <= storage[Daddr+2]; DataOut[31:24] <= storage[Daddr+3]; end else begin DataOut = 0; endend// 下降沿写数always @(negedge CLK) begin if(WR == 1) begin storage[Daddr] <= DataIn[7:0]; storage[Daddr+1] <= DataIn[15:8]; storage[Daddr+2] <= DataIn[23:16]; storage[Daddr+3] <= DataIn[31:24]; endendendmodule
5.PC
PC输出当前指令执行的条数,并可接受下一条执行指令的行数作为下一时钟周期的输出。
module PC( // input input clk, input reset, input PCWre, input [31:0] nextPC, // output output reg[31:0] curPC ); // 上升沿触发 always @(posedge clk or negedge reset) begin if(reset == 0) begin curPC <= 0; // Reset,指令地址初值为0 end else if(PCWre) begin curPC <= nextPC; // 下一指令地址 end //在控制台输出,便于debug $display("curPC: ", curPC, "nextPC:", nextPC); endendmodule
6.Regfile
Regfile是寄存器组,有读写指定寄存器的功能。
module Regfile( input CLK, input Wre, // input CLR, input [4:0] RdReg1addr, input [4:0] RdReg2addr, input [4:0] WrRegaddr, input [31:0] indata, output [31:0] reg1dataOut, output [31:0] reg2dataOut ); reg [31:0] register [31:0]; // 寄存器宽度32位,共31个,R1 -Rr31 integer i; // 变量 assign reg1dataOut = (RdReg1addr== 0)? 0 : register[RdReg1addr]; // 读寄存器1数据 assign reg2dataOut = (RdReg2addr== 0)? 0 : register[RdReg2addr]; // 读寄存器2数据 // psedge? always @(negedge CLK ) begin if ((WrRegaddr != 0) && (Wre == 1)) // $0寄存器不能修改 register[WrRegaddr] <= indata; // 写寄存器,非阻塞赋值"<=" end initial begin for (i=1; i<32; i=i+1) begin register[i] <= 0; // 寄存器清0,非阻塞赋值"<=" end endendmodule
7.Extend
Extend用于将16位数扩展至32位。
module extend( // input input [15:0] extendee, input ExtSel, // output output reg[31:0] result ); integer i; // assign result[15:0] = extendee[15:0]; // assign result[32:16] = 0; always @(ExtSel or extendee) begin for(i = 0; i < 16; i = i + 1) begin result[i] <= extendee[i]; end // 有符号数 if(ExtSel) begin for(i = 16; i < 32; i = i + 1) begin result[i] = extendee[15]; end end else begin for(i = 16; i < 32; i = i + 1) begin result[i] = 0; end end endendmodule
8.Selector
Selector是2选1选择器。当selectBit为0,输出inputA,否则输出inputB。
module selector( // input input selectBit, input [31:0] inputA, inputB, // output output reg[31:0] selectorResult ); always @(selectBit or inputA or inputB) begin if (0 == selectBit) begin selectorResult <= inputA; end else begin selectorResult <= inputB; end endendmodule
9.selectorFor5bit
selectorFor5bit功能与selector类似。
module selectorFor5bit( // input input selectBit, input [4:0] inputA, inputB, // output output reg[4:0] selectorResult ); always @(selectBit or inputA or inputB) begin if (0 == selectBit) begin selectorResult = inputA; end else begin selectorResult = inputB; end endendmodule
10.Decoder
Decoder用于从指令中抽取rs,rt,rd,sa,immediate,ext_sa并输出,这个模块是为了方便顶层模块的各模块的连接。
module decoder( // input input [31:0] ins, // output output reg[5:0] opCode, output reg[4:0] rs, output reg[4:0] rt, output reg[4:0] rd, output reg[4:0] sa, output reg[15:0] immediate, output reg[31:0] ext_sa ); always @(ins) begin opCode=ins[31:26]; rs=ins[25:21]; rt=ins[20:16]; rd=ins[15:11]; sa=ins[10:6]; immediate=ins[15:0]; ext_sa={ 27'b000000000000000000000000000 ,ins[10:6]}; endendmodule
11.singleCycleCPU
singleCycleCPU是顶层模块,主要是实例化各个模块,并连接对应端口。可以理解为将一个CPU的内部原件用导线连起来。
// 很多输出变量,为了Debug的时候,看波形图方便module singleCycleCPU( input clk, input reset, output PCWre, output RegWre, output ExtSel, output DBDataSrc, output [31:0] nextPC, curPC, instcode, output [3:0] ALUop, output [31:0] alu_inputA, output [31:0] alu_inputB, output [31:0] alu_out, output [5:0] opCode, output [4:0] rs, output [4:0] rt, output [4:0] rd, output [4:0] sa, output [15:0] immediate, output [31:0] ext_sa, output [4:0] /*readReg1addr, readReg2addr,*/ wrRegaddr, output [31:0] readReg1out, readReg2out, wrRegdata, output zero, output [31:0] mem_out, output [31:0] ext_immediate, output RegDst, ALUSrcA,ALUSrcB,DataMemRW, InstMemRW, Extsel,WR, RD, PCSrc ); // InstructionMemory(Iaddr,input RW,output reg[31:0] IDataOut); InstructionMemory insMem(curPC, InstMemRW,instcode); // module ALU32(input [3:0] ALUopcode,input [31:0] rega,input [31:0] regb,output reg [31:0] result,output zero); ALU32 alu (ALUop, alu_inputA, alu_inputB, alu_out, zero); // Control Units CU cu(opCode, zero, RegDst, InstMemRW, PCWre, ExtSel, DBDataSrc, WR, RD, ALUSrcB, ALUSrcA, PCSrc, ALUop, RegWre); // module DataMemory(input [31:0] Daddr,input [31:0] DataIn,input CLK,input WR,input RD,,output reg[31:0] DataOut); DataMemory dataMem(alu_out, readReg2out, clk, WR, RD, mem_out); // module PC(input clk,input reset,input PCWre,input [31:0] nextPC,output reg[31:0] curPC); PC pc(clk, reset, PCWre, nextPC, curPC); // regFile( CLK,Wre,[4:0] RdReg1addr, [4:0] RdReg2addr, [4:0] WrRegaddr, [31:0] indata, [31:0] reg1dataOut,[31:0] reg2dataOut) Regfile regfile(clk, RegWre,rs, rt, wrRegaddr, wrRegdata, readReg1out, readReg2out); // extend(input [15:0] extendee,input ExtSel,input SignOrZero, output reg[31:0] result); extend ext(immediate,ExtSel, ext_immediate); // selector(input selectBit,input [31:0] inputA, inputB,output reg[31:0] selectorResult); selector ALUscA(ALUSrcA, readReg1out, ext_sa, alu_inputA); selector ALUscB(ALUSrcB, readReg2out, ext_immediate,alu_inputB); selector DBsc(DBDataSrc, alu_out, mem_out, wrRegdata); selector NextPCsc(PCSrc, curPC + 4, curPC + 4 + (ext_immediate << 2), nextPC); selectorFor5bit WRRegFileSC(RegDst, rt, rd,wrRegaddr); // decoder(ins, opCode, rs, rt, rd, sa, immediata,output reg[31:0] ext_sa decoder dec(instcode, opCode, rs, rt, rd,sa, immediate, ext_sa);endmodule
12.CPUrunner
CPUrunner是本实验的测试模块。主要是声明变量,实例化一个singleCycleCPU,再设置clk和reset信号,整个CPU便开始运行。
// 一堆输出变量,也是为了方便Debugmodule CPUrunner; reg clk; reg reset; wire PCWre; wire RegWre; wire ExtSel; wire DBDataSrc; wire [31:0] nextPC; wire [31:0] curPC; wire [3:0] ALUop; wire [31:0] alu_inputA; wire [31:0] alu_inputB; wire [31:0] alu_out; wire zero; wire [31:0] instcode; wire [5:0] opCode; wire [4:0] rs; wire [4:0] rt; wire [4:0] rd; wire [4:0] sa; wire [15:0] immediate; wire [31:0] ext_sa; wire [4:0] wrRegaddr; wire [31:0] readReg1out; wire [31:0] readReg2out; wire [31:0] wrRegdata; wire [31:0] mem_out; wire [31:0] ext_immediate; wire [31:0] ext_immediate_shift; wire RegDst; wire ALUSrcA; wire ALUSrcB; wire ALUM2Reg; wire DataMemRW; wire InstMemRW; wire Extsel; wire WR; wire RD; wire PCSrc; singleCycleCPU uut( .clk(clk), .reset(reset), .PCWre(PCWre), .RegWre(RegWre), .ExtSel(ExtSel), .DBDataSrc(DBDataSrc), .nextPC(nextPC), .curPC(curPC), .ALUop(ALUop), .alu_inputA(alu_inputA), .alu_inputB(alu_inputB), .alu_out(alu_out), .zero(zero), .instcode(instcode), .opCode(opCode), .rs(rs), .rt(rt), .rd(rd), .sa(sa), .immediate(immediate), .ext_sa(ext_sa), .wrRegaddr(wrRegaddr), .readReg1out(readReg1out), .readReg2out(readReg2out), .wrRegdata(wrRegdata), .mem_out(mem_out), .ext_immediate(ext_immediate), .RegDst(RegDst), .ALUSrcA(ALUSrcA), .ALUSrcB(ALUSrcB), .DataMemRW(DataMemRW), .InstMemRW(InstMemRW), .Extsel(Extsel), .WR(WR), .RD(RD), .PCSrc(PCSrc) ); always #10 clk = ~clk; initial begin // initialize reset = 0; clk = 0; #100 reset=1; endendmodule
六.实验结果及分析
测试代码如表4所示。
这是我的测试代码。可以参考。
然后测试结果什么的,分析什么的,请读者自己跑,自己debug吧:)
毕竟结果分析这些还是要自己写的。
七.实验心得
其实就是debug方法
很多指令的执行都会遇到问题。解决的办法就是看着波形图,找出错的信号,然后根据数据通路往回推,一直推到最早出现问题的信号,这就是问题的根源。
以我的第二条测试指令为例,ori
assign RegDst=(opCode == 6'b000000 || opCode == 6'b000010 || opCode == 6'b010001 || opCode == 6'b010010 || opCode == 6'b011000)? 1 : 0;
再运行,发现问题解决(波形图如图30)。
限于篇幅,其他问题就不列举了,但是解决方法是类似的。
注:
从“一、实验目的”到“四、实验器材”的内容来源于中山大学何朝东老师下发的参考文档。
- #计组实验#单周期CPU设计
- #计组实验#多周期CPU设计
- 单周期CPU实验
- 单周期CPU设计
- 单周期CPU设计
- 单周期CPU设计
- 单周期CPU的设计
- MIPS-单周期CPU设计
- 单周期CPU设计(Verilog)
- (Verilog)单周期CPU设计
- 设计单周期cpu的日子
- MIPS单周期CPU设计(Verilog)
- 单周期cpu设计与实现
- 16位单周期CPU设计
- 16位单周期CPU设计
- 单周期CPU实验之实验介绍及分析
- 单周期CPU实验之学习之旅
- 单周期CPU实验之代码实现及模拟
- 关于设计模式的一些经验
- Ceilometr: 9、Gnocchi对接Ceilometer
- Linux网络编程之I/O复用
- iOS之UIDivice的使用
- 夜神模拟器连接eclipse时候出现unable to connect解决方案
- #计组实验#单周期CPU设计
- MVP+多线程+断电续传 实现app在线升级库 (手把手教你打造自己的lib)
- Java获取时间戳的几种方式
- Java并发(二)——线程相关类与线程池
- Java面试题全集(中)
- AI——Lisp语言实现 合一算法
- 深入理解HTTP协议(二)——协议详解篇
- 深入理解HTTP协议(一)——基础概念篇
- Java-文件的操作