【0基础学会Verilog】001. C语言函数到Verilog模块

多数FPGAVerilog,VHDL相关教程都强调学习的前提条件是至少学习了数字电路设计等先修课程,另外大部分教程都偏重数字电路底层逻辑分析,使得FPGA设计的学习门槛很高,让多数希望入门的同学望而却步。然而,现在的FPGA芯片资源越来越丰富,进行算法设计的抽象层次越来越高,对于底层逻辑并不需要过多理解也可以进行FPGA算法设计;当然,如果要成为真正的FPGA设计的高手,数字电路等硬件相关的知识还是必不可少的,但这应该是入门之后的继续修炼,而不应该是入门学习的门槛。

fpga_icon

Verilog是进行FPGA设计的主流硬件描述语言(HDL,Hardware Description Language)之一,与软件设计语言C语言形式上有较多的相似之处,但本质上不同。软件设计的思维模式和硬件设计的思维模式不一样,有一个转换的过程。进行FPGA设计,除了传统的连线、芯片控制、IO端口控制等功能外,更主要的是将一些基于软件设计语言如C/C++,python等或仿真语言如Matlab等验证通过的算法转换为硬件模块,以期提高运算速度。

本文将一个用C语言描述的简单算术运算函数(function)转换为用硬件描述语言Verilog描述的功能模块(module),以此为例帮助同学们初步认识FPGA设计的主要语言Verilog,熟悉相关语法,了解软件设计思维和硬件设计思维的不同之处。


1. C语言函数

下面是一个很简单的C语言函数,功能是计算表达式((a+b) - c * 8)的值。我们先分析这个函数,再将其转换为Verilog描述的硬件模块。

1.1 一个简单的C语言函数

//C语言函数,计算表达式((a+b) - c * 8)的值
int calc (int a, int b, int c)
{
    int sum = 0;
    sum = (a+b) - c * 8;
    return sum;
}

