如何在函数结束时返回值之前释放访问类型变量?

时间:2017-03-09 11:09:04

标签: vhdl

在函数中,使用sting access type会很方便,例如通过textio.line,最终值是函数的输出。显示问题的简单示例代码,但不是使用access type的动机:

function fun return string is
  variable line_v : line;
begin
  line_v := new string'("Hello VHDL world");
  return line_v.all;  -- line_v is missing deallocate(line_v)
end function;

但是这段代码缺少deallocate(line_v),以避免在未释放字符串的已分配内存时发生内存泄漏。

deallocate(line_v)返回值时,如何制作line_v

4 个答案:

答案 0 :(得分:2)

现在这真的很难看。

首先创建一个受保护的类型:

package TextExtPkg is
  type LinePType is protected 
    procedure copy ( S : in  string ) ;
    impure function get ( EraseLine : boolean := TRUE) return string ;
  end protected LinePType ;
...
end TextExtPkg ;
package body TextExtPkg is 
  type LinePType is protected body
    variable Message : line ;
    procedure copy ( S : in  string ) is 
    begin
      deallocate(Message) ; 
      Message := new string'(S) ; 
    end procedure copy ;
    impure function get ( EraseLine : boolean := TRUE) return string is 
      variable value : string(1 to Message'length) ; 
    begin
      value := Message.all ; 
      if EraseLine then
        deallocate(Message) ; 
      end if ; 
      return value ; 
    end function get ;
  end protected body LinePType ;
end TextExtPkg ;

然后将值放入受保护类型,释放本地指针,然后从受保护类型获取并返回值。它不漂亮,但它的工作原理。

function fun return string is
  variable line_v : line;
  variable PT : LinePType ; 
begin
  line_v := new string'("Hello VHDL world");
  PT.copy(line_v.all) ;  
  deallocate(line_v) ; 
  return PT.get(EraseLine => TRUE) ; 
end function;

我用mine为integer_vector创建了一个write和to_string(这里命名为to_s):

  procedure write (
    L          : inout line; 
    value      : in integer_vector;
    justified  : in side := RIGHT; 
    field      : in width := 0
  ) is
  begin
    for i in value'range loop 
      write(L, value(i), justified, field) ; 
      if i /= value'right then
        write(L, ' ') ;  -- delimiter
      end if ; 
    end loop ;
  end procedure write ; 

  impure function to_s (
    value      : in integer_vector;
    justified  : in side := RIGHT; 
    field      : in width := 0
  ) return string is
    variable L : line; 
    variable PT : LinePType ; 
  begin
    write(L, value, justified, field) ;    
    if L = NULL then return  "" ; end if ; 
    PT.copy(L.all) ;  
    deallocate(L) ; 
    return PT.get(EraseLine => TRUE) ; 
  end function to_s ; 

您可以在http://synthworks.com/TextExtPkg.vhd

找到整个套餐

答案 1 :(得分:2)

取消分配由line_v行访问类型表示的对象需要首先将其值分配给动态详细对象,以用作释放后的返回值。

执行此操作的困难来自于要求动态声明的对象的声明在精化时必须是静态的,这意味着必须知道对象的子类型。

在某些编程语言中,这可以通过使用行内声明来实现,其中一旦知道字符数组的长度,就可以将分配的内存区域的值复制到新声明的内容。

VHDL没有这种能力。但是有一种方法可以通过嵌套函数调用来模拟它:

entity noleak is
end entity;

architecture fum of noleak is
    impure function fun return string is
        use std.textio.all;
        variable line_v : line;
        impure function snip return string is
            variable retstr: string (1 to line_v'length);
        begin
            retstr := line_v.all;
            deallocate (line_v);
            return retstr;
        end function;
    begin
        line_v := new string'("Hello VHDL world!");
         return snip;
    end function;
begin
    process
    begin
        report "fun return = " & fun;
        wait;
    end process;
end architecture;

函数snip是不纯的,因为它修改了line_v访问对象。

功能乐趣不纯,因为它是不纯函数(剪辑)的父级。

声明函数snip包含一个返回字符串变量,其长度取决于访问对象line_v指向的字符串值的长度值。

为什么这可以起作用是因为子程序声明中的声明项可以是另一个子程序规范。这样做可以直接查看前面的声明(在本例中为line_v)。

line_v访问的对象的长度用于动态详细的函数调用,以定义retstr的子类型(它的长度)。

在snip的主体中,line_v访问的对象的值被复制到动态详细的retstr,并且line_v被释放。这表示将分配的对象(堆变量)转换为动态详细的对象(堆栈变量),然后释放已分配的对象。

这与您在VHDL中获得的在线声明一样接近。

这里的想法是防止不执行垃圾收集的VHDL实现中的内存泄漏,如果没有从函数返回访问类型值或者不是模式输出的参数或者不是过程的inout,则可以在此自动解除分配存储

答案 2 :(得分:1)

一种解决方案是返回访问类型而不是字符串:

library IEEE;
use std.textio.all;

  function fun return line is
    variable line_v : line;
  begin
    line_v := new string'("Hello VHDL world");
    return line_v;
  end function;

然后在函数外部解除分配:

library IEEE;
use std.textio.all;

entity E is
end entity E;

architecture E of E is
  function fun return line is
    variable line_v : line;
  begin
    line_v := new string'("Hello VHDL world");
    return line_v;
  end function;
begin

  process
    variable L : line := fun;
  begin
    report L.all;
    deallocate(L);
    wait;
  end process;

end architecture E;

https://www.edaplayground.com/x/3kCu

答案 3 :(得分:1)

你如何在C函数调用中执行此解除分配?

VHDL不允许在行声明,这意味着在函数中释放存储的唯一方法是返回一个动态声明的对象值。这需要使用可以保护的最大长度返回值:

entity noleak is
end entity;

architecture fum of noleak is
    function fun (maxlen: natural := 16) return string is
        use std.textio.all;
        variable line_v : line;
        variable retstr:  string (1 to maxlen);
        variable len:     natural;
    begin
        line_v := new string'("Hello VHDL world!");
        len := line_v.all'length;
        if len <= maxlen then
            retstr(1 to len) := line_v.all;
            deallocate(line_v);
            return retstr(1 to len);
        end if;
        deallocate(line_v);
        return fun(maxlen => len);
    end function;
begin
    process
    begin
        report "fun return = " & fun(maxlen => 17);
        report "fun return = " & fun;
        wait;
    end process;
end architecture;

返回:

ghdl -r noleak
leak.vhdl:87:9:@0ms:(report note): fun return = Hello VHDL world!
leak.vhdl:88:9:@0ms:(report note): fun return = Hello VHDL world!

保护被合并为动态详细描述的字符串数组的长度(它是一个堆栈变量)。

如果Line访问变量表示的字符串比该字符串arrary变量长,则以正确的长度递归调用该函数。 (注意到该功能尚未被宣布为不纯。)

您可以根据您认为在一次传递中起作用的内容来选择maxlen默认值。如果你错了,它会通过递归纠正它。

如果你知道它会出错,你可以通过一个更好的估计。