• CH02 FPGA设计Verilog基础笔记(三)


     

    1、一个完整的设计,除了好的功能描述代码,对于程序的仿真验证是必不可少的。学会如何去验证自己所写的程序,即如何调试自己的程序是一件非常重要的事。而RTL逻辑设计中,学会根据硬件逻辑来写测试程序,即Testbench显得尤其重要。

    编写Testbench的目的是为了对使用硬件描述语言设计的电路进行仿真验证,测试设计电路的功能、性能与设计的预期是否符合。通常过程如下:

    *产生模拟激励(波形);

    *将产生的激励加入到被测试模块中并观察其响应;

    *将输出相应与预期值相比较。

    2、Testbench文件结构为:

    module Test_bench();  //通常无输入无输出

    信号或变量声明定义

    逻辑设计中输入对应reg型

    逻辑设计中输出对应wire型

    使用initial或always语句产生激励

    例化待测试模块

    监控和比较输出响应

    endmodule

    1、  时钟激励设计

    /*************时钟激励产生方法一:50%占空比时钟************/

    parameter ClockPeriod=10;

    initial

               begin

                        clk_i=0;

                        forever

                                 #(ClockPeriod/2) clk_i=~clk_i;

               end

    /*****************时钟激励产生方法二:50%占空比时钟*************/

    initial

               begin

                        clk_i=0;

                        always #(ClockPeriod/2) clk_i=~clk_i;

               end

    /*****************时钟激励产生方法三:产生固定数量的时钟脉冲*************/

    initial

               begin

                        clk_i=0;

                        repeat(6)

                                 #(ClockPeriod/2) clk_i=~clk_i;

               end

    /*****************时钟激励产生方法四:产生非占空比为50%的时钟*************/

    initial

               begin

                        clk_i=0;

                        forever

                                 begin

                                           #((ClockPeriod/2)-2) clk_i=0;

                                           #((ClockPeriod/2)+2) clk_i=1;

                                 end

               end

    2、  复位信号设计

    /*****************复位信号产生方法一:异步复位*************/

    initial

               begin

                        rst_n_i=1;

    #100;

             rst_n_i=0;

             #100;

             rst_n_i=1;

    end

    /*****************复位信号产生方法二:同步复位*************/

             initial

                       begin

                                rst_n_i=1;

    @ (negedge clk_i)

    rst_n_i=0;

    #100;                            //固定时间复位

    repeat(10) @ (negedge clk_i);     //固定周期数复位

    @ (negedge clk_i)

    rst_n_i=1;

    end

    /*****************复位信号产生方法三:复位任务封装*************/

    task reset;

               input [31:0] reset_time;       //复位时间可调,输入复位时间

             RST_ING=0;                          //复位方式可调,低电平或高电平

               begin

                        rst_n=RST_ING;           //复位中

                        #reset_time;                //复位时间

                        rst_n_i=~RST_ING;     //撤销复位,复位结束

               end

    endtask

    3、  双向信号设计

    /*****************双向信号描述一:inout在testbench中定义为wire型变量*************/

    //为双向端口设置中间变量inout_reg作为inout的输出寄存,

    //其中inout变量定义为wire型,使用输出使能控制传输方向

    //inout bir_port;

    wire bir_port;

    reg bir_port_reg;

    reg bi_port_oe;

    assign bi_port=bi_port_oe?bir_port_reg:1’bz;

    /*****************双向信号描述二:强制force**************/

    //当双向端口作为输出口时,不需要对其初始化,只需要开通三态门

    //当双向端口作为输入口时,只需要对其初始化并关闭三态门,初始化赋值

    //需要使用wire型数据,通过force命令来对双向端口进行输入赋值

    //assign dinout=(!en) din: 16’hz;  完成双向赋值

    initial

               begin

                        force dinout=20;

                        #200;

                        force dinout=dinout-1;

               end

    4、  特殊信号设计

    /*****************特殊激励信号产生描述一:输入信号任务封装**************/

    task i_data;

    input [7:0] dut_data;

    begin

               @(posedge data_en); send_data=0;

               @(posedge data_en); send_data=dut_data[0];

               @(posedge data_en); send_data=dut_data[1];

               @(posedge data_en); send_data=dut_data[2];

               @(posedge data_en); send_data=dut_data[3];

               @(posedge data_en); send_data=dut_data[4];

               @(posedge data_en); send_data=dut_data[5];

               @(posedge data_en); send_data=dut_data[6];

             @(posedge data_en); send_data=dut_data[7];

               @(posedge data_en); send_data=1;

    end

    endtask

    //调用方法: i_data(8’hXX);

    /*****************特殊激励信号产生描述二:多输入信号任务封装**************/

    task more_input;

    input [7:0] a;

    input [7:0] b;

    input [31:0] times;

    output [8:0] c;

    begin

               repeat(times)                        //等待times个时钟上升沿

                        @(posedge clk_i)

                                 c=a+b;                //时钟上升沿a,b相加

    end

    entask

    /*****************特殊激励信号产生描述三:输入信号产生,一次SRAM写信号产生**************/

    initial

               begin

                        cs_n=1;               //片选无效

    wr_n=1;              //写使能无效    

    rd_n=1;              //读使能无效

    addr=8’hxx;       //地址无效

    data=8’hzz;       //数据无效

    #100;

    cs_n=0;              //片选有效

    wr_n=0;             

    addr=8’Hf1;

    data=8’h2C;

    #100;

    cs_n=1;

    wr_n=1;

    #10;

    addr=8’hxx;

    data=8’hzz;

             end

    /****************Testbench中@与wait**************/

    //@使用沿触发

    //wait语句使用电平触发

    initial

             begin

                       start=1’b1;

                       wait(en=1’b1);

                       #10;

                       start=1’b0;

             end

    5、  仿真控制语句及系统任务描述

    6、  /****************仿真控制语句及系统任务描述**************/

    $stop          //停止运行仿真,modelsim中可继续仿真

    $stop(n)     //带参数系统任务,根据参数0,1或2不同,输出仿真信息

    $finish        //结束运行仿真,不可继续仿真

    $finish(n)   //带参数系统任务,根据参数0,1或2不同,输出仿真信息

                        //0:不输出任何信息

                        //1:输出当前仿真时刻和位置

                        //2:输出当前仿真时刻、位置和仿真过程中用到的memory以及CPU时间的统计

    $random    //产生随机数

    $random % n       //产生范围-n到n之间的随机数

    {$random} % n     //产生范围0到n之间的随机数

    /****************仿真终端显示描述**************/

    $monitor    //仿真打印输出,打印出仿真过程中的变量,使其终端显示

                        /*

                                           $monitor($time,,,”clk=%d reset=%d out=%d”,clk,reset,out);

    */

             $display    //终端打印字符串,显示仿真结果等

                       /*

                                           $display(“Simulation start !”);

                                           $display(“At time %t,input is %b%b%b,output is %b”,$time,a,b,en,z);

    */

             $time         //返回64位整型时间

             $stime       //返回32位整型时间

             $realtime   //实行实型模拟时间

    /****************文本输入方式:$readmemb/$readmemh**************/

             //激励复杂的数据结构

             //verilog提供了读入文本的系统函数

             $readmemb/$readmemh(“<数据文件名>”<存储器名>);

             $readmemb/$readmemh(“<数据文件名>”<存储器名>,<起始地址>);

             $readmemb/$readmemh(“<数据文件名>”<存储器名>,<起始地址>,<结束地址>);

             readmemb:/*

                                         读取二进制数据,读取文件内容只能包含:空白位置,注释行,二进制数。

                                         数据中不能包含位宽说明和格式说明,每个数字必须是二进制数字。

                                         当地址出现在数据文件中,格式为@hh...h。地址与数字间不允许空白位置,

                                         可出现多个地址。

    */

             module

                       reg [7:0] memory[0:3];        //定义了4个8位存储单元

                       integer i;

                       initial

                                begin

                                         $readmemh(“mem.dat”,memory);        //读取系统文件到存储器中给定的地址

                                         //显示此时存储器的内容

                                         for(i=0;i<4;i=i+1)

                                                   $display(“Memory[%d]=%h”,i,memory[i]);

                                end

             endmodule

    /*

             mem.dat文件内容

             @001

    AB  CD

             @003

    A1    

    */

    //仿真输出为:

    Memory[0]=xx;

    Memory[1]=AB;

    Memory[2]=CD;

    Memory[3]=A1;

    9、  加法器的仿真测试文件编写

    /*******************Design Source文件*****************************/

    module add(a,b,c,d,e);//模块端口

    input [5:0] a;  //输入信号a

    input [5:0] b;

    input [5:0] c;

    input [5:0] d;

    output [7:0] e; //求和信号

    wire [6:0] outa1,outa2; //定义输出线网型

    assign e=outa1+outa2;  

    /*

        通常,我们模块调用写法如下:

        被调用的模块名字-自定义名字-括号内信号

        这里比如括号内的信号, .ina(ina1)

        这种写法最常用,信号的顺序可以调换

        另外还有一种写法可以直接这样写

        adder u1 (ina1,inb1,outa1);

        这种写法必须确保信号的一致性,所以几乎没人采用

    */

    adder u1 (.ina(a),.inb(b),.outa(outa1));    //调用adder模块,自定义名字为u1

    adder u2 (.ina(c),.inb(d),.outa(outa2));

    endmodule

    //adder模块

    module adder(ina,inb,outa); //模块接口

    input [5:0] ina;    //ina-输入信号

    input [5:0] inb;    //inb-输入信号

    output [6:0] outa;  //outa-输入信号

    assign outa=ina+inb;    //求和

    endmodule   //模块结束

    /*******************Simulation Sources文件*****************************/

    `timescale 1ns/1ps

    module add_tv();

    reg [5:0] a;

    reg [5:0] b;

    reg [5:0] c;

    reg [5:0] d;

    wire [7:0] e;

    reg [5:0] i;    //中间变量

    //调用模块

    add uut (.a(a),.b(b),.c(c),.d(d),.e(e));

    initial begin   //initial仿真初始化用的关键词

        a=0;b=0;c=0;d=0;    //必须初始化输入信号

        for(i=1;i<31;i=i+1)

        begin

            #10;

            a=i;

            b=i;

            c=i;

            d=i;

        end

    end

    initial begin

    $monitor($time,,,"%d+%d+%d+%d={%d}",a,b,c,d,e);

    #500

    $finish;

    end

    endmodule   

    /*******************仿真时序图如下*****************************/

  • 相关阅读:
    k8s-存储-volume
    k8s-存储-configmap
    k8s-集群调度
    k8s-常用命令
    k8s-资源限制
    k8s-更改证书时间
    kubeadmin安装k8s
    CCPC-Wannafly Winter Camp Day7 D---二次函数【数论】【构造】
    洛谷P1219 八皇后【dfs】
    2019寒假计数器
  • 原文地址:https://www.cnblogs.com/TheFly/p/11977916.html
Copyright © 2020-2023  润新知