第三章、Verilog 高级语法及用法
casex 和casez 语句 锁存器的生成和利用 Verilog HDL 内置元件的例化
用户自定义的module 例化 例化中的参数传递 Generate 语句 向量的部分选择
casex 和casez 语句
在上一章中,使用了case 语句描述了一个4为输入的二进制数用相应的数字显示到7段数码管上的实例。在这个实例中,仔细考虑din 输入情况。如果在din 的四个位中出现了高阻态z 或者是不定态x 的情况,case 语句如何执行呢?
从语法角度上讲,case 语句在进行敏感表达式和分支值进行比较的时候,每个比较的位都具有4种情况,即:0、1、x 、z 四个不同的值,所以如果有分支选项对应的某个位只要不同,该分支就不会执行。而实际上,从物理意义上讲,高阻态相当于从电路中“断开”,所以这样的位实际过程中是不对电路的执行有影响的,作为分支选项应该考虑这种情况;另外,x 不定态是我们研究电路的时候不关心的,或者说这个位应该是0或1、或者是Z 的其中任何一个,通常在研究过程中这个位对对运算的结果没有影响。例如:假设din 是4位,只要din 的最高位为1,低3位不管为什么都执行某一条语句的情况。所以,verilog 又设计了casez 和casex 语句来解决这两类现象。
casez 和casex 的语法格式和case 完全一样,只是将case 关键字换成了casez 或casex 。
在casez 语句中,如果敏感信号表达式和分支表达式某些位的值(考虑对应二进制的情况下)为高阻态(即z 或Z ),那么对这些位的比较就会忽略,不予考虑,而只关注其他位的比较结果。 例:
这里“?”是高阻态z 的另一种表示形式,它只出现在case 语句的分支选项中!有一种错
casez(sel)
2'b00:out=2'b01; 2'b1?:out=2'b10; 2'b01:out=2'b11; endcase
casex (敏感信号表达式)
值1: 语句1;//case 分支 值2: 语句2; ……
值n : 语句n;
default :语句n+1; endcase
casez (敏感信号表达式)
值1: 语句1;//case 分支 值2: 语句2; ……
值n : 语句n;
default :语句n+1; endcase
误的理解方式认为它是不关心态,要注意!即,它只表示z,不表示x。
若sel的电平值为2’b11或者2’b1z或者2’b10或者2’b1x时,系统会输出out=2’b10。注:此casez语句在Quartus II中仿真结果不一定对,请使用专业的Verilog HDL仿真工具如modelsim去验证该语句的正确性。
在casex语句中,如果敏感信号表达式和分支表达式某些位的值(考虑对应二进制的情况下)为高阻态或不定态(即z或x),则在进行敏感信号表达式和分支值比较时,这些位不参与比较(也就是忽略该位),而只关注其他位的比较结果。
例:
casex(sel)
2'b1x:out=2'b01;
……
endcase
当sel端的值为2’b10或者2’b11或者2’b1z或者2’b1x均有out=2'b01;语句成立。
大多数情况下,一般使用casez语句,case语句用的也较少,强烈不推荐使用casex语句。不过需要说明的是,这三个case语句都是可综合的,都能生成RTL电路。引自《ANAL YSIS OF CASE STATEMENTS》一文。
casez语句的另一种应用------并行case语句。通过实例来说明。
代码如下:
module parallel_case (sel, a, b, c);
input [2:0] sel;
output a, b, c;
reg a, b, c;
always @(sel)
begin
{a, b, c} = 3'b0;
casez (sel)
3'b1??: a = 1'b1;
3'b?1?: b = 1'b1;
3'b??1: c = 1'b1;
endcase
end
endmodule
在这个代码中,如果sel端恰巧是3’b111的情况,那么,a、b和c输出值应该是多少呢?从代码上看,如果sel的高位为高电平的话,a就输出高电平,否则保持初始化值0;同理,当sel的中间位为1的话,b输出1,否则输出初始化值0;当sel的最低位为1的话,c输出1,否则输出0。在接下来的波形仿真验证之前,请准备好自己的答案。先看一下RTL电路:
仿真结果,
从仿真图上我们看出,实际上的输出结果为a 高电平,b 和c 均为低电平。 从语句本身上看,代码的作者想用sel 中的每一个位分别控制a 、b 和c 中的各个引脚的输出电平。而实际上只有第一条语句执行了,因为初始化时候a 、b 、c 均为低,而执行后只有第一条语句运行了,很显然,sel 为111的时候第一条分支语句满足了,于是第一条语句执行,然后case 语句就退出了!它相当于c 语言中的每条分支语句后面的break 的作用!换句话说,这里的case 语句是有优先级的,当第一条分支语句执行后,case 语句跳出,后面的语句都不执行了。
如果说不想让case 语句具有优先级,可以在case 语句的后面加上综合属性,当Quartus II 综合的时候会根据综合属性改变其综合后的结果。//synthesis parallel_case 是并行综合属性,它以注释的形式出现在case 语句后面,它可以使case 语句综合后不在具有优先级,用法见下面代码:
再次综合,看RTL 电路图:
这和刚才的RTL 视图完全不同,可以看出sel 的三个输入分别接到了abc 三个输出端上。仿真图如下:
当sel 为111时候,abc 也都输出了高电平。为其他值时,对应的a 、b 、c 端口也随之变化。
另一个综合属性------full case 语句
在case 语句(含casez 和casex )中,通常使用default 选项来对分支选项中没有列举到的敏感信号表达式的值做统一的处理,从功能上看,如果不需要处理的分支值一般来说是我们不需要的功能,可以不写,但对于综合器来说,如果分支选项不全的话,对FPGA/CPLD 硬件的资源的使用甚至是性能的使用是有影响的。
以下下通过实例演示说明default 选项的作用:
module parallel_case (sel, a, b, c); input [2:0] sel; output a, b, c; reg a, b, c;
always @(sel) begin
{a, b, c} = 3'b0;
casez (sel) //synthesis parallel_case 3'b1??: a = 1'b1; 3'b?1?: b = 1'b1; 3'b??1: c = 1'b1; endcase end
endmodule
module full_case (sel, a, y);
input [1:0] sel;
input [3:0] a;
output reg y;
always @(*)
begin
casez (sel)
2'b00: y = a[0];
2'b01: y = a[1];
2'b10: y = a[2];
endcase
end
endmodule
本代码没有使用default分支项,综合后的RTL视图如下
然后加上default语句,
……
begin
casez (sel)
2'b00: y = a[0];
2'b01: y = a[1];
2'b10: y = a[2];
default:y= 0;
endcase
……
然后再次查看生成的RTL视图,
从语法角度上看,这个case语句实现的是一个多路选择功能,这两个RTL图中也都体现出来了,唯独不同的是,在前一个RTL视图中,还多了一个y$latch!这是一个锁存器。也就是说,当case语句分支选项不全时,综合器会自动生成一个锁存器,一旦敏感表达式不和分支选项值都不同时,电路不会产生新的输出,而是保持上次电路的运行结果。举个例子,假设sel为2’b00,那么y输出的是a[0]的值;而当sel变为2’b11时,由于分支选项没有2’b11,这时候y不会动作,维持之前的a[0]的输出。这个维持就是靠生成的锁存器来决定的。对于带有default语句的,当sel变为2’b11时,执行的是default语句中的结果。
有时候,这种锁存器的功能我们不需要,但是它的产生占用了fpga资源。如果说不想使用default语句,也不想生成锁存器的话,可以采用综合属性full_case来避免锁存器的生成。
full_case用法见下例子,在case语句的后面,以注释形式标注//synthesis full_case即可。
module full_case (sel, a, y);
input [1:0] sel;
input [3:0] a;
output reg y;
always @(*)
begin
casez (sel) //synthesis full_case
2'b00: y = a[0];
2'b01: y = a[1];
2'b10: y = a[2];
endcase
end
endmodule
综合后的RTL视图如下:
和带有default语句的几乎一样。但是需要说明的是,当sel为2’b11时,y输出不是0,而是随机值。在使用这个语句过程中,不要把sel端的外接输入的值设为2’b11!
所以,当分支选项不全时,case条件语句会生成锁存器。合理的使用综合属性可以避免锁存器,或者使用default分支项。同理,对于if条件语句来说,如果没有else选项的话也会生成锁存器。
if生成锁存器举例:
module iflatch(a,b,sel);
input a,sel;
output reg b;
always@(*)
begin
if (sel)
b<=a;
end
endmodule
生成的RTL直接使用了锁存器:
如果给if语句加上else分支的话,例如:else b<=0;则不会生成锁存器。RTL 视图如下:
对于if语句没有相应的综合属性功能来避免锁存器的产生。
对于条件语句生成的锁存器在有的时候可以为我们所用,产生神奇的效果。比如,按键去抖动电路就可以利用这个特点。一般来说,大多数的按键电路采用如下图的方式设计的。
由于按键一般为机械按键,在按键按下和抬起的瞬间,通过示波器可以看到很多的毛刺,
并不是理想状态下的高电平或者低电平,见下图:
摘自???
由上图可以看出,当按键按下瞬间,输出端的电平经过了5ms左右的震荡时间最后才停留在低电平,机械性能不好的按键震荡时间会达到10~20ms。如果对按键不做任何处理,直接使用输入的话必然会造成N多次的按键动作,导致不希望的事情发生。所以在使用之前必须要进行去抖动操作。
假设有这样一电路,2触发器级联,如果将按键的输入接到d触发器的输入端,在适当的时钟作用下,两级d触发器的输出会出现什么特点呢?
推理:如果btn(按键端)一直没有按键按下,那么,在clk的作用下,稳定状态时Q0端和Q1端的电平一定都是高电平;如果说,按键btn已经按下了一段时间(过了抖动的时间了)且处于稳定状态,那么Q1和Q0端输出的一定都是低电平。如果恰好处于抖动期间,在clk 的作用下,Q0和Q1的值一定会随机的在变化,且不稳定,如果适当的选择clk的话,Q0和Q1的一定会不一样的。所以,从理论上可以判定,如果Q[1..0]都为高,则按键没按下,输出高电平;如果Q[1..0]都为低,按键按下且稳定,输出低电平;如果Q[1..0]不都高或者不都低,则处于按键抖动状态,系统这个时候不输出,保持原来的输出状态即可。
故,去抖动电路代码如下:
代码注释:(1)注意这里的写法,和前面的2个d 触发器级联的写法不同,这种写法更简洁,更容易扩展。顺便说一下,如果把微分(上升沿)的代码用这种方法可以写成assign a=(q==2’b01)?1:0;,相关内容参考前面章节。
(2)这里使用了if 语句,由于没有采用else 语句,所以会生成锁存器。也就是说如果Q[1..0]中值Q0和Q1不同的话,out
的值保持之前的输出。
去抖动的效果可以通过仿真图来验证。
从图中可以看出,当抖动产生的时候,q 的值在10和01之间随机变化,当q 值为00或者11时,输出端out 才产生变化。
在实际的应用中,由于按键的机械性能不同,clk 的频率不同,一般来说2级d 触发器级联去抖动效果是不明显的。上图的仿真效果经过了精密的布置才看到效果。所以,一般都用更多级的D 触发器级联去抖动效果才能稳定。代码请读者自行完成。
这可以通过clk 的频率和抖动时间进行计算。如果抖动时间在10ms 左右,使用1KHz 的时钟工作的话,则,在10ms 内大约有10/2个D 触发器级联才能完全去抖动。
module debouncing(btn,clk,out); input clk,btn; output reg out;
reg [1:0] q;
always@(posedge clk) begin
q <= {q[0],btn}; //------(1)
if(q == 2'b11) //------(2) out <= 1;
else if(q == 2'b00) out <= 0; end
endmodule
在之前的章节中,主要讲述了Verilog HDL语言的功能实现语句有assign语句,always语句。其中,assign语句只能用来描述组合电路,这种使用assign实现功能的方式通常也叫数据流描述方式,因为,在组合电路中。输入端的变化会立刻影响到输出端,就像数据“流”过去一样;使用always语句的描述方式通常叫做行为描述,因为在always 语句中,通常不需要知道电路的构成,只需要知道输入是什么,输出是什么,至于怎么实现由EDA工具去决定。本节讲述第三种描述方式—结构描述方式。
所谓结构描述,就是利用已经存在的电路模块进行复制,得到新的电路模块的描述方式。这种复制包括两种:一种使用Verilog HDL内置的电路进行复制,另一种是对用户写的现有的模块进行复制。
Verilog HDL中内置了26个基本的元件,其中门级元件有12个(摘自王金明的数字系统设计与Verilog HDL),这些门包括:与门、与非门、或门、或非门、异或门、同或门、缓冲器、非门、三态门(有4种)等。根据这些门的特点,将它们又可分为多输入
没有这一说,以缓冲器buf 为例,缓冲器从电气意义上讲本身没有什么意义,输入是什么电平,输出就是什么电平,缓冲的作用是为了驱动更多的后级电路而设计的,也就是说,缓冲器中的一个输入可以驱动多个输出,不妨理解成这个图
这个缓冲器的输出就可以驱动多个后级电路了,因为它有3个输出(实际应用中还可以更多),一个输入,故称之为多输出门。非门也是基于同样的原理,不再赘述。
这些门在Verilog HDL 中可以直接调用,不同类型的门有不同的用法约定。 对于多输入门,语法调用格式如下:
内置元件名 <例化生成的元件名>(输出端口,输入端口1,输入端口2,……); 因此类门只有1个输出,多个输入,且输入数量不定。例如:与门有2输入与门,3输入与门,4输入与门等,不论有几个输入,均写在后面即可。
例如:and Myand ( a, b, c, d, e );
这里调用与门生成一个新的与门,名叫Myand ,新的与门有5个端口,其中,a 为输出,b,c,d,e 为4个输入端。生成的电路符号如下:
对于多输出门,语法调用格式如下:
内置元件名 <例化生成的元件名>(输出端口1,输出端口2,输出端口3,……输入端口);
此类门只有一个输入,多个输出,故在生成的新的元件的端口列表中,前面所有的端口均为输出端口,只有排在最后的那个端口是输入端口。
例如:buf MyInst( a, b, c );
本实例生成一个新的元件,名为
MyInst ,它有3个端口,其中a ,b 为输出,c 为输入。 例化后的电路图如下:
对于三态门,语法调用格式如下:
内置元件名 <例化生成的元件名>(输出端口,输入端口,使能端);
三态门只有3个端口,生成的新元件的端口顺序是固定的。
例如:bufif0 Inst( a, b, c );
生成的电路如下:
可以省略例化后的电路名字。
生成的电路如下:
这里QuartusII自动补充了名字comb~0,虽然图中的电路图和本书不一样,但原理是一样的。
元件例化的最大好处在于,即使不知道电路的功能,也可以将电路用HDL实现,非常适合一些老式的电路在FPGA/CPLD上进行移植复制。
例:使用元件例化语句实现以下电路的描述。
上图中,a、b、c、d为电路的端口,为了研究问题方便将内部的部分节点也进行了标注。完整的代码描述如下:
module MyLegacyCircuit(a,b,c,d);
input a,b,c;
output d;
wire m,n,o; //-----(1)
and u1(m,a,b); //-----(2)
not u2(n,a);
and u3(o,c,n);
or u4(d,m,o);
endmodule
代码注释:
(1)这几个中间变量也可以不用定义,元件例化的时候会根据例化后生成的端口自动创建。
(2)以下几个例化不分先后顺序,同样,元件例化语句和assign 语句以及always 语句都是并行执行的,当然相互不能嵌套。
在这个例子中,我们并不知道该电路的功能,但是使用Verilog HDL 很容易描述出来。足以体现出结构描述方式的优势。
除了可以例化Verilog HDL 内部元件,还可以对用户自己写的module 进行例化。 实例:设计一个8位的全加器。
在前几章,我们设计过1位半加器,稍加修改就可以变成1位全加器。1位全加器的模块图如下:
其中ci 为进位输入,a 和b 为两个加数,sum 为和输出,co 为进位位。根据行为描述的方法,可以将1位全加器描述如下:
若要设计8位全加器,可以利用8个1位全加器级联而成。如下图所示:
……
故可以在这个8位的全加器中例化8次1位的全加器,注意进位位的处理方式,代码如下:
module FullAdder1(a,b,ci,co,sum);
input a,b,ci; output co,sum;
assign {co,sum}=a+b+ci;
endmodule
module FullAdder8(a,b,ci,co,sum);
input[7:0] a,b;
input ci;
output [7:0] sum;
output co;
FullAdder1 u0(a[0],b[0], ci, coi1,sum[0]);
FullAdder1 u1(a[1],b[1], coi1,coi2,sum[1]);
FullAdder1 u2(a[2],b[2], coi2,coi3,sum[2]);
FullAdder1 u3(a[3],b[3], coi3,coi4,sum[3]);
FullAdder1 u4(a[4],b[4], coi4,coi5,sum[4]);
FullAdder1 u5(a[5],b[5], coi5,coi6,sum[5]);
FullAdder1 u6(a[6],b[6], coi6,coi7,sum[6]);
FullAdder1 u7(a[7],b[7], coi7,co, sum[7] );
endmodule
代码注释:
这里,由于8位的全加器的进位输入和进位输出各只有1个,故在例化时候,对应的引脚例化可以在内部定义一些临时变量来替代,如coi1,coi2……等等。
从本示例可以看出用户元件例化格式1-隐式例化
用户模块名<例化后模块名>(例化后的端口列表);
注意,例化的端口列表中,对应的端口和用户元件对应的端口功能一致。
以上面的例化后的u5为例,例化后端口a[5](第一个端口)的功能和用户的a端口(第一个端口)的功能是一致的,也就是说a[5]是全加器的一个加数端;同理,coi5是例化后的新元件的进位位,coi6为例化后的进位输出位,sum[5]为和输出位(与用户模块中的sum功能一致)。
为了验证代码的正确性,可以通过查看RTL视图来验证。
从仿真波形看,对应sum 的值为确为a+b+ci 的结果。因此,这种元件例化方式的8位全加器的描述方式是正确的!
用户元件例化格式2-显式例化
用户模块名 <例化后模块名>(.用户端口1(例化后端口1),.用户端口2(例化后端口2), ……);
注意:用户端口前面有个小数点,例化后的模块名是可选项,可以不写,如果要写的话,每个例化的模块名必须各不相同。
例如上面的例化可以使用显式的方式进行例化,部分代码如下:
显示例化的好处就是可以不用按照原用户端口的顺序进行例化,可以打乱顺序例化,但结果不变。例如第一行的例化也写成 FullAdder1 u0(.b(b[0]), .ci(ci), .sum(sum[0]) , .a(a[0]), .co(coi1)); 也是可以的。
显示调用的好处还可以省略端口调用。例如,假设有一个电路有4个端口,代码如下:
例化过程中如果不需要例化d 端口,可以这样写
或者在例化的时候干脆就不写d 端口,像下面这样:
a ainst(.a(x),.b(y),.c(z));
module b(x,y,z); input x,y; output z;
a ainst(.a(x),.b(y),.c(z),.d()); endmodule
module a(a,b,c,d); input a,b; output c,d; assign c=a&b; assign d=a|b; endmodule
……
FullAdder1 u0(.a(a[0]),.b(b[0]),.ci(ci), .co(coi1), .sum(sum[0])); FullAdder1 u1(.a(a[1]),.b(b[1]),.ci(coi1),.co(coi2), .sum(sum[1])); FullAdder1 u2(.a(a[2]),.b(b[2]),.ci(coi2),.co(coi3), .sum(sum[2])); FullAdder1 u3(.a(a[3]),.b(b[3]),.ci(coi3),.co(coi4), .sum(sum[3])); FullAdder1 u4(.a(a[4]),.b(b[4]),.ci(coi4),.co(coi5), .sum(sum[4])); FullAdder1 u5(.a(a[5]),.b(b[5]),.ci(coi5),.co(coi6), .sum(sum[5])); FullAdder1 u6(.a(a[6]),.b(b[6]),.ci(coi6),.co(coi7), .sum(sum[6])); FullAdder1 u7(.a(a[7]),.b(b[7]),.ci(coi7),.co(co), .sum(sum[7]));
……
对于本例,用示意图来表示用户元件和例化后元件的关系。a.v的模块图如下:
b.v的模块图如下:
b.v
建议在例化时候使用显示的方式,这种方式灵活,且一般不容易出错。
例化中的参数传递
在前面章节中,我们学习过parameter关键字的用法,它用来定义一个常量。例如:
module add(a,b,c);
parameter msb = 7;
input [msb:0] a,b;
output [msb:0] c;
assign c = a+b;
endmodule
这里实现的是两个8位数的加法。如果修改了参数msb为4,那么该代码完成的是2个5位数的加法。实际上,这两个加法器的本质都是一样的,都是做a和b的加法,区别仅仅是位宽。那么能否通过这个8位的加法器进行例化,得到一个5位的加法器呢?答案是可以的,在进行元件例化的过程中,如果被例化的模块中有parameter关键字定义的常量,在例化过程中,这个参数可以被重新赋一个新值。
实例:利用上面的8位加法器例化出一个5位的加法器。注意参数传递方式。
module ParameterExp (x,y,z);
input [4:0] x,y;
output [4:0] z;
add #(4) u1(x,y,z);
endmodule
生成的RTL视图如下:
可以看出端口宽度均被例化成了4位。
带参数的例化格式:参数传递采用#为标示符,在紧跟其后的“( )”中写入新的参数值,参数传递位于用户元件和例化后元件名之间。
用户模块名#(新的参数列表)<例化后模块名>(隐式或者显式端口列表);
module_name #(parameter1, parameter2,……) inst_name( port_map); 注释:如果用户的元件中存在多个parameter,那么这些参数按照顺序一一传递,新参数之间用逗号隔开。例如,假设用户元件如下:
module add(a,b,c);
parameter msb = 7;
parameter lsb = 0;
input [msb:lsb] a,b;
output [msb:lsb] c;
assign c = a+b;
endmodule
则在例化时的方式为
module ParameterExp (x,y,z);
input [4:0] x,y;
output [4:0] z;
add #(4,0) u1(x,y,z);
endmodule
按照用户元件中parameter出现的顺序,新参数4传递给msb,新参数0传递给lsb。读者可以调换一下这两个数的顺序,通过查看RTL视图进行验证。
如果在参数传递过程中,不传递新的参数给用户元件,则按照用户元件中的参数进行例化。例如,将上面的代码中传递的第二个参数省略,代码如下:
module ParameterExp (x,y,z);
input [4:0] x,y;
output [4:0] z;
add #(4) u1(x,y,z); //此处省略了第二个参数的传递,默认采用add中的值endmodule
此代码综合后完全正确。
如果想省略第一个参数,只传递第二个参数,怎么办呢?
实际上,参数传递也具有显式和隐式之说,之前讲解的都是隐式传递,显式传递的格式和元件例化的端口列表格式相同。
显式参数传递格式#(.原参数(新参数),.原参数(新参数),……)
module_name
#(.parameter_name(para_value),.parameter_name(para_value),……)
inst_name (port map);
上面的两个参数的显示传递可以写成
add #(.msb(4),.lsb(0)) u1(x,y,z);
如果只想传递第二个参数,不想传递第一个参数,可以写成下面的方式,假设第二个参数传递3,则:
add #(.lsb(3)) u1(x,y,z);
综合后的RTL视图如下:
注意观察图中例化的单元的端口[7..3],证明了第二个参数传递正确。
关于元件例化的一些说明:
在Quartus II中,使用项目组织文件的,也就是说,被例化的module和使用例化的module 必须都在项目中,在Project Navigator中必须包含这两个文件,参见下图:
如果说被例化的元件(这里是add.v)不在项目中,那么必须在例化的文件中使用`include 的语句将其包含到文件中才可以。示意性代码如下:
/* 如果被例化文件不在项目中,需要使用`include “xxx.v”将被例化文件包含*/
`include “路径\add.v” // 置于module 之外
module ParameterExp (x,y,z);
input [4:0] x,y;
output [4:0] z;
add #(4,0) u1(x,y,z);
endmodule
另外还可以将被例化的元件放在例化文件中,将文件名命名为例化的文件,上例子可以修改成一个文件:
/* 这是一个文件,文件名必须存储为ParameterExp.v */
module ParameterExp (x,y,z);
input [4:0] x,y;
output [4:0] z;
add #(4,0) u1(x,y,z);
endmodule
module add(a,b,c);
parameter msb = 7;
parameter lsb = 0;
input [msb:lsb] a,b;
output [msb:lsb] c;
assign c = a+b;
endmodule
localparam
如果在参数定义的代码里面不想上层或例化它的代码传递新的参数,可以使用localparam代替parameter关键字。
例如:
module multiplier (a, b, product);
parameter a_width = 8, b_width = 8;
localparam product_width = a_width+b_width;
input [a_width-1:0] a;
input [b_width-1:0] b;
output[product_width-1:0]product;
CLA_multiplier #(a_width, b_width) u1 (a, b, product); endmodule
此代码中,由于product_width被定义为localparam,所以不能在例化的代码中重新传递参数给product_width,也就是说,在这段代码中,product的位宽始终为16位。
写到小结里面?
localparam和parameter区别
`define:
可以跨模块的定义;
parameter:
本module内有效的定义,可用于参数传递;
localparam:
本module内有效的定义,不可用于参数传递;localparam cannot be used within the module port parameter list.其余情况下和parameter相同,一般情况下,状态机的参数都是用localparam的。
generate语句
继续上一小节的元件例化,给定一个1位全加器,如果要实现8位全加器,进行8次例化即可。试想,如果要实现1024位的全加器,难不成要进行1024次的例化?很显然,工作量太大,也不现实。如果说一定要用元件例化的话,必须换另外一种方式去实现。generate 语句就应用而生了。
generate语句是一个循环,可以产生一个对象(例如元件或者是模块)的多个例化,同时也可以产生多个variable,net,task,function,continous assignment,initial和always。在generate语句中可以引入if-else和case语句,根据条件不同产生不同的实例化。为可变尺度的设计提供了方便。
generate语句关键字,generate,endgenerate,和genvar。以generate for 为例,语法格式如下:
generate
genvar i;
for(i=xxx;i begin: name …… end endgenerate 其中xxx,yyy和zzz是根据需要设置的数值。name在这里是必写项,不能省略。 实例:使用generate语句实现8位全加器的设计,要求例化一位全加器。 分析:8位全加器中的“加数”与“和”都和1位全加器中相应的端口有功能对应,而一位全加器中的进位输入和进位输出则没有对应的端口,不妨先设计一些中间变量暂存这些进位的值;又知,后一级的进位输入是前一级的进位输出,所以只需要定义一个多位的变量即可。由于共有8个例化,算上所有的进位输入输出,一共应该是9个。定义变量cio[7:-1]代表这9个位。从8位全加器来看,8位全加器的进位输入ci应该是这里的cio[-1],8位全加器的输出应该是最后一级的cio[7]。以下完整代码,注意例化的格式: Verilog基本语法 【逻辑值】 逻辑0 表示低电平,GND 逻辑1 表示高电平,VCC 逻辑X 表示未知电平,可能是高电平,也可能是低电平 逻辑Z 表示高阻态,外部没有激励信号,是一个悬空状态 注:高阻态的实质:电路分析时高阻态可做开路理解。 可以把它看作输出(输入)电阻非常大,对下级电路无任何影响。 若为0、x、z则按照假处理;若为1,按真处理。 【进制】 二进制4'b0101 —4位二进制数0101 十进制数4’d2 —4位十进制数2 十六进制数4’ha —4位十六进制数a Verilog中若不指定位宽,默认32位;若不指定位宽不指定进制,默认32位宽的十进制数。 【标识符】 标识符可以是字母、数字、$和_(下划线)的组合,且开头必须是字母或下划线,区分大小写。不建议大小写混合使用。 【数据类型】 寄存器关键字reg,默认初始值位不定值X; reg[31:0] delay_cnt; //[31:0],指定寄存器位宽32位, reg key_reg; // 默认位宽为1. reg类型数据只能在always和initial语句中被赋值。 线网表示结构实体的物理连线,包括wire和tri类型 参数常量,用parameter定义。 parameter H_SYNC = 11'd41; 【运算符】 [条件操作符] ?: 例,a?b:c //如果a为真就选b,否则选择c。 result=(a>=b)?a:b; [逻辑运算符] !&& || [位运算符] ~ & | ^(按位异或) a&b; //自动将位宽小的数高位补零至较大数的位宽,然后按位与操作。[移位运算符] << >> 用0填补移出的空位。左移时位宽增加,右移位宽不变。 [位拼接运算符] {} 例,{a,b} //将a和b拼接起来,作为一个新信号,a为高位。 c={a,b[3:0]}; //a、b位宽均为8位,c为8+4=12位。 Verilog HDL语言基础知识 先来看两个Verilog HDL程序。 例一个8位全加器的Verilog HDL源代码 module adder8(cout,sum,ina,inb,cin); output[7:0] sum; output cout; input[7:0] ina,inb; input cin; @ assign {cout,sum}=ina+inb+cin; 模块的端口声明了模块的输人和输出口。其格式如下: module 模块名(口1,口2,口3,口4,……); 2.模块内容 模块内容包括I/O说明,信号类型声明和功能定义。 (1) I/O说明的格式如下: ; 输人口: input端口名1,端口名2,……端口名N; 输出口: output端口名l,端口名2,……端口名N; I/O说明也可以写在端口声明语句里。其格式如下: module module_name(input portl,input port2,…output portl,output port2,…); (2)信号类型声明: 它是说明逻辑描述中所用信号的数据类型及函数声明。如 ( reg[7:0] out; 数字 (1)整数 在Verilog HDL中,整数型常量(即整常数)有以下4种进制表示形式: ◇二进制整数(b或B); ◇十进制整数(d或D); ◇十六进制整数(h或H); ◇八进制整数(o或O)。 ) 完整的数字表达式为: <位宽>'<进制> <数字>, 位宽为对应二迸制数的宽度,如: 8'b nets型变量wire nets型变量指输出始终根据输入的变化而更新其值的变量,它一般指的是硬件电路中的各种物理连接。Verilog HDL中提供了多种nets型变量,具体见表。 这里着重介绍wire型变量。wire是一种常用的nets型变量,wire型数据常用来表示assign语句赋值的组合逻辑信号。Verilog HDL模块中的输入/输出信号类型缺省时自动定义为wire型。Wire型信号可以用作任何方程式的输入,也可以用作assign语句和实例元件的输出,其取值为0,1,x,z。 wire型变量格式如下: & ⑴.定义宽度为1位的变量: wire 数据名1,数据名2,……数据名n; 例如:wire a,b; register型变量reg register型变量对应的是具有状态保持作用的电路元件,如触发器、寄存器等。register Verilog HDL Verilog HDL是一种硬件描述语言,用于从算法级、门级到开关级的多种抽象 设计层次的数字系统建模。被建模的数字系统对象的复杂性可以介于简单的门和完整的电子数字系统之间。数字系统能够按层次描述,并可在相同描述中显式地进行时序建模。 Verilog HDL 语言具有下述描述能力:设计的行为特性、设计的数据流特性、设计的结构组成以及包含响应监控和设计验证方面的时延和波形产生机制。所有这些都使用同一种建模语言。此外,Verilog HDL语言提供了编程语言接口,通过该接口可以在模拟、验证期间从设计外部访问设计,包括模拟的具体控制和运行。 Verilog HDL语言不仅定义了语法,而且对每个语法结构都定义了清晰的模拟、仿真语义。因此,用这种语言编写的模型能够使用Verilog仿真器进行验证。语言从C编程语言中继承了多种操作符和结构。Verilog HDL提供了扩展的建模能力,其中许多扩展最初很难理解。但是,Verilog HDL语言的核心子集非常易于学习和使用,这对大多数建模应用来说已经足够。当然,完整的硬件描述语言足以对从最复杂的芯片到完整的电子系统进行描述。 =============================== 中文版Verilog HDL简明教程:第1章简介 Verilog HDL是一种硬件描述语言,用于从算法级、门级到开关级的多种抽象设计层次的数字系统建模。被建模的数字系统对象的复杂性可以介于简单的门和完整的电子数字系统之间。数字系统能够按层次描述,并可在相同描述中显式地进行时序建模。 Verilog HDL 语言具有下述描述能力:设计的行为特性、设计的数据流特性、设计的结构组成以及包含响应监控和设计验证方面的时延和波形产生机制。所有这些都使用同一种建模语言。此外,Verilog HDL语言提供了编程语言接口,通过该接口可以在模拟、验证期间从设计外部访问设计,包括模拟的具体控制和运行。 Verilog HDL语言不仅定义了语法,而且对每个语法结构都定义了清晰的模拟、仿真语义。因此,用这种语言编写的模型能够使用Verilog仿真器进行验证。语言从C编程语言中继承了多种操作符和结构。Verilog HDL提供了扩展的建模能力,其中许多扩展最初很难理解。但是,Verilog HDL语言的核心子集非常易于学习和使用,这对大多数建模应用来说已经足够。当然,完整的硬件描述语言足以对从最复杂的芯片到完整的电子系统进行描述。 历史 Verilog HDL语言最初是于1983年由Gateway Design Automation公司为其模 ?Verilog-A 30分钟快速入门教程 进入正题,学了几天的Verilog-A,平台是Agilent ADS,主要参考“Verilog-AMS Language Reference Manual”和ADS的帮助文档。 现在的状态算是入门了,写了个简单的PLL。总结这几天的学习,觉得效率太低,我以前有一定Verilog基础,研一时学过一点VHDL-AMS,学到现在这个状态应该半天就够了;入门的话,30分钟足矣;跟着这个教程走,你会很快了解和熟悉Verilog-A。(前提是有一定的Verilog基础和电路基础) 1、基尔霍夫定律撑起了整个电路学的大厦(当然也可以认为基尔霍夫定律只是麦克斯韦方程的简化版),作为模拟电路描述语言Verilog-A,同样将基尔霍夫定律作为其基本,最重要的两个概念便是流量(Flow)和位(Potential),在电学里是电流和电压,在力学里可以是力和距离,在热学里可以是功率和温差,等等。 在Verilog-A中,你可以将电阻电容电感等器件用一个方程式来表述,比如I(out) <+ V(out)/R,这样就产生了一个电阻,最后Verilog-A仿真器会用某种算法(迭代是最常见的)将I(out)和V(out)求解出来,然后根据这个解去算下一个时刻的I、V等,当然这仅仅是指时域仿真。 2、下面讲Verilog-A的语法: begin end //相当于C语言的一对大括号,与Verilog同 if ( expression ) true_statement ; [ else false_statement ; ] //与Verilog同 case ( expression ) case_item { case_item } endcase for ( procedural_assignment ; expression; procedural_assignment ) statement //case与for语句都跟Verilog、C语言类似 cross( expr [, dir [, time_tol [, expr_tol ]]] ); //cross用来产生一个event,如: @(cross(V(sample) -2.0, +1.0)) //指sample的电压超过2.0时触发该事件,将会执行后面的语句,+1.0表示正向越过,-1.0则相反 ddt( expr ) //求导,如: I(n1,n2) <+ C * ddt(V(n1, n2)); //表示了一个电容 idt( expr ,[ ic [, assert [, abstol ]]] ) //积分,如: V(out) <+ gain * idt(V(in) ,0) + gain * V(in); //比例积分,式中的0表示积分的初值 transition( expr [, time_delay [, rise_time [, fall_time [, time_tol ]]]] ) //将expr的值delay一下并指定上升下降沿时间,相当于一个传输门 ?进入正题,学了几天的Verilog-A,平台是Agilent ADS,主要参考“Verilog-AMS L anguage Reference Manual”和ADS的帮助文档。 现在的状态算是入门了,写了个简单的PLL。总结这几天的学习,觉得效率太低,我以前有一定Verilog基础,研一时学过一点VHDL-AMS,学到现在这个状态应该半天就够了;入门的话,30分钟足矣;跟着这个教程走,你会很快了解和熟悉Verilog-A。(前提是有一定的Verilog 基础和电路基础) 1、基尔霍夫定律撑起了整个电路学的大厦(当然也可以认为基尔霍夫定律只是麦克斯韦方程的 简化版),作为模拟电路描述语言Verilog-A,同样将基尔霍夫定律作为其基本,最重要的两个概念便是流量(Flow)和位(Potential),在电学里是电流和电压,在力学里可以是力和距离,在热学里可以是功率和温差,等等。 在Verilog-A中,你可以将电阻电容电感等器件用一个方程式来表述,比如I(out) <+ V(o ut)/R,这样就产生了一个电阻,最后Verilog-A仿真器会用某种算法(迭代是最常见的)将I(o ut)和V(out)求解出来,然后根据这个解去算下一个时刻的I、V等,当然这仅仅是指时域仿真。 2、下面讲Verilog-A的语法: begin end //相当于C语言的一对大括号,与Verilog同 if ( expression ) true_statement ; [ else false_statement ; ] //与Verilog同 case ( expression ) case_item { case_item } endcase for ( procedural_assignment ; expression; procedural_assignment ) statement //case与for语句都跟Verilog、C语言类似 cross( expr [, dir [, time_tol [, expr_tol ]]] ); //cross用来产生一个event,如: Verilog的词法约定 1Verilog是大小写相关的,其中的关键字全部为小写。 2空白符由空格、制表符、和换行符组成。 3单行注释以“//”开始,verilog将忽略此处到行尾的内容。多行注释以“/*” 开始,以“*/”结束。多行注释不允许嵌套 4操作符有三种:单目操作符、双目操作符和三目操作符。 5数字声明 Verilog中有两种数字生命:指明位数的数字和不指明位数的数字 指明位数的数字表示形式: ║68 第3章 硬件描述语言Verilog HDL基础 3.2 Verilog HDL程序基本结构 Verilog HDL是一种用于数字逻辑电路设计的语言。用Verilog HDL描述的电路设计就是该电路的Verilog HDL模型。Verilog HDL既是一种行为描述的语言,也是一种结构描述的语言。也就是说,既可以用电路的功能描述,也可以用元器件和它们之间的连接来建立所设计电路的Verilog HDL模型。Verilog模型可以是实际电路的不同级别的抽象。这些抽象的级别和它们对应的模型类型共有以下5种。 ?系统级(system):用高级语言结构实现设计模块的外部性能的模型。 ?算法级(algorithm):用高级语言结构实现设计算法的模型。 ?RTL级(Register Transfer Level):描述数据在寄存器之间流动和如何处理这些数据的模型。 ?门级(gate-level):描述逻辑门以及逻辑门之间的连接的模型。 ?开关级(switch-level):描述器件中三极管和储存节点以及它们之间连接的模型。 一个复杂电路系统的完整Verilog HDL模型是由若干个Verilog HDL模块构成的,每一个模块又可以由若干个子模块构成。其中有些模块需要综合成具体电路,而有些模块只是与用户所设计的模块交互的现存电路或激励信号源。利用Verilog HDL语言结构所提供的这种功能就可以构造一个模块间的清晰层次结构来描述极其复杂的大型设计,并对所作设计的逻辑电路进行严格的验证。 Verilog HDL行为描述语言作为一种结构化和过程性的语言,其语法结构非常适合于算法级和RTL级的模型设计。这种行为描述语言具有以下功能。 ?可描述顺序执行或并行执行的程序结构。 ?用延迟表达式或事件表达式来明确地控制过程的启动时间。 ?通过命名的事件来触发其他过程里的激活行为或停止行为。 ?提供了条件、if-else、case、循环程序结构。 ?提供了可带参数且非零延续时间的任务(task)程序结构。 ?提供了可定义新的操作符的函数结构(function)。 ?提供了用于建立表达式的算术运算符、逻辑运算符、位运算符。 ? Verilog HDL语言作为一种结构化的语言也非常适合于门级和开关级的模型设计。因其结构化的特点又使它具有以下功能。 —提供了完整的一套组合型原语(primitive); —提供了双向通路和电阻器件的原语; —可建立MOS器件的电荷分享和电荷衰减动态模型。 Verilog HDL的构造性语句可以精确地建立信号的模型。这是因为在Verilog HDL中,提供了延迟和输出强度的原语来建立精确程度很高的信号模型。信号值可以有不同的强度,可以通过设定宽范围的模糊值来降低不确定条件的影响。 Verilog HDL作为一种高级的硬件描述编程语言,有着类似C语言的风格。其中if语句、case语句等和C语言中的对应语句十分相似。如果读者已经掌握C语言编程的基础,那么学习Verilog HDL并不困难,只要对Verilog HDL某些语句的特殊方面着重理解,并加强上机练习就能很好地掌握它,利用它的强大功能来设计复杂的数字逻辑电路。下面将介绍Verilog Verilog HDL语法基础(1) Verilog的词法约定 1Verilog是大小写相关的,其中的关键字全部为小写。 2空白符由空格、制表符、和换行符组成。 3单行注释以“//”开始,verilog将忽略此处到行尾的内容。多行注释以“/ *”开始,以“*/”结束。多行注释不允许嵌套 4操作符有三种:单目操作符、双目操作符和三目操作符。 5数字声明 Verilog中有两种数字生命:指明位数的数字和不指明位数的数字 指明位数的数字表示形式: 有关Verilog 中的一些语法 位运算符 1) ~ //取反 2) & //按位与 3) | //按位或 4) ^ //按位异或 5) ^~ //按位同或(异或非) 逻辑运算符 在Verilog HDL语言中存在三种逻辑运算符: 1) &&逻辑与 2) || 逻辑或 3) !逻辑非 等式运算符 在Verilog HDL语言中存在四种等式运算符: 1) == (等于) 2) != (不等于) 3) === (等于) 4) !== (不等于) "=="和"!="又称为逻辑等式运算符。其结果由两个操作数的值决定。由于操作数中某些位可能是不定值x和高阻值z,结果可能为不定值x。 而"==="和"!=="运算符则不同,它在对操作数进行比较时对某些位的不定值x和高阻值z也进行比较,两个操作数必需完全一致,其结果才是1,否则为0。"==="和"!=="运算符常用于case表达式的判别,所以又称为"case等式运算符"。 位移运算符 左移:右边的添0 右移:左边的添0,移除的位舍去 举例: 4’b1001<<1 = 5’b10010; 4’b1001<<2 = 6’b100100; 1<<6 = 32’b1000000; 4’b1001>>1 = 4’b0100; 4’b1001>>4 = 4’b0000; 位拼接运算符 1.{a,b[3:0],w,3’b101}也可以写成为 {a,b[3],b[2],b[1],b[0],w,1’b1,1’b0,1’b1} 2.{4{w}} //这等同于{w,w,w,w} 3.{b,{3{a,b}}} //这等同于{b,a,b,a,b,a,b} 负数: 一个数字可以被定义为负数,只需在位宽表达式前加一个减号,减号必须写在数字定义表达式的最前面。注意减号不可以放在位宽和进制之间也不可以放在进制和具体的数之间。见下例:-8'd5 //这个表达式代表5的补数(用八位二进制数表示) wire型变量通常是用来表示单个门驱动或连续赋值语句驱 Verilog-A 30分钟快速入门教程 进入正题,学了几天的Verilog-A,平台是Agilent ADS,主要参考“Verilog-AMS Language Reference Manual”和ADS的帮助文档。 现在的状态算是入门了,写了个简单的PLL。总结这几天的学习,觉得效率太低,我以前有一定Verilog基础,研一时学过一点VHDL-AMS,学到现在这个状态应该半天就够了;入门的话,30分钟足矣;跟着这个教程走,你会很快了解和熟悉Verilog-A。(前提是有一定的Verilog基础和电路基础) 1、基尔霍夫定律撑起了整个电路学的大厦(当然也可以认为基尔霍夫定律只是麦克斯韦方程的简化版),作为模拟电路描述语言Verilog-A,同样将基尔霍夫定律作为其基本,最重要的两个概念便是流量(Flow)和位(Potential),在电学里是电流和电压,在力学里可以是力和距离,在热学里可以是功率和温差,等等。 在Verilog-A中,你可以将电阻电容电感等器件用一个方程式来表述,比如I(out) <+ V(out)/R,这样就产生了一个电阻,最后Verilog-A仿真器会用某种算法(迭代是最常见的)将I(out)和V(out)求解出来,然后根据这个解去算下一个时刻的I、V等,当然这仅仅是指时域仿真。 2、下面讲Verilog-A的语法: begin end //相当于C语言的一对大括号,与Verilog同 if ( expression ) true_statement ; [ else false_statement ; ] //与Verilog同 case ( expression ) case_item { case_item } endcase for ( procedural_assignment ; expression; procedural_assignment ) statement //case与for语句都跟Verilog、C语言类似 cross( expr [, dir [, time_tol [, expr_tol ]]] ); //cross用来产生一个event,如: @(cross(V(sample) -2.0, +1.0)) //指sample的电压超过2.0时触发该事件,将会执行后面的语句,+1.0表示正向越过,-1.0则相反 ddt( expr ) //求导,如: I(n1,n2) <+ C * ddt(V(n1, n2)); //表示了一个电容 idt( expr ,[ ic [, assert [, abstol ]]] ) //积分,如: V(out) <+ gain * idt(V(in) ,0) + gain * V(in); //比例积分,式中的0表示积分的初值 transition( expr [, time_delay [, rise_time [, fall_time [, time_tol ]]]] ) //将expr的值delay一下并指定上升下降沿时间,相当于一个传输门 1、. 2、. 3、Reg型的数据类型默认初始值为X。reg型数据可以赋正值也可以赋负值,但 是当一个reg型数据是一个表达式的操作数的时候,他的值被当做无符号数及正值。 4、在数据类型中?和Z均表示高阻态。 5、Reg型只表示被定义的信号将用在“always”模块内,并不是说reg型一定 是寄存器或触发器的输出。虽然reg型信号常常是寄存器或触发器的输出但是并不一定总是这样。 6、Verilog语言中没有多维数组的存在。Memory型数据类型是通过扩展reg型 数据的弟子和范围来生成的。其格式如下reg[n-1:0]存储器名[m-1:0]; 7、在除法和取余的运算中结果的符号和第一个操作数的符号位是相同的。 8、不同长度的数据进行运算:两个长度不同的数据进行位运算时,系统会自动 地将两者按有端对齐,位数少的操作数会在相应的高位用0填满以便连个操作数安慰进行操作。 9、= = =与!= = =和= =与!= =的区别:后者称为逻辑等是运算符,其结果是 2个操作数的值决定的。由于操作书中某些位可能不定值x和高阻态z结果可能是不定值x。而 = = =和!= = =运算符对操作数的比较时对某些位的高阻态z和不定值x也进行比较,两个操作数必须完全一致,其结果才是1,否则是0. 10、非阻塞和阻塞赋值方式:非阻塞赋值方式(如a<=b)上面语句所赋得变 量值不能立即被下面语句所用,(2)快结束后才能完成这次赋值操作 3在编写克综合的时序逻辑模块时这是最常用的赋值方法。阻塞赋值(如a=b)赋值语句执行完后,块才结束 2 b的值在赋值语句完成后立即执行 3在时序逻辑使用中,可能产生意想不到的结果。 11、模块的描述方式:(RTL为寄存器传输级描述) “(1)数据流描述方式:数据流行描述主要用来描述组合功能,具体用“assign”连续赋值语句来实现。分为两种a、显式连续赋值语句; 1,八位数据通路控制器 `define ON 1?b1 `define OFF 1?b0 wire controlswitch; wire [7:0] in out; assign out = (controlswitch == `ON) ? in : 8?h00; 2,数据在寄存器中的暂时保存 module reg8(en ,clk, data,rst,out); input en,clk,rst; input [7:0] data; output [7:0] out; reg [7:0] out; always @ (posedge clk) begin if(!rst) out <= 0 else if (en) out <= data; else out <= 8?h00; end endmodule 3,状态机 module fsm(clk,rst,a,k1,k2); input clk,rst,a; output k1,k2; reg k1,k2; reg state; parameteter Idle = 2?b00, Start = 2?b01, Stop = 2?b10, Clear = 2?b11; always @ (posedge clk) begin if (!rst) begin state <= Idle; k2 <= 0; k1 <= 0; end else case (state) Idle: begin If(a) begin state <= Start; k1 <= 0; end else state <= Idle; end Start : begin If(!a) state <= Stop; else state <= Start; end Stop :begin If (a) Begin state <= Clear; k2 <= 1; end else state <= Stop; end Clear : begin If (!a) begin state <= Idle; k2 <= 0; k1 <= 1; end else state <= Clear; end endcase endmodule 4,组合逻辑电路设计实例 8位带进位端的加法器的设计实例module adder_8 (cout, sum, a,b,cin); output [7:0] sum; output cout; input [7:0] a; 先记下来,後面會有進一步的解說: 1、不使用初始化语句; 2、不使用延时语句; 3、不使用循环次数不确定的语句,如:forever,while等; 4、尽量采用同步方式设计电路; 5、尽量采用行为语句完成设计; 6、always过程块描述组合逻辑,应在敏感信号表中列出所有的输入信号; 7、所有的内部寄存器都应该可以被复位; 8、用户自定义原件(UDP元件)是不能被综合的。 一:基本 Verilog中的变量有线网类型和寄存器类型。线网型变量综合成wire,而寄存器可能综合成WIRE,锁存器和触发器,还有可能被优化掉。 二:verilog语句结构到门级的映射 1、连续性赋值:assign 连续性赋值语句逻辑结构上就是将等式右边的驱动左边的结点。因此连续性赋值的目标结点总是综合成由组合逻辑驱动的结点。Assign语句中的延时综合时都将忽视。 2、过程性赋值: 过程性赋值只出现在always语句中。 阻塞赋值和非阻塞赋值就该赋值本身是没有区别的,只是对后面的语句有不同的影响。 建议设计组合逻辑电路时用阻塞赋值,设计时序电路时用非阻塞赋值。 过程性赋值的赋值对象有可能综合成wire, latch,和flip-flop,取决于具体状况。如,时钟控制下的非阻塞赋值综合成flip-flop。 过程性赋值语句中的任何延时在综合时都将忽略。 建议同一个变量单一地使用阻塞或者非阻塞赋值。 3、逻辑操作符: 逻辑操作符对应于硬件中已有的逻辑门,一些操作符不能被综合:===、!==。 4、算术操作符: Verilog中将reg视为无符号数,而integer视为有符号数。因此,进行有符号操作时使用integer,使用无符号操作时使用reg。 5、进位: 通常会将进行运算操作的结果比原操作数扩展一位,用来存放进位或者借位。如: Wire [3:0] A,B; Wire [4:0] C; Assign C=A+B; C的最高位用来存放进位。 6、关系运算符: 关系运算符:<,>,<=,>= 和算术操作符一样,可以进行有符号和无符号运算,取决于数据类型是reg,net还是integer。 7、相等运算符:==,!= 注意:===和!==是不可综合的。 可以进行有符号或无符号操作,取决于数据类型 8、移位运算符: 左移,右移,右边操作数可以是常数或者是变量,二者综合出来的结果不同。 9、部分选择: 部分选择索引必须是常量。 10、BIT选择: BIT选择中的索引可以用变量,这样将综合成多路(复用)器。 11、敏感表:Always过程中,所有被读取的数据,即等号右边的变量都要应放在敏感表中,不然,综合时不能正确 文档中心 文档编号 资源类别: HDL语言版本 1.0 密级 内部公开 共41页 Verilog HDL入门教程(仅供内部使用) 拟制: 批准: 批准: 中研基础 中研基础 日期: 日期: 日期: 2004.8.3 yyyy/mm/dd 版权所有不得复制 Verilog HDL 入门教程绝密请输入文档编号日期 2004.8.3 修订版本 1.00 描述 初稿完成 修订记录 作者 2004-08-16 第2页,共41页版权所有,侵权必究 Verilog HDL 入门教程 绝密请输入文档编号 目录 1 前 言 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2 HDL设计方法学简 介 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.1 数字电路设计方 法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.2 硬件描述语 言 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Verilog语法总结 数电基础 能够存储1位二值信号的基本单元电路统称为触发器。 根据逻辑功能的不同特点,可以将数字电路分成两大类: 组合逻辑电路和时序逻辑电路。 组合逻辑电路中,任意时刻的输出仅仅取决于该时刻的输入,与电路原来的状态无关。 时序逻辑电路中,任一时刻的输出不仅取决于当时的输入信号,而且还取决于电路原来的状态。或者说还与以前的输入有关,因此时序逻辑必须具备记忆功能。 组合逻辑电路时序逻辑电路 逻辑值 逻辑0:表示低电平,也就对应我们电路GND; 逻辑1:表示高电平,也就是对应我们电路的 VCC; 逻辑X:表示未知,有可能是高电平,也有可能 是低电平; 逻辑Z:表示高阻态,外部没有激励信号,是一 个悬空状态。 数字进制格式 Verilog数字进制格式包括二进制、八进制、十进 制和十六进制。 一般常用的为二进制、十进制和十六进制。 二进制表示如下:4’b0101 表示4位二进制数字0101 十进制表示如下:4’d2 表示4位十进制数字2(二进制0010) 十六进制表示如下:4’ha 表示4位十六进制数字a(二进制1010) 16’b1001_1010_1010_1001 = 16’h9AA9 第一个表示用几个二进制位可以表示、’为语法、b为二进制,d为十进制,h为16进制,后面则表示要输出输入的字。(括号内为二进制表示,因为最终在计算机中都会变为二进制表示) 标识符 标识符可以是任意一组字母、数字、$符号和_(下划线)符号的组合; 但标识符的第一个字符必须是字母或者下划线; 标识符是区分大小写的; 数据类型 在Verilog 语言中,主要有三大类数据类型:寄存器数据类型、线网数据类型和参数数据类型。 寄存器类型: 寄存器表示一个抽象的数据存储单元,通过赋值语句可以改变寄存器储存的值寄存器数据类型的关键字是reg,reg 类型数据的默认初始值为不定值x reg类型的数据只能在always 语句和initial 语句中被赋值。 如果该过程语句描述的是时序逻辑,即always语句带有时钟信号,则该寄存器变量对应为触发器; 如果该过程语句描述的是组合逻辑,即always语句不带有时钟信号,则该寄存器变量对应为硬件连线; 线网类型: 线网数据类型表示结构实体(例如门)之间的物理连线。线网类型的变量verilog基本语法
Verilog语言基础知识
Verilog语言基础教程
(完整word版)Verilog-A30分钟快速入门教程
Verilog-A 30分钟快速入门教程
Verilog语法入门,初学者必看
3.2.1 Verilog HDL程序入门[共2页]
VerilogHDL语法基础
有关Verilog 中的一些语法
Verilog-A 30分钟快速入门教程
Verilog中的一些语法和技巧
verilog语法练习
verilog_经验(适合初学者)
Verilog HDL 入门教程(华为)
Verilog语法总结