如何在VHDL的端口声明中使用从泛型参数计算的常量?

如何在VHDL的端口声明中使用从泛型参数计算的常量?,vhdl,Vhdl,一个例子是我试图实现的通用寄存器文件,如下所示: entity register_file is generic(reg_width: integer := 32; reg_num: integer := 16); constant sel_num: integer := integer(CEIL(LOG(Real(reg_num)))); port ( data_in: in std_logic_vector(reg_width - 1 downto 0); data_out:

一个例子是我试图实现的通用寄存器文件,如下所示:

entity register_file is
generic(reg_width: integer := 32; reg_num: integer := 16);
constant sel_num: integer := integer(CEIL(LOG(Real(reg_num))));
port (
    data_in: in std_logic_vector(reg_width - 1 downto 0);
    data_out: out std_logic_vector(reg_width - 1 downto 0);
    rd_sel: in std_logic_vector(sel_num - 1 downto 0);
    wr_sel: in std_logic_vector(sel_num - 1 downto 0);
    rd_enable: in std_logic;
    wr_enable: in std_logic;
    clock: in std_logic;
);
end register_file;
这不起作用,因为它似乎是通用的,端口必须是前两个声明,然后是其他声明。如果在端口声明之后移动type和constant,则在处理端口声明时它们不可见


我不熟悉VHDL,我认为这应该是一个常见的问题,但我找不到解决方案。显然,我希望避免使用复制粘贴解决方案。

如果没有其他使用
reg\u num
,只需将
sel\u num
设置为通用

否则,编写一个名为
sel
的函数,将
reg_num
转换为另一个自然值。 问题是,函数放在哪里

在我的设计中,我倾向于将许多常见的数据类型、声明、函数和时钟周期(*)放在一个名为
common
的包中

package common is
   function sel(n : natural) return natural;
   constant clock_period : time := 1 sec / 32000000;

   constant num_regs : natural := 16;
   subtype sel_word is std_logic_vector(sel(num_regs) downto 0);
end common;  -- package body contains the function body
然后我的实体看起来像

use Work.common.all;

entity register_file is
generic(reg_width: integer := 32; reg_num: integer := 16);
port (
    rd_sel: in std_logic_vector(sel(reg_num) downto 0);  -- OK
    wr_sel: in sel_word;                                 -- better?
...
如果设计的不同部分需要不同的“regnum”值,则首选通用方法

否则,在“common”中定位这些细节允许您使用单个文件对整个设计进行参数化,并且对于某些泛型没有硬编码(可能是错误的)值的风险


(*)为什么是时钟频率?从一个参数中缩放所有定时延迟、波特率参数、内存等待状态等非常有用,因为我知道当我更改时钟时它们都将保持正确…

仅为了记录,这里有一个替代方案,尽管在这种情况下它可能不是最佳选择。您不能声明常量并在端口声明中使用它,但可以声明其值依赖于先前声明的泛型的泛型。您的代码如下所示:

entity register_file is
    generic(
        reg_width: integer := 32;
        reg_num: integer := 16;
        sel_num: integer := integer(CEIL(LOG(Real(reg_num))))
    );
    port (
        data_in: in std_logic_vector(reg_width - 1 downto 0);
        data_out: out std_logic_vector(reg_width - 1 downto 0);
        rd_sel: in std_logic_vector(sel_num - 1 downto 0);
        wr_sel: in std_logic_vector(sel_num - 1 downto 0);
        rd_enable: in std_logic;
        wr_enable: in std_logic;
        clock: in std_logic
    );

begin
    assert sel_num = integer(CEIL(LOG(Real(reg_num))));
end;

因为实例化实体的人可能会弄乱这些值,所以架构末尾的断言可以确保它们仍然是一致的。

您指出的问题通常出现在通用模块中,例如,生成 具有多个条目和长度为addr_len的地址总线的通用RAM 指出一个具体的条目。然后,条目的数量通常为
2**
addr_len
,但根据实现情况,可以减少

正如Brian Drummond指出的,拥有一个 可以将具有条目数的值转换为给定条目数的值 所需位,因此
ceil(log2(条目))
,这些位可以在一个公共包中:

package common is
  function ceil_log2(i : natural) return natural;
end package;

library ieee;
use ieee.math_real.all;
package body common is
  function ceil_log2(i : natural) return natural is
  begin
    return integer(ceil(log2(real(i))));  -- Example using real calculation
  end function;
end package body;
如果寄存器文件中的条目数始终为
reg_num=2**len
, 那么使用
ceil_log2
功能的实体可能是:

library ieee;
use ieee.std_logic_1164.all;

library work;
use work.common.all;

entity register_file is
  generic(
    reg_width : integer := 32;
    reg_num   : integer := 16);
  port (
    data_in   : in  std_logic_vector(reg_width - 1 downto 0);
    data_out  : out std_logic_vector(reg_width - 1 downto 0);
    rd_sel    : in  std_logic_vector(ceil_log2(reg_num) - 1 downto 0);
    wr_sel    : in  std_logic_vector(ceil_log2(reg_num) - 1 downto 0);
    rd_enable : in  std_logic;
    wr_enable : in  std_logic;
    clock     : in  std_logic);
end register_file;
但是,使用
寄存器\u文件的模块也必须知道
rd_sel/wr_sel
,以产生连接实体的信号,从而 必须调用
ceil_log2
以确保使用正确的长度。 因此,要使接口更加明确和简单,一种方法是 提供
reg_num
sel_num

entity register_file is
  generic(
    ...
    reg_num   : integer := 16;
    sel_num   : integer :=  4);
  port (
    ...
    rd_sel    : in  std_logic_vector(sel_num - 1 downto 0);
    wr_sel    : in  std_logic_vector(sel_num - 1 downto 0);
    ...
...
architecture syn of register_file is
  ...
begin
  ...
  assert 2 ** sel_num >= reg_num report "sel_num to small to address all registers given by reg_num";
end architecture;

断言将确保该实体正确使用。

< P>一个可供选择的备选方案(我不一定在这种情况下推荐它,这在某种程度上取决于它将被实例化)

。。。删除泛型并使用无约束向量:

entity register_file is
port (
    data_in: in std_logic_vector;
    data_out: out std_logic_vector;
    rd_sel: in unsigned;
    wr_sel: in unsigned;
    rd_enable: in std_logic;
    wr_enable: in std_logic;
    clock: in std_logic;
);
end register_file;
这样,您在实例化寄存器文件的模块中选择的位数选择排列

您可以使用“范围”中的数据定义其他匹配的内部信号或变量。并为设置为
2**rd_sel'length
的寄存器数量定义一个常量

最后,您可以在体系结构中使用断言来确保
data\u in'length=data\u out'length
wr\u sel'length=rd\u sel'length



(注意,我将
sel
端口
无符号
,因为毫无疑问,您最终将使用它们作为索引(即编号),这将保存一些转换。您也可以将它们设为整数,然后寄存器文件的宽度将由传入的整数范围定义)

+1,用于断言交叉检查假设。有一点我没有完全阐明:虽然“ceil_log2”在某种意义上是一个通用构建块,但我的等效“sel”功能专门用于此总线(假设在设计中有多种用途):因此我也可以将“sel_num-1”细节抽象到其中,对于一个整洁的设计,虽然制作reg_num和sel_num泛型肯定是一个没有问题的解决方案,但我更愿意让软件为我计算它。使人为错误的可能性降低:)我考虑过将sel_num作为泛型的想法,但在使用实体时,reg_num更自然。设计规范通常指定寄存器的数量,而不是选择信号的数量。感谢其他提示。最新的Quartus II说:“错误(10552):register_file.vhd(10):register_num in expression中的VHDL表达式错误”:(直到VHDL-2008才订购泛型。您可能必须打开开关才能使其工作。Oto,Altera可能尚未实现此功能。