P6 Design documents

P6 Design documents

Charles Lv7

Verilog流水线CPU设计文档

一、 CPU设计方案综述

(一) 总体设计概述

使用Verilog开发一个简单的流水线CPU,总体概述如下:

  1. 此CPU为32位CPU

  2. 此CPU为流水线设计

  3. 此CPU支持的指令集为:

    {add, sub, ori, lw, sw, beq, lui, nop,jal,j,jr}

  4. add, sub不支持溢出

(二) 关键模块定义

img

宏的定义

Folding coding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
`timescale 1ns / 1ps
//alu
`define _ADD 12'b000000000001
`define _SUB 12'b000000000010
`define _AND 12'b000000000100
`define _OR 12'b000000001000
`define _XOR 12'b000000010000
`define _NOR 12'b000000100000
`define _SLL 12'b000001000000
`define _SRA 12'b000010000000
`define _SRL 12'b000100000000
`define _SLT 12'b001000000000
`define _SLTU 12'b010000000000
//ifu
`define PC_Initial 32'h0000_3000
//ext
`define Zero_Ext 3'b001
`define Sign_Ext 3'b010
`define Lui_Ext 3'b100
//NPC
`define PC4_NPC 4'b0001
`define B_transfer_NPC 4'b0010
`define J_transfer_NPC 4'b0100
`define Jr_NPC 4'b1000
//Controller_for_Reg
`define ALUop_Initial 7'b0000001
`define AluSrc1_Initial 4'b0001
`define AluSrc2_Initial 4'b0001
`define WhichtoReg_Initial 8'b00000001
`define RegDst_Initial 4'b0001
`define DM_type_Initial 6'b000001
//DM
`define Word_DM 6'b000001
`define Half_DM 6'b000010
`define Byte_DM 6'b000100
`define Unsigned_Half_DM 6'b001000
`define Unsigned_Byte_DM 6'b010000
//B_transfer
`define nop_B_trans 4'b0000
`define beq_B_trans 4'b0001
`define bgez_B_trans 4'b0010
`define bgtz_B_trans 4'b0011
`define blez_B_trans 4'b0100
`define bltz_B_trans 4'b0101
`define bne_B_trans 4'b0110
//MD_Unit
`define nop_MDU 4'b0000
`define mult_MDU 4'b0001
`define multu_MDU 4'b0010
`define div_MDU 4'b0011
`define divu_MDU 4'b0100
`define mfhi_MDU 4'b0101
`define mflo_MDU 4'b0110
`define mthi_MDU 4'b0111
`define mtlo_MDU 4'b1000

F级流水线

1. PC

(1) 端口说明

表1-IFU端口说明
序号 信号名 方向 描述
1 clk I 时钟信号
2 reset I 同步复位信号,将PC值置为0x0000_3000:
0:无效
1:复位
3 en I 使能信号,决定是否阻塞
4 NPC[31:0] I 下一周期PC的地址
6 PC[31:0] O 当前执行的PC

(2) 功能定义

表2-IFU功能定义
序号 功能 描述
1 复位 reset有效时,PC置为0x00003000
2 更新PC的值 将PC赋值为NPC

D级流水线

1. GRF

(1) 端口说明

表3-GRF端口说明
序号 信号名 方向 描述
1 clk I 时钟信号
2 reset I 同步复位信号,将32个寄存器中全部清零
1:清零
0:无效
3 WE I 写使能信号
1:可向GRF中写入数据
0:不能向GRF中写入数据
4 A1[4:0] I 5位地址输入信号,指定32个寄存器中的一个,将其中存储的数据读出到RD1
5 A2[4:0] I 5位地址输入信号,指定32个寄存器中的一个,将其中存储的数据读出到RD2
6 A3[4:0] I 5位地址输入信号,指定32个寄存器中的一个,作为RD的写入地址
7 WD[31:0] I 32位写入数据
8 WPC[31:0] I 当前写入GRF的PC
9 RD1[31:0] O 输出A1指定的寄存器的32位数据
10 RD2[31:0] O 输出A2指定的寄存器的32位数据

(2) 功能定义

表4-GRF功能定义
序号 功能 描述
1 同步复位 reset为1时,将所有寄存器清零
2 读数据 将A1和A2地址对应的寄存器的值分别通过RD1和RD2读出
3 写数据 当WE为1且时钟上升沿来临时,将WD写入到A3对应的寄存器内部
4 内部转发 当A1和A2之一与A3相等且写入信号为1时,用WD代替RD1或RD2的输出

2. EXT

(1) 端口说明

表9-EXT端口说明
序号 信号名 方向 描述
1 imm16[15:0] I 代扩展的16位信号
2 sign[2:0] I 无符号或符号扩展选择信号
3’b001:无符号扩展
3’b010:符号扩展
3’b100: 寄存到高位
3 imm32[31:0] O 扩展后的32位的信号

(2) 功能定义

表10-EXT功能定义
序号 功能 描述
1 无符号扩展 当sign为3’b001时,将imm16无符号扩展输出
2 符号扩展 当sign为3‘b010时,将imm16符号扩展输出
3 存储到高位 当sign为3’100时,将imm16存在高16位

3. Controller

(1) 端口说明

表11-Controller端口说明
序号 信号名 方向 描述
1 op[5:0] I instr[31:26]6位控制信号
2 func[5:0] I instr[5:0]6位控制信号
3 rt[4:0] I instr[20:16]5位控制信号
4 AluOp[11:0] O ALU的控制信号
5 WeGrf O GRF写使能信号
0:禁止写入
1:允许写入
6 WeDm O DM的写入信号
0:禁止写入
1:允许写入
7 branch[3:0] O PC转移位置选择信号
4’b0001:其他情况
4’b0010:beq
4’b0100:j||jal
4’b1000:jr;
8 AluSrc1[3:0] O 参与ALU运算的第一个数
4’b0001:RD1
4’b0010:RD2
9 AluSrc2[3:0] O 参与ALU运算的第二个数,来自GRF还是imm
4’b0001:RD2
4’b0010:imm32
4’b0100: offset
10 WhichtoReg[7:0] O 将何种数据写入GRF?
8’b0000_0001:ALU计算结果
8’b0000_0010:DM读出信号
8’b0000_0100:upperImm
8’b0000_1000: PC+4
11 RegDst[3:0] O 写入GRF的哪个寄存器?
4’b0001:rd
4’b0010:rt
4’b0100:31号寄存器
12 SignExt[2:0] O 拓展方式:
3’b001:0拓展
3’b010:符号拓展
3’b100:存储到高位
13 B_change[3:0] O B转移的类型
4’b0001:beq
4’b0010:slt
4’b0100:blez
14 DM_type[5:0] O 存取指令类型:
6’b000001:lw/sw
6’b000010:lh/sh
6’b000100:lb/sb
6’b001000:lhu
6’b010000:lbu
15 MDop[3:0] O 乘除指令选择信号:
4‘b0000:无操作
4’b0001:mult
4’b0010:multu
4’b0011:div
4’b0100:divu
4’b0101:mfhi
4’b0110:mflo
4’b0111:mthi
4’b1000:mtlo
16 D_Tuse_rs[1:0] O 指令的rs寄存器的值从第一次进入D级到被使用的周期数
17 D_Tuse_rt[1:0] O 指令的rt寄存器的值从第一次进入D级到被使用的周期数
18 D_Tnew[1:0] O 指令从进入D开始到产生运算结果需要的周期数

4.NPC

NPC(下一指令计算单元)

该模块根据当前pc(包括D级和F级)和其他控制信号(NPCOp,CMP输出信息),计算出下一指令所在的地址npc,传入IFU模块。

  • 端口定义
信号名 方向 位宽 描述
F_pc I 32 F级指令地址
D_pc I 32 D级指令地址
offset I 32 地址偏移量,用于计算B类指令所要跳转的地址
imm26 I 26 当前指令数据的前26位(0~25),用于计算jal和j指令所要跳转的地址
ra I 32 储存在寄存器($ra或是jalr指令中存储“PC+4”的寄存器)中的地址数据,用于实现jr和jalr指令
ALU_change I 1 B类指令判断结果 1:说明当前B类指令的判断结果为真 0:说明判断结果为假
branch I 4 NPC功能选择 0x000:顺序执行 0x001:B类指令跳转 0x010: jal/j跳转 0x011: jr/jalr跳转
npc O 32 输出下一指令地址
PC8 O 32 jr指令时存储PC+8的值

5.B_transfer(B类指令比较单元)

该单元根据输入的CMPOp信号对当前B指令的类型进行判断,进而对当前输入的数值进行相应比较,最后输出结果。

  • 端口定义
信号名 方向 位宽 描述
A I 32 输入B_transfer单元的第一个数据
B I 32 输入B_transfer单元的第二个数据
Type I 4 Type功能选择信号
0x0001:beq判断
0x0010:slt判断
0x0100:blez判断
ALU_change O 1 判断结果输出
1: 判断结果为真
0:判断结果为假

E级流水线

1. ALU

(1) 端口说明

表5-ALU端口说明
序号 信号名 方向 描述
1 A[31:0] I 参与运算的第一个数
2 B[31:0] I 参与运算的第二个数
3 ALUop[11:0] I 决定ALU做何种操作
12’b0000_0000_0001:无符号加
12’b0000_0000_0010:无符号减
12’b0000_0000_0100:与
12’b0000_0000_1000:或
12‘b0000_0001_0000:异或
12‘b0000_0010_0000:或非
12‘b0000_0100_0000:sll
12‘b0000_1000_0000:sra
12‘b0001_0000_0000:srl
12‘b0010_0000_0000:slt
12‘b0100_0000_0000:sltu
4 res O A与B做运算后的结果

(2) 功能定义

表6-ALU功能定义
序号 功能 描述
1 加运算 res = A + B
2 减运算 res = A - B
3 与运算 res = A & B
4 或运算 res = A | B
5 左移位运算 Res=A<<B

2.MD_Unit

  • 端口定义
方向 信号名 位宽 描述 输入来源
I clk 1 时钟信号 mips.v中的clk
I reset 1 同步复位信号 mips.v中的reset
I en 1 MD_Unit使能信号 判断是否为乘除计算
I MDop 4 指令选择信号 Controller
I A 32 第一个计算数 e_A
I B 32 第二个计算数 e_B
O HI 32 HI寄存器输出
O LO 32 LO寄存器输出
O out 32 取出HI或LO的值
O busy 1 是否在进行乘除计算
Folding coding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
`timescale 1ns / 1ps
`include "macro.v"
module MD_Unit(input clk,
input reset,
input en,
input [3:0] MDop,
input [31:0] A,
input [31:0] B,
output reg [31:0] HI,
output reg [31:0] LO,
output [31:0] out,
output busy);
reg [31:0] cnt = 0;
parameter MU_cycle = 5,D_cycle = 10;
always @(posedge clk)begin
if (reset)begin
HI <= 0;
LO <= 0;
cnt <= 0;
end
else if (busy)begin
cnt <= cnt-1;
end
else begin
case(MDop)
`nop_MDU:begin

