【问题标题】:vhdl can't slice recordsvhdl 不能切片记录
【发布时间】:2019-02-21 03:20:22
【问题描述】:

我不能从一组记录中取出一个片段吗? 我声明这样的记录:

type mytype is record
    one : std_logic;
    two : std_logic;
end record;
Type mytypes is array (natural range <>) of mytype;

这样定义:

signal mt : mytypes(3 downto 0);
signal slv : std_logic_vector (3 downto 0);

当我像这样使用它时没关系:

slv(0) <= mt(0).one;
slv(1) <= mt(1).one;

但是这个:

slv(0 to 1) <= mt(0 to 1).one;

得到错误

"record type mytypes is used but not declared"

那么是不允许获取记录片还是我需要编写一些额外的代码来启用它?

我应该说 - Quartus II

【问题讨论】:

    标签: vhdl slice record


    【解决方案1】:

    您可以对记录数组进行切片

    signal mt, mt2 : mytype(0 to 3);
    mt2(0 to 1) <= mt(0 to 1);
    

    但你不能这样做:

    slv(0 to 1) <= mt(0 to 1).one;
    

    你必须这样做:

    slv(0) <= mt(0).one;
    slv(1) <= mt(1).one;
    

    顺便说一句:如果您使用downto 声明一个数组,则不能使用to 对其进行切片。切片的方向必须与声明的方向一致。

    【讨论】:

    • 我应该说“记录字段的切片数组”。
    • 好吧。谢谢你,但这是一个不合逻辑和任意的限制。我希望我可以编写一些额外的代码来启用它。而且错误消息也不是很有帮助。我浪费了很多时间试图弄清楚我是如何搞砸声明的。
    • 对表示记录元素的选定名称后缀的限制可以追溯到 Ada83 LRM,4.1.3 选定组件。现在可能有点晚了,这是单方面宣布限制不合逻辑和武断的错误场所。不过我同意,Quartus 错误消息并没有太大帮助。您可以先尝试模拟而不是直接进行合成,错误消息通常会更好。这不是你搞砸的声明,而是赋值语句。
    • 只是玩弄它,generate 似乎解决了我的问题,即改变数组的大小而无需更改代码。
    • 如果我误导了我,我深表歉意。数组不是可变的,但我想用一个常量来设置它的大小,我可能稍后会更改。自然地,我希望能够更改常量并保留代码。如果我不能写“slv(0 to msb)
    【解决方案2】:

    VHDL 标准最初基于 Ada'83 LRM,包括记录类型。 Ada 和 VHDL 都共享强类型并要求(在此使用 VHDL 术语)对于后缀是在记录类型声明中声明的元素名称的扩展名称,前缀是适当的(表示记录类型的值)。参见 IEEE 1076-2008:

    8.3 选定名称(第 3 和 4 段)

    选定的名称可以表示记录的元素、由访问值指定的对象或命名实体,其声明包含在另一个命名实体中,特别是在库、包或受保护类型中。此外,选定的名称可以表示其声明包含在库或包中的所有命名实体。

    对于用于表示记录元素的选定名称,后缀应是表示记录对象或值的元素的简单名称。前缀应适合此对象或值的类型。

    意思是

    slv(0 to 1) <= mt(0 to 1).one;
    

    产生错误(应表示强制性要求,参见本标准的 1.3 结构和术语)。

    这里的扩展名是mt(0 to 1).one, the suffix is the element nameone`和前缀mt(0 to 1),切片名是一个数组类型,其元素是一个记录类型。 (8.5,“切片名称表示一个一维数组,由另一个一维数组的一系列连续元素组成。”)

    该问题的原始发布者评论了一个未接受的答案,即使用生成状态找到了一个可行的解决方案,该生成状态会产生多个并发的赋值语句:

    SOME_LABEL:
        for i in 1 downto 0 generate
            slv(i) <= mt(i).one;
        end generate;
    

    其中所选名称的后缀 mt(i) 将是索引名称(8.4“索引名称表示数组的元素。”)。

    您可能会注意到,这只是通过不使用切片名称来回避问题。

    对记录数组进行切片并从中提取元素的一种方法是使用子程序函数,即返回值的表达式(4. 子程序和包)。该函数将从参数指定的 mytypes 类型的数组值的元素中返回子元素 one

        function mytypes_ones (inp: mytypes) return std_logic_vector is
            variable retv:  std_logic_vector(inp'range);
        begin
            for i in inp'range loop
                retv(i) := inp(i).one;
            end loop;
            return retv;
        end function;
    

    返回值是一个 std_logic_vector 值,其长度由mytypes 类型的元素数组的长度决定。

    使用函数和生成语句之间的区别包括提供一个表达式,该表达式允许参数可以是切片名称,并允许返回值包含来自多个记录元素的子元素。虽然该函数需要子程序声明和/或定义,但它的用法是作为可以帮助功能描述流程的表达式。一个函数可以在多个函数调用中使用,而生成语句是特定的。此外,生成语句将详细说明为块语句(14.5.3 生成语句)。一个真正有用的区别是函数调用可以在顺序赋值语句中使用(例如,在进程语句或子程序中)。

    使用问题中的声明创建Minimal, Complete, and Verifiable example

    library ieee;
    use ieee.std_logic_1164.all;
    
    entity mytypes_slices is
    end entity;
    
    architecture fum of mytypes_slices is
        type mytype is record
            one:        std_logic;
            two:        std_logic;
        end record;
        type mytypes is array (natural range <>) of mytype;
        signal mt:      mytypes (3 downto 0) := (
                            (one => '1', two => '0'),   -- mt(3)
                            (one => '1', two => '0'),   -- mt(2)
                            (one => '0', two => '0'),   -- mt(1)
                            (one => '1', two => '0')    -- mt(0)
                        );
        signal slv:     std_logic_vector (3 downto 0);
        signal slv1:    std_logic_vector (3 downto 0);
    
        function mytypes_ones (inp: mytypes) return std_logic_vector is
            variable retv:  std_logic_vector(inp'range);
        begin
            for i in inp'range loop
                retv(i) := inp(i).one;
            end loop;
            return retv;
        end function;
    
    begin
    
        slv  (1 downto 0) <= mytypes_ones(mt)(1 downto 0); -- slice return value
        slv1 (1 downto 0) <= mytypes_ones(mt(1 downto 0)); -- slice parameter
    
    MONITOR:
        process
        begin
            wait for 0 ns;  -- skip default values of slv, slv1, delta cycle delay
            report "slv(1 downto 0) = " & 
                character'value(std_ulogic'image(slv(1))) &
                character'value(std_ulogic'image(slv(0)));
            report "slv1(1 downto 0) = " & 
                character'value(std_ulogic'image(slv1(1))) &
                character'value(std_ulogic'image(slv1(0)));
            wait;
        end process;
    
    end architecture;
    

    ghdl -r mytypes_slices
    mytypes_slices.vhdl:49:9:@0ms:(报告说明): slv(1 down to 0) = 01
    mytypes_slices.vhdl:52:9:@0ms:(报告说明): slv1(1 downto 0) = 01

    我们可以看到函数调用也可以很灵活。对slv 的赋值使用一个函数调用,该函数调用的返回值被切片(8.1“某些形式的名称(索引和选定名称、切片名称和属性名称)包括一个前缀,即名称或函数调用。”)。数组类型参数值也可以切片。两个表达式提供相同的值。

    您还可以注意到切片名称的方向与它们各自数组类型的声明方向相匹配。问题 OP 包含一个额外的错误:

    slv(0 to 1) <= mt(0 to 1).one;
    

    如果允许前缀是一个切片 mt(0 to 1) 将是一个空切片(8.5“如果离散范围是空范围,则切片是一个 空切片。如果离散范围的方向与切片名称前缀表示的数组的索引范围的方向不同”,5.2 标量类型,5.2.1 第 3-6 段,5.3.2.2 索引约束和离散范围) .方向与mt 声明中的方向不匹配。

    【讨论】:

    • 谢谢你。我已经复制了该功能以供将来参考。当我问“我需要编写附加代码来启用切片吗?”时,这就是我的想法。为了澄清,我试图“清理”一个代码示例,该示例最初使用两个并行数组作为变量“一”和“二”,并使用硬编码数字。我想用一个常量替换数组大小,并使用一条记录将两个数组合并为一个。(我可能会在这里提到我主要是一个 C/C++ 程序员)。方向错误是我的错,在复制错误的最小代码中,“to”比“downto”短。
    • 这里不需要感谢和道歉。 askinganswering 指南旨在为有类似问题的未来读者提供高质量的问题和答案作为搜索资源。如果您要编辑您的问题以使其更清楚,这里有潜力。过时的 cmets 可以由其所有者删除或标记为删除。
    猜你喜欢
    • 2014-06-10
    • 2012-05-09
    • 1970-01-01
    • 2013-03-03
    • 1970-01-01
    • 2023-01-11
    • 1970-01-01
    • 2014-12-07
    • 2015-07-09
    相关资源
    最近更新 更多