BUAA计组p4_Verilog_复习tips
p4
- 顶层
- mips.v
- control
- ctrl.v
- mux.v
- datapath
- npc.v
- pc.v
- im.v
- grf.v
- ext.v
- alu.v
- dm.v
- control
- code.txt
- mips.v
p4流程
- 整体架构
- mips.v在最顶层,其下划分为数据通路与控制模块,控制模块中产生控制信号,数据通路中各模块为各功能部件。
- 由底向上构建模块
- 构建各功能部件及控制部件,以及所有MUX归至一个.v各占一个module
- 模块实例化
- 通过模块实例化,在mips顶层模块中建立起各元件之间的联系
- 联系各模块
模块构建相关
- IM与DM的实现
- reg型变量
- reg型数据的默认初始值为不定值x
- 在always模块内被赋值的每一个信号都必须定义为reg型
- reg[n:1] 数据名, [n:1]指数据位宽
- memory型变量
- Verilog HDL通过对reg型变量建立数组来对存储器(RAM,ROM,reg文件)建模。
- 数组中每一个单元通过一个数组索引进行寻址
- memory型数据是通过扩展reg型数据的地址范围来生成的,如
reg [n-1] 存储器名 [m-1:0]; reg [7:0] mema[255:0]; //定义了名为mema的存储器,该存储器有256个8位的存储器,该存储器的地址范围为0到255
- reg [n-1:0]定义了存储器中每一个存储单元的大小,即为32位的寄存器;[m-1:0]定义了有多少个这样的寄存器
- reg型变量
debug有感
- im, npc, pc, grf, alu, dm, ext, ctrl
Part 1
- `define 末尾没有分号!!!
宏定义如果在行末加了分号,会连分号一起进行置换
宏定义是用宏名简单的置换一个字符串,不做语法检查,仅在编译已被宏展开后的源程序时才报错。
`define sw_o 6'b101011
`define beq_o 6'b000100
`define lui_o 6'b001111
- GRF 对于0号寄存器的写入需要忽略,输出与否不影响评测
else if(WE==1 && A3!=0) begin
reg_files[A3] <= WD;
end
- 位拼接运算符必须要指定进制
ext_out <= {{16{1'b0}}, ext_in[15:0]} ; //必须标明1'b0
-
pc模块需要初始化。32'h3000才是初始化PC为0x3000的正确写法。32为位数,针对bits。32'h即表意8个十六进制数,如果写成8'h3000会初始化为0。
-
仔细检查各指令的OpCode以及Funct码是否写对
Part 2
- 初始化
- 由于未对pc.v进行初始化导致不定值的出现
- 对于模块初始化部分、reset部分在一开始构思写码的时候就要重视。
- 在debug出现不定值x时首先考虑此因导致的问题。
- 特殊情况的考虑
- 由于未对写入$0寄存器进行判断导致指令地址正确但是输出值非零
- 仔细看模块构建的要求,对于特殊情况一开始的时候就写在注释或文档里以防忘记
- 基础不变量的输入
- 由于敲错jal指令的OpCode导致jal指令无效,且后续指令输出全乱
- 最基础的不变量一定要再三确认
- 在debug时先看基础常量,如OpCode、Funct、偏移常量、符号/0拓展等是否敲对,再看Controller模块的每一项输出是否正确
Part 3
-
由由底层到顶层debug
- 对于由多个模块组成的顶层模块,需要先对子模块逐个建立testbench逐个debug,之后再对顶层模块的testbench进行调试。
- 由底向上的模块建立需要有序的逐层debug,逐个模块调试,确认各个部件无误后再对顶层模块进行调试。
-
单步运行分段debug
- 在调试子模块出现问题时,务必通过单步运行debug,绝对不要目测debug浪费时间和生命。。。
- 目测看不出问题的,务必熟练调试的各项操作!!!
-
针对错误输出找问题
-
仿真出现高阻值z代表并未连接到有效输入上,首先应考虑连线问题,仿真出现不定值x的情况时首先考虑初始化问题。
-
单个模块调试时,npc直接指定了固定值,因此没有查出bug
-
课上测试
- 指令回顾
1.1 BSZEAL: GPR[Rs]和GPR[Rt]中储存的数字后缀0个数相同时,跳转
- 实现:ALU中添加的输出Equal赋值为1输入至Controller,和baszeal_i于Controller中共同决定NPC的取值。
assign Equal = alu_Result == 0 ? 1 : 0;
//现在发现完全不用加一位,直接改alu_Control用beq的Zero输出完全一致。
always @(*) begin
case(alu_Control)
// others
`suffix : begin
for(i=0;alu_A[i]==0;i=i+1) begin
end
for(j=0;alu_B[j]==0;j=j+1) begin
end
alu_Result <= i - j;
end
endcase
end
always @(*) begin
NPC[1] <= others1_i ;Equal&bszeal_i;
NPC[0] <= others0_i
end
1.2 xor: 异或运算,不赘述。
1.3 lha: 当对应地址对应的半字数据为4的倍数时将该半字符号扩展并写入指定寄存器,否则将0写入指定的寄存器。
- 实现:对半字操作时于Controller中设置DM_mode为2'b10,DM内对该情况按Addr[1:0]分2'b10和2'b00加至三目运算内输出
assign dout = (DM_mode == 2'b00) ? DM_WD :
(DM_mode == 2'b01 ) ? DM_WD_lb : // 预加指令中DM_mode == 2'b01为对8bits操作
(DM_mode == 2'b10 && dm_reg[address][1:0]==2'b00) ? DM_WD_lha_low16 : //具体输出使用位拼接操作
(DM_mode == 2'b10 && dm_reg[address][17:16]==2'b00) ? DM_WD_lha_high16 : 32'b0;
- 课下建议: 计算类、访存类、跳转类指令最好都添加练习一下。
尤其是访存类添加过指定位读入读出操作的控制信号DM_mode的话,会更熟练且课上改动很方便。