将陆续上传新书《自己动手写CPU》,今天是第48篇。
LLbit寄存器在LLbit模块中实现,模块接口如图9-30所示,各接口描述如表9-8所示。
LLbit寄存器的代码如下,源文件是本书光盘Code\Chapter9_2目录下的LLbit_reg.v文件。
module LLbit_reg(
input wire clk,
input wire rst,
// 异常是否发生,为1表示异常发生,为0表示没有异常
input wire flush,
// 写操作
input wire LLbit_i,
input wire we,
// LLbit寄存器的值
output reg LLbit_o
);
always @ (posedge clk) begin
if (rst == `RstEnable) begin
LLbit_o <= 1'b0;
end else if((flush == 1'b1)) begin //如果异常发生,那么设置LLbit_o为0
LLbit_o <= 1'b0;
end else if((we == `WriteEnable)) begin
LLbit_o <= LLbit_i;
end
end
endmodule当有异常发生时,会使得LLbit寄存器的值为0。所以此处有一个输入接口flush,当flush为1时,表示有异常发生(在第11章实现异常处理的时候会详细介绍),从而设置LLbit寄存器的值为0。
在译码阶段的ID模块要增加对ll、sc指令的译码,根据图9-28给出的ll、sc指令格式可得,确定ll、sc指令的过程如图9-31所示。
其中涉及的宏定义如下,正是ll、sc指令的指令码,在本书附带光盘Code\Chapter9_2目录下的defines.v文件可以找到这些定义。
`define EXE_LL 6'b110000 `define EXE_SC 6'b111000
对译码阶段ID模块的代码做如下修改。完整代码位于本书附带光盘Code\Chapter9_2目录下的id.v文件。
module id(
.......
);
.......
always @ (*) begin
if (rst == `RstEnable) begin
......
end else begin
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
wd_o <= inst_i[15:11]; // 默认目的寄存器地址wd_o
wreg_o <= `WriteDisable;
instvalid <= `InstInvalid;
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= inst_i[25:21]; // 默认的reg1_addr_o
reg2_addr_o <= inst_i[20:16]; // 默认的reg2_addr_o
imm <= `ZeroWord;
......
case (op)
......
EXE_LL: begin // ll指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_LL_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b0;
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
end
......
`EXE_SC: begin // sc指令
wreg_o <= `WriteEnable;
aluop_o <= `EXE_SC_OP;
alusel_o <= `EXE_RES_LOAD_STORE;
reg1_read_o <= 1'b1;
reg2_read_o <= 1'b1;
wd_o <= inst_i[20:16];
instvalid <= `InstValid;
alusel_o <= `EXE_RES_LOAD_STORE;
end
......
endmodule译码工作主要是确定要写的目的寄存器、要读取的寄存器情况、要执行的运算等三个方面。分别介绍如下。
(1)ll指令
(2)sc指令
1、修改MEM模块
参考图9-30可知,访存阶段的MEM模块要新增部分接口,新增接口的描述如表9-9所示。
MEM模块主要修改的代码如下,完整代码请参考本书附带光盘Code\Chapter9_2目录下的mem.v文件。
module mem(
......
// 新增的输入接口
input wire LLbit_i,
input wire wb_LLbit_we_i,
input wire wb_LLbit_value_i,
......
// 新增的输出接口
output reg LLbit_we_o,
output reg LLbit_value_o,
......
);
reg LLbit; // 保存LLbit寄存器的最新值
......
// 获取LLbit寄存器的最新值,如果回写阶段的指令要写LLbit,那么回写阶段要写入的
// 值就是LLbit寄存器的最新值,反之,LLbit模块给出的值LLbit_i是最新值
always @ (*) begin
if(rst == `RstEnable) begin
LLbit <= 1'b0;
end else begin
if(wb_LLbit_we_i == 1'b1) begin
LLbit <= wb_LLbit_value_i; // 回写阶段的指令要写LLbit
end else begin
LLbit <= LLbit_i;
end
end
end
always @ (*) begin
if(rst == `RstEnable) begin
......
LLbit_we_o <= 1'b0;
LLbit_value_o <= 1'b0;
end else begin
......
LLbit_we_o <= 1'b0;
LLbit_value_o <= 1'b0;、
mem_ce_o <= `ChipDisable;
mem_we <= `WriteDisable;
case (aluop_i)
......
`EXE_LL_OP: begin // ll指令的访存输出
mem_addr_o <= mem_addr_i;
mem_we <= `WriteDisable;
wdata_o <= mem_data_i;
LLbit_we_o <= 1'b1;
LLbit_value_o <= 1'b1;
mem_sel_o <= 4'b1111;
mem_ce_o <= `ChipEnable;
end
......
`EXE_SC_OP: begin // sc指令的访存输出
if(LLbit == 1'b1) begin
LLbit_we_o <= 1'b1;
LLbit_value_o <= 1'b0;
mem_addr_o <= mem_addr_i;
mem_we <= `WriteEnable;
mem_data_o <= reg2_i;
wdata_o <= 32'b1;
mem_sel_o <= 4'b1111;
mem_ce_o <= `ChipEnable;
end else begin
wdata_o <= 32'b0;
end
end
......
endmodule
MEM模块的代码增加了一个过程,以获得LLbit寄存器的最新值,然后针对ll、sc指令分别给出了对数据存储器的访问信息。
(1)ll指令
(2)sc指令
如果LLbit寄存器的值为1,表示之前已执行过ll指令,并且在ll指令执行后、当前sc指令执行前的这段时间内,没有异常发生,此时,sc指令的访存信息如下。
2、修改MEM/WB模块
从图9-30可知,MEM/WB模块要新增部分接口,新增接口的描述如表9-10所示。
MEM/WB模块要修改的代码如下,作用很直白:在访存阶段没有暂停时,简单地将MEM给出的对LLbit寄存器的写信息传递到访存阶段,完整代码请读者参考本书附带光盘Code\Chapter9_2目录下的mem_wb.v文件。
module mem_wb(
......
input wire mem_LLbit_we,
input wire mem_LLbit_value,
......
output reg wb_LLbit_we,
output reg wb_LLbit_value
);
always @ (posedge clk) begin
if(rst == `RstEnable) begin
......
wb_LLbit_we <= 1'b0;
wb_LLbit_value <= 1'b0;
end else if(stall[4] == `Stop && stall[5] == `NoStop) begin
......
wb_LLbit_we <= 1'b0;
wb_LLbit_value <= 1'b0;
end else if(stall[4] == `NoStop) begin // 判断访存阶段是否暂停
......
wb_LLbit_we <= mem_LLbit_we;
wb_LLbit_value <= mem_LLbit_value;
end
end
endmodule因为一些模块增加了接口,所以要修改顶层模块OpenMIPS,以将这些新增加的接口按照图9-30所示的关系连接起来。具体修改代码不再给出,读者可以参考本书附带光盘Code\Chapter9_2目录下的openmips.v文件。
注意一点,因为目前还没有实现异常处理,所以可以直接设置LLbit模块的输入接口flush为0,表示没有异常发生,当后续章节实现异常处理后,再将其连接到正确的模块。
下一篇将验证OpenMIPS的ll、sc指令是否实现正确。
自己动手写CPU之第九阶段(9)——修改OpenMIPS以实现ll、sc指令
原文:http://blog.csdn.net/leishangwen/article/details/41951375