多周期CPU设计

------更新一下bug(测试代码有毒)-------

和单周期CPU的设计相同,都是为了实现一系列的指令功能,但需要指出的是何为多周期(注意与前面写道的单周期的区别,这也是设计的关键之处)

多周期CPU指的是将整个CPU的执行过程分成几个阶段,每个阶段用一个时钟去完成,然后开始下一条指令的执行,而每种指令执行时所用的时钟数不尽相同,这就是所谓的多周期CPU。

理解完多周期与单周期的区别后,开始我们的多周期CPU设计之路(可以随时对应单周期的设计,注意联系与区别)。

需要设计的指令及格式如下:

==>算术运算指令

(1)add rd, rs, rt


000000


rs(5位)


rt(5位)


rd(5位)


reserved

功能:rd<-rs + rt

(2)sub rd, rs, rt


000001


rs(5位)


rt(5位)


rd(5位)


reserved

完成功能:rd<-rs - rt

(3)addi  rt, rs, immediate


000010


rs(5位)


rt(5位)


immediate(16位)

功能:rt<-rs + (sign-extend)immediate

 

==>逻辑运算指令

(4)or rd, rs, rt


010000


rs(5位)


rt(5位)


rd(5位)


reserved

功能:rd<-rs | rt

(5)and rd, rs, rt


010001


rs(5位)


rt(5位)


rd(5位)


reserved

功能:rd<-rs & rt

(6)ori rt, rs, immediate


010010


rs(5位)


rt(5位)


immediate

功能:rt<-rs | (zero-extend)immediate

 

==>移位指令

(7)sll rd, rs,sa


011000


rs(5位)


未用


rd(5位)


sa


reserved

功能:rd<-rs<<(zero-extend)sa,左移sa位 ,(zero-extend)sa

 

==>传送指令

(8)move  rd, rs


100000


rs(5位)


00000


rd(5位)


reserved

功能:rd<-rs + $0

==>比较指令

(9) slt rd, rs, rt


100111


rs(5位)


rt(5位)


rd(5位)


reserved

功能:如果(rs<rt),则rd=1;  否则 rd=0

==>存储器读写指令

(10)sw rt, immediate(rs)


110000


rs(5位)


rt(5位)


immediate(16位)

功能:memory[rs+ (sign-extend)immediate]<-rt

(11)lw rt, immediate(rs)


110001


rs(5位)


rt(5位)


immediate(16位)

功能:rt <- memory[rs + (sign-extend)immediate]