先对这个函数calc()进行分析,注意这几点:

  • 函数名calc;
  • 有两个输入参数int a, int b;
  • 花括号{}内部是函数体,实现具体功能计算,即计算表达式`(a+b) - c * 8``的值;
  • 计算结果sum返回值的形式告知函数的调用者;

换一个角度来看这个问题,C语言函数calc()就是一个如图1所示实现指定功能的模块,这个功能模块接收外部输入的数据a,b,进行相应的运算,即计算表达式(a+b) - c * 8得到结果sum,最后将运算结果输出到外部模块。

从这个角度来看,硬件设计模块和软件设计模块的结构形式是一致的,都是接收外部输入,执行相关运算,对外输出结果三个部分。

在这里插入图片描述

图1. 功能模块结构示意图

1.2 修改函数的参数形式

下面我们用Verilog语言实现calc()函数同样的功能。 因为Verilog语言没有类似C语言函数返回值这样的概念,我们使用指针calc()函数的参数传递形式稍作修改完成同样的功能,并将新函数命名为calc_v2以示区别。

//使用指针的C语言函数,计算表达式(a+b) - c * 8的值
void calc_v2 (
    int a, 
    int b, 
    int c, 
    int* sum)
{
    int tmp = 0;
    tmp = (a+b) - c * 8;
    *sum = tmp;
}

可以看出,函数calc_v2实现与calc完全相同的功能,形式上将输入参数和输出参数都放在圆括号( )内的参数列表中。本例中用int* sum对外传递函数结果。函数返回值类型设为void,不再用于传递函数结果,这样与我们即将编写的Verilog模块在形式上保持一致。


2. C语言函数转换为Verilog模块

实现上面的calc_v2()模块相同功能,Verilog有两种主要的实现思路,一种是使用组合逻辑实现,另一种是使用时序逻辑实现。有关组合逻辑时序逻辑的概念我们暂时不详细叙述,直接用示例代码进行解释。本例我们用组合逻辑实现与calc_v2()模块相同的功能,我们将Verilog描述的功能模块命名为calc_wire以示区别。

2.1 Verilog模块代码实现

//用组合逻辑实现与calc_v2()函数相同的功能
module calc_wire(
    input wire [31:0] a,
    input wire [31:0] b,
    input wire [31:0] c,
    output wire [31:0] sum
);
    wire [31:0] tmp;
    assign tmp = (a+b) - c * 8;
    assign sum = tmp;
endmodule

2.2 Verilog模块代码分析

上面的Verilog模块calc_wire实现了与C语言函数calc_v2()完全相同的功能,接收a,b两个输入数据,对其进行相关运算(assign tmp = (a+b) - c * 8),最后将结果输出到外部模块(assign sum = tmp)。

接下来我们对calc_wire代码进行详细分析:

  • 第1行用双斜杠//开头的一段文字是注释,这与C语言相同。实际上,同C语言一样,Verilog也可以用/*...*/进行一整段注释;

  • 第2行用关键词module开头,紧跟其后的是模块名calc_wire,类似C语言的函数名,命名规则也与C语言基本相同;

  • 第3行到第5行包含在圆括号()内部,是模块的参数列表,与C语言相似,a,b是输入参数,sum是输出参数;

  • 第6行圆括号()后紧跟一个分号;表示模块的参数列表到此为止,后面就是具体的功能代码了;

  • 第7行声明了一个wire型的变量tmp,它的数据宽度是32比特。回顾一下,C语言int类型是不是也是32比特啊;

  • 第8行执行我们的运算,即求输入参数ab的和,并赋值给我们已经声明的变量tmp;

  • 第9行将运算结果tmp赋值给接口的输出参数sum,也就是将运算结果传递到模块之外;

  • 第10行以关键词endmodule结束模块代码


3. C语言函数与Verilog语言模块的联系与区别

从上面的分析可以看出,Verilog版的calc_wire()模块C语言版的calc_v2函数形式上比较相似,同时也可以有不少不同之处。实际上Verilog在语法上源于C语言,借鉴了很多C语言的做法。但本质上,由于Verilog面向的是资源需求相对紧张的硬件模块设计,所以也有很多不同的地方。

3.1 C语言函数与Verilog语言模块的相似处

Verilog在语法上源于C语言,借鉴了很多C语言的做法,我们总结一下。

  • C语言函数和Verilog模块在结构形式上一致的,都是参数列表+运算代码的形式;
  • C语言运算符和Verilog模块运算符大部分都是相似的,比如本例的+, - , *等;
  • C语言表达式和Verilog模块表达式的形式上也大部分相似,比如本例就完全一致;
  • C语言和Verilog的注释形式是完全一致的;
  • C语言和Verilog的很多关键词控制结构都是相似的。

3.2 C语言函数与Verilog语言模块的不同之处

  • 结构形式的区别

    • C语言函数名calc_v2加花括号{...}表示;
    • Verilog用关键词module模块名calc_wire表示功能模块,并以关键词endmodule结尾;
  • 输入输出参数表达形式的区别

    • C语言函数的参数列表没有明确的方向说明,一个参数是输入还是输出依靠另外的函数说明进行约定;
    • Verilog模块的所用参数都有明确的方向,这里input表示该参数是外部输入数据,output表示该参数是对外输出的运算结果。
  • 变量声明形式的区别

    • C语言int,char,short之类关键词声明变量。例如int tmp表示声明一个整型变量;

    • Verilog使用wire [31:0]这种位宽形式声明变量。例如wire [31:0] tmp表示声明一个位宽为32bit的wire型变量。

    • Verilog有两种主要的变量类型,wire(线网类型)和reg(寄存器类型),其中wire类型主要用于组合逻辑中,reg主要用于时序逻辑中。

    • 为了节省硬件资源,Verilog使用位宽形式[msb:lsb]来精准声明变量所占比特数。wire [31:0] tmp声明一个位宽为32bit的wire型变量,而wire [3:0] dat声明一个位宽为4bit的wire型变量dat,表达的值的范围在0~15之间。

  • 赋值形式有区别

    • C语言使用赋值符=将右边表达式(a+b)-c*8的值赋给左边的变量tmp;

    tmp = (a+b) - c * 8;

    • Verilog使用关键词assign和赋值符=将右边表达式(a+b)-c*8的值赋给左边的变量tmp;

    assign tmp = (a + b) - c * 8;

从上面的分析可以看出,Verilog语言的写法与C语言写法在结构上有很多相似之处,但在细节上也有较多区别。当然从编程的思维看,C语言是软件设计语言,依序串行执行每条语句,而Verilog是硬件描述语言(HDL),用于指导生成FPGA的硬件模块。FPGA的硬件模块都是并行执行的,这是软件思维和硬件思维最大的区别,以后我们会逐步体会。


4 知识点总结

我们用Verilog语言实现与C语言函数相同功能的一个模块,以此为例给大家揭开了FPGA设计的重要工具–硬件描述语言Verilog的神秘面纱。只要你有一定的C语言基础,大致理解上面的示例代码是不太困难的。

Verilog在语法上源于C语言,借鉴了很多C语言的做法。但本质上,由于Verilog面向的是资源需求相对紧张的硬件模块设计,所以也有很多不同的地方。特别强调Verilog语言几个要点:

  • Verilog用关键词moduleendmodule表示模块结构;
  • Verilog用精准的位宽形式声明变量以便节省硬件资源,类似wire [7:0] dat表示声明一个8比特位宽的wire型变量dat
  • Verilog使用assignwire型变量赋值,形如assign dat = (a + b)表示将a+b的结果值赋给变量dat
  • Verilog模块的输入和输出参数都有明确的方向,input表示输入,output表示输出。

本篇是[0基础学会Verilog]专栏的一部分。本专栏以一系列0基础FPGA相关教程致力于帮助有一定C语言软件设计基础的同学走进FPGA设计的大门,包括Verilog语言教程FPGA设计基础FPGA设计案例分析等。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值