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语句的判断先判断条件,执行语句的优先级都是相同的;
原文地址:https://www.cnblogs.com/caesura-k/p/13261178.html