OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5601|回复: 0

明德扬FPGA连载课程第一阶段第三章VERILOG(2)

[复制链接]

31

主题

36

帖子

0

精华

初级会员

Rank: 2

积分
142
金钱
142
注册时间
2018-7-19
在线时间
8 小时
发表于 2018-11-6 09:22:21 | 显示全部楼层 |阅读模式
3.2 数字3.2.1数字表示方式
      在Verilog中的数字表示方式,最常用的格式是:<</span>位宽>’<</span>基数><</span>数值>,如,4’b1011。
      位宽:描述常量所含位数的十进制整数,是可选项。4’b1011中的4就是位宽。通俗理解就是4根线。如果没有这一项,可以从常量的值推断出。例如’b1011可知位宽是4;’b10010可推断出位宽为5。
       基数:表示数值是多少进制。可以是b,B,d,D,o,O,h或者H,分别表示二进制、十进制、八进制和十六进制。如果没有此项,则缺省默认为十进制数。
      例如,二进制的4’b1011,可以写成十进制的4’d11,也可以写成十六进制的4’hb,或者八进制的4’o13,还可以不写基数直接写成11。不管怎么样,只要二进数相同,写成十进制、八进制和十六进制,都是同样的数字。
      


3.2.2二进制是基础
      在数字电路中,如果芯片A给芯片B传递数据,例如传递0或者1信息。可以将芯片A和芯片B,通过一个管脚进行相连。然后由芯片A控制该管脚为高电平或者低电平,通过高低电平来表示0和1。例如,芯片B检测到该管脚为低电平时,表示收到0;当芯片B检测到该管脚为高电平时,表示收到1。




      如果用低电平表示收到1,用高电平表示收到0,这可不可以呢?当然可以,只要芯片A和芯片B事先协定。芯片A要发数字1时,会将该管脚置为低电平。芯片B检测到该管脚为低电平,知道收到了数字1,通信完成。





       一个管脚拥有高低电平两种状态,可以分别表示数字0和1两种情况。如果芯片A要发数字0、1、2、3给芯片B,那该怎么办呢?
       可以让芯片A和芯片B连接两根管脚,即两条线,a和b。当两条线都为低电平时,表示发送数字0;当a为高电平,b为低电平时,表示发送数字1;当a为低电平,b为高电平时,表示发送数字2;当两条线都是高电平时,表示数字3。





       按照同样的道理,芯片A要发送数据4,5,6,7给芯片B的时候,只要再添加一条线就可以了。三根线一共有8种状态,可以表示8个数字。
      综上所述,我们可能通过线的不同电平状态,表示不同的含义。有多少个不同状态,就可以表示多少个数字。


       如果芯片A要发送+1,-1,0,+2等数字给芯片B,这里有正负了,那又该如何表示呢?参考前面的思想,线的高低电平表示的含义,是由芯片双方向事先约定好的。既然是这样,那么我们拿一根线出来,例如低电平表示正数,高电平表示负数。



       上面就是三根线,我们用线c表示正负,0表示正数,1表示负数。用线a和线b表示数值。

       3’b111,可以解释为十进制数7,也可以解释为有符号数原码“-3”,也可以解释为有符号数补码“-1”,这取决于工程师对二进制数的定义。只要这个定义不影响到电路之间的通信那就绝对不会有问题。
       所以,数字中的“0”和“1”不仅可以表示含义,也可以表示其他意义,如正负符号等。同样的道理,

       在数字电路中,二进制数是其他如八进制、十进制、十六进制、有符号数、无符号数、小数等的根本。在FPGA设计中,不清楚小数、有符号数的计算方法,最根本的原因是不清楚这些数据所对应的二进制值。只要理解了它所对应的二进制值,很多问题都可以解决。
      例如,有初学者经常问,FPGA中如何实现小数计算,如“0.5+0.25”这个功能。
