挺久没有更新,今天更新一篇小文章。最近正在整理一个SDRAM控制器的教程(VHDL),现在更新的小文章是想为后续的SDRAM教程以及其它比较大的教程做铺垫。本文主要讲关于VHDL编码风格(Verilog也可以用同样的思想),这篇文章的核心思想就是:设计中的所有状态都应该被明确声明在寄存器中。
这句话什么意思先不谈,直接上两段代码,第一段如下:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use work.ff.all; -- 一些常用的元件包,这里面是可以自己写的
-- 这篇文章主要就调用了基本的D触发器,里面还有什么可以先忽略
entity Bad_Counter is
generic( n: integer := 4 );
port( clk, rst: in std_logic;
output: buffer std_logic_vector(n-1 downto 0) );
end Bad_Counter;
architecture impl of Bad_Counter is
signal nxt: std_logic_vector(n-1 downto 0);
begin
process(clk) begin
if rising_edge(clk) then
if rst then
output <= (others => '0');
else
output <= output + 1;
end if;
end if;
end process;
end impl;
一个4位计数器,能实现设计的功能,但其中output <= output + 1存在一点问题,这种写法非常C语言化,这也是很多人习惯的写法。为什么说这种写法不好,因为它混淆了当前状态和下一状态。先不多解释,看下一段代码如下:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use work.ff.all; -- 一些常用的元件包,这里面是可以自己写的
-- 这篇文章主要就调用了基本的D触发器,里面还有什么可以先忽略
entity Good_Counter is
generic( n: integer := 4 );
port( clk, rst: in std_logic;
output: buffer std_logic_vector(n-1 downto 0) );
end Good_Counter;
architecture impl of Good_Counter is
signal nxt: std_logic_vector(n-1 downto 0);
begin
nxt <= (others=>'0') when rst else output+1;
count: vDFF generic map(n) port map(clk, nxt, output);
end impl;
这段代码同样的是4位计数器,为什么用这两段代码做比较,非常典型,第一段代码的思想就是C语言的思想来写的,很多人可能没有注意自己在写“数字电路”,也没有特别关注写出来的代码综合出来是什么样子。第二段代码明确表明nxt是下一状态,并且nxt是通过当前状态output得到的,这很重要,与此同时还调用了一个D触发器元件,这个D触发器为什么用,之前在异步FIFO(二)中有谈到:在实现所有的状态变量都应该被明确的声明位D触发器,不要让编译器去推断应该用什么触发器。
讲到这里,可能还是不懂这两份代码有什么具体的差别,我做了一个实验,把两份代码都综合了一遍,使用了两个不同的版本,Quartus20.3和Quartus13.1(不同版本的软件,无论是效率还是开发便捷程度还是有一定差别的)
先看Quartus13.1综合出的两份原理图
这幅原理图是第一份代码综合出来了,大概看一下没什么问题,代码也是这样写的。
再看第二份代码综合出来的原理图
区别来了,很明显,第一份代码因为用output <= output + 1的原因,左右两端使用了相同的信号,混淆当前状态和下一状态,下一状态被隐藏起来。但第二份代码综合出来的很明显,nxt是下一状态,并且nxt是通过当前状态output得到的。当然还要个比较好的地方,第二份代码直接调用D触发器元件(在package里面写了),告诉编译器状态变量要声明为D触发器,而不是让编译器自己判断(尽管它可以)。
再看Quartus20.3综合出来的原理图
这个原理图是第一份代码Bad_Counter 综合出来的,很显然现在的EDA真行,可以把不那么优秀的代码综合出优秀代码才能综合出的原理图了。当然这是很简单代码的情况下。
这个原理图是第二份代码Good_Counter综合出来的,除了下一状态的命名和位数区间不一样外,其它的两个代码综合出来的没有区别!
小结:从上面的四幅原理图看,尽管随着EDA的发展让不那么优秀的代码综合出和优秀代码一样的原理图,但良好编码风格的代码仍然非常必要,即使是13.1和20.3巨大版本差异的情况下,良好编码风格的代码综合出来的原理图也是一样的。但在代码风格不好的情况下,13.1和20.3却有比较大的差距。人作为设计的主体,不能完全依靠EDA的优化,编译器也不是万能的,有些其它地方未必会优化的那么好,在大型复杂项目中不太好的编码风格可能编译器也未必能够给出比较好的优化,好的编码风格可以让我们更好的理解一些底层的设计,也能知道代码会如何综合。更重要的是需要知道自己数字设计师!用数字的思想去设计FPGA。这些只是自己的意见,大家可以做一个参考,有不对的地方也欢迎批评指正。
没有回复内容