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

时间:2013-11-19 13:29:42

标签: 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: 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的新手,我认为这应该是一个常见的问题,但我找不到解决方案。显然,我想避免使用复制粘贴解决方案。

4 个答案:

答案 0 :(得分:5)

如果没有reg_num的其他用途,只需将sel_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”中可以让您使用单个文件对整个设计进行参数化,并且不存在某些泛型的硬编码(可能是错误)值的风险。

(*)为什么时钟频率?从一个参数扩展所有时序延迟,波特率参数,存储器等待状态等是非常有用的,知道当我改变时钟时它们都将保持正确...

答案 1 :(得分:2)

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

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;

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

答案 2 :(得分:1)

您指出的问题通常发生在通用模块上,例如制作一个 具有多个条目的通用RAM和长度为addr_len的地址总线 指出一个特定的条目。条目数通常为2 ** addr_len,但根据实施情况可能会更少。

正如Brian Drummond指出的那样,拥有一个功能可能非常有用 可以将具有条目数的值转换为给出数量的值 所需的位,因此ceil(log2(entries)),这样可以在一个共同的包中:

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;

但是,使用register_file的模块也必须知道其长度 rd_sel/wr_sel,以便产生连接实体的信号,从而 必须致电ceil_log2以确保使用正确的长度。 因此,要使界面更明确,更简单,一种方法就是简单 同时提供reg_numsel_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;

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

答案 3 :(得分:1)

考虑的替代方案(我不一定在这种情况下推荐它,它在某种程度上取决于它将如何实例化)

...删除泛型并使用不受约束的向量:

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;

这样,在实例化寄存器文件的模块中选择的位数就会选择排列。

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

最后,您可以在架构中使用断言来确保data_in'length = data_out'lengthwr_sel'length = rd_sel'length


(注意我制作了sel个端口unsigned因为您无疑会最终将它们用作索引(即数字)并且它会节省一些转换。您还可以使它们成为整数,那么寄存器文件的宽度将由传入的整数范围定义