]> git.the-white-hart.net Git - vhdl/commitdiff
Add integer queue container type with unit tests
authorrs <>
Tue, 13 Jan 2026 04:05:49 +0000 (22:05 -0600)
committerrs <>
Tue, 13 Jan 2026 04:05:49 +0000 (22:05 -0600)
libraries/simulated/pkg_intqueue.vhd [new file with mode: 0644]
libraries/simulated/tests/test_intqueue.vhd [new file with mode: 0644]

diff --git a/libraries/simulated/pkg_intqueue.vhd b/libraries/simulated/pkg_intqueue.vhd
new file mode 100644 (file)
index 0000000..83cbc82
--- /dev/null
@@ -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 (file)
index 0000000..59ec430
--- /dev/null
@@ -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;