一、电路设计的语法
1.设计不用的语法
a)initial 【设计不用,仿真时使用】
b) #5 【设计不用,仿真时使用】
下面的均设计不用,仿真时亦不用
task/function
for/while/repeat/forever
integer
casex/casez
force/wait/fork
模块内部不能有X态(不定态)、Z态(高阻态),内部不能有三态接口
2.设计使用的语法
a)reg/wire、parameter
b)assign【建议改名时使用】、always
c)只允许使用if else 和case两种条件语句
只用a)、b)、c)就能搞定所有的设计
d)算术运算符(+,-,*,/,%)
e)赋值运算符(<=,=)【时序逻辑用<=,组合逻辑用=,其他 情况不存在】
注:不必纠结二者的区别,只需记住时序逻辑用<=,组合逻辑用=
f)关系运算符(>,<,>=,<=)
g)逻辑运算符(&&,||,!)【为避免歧义,逻辑运算符两边必须用1bit信号】
h)位运算符(~,|,^,&)【重点关注异或】
i) 移位运算符(<<,>>)
j)拼接运算符({})
二、电路设计的结构
1.电路设计的3种结构
1)组合逻辑
always @(*) begin
语句
end
2)时序逻辑
a)同步复位的时序逻辑
always @ (posedge clk )begin
if(rst_n==1'b0)begin
语句
else begin
语句
end
end
b)异步复位的时序逻辑
always @ (posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
语句
else begin
语句
end
end
三、电路设计的要点
1.一个always只产生一个信号
例如
always@(posedge clk or negedge rst_n) begin
if(rst_n==1’b0)begin
b <= 1’b0;
a <= 1’b0;
else begin
b <= 1’b1;
a <= b;
end
end
通常的verilog代码,一个 always里面会设计多个信号,例如上图,这个 always 语句同时设计了a和 b两个信号。明德扬建议一个 always只设计一个信号,例如上面 代码应该改为以下代码。
always@(posedge clk or negedge rst_n) begin
if(rst_n==1’b0)begin
b <= 1’b0;
else begin
b <= 1’b1;
end
end
always@(posedge clk or negedge rst_n) begin
if(rst_n==1’b0)begin
a <= 1’b0;
else begin
a <= b;
end
end
a). 一心一用。每段 always 同时只考虑 1 个信号的设计,不用考虑其他无关的信号。 例如设计 a 时,就全力想 a 信号,不用考虑 b 信号。这样有助于写出最简、最高 效的代码。
b). 有助于按照定义实现信号。设计的第一步是定义信号,该信号干什么用,什么时 候为 0,什么时候为其他值,都在设计前定义好。always 就根据这个定义来写代 码,非常简单。
c). 方便查找和定位问题。如果代码出现问题,方便查找问题的源头。只要找到与定 义不一致的那段 always代码,修改该段代码,就能解决好问题。
d). 修改代码影响最小。修改代码时,只修改本信号,而不会对其他信号造成影响。 否则很有可能修改 1 个信号,导致其他本来已经正确的信号又变错误。很多同学 设计时,容易反反复复,经常按下葫芦又起瓢,最后是勉勉强强成功,但这样的 系统,可靠性能有多高呢。
e). 有助于衡量工作进度。做好设计定义后,就非常清楚有多少信号需要设计。只要 按照定义写好代码,该信号的设计就基本完成,出错的概率很低。即使后期有错 误,修改相应的那个信号就解决问题。但如果多个信号集中到一个 always,每个 信号之间相互影响,不到最后时刻,你都不清楚还有多少设计未完成。工作中曾 经出现这样情景:产品设计号称进度是 99%,但后期修改 1 个错误,进而影响了 其他信号,基本上其他信号都要重新设计,进度一下子又退回了 50%。
f). 容易修改代码。多个信号集中在一个 always,修改代码时,你只能见缝插针,还 要极力避免影响其他信号,非常困难。
g). 有助于硬件思维设计。一个 always设计 1个信号,这段 always很容易理解为一个 电路模块,从而设计出最高效的电路。多个信号在一个 always,容易按软件思维 来进行 FPGA设计。
2.一个信号只能在一个always产生
上图第1个触发器的输出值为 0,第2个触发器的输出值为 1,由于两个触发器输 出信号名都是 b,那自然就是同一根线,那么在这种情况下,b的值到底是 0还是 1呢? 谁也不知道,因此FPGA内部不允许有这种不确定的情况出来。所以下面两段代码是 不允许的,b只能在一个always里产生,而不能是两个。
always@(posedge clk or negedge rst_n) begin
if(rst_n==1’b0)begin
b <= 1’b0;
else begin
if(a==1’b1)
b <= 1’b1;
end
end
always@(posedge clk or negedge rst_n) begin
if(rst_n==1’b0)begin
b <= 1’b0;
else begin
if(c==1’b1)
b <= 1’b0;
end
end
3.硬件设计思维
FPGA不是一行一行顺序执行的,FPGA是将代码变成 电路后,上电运行的。其 always描述的是一个信号在什么情况下,这个信号的值为多 少;在其他情况下,值为多少。
下面就是一个很典型的软件思维写出的代码。
always@(posedge clk or negedge rst_n) begin
if(rst_n==1’b0)begin
b <= 4’b0;
else begin
b<= b+1; //line_0
if(b==8)
b <= 0;//line_1
end
end
例如某个信号在三种情况下取不同的值:条件 A满足时值为2、条件 B满足时值 为1,以及其他情况时值为 0,如上图。则对应的always代码如下:
always@(posedge clk or negedge rst_n) begin
if(rst_n==1’b0)begin
a <= 0;
else if(条件 A)
a <= 2;
else if(条件B)
a <= 1;
else
a <= 0;
end
FPGA设计的步骤:首先考虑这个信号有多少种值,然后这些值分别在哪几种情况 获得。把情况划分好之后,就可以用 if else语句来写代码了。
前面错误的代码,其正确的代码应该是这样的,将情况分成 b等于7(值为0)和 b不等于7(值等于上一次值加 1)两种情况:
always@(posedge clk or negedge rst_n) begin
if(rst_n==1’b0)begin
b <= 4’b0;
else begin
if(b==7)
b <= 0;//line_1
else
b <= b+1;
end
end
4.条件判断只允许使用if else 和 case ,其他全不用(包括casex)。
5.含有posedge或negedge的,一定是D触发器,是时序电路。
6.设计时,如果你想立刻有结果,那就用组合逻辑,如果你想延时一拍有结果,那就用时序逻辑。
重点:
1,三种电路
2,两种条件
3,一一法则
即标红部分。