FPGA-赋值冲突

Verilog 语法(2)
参数声明一致
推荐在module 端口声明之前声明参数,在module例化语句的最开始的时候修改参数。
module xxx
#(parameter WIDTH=8)

input a,
output b
);
xxx #(.WIDTH(4))
三种描述方式
1、结构化描述方式
module example(input a,b,c,d;
output o);
wire tmp0,tmp1;
XOR1 mo(.O(tmp0),.IO(a),.I1(b));
XOR2 m1(.O(tmp1),IO©,.I1(d));
AND2 m2(.O(o),IO(tmp0),.I1(tmp1));
endmodule
2、数据流描述方式
module example(input a,b,c,d,
output o);
wire tmp0,tmp1;
assign tmp0=a^b;
assign tmp1=c^d;
assign o=tmp0&tmp1;
endmodule
3、行为级描述方式
module example(input a,b,c,d,
output reg o);
wire tmp0,tmp1;
always @(a,b,c,d)
if(a!=b&&c!=d)
o=1’b1;
else
o=1’b0;
endmodule
Verilog中应该禁止的写法
(1)在时序always 中使用阻塞赋值
针对时序always中的单条代码来说,无论是阻塞赋值还是非阻塞赋值,等号左侧的变量都会综合为触发器,always中非阻塞语句不是立即生效的,因此在书写的时候,非阻塞语句无所谓先后,但是阻塞赋值符号是立即实现的,因此具有阻塞赋值语句的时序always,其语句的先后顺序就显得很重要了。
例如:
always @(posedge clk or negedge rst_n)
begin
n=a&b; //the same as n<=a&b;
m<=n; //the same as m<=a&b;
end
always @(posedge clk or negedge rst_n)
begin
m<=n; //the same as: m<=pre clock n
n=a&b; //the same as :n<=a&b;
end
(2) 在组合always中使用非阻塞赋值
在组合always中,推荐全部使用阻塞赋值方式,注意好语句顺序,代码的综合和仿真不会有什么问题,由于always敏感量中不会出现敏感边沿事件,所以非阻塞赋值不会综合为寄存器,使用非阻塞赋值方式的组合always,会导致前仿真与后仿真不一致的情况。例如:
reg t;
always @(a)
begin
t<=a;
o=t;
end
这会带来仿真的问题
**

正确的变量访问思路**

变量的访问思路,简而言之就是“一写多读”,如果有多个并行语句操作一个变量时,有且只能有一个固定的语句对变量进行写操作,而所有的并行语句都可以对变量进行读操作,因为一个变量只能有一个驱动源,如果被多个驱动源驱动,就会产生冲突,例如:
always @(a)
c=a;
always @(b)
c=b; //multi-driver
以下的读变量代码没问题
always @(a)
c=a;
always @(a)
d=~a;
写变量注意事项
关于一写,就是把握一个原则,每次动作只能修改一次变量的值,动作分为组合动作和时序动作,组合逻辑输入信号的每一次变化称为一个组合动作,时钟信号的每一次有效沿称为一个时序动作,正所谓每次动作只能修改一次变量的值。例如下述的代码就想在一次动作发生后改变两次变量的值。
always @(a,b) //错误的组合动作思路
begin
c=a;
c=b;
end
always @(posedge clk) //错误的时序动作思路
begin
d<=a;
d<=b;
end
在实际中,由于路径延迟等原因,输出的确有可能发生多次反转,这不是FPGA设计的初衷,而是组合逻辑中的竞争与冒险。
读变量注意事项
关于多读,其实就是该变量可以作为多个并行语句的输入变量,无论这些语句是描述时序逻辑的还是组合逻辑的,不过当读变量的并行语句同时也是那唯一的一条写变量并行语句的时候,一定要避免引入反馈。
例如下述的代码就存在问题
always @(a)
begin
c=a^c;
end
但是如下的代码就是没有问题的
always @(posegde clk)
begin
c<=a^c;
end
这是因为时序逻辑在时钟上升沿才将输入搬移到输出的,其反馈路径实际上是被触发器截断的,所以上述时序逻辑中的代码不叫做反馈。

赋值冲突

赋值冲突,是写变量时经常遇见的一类问题,主要分为两类
1.两个及以上并行语句赋值冲突
这会造成编译器报错
2.两个及以上串行语句赋值冲突
(1)组合并行语句内串行语句的赋值冲突
这类赋值冲突分为三种:
第一种,无反馈的组合串行赋值冲突,这类冲突发生时,按照HDL串行语句的执行顺序,写在最后面的一条语句才是有效的例如:
always @(a,b)
begin
t=a&b; //无效赋值
t=a|b; //有效赋值
end
第二种:反馈在前的组合串行赋值冲突,这种赋值冲突的分析结果与上一种一样,尽管反馈现象是不好的,但是由于后续的无反馈代码对信号的值进行了重新设置,使得反馈代码无效,例如:
always @(a,b)
begin
t=~t; //无效赋值
t=a|b; //有效赋值
end
第三种,反馈在后的组合串行赋值冲突,这类赋值冲突的分析结果与前两种截然不同,它不是简单地忽略掉之前代码对变量的赋值操作,而是将之前代码得到的变量值代入到最后一个具有反馈的赋值语句中,例如:
always @(a,b,c)
begin
t=a&b;
t=t&c; //equal to: t=a&b&c
end
(2) 时序并行语句内串行语句的赋值冲突
在时序逻辑中实际上是不存在真正的反馈的,因为位于描述时序逻辑的串行语句中出现赋值冲突时,按照HDL串行语句的执行思路,写在最后面的一条语句才是有效的。
always @(posedge clk)
begin
t<=a&b; //无效赋值
t<=a|b; //有效赋值
end
(3)利用赋值冲突编写代码
赋值冲突不是一无是处,有一种利用无反馈的组合串行赋值冲突来简化HDL代码的技巧
例如:
always @(sel)
begin
case(sel)
2’b00:
begin
flaga=1’b1;
flagb=1’b0;
flagc=1’b0;
flagd=1’b0;
end
2’b01:
begin
flaga=1’b0;
flagb=1’b1;
flagc=1’b0;
flagd=1’b0;
end
2’b10:
begin
flaga=1’b0;
flagb=1’b0;
flagc=1’b1;
flagd=1’b0;
end
2’b11:
begin
flaga=1’b0;
flagb=1’b0;
flagc=1’b0;
flagd=1’b1;
end
default:
begin
flaga=1’b0;
flagb=1’b0;
flagc=1’b0;
flagd=1’b0;
end
endcase
end
由于描述的是一个组合逻辑电路,必须在每一个条件分支都保证flaga-flagd必须有一个确定的值,否则会在设计中引入锁存器,只要利用无反馈的组合串行赋值冲突,便可以大大简化代码:
always @(sel)
begin
flaga=1’b0;
flagb=1’b0;
flagc=1’b0;
flagd=1’b0;
case(sel)
2’b00: flaga=1’b1;
2’b01:flagb=1’b1;
2’b10: flagc=1’b1;
2’b11: flagd=1’b1;
endcase
end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值