欢迎来看我的VHDL学习笔记,这是我写的第一篇,是关于我在用元件递归来设计n输入与门的全过程。

上个月没有时间主要是懒,所以没有达成每月至少一篇的小目标,这个与争取多写几篇。

设计n输入与门的想法是在做8421BCD计数器实验的时候冒出来的。设计这个计数器是用的D触发器,所以需要用到三输入的与门和或门。然后,我就联想到,如果是n输入的与门该怎么实现呢?

1 元件递归设计

首先声明一点,元件递归的设计不是正常的设计,而且不可综合

元件(component)是重复使用entity的一种方式,可以在此基础上设计出通用的实体在项目中使用,具体语法请查阅IEEE Std 1076-2019

在上数电课的时候,应该都知道设计多级逻辑门的时候最好要设计比较平衡的结构,以降低延迟带来的影响。所以,我在设计n输入与门的时候也想往这个方向靠。但是事实证明我这个考虑似乎是多余的,见下一章
为了实现平衡的设计,我准备使用二分法。方案一,通过generate循环,使用二维数组存储每次二分的结果,循环次数可以通过\(n = \lceil log_{2}{m} \rceil\)得到;但这个方案感觉比较麻烦,选择放弃。
方案二,利用元件自己调用自己,通过递归来实现二分法。首先,将输入端口数port_num为1或者2作为递归结束条件,直接使用and操作符输出结果;然后,针对输入端口数port_num为奇数和偶数分别进行操作,将二分后的结果连接到信号re中,最后对re做与操作输出到outp。代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity add_gate is
generic (
port_num : integer := 6
);
port (
inp : in unsigned(port_num - 1 downto 0);
outp : out std_logic
);
end add_gate;

architecture behavioral of add_gate is
component add_gate is
generic (
port_num : integer := 6
);
port (
inp : in unsigned(port_num - 1 downto 0);
outp : out std_logic
);
end component;

signal re : unsigned(1 downto 0) := "00";
begin

gen : if port_num = 1 generate
outp <= inp(0);
elsif port_num = 2 generate
outp <= inp(0) and inp(1);
elsif port_num mod 2 = 1 generate -- odd
add1 : add_gate generic map (port_num => port_num / 2 + 1)
port map (inp => inp(port_num / 2 downto 0), outp => re(0));
add2 : add_gate generic map (port_num => port_num / 2)
port map (inp => inp(port_num - 1 downto port_num / 2 + 1), outp => re(1));
outp <= re(0) and re(1);
else generate -- even
add1 : add_gate generic map (port_num => port_num / 2)
port map (inp => inp(port_num / 2 - 1 downto 0), outp => re(0));
add2 : add_gate generic map (port_num => port_num / 2)
port map (inp => inp(port_num - 1 downto port_num / 2), outp => re(1));
outp <= re(0) and re(1);
end generate;

end behavioral;

简单写了个仿真文件,仿真结果如下。

综合前仿真结果

当然了,元件递归是不可综合的,综合是会出现如下的弹窗。

综合元件递归综合时出错

2 正常的n输入与门设计

一般来说,设计n输入与门是不会使用二分法的。虽然思路没错,但是,真的没有必要,原因我会在后面提到。下面两段代码都是比较正常的而且可以使用的n输入与门的设计。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-- 示例一
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity add_gate is
generic (
port_num : integer := 6
);
port (
inp : in unsigned(port_num - 1 downto 0);
outp : out std_logic
);
end add_gate;

architecture behavioral of add_gate is
signal tmp : unsigned(port_num - 1 downto 0);
begin

tmp(0) <= inp(0);
gen : for i in 1 to port_num - 1 generate
tmp(i) <= tmp(i - 1) and inp(i);
end generate;
outp <= tmp(port_num - 1);

end behavioral;

示例一使用for/generate将后加入的输入信号和之前的结果进行与操作,RTL原理图如下。

示例一RTL原理图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- 示例二
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity add_gate is
generic (
port_num : integer := 6
);
port (
inp : in unsigned(port_num - 1 downto 0);
outp : out std_logic
);
end add_gate;

architecture behavioral of add_gate is
begin

outp <= '1' when inp = to_unsigned(2 ** port_num - 1, port_num) else
'0';

end behavioral;

示例二利用了n输入与门的实质,即所有输入都为高电平才输出高电平,否则输出低电平,RTL原理图如下。

示例二RTL原理图

接下来,就要说说为什么二分法没有必要了。很显然,示例一的RTL图是一点都不平衡的设计,如果在实际连接中使用这个结构势必会出现延时的问题。但是,如果综合这两个示例,如下所示,就会发现这两个示例的RTL原理图虽然大相径庭,但是他们的综合后原理图是一模一样的。图中的LUT(查找表)是FPGA的基本组成单元,可以当做是一个n*1的RAM。以图中的6输入LUT为例,LUT内部存储了\(2^n\)个1bit的数据,地址是6位的;输入信号是地址总线,LUT根据输入的地址查找到相应地址的数据,将数据直接输出到LUT的输出端口上。

示例一综合后原理图
示例二综合后原理图

3 总结

虽然这次用了元件递归来设计n输入与门才发现元件递归是不能综合的 :&(蛆音娘_扶额) ,并且综合工具的优化功能是很强大的,但是,这次尝试真的特别有意思,让我对FPGA的设计有了新的关注点。之后的学习过程中,我准备将之前学过的算法设计思想添加到FPGA设计中,找到新的乐趣。 :@(击掌)

最后的最后,请别忘了一键三连收藏分享。