• FPGA:verilogHDL简单小结


    FPGA(Field Programmable Gate Array)现场 可编程 逻辑门 阵列;

      是主要使用逻辑门(LE)和查找表(LUT)来生成逻辑电路的器件,还包含可编程逻辑,互连线,寄存器等资源;

      veilog HDL(hardware description language)硬件描述语言是通过描述硬件来产生与之相对应的硬件电路的语言;是FPGA的主要语言之一;

    硬件描述语言和软件编程语言有什么区别呢?

      软件编程语言编译之后是工作在堆栈和内存上,是对堆栈和内存的数据处理,逻辑上数据的处理是单线程的;

      硬件描述语言编译之后得到的是硬件电路,是具体的物理电路连接,器件间信号的传递可以实现并行执行;

    1 verilog模块

    verilog代码是以模块为最小仿真单位存在;可以将特定的逻辑功能封装成模块,在顶层模块中对子模块实例化来调用子模块,组成完整的项目;

    verilog模块主要由三部分组成:端口声明A,内部信号量声明B,功能定义C;

    每个.v文件就是一个verilog模块,.v文件的名字要与module中定义的名字相同;以下为两个module举例:

    /***********顶层模块:trist1.v***********************/
    /***********以下使用三态驱动器模块举例*****************/
    /***********以下的功能定义部分C1,C2,C3是并行执行的******/
    module trist1
    (
        output out,         //A:端口声明;
        input in,          //A:
        input enable        //A:默认端口声明变量是wire型;
     );
    wire [2:0] con1;        //B:内部信号声明;
    wire [7:0] con2;        //B
    wire[7:0] con;         //B
    reg [7:0] and_con;       //B
    
    mytri tri_inst(out,in,enable);    //C1:调用子模块mytri,
    assign con={con1[2:0],con2[4:0]}   //C2:组合逻辑,always块也属于组合逻辑;
    and and_inst(and_sum,con1,con2);   //C3:使用实例元件and,
    endmodule
    
    
    /******** 子模块 mytri.v***************************/
    module mytri                    //子模块被顶层trist1.v调用时,会生成实例元件tri_inst;实例元件的名字必须具有唯一性;
    (
        output out,
        input in,
        input enable
    );
    assign out= enable? in:1'bz;    //assign声明组合逻辑;组合逻辑中被赋值的变量必须是net型;wire型属于net型;
    endmodule    

    2数据类型

    verilog共有19种数据类型,如large、medium、scalared、time、small、tri、trio、tri1、triand、trior、trireg、vectored、wand、wor等

    其中reg、wire、interger、parameter为基本的四种数据类型;以下简单说明一下;

      2.1 parameter

        parameter用来定义常量;先了解一下常量在verilog中的表达方式,然后再了解一下parameter如何使用;

        2.1.1 常量的表示方式

    /*常量由位宽,进制,具体数值组成,中间不能加空格;
    **位宽表示当前常量用二进制表示的具体位数,
    **进制表示后面的具体数值使用的进制,
    **具体数值表示实际值;在数字电路中,x代表不定值,z代表高阻值;
    **常量的位宽和进制缺省时,默认是32位位宽,10进制;*/
    parameter NUM1 = 8'b11001000;  
    // 8表示二进制位数为8;'b表示为当前常量使用2进制表示; 1100 1000为具体数值;
    
    parameter NUM2 = 8'hc8;      
    // 8表示为二进制位数为8,'h表示当前常量使用16进制表示,c8为具体数值;
    
    parameter NUM3 = 4'b10x0;     
    // 4表示二进制位数为4,'b表示当前常量用2进制表示;10x0为具体数值,其中bit1为不定值;
    
    parameter NUM4 = 8'h4z;      
    // 8表示二进制位数为8,'h表示当前常量用16进制表示;4z为具体数值,其中bit[3:0]为高阻值;
    
    parameter NUM5 = 8'h4?;      
    // 8表示二进制位数为8,'h表示当前常量用16进制表示;4?为具体数值,其中bit[3:0]为高阻值;

        2.1.2 parameter 声明

          可以在模块内声明,也可以在模块外声明;这里应该要补充一下模块外声明原型和调用方式的,等遇见了再补充把;

    //在模块内部定义了一个常量OUT,作用域为当前模块;定义之后可以修改,也可以在被其他模块调用时修改值;
    module param1
    (
        input clk,
        output reg[3:0] sum
    );
        parameter OUT= 4'b1100;  //模块内声明常数参数,搭配状态机较为常用;
        always@(posedge clk)
        begin
           sum <= OUT;
        end
    endmodule
    
    //在模块外部定义模块使用的常量ADD2;
    module param2
    #(parameter ADD2 = 2'd1)
    (
        input clk,
        input [2:0]din,
        output reg [3:0]sum
    );
        always@(posedge clk)
        begin
            sum <= din+ADD2;
        end
    endmodule

      2.2 wire

        wire属于网络数据类型,相当于电路的物理连线;在逻辑综合中,会被映射为真实的物理连线;

        可以用作任何方程式的数据,组合逻辑或实例元件的输出;

        在assign语句中被赋值的变量必须是wire类型;实例元件被调用后的输出必须赋值给wire型;默认缺省输入输出为wire型;

    wire clk;          //定义了一个wire类型的变量clk;    
    assign clk = sys_clk;   //作为组合逻辑的输出;
    
    wire [7:0] data;        //定义了一个wire类型的变量data;
    ins instance1(
      .inst_output (data),  //作为实例元件instance1的输出;
      ...
    );

      2.3 reg

        reg是数据存储单元的抽象,相当于芯片的寄存器;在逻辑综合中,会被映射为真实的物理寄存器;

        在always块内被赋值的信号都必须被定义成reg类型;reg数据缺省值为x不定值;

    reg[7:0] dataByte;always@(posedge clk)
    if(clk)
        dataByte <=revByte;//在always块内被赋值的信号都要为reg型的;

      2.4 memory

        通过组合reg型数据来表示存储器memory,相当于芯片的ram、rom存储器;存储器深度只能为一维数组,数组下标同c一样需要为常量;

    reg[7:0] memFirst[255:0];  //定义了一个寄存器组memFirst,有256个寄存器,其中每个寄存器为8位的reg型;
    memFirst[3]= 0;        //寄存器组的赋值需要给每个单元单独赋值,使用时在逻辑块外单独赋值报错了,放到逻辑块内赋值就可以了;
                     //并且寄存器组的寄存器不能单独使用某些位的数据,如memFirst[7:3];一般定义的寄存器就可以直接取某些位的数值,如data[7:2];
                     //大概在定义的时候,在逻辑块外一起赋值也是可以的;如果单独赋初值就是逻辑块了,可是又不符合逻辑块规则;寄存器也是把;

    3 运算符

      

       等式运算符:"=="和"!="为逻辑等式运算符,逻辑值可为不定值;而"==="和"!=="进行比较时对某些位的不定值x和高阻值z也进行比较,逻辑值非0即真;

      阻塞赋值"=":阻塞赋值的意思是在当前块中,第一条语句的执行会阻塞第二条语句的执行;在当前块中语句顺序赋值,由于电路时序约束可能会出现数据交换意外;

      非阻塞赋值"<=":非阻塞赋值的意思是begin-end块中,第一条语句的执行与否不会阻塞第二条语句的执行;块中语句一起执行完之后再一起赋值,较为常用;

      3.1 组合逻辑与阻塞赋值 " = "

        组合逻辑电路表示输出只与输入有关,与电路当前状态无关;根据电路特性,需要使用阻塞赋值;

        对于阻塞赋值的信号,如果信号在跳变沿变化为状态B,则跳变沿的取值为新的状态B;其他信号在跳变沿对其取样的时候,取样信号为状态B;

      3.2 时序逻辑与非阻塞赋值" <= "

        时序逻辑电路表示输出不仅与输入有关,还与电路当前状态有关;根据电路特性,需要使用非阻塞赋值;

        对于非阻塞赋值的信号,如果信号在跳变沿变化为状态B,跳变沿的取值并没有立即变化,而是为之前的状态A;其他信号在跳变沿对其取样的时候,取样信号为状态A; 

     4 testbench

      quartus软件具有多种仿真功能;可以查看RTL(register transfer level)视图,可以编写VWF波形文件,可以硬件在线调试,也可以通过modelsim等软件仿真信号;

      硬件在线调试:菜单栏Tools >> SignalTap.. >>然后配置一下时钟和查看参数  >> 重新编译工程加载进配置 >> 重新打开SignalTap,加载运行即可查看;

    wire clk_200m/*synthesis keep*/;  //表示仿真时保留该wire类型;
    wire clk_200m;                    //仿真过程中会自动优化wire类型的变量,以至于不能观察wire类型的信号;

      软件仿真调试:编写好tb文件 >> 菜单栏Tools >> setting >> simulation >>配置仿真的testbench文件 >>重新编译后即可查看RTL仿真信号; 

      以下是testbencn文件的基础写法举例;

    /**ipcore_tb.v 模块*****************************************/
    `timescale 1ns/1ps     //`时间单位/时间精度:时间单位表示#的延时单位,时间精度表示执行时间的误差范围;
    module ipcore_tb();    //当前模块为ipcore_tb,模块名等于文件名;
    
    reg sys_clk;
    reg sys_rst_n; 
    initial begin                   //initial为testbench的语法功能,RTL模块中不可以使用;   
        sys_rst_n = 1'b0;
        sys_clk = 1'b0;
        #40 sys_rst_n = 1'b1;       //#40表示延时40ns;
    end
    always #10 sys_clk <= ~sys_clk; //50MHz,20ns一个周期; inst instance_1 ( .sys_clk(sys_clk), .sys_rst_n(sys_rst_n) ); endmodule

     5 简单模块举例

    /**mux.v 二选一多路选择器 **************************************
    *** always@()中的*为通配符,表示当always块内的输入信号发生变化,即执行当前块;*/
    module mux
    (    input in1,
        input in2,
        input sel,
        output reg[0:0] out
    );
    always@(*) begin
        case(sel)
            1'b0: out <= in1;
            1'b1: out <= in2;
            default: out <= out;
        endcase
    end
    endmodule
    
    /**adder.v 加法器 **********************************************/
    module adder (
        input add1,
        input add2,
        output sum,
        output carry
    );    
        assign {carry,sum} = add1 + add2;
    endmodule
    
    /**decode.v  译码器:将输入二进制数,转换成信号输出的逻辑器件;************
    **3-8译码器:将输入的3'bxxx转换成二进制后输出8线;***********************
    **8421BCD译码器:使用4个二进制来表示10进制数***************************/
    module decode
    (
        input reg[2:0] binary_in,
        output reg[7:0] out
    );
    always@(*) begin
        case(binary_in)
            3'b000: out <= 8'b0000_0001;
            3'b001: out <= 8'b0000_0010;
            3'b010: out <= 8'b0000_0100;
            3'b011: out <= 8'b0000_1000;
            3'b100: out <= 8'b0001_0000;
            3'b101: out <= 8'b0010_0000;
            3'b110: out <= 8'b0100_0000;
            3'b111: out <= 8'b1000_0000;
            default: out <= out;
        endcase
    end
    endmodule
    //if语句的判断是顺序执行的,前面的判断优先级要稍微高于后面的语句;
    //case语句的判断先判断条件,执行语句的优先级都是相同的;
  • 相关阅读:
    poj 1840(五元三次方程组)
    Selenium(二)开发环境的搭建
    Selenium(一)自动化测试简介
    (二)AppScan使用教程
    (一)AppScan的安装及破解
    (一)python3.7的安装
    读完《大道至简》后的反思
    BZOJ3585: mex
    BZOJ3544: [ONTAK2010]Creative Accounting
    BZOJ3531: [Sdoi2014]旅行
  • 原文地址:https://www.cnblogs.com/caesura-k/p/13261178.html
Copyright © 2020-2023  润新知