MIPS单周期CPU-Verilog

MIPS单周期CPU-Verilog
shikiMIPS单周期CPUVerilog设计
设计草稿
模块设计
mips
顶层模块,将各个模块连接,设置clk,reset输入端口。 #### PC 1.
输入端口设置 + [31:0]imm:PC跳转值 + PCset:beq类PC跳转选择 +
PCjump:J指令跳转选择 + clk:时钟信号 + reset:同步复位信号 2.
输出端口设置 + [31:0]PCout:当前PC值 3.
模块内部实现:根据PCset与PCjump选择PC寄存器值的变化行为,reset有效时PC寄存器值设置为0x3000。
4. 部分代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16always @(posedge clk) begin
if (reset==1) begin
pc<=16'h3000;
end
else begin
if (PCset==1) begin
pc<=pc+4+imm;
end
else if (PCjump==1) begin
pc<=imm;
end
else begin
pc<=pc+4;
end
end
end
1
2
3
4
5reg [31:0] Rom[0:12'd4095];
initial begin
$readmemh("code.txt",Rom);
end
assign code = Rom[(pc-16'h3000)>>2];
| :----: | :----: | :----: | :----: | :----: | :----: | :----: | :----:
| :----: | :----: |
| RegDst | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| ALUsrc | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | | MemtoReg | 0 | 0 | 0 |
0 | 1 | 0 | 0 | 0 | 0 |
| RegWrite | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 |
| MemWrite | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| PCset | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| PCjump | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
| EXTop | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 |
| lui | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| PCtoReg | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
| RegtoPC | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| ALUop | 00 | 01 | 10 | 11 | 00 | 00 | 00 | 00 | 00 |
+ 部分代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15assign RegDst = ((opcode==`add&&func==`addfun)||(opcode==`sub&&func==`subfun));
assign ALUsrc = (opcode==`ori||opcode==`lw||opcode==`sw);
assign MemtoReg = (opcode==`lw);
assign RegWrite = ((opcode==`add&&func==`addfun)||(opcode==`sub&&func==`subfun)||opcode==`ori||opcode==`lw||opcode==`jal||opcode==`lui);
assign MemWrite = (opcode==`sw);
assign PCset = (opcode==`beq);
assign PCjump = (opcode==`jal||(opcode==`jr&&func==`jrfun));
assign EXTop = (opcode==`lw||opcode==`sw);
assign lui = (opcode==`lui);
assign PCtoReg = (opcode==`jal);
assign RegtoPC = (opcode==`jr&&func==`jrfun);
assign ALUop = ((opcode==`add&&func==`addfun)||opcode==`lw||opcode==`sw)?0:
((opcode==`sub&&func==`subfun))?1:
(opcode==`ori)?2:
(opcode==`beq)?3:0;
1. 输入端口设置 + [4:0]A1:PD1输出对应寄存器地址 +
[4:0]A2:PD2输出对应寄存器地址 + [31:0]WD:写入寄存器的值 +
[4:0]A3:写入寄存器地址 + [31:0]pc:当前pc值,用于输出测试 +
clk:时钟信号 + reset:同步复位信号 + WE:写使能信号 2. 输出端口设置 +
[31:0]PD1:A1寄存器对应值 + [31:0]PD2:A2寄存器对应值 3.
模块内部实现
设置一32位寄存器数组grf[0:31]作为寄存器堆,reset有效时全部归零,若写使能信号有效则系统调用输出所需测试的信息,若A3!=0则写入到对应位置(0号寄存器恒为0)。
4. 部分代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16always @(posedge clk) begin
if (reset==1) begin
for (i = 0;i<32 ;i=i+1 ) begin
grf[i]<=0;
end
end
else begin
if (WE==1) begin
if (A3!=0) begin
grf[A3]<=WD;
end
$display("@%h: $%d <= %h", pc, A3, WD);
end
end
end
assign PD1
4. 部分代码
1
2assign output1 = (EXTop == 1)?{{16{input1[15]}},input1}:
{{16{1'b0}},input1};
4. 部分代码
1
2
3
4
5assign output1 = (op==0)?(input1+input2):
(op==1)?(input1-input2):
(op==2)?(input1|input2):
(op==3)?(input1==input2):
0;
使用一个8位寄存器数组Ram[0:0x3fff]作为内存,reset有效时所有内存清零,读写控制信号为1时,将addr,addr+4,addr+8,addr+12的寄存器值写为输入值,输出同理。
4. 部分代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16always @(posedge clk) begin
if (reset==1) begin
for (i = 0;i<=14'h3fff ;i=i+1 ) begin
Ram[i]<=0;
end
end
else begin
if (WR==1) begin
Ram[addr] <= W[7:0];
Ram[addr+1] <= W[15:8];
Ram[addr+2] <= W[23:16];
Ram[addr+3] <= W[31:24];
$display("@%h: *%h <= %h", pc, {{18{1'b0}},addr}, W);
end
end
end
根据模块信息及Control模块的输出信息进行端口选择,并将选择后的端口接入至其他模块
### 备注 #### Control各输出端口含义 -
RegDst:有效时选择rd值作为GRF写入地址 -
ALUsrc:有效时选择EXT输出值作为ALU其中一个输入,反之为寄存器输出其中一个输出值
- MemtoReg:有效时选择DM读出值作为寄存器写入值 -
RegWrite:作为GRF写使能信号 - MemWrite:作为DM读写控制信号 -
PCset:PC模块对应输入,有效时,若ALU输出结果为1则将imm符号扩展后左移2为的值作为PC模块输入。
- PCjump:PC模块对应输入,有效时将jump地址作为PC模块输入 -
EXTop:EXT模块op端口输入 -
lui:有效时选择imm加载到高位的值作为寄存器写入数据 -
PCtoReg:有效时将PC值写入31号寄存器 -
RegtoPC:有效时选择相应位置寄存器输出作为jump地址 -
ALUop:作为ALU模块op端口输入