==>分支指令

    (12)beq rs,rt, immediate (说明:immediate是从pc+4开始和转移到的指令之间间隔条数


110100


rs(5位)


rt(5位)


immediate(16位)

功能:if(rs=rt) pc <-pc+ 4 + (sign-extend)immediate <<2

==>跳转指令

(13)j addr


111000


addr[27..2]

功能:pc <{pc[31..28],addr[27..2],0,0},转移

(14)jr rs


111001


rs(5位)


未用


未用


reserved

功能:pc<-rs,转移

==>调用子程序指令

(15)jal addr


111010


addr[27..2]

功能:调用子程序,pc <- {pc[31..28],addr[27..2],0,0};$31<-pc+4,返回地址设置;子程序返回,需用指令 jr  $31。

==>停机指令

(16)halt (停机指令)


111111


00000000000000000000000000(26位)

不改变pc的值,pc保持不变。

设计原理

(1) 取指令(IF):根据程序计数器pc中的指令地址,从存储器中取出一条指令,同时,pc根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令时,则控制器把“转移地址”送入pc,当然得到的“地址”需要做些变换才送入pc。

(2) 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。

(3) 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。

(4) 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。

(5) 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。

实验中就按照这五个阶段进行设计,这样一条指令的执行最长需要五个(小)时钟周期才能完成,但具体情况怎样?要根据该条指令的情况而定,有些指令不需要五个时钟周期的,这就是多周期的CPU。

MIPS32的指令的三种格式:

R类型:

31       26 25       21 20      16 15       11 10        6 5       0


op


rs


rt


rd


sa


func

6位         5位       5位       5位        5位        6位

I类型:

31        26 25         21 20        16 15                       0


op


rs


rt


immediate

6位         5位          5位                16位

J类型:

31        26 25                                                0


op


address

6位                            26位

其中,

op:为操作码;

rs:为第1个源操作数寄存器,寄存器地址(编号)是00000~11111,00~1F;

rt:为第2个源操作数寄存器,或目的操作数寄存器,寄存器地址(同上);

rd:为目的操作数寄存器,寄存器地址(同上);

sa:为位移量(shift amt),移位指令用于指定移多少位;

func:为功能码,在寄存器类型指令中(R类型)用来指定指令的功能;

immediate:为16位立即数,用作无符号的逻辑操作数、有符号的算术操作数、数据加载(Laod)/数据保存(Store)指令的数据地址字节偏移量和分支指令中相对程序计数器(PC)的有符号偏移量;

   address:为地址。

状态的转移有的是无条件的,例如从IF状态转移到ID 和 EXE状态就是无条件的;有些是有条件的,例如ID 或 EXE状态之后不止一个状态,到底转向哪个状态由该指令功能,即指令操作码决定。每个状态代表一个时钟周期。

图3是多周期CPU控制部件的电路结构,三个D触发器用于保存当前状态,是时序逻辑电路,RST用于初始化状态“000“,另外两个部分都是组合逻辑电路,一个用于产生下一个阶段的状态,另一个用于产生每个阶段的控制信号。从图上可看出,下个状态取决于指令操作码和当前状态;而每个阶段的控制信号取决于指令操作码、当前状态和反映运算结果的状态zero标志等。

图4是一个简单的基本上能够在单周期上完成所要求设计的指令功能的数据通路和必要的控制线路图。其中指令和数据各存储在不同存储器中,即有指令存储器和数据存储器。访问存储器时,先给出地址,然后由读/写信号控制(1-写,0-读。当然,也可以由时钟信号控制,但必须在图上画出来)。对于寄存器组,读操作时,给出寄存器地址(编号),输出端就直接输出相应数据;而在写操作时,在 WE使能信号为1时,在时钟边沿触发写入。图中控制信号功能如表1所示,表2是ALU运算功能表。

特别提示,图上增加IR指令寄存器,目的是使指令代码保持稳定,还有pc增加写使能控制信号pcWre,也是确保pc适时修改,原因都是和多周期工作的CPU有关。ADR、BDR、ALUout、ALUM2DR四个寄存器不需要写使能信号,其作用是切分数据通路,将大组合逻辑切分为若干个小组合逻辑,大延时变为多个分段小延时。

表1 控制信号作用


控制信号名


状态“0”


状态“1”


PCWre


PC不更改,相关指令:halt


PC更改,相关指令:除指令halt外


ALUSrcB


来自寄存器堆data2输出,相关指令:add、sub、addi、or、and、ori、move、beq、slt


来自sign或zero扩展的立即数,相关指令:addi、ori、lw、sw、sll


ALUM2Reg


来自ALU运算结果的输出,相关指令:add、sub、addi、or、and、ori、slt、sll、move


来自数据存储器(Data MEM)的输出,相关指令:lw


RegWre


无写寄存器组寄存器,相关指令:

beq、j、sw、jr、halt


寄存器组寄存器写使能,相关指令:add、sub、addi、or、and、ori、move、slt、sll、lw、jal


WrRegData


写入寄存器组寄存器的数据来自pc+4(pc4),相关指令:jal,写$31


写入寄存器组寄存器的数据来自存储器、寄存器组寄存器和ALU运算结果,相关指令:add、addi、sub、or、and、ori、slt、sll、move、lw


InsMemRW


读指令存储器(Ins. Data),初始化为0


写指令存储器


DataMemRW


读数据存储器(Data MEM),相关指令:lw


写数据存储器,相关指令:sw


IRWre


IR(指令寄存器)不更改


IR寄存器写使能。向指令存储器发出读指令代码后,这个信号也接着发出,在时钟上升沿,IR接收从指令存储器送来的指令代码。与每条指令都相关。


 


ALUOp[2..0]


ALU 8种运算功能选择(000-111),看功能表


PCSrc[1..0]


00:pc<-pc+4,相关指令:add、addi、sub、or、ori、and、move、

slt、sll、sw、lw、beq(zero=0)

01:pc<-pc+4+(sign-extend)immediate,同时zero=1,相关指令:beq

10:pc<-rs,相关指令:jr

11:pc<-pc(31..28],addr,0,0  ,相关指令:j、jal


RegOut[1..0]


写寄存器组寄存器的地址,来自:

00:0x1F($31),相关指令:jal,用于保存返回地址($31<-pc+4)

01:rt字段,相关指令:addi、ori、lw

10:rd字段,相关指令:add、sub、or、and、move、slt、sll

11:未用


ExtSel[1..0]


00:(zero-extend)sa,相关指令:sll

01:(zero-extend)immediate,相关指令:ori

10:(sign-extend)immediate,相关指令:addi、lw、sw、beq

11:未用

相关部件及引脚说明:

InstructionMemory指令存储器

Iaddr,指令地址输入端口

DataIn,存储器数据输入端口

DataOut,存储器数据输出端口

RW,指令存储器读写控制信号,为1写,为0读

DataMemory数据存储器

Daddr,数据地址输入端口

DataIn,存储器数据输入端口

DataOut,存储器数据输出端口

RW,数据存储器读写控制信号,为1写,为0读

RegisterFile:(寄存器组)

Read Reg1,rs寄存器地址输入端口

Read Reg2,rt寄存器地址输入端口

Write Reg,将数据写入的寄存器,其地址输入端口(rt、rd)

Write Data,写入寄存器的数据输入端口

Read Data1,rs寄存器数据输出端口

Read Data2,rt寄存器数据输出端口

WE,写使能信号,为1时,在时钟上升沿写入

IR:    指令寄存器,用于存放正在执行的指令代码

ALU

result,ALU运算结果

zero,运算结果标志,结果为0输出1,否则输出0

表2 ALU运算功能表


ALUOp[2..0]


功能


描述


000


Y = A + B



001


Y = A – B



010


if (A<B)

Y = 1; else Y = 0;


比较A与B


011


Y = A>>B


A右移B位


100


Y = A<<B


A左移B位


101


Y = A ∨ B



110


Y = A ∧ B



111


Y = A ⊕ B


异或

分析与设计

此次实验是在上次单周期CPU基础上的改进,基本框架是相同的,但是相比单周期CPU的设计最大的不同就是“多周期”,何为多周期,在实现上与单周期又有何区别?简单的来说就是每个时钟周期内只执行一个阶段,而不是像单周期那样一个时钟周期就执行完整个指令,这就是单周期与多周期的主要区别。当然,由此也衍生出了其他的几个区别,比如,数据传输的延迟问题,增加的跳转指令等使得数据通路图变得复杂了很多。

根据这些区别,就可以开始在单周期CPU基础进行改进了。具体如下:

首先,确定每个指令的状态转化关系,具体转化图见上面原理分析,例如指令add的指令状态转化是IF(000)->ID(001)->EXE(110)->WB(111)->IF;所以,需要设置两个3位的状态变量(stage)和(next_stage)来表示状态的转变。由于指令是用来控制指令执行的,所以需要把指令状态的转变实现发在控制单元(controlUnit)中。

其次,就是数据传输延迟的问题,从数据通路图中可以看出,寄存器(RegisterFile)输出处存在两个延迟(ADR)和(BDR),计算单元(ALU)的输出处存在一个延迟,数据存储器(DataMemory)输出存在一个延迟,指令寄存器(InsMemory)输出处存在一个延迟,当然这里延迟需要控制信号IRWre的额外控制。综上来看,前四个延迟可以设计一个叫DataLate的简单模板模块(因为它们的输入、输出完全相同),具体实现如下。最后一个延迟可以放在INSMemory模块中。

module DataLate(input [31:0] i_data,

input clk,

output reg [31:0] o_data);

always @(negedge clk) begin

o_data = i_data;

end

endmodule

最后就是融入增加的跳转指令,比如根据数据通路图增添了一个如下的地址模块

另外,其他增加的数据线的增加就具体加入到相应模块中作为输入、输出。

所以,现在在单周期CPU的基础上,可以画出整个多周期CPU的逻辑图。

一、         控制单元(controlUnit.v)

相比单周期的CU,多周期的CU在输入输出上大致相同,但具体控制内容、存在比较大的差别。

1、指令状态转化的实现,前面已经提到。

2、控制信号的赋值。由于多周期指令信号控制状态的不同而可能改变,所以这里实现各控制信号的时候不再像单周期那样单纯利用操作码来实现。

类似的,写出控制信号与指令、指令状态的关系表,如下:


Stage


Ins


Zero


PCWre


ALUSrcB


ALUM2Reg


RegWre


WrRegData


InsMemRW


DataMemRW


IRWre


ExtSel[1..0]


PCSrc

[1..0]


RegOut

[1..0]


ALUOp[2..0]


sif

(000)


x


x


1


x


x


0


x


1


0


1


xx


xx


xx


xxx


sid

(001)


j


x


0


x


x


0


x


x


0


0


xx


11


xx


xxx


jal


x


0


x


x


1


0


x


0


0


xx


11


00


xxx


jr


x


0


x


x


0


x


x


0


0


xx


10


xx


xxx


halt


x


0


x


x


0


x


x


0


0


xx


xx


xx


xxx


exe1

(110)


add


x


0


0


x


0


x


x


0


0


xx


xx


xx


000


sub


x


0


0


x


0


x


x


0


0


xx


xx


xx


001


addi


x


0


1


x


0


x


x


0


0


10


xx


xx


000


or


x


0


0


x


0


x


x


0


0


xx


xx


xx


101


and


x


0


0


x


0


x


x


0


0


xx


xx


xx


110


ori


x


0


1


x


0


x


x


0


0


01


xx


xx


101


move


x


0


0


x


0


x


x


0


0


xx


xx


xx


000


slt


x


0


0


x


0


x


x


0


0


xx


xx


xx


010


sll


x


0


1


x


0


x


x


0


0


00


xx


xx


100


exe2

(101)


beq


0


0


0


x


0


x


x


0


0


10


00


xx


001


beq


1


0


0


x


0


x


x


0


0


10


01


xx


001


exe3

(010)


sw


x


0


1


x


0


x


x


0


0


10


xx


xx


000


lw


x


0


1


x


0


x


x


0


0


10


xx


xx


000


smem

(011)


sw


x


0


x


x


0


x


x


1


0


10


00


xx


xxx


lw


x


0


x


x


0


x


x


0


0


10


xx


xx


xxx


wb1

(111)


add


x


0


x


0


1


1


x


0


0


xx


00


10


xxx


sub


x


0


x


0


1


1


x


0


0


xx


00


10


xxx


addi


x


0


x


0


1


1


x


0


0


xx


00


01


xxx


or


x


0


x


0


1


1


x


0


0


xx


00


10


xxx


and


x


0


x


0


1


1


x


0


0


xx


00


10


xxx


ori


x


0


x


0


1


1


x


0


0


xx


00


01


xxx


move


x


0


x


0


1


1


x


0


0


xx


00


10


xxx


slt


x


0


x


0


1


1


x


0


0


xx


00


10


xxx


sll


x


0


x


0


1


1


x


0


0


xx


00


10


xxx


wb2

(100)


lw


x


0


x


1


1


1


x


0


0


xx


00


01


xxx

根据以上关系表,写出对应控制信号的实现。

`timescale 1ns / 1ps

module controlUnit(input [5:0] opcode,
                   input zero, clk, Reset,
		   output reg PCWre, InsMemRW, IRWre, WrRegData, RegWre, ALUSrcB, ALUM2Reg, DataMemRW,
		   output reg [1:0] ExtSel, RegOut, PCSrc,
						 output reg [2:0] ALUOp, state_out);
    parameter [2:0] sif = 3'b000,   // IF state
	                 sid = 3'b001,   // ID state
						  exe1 = 3'b110,  // add、sub、addl、or、and、ori、move、slt、sll
						  exe2 = 3'b101,  // beq
						  exe3 = 3'b010,  // sw、lw
						  smem = 3'b011,  // MEM state
						  wb1 = 3'b111,   // add、sub、addl、or、and、ori、move、slt、sll
						  wb2 = 3'b100;   // lw

	 parameter [5:0] addi = 6'b000010,
                    ori = 6'b010010,
                    sll = 6'b011000,
                    add = 6'b000000,
                    sub = 6'b000001,
                    move = 6'b100000,
                    slt = 6'b100111,
                    sw = 6'b110000,
                    lw = 6'b110001,
                    beq = 6'b110100,
                    j = 6'b111000,
                    jr = 6'b111001,
                    Or = 6'b010000,
                    And = 6'b010001,
                    jal = 6'b111010,
                    halt = 6'b111111;

	 reg [2:0] state, next_state;

	initial begin
	   PCWre = 0;
		InsMemRW = 0;
		IRWre = 0;
		WrRegData = 0;
		RegWre = 0;
		ALUSrcB = 0;
		ALUM2Reg = 0;
		DataMemRW = 0;
		ExtSel = 2'b11;
		RegOut = 2'b11;
		PCSrc = 2'b00;
		ALUOp = 0;
		state = sif;
		state_out = state;
	end

	always @(posedge clk) begin
	     if (Reset == 0) begin
		      state <= sif;
		  end else begin
		      state <= next_state;
		  end
		  state_out = state;
	 end

	always @(state or opcode) begin
	case(state)
	    sif: next_state = sid;
		 sid: begin
		     case (opcode[5:3])
			      3'b111: next_state = sif; // j, jal, jr, halt等指令
               3'b110: begin
                   if (opcode == 6'b110100) next_state = exe2; // beq指令
                   else next_state = exe3; // sw, lw指令
               end
               default: next_state = exe1; // add, sub, slt, sll等指令
           endcase
		 end
		 exe1: next_state = wb1;
       exe2: next_state = sif;
       exe3: next_state = smem;
		 smem: begin
		     if (opcode == 6'b110001) next_state = wb2; // lw指令
                else next_state = sif; // sw指令
       end
       wb1: next_state = sif;
       wb2: next_state = sif;
       default: next_state = sif;
	endcase
	end

	always @(state) begin

        // 确定PCWre的值
        if (state == sif && opcode != halt) PCWre = 1;
        else PCWre = 0;

        // 确定InsMemRW的值
        InsMemRW = 1;

        // 确定IRWre的值
        if (state == sif) IRWre = 1;
        else IRWre = 0;

        // 确定WrRegData的值
        if (state == wb1 || state == wb2) WrRegData = 1;
        else WrRegData = 0;

        // 确定RegWre的值
        if (state == wb1 || state == wb2 || opcode == jal) RegWre = 1;
        else RegWre = 0;

		  // 确定ALUSrcB的值
        if (opcode == addi || opcode == ori || opcode == sll || opcode == sw || opcode == lw) ALUSrcB = 1;
        else ALUSrcB = 0;

		  // 确定DataMemRW的值
        if (state == smem && opcode == sw) DataMemRW = 1;
        else DataMemRW = 0;

		  // 确定ALUM2Reg的值
        if (state == wb2) ALUM2Reg = 1;
        else ALUM2Reg = 0;

		  // 确定ExtSel的值
        if (opcode == ori) ExtSel = 2'b01;
        else if (opcode == sll) ExtSel = 2'b00;
        else ExtSel = 2'b10;

		  // 确定RegOut的值
        if (opcode == jal) RegOut = 2'b00;
        else if (opcode == addi || opcode == ori || opcode == lw) RegOut = 2'b01;
        else RegOut = 2'b10;

		  // 确定PCSrc的值
        case(opcode)
            j: PCSrc = 2'b11;
            jal: PCSrc = 2'b11;
            jr: PCSrc = 2'b10;
            beq: begin
                if (zero) PCSrc = 2'b01;
                else PCSrc = 2'b00;
            end
            default: PCSrc = 2'b00;
        endcase

		  // 确定ALUOp的值
        case(opcode)
            sub: ALUOp = 3'b001;
            Or: ALUOp = 3'b101;
            And: ALUOp = 3'b110;
            ori: ALUOp = 3'b101;
            slt: ALUOp = 3'b010;
            sll: ALUOp = 3'b100;
            beq: ALUOp = 3'b001;
            default: ALUOp = 3'b000;
        endcase

		  // 防止在IF阶段写数据
        if (state == sif) begin
            RegWre = 0;
            DataMemRW = 0;
        end
    end

endmodule

二、         算术运算单元(ALU)

模块ALU接收寄存器的数据和控制信号作为输入,将结果输出,具体设计如下:

`timescale 1ns / 1ps
module ALU(input [31:0] ReadData1, ReadData2, inExt,
           input ALUSrcB,
			  input [2:0] ALUOp,
			  output wire zero,
			  output reg [31:0] result);

	 initial begin
        result = 0;
    end

    wire [31:0] B;
	 assign B = ALUSrcB? inExt : ReadData2;
	 assign zero = (result? 0 : 1);

	 always @(ReadData1 or ReadData2 or B or ALUOp) begin
        case(ALUOp)
            3'b000: result = ReadData1 + B;  // A + B
            3'b001: result = ReadData1 - B;  // A - B
            3'b010: result = (ReadData1 < B ? 1 : 0);  // 比较A与B
            3'b011: result = ReadData1 >> B; // A右移B位
            3'b100: result = ReadData1 << B; // A左移B位
            3'b101: result = ReadData1 | B; // 或
            3'b110: result = ReadData1 & B; // 与
            3'b111: result = (~ReadData1 & B) | (ReadData1 & ~B); // 异或
        default: result = 0;
    endcase
  end

endmodule

三、         PC模块(PC)

相比单周期的PC单元,这里的PC模块中多了一个四选一的的地址数据选择器,目的在于根据控制信号正确匹配pc地址,同样输出当前PC地址,具体设计如下:

`timescale 1ns / 1ps
module PC(input clk, Reset, PCWre,
          input [1:0] PCSrc,
          input wire [31:0] imm, addr, RDout1,
          output reg [31:0] Address);

	 always @(PCWre or negedge Reset) begin // 这里和单周期不太一样,存在延迟的问题,只有当pcWre改变的时候或者Reset改变的时候再检测
        if (Reset == 0) begin
            Address = 0;
        end else if (PCWre) begin
            if (PCSrc == 2'b00) begin
				    Address = Address+4;
				end else if (PCSrc == 2'b01) begin
				    Address = imm*4+Address+4;
				end else if (PCSrc == 2'b10) begin
				    Address = RDout1;
				end else if (PCSrc == 2'b11) begin
				    Address = addr;
				end
        end
    end

endmodule

四、         PCAddr模块(补充address)

用于跳转指令的地址补充,输出32位的地址,模块实现如下:

`timescale 1ns / 1ps

module PCAddr(input [25:0] in_addr,
              input [31:0] PC0,
				  output reg [31:0] addr);
    wire [27:0] mid;
	 assign mid = in_addr << 2;
    always @(in_addr) begin
        addr <= {PC0[31:28], mid[27:0]};
    end

endmodule

五、         扩展单元(Extend)

相比单周期的扩展,此处的扩展内容多了一些,包括sa扩展、立即数扩展等,扩展选择由控制信号ExtSel控制,最后输出完整32位数据。

`timescale 1ns / 1ps

module Extend(input [15:0] in_num,
              input [1:0] ExtSel,
				  output reg [31:0] out);

    always @(in_num or ExtSel) begin
        case(ExtSel)
            2'b00: out <= {{27{0}}, in_num[10:6]}; // 扩充 sa
            2'b01: out <= {{16{0}}, in_num[15:0]}; // 扩充立即数, 如 ori指令
            2'b10: out <= {{16{in_num[15]}}, in_num[15:0]}; // 符号扩充立即数,如addi、lw、sw、beq指令
            default: out <= {{16{in_num[15]}}, in_num[15:0]}; // 默认符号扩展
        endcase
    end

endmodule

六、         数据存储单元(DataMemory)

数据存储单元的功能是读取数据,根据数据通路图可以有如下模块设计:

`timescale 1ns / 1ps

module DataMemory(input [31:0] addr, Data2,
                  input DataMemRW,
		  output reg [31:0] DataOut);

	 reg [7:0] memory [0:63];
	 integer i;
	 initial begin
	     for (i = 0; i < 64; i = i+1) memory[i] <= 0;
	 end

    always @(addr or Data2 or DataMemRW) begin
      if (DataMemRW) begin // write data
          memory[addr] = Data2[31:24];
          memory[addr+1] = Data2[23:16];
          memory[addr+2] = Data2[15:8];
          memory[addr+3] = Data2[7:0];
      end else begin // read data
          DataOut[31:24] = memory[addr];
          DataOut[23:16] = memory[addr+1];
          DataOut[15:8] = memory[addr+2];
          DataOut[7:0] = memory[addr+3];
      end
    end

endmodule

七、         指令存储单元(InsMemory)

将指令集以二进制的文件(my_store.txt)存入当前目录,然后通过读取文件的方式将指令存储到内存中,最后实现指令的读取。其中,

内部指令实现:

将需要测试的汇编指令程序转化为指令代码,如下:


地址


汇编程序


指令代码


op(6)


rs(5)


rt(5)


rd(5)/immediate (16)


16进制数代码


0x00000000


j 0x00000008


111000


00 00000000 00000000 00000010


 


0x00000004


jr  $31


111001


11111


00000


0x00000008


addi  $1,$0,8


000010


00000


00001


0000 0000 0000 1000


=


08010008


0x0000000C


ori  $2,$0,2


010010


00000


00010


0000 0000 0000 0010


=


48020002


0x00000010


add  $3,$1,$2


000000


00001


00010


00011 00000000000


=


00221800


0x00000014


sub  $4,$1,$2


000001


00001


00010


00100 00000000000


=


04222000


0x00000018


and  $5,$3,$2


010001


00011


00010


00101 00000000000


=


44622800


0x0000001C


or  $6,$1,$2


010000


00001


00010


00110 00000000000


=


40223000


0x00000020


move  $11,$1


100000


00001


00000


01011 00000000000


=


80205800


0x00000024


slt  $7,$1,$2


100111


00001


00010


00111 00000000000


=


9C223800


0x00000028


slt  $8,$2,$1


100111


00010


00001


01000 00000000000


=


9C414000


0x0000002C


sll  $2,$2,2


011000


00010


00000


00010 00010 000000


=


60401080


0x00000030


beq  $1,$2,-2 转02C


110100


00001


00010


1111 1111 1111 1110


=


D022FFFE


0x00000034


sw  $9,0($3)


110000


00011


01001


0000 0000 0000 0000


=


C0690000


0x00000038


jal  0x00000004


111010


00 00000000 00000000 0000001


=


0x0000003C


lw  $10,2($1)


110001


00001


01010


0000 0000 0000 0010


=


C42A0002


0x00000040


halt


111111


00000


00000


0000000000000000


=


FC000000

根据上表,可以创建my_store.txt的二进制指令文件,从而进行存取,二进制文件如下:

最终模块设计如下:

`timescale 1ns / 1ps

module InsMemory(input [31:0] addr,
                 input InsMemRW, IRWre, clk,
					  output reg [31:0] ins);

    reg [31:0] ins_out;
	 reg [7:0] mem [0:127];

	 initial begin
	     $readmemb("my_store.txt", mem);
		  //ins_out = 0;
	 end

    always @( addr or InsMemRW) begin
        if (InsMemRW) begin
          ins_out[31:24] = mem[addr];
          ins_out[23:16] = mem[addr+1];
          ins_out[15:8] = mem[addr+2];
          ins_out[7:0] = mem[addr+3];
        end
	 end

	 always @(posedge clk) begin
	     if (IRWre) ins <= ins_out;
	 end

endmodule

八、         寄存器单元(RegFile)

寄存器文件单元的功能是接收instructionMemory中的rs,rt,rd作为输入,输出对应寄存器的数据,从而达到取寄存器里的数据的目的,具体设计如下:

需要注意的是,在其内部实现的过程中,为了防止0号寄存器写入数据需要在writeReg的时候多加入一个判断条件,即writeReg不等于0时写入数据。

`timescale 1ns / 1ps

module RegFile(input [4:0] rs, rt, rd,
               input clk, RegWre, WrRegData,
		      	input [1:0] RegOut,
			  	   input [31:0] PC4, memData,
			 	   output reg [31:0] data1, data2);

    reg [31:0] i_data;

	 reg [4:0] temp;

	 reg [31:0] register [0:31];
	 integer i;
    initial begin
        for (i = 0 ; i < 32; i = i+1)
		      register[i] = 0;
    end

    always @(negedge clk) begin
	     case(RegOut)
	         2'b00: temp = 5'b11111;
		      2'b01: temp = rt;
		      2'b10: temp = rd;
				default temp = 0;
        endcase
		  assign i_data = WrRegData? memData : PC4;
	     assign data1 = register[rs];
        assign data2 = register[rt];
        if ((temp != 0) && (RegWre == 1)) begin // temp != 0 确保零号寄存器不会改变
            register[temp] <= i_data;
        end
    end

endmodule

九、         二选一数据模块(DataSelect_2)

简单的数据二选一,用于数据存储单元后面的数据选择,可见数据通路图,实现如下:

`timescale 1ns / 1ps

module DataSelect_2(input [31:0] A, B,
                    input Sign,
						  output wire [31:0] Get);

    assign Get = Sign ? B : A;

endmodule

十、         数据延迟模板模块(DataLate)

用于数据延迟,目的是使得数据正常输入输出,从数据通路图中可知此模板可在四处地方有用,已分析,所以具体模板实现如下:

`timescale 1ns / 1ps

module DataLate(input [31:0] i_data,
                input clk,
                output reg [31:0] o_data);

    always @(negedge clk) begin
        o_data = i_data;
    end

endmodule

十一、顶层模块(Top)

顶层模块(Top)是整个CPU的控制模块,通过连接各个子模块来达到运行CPU的目的,整个模块设计可以如下:

`include "ALU.v"
`include "DataLate.v"
`include "DataMemory.v"
`include "DataSelect_2.v"
`include "Extend.v"
`include "InsMemory.v"
`include "PC.v"
`include "PCAddr.v"
`include "RegFile.v"
`include "ControlUnit.v"
`timescale 1ns / 1ps

module Top(input clk, reset,
           output wire [2:0] state_out,
           output wire [5:0] opcode,
			  output wire [4:0] rs, rt, rd,
           // output ins[31:26], ins[25:21], ins[20:16], ins[15:11],
           output wire [31:0] ins, ReadData1, ReadData2, pc0, result);

	 assign opcode = ins[31:26];
	 assign rs = ins[25:21];
	 assign rt = ins[20:16];
	 assign rd = ins[15:11];

    // 数据通路
    wire [31:0] j_addr, out1, out2, result1, i_IR, extendData, LateOut1, LateOut2, DataOut;
    wire zero;

    // 控制信号
    wire [2:0] ALUOp;
    wire [1:0] ExtSel, RegOut, PCSrc;
    wire PCWre, IRWre, InsMemRW, WrRegData, RegWre, ALUSrcB, DataMemRW, ALUM2Reg;

	 PC pc(clk, reset, PCWre, PCSrc, extendData, j_addr, ReadData1, pc0);

	 InsMemory insmemory(pc0, InsMemRW, IRWre, clk, ins);

	 PCAddr pcaddr(ins[25:0], pc0, j_addr);

	 RegFile regfile(ins[25:21], ins[20:16], ins[15:11], clk, RegWre, WrRegData, RegOut, (pc0+4), LateOut2, ReadData1, ReadData2);

         DataLate ADR(ReadData1, clk, out1);
	 DataLate BDR(ReadData2, clk, out2);

	 Extend extend(ins[15:0], ExtSel, extendData);

         ALU alu(out1, out2, extendData, ALUSrcB, ALUOp, zero, result);

	 DataLate ALUout(result, clk, result1);

	 DataMemory datamemory(result1, out2, DataMemRW, DataOut);

	 DataSelect_2 dataselect_2(result, DataOut, ALUM2Reg, LateOut1);

	 DataLate ALUM2DR(LateOut1, clk, LateOut2);

	controlUnit control(ins[31:26], zero, clk, reset,PCWre, InsMemRW, IRWre, WrRegData, RegWre, ALUSrcB, ALUM2Reg, DataMemRW, ExtSel, RegOut, PCSrc, ALUOp, state_out);

endmodule

十二、测试程序(test)

从顶层模块中可以看出整个CPU的输入只有时钟信号clk和重置信号Reset,所以测试程序代码比较简单。(参照单周期CPU)

clk = 0;

reset= 0;

clk= ~clk;

//Wait 100 ns for global reset to finish

#100;

reset = 1;

forever #100 clk = ~clk;

最后,套路贴结果(部分):

至此,完工!!!

时间: 2024-10-16 04:43:01

多周期CPU设计的相关文章

单周期CPU设计

终于有点时间了,恰好多周期的设计也已经完成,其实只想写写多周期的,无奈单周期补上才好,哈哈哈~ -----+-----黄金分割线-----+----- 首先要理解什么叫单周期CPU(与后面多周期CPU对比)单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一条指令的执行,即一条指令用一个时钟周期完成. 单周期CPU的功能:能够实现一些指令功能操作.需设计的指令与格式如下: ==>算术运算指令 (1)add rd , rs, rt  (说明:以助记符表示,是汇编指令:以代码表示,是机

基于模型机的Micro cpu设计

第一章    模型机基本结构 由功能分析, 本次组成原理实验中设计的模型机包含下面这些部件:算术逻辑运算部件(ALU).程序计数器(PC).指令寄存器(IR).存储器(RAM).时序和微程序控制部件.模型机的数据通路为单总线结构,总线宽度为8位. 第二章    设计思想 1.基于状态机的模型机 如图1所示,整体模型机的设计采用了状态机的思想,将cpu的取指令.指令译码.指令执行所对应的操作拆分到各个状态中,并由此设计模型机的微操作. 图1 - 时钟控制信号状态机模型示意图 2.周期.节拍.脉冲制

cpu设计--&gt;cpu指令设计与全程逻辑分析

CPU指令设计,除了命名之外,更重要的是分析出指令如何才能够实现.对于图 3 1的CPU结构,如果指令是预先放到irom里的,那么,指令执行时要一条一条地从irom取出来,放到ir指令寄存器中,提供给control进行分析执行.每一条指令如何转变成机器动作,CPU的设计者必须认真地进行分析和规划.这一过程叫指令全程动作分析,简称指令全程分析. 我们针对图 3-1的结构,可以尝试设计一些用符号表示的汇编指令,然后对这些汇编指令如何实现,进行细致地分析.汇编指令的二进制数表示就是机器指令.汇编指令和

基于状态机的简易RISC CPU设计

目录 一.什么是CPU? 二.RISC CPU结构 1.时钟发生器 2.指令寄存器 3.累加器 4.RISC CPU算术逻辑运算单元 5.数据控制器 6.状态控制器 7.程序计数器 8.地址多路器 9.外围模块 10.地址译码器 a.RAM b.ROM 三.RISC CPU中各部件的相互连接关系 四.RISC CPU和它的外围电路 五.RISC CPU的寻址方式和指令系统 六.RISC CPU的操作和时序 正文 一.什么是CPU? CPU 即中央处理单元的英文缩写,它是计算机的核心部件.计算机进

简单CPU设计实践

目录 开始前的话. 3 总体组成. 3 运算器. 3 补码... 3 算术单元... 4 逻辑单元... 5 算术逻辑单元... 6 移位器... 7 运算器综合... 8 溢出判断... 9 运算器设计的总结和补充... 11 寄存器组. 11 注意事项... 11 使能端... 12 构建寄存器组... 12 控制器. 13 程序计数器... 14 指令寄存器... 14 数据通路设计... 14 控制字... 15 指令译码器... 16 状态寄存器... 18 控制器综合... 18 C

cpu设计过程

一款CPU是如何设计出来的? 前面一段,我们了解了芯片的制造过程,也就是如何从沙子中提取硅.把硅切成片,在片上通过离子注入实现PN结.实现各种二极管.三极管.CMOS管.从而实现千万门级大规模集成电路的大致流程.接下来,我们继续了解一下,一款CPU是如何设计出来的.集成电路设计一般分为模拟IC设计.数字IC设计以及数模混合等.而数字IC设计,比如设计一款ARM Soc CPU芯片的基本流程如下: 1)设计芯片规格:根据需求,设计出基本的框架.功能.模块划分.有些复杂的芯片可能还需要建模.使用MA

cpu设计--&gt;cpu必须有哪些指令

从软件的角度来看CPU,似乎CPU 就是一连串的指令符号构成的.因而,我们设计一个CPU,首先就要考虑应该设计哪些指令.计算机的指令系统需要根据任务需求来规划,一般都会有算术运算指令.逻辑运算指令.数据传送指令.访问存储器指令.结构转移指令等. 我们设计一个简单的CPU,一般也要有加.减.乘.除这四种算数运算,所以该CPU要有加.减.乘.除运算的指令.其实任何一个CPU都不能缺少算术运算的功能,因而算术运算的指令对每一个CPU都是不能缺少的指令. 如果你是一个软件编程人员,那么你一定熟悉程序的基

cpu设计--&gt;将基本动作组织成指令

将连接好的器件结构的基本动作进行适当地组织,就可以完成一些稍微复杂一点的功能,这些功能用特定的名称表达出来,就是我们所说的指令. 我们想设计一个数据通过指令给出,能够完成算术运算,并能将运算结果送到寄存器out输出的CPU.根据这个任务目标的需要,我们可以对图 3-1的结构设计出下面一些基本功能指令. (1)将dram的数取出送到da: (2)将dram的数取出送到out输出: (3)将dram的数与da的数相加,结果放在da: (4)将dram的数与da的数相减,结果放在da: (5)将dra

cpu设计--&gt;简单cpu组成

从逻辑上讲,大家都知道计算机是由运算器.控制器.存储器.输入设备和输出设备组成的.这仅仅是一个极其概括的认识,是非专业的理解.对于想要设计CPU的人来说,这种粗浅的理解显然是差距太大了.即使是最简单的计算机CPU结构,也远不是“运算器和控制器统称CPU”的那种概念,而实际应该理解成:计算机的CPU是由控制单位.运算单位和存储单位构成的.什么是控制单位.运算单位和存储单位?在图 3-1中我们用三种颜色粗略地给出了一个简单的CPU结构,从左向右分别是控制单位.运算单位和存储单位. 控制单位有指令寄存