首先,众所周知的,0.5+0.25的结果为0.75。
      其次,我们可以考虑,0.5、0.25和0.75用二进制该如何表示?这取决于工程师的做法,因为这种表示方法有很多种,例如定点小数,浮点小数,甚至如前面所讨论,用几根线自行来定义,只要能正常通信,那就绝对没有问题。
      假设,某工程师用三根线,自行定义了二进制值所表示的小数值。



      为了说明二进制值的意义是可以随便定义的,我特意将数字顺序打乱。当然,有读者可能说为什么只有这几种小数呢?这是因为我假定本系统就只有这几种数字,如果想表示更多数字,那就增加线就行了。
      有了上面定义之后,要实现“0.5+0.25”就很容易了,其实就是3’b001和3’b100“相加”,期望得到3’b010。如果我们直接使用3’b001 + 3’b100,结果为“101”了,不是想要的结果。那怎么办呢?可以这么写:



     当然,这是其中一种写法。总之,只要能实现所对应的功能,结果正确就可以。
有读者问,按上面的表格0.1+0.8应该为0.9,但上面没有0.9的表示。这个其实是设计者这个表格定义有缺陷,或者设计者认为不会出现这个情况吧。总之,笔者要表达的是,只要定义好所对应的二进制数,很多功能是很容易设计的。

      当然,实际的工程中,我们通常会遵守约定成俗的做法,没必要自己搞得另类。例如下面是常用的定点小数的定义。


      现在要实现0+0.5=0.5,也就是3’b000和3’b100相加,期望能得到3’b100。我们发现直接用二进制3’b000+3’b100就可以得到3’b100。
      要实现0.125+0.75=0.8725,也就是3’b001和3’b110相加,期望能得到3’b111。我们发现直接用二进制3’b001+3’b110就可以得到3’b111。  
      要0.5+0.75=1.25,这个1.25已经超出了表示范围,要不就增加信号位宽,要不只能表示小数位。如果只是表示小数位,那结果就是0.25。也就是3’b100和3’b110相加,期望得到3’b010。我们发现3’b100 + 3’b110 = 4’b1010,用3位表示就是3’b010,也就是0.25了。
      综上所述,对于定点小数的计算很简单,就是直接相加。

3.2.3不定态
      前面讲过,数字电路只有高电平和低电平,分别表示1和0。但代码中经常能看到x和z,如1’bx,1’bz。那么这个x和z是什么电平呢?答案是没有实际的电平来对应。这个x和z是更多地用来表示设计者的意图或者用于仿真目的,告诉仿真器和综合器怎么解释这段代码。
      X态,称之为不定态,设计者常用于判断条件,用于告诉综合工具,设计者不关心它的电平是多少,是0还是1都可以。



上面的例子,条件是din==4’b10x0,这个条件等价于din==4’b1000||din==4’b1010,其中“||”是“或”符号。


      明德扬则建议,直接写成din==4’b1000||din==4’b1010,好于写成“din==4’b10x0”,直接简单明了。
      仿真的时候,有些信号产生了不定态,那么设计者就要认真分析,这个不定态是不是应该的。如果真的不关心它是0还是1,那么可以不解决。但明德扬建议,所有信号都不应该处于不定态,是0还是1,写清楚,不要给设计添加“思考”的麻烦。
3.2.4高阻态
      Z态,一般称之为高阻态,表示设计者不驱动这个信号(既不给0也不给1),通常用于三态门接口当中。


      上图就是三态总线的应用案例。图中的连接总线对于CPU和FPGA来说,既当作输入又当作输出,是双向接口。一般的硬件电路中,会将该线接上一个上拉电阻(弱上拉)或下拉电阻(弱下拉)。
      当CPU和FPGA都不驱动该总线时,A点保持为高电平。当FPGA不驱动该总线,CPU驱动该总线时,A点的值就由CPU决定。当CPU不驱动该总线,FPGA驱动该总线时,A点的值就由FPGA决定。FPGA和CPU不能同时驱动该总线,否则A的电平就不确定了。通常FPGA和CPU何时驱动总线,是按协议事先协商好的。


      上图是典型的I2C的时序。I2C的总线SDA就是一个三态信号。I2C协议已规定好上面的时间中,哪段时间是由主设备驱动,哪段时间是由从设备驱动,双方都要遵守协议,不能存在同时驱动的情况。

      那么FPGA在设计中,是如何做到“不驱动”这一行为呢?这是因为FPGA内部有三态门。



      三态门是一个硬件,上图是它的典型结构。三态门有四个接口,例如上图中的写使能wr_en、写数据wr_data、读数据rd_data和与外面器件相连的三态信号data。
      注意写使能信号,当该信号有效时,三态门会将wr_data的值赋给三态线data,此时data的值由wr_data决定,当wr_data为0时,data值就为0;当wr_data为1时,data值就为1。
      当写使能信号无效时,则不管wr_data值是多少,都不会对外面的data值有影响,也就是不驱动。
      那么在Verilog中,是通过如下两行代码来描述这一功能的。



      综合器看到这两行代码,就知道要综合成三态门了。
      这个高阻z的作用就在于这里。而且注意到,硬件上用三态线是为了减少管脚,而在FPGA内部没有必要减少连线,所以使用三态信号是没有意义的。
      也就是说,明德扬的设计建议,FPGA内部不要使用高阻态“z”,没有必要给自己添加“思考”的麻烦。当然,使用了也不会报错,也能实现功能。
      总结一点,高阻态“z”是表示“不驱动总线”这个行为,实际上数字电路就是高电平或者低电平,不存在其他电平的情况。

