FPGA系统性学习笔记连载_Day6 FPGA三种建模方式区别及Verilog语法基础篇

2021-04-01 10:03:00 浏览数 (1)

FPGA系统性学习笔记连载_Day6 FPGA三种建模方式区别及Verilog语法基础篇

本系列为FPGA系统性学习学员学习笔记整理分享,如有学习或者购买开发板意向,可加交流群联系群主。

连载《叁芯智能fpga设计与研发-第6天》 【FPGA数据流建模、行为级建模、结构化建模 区别】及【Verilog HDL语法基础】

原创作者:紫枫术河 转载请联系群主授权,否则追究责任

这篇文件记录,FPGA的3种建模方式及基本的Verilog HDL语法,内容会根据学习进度,不断更新。

一、FPGA的3种建模方式

A、数据流建模(assign)

在数字电路中,信号经过组合逻辑时会类似于数据流动,即信号从输入流向输出,并不会在其中存储。当输入变化时,总会在一定时间以后体现在输出端。同样,我们可以模拟数字电路的这一特性,对其进行建模,这种建模方式通常被称为数据流建模。

1.1、是使用连续赋值语句(assign)对电路的逻辑功能进行描述,该方式特别便于对组合逻辑电路建模

1.2、连续驱动,连续赋值语句是连续驱动的,也就是说只要输入发生变化,都会导致该语句的重新计算。

1.3、只有线网型的变量才能在assign语句中被赋值

1.4、因为assign语句中被赋值的变量,在仿真器中不会存储其值,所以该变量必须是线网(Nets)类型,不能是寄存器(reg)类型

1.5、线网类型的变量,可以被多重驱动,也就是说可以在多个assign语句中驱动同一个net

1.6、reg型变量,不能被不同的行为进程(eg:always块)驱动

1.7、建议使用assign对组合逻辑建模,这是因为assign语句的连续驱动特点与组合逻辑的行为非常相似,而且在assign语句中加延时可以非常精确地模拟组合逻辑的惯性延时。

1.8、assign语句与行为语句块(always和initial)、其它连续赋值语句、门级模型之间是并行的。

一个连续赋值语句是一个独立的进程,进程之间是并发的,同时也是交织的。

B、行为级建模(initial、always)

行为方式的建模是指采用对信号行为级的描述(不是结构级的描述)的方法来建模。

在表示方面,类似数据流的建模方式,但一般是把用initial 块语句或always 块语句描述的归为行为建模方式。

行为建模方式通常需要借助一些行为级的运算符如加法运算符( ),减法运算符(-)等。

需要先建立以下概念:

  • 2.1、只有寄存器(reg)类型的信号才可以在always和initial 语句中进行赋值,类型定义通过reg语句实现。
  • 2.2、always 语句是一直重复执行,由敏感表(always 语句括号内的变量)中的变量触发。
  • 2.3、always 语句从0 时刻开始。
  • 2.4、在begin 和end 之间的语句是顺序执行,属于串行语句。

C、结构化描述(反映了一个设计的层次结构)

结构化描述就是说在设计中实例化已有的功能模块,这些功能模块包括门原语、用户自定义原语(UDP)和其他模块(module)。

以下是结构化描述的3种实例类型:

  • 3.1、实例化其他模块
  • 3.2、实例化门(如与门and、异或门xor等)
  • 3.3、实例化UDP

二、RTL级、Behavior级

RTL级,register transfer level,指的是用寄存器这一级别的描述方式来描述电路的数据流方式;

Behavior级,(行为级)指的是仅仅描述电路的功能而可以采用任何verilog语法的描述方式。

2种方式的区别:

1、目的区别:

行为级描述:目的是加快仿真速度,做法是尽量减少一个always块中要执行的语句数量,其结果不是为了综合,只关注算法。

有行为综合工具,可以直接将行为级的描述综合为RTL级的,比如Behavioral Compiler。

2、形式区别:

RTL级描述:
  • 目的是为了综合工具能够正确的识别而编写的代码,verilog中有一个可综合的子集,不同的综合工具支持的也有所不同;
  • RTL级的描述就会更详细一些,并且从寄存器的角度,把数据的处理过程表达出来。可以容易地被综合工具综合成电路的形式。
  • 可以采用任何verilog语法 的描述方式。鉴于这个区别,RTL级描述的目标就是可综合。

行为级描述:

更多的是采取直接赋值的形式,只能看出结果,看不出数据流的实际处理过程。其中又大量采用算术运算,延迟等一些无法综合的语句。常常只用于验证仿真。

3、电路区别:

RTL级,register transfer level

指的是用寄存器这一级别的描述方式来描述电路的数据流方式;RTL在很大程度上是对流水线原理图的描述。哪里是组合逻辑,哪里是寄存器,设计者应该了然于胸。

组合逻辑到底如何实现,取决于综合器和限制条件。RTL是晶体管传输级,描述硬件的相互联接关系,一般都可以综合;

Behavior级

指的是仅仅描述电路的功能而在硬件设计中有一句著名的话:thinking of hardware。 简单说,rtl就是用寄存器和组合逻辑组成,不能再用其他construct; behavior就是指定输入和输出之间的关系

4、 同样是for语句,如果循环条件是常数,就是RTL的,如果是变量,就是behavior的。

三、verilog语法基础

1、信号的类别

输入 :input

输出 :output

输入输出 :inout

2、内部信号

寄存器信号(时序逻辑)reg oe;

线网型信号(组合逻辑) wire oe;

3、端口的位宽的类别

位宽为8的输入信号 input [7:0] data

