【问题标题】:Passing the (initial) value of a shared variable to a generic during component instantiation在组件实例化期间将共享变量的(初始)值传递给泛型
【发布时间】:2015-06-15 08:56:19
【问题描述】:

我正在尝试构建一个测试平台,使每个测试用例都由一个记录表示,该记录包含测试用例的所有参数,例如输入文件名,用于 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 工具。
我的问题是,是否可以使用共享变量将通用值传递给如上所示的模块(第一个代码 sn-p)?
此外,这种共享变量的使用是否适合测试台?我会为test_case_1 使用常量或信号,但由于在test_case_t 中使用string_ptr 类型,这似乎是不允许的。

【问题讨论】:

  • 放弃尝试将泛型映射到变量(或信号)。在运行时,泛型是常量。它们的值在细化时最终确定。只需在测试模块上创建一个输入端口,类型为 test_case_t。
  • 澄清一下,我并没有真正将共享变量用作“变量”。我只为它们分配一次初始值。我不明白为什么(共享)变量的初始值不能用于设置常量/泛型,因为变量的初始值在细化时是常量。我本来可以为test_case 使用一个常量,但是常量不能保存访问类型(或者通常是任何具有动态分配的类型),这是由于可变长度字符串应该是test_case 的一部分而需要的。因此,第二个 sn-p 中显示了解决方法。
  • 然后将它们设为常量。一种方法是创建一个字符串表(字符串数组 - 或者如果您需要可变长度字符串,访问字符串数组)并将索引保​​存在记录中。
  • 这是 VHDL 简化与 Ada 相去甚远的一个领域——Ada 具有区分记录,无缝处理可变长度字符串,无需访问类型。
  • 字符串表听起来像是一个可行的解决方案。我会试一试。借助大多数 IDE 提供的大量 VHDL 支持,我通常会尽量避免使用许多间接方式(有时我太努力了)。

标签: vhdl xilinx hdl


【解决方案1】:

(从 cmets 总结和扩展...)放弃尝试将泛型映射到变量(或信号)。在运行时,泛型是常量。它们的值在细化时最终确定。

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

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

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

例如,在这里我可能将空格填充的固定长度字符串直接存储在记录中,一个“填充”函数用于从任意字符串填充记录成员,并在它们的使用点存储一个“修剪”函数。与字符串表相比,这将消除两级间接。

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));

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

【讨论】:

    【解决方案2】:

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

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

    IEEE 标准 1076-2008(LRM):

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

    14.3.2 通用子句

    泛型子句的详细说明包括按给定顺序详细说明该子句中包含的每个等效的单个通用声明。泛型声明的详细说明确定了泛型可以随后被引用。

    6.5.6.2 通用条款(第 5 段)

    由泛型类型表示的子类型由泛型关联列表中对应的实际指定。如果没有为给定的正式泛型类型指定此类实际值(因为正式泛型未关联或因为实际值是开放的),则会出现错误。

    (请注意,泛型的实际是一个表达式,关联列表是泛型映射。另请注意,这与 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 段:

    泛型常量的值可以由泛型关联列表中对应的实际值指定。如果没有为给定的形式泛型常量指定此类实际值(因为形式泛型未关联或因为实际值是开放的),并且如果为该泛型指定了默认表达式,则此表达式的值是泛型的值.如果没有为给定的形式泛型常量指定实际值并且相应的接口元素中不存在默认表达式,则这是错误的。如果复合形式泛型常量的某些子元素是连接的,而其他子元素是未连接的或未关联的,则会发生错误。

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

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

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

    【讨论】:

    • 感谢大卫的详细回答。虽然布赖恩斯的回答为我的问题提供了解决方案,但您的回答是对我最初问题的回答,因此我将接受它作为“答案”。我要感谢你们俩 ;-)
    【解决方案3】:

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

    我喜欢在模型中使用共享变量/受保护类型对象,如下图:

    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/ 下载。

    【讨论】:

      猜你喜欢
      • 2022-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-25
      相关资源
      最近更新 更多