设计要求

  • 处理器为 32 位单周期处理器,不考虑延迟槽,应支持的指令集为:add, sub, ori, lw, sw, beq, lui, jal, jr, nop,其中:

    • nop 为空指令,机器码 0x00000000,不进行任何有效行为(修改寄存器等)。

      • add, sub 按无符号加减法处理(不考虑溢出)。
  • 需要采用模块化层次化设计。顶层文件为 mips.v,有效的驱动信号要求包括且仅包括同步复位信号 reset 时钟信号 clk,接口定义如下:

    1
    2
    3
    4
    module mips(
    input clk,
    input reset
    );

设计草稿

数据通路模块定义

PC

信号名 方向 功能
NPC[31:0] I 输入下一个 PC 值
clk I 时钟信号
reset I 同步复位信号
PC[31:0] O 输出当前 PC 值

NPC

信号名 方向 功能
PC[31:0] I 输入当前的 PC 值,用于计算 NPC 值
offset[31:0] I 偏移量
nPC_sel[2:0] I NPC 输出选择信号
Branch I 跳转信号
instr_index[25:0] I 机器码 26 位 instr_index
Ra[31:0] I 输入$ra 寄存器的值
NPC[31:0] O 输出 NPC 值
PC_add_4[31:0] O 输出 PC+4 值

nPC_sel 的信号功能:

信号 功能
3’b000 NPC = PC + 4
3’b001 NPC = Branch ? PC + 4 + (offset << 2) : PC + 4
3’b010 NPC = (PC31...28的 32 位无符号扩展数 << 28) + (instr_index 的 32 位无符号拓展数 << 2)
3’b011 NPC = Ra

IM

信号 方向 功能
A[31:0] I 输入指令地址
RD[31:0] O 输出指令

由于指令地址起始值为 0x0000_3000,对 A 减去 0x0000_3000 后,取其[13:2]位值,记为 no,从指令存储器中取第 no 条指令输出给 RD。

指令存储器容量为 4096 × 32bit。

GRF

信号 方向 功能
A1[4:0] I 输入需要读取的寄存器 Reg1 的序号
A2[4:0] I 输入需要读取的寄存器 Reg2 的序号
A3[4:0] I 输入需要写入的寄存器的序号
WD3[31:0] I 写入数据
clk I 时钟信号
WE3 I 写使能信号
reset I 同步复位信号
PC[31:0] I 相应指令的储存地址
RD1[31:0] O 输出从寄存器 Reg1 中读取的内容
RD2[31:0] O 输出从寄存器 Reg2 中读取的内容

0 号寄存器值始终为 0。

DM

信号 方向 功能
A[31:0] I 输入内存地址
WD[31:0] I 输入需要写入的数据
clk I 时钟信号
reset I 同步复位信号
WE I 写使能信号
PC[31:0] I 相应指令的储存地址
RD[31:0] O 输出从内存中读取的数据

取 A[13:2]的值记为 no,表示数据存储器第 no 处位置。

数据存储器容量为 3072 × 32bit。

ALU

信号 方向 功能
SrcA[31:0] I 输入计算数 A
SrcB[31:0] I 输入计算数 B
Shift[4:0] I 输入移位量 Shift
Op[2:0] I 操作数 Op
Result[31:0] O 输出运算结果
Zero O Zero = (A == B);

Op 信号功能:

信号 类型 操作
3’b000 ADD(无符号,不考虑溢出) SrcA + SrcB
3’b001 SUB(无符号,不考虑溢出) SrcA - SrcB
3’b010 OR SrcA | SrcB
3’b011 SHIFT SrcB << Shift

EXT

信号 方向 功能
Imm[15:0] I 输入 16 位立即数
ExtOp I 扩展类型
Ext_Imm[31:0] O 输出扩展立即数

ExtOp 信号功能:

信号 操作
1’b0 无符号拓展
1’b1 符号拓展

控制单元模块

信号 方向 功能
Op[5:0] I 机器码 Op 字段
Funct[5:0] I 机器码 Funct 字段
RegDst[1:0] O 指定要写入的寄存器地址
ALUSrc[1:0] O 指定 ALU 的 SrcB
MemtoReg[1:0] O 指定写入 GRF 的源数据
RegWrite O GRF 写使能信号
MemWrite O DM 写使能信号
nPC_sel[2:0] O 指定 NPC 的值
ExtOp[1:0] O 指定立即数扩展类型
ALUOp[2:0] O 指定 ALU 运算类型
ShiftOp O 指定移位量