4、连续赋值语句

将右边的值赋值给左边 assign out = sel?a:b;

5、在assign语句中被赋值了的变量,都要申明为 wire类型

代码语言:javascript复制
module and_gate (
      input wire  [3:0] a,
      input wire  [1:0] b,
      output wire s
  );
 
      assign  s = &a;
 
endmodule

6、在initial、always语句中被赋值了的变量,都要申明为 reg类型

代码语言:javascript复制
module and_gate_tb;
    reg tb_a;
    reg tb_b;
  
    initial begin
        tb_a = 1;
        tb_b = 1;
    end
 
endmodule

7、always语法

1>、always @ (posedge clk,negedge rst_n) begin----end

always:表示总是的意思;

@:表示等到的意思;

(posedge clk,negedge rst_n):括号代表对什么敏感,这里就是对时钟上升沿或者rst_n的下降沿敏感;

逗号:或的意思,可以写成 or

注:这句话的意思是,等到 clk的上升沿 或 rst_n下降沿到了,就执行 begin----end之间的语句

代码语言:javascript复制
always @ (posedge clk,negedge rst_n) begin   //异步复位,跟时钟无关
    if(rst_n == 0)
        q <= 0;
    else
        q <= q   1'b1;
end
 
always @ (posedge clk or negedge rst_n) begin   //异步复位,跟时钟无关
    if(rst_n == 0)
        q <= 0;
    else
        q <= q   1'b1;
end

2>、always @ (*) begin---end

(*):代表本always块用到的所有变量,综合工具和仿真工具,会自动将本always块,使用到的所有的敏感信号加入敏感信号列表,当敏感信号状态发生改变,就执行begin--end中的语句

代码语言:javascript复制
always @ (*) begin
    casex (din)
        8'b0000_0001 : bin = 3'b000;  
        8'b0000_0010 : bin = 3'b001;
        8'b0000_0100 : bin = 3'b010;  
        8'b0000_1000 : bin = 3'b011;
        8'b0001_0001 : bin = 3'b100;  
        8'b0010_0001 : bin = 3'b101;
        8'b0100_0001 : bin = 3'b110;
        8'b1000_0000 : bin = 3'b111;
        default      : bin = 3'b000;
    endcase  
end

3>、case语法

代码语言:javascript复制
always @ (*) begin
    casex (din)
        8'b0000_0001 : bin = 3'b000;  
        8'b0000_0010 : bin = 3'b001;
        8'b0000_0100 : bin = 3'b010;  
        8'b0000_1000 : bin = 3'b011;
        8'b0001_0001 : bin = 3'b100;  
        8'b0010_0001 : bin = 3'b101;
        8'b0100_0001 : bin = 3'b110;
        8'b1000_0000 : bin = 3'b111;
        default      : bin = 3'b000;
    endcase  
end

4>、casex语法

casex -- endcase: 成对出现,注意 以下b1xxx_xxxx的写法,是简化写法,1后面的x代表值是随机的不关心的;

例如 8b1xxx_xxxx = 8b1000_1111/8b10101_1010,8b0xxx_xxxx=8b0000_0000/8b01010_0101

代码语言:javascript复制
always @ (*) begin
    casex (din)
        8'b1xxx_xxxx : bin = 3'b111;
        8'b01xx_xxxx : bin = 3'b110;  
        8'b001x_xxxx : bin = 3'b101;
        8'b0001_xxxx : bin = 3'b100;  
        8'b0000_1xxx : bin = 3'b011;
        8'b0000_01xx : bin = 3'b010;  
        8'b0000_001x : bin = 3'b001;
        8'b0000_0001 : bin = 3'b000;
        default      : bin = 3'b000;
    endcase  
end

5>、repeat语法

代码语言:javascript复制
initial begin
    repeat(15) begin
        din = {$random}%6;
        #20;
    end
end

6>、位拼接

代码语言:javascript复制
wire  [7:0]  A;
wire   B;
wire   C;
wire  [1:0]  D;
 
B = A[7];
C = A[6];
D = (B,C);
D = A[7:6];

7>、仿真文件,首先要定义时间精度,因为延时语句以这个作为时间刻度,#代表延时标记

1ns是周期,1ps是精度

注:延时语句是非综合语句,是不能形成电路的

代码语言:javascript复制
`timescale 1ns/1ps
 
module and_gate_tb;
  
    reg [1:0] tb_a;
    reg [1:0] tb_b;
  
    and_gate and_gate_inst(
        .a (tb_a),
        .b (tb_b),
        .s ()
    );
 
    initial begin
        tb_a = 2'b00;
        tb_b = 2'b00;
        #20;
        tb_a = 2'b01;
        tb_b = 2'b10;
    end
 
endmodule

【QQ交流群】

群号:173560979,进群暗语:FPGA技术江湖粉丝。

多年的FPGA企业开发经验,各种通俗易懂的学习资料以及学习方法,浓厚的交流学习氛围,QQ群目前已有1000多名志同道合的小伙伴,无广告纯净模式,给技术交流一片净土,从初学小白到行业精英业界大佬等,从军工领域到民用企业等,从通信、图像处理到人工智能等各个方向应有尽有。

【微信交流群】

现微信交流群已建立09群,人数已达数千人,欢迎关注“FPGA技术江湖”微信公众号,可获取进群方式。

后续会持续更新,带来Vivado、 ISE、Quartus II 、candence等安装相关设计教程,学习资源、项目资源、好文推荐等,希望大侠持续关注。

江湖偌大,继续闯荡,愿大侠一切安好,有缘再见!

0 人点赞