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
    • code.txt

p4流程

  1. 整体架构
    • mips.v在最顶层,其下划分为数据通路与控制模块,控制模块中产生控制信号,数据通路中各模块为各功能部件。
  2. 由底向上构建模块
    • 构建各功能部件及控制部件,以及所有MUX归至一个.v各占一个module
  3. 模块实例化
    • 通过模块实例化,在mips顶层模块中建立起各元件之间的联系
  4. 联系各模块

模块构建相关

  1. 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]定义了有多少个这样的寄存器

debug有感

  • im, npc, pc, grf, alu, dm, ext, ctrl
Part 1
  1. `define 末尾没有分号!!!

宏定义如果在行末加了分号,会连分号一起进行置换
宏定义是用宏名简单的置换一个字符串,不做语法检查,仅在编译已被宏展开后的源程序时才报错。

`define sw_o 6'b101011
`define beq_o 6'b000100
`define lui_o 6'b001111
  1. GRF 对于0号寄存器的写入需要忽略,输出与否不影响评测
else if(WE==1 && A3!=0) begin
  	reg_files[A3] <= WD;
  end
  1. 位拼接运算符必须要指定进制
ext_out <= {{16{1'b0}}, ext_in[15:0]} ; //必须标明1'b0
  1. pc模块需要初始化。32'h3000才是初始化PC为0x3000的正确写法。32为位数,针对bits。32'h即表意8个十六进制数,如果写成8'h3000会初始化为0。

  2. 仔细检查各指令的OpCode以及Funct码是否写对

Part 2
  1. 初始化
    • 由于未对pc.v进行初始化导致不定值的出现
    • 对于模块初始化部分、reset部分在一开始构思写码的时候就要重视。
    • 在debug出现不定值x时首先考虑此因导致的问题。
  2. 特殊情况的考虑
    • 由于未对写入$0寄存器进行判断导致指令地址正确但是输出值非零
    • 仔细看模块构建的要求,对于特殊情况一开始的时候就写在注释或文档里以防忘记
  3. 基础不变量的输入
    • 由于敲错jal指令的OpCode导致jal指令无效,且后续指令输出全乱
    • 最基础的不变量一定要再三确认
    • 在debug时先看基础常量,如OpCode、Funct、偏移常量、符号/0拓展等是否敲对,再看Controller模块的每一项输出是否正确
Part 3
  1. 由底层到顶层debug

    • 对于由多个模块组成的顶层模块,需要先对子模块逐个建立testbench逐个debug,之后再对顶层模块的testbench进行调试。
    • 由底向上的模块建立需要有序的逐层debug,逐个模块调试,确认各个部件无误后再对顶层模块进行调试。
  2. 单步运行分段debug

    • 在调试子模块出现问题时,务必通过单步运行debug,绝对不要目测debug浪费时间和生命。。。
    • 目测看不出问题的,务必熟练调试的各项操作!!!
  3. 针对错误输出找问题

    • 仿真出现高阻值z代表并未连接到有效输入上,首先应考虑连线问题,仿真出现不定值x的情况时首先考虑初始化问题。

    • 单个模块调试时,npc直接指定了固定值,因此没有查出bug


课上测试

  1. 指令回顾
    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;

  1. 课下建议: 计算类、访存类、跳转类指令最好都添加练习一下。
    尤其是访存类添加过指定位读入读出操作的控制信号DM_mode的话,会更熟练且课上改动很方便。
posted @ 2020-12-06 09:57  Frida_h  阅读(1361)  评论(0编辑  收藏  举报