From: rs <> Date: Tue, 13 Jan 2026 04:05:49 +0000 (-0600) Subject: Add integer queue container type with unit tests X-Git-Url: https://git.the-white-hart.net/?a=commitdiff_plain;h=6eca2b1755de523cec73b8fef47f47e9d4a127bc;p=vhdl Add integer queue container type with unit tests --- diff --git a/libraries/simulated/pkg_intqueue.vhd b/libraries/simulated/pkg_intqueue.vhd new file mode 100644 index 0000000..83cbc82 --- /dev/null +++ b/libraries/simulated/pkg_intqueue.vhd @@ -0,0 +1,166 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +package pkg_intqueue is + + -- Internal types (do not use) + type int_array_t is array(natural range <>) of integer; + type p_int_array_t is access int_array_t; + type page_t; + type p_page_t is access page_t; + type page_t is record + head_idx: natural; + tail_idx: natural; + values: p_int_array_t; + prev_page: p_page_t; + next_page: p_page_t; + end record; + + ---------------------------------------------------------------------------- + -- Integer queue API + + type intqueue_t is record + page_size: positive; + num_items: natural; + head_page: p_page_t; + tail_page: p_page_t; + end record; + + function intqueue_create(page_size: in positive := 4096) return intqueue_t; + procedure intqueue_clear(q: inout intqueue_t); + procedure intqueue_enq(q: inout intqueue_t; value: in integer); + procedure intqueue_deq(q: inout intqueue_t; value: out integer); + function intqueue_peek(q: in intqueue_t) return integer; + function intqueue_len(q: in intqueue_t) return integer; + +end pkg_intqueue; + + +package body pkg_intqueue is + + function intqueue_create(page_size: in positive := 4096) return intqueue_t is + begin + return ( + page_size => page_size, + num_items => 0, + head_page => null, + tail_page => null + ); + end function; + + + procedure intqueue_clear(q: inout intqueue_t) is + variable p_this: p_page_t; + begin + p_this := q.head_page; + while p_this /= null loop + p_this := p_this.prev_page; + deallocate(p_this.values); + deallocate(p_this); + end loop; + q.num_items := 0; + q.head_page := null; + q.tail_page := null; + end procedure; + + + procedure intqueue_enq(q: inout intqueue_t; value: in integer) is + variable temp_page: p_page_t; + begin + if q.head_page = null then + -- First page added + --report "First page added"; + temp_page := new page_t; + temp_page.head_idx := 0; + temp_page.tail_idx := 0; + temp_page.prev_page := null; + temp_page.next_page := null; + temp_page.values := new int_array_t(q.page_size-1 downto 0); + q.head_page := temp_page; + q.tail_page := temp_page; + elsif q.head_page.head_idx = q.page_size then + -- Existing head page is full + --report "New page added"; + temp_page := new page_t; + temp_page.values := new int_array_t(q.page_size-1 downto 0); + temp_page.head_idx := 0; + temp_page.tail_idx := 0; + temp_page.next_page := null; + temp_page.prev_page := q.head_page; + q.head_page.next_page := temp_page; + q.head_page := temp_page; + end if; + + -- Space to insert is now guaranteed + temp_page := q.head_page; + temp_page.values(temp_page.head_idx) := value; + -- Strange bug? ISIM crashes when two layers of pointers are dereferenced in one expression + --q.head_page.values(q.head_page.head_idx) := value; + temp_page.head_idx := temp_page.head_idx + 1; + q.num_items := q.num_items + 1; + end procedure; + + + procedure intqueue_deq(q: inout intqueue_t; value: out integer) is + variable temp_page: p_page_t; + begin + if q.tail_page = null then + -- Nothing queued + value := 0; + return; + elsif q.tail_page.tail_idx = q.tail_page.head_idx then + -- Page is currently empty + value := 0; + return; + end if; + + -- Values to remove are now guaranteed + temp_page := q.tail_page; + value := temp_page.values(temp_page.tail_idx); + -- Strange bug? ISIM crashes when two layers of pointers are dereferenced in one expression + --value := q.tail_page.values(q.tail_page.tail_idx); + temp_page.tail_idx := temp_page.tail_idx + 1; + q.num_items := q.num_items - 1; + + if q.tail_page.tail_idx = q.page_size then + -- Page has been exhausted + --report "Freeing exhausted page"; + temp_page := q.tail_page; + q.tail_page := q.tail_page.next_page; + if q.tail_page = null then + --report "(only page)"; + q.head_page := null; + else + --report "(other pages remain)"; + q.tail_page.prev_page := null; + end if; + deallocate(temp_page.values); + deallocate(temp_page); + end if; + end procedure; + + + function intqueue_peek(q: in intqueue_t) return integer is + variable temp_page: p_page_t; + begin + if q.tail_page = null then + return 0; + elsif q.tail_page.tail_idx = q.tail_page.head_idx then + return 0; + else + temp_page := q.tail_page; + return temp_page.values(q.tail_page.tail_idx); + -- Strange bug? ISIM crashes when two layers of pointers are dereferenced in one expression + --return q.tail_page.values(q.tail_page.tail_idx); + end if; + end function; + + + function intqueue_len(q: in intqueue_t) return integer is + begin + return q.num_items; + end function; + +end pkg_intqueue; diff --git a/libraries/simulated/tests/test_intqueue.vhd b/libraries/simulated/tests/test_intqueue.vhd new file mode 100644 index 0000000..59ec430 --- /dev/null +++ b/libraries/simulated/tests/test_intqueue.vhd @@ -0,0 +1,96 @@ +library work; +use work.pkg_intqueue.all; + + +entity test_intqueue is +end test_intqueue; + + +architecture behavior of test_intqueue is + + shared variable q: intqueue_t; + +begin + + p_test: process + variable v: integer := -1; + begin + -- Create queue with page size of 5, should be empty + -- Page size is small for testing purposes, realistic size is in the thousands + report "A"; + q := intqueue_create(5); + assert intqueue_len(q) = 0 report "Initial queue not empty"; + + -- Dequeue from empty queue should yield zero with no change to length + report "B"; + intqueue_deq(q, v); + assert v = 0 report "Dequeue from empty queue does not return zero"; + assert intqueue_len(q) = 0 report "Dequeue from empty queue leaves nonempty queue"; + + -- Enqueue item, queue should have length of 1 + report "C"; + intqueue_enq(q, 1); + assert intqueue_len(q) = 1 report "Enqueue into empty queue leaves wrong length"; + + -- Enqueue more items, spill over into a second page + report "D"; + intqueue_enq(q, 2); + intqueue_enq(q, 3); + intqueue_enq(q, 4); + intqueue_enq(q, 5); + intqueue_enq(q, 6); + assert intqueue_len(q) = 6 report "Enqueue of multiple items leaves wrong length"; + + -- Dequeue item, first item was "1", should have length of 5 now + report "E"; + intqueue_deq(q, v); + assert v = 1 report "Dequeue of first item gives incorrect value"; + assert intqueue_len(q) = 5 report "Dequeue of first item leaves incorrect length"; + + -- Dequeue all items, length should be zero and first page should be deallocated + report "F"; + intqueue_deq(q, v); + assert v = 2; + intqueue_deq(q, v); + assert v = 3; + intqueue_deq(q, v); + assert v = 4; + intqueue_deq(q, v); + assert v = 5; + intqueue_deq(q, v); + assert v = 6; + assert intqueue_len(q) = 0; + + -- Enqueue items all the way to the end of the current page, but no further + report "G"; + intqueue_enq(q, 7); + intqueue_enq(q, 8); + intqueue_enq(q, 9); + intqueue_enq(q, 10); + + -- Check that peek returns the tail item + report "H"; + assert intqueue_peek(q) = 7; + assert intqueue_len(q) = 4; + + -- Drain queue, should leave with no pages + report "I"; + intqueue_deq(q, v); + intqueue_deq(q, v); + intqueue_deq(q, v); + intqueue_deq(q, v); + assert intqueue_len(q) = 0; + + -- Enqueue new items + report "J"; + intqueue_enq(q, 11); + intqueue_enq(q, 12); + assert intqueue_len(q) = 2; + intqueue_deq(q, v); + assert v = 11; + + report "K"; + wait; + end process; + +end;