diff --git a/vunit/builtins.py b/vunit/builtins.py index 0f7c6e7de6..8c2eff50bf 100644 --- a/vunit/builtins.py +++ b/vunit/builtins.py @@ -68,13 +68,61 @@ def _add_files(self, pattern): self._vunit_lib.add_source_file(file_name) - def _add_data_types(self): + def _add_data_types(self, use_external=None, impls=None): """ Add data types packages + + :param use_external: list of Booleans, to select whether to enable external models for string and/or + integer_vector, respectively. + :param impls: optional list of lists containing alternative implementations for external models. """ self._add_files(join(VHDL_PATH, "data_types", "src", "types", "*.vhd")) self._add_files(join(VHDL_PATH, "data_types", "src", "*.vhd")) +# Add sources corresponding to VHPIDIRECT arrays (or their placeholders) + + from vunit.test.common import simulator_check + + use_ext = [False, False] + files = [None, None] + + if use_external is not None: + for ind, val in enumerate(use_external): + use_ext[ind] = val + if impls is not None: + for ind, val in enumerate(impls): + files[ind] = val + + for val in use_ext: + if val and simulator_check(lambda simclass: not simclass.supports_vhpi()): + raise RuntimeError("the selected simulator does not support VHPI; must use non-VHPI packages...") + + ext_path = join(VHDL_PATH, "data_types", "src", "external") + + def default_pkg(cond, type_str): + """ + Return name of VHDL file with default VHPIDIRECT foreign declarations. + """ + nostr = 'no' + if cond: + nostr = '' + return join(ext_path, 'external_' + type_str + '-' + nostr + 'vhpi.vhd') + + if not files[0]: + files[0] = [ + default_pkg(use_ext[0], 'string'), + join(ext_path, "external_string-body.vhd") + ] + if not files[1]: + files[1] = [ + default_pkg(use_ext[1], 'integer_vector'), + join(ext_path, "external_integer_vector-body.vhd") + ] + + for _, flist in enumerate(files): + for name in flist: + self._add_files(name) + def _add_array_util(self): """ Add array utility @@ -168,11 +216,15 @@ def add_verilog_builtins(self): """ self._vunit_lib.add_source_files(join(VERILOG_PATH, "vunit_pkg.sv")) - def add_vhdl_builtins(self): + def add_vhdl_builtins(self, use_external=None, impls=None): """ Add vunit VHDL builtin libraries + + :param use_external: list of Booleans, to select whether to enable external models for string and/or + integer_vector, respectively. + :param impls: optional list of lists containing alternative implementations for external models. """ - self._add_data_types() + self._add_data_types(use_external=use_external, impls=impls) self._add_files(join(VHDL_PATH, "*.vhd")) for path in ("core", "logging", "string_ops", "check", "dictionary", "run", "path"): self._add_files(join(VHDL_PATH, path, "src", "*.vhd")) diff --git a/vunit/ui.py b/vunit/ui.py index 73520f2f8b..93aaa24c4a 100644 --- a/vunit/ui.py +++ b/vunit/ui.py @@ -118,7 +118,7 @@ A list of PLI file names. ``ghdl.flags`` - Extra arguments passed to ``ghdl --elab-run`` command *before* executable specific flags. Must be a list of strings. + Extra arguments passed to ``ghdl --elab-run`` command *before* executable specific flags. Must be a list of strings. ``incisive.irun_sim_flags`` @@ -290,12 +290,17 @@ class VUnit(object): # pylint: disable=too-many-instance-attributes, too-many-p """ @classmethod - def from_argv(cls, argv=None, compile_builtins=True, vhdl_standard=None): + def from_argv(cls, argv=None, compile_builtins=True, vhdl_standard=None, use_external=None, impls=None): """ Create VUnit instance from command line arguments. :param argv: Use explicit argv instead of actual command line argument :param compile_builtins: Do not compile builtins. Used for VUnit internal testing. + :param vhdl_standard: The VHDL standard used to compile files into this library, + if None the VUNIT_VHDL_STANDARD environment variable is used + :param use_external: list of Booleans, to select whether to enable external models for string and/or + integer_vector, respectively; if None, all of them are disabled + :param impls: optional list of lists containing alternative implementations for external models :returns: A :class:`.VUnit` object instance :example: @@ -307,10 +312,16 @@ def from_argv(cls, argv=None, compile_builtins=True, vhdl_standard=None): """ args = VUnitCLI().parse_args(argv=argv) - return cls.from_args(args, compile_builtins=compile_builtins, vhdl_standard=vhdl_standard) + return cls.from_args( + args, + compile_builtins=compile_builtins, + vhdl_standard=vhdl_standard, + use_external=use_external, + impls=impls + ) @classmethod - def from_args(cls, args, compile_builtins=True, vhdl_standard=None): + def from_args(cls, args, compile_builtins=True, vhdl_standard=None, use_external=None, impls=None): """ Create VUnit instance from args namespace. Intended for users who adds custom command line options. @@ -319,12 +330,23 @@ def from_args(cls, args, compile_builtins=True, vhdl_standard=None): :param args: The parsed argument namespace object :param compile_builtins: Do not compile builtins. Used for VUnit internal testing. + :param vhdl_standard: The VHDL standard used to compile files into this library, + if None the VUNIT_VHDL_STANDARD environment variable is used + :param use_external: list of Booleans, to select whether to enable external models for string and/or + integer_vector, respectively; if None, all of them are disabled + :param impls: optional list of lists containing alternative implementations for external models :returns: A :class:`.VUnit` object instance - """ - return cls(args, compile_builtins=compile_builtins, vhdl_standard=vhdl_standard) + """ + return cls( + args, + compile_builtins=compile_builtins, + vhdl_standard=vhdl_standard, + use_external=use_external, + impls=impls + ) - def __init__(self, args, compile_builtins=True, vhdl_standard=None): + def __init__(self, args, compile_builtins=True, vhdl_standard=None, use_external=None, impls=None): self._args = args self._configure_logging(args.log_level) self._output_path = abspath(args.output_path) @@ -371,8 +393,9 @@ def test_filter(name, attribute_names): self._test_bench_list = TestBenchList(database=database) self._builtins = Builtins(self, self._vhdl_standard, simulator_class) + self._compile_builtins = compile_builtins if compile_builtins: - self.add_builtins() + self.add_builtins(use_external=use_external, impls=impls) def _create_database(self): """ @@ -858,7 +881,6 @@ def _main(self, post_run): """ Base vunit main function without performing exit """ - if self._args.export_json is not None: return self._main_export_json(self._args.export_json) @@ -1056,11 +1078,15 @@ def _run_test(self, test_cases, report): no_color=self._args.no_color) runner.run(test_cases) - def add_builtins(self): + def add_builtins(self, use_external=None, impls=None): """ Add vunit VHDL builtin libraries + + :param use_external: list of Booleans, to select whether to enable external models for string and/or + integer_vector, respectively. + :param impls: optional list of lists containing alternative implementations for external models. """ - self._builtins.add_vhdl_builtins() + self._builtins.add_vhdl_builtins(use_external=use_external, impls=impls) def add_com(self): """ diff --git a/vunit/vhdl/data_types/src/byte_vector_ptr_pkg.vhd b/vunit/vhdl/data_types/src/byte_vector_ptr_pkg.vhd new file mode 100644 index 0000000000..c2670d50fb --- /dev/null +++ b/vunit/vhdl/data_types/src/byte_vector_ptr_pkg.vhd @@ -0,0 +1,37 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com +-- +-- The purpose of this package is to provide a byte vector access type (pointer) +-- that can itself be used in arrays and returned from functions unlike a +-- real access type. This is achieved by letting the actual value be a handle +-- into a singleton datastructure of string access types. +-- + +use work.byte_vector_pkg.all; +use work.string_ptr_pkg.all; + +package byte_vector_ptr_pkg is + + alias byte_vector_ptr_t is string_ptr_t; + alias null_byte_vector_ptr is null_string_ptr; + + alias new_byte_vector_ptr is new_string_ptr[natural, integer, val_t return ptr_t]; + alias new_byte_vector_ptr is new_string_ptr[natural, integer, natural return ptr_t]; + alias new_byte_vector_ptr is new_string_ptr[string, integer return ptr_t]; + + alias is_external is is_external[ptr_t return boolean]; + alias deallocate is deallocate[ptr_t]; + alias length is length[ptr_t return integer]; + alias set is set[ptr_t, natural, natural]; + alias get is get[ptr_t, natural return natural]; + alias reallocate is reallocate[ptr_t, natural, natural]; + alias resize is resize[ptr_t, natural, natural, natural]; + +-- alias write_byte is write_char; +-- alias read_byte is read_char; +-- alias byte_vector_access_t is string_access_t; + +end package; diff --git a/vunit/vhdl/data_types/src/data_types_context.vhd b/vunit/vhdl/data_types/src/data_types_context.vhd index b31fa7602e..dda802938f 100644 --- a/vunit/vhdl/data_types/src/data_types_context.vhd +++ b/vunit/vhdl/data_types/src/data_types_context.vhd @@ -6,6 +6,8 @@ context data_types_context is library vunit_lib; + use vunit_lib.byte_vector_pkg.all; + use vunit_lib.byte_vector_ptr_pkg.all; use vunit_lib.integer_vector_ptr_pkg.all; use vunit_lib.integer_vector_ptr_pool_pkg.all; use vunit_lib.integer_array_pkg.all; diff --git a/vunit/vhdl/data_types/src/external/external_string-body.vhd b/vunit/vhdl/data_types/src/external/external_string-body.vhd new file mode 100644 index 0000000000..e7d487583d --- /dev/null +++ b/vunit/vhdl/data_types/src/external/external_string-body.vhd @@ -0,0 +1,31 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +package body external_string_pkg is + procedure + write_char( + id : integer; + i : integer; + v : character + )is begin + assert false report "VHPI write_char" severity failure; + end; + + impure function + read_char( + id : integer; + i : integer + ) return character is begin + assert false report "VHPI read_char" severity failure; + end; + + impure function + get_ptr( + id : integer + ) return extstring_access_t is begin + assert false report "VHPI get_string_ptr" severity failure; + end; +end package body; diff --git a/vunit/vhdl/data_types/src/external/external_string-novhpi.vhd b/vunit/vhdl/data_types/src/external/external_string-novhpi.vhd new file mode 100644 index 0000000000..8d8941b8b4 --- /dev/null +++ b/vunit/vhdl/data_types/src/external/external_string-novhpi.vhd @@ -0,0 +1,27 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.string_pkg.all; + +package external_string_pkg is + procedure + write_char( + id : integer; + i : integer; + v : character + ); + + impure function + read_char( + id : integer; + i : integer + ) return character; + + impure function + get_ptr( + id : integer + ) return extstring_access_t; +end package; diff --git a/vunit/vhdl/data_types/src/external/external_string-vhpi.vhd b/vunit/vhdl/data_types/src/external/external_string-vhpi.vhd new file mode 100644 index 0000000000..513bfca55c --- /dev/null +++ b/vunit/vhdl/data_types/src/external/external_string-vhpi.vhd @@ -0,0 +1,30 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.string_pkg.all; + +package external_string_pkg is + procedure + write_char( + id : integer; + i : integer; + v : character + ); + attribute foreign of write_char : procedure is "VHPIDIRECT write_char"; + + impure function + read_char( + id : integer; + i : integer + ) return character; + attribute foreign of read_char : function is "VHPIDIRECT read_char"; + + impure function + get_ptr( + id : integer + ) return extstring_access_t; + attribute foreign of get_ptr : function is "VHPIDIRECT get_string_ptr"; +end package; diff --git a/vunit/vhdl/data_types/src/string_ptr_pkg-body-200x.vhd b/vunit/vhdl/data_types/src/string_ptr_pkg-body-200x.vhd index 83406a7215..ec2eb4f22f 100644 --- a/vunit/vhdl/data_types/src/string_ptr_pkg-body-200x.vhd +++ b/vunit/vhdl/data_types/src/string_ptr_pkg-body-200x.vhd @@ -6,10 +6,16 @@ package body string_ptr_pkg is type string_ptr_storage_t is protected - impure function new_string_ptr ( - length : natural := 0 + impure function new_vector ( + len : natural := 0; + id : integer := 0; + val : val_t := val_t'low ) return natural; + impure function is_external ( + ref : natural + ) return boolean; + procedure deallocate ( ref : natural ); @@ -20,29 +26,25 @@ package body string_ptr_pkg is procedure set ( ref : natural; - index : natural; - value : val_t + index : natural := 0; + value : val_t := val_t'low ); impure function get ( ref : natural; - index : natural + index : natural := 0 ) return val_t; - procedure reallocate ( - ref : natural; - length : natural - ); - procedure reallocate ( ref : natural; value : string ); procedure resize ( - ref : natural; - length : natural; - drop : natural := 0 + ref : natural; + len : natural; + drop : natural := 0; + val : val_t := val_t'low ); impure function to_string ( @@ -51,109 +53,238 @@ package body string_ptr_pkg is end protected; type string_ptr_storage_t is protected body - variable current_index : integer := 0; - variable ptrs : vava_t := null; + type storage_t is record + id : integer; + len : integer; -- 0: default/internal; >0: external through access; <0: external through funcs + end record; + constant null_storage : storage_t := (integer'low, integer'low); + + type storage_vector_t is array (natural range <>) of storage_t; + type storage_vector_access_t is access storage_vector_t; + + type ptr_storage is record + id : integer; + ptr : integer; + eptr : integer; + ids : storage_vector_access_t; + ptrs : vava_t; + eptrs : evava_t; + end record; + + variable st : ptr_storage := (0, 0, 0, null, null, null); + + procedure reallocate_ptrs ( + acc : inout vava_t; + len : integer + ) is + variable old : vava_t := acc; + begin + if old = null then + acc := new vav_t'(0 => null); + elsif old'length <= len then + -- Reallocate ptr pointers to larger ptr; use more size to trade size for speed + acc := new vav_t'(0 to acc'length + 2**16 => null); + for i in old'range loop acc(i) := old(i); end loop; + deallocate(old); + end if; + end; + + procedure reallocate_eptrs ( + acc : inout evava_t; + len : integer + ) is + variable old : evava_t := acc; + begin + if old = null then + acc := new evav_t'(0 => null); + elsif old'length <= len then + acc := new evav_t'(0 to acc'length + 2**16 => null); + for i in old'range loop acc(i) := old(i); end loop; + deallocate(old); + end if; + end; - impure function new_string_ptr ( - length : natural := 0 - ) return natural is - variable old_ptrs : string_access_vector_access_t; + procedure reallocate_ids ( + acc : inout storage_vector_access_t; + len : integer + ) is + variable old : storage_vector_access_t := acc; begin - if ptrs = null then - ptrs := new vav_t'(0 => null); - elsif ptrs'length <= current_index then - -- Reallocate ptr pointers to larger ptr - -- Use more size to trade size for speed - old_ptrs := ptrs; - ptrs := new vav_t'(0 to ptrs'length + 2**16 => null); - for i in old_ptrs'range loop - ptrs(i) := old_ptrs(i); - end loop; - deallocate(old_ptrs); + if old = null then + acc := new storage_vector_t(0 to 0); + elsif old'length <= len then + acc := new storage_vector_t(0 to acc'length + 2**16); + for i in old'range loop acc(i) := old(i); end loop; + deallocate(old); end if; - ptrs(current_index) := new string'(1 to length => val_t'low); - current_index := current_index + 1; - return current_index-1; end; - procedure deallocate ( + impure function new_vector ( + len : natural := 0; + id : integer := 0; + val : val_t := val_t'low + ) return natural is begin + reallocate_ids(st.ids, st.id); + if id = 0 then + st.ids(st.id) := (id => st.ptr, len => 0); + + reallocate_ptrs(st.ptrs, st.ptr); + st.ptrs(st.ptr) := new vec_t'(1 to len => val); + st.ptr := st.ptr + 1; + else + assert len>0 report "Length of external memory cannot be 0" severity error; + if id > 0 then + st.ids(st.id) := (id => st.eptr, len => len); + + reallocate_eptrs(st.eptrs, st.eptr); + st.eptrs(st.eptr) := get_ptr(id-1); + st.eptr := st.eptr + 1; + else + st.ids(st.id) := (id => -id-1, len => -len); + end if; + end if; + st.id := st.id + 1; + return st.id-1; + end; + + impure function is_external ( ref : natural + ) return boolean is begin + return st.ids(ref).len /= 0; + end; + + -- @TODO Remove check_external when all the functions/procedures are implemented + procedure check_external ( + ref : natural; + s : string ) is begin - deallocate(ptrs(ref)); - ptrs(ref) := null; + assert not is_external(ref) report s & " not implemented for external model" severity error; + end; + + procedure deallocate ( + ref : natural + ) is + variable s : storage_t := st.ids(ref); + begin + -- @TODO Implement deallocate for external models + check_external(ref, "deallocate"); + deallocate(st.ptrs(s.id)); + st.ptrs(s.id) := null; end; impure function length ( ref : natural - ) return integer is begin - return ptrs(ref)'length; + ) return integer is + variable s : storage_t := st.ids(ref); + begin + if s.len /= 0 then + return abs(s.len); + else + return st.ptrs(s.id)'length; + end if; end; procedure set ( ref : natural; - index : natural; - value : val_t - ) is begin - ptrs(ref)(index) := value; + index : natural := 0; + value : val_t := val_t'low + ) is + variable s : storage_t := st.ids(ref); + begin + --report "set(" & to_string(s.id) & ", " & to_string(index) & ") len(" & to_string(s.len) & "): " & to_string(value) severity note; + if s.len /= 0 then --is_external + if s.len < 0 then + write_char(s.id, index, value); + else + st.eptrs(s.id)(index+1) := value; + end if; + else + st.ptrs(s.id)(index) := value; + end if; end; impure function get ( ref : natural; - index : natural - ) return val_t is begin - return ptrs(ref)(index); - end; - - procedure reallocate ( - ref : natural; - length : natural - ) is - variable old_ptr, new_ptr : string_access_t; + index : natural := 0 + ) return val_t is + variable s : storage_t := st.ids(ref); begin - deallocate(ptrs(ref)); - ptrs(ref) := new string'(1 to length => val_t'low); + --report "get(" & to_string(s.id) & ", " & to_string(index) & ") len(" & to_string(s.len) & ")" severity note; + if s.len /= 0 then --is_external + if s.len < 0 then + return read_char(s.id, index); + else + return st.eptrs(s.id)(index+1); + end if; + else + return st.ptrs(s.id)(index); + end if; end; procedure reallocate ( ref : natural; value : string ) is - variable old_ptr, new_ptr : string_access_t; + variable s : storage_t := st.ids(ref); variable n_value : string(1 to value'length) := value; begin - deallocate(ptrs(ref)); - ptrs(ref) := new string'(n_value); + if s.len /= 0 then --is_external + if s.len < 0 then + -- @FIXME The reallocation request is just ignored. What should we do here? + --check_external(ptr, "reallocate"); + else + -- @TODO Implement reallocate for external models (through access) + check_external(ref, "reallocate"); + end if; + else + deallocate(st.ptrs(s.id)); + st.ptrs(s.id) := new vec_t'(n_value); + end if; end; procedure resize ( - ref : natural; - length : natural; - drop : natural := 0 + ref : natural; + len : natural; + drop : natural := 0; + val : val_t := val_t'low ) is - variable old_ptr, new_ptr : string_access_t; - variable min_length : natural := length; + variable oldp, newp : string_access_t; + variable min_len : natural := len; + variable s : storage_t := st.ids(ref); begin - new_ptr := new string'(1 to length => val_t'low); - old_ptr := ptrs(ref); - if min_length > old_ptr'length - drop then - min_length := old_ptr'length - drop; + if s.len /= 0 then + -- @TODO Implement resize for external models + check_external(ref, "resize"); + else + newp := new vec_t'(1 to len => val); + oldp := st.ptrs(s.id); + if min_len > oldp'length - drop then + min_len := oldp'length - drop; + end if; + for i in 1 to min_len loop + newp(i) := oldp(drop + i); + end loop; + st.ptrs(s.id) := newp; + deallocate(oldp); end if; - for i in 1 to min_length loop - new_ptr(i) := old_ptr(drop + i); - end loop; - ptrs(ref) := new_ptr; - deallocate(old_ptr); end; impure function to_string ( ref : natural - ) return string is begin - return ptrs(ref).all; + ) return string is + variable s : storage_t := st.ids(ref); + begin + if s.len /= 0 then + -- @TODO Implement to_string for external models + check_external(ref, "to_string"); + else + return st.ptrs(s.id).all; + end if; end; end protected body; - shared variable string_ptr_storage : string_ptr_storage_t; + shared variable vec_ptr_storage : string_ptr_storage_t; function to_integer ( value : ptr_t @@ -169,77 +300,137 @@ package body string_ptr_pkg is end; impure function new_string_ptr ( - length : natural := 0 + len : natural := 0; + id : integer := 0; + val : val_t := val_t'low ) return ptr_t is begin - return (ref => string_ptr_storage.new_string_ptr(length)); + return (ref => vec_ptr_storage.new_vector( + len => len, + val => val, + id => id + )); end; impure function new_string_ptr ( - value : string + len : natural := 0; + id : integer := 0; + val : natural + ) return ptr_t is begin + return (ref => vec_ptr_storage.new_vector( + len => len, + val => character'val(val), + id => id + )); + end; + + impure function new_string_ptr ( + value : string; + id : integer := 0 ) return ptr_t is - variable result : ptr_t := new_string_ptr(value'length); + variable ptr : string_ptr_t := new_string_ptr(value'length, 0, 0); variable n_value : string(1 to value'length) := value; begin for i in 1 to n_value'length loop - set(result, i, n_value(i)); + set(ptr, i, n_value(i)); end loop; - return result; + return ptr; + end; + + impure function is_external ( + ptr : ptr_t + ) return boolean is begin + return vec_ptr_storage.is_external(ptr.ref); end; procedure deallocate ( ptr : ptr_t - ) is - begin - string_ptr_storage.deallocate(ptr.ref); + ) is begin + vec_ptr_storage.deallocate(ptr.ref); end; impure function length ( ptr : ptr_t ) return integer is begin - return string_ptr_storage.length(ptr.ref); + return vec_ptr_storage.length(ptr.ref); + end; + + procedure set ( + ptr : ptr_t; + index : natural := 0; + value : val_t := val_t'low + ) is begin + vec_ptr_storage.set(ptr.ref, index, value); end; procedure set ( ptr : ptr_t; - index : natural; - value : val_t + index : natural := 0; + value : natural := 0 ) is begin - string_ptr_storage.set(ptr.ref, index, value); + vec_ptr_storage.set(ptr.ref, index, character'val(value)); end; impure function get ( - ptr : ptr_t; - index : natural + ptr : ptr_t; + index : natural := 0 ) return val_t is begin - return string_ptr_storage.get(ptr.ref, index); + return vec_ptr_storage.get(ptr.ref, index); + end; + + impure function get ( + ptr : ptr_t; + index : natural := 0 + ) return natural is begin + return character'pos(vec_ptr_storage.get(ptr.ref, index)); end; procedure reallocate ( - ptr : ptr_t; - length : natural + ptr : ptr_t; + value : string ) is begin - string_ptr_storage.reallocate(ptr.ref, length); + vec_ptr_storage.reallocate(ptr.ref, value); + end; + + procedure reallocate ( + ptr : ptr_t; + len : natural; + val : val_t := val_t'low + ) is + variable n_value : string(1 to len) := (1 to len => val); + begin + vec_ptr_storage.reallocate(ptr.ref, n_value); end; procedure reallocate ( ptr : ptr_t; - value : string + len : natural; + value : natural + ) is begin + reallocate(ptr, len, character'val(value)); + end; + + procedure resize ( + ptr : ptr_t; + len : natural; + drop : natural := 0; + val : val_t := val_t'low ) is begin - string_ptr_storage.reallocate(ptr.ref, value); + vec_ptr_storage.resize(ptr.ref, len, drop, val); end; procedure resize ( - ptr : ptr_t; - length : natural; - drop : natural := 0 + ptr : ptr_t; + len : natural; + drop : natural := 0; + val : natural ) is begin - string_ptr_storage.resize(ptr.ref, length, drop); + vec_ptr_storage.resize(ptr.ref, len, drop, character'val(val)); end; impure function to_string ( ptr : ptr_t ) return string is begin - return string_ptr_storage.to_string(ptr.ref); + return vec_ptr_storage.to_string(ptr.ref); end; function encode ( @@ -252,15 +443,15 @@ package body string_ptr_pkg is code : string ) return ptr_t is variable ret_val : ptr_t; - variable index : positive := code'left; + variable index : positive := code'left; begin decode(code, index, ret_val); return ret_val; end; procedure decode ( - constant code : string; - variable index : inout positive; + constant code : string; + variable index : inout positive; variable result : out ptr_t ) is begin decode(code, index, result.ref); diff --git a/vunit/vhdl/data_types/src/string_ptr_pkg-body-93.vhd b/vunit/vhdl/data_types/src/string_ptr_pkg-body-93.vhd index 7f06841093..e22b456d33 100644 --- a/vunit/vhdl/data_types/src/string_ptr_pkg-body-93.vhd +++ b/vunit/vhdl/data_types/src/string_ptr_pkg-body-93.vhd @@ -5,105 +5,300 @@ -- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com package body string_ptr_pkg is - shared variable current_index : integer := 0; - shared variable ptrs : vava_t := null; + type storage_t is record + id : integer; + len : integer; -- 0: default/internal; >0: external through access; <0: external through funcs + end record; + constant null_storage : storage_t := (integer'low, integer'low); + + type storage_vector_t is array (natural range <>) of storage_t; + type storage_vector_access_t is access storage_vector_t; + + type ptr_storage is record + id : integer; + ptr : integer; + eptr : integer; + ids : storage_vector_access_t; + ptrs : vava_t; + eptrs : evava_t; + end record; + + shared variable st : ptr_storage := (0, 0, 0, null, null, null); + + procedure reallocate_ptrs ( + acc : inout vava_t; + len : integer + ) is + variable old : vava_t := acc; + begin + if old = null then + acc := new vav_t'(0 => null); + elsif old'length <= len then + -- Reallocate ptr pointers to larger ptr; use more size to trade size for speed + acc := new vav_t'(0 to acc'length + 2**16 => null); + for i in old'range loop acc(i) := old(i); end loop; + deallocate(old); + end if; + end; + + procedure reallocate_eptrs ( + acc : inout evava_t; + len : integer + ) is + variable old : evava_t := acc; + begin + if old = null then + acc := new evav_t'(0 => null); + elsif old'length <= len then + acc := new evav_t'(0 to acc'length + 2**16 => null); + for i in old'range loop acc(i) := old(i); end loop; + deallocate(old); + end if; + end; + + procedure reallocate_ids ( + acc : inout storage_vector_access_t; + len : integer + ) is + variable old : storage_vector_access_t := acc; + begin + if old = null then + acc := new storage_vector_t(0 to 0); + elsif old'length <= len then + acc := new storage_vector_t(0 to acc'length + 2**16); + for i in old'range loop acc(i) := old(i); end loop; + deallocate(old); + end if; + end; impure function new_string_ptr ( - length : natural := 0 + len : natural := 0; + id : integer := 0; + val : val_t := val_t'low + ) return ptr_t is begin + reallocate_ids(st.ids, st.id); + if id = 0 then + st.ids(st.id) := (id => st.ptr, len => 0); + + reallocate_ptrs(st.ptrs, st.ptr); + st.ptrs(st.ptr) := new vec_t'(1 to len => val); + st.ptr := st.ptr + 1; + else + assert len>0 report "Length of external memory cannot be 0" severity error; + if id > 0 then + st.ids(st.id) := (id => st.eptr, len => len); + + reallocate_eptrs(st.eptrs, st.eptr); + st.eptrs(st.eptr) := get_ptr(id-1); + st.eptr := st.eptr + 1; + else + st.ids(st.id) := (id => -id-1, len => -len); + end if; + end if; + st.id := st.id + 1; + return (ref => st.id-1); + end; + + impure function new_string_ptr ( + len : natural := 0; + id : integer := 0; + val : natural + ) return ptr_t is begin + return new_string_ptr( + len => len, + val => character'val(val), + id => id + ); + end; + + impure function new_string_ptr ( + value : string; + id : integer := 0 ) return ptr_t is - variable old_ptrs : vava_t; - variable retval : ptr_t := (ref => current_index); + variable ptr : string_ptr_t := new_string_ptr(value'length, 0, 0); + variable n_value : string(1 to value'length) := value; begin - if ptrs = null then - ptrs := new vav_t'(0 => null); - elsif ptrs'length <= current_index then - -- Reallocate ptr pointers to larger ptr - -- Use more size to trade size for speed - old_ptrs := ptrs; - ptrs := new vav_t'(0 to ptrs'length + 2**16 => null); - for i in old_ptrs'range loop - ptrs(i) := old_ptrs(i); - end loop; - deallocate(old_ptrs); - end if; - ptrs(current_index) := new string'(1 to length => val_t'low); - current_index := current_index + 1; - return retval; + for i in 1 to n_value'length loop + set(ptr, i, n_value(i)); + end loop; + return ptr; end; - procedure deallocate ( + impure function is_external ( ptr : ptr_t + ) return boolean is begin + return st.ids(ptr.ref).len /= 0; + end; + + -- @TODO Remove check_external when all the functions/procedures are implemented + procedure check_external ( + ptr : ptr_t; + s : string ) is begin - deallocate(ptrs(ptr.ref)); - ptrs(ptr.ref) := null; + assert not is_external(ptr) report s & " not implemented for external model" severity error; + end; + + procedure deallocate ( + ptr : ptr_t + ) is + variable s : storage_t := st.ids(ptr.ref); + begin + -- @TODO Implement deallocate for external models + check_external(ptr, "deallocate"); + deallocate(st.ptrs(s.id)); + st.ptrs(s.id) := null; end; impure function length ( ptr : ptr_t - ) return integer is begin - return ptrs(ptr.ref)'length; + ) return integer is + variable s : storage_t := st.ids(ptr.ref); + begin + if s.len /= 0 then + return abs(s.len); + else + return st.ptrs(s.id)'length; + end if; end; procedure set ( ptr : ptr_t; - index : natural; - value : val_t + index : natural := 0; + value : val_t := val_t'low + ) is + variable s : storage_t := st.ids(ptr.ref); + begin + --report "set(" & to_string(s.id) & ", " & to_string(index) & ") len(" & to_string(s.len) & "): " & to_string(value) severity note; + if s.len /= 0 then --is_external + if s.len < 0 then + write_char(s.id, index, value); + else + st.eptrs(s.id)(index+1) := value; + end if; + else + st.ptrs(s.id)(index) := value; + end if; + end; + + procedure set ( + ptr : ptr_t; + index : natural := 0; + value : natural := 0 ) is begin - ptrs(ptr.ref)(index) := value; + set(ptr, index, character'val(value)); end; impure function get ( ptr : ptr_t; - index : natural - ) return val_t is begin - return ptrs(ptr.ref)(index); + index : natural := 0 + ) return val_t is + variable s : storage_t := st.ids(ptr.ref); + begin + --report "get(" & to_string(s.id) & ", " & to_string(index) & ") len(" & to_string(s.len) & ")" severity note; + if s.len /= 0 then --is_external + if s.len < 0 then + return read_char(s.id, index); + else + return st.eptrs(s.id)(index+1); + end if; + else + return st.ptrs(s.id)(index); + end if; end; - procedure reallocate ( - ptr : ptr_t; - length : natural - ) is - variable old_ptr, new_ptr : string_access_t; - begin - deallocate(ptrs(ptr.ref)); - ptrs(ptr.ref) := new string'(1 to length => val_t'low); + impure function get ( + ptr : ptr_t; + index : natural := 0 + ) return natural is begin + return character'pos(get(ptr, index)); end; procedure reallocate ( ptr : ptr_t; value : string ) is - variable old_ptr, new_ptr : string_access_t; + variable s : storage_t := st.ids(ptr.ref); variable n_value : string(1 to value'length) := value; begin - deallocate(ptrs(ptr.ref)); - ptrs(ptr.ref) := new string'(n_value); + if s.len /= 0 then --is_external + if s.len < 0 then + -- @FIXME The reallocation request is just ignored. What should we do here? + --check_external(ptr, "reallocate"); + else + -- @TODO Implement reallocate for external models (through access) + check_external(ptr, "reallocate"); + end if; + else + deallocate(st.ptrs(s.id)); + st.ptrs(s.id) := new vec_t'(n_value); + end if; + end; + + procedure reallocate ( + ptr : ptr_t; + len : natural; + val : val_t := val_t'low + ) is + variable n_value : string(1 to len) := (1 to len => val); + begin + reallocate(ptr, n_value); + end; + + procedure reallocate ( + ptr : ptr_t; + len : natural; + value : natural + ) is begin + reallocate(ptr, len, character'val(value)); end; procedure resize ( - ptr : ptr_t; - length : natural; - drop : natural := 0 + ptr : ptr_t; + len : natural; + drop : natural := 0; + val : val_t := val_t'low ) is - variable old_ptr, new_ptr : string_access_t; - variable min_length : natural := length; + variable oldp, newp : string_access_t; + variable min_len : natural := len; + variable s : storage_t := st.ids(ptr.ref); begin - new_ptr := new string'(1 to length => val_t'low); - old_ptr := ptrs(ptr.ref); - if min_length > old_ptr'length - drop then - min_length := old_ptr'length - drop; + if s.len /= 0 then + -- @TODO Implement resize for external models + check_external(ptr, "resize"); + else + newp := new vec_t'(1 to len => val); + oldp := st.ptrs(s.id); + if min_len > oldp'length - drop then + min_len := oldp'length - drop; + end if; + for i in 1 to min_len loop + newp(i) := oldp(drop + i); + end loop; + st.ptrs(s.id) := newp; + deallocate(oldp); end if; - for i in 1 to min_length loop - new_ptr(i) := old_ptr(drop + i); - end loop; - ptrs(ptr.ref) := new_ptr; - deallocate(old_ptr); + end; + + procedure resize ( + ptr : ptr_t; + len : natural; + drop : natural := 0; + val : natural + ) is begin + resize(ptr, len, drop, character'val(val)); end; impure function to_string ( ptr : ptr_t - ) return string is begin - return ptrs(ptr.ref).all; + ) return string is + variable s : storage_t := st.ids(ptr.ref); + begin + if s.len /= 0 then + -- @TODO Implement to_string for external models + check_external(ptr, "to_string"); + else + return st.ptrs(s.id).all; + end if; end; function to_integer ( @@ -115,22 +310,10 @@ package body string_ptr_pkg is impure function to_string_ptr ( value : integer ) return ptr_t is begin - -- @TODO maybe assert that the ref is valid + -- @TODO maybe assert that the index is valid return (ref => value); end; - impure function new_string_ptr ( - value : string - ) return ptr_t is - variable result : ptr_t := new_string_ptr(value'length); - variable n_value : string(1 to value'length) := value; - begin - for i in 1 to n_value'length loop - set(result, i, n_value(i)); - end loop; - return result; - end; - function encode ( data : ptr_t ) return string is begin diff --git a/vunit/vhdl/data_types/src/string_ptr_pkg.vhd b/vunit/vhdl/data_types/src/string_ptr_pkg.vhd index 43376b3157..a841105c6e 100644 --- a/vunit/vhdl/data_types/src/string_ptr_pkg.vhd +++ b/vunit/vhdl/data_types/src/string_ptr_pkg.vhd @@ -11,12 +11,15 @@ -- use work.string_pkg.all; +use work.external_string_pkg.all; use work.codec_pkg.all; use work.codec_builder_pkg.all; package string_ptr_pkg is + subtype index_t is integer range -1 to integer'high; + type string_ptr_t is record ref : index_t; end record; @@ -24,80 +27,122 @@ package string_ptr_pkg is alias ptr_t is string_ptr_t; alias val_t is character; + alias vec_t is string; alias vav_t is string_access_vector_t; + alias evav_t is extstring_access_vector_t; alias vava_t is string_access_vector_access_t; + alias evava_t is extstring_access_vector_access_t; function to_integer ( - value : string_ptr_t + value : ptr_t ) return integer; impure function to_string_ptr ( value : integer - ) return string_ptr_t; + ) return ptr_t; impure function new_string_ptr ( - length : natural := 0 - ) return string_ptr_t; + len : natural := 0; + id : integer := 0; + val : val_t := val_t'low + ) return ptr_t; impure function new_string_ptr ( - value : string - ) return string_ptr_t; + len : natural := 0; + id : integer := 0; + val : natural + ) return ptr_t; + + impure function new_string_ptr ( + value : string; + id : integer := 0 + ) return ptr_t; + + impure function is_external ( + ptr : ptr_t + ) return boolean; procedure deallocate ( - ptr : string_ptr_t + ptr : ptr_t ); impure function length ( - ptr : string_ptr_t + ptr : ptr_t ) return integer; procedure set ( - ptr : string_ptr_t; - index : natural; - value : character + ptr : ptr_t; + index : natural := 0; + value : val_t := val_t'low + ); + + procedure set ( + ptr : ptr_t; + index : natural := 0; + value : natural := 0 ); impure function get ( - ptr : string_ptr_t; - index : natural - ) return character; + ptr : ptr_t; + index : natural := 0 + ) return val_t; + + impure function get ( + ptr : ptr_t; + index : natural := 0 + ) return natural; procedure reallocate ( - ptr : string_ptr_t; - length : natural + ptr : ptr_t; + value : string ); procedure reallocate ( - ptr : string_ptr_t; - value : string + ptr : ptr_t; + len : natural; + val : val_t := val_t'low + ); + + procedure reallocate ( + ptr : ptr_t; + len : natural; + value : natural + ); + + procedure resize ( + ptr : ptr_t; + len : natural; + drop : natural := 0; + val : val_t := val_t'low ); procedure resize ( - ptr : string_ptr_t; - length : natural; - drop : natural := 0 + ptr : ptr_t; + len : natural; + drop : natural := 0; + val : natural ); impure function to_string ( - ptr : string_ptr_t + ptr : ptr_t ) return string; function encode ( - data : string_ptr_t + data : ptr_t ) return string; function decode ( code : string - ) return string_ptr_t; + ) return ptr_t; procedure decode ( constant code : string; variable index : inout positive; - variable result : out string_ptr_t + variable result : out ptr_t ); - alias encode_string_ptr_t is encode[string_ptr_t return string]; - alias decode_string_ptr_t is decode[string return string_ptr_t]; + alias encode_string_ptr_t is encode[ptr_t return string]; + alias decode_string_ptr_t is decode[string return ptr_t]; constant string_ptr_t_code_length : positive := integer_code_length; diff --git a/vunit/vhdl/data_types/src/types/byte_vector_pkg.vhd b/vunit/vhdl/data_types/src/types/byte_vector_pkg.vhd new file mode 100644 index 0000000000..c2ed095ee5 --- /dev/null +++ b/vunit/vhdl/data_types/src/types/byte_vector_pkg.vhd @@ -0,0 +1,17 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2019, Lars Asplund lars.anders.asplund@gmail.com + +use work.string_pkg.all; + +package byte_vector_pkg is + alias byte_vector_access_t is string_access_t; + alias byte_vector_access_vector_t is string_access_vector_t; + alias byte_vector_access_vector_access_t is string_access_vector_access_t; + + alias extbytevec_access_t is extstring_access_t; + alias extbytevec_access_vector_t is extstring_access_vector_t; + alias extbytevec_access_vector_access_t is extstring_access_vector_access_t; +end package; diff --git a/vunit/vhdl/data_types/src/types/string_pkg.vhd b/vunit/vhdl/data_types/src/types/string_pkg.vhd index 55be22babb..76ce9bfa56 100644 --- a/vunit/vhdl/data_types/src/types/string_pkg.vhd +++ b/vunit/vhdl/data_types/src/types/string_pkg.vhd @@ -8,4 +8,8 @@ package string_pkg is type string_access_t is access string; type string_access_vector_t is array (natural range <>) of string_access_t; type string_access_vector_access_t is access string_access_vector_t; + + type extstring_access_t is access string(1 to integer'high); + type extstring_access_vector_t is array (natural range <>) of extstring_access_t; + type extstring_access_vector_access_t is access extstring_access_vector_t; end package;