在组件实例化期间将共享变量的(初始)值传递给泛型

Passing the (initial) value of a shared variable to a generic during component instantiation

我正在尝试构建一个测试平台,每个测试用例都由一个记录表示,该记录包含测试用例的所有参数,例如输入文件名、用于 DUT 实例化等的泛型。这个想法是只需要更改一个分配即可在不同的测试用例之间切换。

type string_ptr is access string;
type test_case_t is record  
    input_file : string_ptr;  
    ...  
end record;  

shared variable test_case_1 : test_case_t := (  
    input_file => new string'("path to input file 1")
    ...  
);  
shared variable TEST_CASE : test_case_t := test_case_1;

dut: my_module
    generic map (
        file_name => TEST_CASE.input_file.all
    );
        ...

字符串指针用于 input_file,因为在记录类型声明(在本例中,VHDL-2008 之前)中不允许使用无约束数组。
由于 test_case_t 中的字符串指针,我必须为 test_case_1.
使用共享变量 但是,当我尝试在 Xilinx ISE/ISim 14.4 中模拟此测试台时,模拟器一直使用通用的默认值,而不是我在测试台中传递给它的值(我认为这是一个错误)。

我试图通过

解决这个问题
constant INPUT_FILE : string := TEST_CASE.input_file.all;

dut: my_module
    generic map (
        file_name => INPUT_FILE
    );
    ...

这基本上是使用共享变量的初始值作为常量的初始值。但是,此分配甚至会在仿真开始之前使 Xilinx 编译器崩溃(我认为是另一个错误)。

此时我不再信任 Xilinx 工具。
我的问题是共享变量是否可以用于将通用值传递给模块,如上所示(第一个代码片段)?
另外,这种共享变量的使用是否适合测试平台?我会为 test_case_1 使用常量或信号,但由于在 test_case_t 中使用 string_ptr 类型,这似乎是不允许的。

(根据评论进行总结和扩展...)放弃尝试将泛型映射到变量(或信号)。在运行时,泛型是常量。它们的值在详细说明时最终确定。

您说得对,常量不能保存访问类型,并且您不能在记录中保存可变长度字符串。这是 VHDL 与 Ada 相去甚远的一个领域 - 后者具有区分记录,可以无缝处理可变长度字符串,不需要访问类型。

一种方法是制作一个字符串 table(字符串数组 - 或者如果您需要可变长度字符串,访问字符串数组)并将索引保​​存在记录中。

避免间接访问是好的。在需要常量的地方滥用变量……不太好。我的怪癖是倾向于避免访问类型,除非有充分的理由使用它们。

例如,在这里我可能会直接在记录中存储 space 填充的固定长度字符串,一个 "pad" 函数从任意字符串填充记录成员,以及一个 "trim" 在他们的使用点发挥作用。与字符串 table 相比,这将消除两级间接。

subtype filename is string(1 to 100);

type test_case_t is record  
    input_file : filename;  
    ...  
end record;  

function pad (name : string ) return filename;
function trim (name : filename ) return string;

constant test_case_1 : test_case_t := (  
    input_file => pad("path to input file 1"),
    ...  
);  

file_open(f, trim(test_case_1.input_file));

泛型可以是(子)类型的文件名。因此,上面的大部分声明通常都在一个包中,在主测试台和任何使用此类通用的实体中都使用。

您可以将任何固定长度的字符串表达式作为泛型常量的值作为泛型映射中的实际值关联到具有未绑定子类型指示的形式字符串类型。

为了理解为什么这有效,我们转向对泛型的阐述。

IEEE 标准 1076-2008(LRM):

14.3 块、包或子程序头的详细说明

14.3.2 通用子句

Elaboration of a generic clause consists of the elaboration of each of the equivalent single generic declarations contained in the clause, in the order given. The elaboration of a generic declaration establishes that the generic can subsequently be referenced.

6.5.6.2 通用条款(第 5 段)

The subtype denoted by a generic type is specified by the corresponding actual in a generic association list. It is an error if no such actual is specified for a given formal generic type (either because the formal generic is unassociated or because the actual is open).

(请注意,泛型的实际是一个表达式,关联列表是泛型映射。另请注意,这与 Brian 的预期不符,即如何从他的 Ada 中确定作为泛型的字符串的子类型指示背景。)

所以这意味着:

entity my_module is
    generic ( constant file_name: string := "default_string");
end entity;