RegDst 信号功能:

信号 写入寄存器地址
2’b00 Instr[20:16]
2’b01 Instr[15:11]
2’b10 31

ALUSrc 信号功能:

信号 SrcB
2’b00 GRF 的 RD2
2’b01 Ext_Imm

MemtoReg 信号功能:

信号 写入 GRF 的源数据
2’b00 ALU 的 Result
2’b01 DM 的 RD
2’b10 PC+4

ShiftOp 信号功能:

信号 移位量
1’b0 16

nPC_sel 信号接入 NPC,ExtOp 信号接入 EXT,ALUOp 信号接入 ALU。

指令与信号的对应关系参照 Logisim 的 Control Unit:

测试方案

借助讨论区的.asm 自动生成文件main.exe,生成随机测试代码。然后根据自己编写的脚本将 Mars 和 ISE 的输出结果重定向到相应的文件:

1
2
3
4
5
6
7
8
9
10
11
@echo off
Rem This is a bat to generate outputs of ISE and Mars
main.exe
cd ..
java -jar mars.jar mc CompactDataAtZero a dump .text HexText P4/code.txt P4/std.asm
cd P4
fuse -nodebug -prj mips_stx_beh.prj -o mips.exe mips_tb > nul
mips.exe -nolog -tclbatch mips.tcl > outputs/ise_output.txt
cd ..
java -jar mars.jar P4/std.asm mc CompactLargeText coL1 ig> P4/outputs/mars_output.txt
echo "The program is over"

最后将两个 txt 文本的内容对拍以查找错误。

思考题

1. 阅读下面给出的 DM 的输入示例中(示例 DM 容量为 4KB,即 32bit × 1024 字),根据你的理解回答,这个 addr 信号又是从哪里来的?地址信号 addr 位数为什么是 [11:2] 而不是 [9:0] ?

addr信号来自ALU的输出。

由于输入的 memory address 地址值都是 4 的倍数,所以取addr的[11:2]位。

2. 思考上述两种控制器设计的译码方式,给出代码示例,并尝试对比各方式的优劣。

1.指令对应的控制信号如何取值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
always @(*)begin
case(Op)
`ori:begin
RegDst = 2'b00;
ALUSrc = 1'b1;
MemtoReg = 2'b0;
RegWrite = 1'b1;
MemWrite = 1'b0;
nPC_sel = 3'b0;
ExtOp = 1'b0;
ALUctr = 3'b010;
ShiftOp = 1'b0;
end
endcase
end

2.控制信号每种取值所对应的指令

1
2
3
4
5
6
7
8
9
10
11
12
wire add = Op == 6'b0 && Funct == 6'h20;
wire sub = Op == 6'b0 && Funct == 6'h22;

assign RegDst = (add || sub) ? 2'b01 : 0;
assign ALUSrc = 0;
assign MemtoReg = 0;
assign RegWrite = (add) ? 1 : 0;
assign MemWrite = 0;
assign nPC_sel = 0;
assign ExtOp = 0;
assign ALUctr = sub ? 3'b001 : 0;
assign ShiftOp = 0;

第一种方式便于查错,但代码量大。第二种便于插入新指令、代码量小,但不容易维护。

3. 在相应的部件中,复位信号的设计都是同步复位,这与 P3 中的设计要求不同。请对比同步复位异步复位这两种方式的 reset 信号与 clk 信号优先级的关系。

同步复位,clk信号优先级>reset信号:

1
2
3
4
5
always@(posedge clk)begin
if(reset)begin
//...
end
end

异步复位,clk信号优先级<reset信号:

1
2
3
always@(posedge clk or posedge reset)begin
//...
end

4. C 语言是一种弱类型程序设计语言。C 语言中不对计算结果溢出进行处理,这意味着 C 语言要求程序员必须很清楚计算结果是否会导致溢出。因此,如果仅仅支持 C 语言,MIPS 指令的所有计算指令均可以忽略溢出。 请说明为什么在忽略溢出的前提下,addi 与 addiu 是等价的,add 与 addu 是等价的。提示:阅读《MIPS32® Architecture For Programmers Volume II: The

MIPS32® Instruction Set》中相关指令的 Operation 部分。

addi

addiu

可见若不考虑溢出,addiaddiu最终都是将GPR[rs] + sign_extend(immediate)赋值到GPR[rt]中。addaddu同理。