3.2 数据类型
      Verilog HDL的信号类型有很多种,但主要包括两种数据类型:线网类型(net type) 和寄存器类型(reg type)。明德扬的设计,也是只会使用这两个类型。

3.2.1线网类型wire
      线网类型用于对结构化器件之间的物理连线的建模。如器件的管脚,内部器件如与门的输出等。以上面的加法器为例,输入信号A,B是由外部器件所驱动,异或门X1的输出S1是与异或门X2输入脚相连的物理连接线,它由异或门X1所驱动。
      由于线网类型代表的是物理连接线,因此它不存贮逻辑值。必须由器件所驱动。通常由assign 进行赋值。如 assign  A =  B ^ C;

      wire 类型定义语法如下:
      wire [msb: lsb] wire1, wire2, . . .,wireN;
&#216;  msb 和lsb 定义了范围,表示了位宽。例如[7:0]是8位位宽,也就是可以表示成8’b0至8’b1111_1111;
&#216;  msb和lsb必须为常数值;
&#216;  如果没有定义范围,缺省值为1位;
&#216;  信号没有定义数据类型时,缺省为wire 类型。
&#216;  对数组类型,请按降序方式,如[7:0] ;不要写成[0:7]。

      wire [3:0]   Sat; // S a t 为4 位线型信号
      wire Cnt; //1 位线型信号
      wire [0:31]   Kisp, Pisp, Lisp ;// Kisp, Pisp, Lisp 都是32位的线型信号,不建议这样定义。

3.3.2寄存器类型reg
      reg 是最常用的寄存器类型,寄存器类型通常用于对存储单元的描述,如D型触发器、ROM 等。存储器类型的信号当在某种触发机制下分配了一个值,在分配下一个值之时保留原值。但必须注意的是,reg 类型的变量,不一定是存储单元,如在always 语句中进行描述的必须用reg 类型的变量。
      reg 类型定义语法如下:
      reg  [msb: lsb] reg1, reg2, . . . r e g N;
&#216;  msb 和lsb 定义了范围,表示了位宽。例如[7:0]是8位位宽,也就是可以表示成8’b0至8’b1111_1111;
&#216;  msb和lsb必须为常数值;
&#216;  如果没有定义范围,缺省值为1位;
&#216;  信号没有定义数据类型时,缺省为wire 类型,不是reg型。
&#216;  对数组类型,请按降序方式,如[7:0] ;不要写成[0:7]。

例如:
reg [3:0]   Sat; // S a t 为4 位寄存器。
reg Cnt; //1 位寄存器。
reg [1:32]  Kisp, Pisp, Lisp ;

3.3.3Wire和reg定义的场合区分
      Reg型信号不一定生成寄存器。那么什么时候使用wire类型,什么时候用reg类型,明德扬总结出一套方法:在本模块中,使用always设计的信号都定义为reg型;其他都用wire型。



       上面代码中,cnt1是用always设计的,所以要用reg型。Add_cnt1和end_cnt不是由always产生的,所以定义为wire型。





正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-23 01:47

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表