architecture foo of my_module is
begin
UNLABELED:
    process
    begin
        report "generic file_name = " & file_name;
        wait;
    end process;
end architecture;

entity foo is
end entity;

architecture fum of foo is

    type string_ptr is access string;
    type test_case_t is 
        record  
            input_file : string_ptr;  
        end record;  
    shared variable test_case_1 : test_case_t := 
            (input_file => new string'("""path to input file 1"""));  
    shared variable TEST_CASE : test_case_t := test_case_1;

    component my_module is
        generic (
            constant file_name: string := "default string"
        );
    end component;
begin
dut: my_module
    generic map (
        file_name => TEST_CASE.input_file.all
    );
end architecture;

是合法的 VHDL:

ghdl -a my_module.vhdl
ghdl -e foo
ghdl -r foo
my_module.vhdl:10:9:@0ms:(report note): generic file_name = "path to input file 1"

还有这个:

architecture fuu of foo is

    -- type string_ptr is access string;
    -- type test_case_t is
    --     record
    --         input_file : string_ptr;
    --     end record;
    -- shared variable test_case_1 : test_case_t :=
    --         (input_file => new string'("""path to input file 1"""));
    -- shared variable TEST_CASE : test_case_t := test_case_1;

    component my_module is
        generic (
            constant file_name: string := "default string"
        );
    end component;
begin
dut: my_module
    generic map (
        file_name => """some other string""" -- TEST_CASE.input_file.all
    );
end architecture;

给出:

ghdl -a my_module.vhdl
ghdl -e foo
ghdl -r foo
my_module.vhdl:10:9:@0ms:(report note): generic file_name = "some other string"

所以这告诉了我们一些事情。

ISIM 在实现泛型方面不太符合标准。从这两种架构的差异我们可以看出,您的特定 ISIM 版本可能无法根据上面引用的 6.5.6.2 确定实际的子类型,注意您正在尝试处理字符串子类型(长度)。 (如果有人指出 Xilinx 会修复它,您可能会想到)。

您可以尝试在实体声明的通用子句中不提供默认表达式(注意组件声明应该匹配)。见 6.5.6.2 第 4 段:

The value of a generic constant may be specified by the corresponding actual in a generic association list. If no such actual is specified for a given formal generic constant (either because the formal generic is unassociated or because the actual is open), and if a default expression is specified for that generic, the value of this expression is the value of the generic. It is an error if no actual is specified for a given formal generic constant and no default expression is present in the corresponding interface element. It is an error if some of the subelements of a composite formal generic constant are connected and others are either unconnected or unassociated.

这是非此即彼的情况,如果两者都不是错误。

传播顶级常量或泛型(在工具实现支持的情况下)需要通过每个连续的分层块元素的泛型传递每个唯一值。这将要求实现正确支持 6.5.6.2。 (参见上面关于 6.5.6.2 第 4 段的内容)。

如果你真的很聪明并且模拟和综合实现正确地支持配置声明,那么可能有一种方法可以通过配置来做一些事情(提供两个不同的使用子句和两个不同的包,提供组件声明和通用子句中的两个不同的实际值- 这还要求 VHDL 实现也支持 6.5.6.2,另请参见上面的第 4 段)。此方法允许您为不同的实际值集模拟不同的配置。

正如 Brian 所说,对于泛型,每次模拟只能使用一个文件 运行。这对于输出文件是可以的,但是,对于输入文件,您可能想要读取一个,然后再读取另一个,依此类推。

我喜欢在模型中使用共享 variable/protected 类型的对象,如下所示:

library osvvm ;    
architecture Test1 of Model1 is 
  use osvvm.NamePkg.all ; 
  shared variable FileName : NamePType ; 
  file TestFile : TEXT ;
begin

  Functionality : process
  begin
    WaitForTransaction(. . .) ; 
    case ModelRec.Operation is
      . . . 
      when OPEN_NEW_FILE => 
        file_close(TestFile) ;
        file_open(Status, TestFile, FileName.Get, READ_MODE);

然后在将文件移交给该模型的测试平台部分,我使用外部名称访问文件:

architecture Test_Model1_1 of TestCtrl is
  alias Model1FileName is <<variable 
    .tbmemio.U_Model1.FileName : NamePType>> ;
begin
  . . . 
  Model1TestProc : process
  begin 
    . . .
    Model1FileName.set("Test1.txt") ; 
    . . . 

NamePkg 位于 OSVVM 库中,可从 http://osvvm.org/ 下载。