--- /dev/null
+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;
--- /dev/null
+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;