From 8fc7170053ed8d7ea90041efb4a0d8f4faec684c Mon Sep 17 00:00:00 2001 From: Sebastian Abrahamsson Date: Sun, 4 Oct 2026 12:37:23 +0200 Subject: [PATCH 1/3] Add get_key and get_value_type to dict_pkg for key enumeration Co-Authored-By: Claude Sonnet 5.5 --- docs/news.d/+dict_key_iteration.feature.rst | 1 + vunit/vhdl/data_types/src/dict_pkg-body.vhd | 29 +++++++++++++ vunit/vhdl/data_types/src/dict_pkg.vhd | 13 ++++++ vunit/vhdl/data_types/test/tb_dict.vhd | 46 +++++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 docs/news.d/+dict_key_iteration.feature.rst diff --git a/docs/news.d/+dict_key_iteration.feature.rst b/docs/news.d/+dict_key_iteration.feature.rst new file mode 100644 index 000000000..4c2abcc49 --- /dev/null +++ b/docs/news.d/+dict_key_iteration.feature.rst @@ -0,0 +1 @@ +Added ``get_key`` and ``get_value_type`` to ``dict_pkg`` for enumerating a ``dict_t`` and reading each value by type. diff --git a/vunit/vhdl/data_types/src/dict_pkg-body.vhd b/vunit/vhdl/data_types/src/dict_pkg-body.vhd index afa23e069..e065516e3 100644 --- a/vunit/vhdl/data_types/src/dict_pkg-body.vhd +++ b/vunit/vhdl/data_types/src/dict_pkg-body.vhd @@ -280,6 +280,35 @@ package body dict_pkg is return get_value_ptr(dict, key_hash, key) /= null_string_ptr; end; + impure function get_key ( + dict : dict_t; + index : natural + ) return string is + variable offset : natural := index; + variable bucket_length : natural; + variable bucket_keys : integer_vector_ptr_t; + begin + assert index < num_keys(dict) report "key index " & to_string(index) & " out of range"; + for bucket_idx in 0 to length(dict.p_bucket_lengths)-1 loop + bucket_length := get(dict.p_bucket_lengths, bucket_idx); + if offset < bucket_length then + bucket_keys := to_integer_vector_ptr(get(dict.p_bucket_keys, bucket_idx)); + return to_string(to_string_ptr(get(bucket_keys, offset))); + end if; + offset := offset - bucket_length; + end loop; + return ""; + end; + + impure function get_value_type ( + dict : dict_t; + key : string + ) return data_type_t is + begin + assert has_key(dict, key) report "missing key '" & key & "'"; + return get_value_type(dict, hash(key), key); + end; + procedure remove ( dict : dict_t; key : string diff --git a/vunit/vhdl/data_types/src/dict_pkg.vhd b/vunit/vhdl/data_types/src/dict_pkg.vhd index a595aa72a..91e0e0250 100644 --- a/vunit/vhdl/data_types/src/dict_pkg.vhd +++ b/vunit/vhdl/data_types/src/dict_pkg.vhd @@ -50,6 +50,19 @@ package dict_pkg is dict : dict_t ) return natural; + -- Key at position index, 0 <= index < num_keys(dict). The order is unspecified but stable + -- as long as the dict is not modified. Fails if index is out of range. + impure function get_key ( + dict : dict_t; + index : natural + ) return string; + + -- The data type of the value stored for key. Fails if key doesn't exist. + impure function get_value_type ( + dict : dict_t; + key : string + ) return data_type_t; + procedure remove ( dict : dict_t; key : string diff --git a/vunit/vhdl/data_types/test/tb_dict.vhd b/vunit/vhdl/data_types/test/tb_dict.vhd index 3b7ed4b79..a2a002c4a 100644 --- a/vunit/vhdl/data_types/test/tb_dict.vhd +++ b/vunit/vhdl/data_types/test/tb_dict.vhd @@ -15,6 +15,7 @@ use ieee.numeric_bit.all; use ieee.numeric_std.all; use work.dict_pkg.all; +use work.data_types_private_pkg.all; use work.queue_pkg.all; use work.integer_vector_ptr_pkg.all; use work.string_ptr_pkg.all; @@ -29,6 +30,31 @@ architecture a of tb_dict is begin main : process + procedure check_enumeration(dict : dict_t; expected : natural) is + variable seen : natural := 0; + begin + check_equal(num_keys(dict), expected); + for i in 0 to num_keys(dict)-1 loop + assert has_key(dict, get_key(dict, i)); + for j in 0 to i-1 loop + assert get_key(dict, j) /= get_key(dict, i) report "duplicate key"; + end loop; + if get_key(dict, i) = "s" then + check(get_value_type(dict, "s") = vhdl_string); seen := seen + 1; + elsif get_key(dict, i) = "b" then + check(get_value_type(dict, "b") = vhdl_boolean); seen := seen + 1; + elsif get_key(dict, i) = "r" then + check(get_value_type(dict, "r") = vhdl_real); seen := seen + 1; + elsif get_key(dict, i) = "t" then + check(get_value_type(dict, "t") = vhdl_time); seen := seen + 1; + elsif get_key(dict, i) = "i" then + check(get_value_type(dict, "i") = vhdl_integer); seen := seen + 1; + else + check(get_value_type(dict, get_key(dict, i)) = vhdl_integer); + end if; + end loop; + check_equal(seen, 4 + boolean'pos(has_key(dict, "i"))); + end; variable dict : dict_t; variable dict_value : dict_t; variable integer_vector_ptr : integer_vector_ptr_t; @@ -254,6 +280,26 @@ begin queue := get_queue_t_ref(dict, "key"); check_equal(pop_integer(queue), 17); + elsif run("test enumerate keys with types") then + dict := new_dict; + set_string(dict, "s", "value"); + set_integer(dict, "i", 1); + set_boolean(dict, "b", true); + set_real(dict, "r", 1.5); + set_time(dict, "t", 1 ns); + check_enumeration(dict, 5); + remove(dict, "i"); + check_enumeration(dict, 4); + check_false(has_key(dict, "i")); + for i in 1 to 200 loop + set_integer(dict, "k" & integer'image(i), i); + end loop; + check_enumeration(dict, 4 + 200); + for i in 1 to 200 loop + remove(dict, "k" & integer'image(i)); + end loop; + check_enumeration(dict, 4); + elsif run("Test push and pop dict_t") then queue := new_queue; dict := new_dict; From 4899efeeb0b892b36617b271970344c54502c7f1 Mon Sep 17 00:00:00 2001 From: Sebastian Abrahamsson Date: Sun, 4 Oct 2026 12:37:23 +0200 Subject: [PATCH 2/3] Fix new_dict after deallocate of a grown dict The pointer pool recycles pointers that can be longer than requested, while dict_t uses the pointer lengths as the bucket count and bucket sizes. Co-Authored-By: Claude Sonnet 5.5 --- .../+dict_new_after_deallocate.bugfix.rst | 1 + vunit/vhdl/data_types/src/dict_pkg-body.vhd | 32 ++++++++++++------- vunit/vhdl/data_types/test/tb_dict.vhd | 14 ++++++++ 3 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 docs/news.d/+dict_new_after_deallocate.bugfix.rst diff --git a/docs/news.d/+dict_new_after_deallocate.bugfix.rst b/docs/news.d/+dict_new_after_deallocate.bugfix.rst new file mode 100644 index 000000000..5ea02f20c --- /dev/null +++ b/docs/news.d/+dict_new_after_deallocate.bugfix.rst @@ -0,0 +1 @@ +Fixed ``new_dict`` crashing after ``deallocate`` of a dict that had grown beyond one bucket, caused by recycled pointers being longer than requested. diff --git a/vunit/vhdl/data_types/src/dict_pkg-body.vhd b/vunit/vhdl/data_types/src/dict_pkg-body.vhd index e065516e3..10c1fad37 100644 --- a/vunit/vhdl/data_types/src/dict_pkg-body.vhd +++ b/vunit/vhdl/data_types/src/dict_pkg-body.vhd @@ -11,26 +11,36 @@ package body dict_pkg is constant meta_length : natural := meta_num_keys+1; constant new_bucket_size : natural := 1; + -- The pool recycles pointers that can be longer than requested but dict_t uses length as bucket count and size + impure function new_exact_ptr ( + length : natural + ) return integer_vector_ptr_t is + variable ptr : integer_vector_ptr_t := new_integer_vector_ptr(int_pool, length); + begin + resize(ptr, length); + return ptr; + end; + impure function new_dict return dict_t is variable dict : dict_t; variable tmp : integer_vector_ptr_t; constant num_buckets : natural := 1; begin - dict := (p_meta => new_integer_vector_ptr(int_pool, meta_length), - p_bucket_lengths => new_integer_vector_ptr(int_pool, num_buckets), - p_bucket_keys => new_integer_vector_ptr(int_pool, num_buckets), - p_bucket_values => new_integer_vector_ptr(int_pool, num_buckets), - p_bucket_value_types => new_integer_vector_ptr(int_pool, num_buckets)); + dict := (p_meta => new_exact_ptr(meta_length), + p_bucket_lengths => new_exact_ptr(num_buckets), + p_bucket_keys => new_exact_ptr(num_buckets), + p_bucket_values => new_exact_ptr(num_buckets), + p_bucket_value_types => new_exact_ptr(num_buckets)); set(dict.p_meta, meta_num_keys, 0); for i in 0 to length(dict.p_bucket_lengths)-1 loop -- Zero items in bucket set(dict.p_bucket_lengths, i, 0); - tmp := new_integer_vector_ptr(int_pool, new_bucket_size); + tmp := new_exact_ptr(new_bucket_size); set(dict.p_bucket_keys, i, to_integer(tmp)); - tmp := new_integer_vector_ptr(int_pool, new_bucket_size); + tmp := new_exact_ptr(new_bucket_size); set(dict.p_bucket_values, i, to_integer(tmp)); - tmp := new_integer_vector_ptr(int_pool, new_bucket_size); + tmp := new_exact_ptr(new_bucket_size); set(dict.p_bucket_value_types, i, to_integer(tmp)); end loop; return dict; @@ -227,9 +237,9 @@ package body dict_pkg is -- Create new buckets for i in old_num_buckets to num_buckets-1 loop - set(dict.p_bucket_keys, i, to_integer(new_integer_vector_ptr(int_pool, new_bucket_size))); - set(dict.p_bucket_values, i, to_integer(new_integer_vector_ptr(int_pool, new_bucket_size))); - set(dict.p_bucket_value_types, i, to_integer(new_integer_vector_ptr(int_pool, new_bucket_size))); + set(dict.p_bucket_keys, i, to_integer(new_exact_ptr(new_bucket_size))); + set(dict.p_bucket_values, i, to_integer(new_exact_ptr(new_bucket_size))); + set(dict.p_bucket_value_types, i, to_integer(new_exact_ptr(new_bucket_size))); set(dict.p_bucket_lengths, i, 0); end loop; diff --git a/vunit/vhdl/data_types/test/tb_dict.vhd b/vunit/vhdl/data_types/test/tb_dict.vhd index a2a002c4a..9e6033f8d 100644 --- a/vunit/vhdl/data_types/test/tb_dict.vhd +++ b/vunit/vhdl/data_types/test/tb_dict.vhd @@ -300,6 +300,20 @@ begin end loop; check_enumeration(dict, 4); + elsif run("test new dict after deallocate of a grown dict") then + dict := new_dict; + for i in 1 to 200 loop + set_integer(dict, "k" & integer'image(i), i); + end loop; + deallocate(dict); + dict := new_dict; + check_equal(num_keys(dict), 0); + for i in 1 to 200 loop + set_integer(dict, "k" & integer'image(i), i); + end loop; + check_equal(num_keys(dict), 200); + check_equal(get_integer(dict, "k17"), 17); + elsif run("Test push and pop dict_t") then queue := new_queue; dict := new_dict; From b0d5db380fd257be1f2a98e6ec06a9b20342d9a6 Mon Sep 17 00:00:00 2001 From: Sebastian Abrahamsson Date: Tue, 6 Oct 2026 07:40:16 +0200 Subject: [PATCH 3/3] Use integer'image in get_key for VHDL-93/2002 to_string(natural) is VHDL-2008 only. Co-Authored-By: Claude Opus 5.5 --- vunit/vhdl/data_types/src/dict_pkg-body.vhd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vunit/vhdl/data_types/src/dict_pkg-body.vhd b/vunit/vhdl/data_types/src/dict_pkg-body.vhd index 10c1fad37..4add140d7 100644 --- a/vunit/vhdl/data_types/src/dict_pkg-body.vhd +++ b/vunit/vhdl/data_types/src/dict_pkg-body.vhd @@ -298,7 +298,7 @@ package body dict_pkg is variable bucket_length : natural; variable bucket_keys : integer_vector_ptr_t; begin - assert index < num_keys(dict) report "key index " & to_string(index) & " out of range"; + assert index < num_keys(dict) report "key index " & integer'image(index) & " out of range"; for bucket_idx in 0 to length(dict.p_bucket_lengths)-1 loop bucket_length := get(dict.p_bucket_lengths, bucket_idx); if offset < bucket_length then