end
`mult_MDU:begin
if (en)begin
{HI,LO} <= $signed(A)*$signed(B);
cnt <= MU_cycle;
end
end
`multu_MDU:begin
if (en)begin
{HI,LO} <= A*B;
cnt <= MU_cycle;
end
end
`div_MDU:begin
if (en)begin
HI <= $signed(A)%$signed(B);
LO <= $signed(A)/$signed(B);
cnt <= D_cycle;
end
end
`divu_MDU:begin
if (en)begin
HI <= A%B;
LO <= A/B;
cnt <= D_cycle;
end
end
`mfhi_MDU:begin
//out <= HI;
end
`mflo_MDU:begin
//out <= LO;
end
`mthi_MDU:begin
HI <= A;
end
`mtlo_MDU:begin
LO <= A;
end
endcase
end
end
assign busy = (cnt != 0);
assign out= (MDop==`mflo_MDU)?LO:
(MDop==`mfhi_MDU)?HI:
32'b0;
endmodule

M级流水线

1. DM_In

  • 端口定义
方向 信号名 位宽 描述 输入来源
I low2 2 时钟信号 Address低两位
I WD 31 处理前写入结果 32位写入数据
I DM_type 6 决定存取指令类型 存取指令类型:
6’b000001:lw/sw
6’b000010:lh/sh
6’b000100:lb/sb
6’b001000:lhu
6’b010000:lbu
O data_byteen 4 字节使能信号
O DM_input 32 处理后的写入结果
Folding coding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
`timescale 1ns / 1ps
`include "macro.v"
module DM_In(input [1:0] low2,
input [31:0] WD,
input [5:0] DM_type,

output [3:0] data_byteen,
output [31:0] DM_input
);
wire [7:0] new_b;
wire [15:0] new_h;
//initial
assign new_b=WD[7:0];
// assign new_b = (low2 == 2'b00)?WD[7:0]:
// (low2 == 2'b01)?WD[15:8]:
// (low2 == 2'b10)?WD[23:16]:
// WD[31:24];
// assign new_h = (low2[1] == 1'b1)? WD[31:16]:
// WD[15:0];
assign new_h=WD[15:0];
//lb,sb
assign data_byteen = (DM_type == `Word_DM)?4'b1111:
(DM_type == `Half_DM||DM_type == `Unsigned_Half_DM)?( (low2[1] == 1'b1)?4'b1100:4'b0011):
(DM_type == `Byte_DM||DM_type == `Unsigned_Byte_DM)?( (low2 == 2'b00)?4'b0001:
(low2 == 2'b01)?4'b0010:
(low2 == 2'b10)?4'b0100:
4'b1000):
4'b0000;
//lh,sh
assign DM_input = (DM_type == `Word_DM)?WD:
(DM_type == `Half_DM||DM_type == `Unsigned_Half_DM)?((low2[1] == 1'b1)?{new_h,{16{1'b0}}}:{{16{1'b0}},new_h}):
(DM_type == `Byte_DM||DM_type == `Unsigned_Byte_DM)?((low2 == 2'b00)?{{24{1'b0}},new_b}:
(low2 == 2'b01)?{{16{1'b0}},new_b,{8{1'b0}}}:
(low2 == 2'b10)?{{8{1'b0}},new_b,{16{1'b0}}}:
{new_b,{24{1'b0}}}):
32'd0;
// assign DM_input = WD;

endmodule

2.DM_Out

  • 端口定义
方向 信号名 位宽 描述 输入来源
I low2 2 时钟信号 Address低两位
I DM_output 32 处理前读入信号 32位读入数据
I DM_type 6 决定存取指令类型 存取指令类型:
6’b000001:lw/sw
6’b000010:lh/sh
6’b000100:lb/sb
6’b001000:lhu
6’b010000:lbu
O RD 32 处理后的读入结果
Folding coding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
`timescale 1ns / 1ps
`include "macro.v"
module DM_Out(input [1:0] low2,
input [5:0] DM_type,
input [31:0] DM_output,

output [31:0] RD
);
wire [7:0] DM_output_3,DM_output_2,DM_output_1,DM_output_0;
assign DM_output_0 = DM_output[7:0];
assign DM_output_1 = DM_output[15:8];
assign DM_output_2 = DM_output[23:16];
assign DM_output_3 = DM_output[31:24];
wire [31:0] out_h,out_b,out_hu,out_bu;
//sb,lb
assign out_bu = (low2 == 2'b00)?{{24{1'b0}},DM_output_0}:
(low2 == 2'b01)?{{16{1'b0}},DM_output_1,{8{1'b0}}}:
(low2 == 2'b10)?{{8{1'b0}},DM_output_2,{16{1'b0}}}:
{DM_output_3,{24{1'b0}}};
assign out_b = (low2 == 2'b00)?{{24{DM_output_0[7]}},DM_output_0}:
(low2 == 2'b01)?{{16{DM_output_1[7]}},DM_output_1,{8{1'b0}}}:
(low2 == 2'b10)?{{8{DM_output_2[7]}},DM_output_2,{16{1'b0}}}:
{DM_output_3,{24{1'b0}}};
//sh,lh
wire [15:0] low_h;
assign low_h = (low2 == 2'b00)? {DM_output_1,DM_output_0}:
{DM_output_3,DM_output_2};
assign out_hu =(low2[1] == 1'b1)?{low_h,{16{1'b0}}}:
{{16{1'b0}},low_h};
assign out_h = (low2[1] == 1'b1)? {low_h,{16{1'b0}}}:
{{16{low_h[15]}},low_h};
//output
assign RD = (DM_type == `Word_DM)?DM_output:
(DM_type == `Half_DM)?(out_h):
(DM_type == `Byte_DM)?out_b:
(DM_type == `Unsigned_Half_DM)?out_hu:
(DM_type == `Unsigned_Byte_DM)?out_bu:
32'b0;
endmodule

各级流水线寄存器

1.IF_ID

D_Reg(IF/ID流水寄存器)

  • 端口定义
方向 信号名 位宽 描述 输入来源
I clk 1 时钟信号 mips.v中的clk
I reset 1 同步复位信号 mips.v中的reset
I en 1 D级寄存器使能信号 stall信号取反
I F_instr 32 F级instr输入 IFU_instr
I F_pc 32 F级pc输入 IFU_pc
O D_instr 32 D级instr输出
O D_pc 32 D级pc输出

2.ID_EX

E_Reg(ID/EX流水寄存器)

  • 端口定义
方向 信号名 位宽 描述 输入来源
I clk 1 时钟信号 mips.v中的clk
I reset 1 同步复位信号 mips.v中的reset
I clr 1 E级寄存器清空信号 HazardUnit中stall信号
I D_RD1 32 D级GRF输出RD1 通过B_transfer_D1转发的数据
I D_RD2 32 D级GRF输出RD2 通过B_transfer_D2转发的数据
I D_instr_s 5 D级instr的shamt D_instr的s域数据
I D_A1 5 D级A1输入 D_instr的rs域数据
I D_A2 5 D级A2输入 D_instr的rt域数据
I D_A3 5 D级A3输入 通过MUX_A3选择出的数据
I D_imm32 32 D级imm32输入 通过EXT模块扩展出的数据
I D_PC 32 D级PC输入 前一级相同信号
I D_PC8 32 D级PC8输入 前一级相同信号
I Tnew_D 2 D级指令的Tnew输入 前一级相同信号
I D_Wegrf 1 D级控制信号输入 前一级相同信号
I D_WeDm 1 D级控制信号输入 前一级相同信号
I D_ALUop 7 D级控制信号输入 前一级相同信号
I D_AluSrc1 4 D级控制信号输入 前一级相同信号
I D_AluSrc2 4 D级控制信号输入 前一级相同信号
I D_WhichtoReg 8 D级控制信号输入 前一级相同信号
I D_RegDst 4 D级控制信号输入 前一级相同信号
I D_DM_type 6 D级控制信号输入 前一级相同信号
I D_ALU_change 1 D级ALU_change输入 前一级相同信号
I D_MDop 4 D级MDop输入 前一级相同信号
O E_RD1 32 E级RD1输出
O E_RD2 32 E级RD2输出
O E_instr_s 5 移位指令的位移数
O E_A1 5 E级A1输出
O E_A2 5 E级A2输出
O E_A3 5 E级A3输出
O E_imm32 32 E级imm32输出
O E_PC 32 E级PC输出
O E_PC8 32 E级PC8输出
O E_Tnew 2 E级指令的Tnew输出
O E_Wegrf 1 E级控制信号输出
O E_WeDm 1 E级控制信号输出
O E_ALUop 7 E级控制信号输出
O E_AluSrc1 4 E级控制信号输出
O E_AluSrc2 1 E级控制信号输出
O E_WhichtoReg 1 E级控制信号输出
O E_RegDst 3 E级控制信号输出
O E_DM_type 6 E级控制信号输出
O E_ALU_change 1 E级ALU_change输出
O E_MDop 4 E级MDop输出
  • 运算功能

    Tnew_E = (Tnew_D > 0) ? Tnew_D - 1: 0Tnew_E=(Tnew_D>0)?Tnew_D−1:0

3.EX_MEM

M_Reg(EX/MEM流水寄存器)

  • 端口定义
方向 信号名 位宽 描述 输入来源
I clk 1 时钟信号 mips.v中的clk
I reset 1 同步复位信号 mips.v中的reset
I E_A2 5 E级A2输入 ALU_out数据
I E_A3 5 E级A3输入(转发值) MUX_ALU选择出来的数据
I E_RD2 32 E级RD2输入 前一级相同信号
I E_ALUout 32 E级res输入 前一级相同信号
I E_PC 32 E级PC输入 前一级相同信号
I E_PC8 32 E级PC8输入 前一级相同信号
I E_Tnew 2 E级Tnew输入 前一级相同信号
I E_Wegrf 1 E级控制信号输入 前一级相同信号
I E_WeDm 1 E级控制信号输入 前一级相同信号
I E_WhichtoReg 8 E级控制信号输入 前一级相同信号
I E_RegDst 4 E级控制信号输入 前一级相同信号
I E_DM_type 6 E级控制信号输入 前一级相同信号
I E_ALU_change 1 E级ALU_change输入 前一级相同信号
I E_imm32 32 E级imm32输入 前一级相同信号
O M_A2 5 M级A2输出
O M_A3 5 M级A3输出
O M_RD2 32 M级RD2输出
O M_ALUout 32 M级res输出
O M_PC 32 M级PC输出
O M_PC8 32 M级PC8输出
O M_Tnew 2 M级Tnew输出
O M_Wegrf 1 M级Tnew输出
O M_WeDm 1 M级控制信号输出
O M_WhichtoReg 8 M级控制信号输出
O M_RegDst 4 M级控制信号输出
O M_DM_type 6 M级控制信号输出
O M_ALU_change 1 M级ALU_change输出
O M_imm32 32 M级imm32输出
  • 运算功能

    Tnew_M = (Tnew_E > 0) ? Tnew_E - 1: 0Tnew_M=(Tnew_E>0)?Tnew_E−1:0

4.MEM_WB

W_Reg(MEM/WB流水寄存器)

  • 接口定义
方向 信号名 位宽 描述 输入来源
I clk 1 时钟信号 mips.v中的clk
I reset 1 同步复位信号 mips.v中的reset
I M_A3 5 M级A3输入 前一级相同信号
I M_RD 32 M级RD输入 前一级相同信号
I M_PC 32 M级PC输入 前一级相同信号
I M_PC8 32 M级PC8输入 前一级相同信号
I M_Wegrf 1 M级控制信号输入 前一级相同信号
I M_WhichtoReg 1 M级控制信号输入 前一级相同信号
I M_RegDst 4 M级控制信号输入 前一级相同信号
I M_imm32 32 M级imm32输入 前一级相同信号
I M_ALU_change 1 M级ALU_change输入 前一级相同信号
I M_Tnew 2 M级Tnew输入 前一级相同信号
O W_A3 5 W级A3输出
O W_ALUout 32 W级res输出
O W_RD 32 W级RD输出
O W_PC 32 W级PC输出
O W_PC8 32 W级PC8输出
O W_Wegrf 1 W级控制信号输出
O W_WhichtoReg 8 W级控制信号输出
O W_RegDst 4 W级控制信号输出
O W_imm32 32 W级imm32输出
O W_ALU_change 1 W级ALU_change输出
O W_Tnew 2 W级Tnew输出

暂停、转发处理及相关多路选择器

(一).冲突综合单元(HazardUnit)

方向 信号名 位宽 描述
I D_A1 5 D级A1端输入
I D_A2 5 D级A2端输入
I E_A1 5 E级A1端输入
I E_A2 5 E级A2端输入
I M_A2 5 M级A2端输入
I E_A3 5 E级A3端输入
I M_A3 5 M级A3端输入
I W_A3 5 W级A3端输入
I D_Tuse_rs 2 D_Tuse_rs输入
I D_Tuse_rt 2 D_Tuse_rt输入
I E_Tnew 2 E级Tnew输入
I M_Tnew 2 M级Tnew输入
I W_Tnew 2 W级Tnew输入
I E_Wegrf 1 E级Wegrf输入
I M_Wegrf 1 M级Wegrf输入
I W_Wegrf 1 W级Wegrf输入
I E_WhichtoReg 8 E级WhichtoReg输入
I M_WhichtoReg 8 M级WhichtoReg输入
I start 1 乘除开始信号
I busy 1 乘除忙碌信号
I MDU_en 1 D级将进行乘除运算信号
O SelB_D1 2 B_transfer的D1输入转发信号
O SelB_D2 2 B_transfer的D2输入转发信号
O SelALU_A 2 ALU输入A转发信号
O SelALU_B 2 ALU输入B转发信号
O SelDM 1 DM写入WD转发信号
O stall 1 冲突信号
Folding coding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
`timescale 1ns / 1ps
`include "macro.v"
module HazardUnit(input [4:0] D_A1,
input [4:0] D_A2,
input [4:0] E_A1,
input [4:0] E_A2,
input [4:0] M_A2,
input [4:0] E_A3,
input [4:0] M_A3,
input [4:0] W_A3,

input [1:0] D_Tuse_rs,
input [1:0] D_Tuse_rt,
input [1:0] E_Tnew,
input [1:0] M_Tnew,
input [1:0] W_Tnew,
input E_Wegrf,
input M_Wegrf,
input W_Wegrf,
input [7:0] E_WhichtoReg,
input [7:0] M_WhichtoReg,
input start,
input busy,
input MDU_en,

output [2:0] SelB_D1,
output [2:0] SelB_D2,
output [1:0] SelALU_A,
output [1:0] SelALU_B,
output SelDM,
output stall
);


//stall
wire stall_rs,stall_rt,stall_md;
//stop
assign stall_rs = (D_A1!= 0)&&((D_Tuse_rs<E_Tnew)&&(D_A1 == E_A3)&&E_Wegrf||
(D_Tuse_rs<M_Tnew)&&(D_A1 == M_A3)&&M_Wegrf||
(D_Tuse_rs<W_Tnew)&&(D_A1 == W_A3)&&W_Wegrf);


assign stall_rt = (D_A2!= 0)&&((D_Tuse_rt<E_Tnew)&&(D_A2 == E_A3)&&E_Wegrf||
(D_Tuse_rt<M_Tnew)&&(D_A2 == M_A3)&&M_Wegrf||
(D_Tuse_rt<W_Tnew)&&(D_A2 == W_A3)&&W_Wegrf);

assign stall_md=(start||busy)&&(MDU_en);

//output

assign SelB_D1 =(D_A1 == E_A3)&&(E_Tnew == 0)&&D_A1&&E_Wegrf&&(E_WhichtoReg==8'b0000_1000)?3'b100:
(D_A1 == M_A3)&&(M_Tnew == 0)&&D_A1&&M_Wegrf&&(M_WhichtoReg==8'b0000_1000)?3'b011:
(D_A1 == M_A3)&&(M_Tnew == 0)&&D_A1&&M_Wegrf?3'b010:
(D_A1 == W_A3)&&(W_Tnew == 0)&&D_A1&&W_Wegrf?3'b001:
3'b000;



assign SelB_D2 =(D_A2 == E_A3)&&(E_Tnew == 0)&&D_A2&&E_Wegrf&&(E_WhichtoReg==8'b0000_1000)?3'b100:
(D_A2 == M_A3)&&(M_Tnew == 0)&&D_A2&&M_Wegrf&&(M_WhichtoReg==8'b0000_1000)?3'b011:
(D_A2 == M_A3)&&(M_Tnew == 0)&&D_A2&&M_Wegrf?3'b010:
(D_A2 == W_A3)&&(W_Tnew == 0)&&D_A2&&W_Wegrf?3'b001:
3'b000;



assign SelALU_A = (E_A1 == M_A3)&&(M_Tnew == 0)&&E_A1&&M_Wegrf&&(M_WhichtoReg==8'b0000_1000)?2'b11:
(E_A1 == M_A3)&&(M_Tnew == 0)&&E_A1&&M_Wegrf?2'b10:
(E_A1 == W_A3)&&(W_Tnew == 0)&&E_A1&&W_Wegrf?2'b01:
2'b00;
//assign SelALU_A = 2'b00;


assign SelALU_B = (E_A2 == M_A3)&&(M_Tnew == 0)&&E_A2&&M_Wegrf&&(M_WhichtoReg==8'b0000_1000)?2'b11:
(E_A2 == M_A3)&&(M_Tnew == 0)&&E_A2&&M_Wegrf?2'b10:
(E_A2 == W_A3)&&(W_Tnew == 0)&&E_A2&&W_Wegrf?2'b01:
2'b00;



assign SelDM = (M_A3 == W_A3)&&M_A3&&(W_Tnew == 0)&&W_Wegrf;
//assign SelDM = 1'b0;


assign stall = stall_rs||stall_rt||stall_md;

endmodule

(二).控制和冒险简述

  • 对于控制冒险,本实验要求大家实现比较过程前移至 D 级,并采用延迟槽

  • 对于数据冒险,两大策略及其应用:

    1
    假设当前我需要的数据,其实已经计算出来,只是还没有进入寄存器堆,那么我们可以用**转发**( Forwarding )来解决,即不引用寄存器堆的值,而是直接从后面的流水级的供给者把计算结果发送到前面流水级的需求者来引用。如果我们需要的数据还没有算出来。则我们就只能**暂停**( Stall ),让流水线停止工作,等到我们需要的数据计算完毕,再开始下面的工作。

(三).冒险处理

冒险处理我们均通过“A_T”法实现——

转发(forward)

当前面的指令要写寄存器但还未写入,而后面的指令需要用到没有被写入的值时,这时候会产生数据冒险,我们首先考虑进行转发。我们假设所有的数据冒险均可通过转发解决。也就是说,当某一指令前进到必须使用某一寄存器的值的流水阶段时,这个寄存器的值一定已经产生,并存储于后续某个流水线寄存器中

在这一阶段,我们不管需要的值有没由计算出,都要进行转发,即暴力转发。为实现这一机制,我们要清楚哪些模块需要转发后的数据(需求者)和保存着写入值的流水寄存器(供应者

  • 供应者及其产生的数据
流水级 产生数据 MUX名&选择信号名 MUX输出名
E E_imm32,
E_PC8
直接流水线传递 直接流水线传递
M M_ALUout,
M_PC8
直接流水线传递 直接流水线传递
W w_res,
w_RD,
w_imm32,
W_PC8
w_WhichtoReg WD

注:当M级指令为读hi和lo的指令时, M_AO中的结果是从上一周期在乘除槽中读取的hi或lo的值;如果是其他指令,M_AO是上一周期ALU的计算结果。

  • 需求者及其产生的数据
接收端口 选择数据 HMUX名&选择信号名 MUX输出名
B_transfer_D1 D_V1,
M_out,
E_out
SelB_D1 d_b_transfer1
B_transfer_D2 d_RD2,
m_res,
e_res
SelB_D2 d_b_transfer2
ALU_A e_RD1,
WD,
m_res
SelALU_A e_A
ALU_B e_RD2,
WD,
m_res
SelALU_B e_B
DM_WD m_RD2,
WD
SelDM M_WD_f
NPC_ra D_V1_f ,
E_PC8 ,
M_PC8
SelJr ra

从上表可以看出,W级中的数据没有转发到D级,原因是我们在GRF内实现了内部转发机制,将GRF输入端的数据(还未写入)及时反映到RD1或这RD2,判断条件为A3 == A2或者A3 == A1

此时为了生成HMUX的选择信号,我们需要向HCU(冒险控制器)输入”A”数据,然后进行选择信号的计算,执行转发的条件为——

  • 前位点的读取寄存器地址和某转发输入来源的写入寄存器地址相等且不为 0

  • 写使能信号有效

    转发的构造

    img

暂停(stall)

接下来,我们来处理通过转发不能处理的数据冒险。在这种情况下,新的数据还未来得及产生。我们只能暂停流水线,等待新的数据产生。为了方便处理,我们仅仅为D级的指令进行暂停处理。

我们把Tuse和Tnew作为暂停的判断依据——

  • Tuse:指令进入 D 级后,其后的某个功能部件经过多少时钟周期就必须要使用寄存器值。对于有两个操作数的指令,其每个操作数的 Tuse 值可能不等(如 store 型指令 rs、rt 的 Tuse 分别为 1 和 2 )。
  • Tnew:位于 E 级及其后各级的指令,再经过多少周期就能够产生要写入寄存器的结果。在我们目前的 CPU 中,W 级的指令Tnew 恒为 0;对于同一条指令,Tnew@M = max(Tnew@E - 1, 0)、

在这一阶段,我们找到D级生成的Tuse_rs和Tuse_rt和在E,M,W级寄存器中流水的Tnew_D,Tnew_M,Tnew_W,如下表所示

  • Tuse表和计算表达式
指令类型 Tuse_rs Tuse_rt
calc_R 1 1
calc_I 1 X
shift X 1
shiftv 1 1
load 1 X
store 1 2
md 1 1
mt 1 X
mf X X
branch 0 0
j / jr X X
jal / jalr 0 X
lui X X
  • Tnew表和计算表达式
指令类型 Tnew_D Tnew_E Tnew_M Tnew_W
calc_R 2 1 0 0
calc_I 2 1 0 0
shift 2 1 0 0
shiftv 2 1 0 0
load 3 2 1 0
store X X X X
md X X X X
mt X X X X
mf 2 1 0 0
branch X X X X
jal / jalr 0 0 0 0
j / jr X X X X
lui 1 0 0 0

然后我们Tnew和Tuse传入HCU(冒险控制器中),然后进行stall信号的计算。如果满足以下条件则stall有效——

  • Tnew > Tuse

  • 前位点的读取寄存器地址和某转发输入来源的写入寄存器地址相等且不为 0

  • 写使能信号有效

  • 当E级延迟槽在进行运算(start | busy)时,D级为md、mt、mf指令

  • 阻塞的构造(D级)

真值表

端口 addu subu ori lw sw lui beq
op 000000 000000 001101 100011 101011 001111 000100
func 100001 100011
AluOp 0000001 0000010 0001000 0000000 0000000 0000000 0000000
WeGrf 1 1 1 1 0 1 0
WeDm 0 0 0 0 1 0 0
branch 0001 0001 0001 0001 0001 0001 0010
AluSrc1 0001 0001 0001 0001 0001 0001 0001
AluSrc2 0001 0001 0010 0010 0010 0001 0001
WhichtoReg 0001 0001 0001 0010 0001 0100 0001
RegDst 0001 0001 0010 0010 0010 0010 1010
SignExt 0 0 0 1 1 0 1
端口 andi jal j jr sll add sub
op 001100 000011 000010 000000 000000 000000 000000
func 001000 000000 100000 100010
AluOp 0000100 0000000 0000000 0000000 0010000 0000000 0000001
WeGrf 1 1 0 0 1 1 1
WeDm 0 0 0 0 0 0 0
branch 0001 0100 0100 1000 0001 0001 0001
AluSrc1 0001 0001 0001 0001 0010 0001 0001
AluSrc2 0010 0001 0001 0001 0100 0001 0001
WhichtoReg 0001 1000 0001 0001 0001 0001 0001
RegDst 0010 0100 0001 0001 0001 0001 0001
SignExt 1 0 0 0 0 0 0

二、 测试方案

(1) 测试代码:

Folding coding

.text

ori $a0,$0,0x100

ori $a1,$a0,0x123

lui $a2,456

lui $a3,0xffff

ori $a3,$a3,0xffff

addu $s0,$a0,$a2

addu $s1,$a0,$a3

addu $s4,$a3,$a3

subu $s2,$a0,$a2

subu $s3,$a0,$a3

sw $a0,0($0)

sw $a1,4($0)

sw $a2,8($0)

sw $a3,12($0)

sw $s0,16($0)

sw $s1,20($0)

sw $s2,24($0)

sw $s3,44($0)

sw $s4,48($0)

lw $a0,0($0)

lw $a1,12($0)

sw $a0,28($0)

sw $a1,32($0)

ori $a0,$0,1

ori $a1,$0,2

ori $a2,$0,1

beq $a0,$a1,loop1

beq $a0,$a2,loop2

loop1: sw $a0,36($t0)

loop2: sw $a1,40($t0)

jal loop3

jal loop3

sw $s5,64($t0)

ori $a1,$a1,4

jal loop4

loop3:sw $a1,56($t0)

sw $ra,60($t0)

ori $s5,$s5,5

jr $ra

loop4: sw $a1,68($t0)

sw $ra,72($t0)

(2) 该CPU运行结果

Folding coding

@00003000: $ 4 <= 00000100
@00003004: $ 5 <= 00000123
@00003008: $ 6 <= 01c80000
@0000300c: $ 7 <= ffff0000
@00003010: $ 7 <= ffffffff
@00003014: $16 <= 01c80100
@00003018: $17 <= 000000ff
@0000301c: $20 <= fffffffe
@00003020: $18 <= fe380100
@00003024: $19 <= 00000101
@00003028: *00000000 <= 00000100
@0000302c: *00000004 <= 00000123
@00003030: *00000008 <= 01c80000
@00003034: *0000000c <= ffffffff
@00003038: *00000010 <= 01c80100
@0000303c: *00000014 <= 000000ff
@00003040: *00000018 <= fe380100
@00003044: *0000002c <= 00000101
@00003048: *00000030 <= fffffffe
@0000304c: $ 4 <= 00000100
@00003050: $ 5 <= ffffffff
@00003054: *0000001c <= 00000100
@00003058: *00000020 <= ffffffff
@0000305c: $ 4 <= 00000001
@00003060: $ 5 <= 00000002
@00003064: $ 6 <= 00000001
@00003074: *00000028 <= 00000002
@00003078: $31 <= 0000307c
@0000308c: *00000038 <= 00000002
@00003090: *0000003c <= 0000307c

Folding coding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//testbench模块
`timescale 1ns/1ps

module mips_txt;

reg clk;
reg reset;

wire [31:0] i_inst_addr;
wire [31:0] i_inst_rdata;

wire [31:0] m_data_addr;
wire [31:0] m_data_rdata;
wire [31:0] m_data_wdata;
wire [3 :0] m_data_byteen;

wire [31:0] m_inst_addr;

wire w_grf_we;
wire [4:0] w_grf_addr;
wire [31:0] w_grf_wdata;

wire [31:0] w_inst_addr;

mips uut(
.clk(clk),
.reset(reset),

.i_inst_addr(i_inst_addr),
.i_inst_rdata(i_inst_rdata),

.m_data_addr(m_data_addr),
.m_data_rdata(m_data_rdata),
.m_data_wdata(m_data_wdata),
.m_data_byteen(m_data_byteen),

.m_inst_addr(m_inst_addr),

.w_grf_we(w_grf_we),
.w_grf_addr(w_grf_addr),
.w_grf_wdata(w_grf_wdata),

.w_inst_addr(w_inst_addr)
);

integer i;
reg [31:0] fixed_addr;
reg [31:0] fixed_wdata;
reg [31:0] data[0:4095];
reg [31:0] inst[0:4095];

assign m_data_rdata = data[m_data_addr >> 2];
assign i_inst_rdata = inst[(i_inst_addr - 32'h3000) >> 2];

initial begin
$readmemh("code.txt", inst);
for (i = 0; i < 4096; i = i + 1) data[i] <= 0;
end

initial begin
clk = 0;
reset = 1;
#20 reset = 0;
end

always @(*) begin
fixed_wdata = data[m_data_addr >> 2];
fixed_addr = m_data_addr & 32'hfffffffc;
if (m_data_byteen[3]) fixed_wdata[31:24] = m_data_wdata[31:24];
if (m_data_byteen[2]) fixed_wdata[23:16] = m_data_wdata[23:16];
if (m_data_byteen[1]) fixed_wdata[15: 8] = m_data_wdata[15: 8];
if (m_data_byteen[0]) fixed_wdata[7 : 0] = m_data_wdata[7 : 0];
end


always @(posedge clk) begin
if (~reset) begin
if (w_grf_we && (w_grf_addr != 0)) begin
$display("%d@%h: $%d <= %h", $time, w_inst_addr, w_grf_addr, w_grf_wdata);
end
end
end

always @(posedge clk) begin
if (reset) for (i = 0; i < 4096; i = i + 1) data[i] <= 0;
else if (|m_data_byteen) begin
data[fixed_addr >> 2] <= fixed_wdata;
$display("%d@%h: *%h <= %h", $time, m_inst_addr, fixed_addr, fixed_wdata);
end
end
always #2 clk <= ~clk;

endmodule

三、 思考题

(一)为什么需要有单独的乘除法部件而不是整合进 ALU?为何需要有独立的 HI、LO 寄存器?

因为ALU在计算乘除延迟的周期中时依然可以支持如加减法之类的其他计算,如果将乘除法部件整合进ALU,将不利于乘除运算时的加减法运算,也不符合“高内聚,低耦合”的设计思想。

而增加独立的HI和LO寄存器,获得了更多的操作空间,将更有利于各种运算操作的实现。

(二)真实的流水线 CPU 是如何使用实现乘除法的?请查阅相关资料进行简单说明。

​ 真实的CPU是通过多次加减法来实现乘法和除法的,所以显而易见乘除法所消耗的时间明显长于加减法。

​ 乘法的计算语言加法和左移运算,并通过另一个寄存器{HI,LO}来保留计算结果,而除法则是依靠减法和左移。由此可以很清晰的分析出为什么HI存储乘法高位及除法的余数,而LO存储乘法低位和除法的商。

(三)请结合自己的实现分析,你是如何处理 Busy 信号带来的周期阻塞的?

​ 当MDU处于Busy信号或者en信号时将进行乘除法运算,如果此时位于D级的指令为乘除运算,则我们会选择在HazardUnit中控制产生阻塞信号,从而形成周期阻塞。

(四) 请问采用字节使能信号的方式处理写指令有什么好处?(提示:从清晰性、统一性等角度考虑)

​ 这样的方式写入字节有利于保持字对齐,清晰度较高,而从统一性处理将一个字分成四个字节,将有利于对字,半字和字节的处理保持统一,有利于维护代码的运行及debug

(五)请思考,我们在按字节读和按字节写时,实际从 DM 获得的数据和向 DM 写入的数据是否是一字节?在什么情况下我们按字节读和按字节写的效率会高于按字读和按字写呢?

​ 不是,当按字节写和按字节读时那个字是非对齐的位置时会高于按字读和按字写。

(六)为了对抗复杂性你采取了哪些抽象和规范手段?这些手段在译码和处理数据冲突的时候有什么样的特点与帮助?

​ 增加更多的板块,如分出多路选择器等让代码实现“高内聚,低耦合”的要求。尽可能简化每一个模块的复杂度,尽量使他们彼此独立。将指令分析和模块的功能分离,让模块只受译码器给出的信号的控制。

​ 除此之外,增加宏的定义,避免对相同常量段的重复修改,同时宏的定义也有利于使代码更加清晰。

(七)在本实验中你遇到了哪些不同指令类型组合产生的冲突?你又是如何解决的?相应的测试样例是什么样的?

对于乘除模块的冲突我们是直接选择延迟的方式进行处理的,而对于其他方式则见上方冲突转发部分。

(八)如果你是手动构造的样例,请说明构造策略,说明你的测试程序如何保证覆盖了所有需要测试的情况;如果你是完全随机生成的测试样例,请思考完全随机的测试程序有何不足之处;如果你在生成测试样例时采用了特殊的策略,比如构造连续数据冒险序列,请你描述一下你使用的策略如何结合了随机性达到强测的效果。

我使用的是完全随机生成的测试样例,并对其缩小输出寄存器的范围,从而构造更多的数据冒险,但由于数据容易超出Mars的data区,这种方式不利于对 存储指令进行测试,需要单独设立板块进行测试。

为了达到强测效果,我们要尽可能的增加代码长度,并多设计冒险方式的代码段。

选做题

(一)请评估我们给出的覆盖率分析模型的合理性,如有更好的方案,可一并提出。

​ 由于转发比阻塞的效率更高,所以我们在编码时的基本原则是尽可能转发,而在这个模型中,转发的得分明显高于阻塞,合理。

​ 本覆盖率分析模型的指令集按需分类,更有利于集中式处理,使效率更高。

四.自动化测试模块

命令行自动测试

具体源码可见:Charles_BUAA_CO_2022/p7/statistic at main · Charles2530/Charles_BUAA_CO_2022 (github.com)

Folding coding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import os
i = 1
while i <= 5000:
print('num = ' + str(i))
os.chdir('D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic')
os.system('python test.py')
os.system(
'java -jar Mars.jar nc db lg ex mc LargeText 100000 test.asm >mips_out.txt')
os.system(
'java -jar mars.jar db mc CompactDataAtZero a dump .text HexText ../mips/code.txt test.asm > mips_log.txt')
os.system(
'java -jar Mars.jar a db mc CompactDataAtZero dump 0x00004180-0x00004ffc HexText ../mips/handler.txt test.asm > mips_log.txt')
os.chdir('D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\mips')
os.system(
'D:\\SoftWare\\ISE\\14.7\\ISE_DS\\ISE\\bin\\nt64\\fuse -nodebug -prj mips.prj \
-o mips.exe mips_txt 1>../statistic/isim_log.txt 2>../statistic/isim_warning_log.txt')
os.system(
'mips.exe -nolog -tclbatch mips.tcl > isim_out.txt')
os.chdir('D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic')
os.system('python diff.py')
i = i+1
Folding coding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
import random

#Calc_r = ['add']
Calc_r = ['add', 'sub', 'and', 'or', 'slt', 'sltu']
Calc_i = ['ori', 'addi', 'ori']
Load = ['lb']
# Load = ['lw', 'lb', 'lh']
Store = ['sb']
# Store = ['sw', 'sb', 'sh']
B_type = ['beq', 'bne']
J_type = ['jal']
Shift = []
Shift_v = []
md = ['mult', 'multu', 'div', 'divu']
mf_mt = ['mfhi', 'mflo', 'mthi', 'mtlo']

filename = "D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p6\\statistic\\test.asm"
# 输出文件位置
label = [0]
# 输出label的编号范围,事先存入0防止在第一次输出标签前出现跳转指令
cnt = 0
# 可执行代码的行数
flag = 1
# 当前所标出过的编号号码
jal = []
# 使用过的jal对应标签编号
R_num = len(Calc_r)
I_num = len(Calc_i)
L_num = len(Load)
S_num = len(Store)
B_num = len(B_type)
J_num = len(J_type)
MD_num = len(md)
MF_num = len(mf_mt)
# 各类指令出现频率设置
f_R = 10*random.randint(1, 4)
f_I = 10*random.randint(1, 4)
f_L = 40*random.randint(1, 4)
f_S = 40*random.randint(1, 4)
f_B = 0*random.randint(1, 4)
f_J = 0*random.randint(1, 4)
f_md = 5*random.randint(1, 4)
f_mf = 5*random.randint(1, 4)
num = f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num + \
f_B*B_num+f_J*J_num+f_md*MD_num+f_mf*MF_num+1
begin = 28
end = 31


class get_Ori:
def __init__(self):
# 对应指令生成随机数
self.rs = random.randint(begin, end)
self.rt = random.randint(begin, end)
self.imm16 = random.randint(0, 1 << 16-1)
self.main()

def main(self):
if self.rs == 28 or self.rs == 29 or self.rs == 30:
self.rs = self.rs-3
if self.rt == 28 or self.rt == 29 or self.rt == 30:
self.rt = self.rt-3
self.code = 'ori' + ' ' + '$' + \
str(self.rt) + ',' + '$' + \
str(self.rs) + ',' + str(self.imm16) + '\n'


class get_Code:
def __init__(self):
# 对应指令生成随机数
self.rs = random.randint(begin, end)
self.rt = random.randint(begin, end)
self.rd = random.randint(begin, end)
self.imm16 = random.randint(0, 1 << 8-1)
self.imm26 = random.randint(0, 1 << 26-1)
#self.mem = random.randint(0, 100)
self.mem = random.randint(0, 3) << 6
# self.mem = random.randint(0, 3)
# 存储指令类型
self.list = []
# get函数
self.get_R()
self.get_I()
self.get_L()
self.get_S()
self.get_B()
self.get_J()
self.get_MD()
self.get_MF()
self.get_Label()
self.main()

def get_R(self):
random1 = random.randint(0, R_num - 1)
type1 = Calc_r[random1]
self.list.append(type1)

def get_I(self):
random2 = random.randint(0, I_num - 1)
type2 = Calc_i[random2]
self.list.append(type2)

def get_L(self):
random3 = random.randint(0, L_num - 1)
type3 = Load[random3]
self.list.append(type3)

def get_S(self):
random4 = random.randint(0, S_num - 1)
type4 = Store[random4]
self.list.append(type4)

def get_B(self):
random5 = random.randint(0, B_num - 1)
type5 = B_type[random5]
self.list.append(type5)

def get_J(self):
random6 = random.randint(0, J_num - 1)
type6 = J_type[random6]
self.list.append(type6)

def get_MD(self):
random7 = random.randint(0, MD_num - 1)
type7 = md[random7]
self.list.append(type7)

def get_MF(self):
random8 = random.randint(0, MF_num - 1)
type8 = mf_mt[random8]
self.list.append(type8)

def get_Label(self):
random9 = random.randint(0, len(label)-1)
ran = label[random9]
return ran

def main(self):
sel = random.randint(0, num+100)
if self.rs == 28 or self.rs == 29 or self.rs == 30:
self.rs = self.rs-3
if self.rt == 28 or self.rt == 29 or self.rt == 30:
self.rt = self.rt-3
if self.rd == 28 or self.rd == 29 or self.rd == 30:
self.rd = self.rd-3
# 控制参数类型
if sel in range(0, f_R*R_num):
# 通过控制随机数的范围来决定输出各种指令的频率,并用各种指令的数目保证各指令出现概率基本相同
self.code = self.list[0] + ' ' + '$' + \
str(self.rd) + ' ' + ',' + '$' + \
str(self.rs) + ' ' + ',' + '$' + str(self.rt) + '\n'
elif sel in range(f_R*R_num, f_R*R_num+f_I*I_num):
self.code = self.list[1] + ' ' + '$' + \
str(self.rt) + ',' + '$' + \
str(self.rs) + ',' + str(self.imm16) + '\n'
elif sel in range(f_R*R_num+f_I*I_num, f_R*R_num+f_I*I_num+f_L*L_num):
self.code = self.list[2] + ' ' + '$' + \
str(self.rt) + ',' + str(self.mem) + \
'(' + '$' + '0' + ')'+'\n'
elif sel in range(f_R*R_num+f_I*I_num+f_L*L_num, f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num):
self.code = self.list[3] + ' ' + '$' + \
str(self.rt) + ',' + str(self.mem) + \
'(' + '$' + '0' + ')'+'\n'
elif sel in range(f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num, f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num):
self.code = self.list[4] + ' ' + '$' + \
str(self.rt) + ',' + '$' + \
str(self.rs) + ',' + 'label_' + \
str(self.get_Label()) + '\n'+'nop' + '\n'
elif sel in range(f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num, f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num+f_J*J_num):
if self.list[5] == 'jal':
node = self.get_Label()
self.code = self.list[5] + ' ' + \
'label_' + str(node) + '\n'+'nop' + '\n'
jal.append(node)
elif self.list[5] == 'j':
self.code = self.list[5] + ' ' + \
'label_' + str(self.get_Label()) + '\n' + 'nop' + '\n'
elif sel in range(f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num+f_J*J_num, f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num+f_J*J_num+f_md*MD_num):
self.code = self.list[6] + ' ' + '$' + \
str(self.rt) + ',' + '$' + \
str(self.rs) + '\n'
elif sel in range(f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num+f_J*J_num+f_md*MD_num, f_R*R_num+f_I*I_num+f_L*L_num+f_S*S_num+f_B*B_num+f_J*J_num+f_md*MD_num+f_mf*MF_num):
self.code = self.list[7] + ' ' + '$' + \
str(self.rs) + '\n'
else:
self.code = 'lui' + ' ' + \
'$' + str(self.rt) + ',' + str(self.imm16) + '\n'


with open(filename, 'w+') as f:
for cnt in range(2*begin, 2*end):
b = get_Ori()
f.write(b.code)

cnt = 0
for cnt in range(0, num):
a = get_Code()
f.write(a.code)
if random.randint(0, num) == 1 and label != []:
# 通过控制random范围来决定标签和jr出现的概率
f.write('label_' + str(flag) + ':' + '\n')
label.append(flag)
flag = flag+1
# f.write('jr $ra' + '\n')
# f.write('nop' + '\n')
# if cnt == 10:
# f.write('jr $ra' + '\n')
# f.write('nop' + '\n')
if cnt == num-5:
f.write('label_0' + ':' + '\n')
f.close()

Folding coding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# from filediff.diff import file_diff_compare
import os

tmp = open(
"D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic\\mips_out.txt", "r")
out = open(
"D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic\\mips_out_another.txt", "w")
std = open(
"D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\mips\\isim_out.txt", "r")
isim = open(
"D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic\\isim_out.txt", "w")
while True:
line = tmp.readline()
if not line:
break
line = line.replace(" ", "")
line = line.replace("\t", "")
out.write(line)
out.write("\n")
while True:
line = std.readline()
if not line:
break
if line[0].isspace():
line = line.replace(" ", "")
line = line.replace("\t", "")
isim.write(line)
elif line[0] == '@':
line = line.replace(" ", "")
line = line.replace("\t", "")
isim.write(line)
isim.write("\n")
out.close()
tmp.close()
std.close()
isim.close()
result = open(
"D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic\\result.txt", "a+")
os.system("cmp.exe")
result.write(
"---------------------------------test------------------------------------------- "+"\n\n")
result.close()

Folding coding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <bits/stdc++.h>
using namespace std;
#define M 100007
#define N 1007
#define INF 0x3f3f3f3f
#define ll long long
#define db double

int main() {
char s1[400], s2[400];
char s3[400], s4[400];
int flag = 1;
FILE* fp1;
FILE* fp2;
FILE* fp3;
FILE* fp4;
FILE* fp5;
FILE* fp6;
fp1 = fopen(
"D:\\coding_file\\study\\Lesson\\co_"
"lesson\\lesson\\p7\\statistic\\mips_out_another.txt",
"r");
fp2 = fopen(
"D:\\coding_file\\study\\Lesson\\co_"
"lesson\\lesson\\p7\\statistic\\isim_out.txt",
"r");
fp3 = fopen(
"D:\\coding_file\\study\\Lesson\\co_"
"lesson\\lesson\\p7\\statistic\\result.txt",
"a+");
fp4 = fopen(
"D:\\coding_file\\study\\Lesson\\co_"
"lesson\\lesson\\p7\\statistic\\test.asm",
"r");
fp5 = fopen(
"D:\\coding_file\\study\\Lesson\\co_lesson\\lesson\\p7\\statistic\\p7_"
"error_code.asm",
"a+");
fp6 = fopen(
"D:\\coding_file\\study\\Lesson\\co_"
"lesson\\lesson\\p7\\statistic\\isim_out_another.txt",
"w");
int i = 0, j = 0;
int n = 0;
int line = 0;
int l1 = 0, l2 = 0;
while (fgets(s1, 400, fp1)) {
string s = s1;
// printf("%d", s.find("$0"));
if (s.find("$0") != -1) {
continue;
}
line++;
if (s1[0] == 'I') {
fgets(s1, 400, fp1);
fgets(s1, 400, fp1);
}
fgets(s2, 400, fp2);
if (s2[0] == 'I') {
fgets(s2, 400, fp2);
fgets(s2, 400, fp2);
}
l1++;
l2++;
for (i = 0; s1[i] != '\0'; i++) {
if (s1[i] == '@') {
break;
}
}
for (j = 0; s2[j] != '\0'; j++) {
if (s2[j] == '@') {
break;
}
}
fprintf(fp6, "%s", s2 + j);
if (strcmp(s1 + i, s2 + j)) {
fprintf(fp3, "Error in line %d\n", line);
fprintf(fp3, "get :%s", s2);
fprintf(fp3, "expect :%s\n\n", s1 + i);
flag = 0;
}
}
if (flag) {
fprintf(fp3, "Accept\n");
printf("Accept\n");
} else {
printf("Error\n");
fprintf(fp5, "#error begin with\n");
while (fgets(s3, 400, fp4)) {
fputs(s3, fp5);
}
fprintf(fp5, "#error end\n\n\n");
}
fclose(fp1);
fclose(fp2);
fclose(fp3);
return 0;
}

五、规范化编码

1、命名风格

  • 各级之间使用流水级_instr_方向的方式,来有效地对它们进行区分,如:

    D/E 寄存器的输入端口就可以命名为 D_instr_i

  • 在顶层模块中,我们需要实例化调用子模块,这个过程会产生很多负责接线的“中间变量”,推荐流水级_wirename的方式,并且将同级的信号尽可能都声明在一起。

2、模块逻辑排布(看图说话)

img

3、常量、字面量与宏

对于指令不同的字段,直接定义 wire 型变量如 op、rs 映射到 instr 的对应位上,直观且简短。

对于控制器译出的信号,如果仅在一个模块内使用,可以使用 localparam 定义。但有很多信号需要被多个模块跨文件使用到(如 alu 的控制信号需要同时在控制器与 alu 出现),并且,我们需要为工程的扩展做好准备,因此更推荐编写一个单独的**宏定义文件(如下)**来供其他的模块用 `include 引用。

img

4、译码风格

  • 指令驱动型:整体在一个 case 语句之下,通过判断指令的类型,来对所有的控制信号一一进行赋值。这种方法便于指令的添加,不易遗漏控制信号,但是整体代码量会随指令数量增多而显著增大。
    img
  • 控制信号驱动型:为每个指令定义一个 wire 型变量,使用或运算描述组合逻辑,对每个控制信号进行单独处理。这种方法在指令数量较多时适用,且代码量易于压缩,缺陷是如错添或漏添了某条指令,很难锁定出现错误的位置。
    img
  • Title: P6 Design documents
  • Author: Charles
  • Created at : 2022-12-26 21:11:47
  • Updated at : 2023-11-05 21:36:02
  • Link: https://charles2530.github.io/2022/12/26/p6-design-documents/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
P6 Design documents