第7章拿下常量数据类型
在常量中,主要有以下三个门派:整数型、实数型、字符串型。这三大门派都是由0、1、X、Z这四种基本元素组成,其中,0代表逻辑0或“假”,1代表逻辑1或“真”,X代表未知,Z代表高阻。
7.1我该如何表达整数
7.1.1 Verilog中整数长啥样?
我们从小学开始就知道什么是整数,但是在Verilog HDL中,怎么表达这个陪伴我们多年的整数呢?在整数中,有以下4种表示形式:
特别地,在Verilog HDL中十六进制数用一个简单代号h或H表示,但在C语言中,用0x表示十六进制数,不要混淆。
整数一般用下面这种方式描述自己:
<位宽>’<进制><数字>:全面的描述方式,具体表示如下所示。
8’d23 //位宽为8的整数23的十进制表示
8’b00010111 //位宽为8的整数23的二进制表示
8’o27 //位宽为8的整数23的八进制表示
8’h17 //位宽为8的整数23的十六进制表示
<位宽>是从二进制的角度而言,需要与实际数值的位宽相符,当位宽数小于数值的实际位数时,相应的高位部分被省略;当位宽大于数值的实际位数且最高位为0或者1时,则高位部分补0,最高位为X则高位部分补X,最高位为Z则高位部分补Z。如:
4’b110010 //等价于4’b0010
8’b10110 //等价于8’b00010110
8’b00111 //等价于8’b00000111
5’bx11 //等价于5’bxxx11
6’bz001 //等价于6’bzzz001
7.1.2负数
在Verilog HDL中,负数是按照补码的方式存储的,其表示方法跟我们所认识的负数一样,只需要在整数的表达式前面加上负号。
例如:-8’d32
特别地,在表示一个整数的相反数时,可以通过按位取反再加1来实现,如下图所示:
图中,输入信号按时钟加1,输出信号是将输入信号按位取反加1得到,例如由红线圈出来的数字8,作为输入信号定义为8’b0001000,按位取反加1得到8’b1111000,即-8。由此可见,输出信号为输入信号的相反数。
7.1.3X和Z之间的事
在数字电路中, 0和1是我们能见到的常态,X和Z却扮演着特殊的角色,X代表不定值,Z代表高阻值,Z也可以写作“?”,通常用在case表达式中,这样比较醒目。这四种逻辑值的含义如图所示:
二进制的一个Z或X表示一位Z或X,八进制的一个Z或X表示三位都处于Z或X,十六进制的一个Z或X表示四位都处于Z或X。如:
5’b0110X:位宽为5的二进制数,最低1位为不确定值
16’h4a5z:位宽为16的十六进制数,最低4位为高阻值
当这种特殊角色的X和Z遇到运算时,它们会擦出怎样的火花呢?下面就举两个简单运算的例子作为说明,计算结果都是通过仿真工具Modelsim得到的。如:
datain & b1:
上图中,datain是输入信号,每个时钟加1,b1是常量8’b0110111x,dataout为计算结果。由仿真结果可知,当x参与运算时,与“0”、“1”相同,即0 & x得到0,1 & x得到x。
datain & b2:
上图中,datain是输入信号,每个时钟加1,b2是常量8’b011011z1,dataout为计算结果。由仿真结果可知,当z参与运算时,最终还是变成x,即0 & z得到0,1 & z得到x。
由此可知,x和z在参与运算时结果会有所不同(具体每种运算对应的结果将会在介绍运算符时再阐述),可是,在真实的电路中这4种逻辑值又扮演着怎样的角色呢?
其实,在综合工具中或者实际的电路中,并没有x值,只存在0、1、z三种状态,或者亚稳态,它既不是0也不是1。(MOS管形成数字电路0 1 z的电压值,z可能与上下拉阻值相关)
7.1.4变与不变的参量型
parameter是Verilog HDL中常用来定义常量的参数类型,大家可能都觉得它定义的参数就是不变的,其实,parameter具有两面性,就像一个硬币有正反面,投射到parameter上,一面是不变的,另一面是可变的,我们先一起看看它的基本用法吧。
parameter经常用于定义延迟时间和变量宽度。用parameter定义的赋值语句的右边必须是一个常数表达式,即该表达式只能包含数字或先前已定义过的参数。如下例所示:
parameter sqrt= 16'b0110011110110001; //定义参数sqrt为16位位宽的二进制数
parameter a=8’h14,b=sqrt+1; //用常数表达式对b赋值
上面介绍了parameter的基本用法,看上去被它定义过的参数都是不变的,确实,parameter定义的参数是本地的,其定义在本模块内是有效的,即在本模块内该参数的值保持不变。可是,parameter什么时候又是变的呢?
答案就在于当模块或实例引用时可通过参数传递改变在被引用模块或实例中已定义的参数。具体来说,举个例子吧。
module Demodulate(Symbol,DataBit);
parameter Width=4, Mode=3,Length=6;
.
.
.
endmodule
module Top;
wire [7:0] Symbol1, Symbol2,Symbol3, Symbol4;
wire [7:0] DataBit1, DataBit2,DataBit3, DataBit4;
Demodulate #(5,2 ,8) Demod1(Symbol1, DataBit1); //right
Demodulate #(3,2) Demod2(Symbol2, DataBit2); //right
Demodulate #(2) Demod3(Symbol3, DataBit3); //right
Demodulate #(6, ,7) Demod4(Symbol4, DataBit4); //wrong
Endmodule
上例中,先定义一个解调模块Demodulate,端口参数为Symbol、DataBit,其中用parameter定义内部参数Width、Mode、Length,在顶层模块Top中四次引用Demodulate实例时,通过参数的传递来改变定义时已规定的参数值,即通过#(5,2,8)向Demod1调用的是参数Width=5、Mode=2、Length=8的Demodulate模块,通过#(3,2)向Demod2调用的是参数Width=3、Mode=2、Length=6的Demodulate模块,通过#(2) 向Demod3调用的是参数Width=2、Mode=3、Length=6的Demodulate模块,于是,有人想这下省事儿了,不用给所有参数赋新值,就出现了Demod4这样的调用情况,确实,我们在顶层文件中不用对parameter定义的所有参数都赋新值,但是也不能跳跃赋值,所以Demod4这样的使用方法是错误的。也就是说,#()中的参数由前向后依次与模块中用parameter定义的参数的先后顺序对应,当相应位缺少时,就保持parameter定义的大小,但顺序不变。
如果只想改变前后的参数,不想变中间的参数,即在本例中只想改变Width和Length的大小,却想保留Mode的值,又怎么办呢?那就只能把所有的参数都写全,这个事省不了,该出手时就得出手啊。这种利用参数编写模块的方法使得已编写的底层模块具有更大的灵活性,尤其是当底层模块较多,并且每个底层模块parameter参数也较多时,当在顶层调用这些模块并且又要修改其中的参数时,这种方法的优越性立竿见影,就不用挨个到底层去修改啦,还减少了修改出错的概率,既省马达又省电啊。
可见,parameter参数型也不是一层不变的,它也会根据用户的需求做出相应的改变,各种灵活应变,让写程序的人都不得不为之赞叹啊。不管怎样,形成电路后,变或者不变,参数就在那里,不偏不倚。
7.1.5我和整数的约定
1.下划线来也
为了方便我们清晰的阅读程序,同时也为了减小犯低级错误的概率,下划线出现了。它可以用来分隔数的表达,通常用在数值表达式较长的情况下,但是下划线只能放在具体数值的中间,不可以放在位宽和进制处。例如:
16’b0100_1011_1101_0110 //正确的表达
16’b_1110_1101_0101_0001 //错误,不能放在第一个数的前面,只能放在数值中间
2.位宽去哪儿了
在上面7.1.1我们讲述了整数的一般描述方式,可是孰知它还存一种懒人适用的方式。(比喻不是很恰当,直接说成懒的方式,同时强调这种偷懒不写位宽可能造成一些问题特别举例拼接符超过32位情况),如以下两种方式:
(1)’<进制><数字>:缺省位宽,约定为32位宽度。如:
‘d23 //位宽为32的整数23的十进制表示
‘b10111 //位宽为32的整数23的二进制表示
‘o27 //位宽为32的整数23的八进制表示
‘h17 //位宽为32的整数23的十六进制表示
(2)<数字>:缺省进制,约定为十进制,32位宽度,也可以用科学浮点数表示。如:
23 //位宽为32的整数23的十进制表示
说明:这种方式默认是十进制,其它方式都需要说明进制。
原来,省去的位宽我们都约定为32位,这样便于大家对整数的定义,最终如何表达还是要根据自己编程的习惯来选择。
7.2实数常量是几许人物
实数常量可以用十进制表示也可以用科学浮点数表示,如下例所示。
12 //表示12
23e-4 //表示0.0023
23E4 //表示230000。
有人不禁会问,怎么还有科学计数法,而且还能表示小数呢?其实,在实际中,FPGA是不能识别科学计数法的,更不能直接处理小数,它在实际中表示0,如图所示。
图中,分别定义xiaoshu0=23e-2,xiaoshu1=23e-4,xiaoshu2=23e4,xiaoshu3=23e6,这个四个值xiaoshu0,xiaoshu1,xiaoshu2,xiaoshu3都是用科学定义法表示,分别代表0.23、0.0023、230000、23000000,由图中圈红的地方可见,这四个数在Verilog中都为0,所以,不管是小数还是整数,用科学定义法表示的数在Verilog中都表示0。
7.3 字符串是怎么回事
知道C语言中字符串是怎么回事的人,差不多就知道这里的字符串啦。Verilog语言中,字符串是双引号内的字符序列,常常用于命令内需要显示的信息,不能分成多行书写,可以使用一些C语言的转义符,如\n、\t等等,同时,它还可以使用一些C语言中的各种数值型式控制符,如:%b(二进制)、%o(八进制)、%d(十进制)、%h(十六进制)、%t(时间类型)、%s (字符串类型)。
用户805261 2014-10-29 11:22
用户377235 2014-10-27 17:05
千里之行始于足下,脚踏实地地做好每一项工作,才是走向成功的基础
用户365214 2013-7-23 15:29
xucun915_925777961 2013-7-3 08:30
用户1670807 2013-7-2 16:43
用户1670807 2013-7-2 16:41
用户1670807 2013-7-2 12:19
用户436802 2013-5-30 09:18
用户377235 2013-4-12 10:00
有同感,好文章。
handong123123_906892115 2013-3-21 16:23