学校资料
设计一个多周期CPU,该CPU至少能实现以下指令功能操作。指令与格式如下:
==> 算术运算指令
1. add rd , rs, rt (说明:以助记符表示,是汇编指令;以代码表示,是机器指令)
000000 | rs(5位) | rt(5位) | rd(5位) | reserved |
---|
功能:rd←rs + rt。reserved为预留部分,即未用,一般填“0”。
2. addi rt , rs ,immediate
000010 | rs(5位) | rt(5位) | immediate(16位) |
---|
功能:rt←rs + (sign-extend)immediate;immediate符号扩展再参加“加”运算。
3. sub rd , rs , rt
000001 | rs(5位) | rt(5位) | rd(5位) | reserved |
---|
功能:rd←rs - rt
==> 逻辑运算指令
4. ori rt , rs ,immediate
010010 | rs(5位) | rt(5位) | immediate(16位) |
---|
功能:rt←rs | (zero-extend)immediate;immediate做“0”扩展再参加“或”运算。
5. and rd , rs , rt
010001 | rs(5位) | rt(5位) | rd(5位) | reserved |
---|
功能:rd←rs & rt;逻辑与运算。
6. or rd , rs , rt
010000 | rs(5位) | rt(5位) | rd(5位) | reserved |
---|
功能:rd←rs | rt;逻辑或运算。
==>移位指令
7. sll rd, rt,sa
011000 | 未用 | rt(5位) | rd(5位) | sa | reserved |
---|
功能:rd<-rt<<(zero-extend)sa,左移sa位 ,(zero-extend)sa
==>比较指令
8. sltiu rt, rs,immediate 不带符号
100011 | rs(5位) | rt(5位) | immediate(16位) |
---|
功能:if (rs <(sign-extend)immediate) rt =1 else rt=0, 具体请看表2 ALU运算功能表,不带符号
==> 存储器读/写指令
9. sw rt ,immediate(rs) 写存储器
110000 | rs(5位) | rt(5位) | immediate(16位) |
---|
功能:memory[rs+ (sign-extend)immediate]←rt;immediate符号扩展再相加。即将rt寄存器的内容保存到rs寄存器内容和立即数符号扩展后的数相加作为地址的内存单元中。
10. lw rt , immediate(rs) 读存储器
110001 | rs(5位) | rt(5位) | immediate(16位) |
---|
功能:rt ← memory[rs + (sign-extend)immediate];immediate符号扩展再相加。
即读取rs寄存器内容和立即数符号扩展后的数相加作为地址的内存单元中的数,然后保存到rt寄存器中。
==> 分支指令
11. beq rs,rt,immediate
110100 | rs(5位) | rt(5位) | immediate(16位) |
---|
功能:if(rs=rt) pc←pc + 4 + (sign-extend)immediate <<2 else pc ←pc + 4
特别说明:immediate是从PC+4地址开始和转移到的指令之间指令条数。immediate符号扩展之后左移2位再相加。为什么要左移2位?由于跳转到的指令地址肯定是4的倍数(每条指令占4个字节),最低两位是“00”,因此将immediate放进指令码中的时候,是右移了2位的,也就是以上说的“指令之间指令条数”。
12. bltz rs,immediate
110110 | rs(5位) | 00000 | immediate(16位) |
---|
功能:if(rs<0) pc←pc + 4 + (sign-extend)immediate <<2 else pc ←pc + 4
==>跳转指令
13. j addr
111000 | addr[27…2] |
---|
功能:pc <-{(pc+4)[31..28],addr[27..2],2{0}},无条件跳转。
说明:由于MIPS32的指令代码长度占4个字节,所以指令地址二进制数最低2位均为0,将指令地址放进指令代码中时,可省掉!这样,除了最高6位操作码外,还有26位可用于存放地址,事实上,可存放28位地址了,剩下最高4位由pc+4最高4位拼接上。
14. jr rs
111001 | rs(5位) | 未用 | 未用 | reserved |
---|
功能:调用子程序,pc <- {(pc+4)[31:28],addr[27:2],2’b00}; 31<-pc+4,返回地址设置;子程序返回,需用指令jr 31 < - p c + 4 , 返 回 地 址 设 置 ; 子 程 序 返 回 , 需 用 指 令 j r 31。跳转地址的形成同 j addr 指令。
15. j al addr
111000 | addr[27…2] |
---|
功能:调用子程序,pc <- {(pc+4)[31:28],addr[27:2],2’b00}; 31<-pc+4,返回地址设置;子程序返回,需用指令jr 31 < - p c + 4 , 返 回 地 址 设 置 ; 子 程 序 返 回 , 需 用 指 令 j r 31。跳转地址的形成同 j addr 指令。
==> 停机指令
16. halt
111111 | 00000000000000000000000000(26位) |
---|
功能:停机;不改变PC的值,PC保持不变。
多周期CPU指的是将整个CPU的执行过程分成几个阶段,每个阶段用一个时钟去完成,然后开始下一条指令的执行,而每种指令执行时所用的时钟数不尽相同,这就是所谓的多周期CPU。
实验中就按照这五个阶段进行设计,这样一条指令的执行最长需要五个(小)时钟周期才能完成,但具体情况怎样?要根据该条指令的情况而定,有些指令不需要五个时钟周期的,这就是多周期的CPU。
状态的转移有的是无条件的,例如从sIF状态转移到sID就是无条件的;有些是有条件的,例如sEXE状态之后不止一个状态,到底转向哪个状态由该指令功能,即指令操作码决定。每个状态代表一个时钟周期。
图3是多周期CPU控制部件的电路结构,三个D触发器用于保存当前状态,是时序逻辑电路,RST用于初始化状态“000“,另外两个部分都是组合逻辑电路,一个用于产生下一个阶段的状态,另一个用于产生每个阶段的控制信号。从图上可看出,下个状态取决于指令操作码和当前状态;而每个阶段的控制信号取决于指令操作码、当前状态和反映运算结果的状态zero标志和符号sign标志。
图4是一个简单的基本上能够在多周期CPU上完成所要求设计的指令功能的数据通路和必要的控制线路图。其中指令和数据各存储在不同存储器中,即有指令存储器和数据存储器。访问存储器时,先给出内存地址,然后由读或写信号控制操作。对于寄存器组,给出寄存器地址(编号),读操作时不需要时钟信号,输出端就直接输出相应数据;而在写操作时,在 WE使能信号为1时,在时钟边沿触发将数据写入寄存器。图中控制信号功能如表1所示,表2是ALU运算功能表。
特别提示,图上增加IR指令寄存器,目的是使指令代码保持稳定,pc写使能控制信号PCWre,是确保pc适时修改,原因都是和多周期工作的CPU有关。ADR、BDR、ALUoutDR、DBDR四个寄存器不需要写使能信号,其作用是切分数据通路,将大组合逻辑切分为若干个小组合逻辑,大延迟变为多个分段小延迟。
控制信号名 | 状态“0” | 状态“1” |
---|---|---|
RST | 初始化PC为0 | PC接收新地址 |
PCWre | PC不更改,相关指令:halt,另外,除‘000’状态之外,其余状态慎改PC的值 | PC更改,相关指令:除指令halt外,另外,在‘000’状态时,修改PC的值合适 |
ALUSrcA | 来自寄存器堆data1输出,相关指令:add、sub、addi、or、and、ori、beq、bltz、slt、sltiu、sw、lw | 来自移位数sa,同时,进行(zero-extend)sa,即 {{27{1’b0}},sa},相关指令:sll |
ALUSrcB | 来自寄存器堆data2输出,相关指令:add、sub、or、and、beq、bltz、slt、sll | 来自sign或zero扩展的立即数,相关指令:addi、ori、sltiu、lw、sw |
DBDataSrc | 来自ALU运算结果的输出,相关指令:add、addi、sub、ori、or、and、sltiu、slt、sll | 来自数据存储器(Data MEM)的输出,相关指令:lw |
RegWre | 无写寄存器组寄存器,相关指令:beq、bltz、j、sw、jr、halt | 寄存器组寄存器写使能,相关指令:add、sub、addi、or、and、ori、slt、sltiu、sll、lw、jal |
InsMemRW | 写指令存储器 | 读指令存储器(Ins. Data) |
mRD | 输出高阻态 | 读数据存储器,相关指令:lw |
mWR | 无操作 | 写数据存储器,相关指令:sw |
RegDst | 写寄存器组寄存器的地址,来自:00:0x1F( 31),相关指令:jal,用于保存返回地址( 31 ) , 相 关 指 令 : j a l , 用 于 保 存 返 回 地 址 ( 31<-pc+4);01:rt字段,相关指令:addi、ori、sltiu、lw;10:rd字段,相关指令:add、sub、or、and、slt、sll;11:未用; | |
ExtSel | (zero-extend)immediate(0扩展),相关指令:ori、sltiu | (sign-extend)immediate(符号扩展),相关指令:addi、sw、lw、beq、bltz |
PCSrc[1..0] | 00:pc<-pc+4,相关指令:add、addi、sub、or、ori、and、slt、sltiu、sll、sw、lw、beq(zero=0)、bltz(sign=0,或zero=1);01:pc<-pc+4+(sign-extend)immediate,相关指令:beq(zero=1)、bltz(sign=1,zero=0);10:pc<-rs,相关指令:jr; 11:pc<-{(pc+4)[31:28],addr[27:2],2’b00},相关指令:j、jal; | |
ALUOp[2..0] | ALU 8种运算功能选择(000-111),看功能表 |
值得注意的问题,设计时,用模块化、层次化的思想方法设计,关于如何划分模块、如何整合成一个系统等等,是必须认真考虑的问题。
设计思路以及流程:
控制信号、指令以及执行状态相互关系的过程
理清控制信号、指令以及执行状态三者之间的关联是设计多周期CPU的第一个步骤,也是主要难点之一。在多周期CPU中,部分控制信号遇到在特定的状态下控制单元才对该控制信号进行相应的改变,而部分信号则与当前执行状态无关,可以在取指令阶段时就赋予需要的值。例如,对于任何一条指令,信号量RegWre的状态时比较需要注意的,假如再对于需要写寄存器组的指令一开始就对所有信号量赋予相应的值,那么寄存器组的写使能端信号量RegWre为使能状态,此时,在指令执行处理过程中,可能不止在结果写回的时候写入数据,在指其余的执行过程中都可能发生寄存器组写入数据,这时候可能导致原来寄存器组一些数据被修改替换了,再次使用的时候得到的结果与预期的就不一样了,存在比较大的危害性。同时指令的执行不一定需要所有的模块,比如跳转指令,其无需对数据寄存器进行读写操作,则数据寄存器的控制信号mRD、mWR都无需使用到,因此为了防止出现一些不必要的错误,统一将指令相对应的无关的使能控制信号(x)默认为低电平(0),无需ALU运算的(例如跳转指令)默认将其操作变为加操作(000).
完成控制信号、指令以及执行状态的关系表以后 ,对于如何实现多周期CPU依旧很困惑,主要不清楚如何将一条分多个时钟周期执行并且能够保持正确,以及如何保证中间过程不会使用到非对应的数据或在寄存器中写入了错误数据。此时,思考实验原理中的图2 多周期CPU状态转移图,总结多周期CPU状态情况,五个执行状态并非所有的指令都有,则我们需要创建一个状态机,具体如实验原理中的图3 多周期CPU控制部件的原理结构图,依据指令的操作码以及当前的CPU状态,在时钟的上升沿触发得到下一个CPU状态,同时控制信号亦依据当前状态进行修改。例如,在指令写回寄存器组阶段--sWB阶段的时候,控制信号RegWre为1,即寄存器组可以写此时,至于其他的执行阶段,则为0,即寄存器组使能端失效,不能写,依据当前CPU的状态设置控制信号可以有效的避免写入错误数据入寄存器组。结合实验原理中的图4 多周期CPU数据通路和控制线路图,首先将其模块化,分成多个模块相连起来,同时与之前设计的单周期CPU不同的是,需要添加一些临时数据的寄存器,其中寄存器ADR和BDR用来保留寄存器组中rs和rt中相对应的内容,寄存器ALUoutDR用来保留ALU的运算结果,寄存器DBDR用来保留写回数据,这些寄存器的主要作用是切分数据通路,将大组合逻辑切分为若干个小组合逻辑,大延迟变成多个分段小延迟。同时,为了使指令代码保持稳定,需要增加指令寄存器IR。
依据图2 多周期CPU数据通路和控制线路图,将CPU划分为14个模块。其中没有具体细分每个功能模块,将一些数据选择的模块并入到需要的功能模块中,没有完全依据多周期CPU数据通路图进行划分,否则需要过多模块,划分的太过冗余。模块划分结果如图三所示。
实现代码:
module pcAdd(
input RST,
input [1:0] PCSrc, //数据选择器输入
input [31:0] immediate, //偏移量
input [25:0] addr,
input [31:0] curPC,
input [31:0] rs,
output reg[31:0] nextPC //新指令地址
);
initial begin
nextPC = 0;
end
reg [31:0] pc;
always@(*)
begin
if(!RST) begin
nextPC = 0;
end
else begin
pc = curPC + 4;
case(PCSrc)
2'b00: nextPC = curPC + 4;
2'b01: nextPC = curPC + 4 + immediate * 4;
2'b10: nextPC = rs;
2'b11: nextPC = {pc[31:28],addr,2'b00};
endcase
end
end
endmodule
实现代码:
module PC(
input CLK, //时钟
input RST, //是否重置地址。0-初始化PC,否则接受新地址
input PCWre, //是否接受新的地址。0-不更改;1-可以更改
input [31:0] nextPC, //新指令地址
output reg[31:0] curPC //当前指令的地址
);
initial begin
curPC <= 0;
end
always@(posedge CLK or negedge RST)
begin
if(!RST) // Reset == 0, PC = 0
begin
curPC <= 0;
end
else
begin
if(PCWre) // PCWre == 1
begin
curPC <= nextPC;
end
else // PCWre == 0, halt
begin
curPC <= curPC;
end
end
end
endmodule
实现代码:
module InsMEM(
input [31:0] IAddr,
input InsMemRW, //状态为'0',写指令寄存器,否则为读指令寄存器
output reg[31:0] IDataOut
);
reg [7:0] rom[128:0]; // 存储器定义必须用reg类型,存储器存储单元8位长度,共128个存储单元,可以存32条指令
// 加载数据到存储器rom。注意:必须使用绝对路径
initial
begin
//绝对路径
$readmemh("F:\\Vivado\\MultiCycleCPU\\romData.txt", rom);
end
//大端模式
always@(IAddr or InsMemRW)
begin
//取指令
if(InsMemRW)
begin
IDataOut[7:0] = rom[IAddr + 3];
IDataOut[15:8] = rom[IAddr + 2];
IDataOut[23:16] = rom[IAddr + 1];
IDataOut[31:24] = rom[IAddr];
end
end
endmodule
实现代码:
module IR(
input [31:0] instruction,
input CLK,
input IRWre,
output reg[31:0] IRInstruction
);
initial begin
IRInstruction = 0;
end
always@(posedge CLK)
begin
if(IRWre) begin
IRInstruction <= instruction;
end
end
endmodule
实现代码:
module InstructionCut(
input [31:0] instruction,
output reg[5:0] op,
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[25:0] addr
);
initial begin
op = 5'b00000;
rs = 5'b00000;
rt = 5'b00000;
rd = 5'b00000;
end
always@(instruction)
begin
op = instruction[31:26];
rs = instruction[25:21];
rt = instruction[20:16];
rd = instruction[15:11];
sa = instruction[10:6];
immediate = instruction[15:0];
addr = instruction[25:0];
end
endmodule
实现代码:
module ControlUnit(
input CLK,
input RST,
input zero, //ALU运算结果是否为0,为0时候为1
input [5:0] op, //指令的操作码
output reg IRWre, //IR的写使能信号
output reg PCWre, //PC是否更改的信号量,为0时候不更改,否则可以更改
output reg ExtSel, //立即数扩展的信号量,为0时候为0扩展,否则为符号扩展
output reg InsMemRW, //指令寄存器的状态操作符,为0的时候写指令寄存器,否则为读指令寄存器
output reg WrRegDSrc, //写入寄存器的数据选择信号
output reg [1:0] RegDst,//写寄存器组寄存器的地址,为0的时候地址来自rt,为1的时候地址来自rd
output reg RegWre, //寄存器组写使能,为1的时候可写
output reg ALUSrcA, //控制ALU数据A的选择端的输入,为0的时候,来自寄存器堆data1输出,为1的时候来自移位数sa
output reg ALUSrcB, //控制ALU数据B的选择端的输入,为0的时候,来自寄存器堆data2输出,为1时候来自扩展过的立即数
output reg [1:0]PCSrc, //获取下一个pc的地址的数据选择器的选择端输入
output reg [2:0]ALUOp, //ALU 8种运算功能选择(000-111)
output reg mRD, //数据存储器读控制信号,为0读
output reg mWR, //数据存储器写控制信号,为0写
output reg DBDataSrc //数据保存的选择端,为0来自ALU运算结果的输出,为1来自数据寄存器(Data MEM)的输出
);
reg [2:0] state, nextState; //记录状态
parameter [2:0] iniState = 3'b111,
sIF = 3'b000,
sID = 3'b001,
sEXE = 3'b010,
sMEM = 3'b100,
sWB = 3'b011;
initial begin
state = iniState;
PCWre = 0;
InsMemRW = 0;
IRWre = 0;
RegWre = 0; ;
ExtSel = 0;
PCSrc = 2'b00;
RegDst = 2'b11;
ALUOp = 0;
ExtSel = 0;
WrRegDSrc = 0;
ALUSrcA = 0;
ALUSrcB = 0;
DBDataSrc = 0;
mRD = 0;
mWR = 0;
end
//状态机
always@(posedge CLK) begin
if(!RST) begin
state <= sIF;
end else begin
state <= nextState;
end
end
always@(state or op or zero) begin
// 状态更新
case(state)
iniState : nextState = sIF;
sIF: nextState = sID;
sID: begin
case(op[5:3])
3'b111: nextState = sIF; //指令j,jal,jr,halt
default: nextState = sEXE;
endcase
end
sEXE: begin
if((op == 6'b110100) || (op == 6'b110110)) begin
//beq,bltz
nextState = sIF;
end else if(op == 6'b110000 || op == 6'b110001) begin
//sw,lw
nextState = sMEM;
end else begin
nextState = sWB;
end
end
sMEM: begin
if(op == 6'b110000) begin
//sw
nextState = sIF;
end else begin
//lw
nextState = sWB;
end
end
sWB: nextState = sIF;
endcase
// 信号量
// PCWre and InsMemRW
if(nextState == sIF && op != 6'b111111 && state != iniState) begin
// halt
PCWre = 1;
InsMemRW = 1;
end else begin
PCWre = 0;
InsMemRW = 0;
end
// IRWre
if(state == sIF || nextState == sID) begin
IRWre = 1;
end else begin
IRWre = 0;
end
// ALUSrcA
if(op == 6'b011000) begin
// sll
ALUSrcA = 1;
end else begin
ALUSrcA = 0;
end
// ALUSrcB
if(op == 6'b000010 || op == 6'b010010 || op == 6'b110000 || op == 6'b110001 || op == 6'b100111) begin
// addi,ori,sw,lw,sltiu
ALUSrcB = 1;
end else begin
ALUSrcB = 0;
end
// DBDataSrc
if(op == 6'b110001) begin
// lw
DBDataSrc = 1;
end else begin
DBDataSrc = 0;
end
// RegWre and WrRegDSrc and RegDst
if((state == sWB && op != 6'b110100 && op != 6'b110000 && op != 6'b110110) || (op == 6'b111010 && state == sID)) begin
// 非beq,sw,bltz
RegWre = 1;
if(op == 6'b111010) begin
// jal
WrRegDSrc = 0;
RegDst = 2'b00;
end else begin
WrRegDSrc = 1;
if(op == 6'b000010 || op == 6'b010010 || op == 6'b100111 || op == 6'b110001) begin
// addi, ori, sltiu, lw
RegDst = 2'b01;
end else begin
// add, sub, or, and, slt, sll
RegDst = 2'b10;
end
end
end else begin
RegWre = 0;
end
// InsMemRW
if(op != 6'b111111)
InsMemRW = 1;
// mRD
mRD = (op == 6'b110001) ? 1 : 0; // lw
// mWR
mWR = (state == sMEM && op == 6'b110000) ? 1 : 0; // sw
// ExtSel
ExtSel = (op == 6'b000010 || op == 6'b110001 || op == 6'b110000 || op == 6'b110100 || op == 6'b110110) ? 1 : 0; // addi、lw、sw、beq、bltz
// PCSrc
if(op == 6'b111001) begin
// jr
PCSrc = 2'b10;
end else if((op == 6'b110100 && zero) || (op == 6'b110110 && !zero)) begin
// beq 和 bltz跳转
PCSrc = 2'b01;
end else if(op == 6'b111010 || op == 6'b111000) begin
// j,jal
PCSrc = 2'b11;
end else begin
PCSrc = 2'b00;
end
// ALUOp
case(op)
6'b000010: ALUOp = 3'b000; // addi
6'b010010: ALUOp = 3'b101; // ori
6'b010000: ALUOp = 3'b101; // or
6'b000001: ALUOp = 3'b001; // sub
6'b010001: ALUOp = 3'b110; // and
6'b011000: ALUOp = 3'b100; // sll
6'b110100: ALUOp = 3'b001; // beq
6'b100110: ALUOp = 3'b011; // slt
6'b100111: ALUOp = 3'b010; // sltiu
6'b110110: ALUOp = 3'b001; // bltz
6'b110001: ALUOp = 3'b000; //sw
6'b110000: ALUOp = 3'b000; //lw
endcase
end
endmodule
实现代码:
module RegisterFile(
input CLK, //时钟
input [4:0] ReadReg1, //rs寄存器地址输入端口
input [4:0] ReadReg2, //rt寄存器地址输入端口
input [4:0] rd, //rd寄存器
input [31:0] WriteData, //写入寄存器的数据输入端口
input [1:0] RegDst, //写寄存器组地址选择信号
input RegWre, //WE,写使能信号,为1时,在时钟边沿触发写入
output reg[31:0] ReadData1, //rs寄存器数据输出端口
output reg[31:0] ReadData2, //rt寄存器数据输出端口
output reg[31:0] WriteReg //写回数据寄存器
);
initial begin
ReadData1 <= 0;
ReadData2 <= 0;
WriteReg <= 0;
end
reg [31:0] regFile[0:31]; // 寄存器定义必须用reg类型
integer i;
initial begin
for (i = 0; i < 32; i = i+ 1) regFile[i] <= 0;
end
always@(ReadReg1 or ReadReg2)
begin
ReadData1 = regFile[ReadReg1];
ReadData2 = regFile[ReadReg2];
//$display("regfile %d %d\n", ReadReg1, ReadReg2);
end
always@(negedge CLK)
begin
//$0恒为0,所以写入寄存器的地址不能为0
if(RegWre && WriteReg)
begin
regFile[WriteReg] <= WriteData;
end
end
always@(*)
begin
case(RegDst)
2'b00: WriteReg = 31;
2'b01: WriteReg = ReadReg2;
2'b10: WriteReg = rd;
endcase;
end
endmodule
实现代码:
module SignZeroExtend(
input wire [15:0] immediate, //立即数
input ExtSel, //状态'0',0扩展,否则符号位扩展
output [31:0] extendImmediate
);
assign extendImmediate[15:0] = immediate;
assign extendImmediate[31:16] = ExtSel ? (immediate[15] ? 16'hffff : 16'h0000) : 16'h0000;
endmodule
实现代码:
module ALU(
input ALUSrcA,
input ALUSrcB,
input [31:0] ReadData1,
input [31:0] ReadData2,
input [4:0] sa,
input [31:0] extend,
input [2:0] ALUOp,
output reg zero,
output reg[31:0] result
);
reg [31:0] A;
reg [31:0] B;
initial begin
result = 0;
zero = 0;
end
always@(ReadData1 or ReadData2 or ALUSrcA or ALUSrcB or ALUOp)
begin
//定义两个输入端口
A = (ALUSrcA == 0) ? ReadData1 : sa;
B = (ALUSrcB == 0) ? ReadData2 : extend;
case(ALUOp)
3'b000: result = A + B;
3'b001: result = A - B;
3'b010: result = (A < B) ? 1 : 0;
3'b011: result = (((ReadData1 < ReadData2) && (ReadData1[31] == ReadData2[31] )) ||( ( ReadData1[31] ==1 && ReadData2[31] == 0))) ? 1:0;
3'b100: result = B << A;
3'b101: result = A | B;
3'b110: result = A & B;
3'b111: result = A ^ B;
endcase
zero = (result == 0) ? 1 : 0;
end
endmodule
实现代码:
module DataMEM(
/*
Daddr,数据存储器地址输入端口
DataIn,数据存储器数据输入端口
DataOut,数据存储器数据输出端口
mRD,数据存储器读控制信号,为0读
mWR,数据存储器写控制信号,为0写
*/
input mRD,
input mWR,
input DBDataSrc,
input [31:0] DAddr,
input [31:0] DataIn,
output reg[31:0] DataOut,
output reg[31:0] DB
);
initial begin
DB <= 16'b0;
end
reg [7:0] ram [0:31]; // 存储器定义必须用reg类型
always@(mRD or DAddr or DBDataSrc)
begin
//读
DataOut[7:0] = mRD ? ram[DAddr + 3] : 8'bz; // z 为高阻态
DataOut[15:8] = mRD ? ram[DAddr + 2] : 8'bz;
DataOut[23:16] = mRD ? ram[DAddr + 1] : 8'bz;
DataOut[31:24] = mRD ? ram[DAddr] : 8'bz;
DB = (DBDataSrc == 0) ? DAddr : DataOut;
end
always@(mWR or DAddr)
begin
//写
if(mWR)
begin
ram[DAddr] = DataIn[31:24];
ram[DAddr + 1] = DataIn[23:16];
ram[DAddr + 2] = DataIn[15:8];
ram[DAddr + 3] = DataIn[7:0];
end
//$display("mwr: %d $12 %d %d %d %d", mWR, ram[12], ram[13], ram[14], ram[15]);
end
endmodule
实现代码:
module TempReg(
input CLK,
input [31:0] IData,
output reg[31:0] OData
);
initial begin
OData = 0;
end
always@(posedge CLK) begin
OData <= IData;
end
endmodule