VHDL常见错误分析
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
VHDL常见错误分析
VHDL常见错误分析
一、较好的RTL代码风格
1、可综合
用HDL语言编程,应时刻牢记所写代码能否反映期望的硬件及其功能,而且电路结构是可实现的。
为保证设计的可综合,代码中的指令和数据类型必须是综合工具所支持的,同时代码的结构也必须使综合得到的硬件功能和代码所体现的功能一致。
2、简洁清晰,易于理解
最易懂的写法也就是效率最高的写法。
使用一致的命名规则。
3、注释清楚
4、模块化程度高,易于修改、移植和复用
需要将代码中的常数写成参数化的形式。
5、模块例化时,为清楚起见,使用显示的端口说明
6、为了避免很大的路径延时,不要使用特别长的嵌套的if结构。
7、资源共享
资源共享的主要思想是通过数据缓冲器或多路选择的方法来共享数据通路中工作单元(为了提高速度,对于关键路径不要使用该技术。
一般在算术单元和关键路径中不采用资源复用。
)
8、避免使用门限时钟
门限时钟可能会引入短脉冲干扰,增加时钟延时、时钟扭曲以及其他一些后果。
9、代码中不要带有延时信息(行为级描述与testbench例外)
10、进行数学运算时,要有好的顺序和分组。
好的顺序及分组可能影响综合结果。
ADD=A1+A2+A3+A4; --三级加法,较差
ADD=(A1+A2)+(A3+A4); --二级加法,较好为了符合时序要求,综合工具能重新构造算术单元。
我们还是要按所需结果书写代码。
11、代码中不要带初始化赋值语句。
综合工具会忽略掉这种初始化的语句,所以会导致综合前后仿真结果不一致。
二、对可综合设计的一些建议
1、在电路内部应尽量避免使用三态门,但是在电路边缘引脚处可以使用。
2、内部总线应该尽量避免使用双向总线,一般使用单向总线。
3、模块的划分应该尽量以寄存器为边界。
4、应该尽量避免使用锁存器,因为有可能传输毛刺,而寄存器不会传输毛刺。
5、时序逻辑应该注意信号边沿的检测,防止亚稳态的传输。
6、串并转换应尽量使用移位寄存器,少用计数器,以提高速度。
7、组合环应在边沿加上寄存器,使其在时钟的控制下工作。
8、时钟分频和倍频电路一般不易使用高层描述;分频后的信号一般不作为时钟信号,因为其驱动能力较弱,容易产生时钟偏斜。
9、在设计的顶层,最好不要实例化门,因为这可能导致代码难以优化。
10、如果要使用门限时钟,内部生成的时钟,或者使用时钟的两个沿,则应该构
造一个时钟生成单元模块。
11、建议将状态机分成两个部分:一部分用于组合逻辑,一部分用于时序逻辑。
12、状态机的状态编码最好也使用参数化的表示方式,这有利于将来的改动。
三、常见错误及举例
1、行为描述中常见问题
在VHDL 语言中,一条信号带入语句就可以创建一个驱动器。
当同一赋值目标处于不同进程中时,其赋值结果就比较复杂。
这可以看成是多个信号驱动源连在一起,可以发生线或、线与或者三态等不同结果。
若处理不当,则会出现驱动器冲突。
所以应避免在不同的进程中对同一个信号赋值,即多重源赋值问题。
如例1所示。
例1:
process(clk1)
begin
if clk1'event and clk1='1' then
y<=m;
end if;
end process;
process(clk2)
begin
if clk2'event and clk1='2' then
if ena=?1? then
y<=n; --禁止使用
end if;
end if;
end process;
2、RTL 描述中常见问题
RTL 描述又称为寄存器描述。
在寄存器描述中,VHDL 语言的使用并不是任意的,而是有一定限制的。
2.1 在一个进程中,禁止使用两个寄存器描述,对两个寄存器的描述是不允许的。
如例2 所示:
例2:
process(clk1,clk2)
begin
if (clk1'event and clk1='1' )then
y<=m;
end if;
if (clk2'event and clk2='1' )then --错误
x<=m;
end if;
end process;
2.2 在进程中,对变量要先读后写。
如例3 所示:
例3:
process(clk,reset)
variable a:std_logic;
begin
if reset = '1' then
a:= '0';
outsignal<='0';
elsif rising_edge(clk) then
outsignal<=a; --先读
a := insignal; --后写
end if;
end process;
因为变量值是立即获得的,所以如果先写后读就会产生长的组合逻辑和锁存器(或寄存器)。
因此在编写代码过程中,对变量要先读后写。
2. 3 用IF 语句描述寄存器功能时,禁止使用ELSE 项。
对于RTL 描述方式中的IF 语句在其描述寄存器功能时,禁止采用ELSE项,因为这种赋值方式相当于检测如果没有时钟信号时,则赋新值,而实际上不可能有这样的硬件电路与之对应,如例3 所示。
而如例4所示的ELSE 项在RTL 描述方式中是可以使用的。
例3:
process(clk)
begin
if clk'event and clk='1' then
y<=m;
else --禁止使用
y<=n;
end if;
end process;
例4:
process(clk)
begin
if clk'event and clk='1' then
if ena='1' then
y<=m;
else --可以使用
y<=n;
end if;
end if;
end process;
2. 3 在组合逻辑进程中,其敏感向量表中要包含所有要读取得信号,以防止出现不必要的锁存器。
如例5所示:
例5:
compare:process --列出敏感信号
begin
if data=count then
equals <=‘1’;
end if;
end process;
2. 4 避免使用门限时钟,门限时钟可能会引入短脉冲干扰,增加时钟延时、时钟扭曲等。
如例6所示。
例6:
PRO1:PROCESS (CLOCK,A,B,C)
BEGIN
IF NOT(CLOCK?EVENT AND CLOCK=?1?)THEN --错误
X<=(A XOR B)OR C ;
END IF;
END PROCESS;
3、语言描述中常见的语法错误
3.1程序头
在程序头中,应该列出程序中所有使用到的程序库及库中什么程序包。
例如,在程序中使用“+”运算,那么程序头必须列出程序行“use IEEE.std_logic_arith.all;”。
3.2 实体和结构
实体名不能使用VHDL语言中的关键字,必须符合VHDL的文字规则。
例如实体名不能以数字开头,名字中不能出现“-”、“#”等。
3.3 端口
端口命名必须符合VHDL文字规则;
必须正确标出端口模式及其数字类型
不要在代码中使用buffer类型的端口读取输出数据,要使用out 类型,再增加另外变量或信号,以获取输出值。
因为buffer类型的端口不能连接到其他类型的端口上,因此buffer类型就会在整个设计的端口中传播下去。
如例7所示。
例7:
PROCESS (CLK, RST_n)
variable out_var : std_logic;
BEGIN
IF RST_n = '0' THEN
Outsignal <= '0';
out_var <='0';
outsign2 <= '0';
ELSIF CLK'event AND CLK = '1' THEN
Outsign2 <= out_var; -- the same as Outsignal
out_var := input1 and input2;
Outsignal <= input1 and input2;
END IF;
END PROCESS;
4、其他常见错误
4.1、在一个表达式中有两个以上的运算符时,需要使用括号将这
些运算分组。
如果一串运算中的算符相同。
且是AND、OR、XOR这三个算符中的一种,则不需要使用括号;如果一串运算中的算符不同或者有除这三种运算符之外的算符,则必须使用括号。
例如:
A and
B and
C and
D 正确
(A or B) xor D 正确
A or
B xor D 错误,不为同种算符需要加括号
还要注意:
严格遵循在基本操作符简的操作数是同数据类型的规则;
严格遵循操作数的数据类型必须与操作符所要求的数据类型完全一致。
4.2、在写多路选择器MUX时忘记else语句而引入了锁存器
例如:
process (sel,a,b)
begin
if (sel=?1?) then dout<=a;
end if ;
end process;
没有else分支导致综合后形成锁存器而非数据选择器。
4.3、CASE语句赋值条件重叠而导致错误。
例如:
SIGNAL V ALUE:INTEGER RANGE O TO 15;
SIGNAL OUT1:STD_LOGIC;………….......................
CASE V ALUE IS
WHEN 0 TO 10 =>OUT1<=?1?;
WHEN 5 TO 15=>OUT1<=?0?;
WHEN OTHERS=>OUT1<=?Z?;
END CASE;
赋值范围5 to 10 部分发生重叠。
4.4、CASE语句赋值条件不完整而导致错误。
例如:
signal s : std_logic_vector(1 downto 0); ………….......................
Process(s,a,b,c,d)
begin
Case s is
When “00”=> dout <=a;
When “01”=> dout <=b;
When “10”=> dout <=c;
When “11”=> dout <=d;
End case;
End process;
没有写others而当出现‘Z’‘X’等信号时将发生未知状态。
4.5、赋值时数据的位数对应关系错误
例如:
signal tmp :std_logic_vector(7 downto 0); ………….......................
Tmp <= “000000000”;
8位的数据宽度不能赋给9位的数据。
4.6、在多个if语句嵌套使用时,缺少对应的end if 而导致错误。
例如:process(clk,ena,din)
begin
if clk?event and clk=?1? then
if ena=?1?then d out<=din;
end if;
end process;
4.7、缺少对应的end process而导致错误。
例如:
com1:process(a,b,c)
begin
………….......................
com2:process(a,b,d)
begin
………….......................
end process;
4.8、进程的敏感信号表不完整而导致错误。
例如:
process(sel,a)
begin
if sel=?1?then d out <= a;
else dout <= b;
end if;
end process;
由于在敏感信号表中没有写b而当b的值发生变化时将不会启动进程。
4.9、元件例化语句中使用位置关系进行端口对应时,对应端口位置错误或者数
据位宽度不一致而导致错误。
4.10、在实体、结构体末尾书写end 时,可以将end后面的结构体名、实体名省略而直接简单写为end。
而在书写end if; end process; end case;时,后面的if、process、case都不能省略。
4.11、其他书写错误,例如:
1)将downto写为to
din :in std_logic_vector( 7 to 0);
2)对信号的赋值语句错写成:=或者=
signal a,b :std_logic; ………….......................
a := b;
a = b;
3)对变量的赋值语句错写成<=或者=
variable a,b :std_logic; ………….......................
a <= b;
a = b;
4)实体定义输入输出端口时最后一行多写了“;”例如:ENTITY counter IS
PORT
(
din : IN INTEGER RANGE 0 TO 15;
clk : IN STD_LOGIC;
crl : IN STD_LOGIC;
ena : IN STD_LOGIC;
load: IN STD_LOGIC;
count: OUT INTEGER RANGE 0 TO 15;
);
END counter;
5) “”与‘’书写错误,例如:
signal a : std_logic;
signal b : std_logic_vector(3 downto 0); ………….......................
a <= “1”;
b <= …0000?;。