Exemplo n.º 1
0
class SkipList(HW_sim_object):
    def __init__(self, env, period, size, outreg_width, enq_fifo_depth,
                 rd_latency, wr_latency, outreg_latency):
        super(SkipList, self).__init__(env, period)

        self.env = env
        self.period = period
        self.outreg_width = outreg_width
        self.outreg_latency = outreg_latency
        self.enq_fifo_depth = enq_fifo_depth

        # Process communication pipes
        self.search_in_pipe = simpy.Store(env)
        self.search_out_pipe = simpy.Store(env)
        self.enq_in_pipe = simpy.Store(env)
        self.enq_out_pipe = simpy.Store(env)
        self.deq_in_pipe = simpy.Store(env)
        self.deq_out_pipe = simpy.Store(env)
        self.outreg_ins_in_pipe = simpy.Store(env)
        self.outreg_ins_out_pipe = simpy.Store(env)
        self.outreg_rem_in_pipe = simpy.Store(env)
        self.outreg_rem_out_pipe = simpy.Store(env)
        self.nodes_r_in_pipe = simpy.Store(env)
        self.nodes_r_out_pipe = simpy.Store(env)
        self.nodes_w_in_pipe = simpy.Store(env)
        self.nodes_w_out_pipe = simpy.Store(env)

        # Block RAM for node memory
        depth = size
        self.nodes = BRAM(self.env, period, self.nodes_r_in_pipe,
                          self.nodes_r_out_pipe, self.nodes_w_in_pipe,
                          self.nodes_w_out_pipe, depth, wr_latency, rd_latency)
        # FIFO for free node list
        self.free_node_list = Fifo(size)
        # Output register on dequeue side
        self.outreg = out_reg(self.env, period, self.outreg_ins_in_pipe,
                              self.outreg_ins_out_pipe,
                              self.outreg_rem_in_pipe,
                              self.outreg_rem_out_pipe, outreg_width,
                              outreg_latency)
        # FIFO for enqueing into the skip list
        self.enq_fifo = Fifo(enq_fifo_depth)

        # Set current size and max level to zero
        self.num_entries = 0
        self.currMaxLevel = 0

        # Push all free nodes in free list FIFO
        for addr in range(size):
            self.free_node_list.push(addr)

        # log2_size is max height skip list will grow to
        self.log2_size = int(math.log(size, 2))

        # Head and tail pointers for each level, representing -inf and +inf
        self.head = self.log2_size * [0]
        self.tail = self.log2_size * [0]

        # Busy flag
        self.busy = 1

        # Next value to be output
        self.next_val = None

        # Lists to store time measurements
        self.bg_search_nclks_list = []
        self.bg_enq_nclks_list = []
        self.bg_deq_nclks_list = []

        # register processes for simulation
        self.run(env)

    def run(self, env):
        self.env.process(self.initSkipList())
        self.env.process(self.search())
        self.env.process(self.enqueue())
        self.env.process(self.dequeue())
        self.enq_sl_proc = self.env.process(self.enq_sl())
        self.deq_sl_proc = self.env.process(self.deq_sl())

    def __str__(self):
        outStr = ""
        # Level 0 head and tail
        h0 = self.head[0]
        t0 = self.tail[0]

        # Loop through all levels in descending order
        for i in range(self.currMaxLevel, -1, -1):
            val0, hsp0, mdp0, lvl0, r0, l0, u0, d0 = self.nodes.mem[h0]
            # -inf
            outStr += "-oo--"
            # For every node in level 0...
            while r0 != t0:
                val0, hsp0, mtp0, lvl0, r0, l0, u0, d0 = self.nodes.mem[r0]
                # Print value in level i if it exists in level 0
                j = 0
                u = u0
                while j < i:
                    if u == -1:
                        # print dashes if no connection between level 0 and level i
                        if val0 < 10:
                            outStr += "---"
                        elif val0 < 100:
                            outStr += "----"
                        else:
                            outStr += "-----"
                        break
                    else:
                        val, hsp, mdp, lvl, r, l, u, d = self.nodes.mem[u]
                        j += 1
                if i == j:
                    outStr += str(val0) + "--"
            # +inf
            outStr += "+oo\n"
        return outStr

    def initSkipList(self):
        prev_h = -1
        prev_t = -1
        # Initialize head and tail pointers up to log2(maxsize) levels
        for i in range(self.log2_size):
            h = self.free_node_list.pop()
            t = self.free_node_list.pop()
            self.head[i] = h
            self.tail[i] = t

            if i > 0:
                # Read head from lower level
                # Send read request
                self.nodes_r_in_pipe.put(prev_h)
                # Wait for response
                prev_val, prev_hsp, prev_mdp, prev_lvl, prev_r, prev_l, prev_u, prev_d = yield self.nodes_r_out_pipe.get(
                )
                # Write back w/ up ptrs set to this level
                self.nodes_w_in_pipe.put((prev_h, [
                    prev_val, prev_hsp, prev_mdp, prev_lvl, prev_r, prev_l, h,
                    prev_d
                ]))
                yield self.nodes_w_out_pipe.get()
                # Read tail from lower level
                self.nodes_r_in_pipe.put(prev_t)
                prev_val, prev_hsp, prev_mdp, prev_lvl, prev_r, prev_l, prev_u, prev_d = yield self.nodes_r_out_pipe.get(
                )
                # Write back w/ up ptrs set to this level
                self.nodes_w_in_pipe.put((prev_t, [
                    prev_val, prev_hsp, prev_mdp, prev_lvl, prev_r, prev_l, t,
                    prev_d
                ]))
                yield self.nodes_w_out_pipe.get()

            # Write current level's head/tail
            self.nodes_w_in_pipe.put(
                (h, [POS_INF, -1, -1, i, t, -1, -1, prev_h]))
            yield self.nodes_w_out_pipe.get()
            self.nodes_w_in_pipe.put(
                (t, [NEG_INF, -1, -1, i, -1, h, -1, prev_t]))
            yield self.nodes_w_out_pipe.get()

            prev_h = h
            prev_t = t

        self.busy = 0
#        print ("sl init done @", self.env.now)

# Search for value starting at startNode and stopping at stopLevel

    def search(self):
        while True:
            # wait for search command
            (startNode, stopLevel, value) = yield self.search_in_pipe.get()
            t1 = self.env.now
            n = startNode
            self.nodes_r_in_pipe.put(n)
            val, hsp, mdp, lvl, r, l, u, d = yield self.nodes_r_out_pipe.get()
            dn = d
            while True:
                # Move right as long as value is smaller than nodes on this level
                if value < val:
                    if r != -1:
                        dn = d
                        n = r
                        self.nodes_r_in_pipe.put(n)
                        val, hsp, mdp, lvl, r, l, u, d = yield self.nodes_r_out_pipe.get(
                        )
                else:
                    # Backtrack one
                    n = l
                    # Stop if stopLevel reached
                    if lvl == stopLevel:
                        break
                    else:
                        # Otherwise, go down
                        self.nodes_r_in_pipe.put(dn)
                        val, hsp, mdp, lvl, r, l, u, d = yield self.nodes_r_out_pipe.get(
                        )
            # Output result
            nclks = self.env.now - t1
            self.search_out_pipe.put((n, dn, nclks))

    def enq_sl(self):
        while True:
            try:
                yield self.env.timeout(self.period)
                # If enq_fifo not empty and there's room in skip list, process entry
                if self.enq_fifo.fill_level(
                ) > 0 and self.free_node_list.fill_level() >= (
                        self.currMaxLevel + 1) and self.busy == 0:
                    #print ("enq_sl:", self.env.now)
                    self.busy = 1
                    t1 = self.env.now
                    (value, (hsp, mdp)) = self.enq_fifo.pop()

                    # Update max level
                    self.currMaxLevel = int(
                        math.log(self.num_entries - self.outreg.num_entries,
                                 2))
                    # Generate random number between 0 and current max level (inclusive)
                    level = random.randint(0, self.currMaxLevel)
                    # Start search from head of skip list
                    startNode = self.head[self.currMaxLevel]
                    uNode = -1
                    search_nclks_tot = 0
                    # Insert new nodes at each level starting at randomly selected level and descending to level zero
                    while level >= 0:
                        # Find insertion point at this level starting from the closest preceding node
                        self.search_in_pipe.put((startNode, level, value))
                        (n, startNode,
                         search_nclks) = yield self.search_out_pipe.get()
                        search_nclks_tot += search_nclks
                        # Read node at insertion point
                        self.nodes_r_in_pipe.put(n)
                        lVal, lHsp, lMdp, lLvl, lR, lL, lU, lD = yield self.nodes_r_out_pipe.get(
                        )
                        # Get new node from free list
                        newNode = self.free_node_list.pop()
                        # Connect left neighbor to new node
                        self.nodes_w_in_pipe.put(
                            (n, [lVal, lHsp, lMdp, lLvl, newNode, lL, lU, lD]))
                        yield self.nodes_w_out_pipe.get()
                        # Connect right neighbor
                        self.nodes_r_in_pipe.put(lR)
                        rVal, rHsp, rMdp, rLvl, rR, rL, rU, rD = yield self.nodes_r_out_pipe.get(
                        )
                        self.nodes_w_in_pipe.put(
                            (lR, [rVal, rHsp, rMdp, rLvl, rR, newNode, rU,
                                  rD]))
                        yield self.nodes_w_out_pipe.get()
                        # Connect with level above if any
                        if uNode != -1:
                            self.nodes_r_in_pipe.put(uNode)
                            uVal, uHsp, uMdp, uLvl, uR, uL, uU, uD = yield self.nodes_r_out_pipe.get(
                            )
                            self.nodes_w_in_pipe.put(
                                (uNode,
                                 [uVal, uHsp, uMdp, uLvl, uR, uL, uU,
                                  newNode]))
                            yield self.nodes_w_out_pipe.get()
                        # Connect new node to l/r neighbors on same level and up.  Down ptr is connected in next cycle
                        newVal, newHsp, newMdp, newLvl, newR, newL = value, hsp, mdp, level, lR, n
                        self.nodes_w_in_pipe.put((newNode, [
                            newVal, newHsp, newMdp, newLvl, newR, newL, uNode,
                            -1
                        ]))
                        yield self.nodes_w_out_pipe.get()
                        uNode = newNode
                        uVal, uHsp, uMdp, uLvl, uR, uL, uU = newVal, newHsp, newMdp, newLvl, newR, newL, newNode
                        # Next level down
                        level -= 1
                    # Write time measurements to lists
                    self.bg_search_nclks_list.append(search_nclks_tot)
                    enq_nclks = self.env.now - t1 - search_nclks_tot
                    self.bg_enq_nclks_list.append(enq_nclks)
                    self.busy = 0

            except simpy.Interrupt as i:
                #                print ("enq_sl stopped")
                break

    def enqueue(self):
        while True:
            # Wait for enqueue command
            (value, hsp, mdp) = yield self.enq_in_pipe.get()
            t1 = self.env.now
            # Wait if out reg and enqueue FIFO are full
            while (self.outreg.num_entries == self.outreg.width
                   and self.enq_fifo.fill_level()
                   == self.enq_fifo_depth) or self.outreg.busy == 1:
                yield self.env.timeout(self.period)
            # Insert into output reg
            self.outreg_ins_in_pipe.put((value, [hsp, mdp]))
            (out_reg_val, out_reg_ptrs) = yield self.outreg_ins_out_pipe.get()
            if out_reg_val != -1:
                # out reg insert returned an entry (either same new entry or one that was evicted from out reg)
                # push entry into enqueue FIFO
                self.enq_fifo.push((out_reg_val, out_reg_ptrs))

            enq_nclks = self.env.now - t1
            self.enq_out_pipe.put((0, enq_nclks))
            self.num_entries += 1

    def deq_sl(self):
        while True:
            try:
                # Wait one clock
                yield self.env.timeout(self.period)
                # If there's room in out reg and there are entries in skip list and it's not busy
                if (
                        self.outreg.num_entries < self.outreg.width
                ) and self.num_entries > self.outreg.num_entries and self.busy == 0:
                    t1 = self.env.now
                    self.busy = 1
                    # Point to tail node in level 0
                    t = self.tail[0]
                    # Read tail
                    self.nodes_r_in_pipe.put(t)
                    tVal, tHsp, tMdp, tLvl, tR, tL, tU, tD = yield self.nodes_r_out_pipe.get(
                    )
                    # Read node to dequeue
                    self.nodes_r_in_pipe.put(tL)
                    dqVal, dqHsp, dqMdp, dqLvl, dqR, dqL, dqU, dqD = yield self.nodes_r_out_pipe.get(
                    )

                    # Send dequeued value to out reg
                    self.outreg.ins_in_pipe.put((dqVal, [dqHsp, dqMdp]))
                    (tmpVal, tmpPtrs) = yield self.outreg_ins_out_pipe.get()
                    # tmpVal should be -1 because there was room available in out reg
                    if tmpVal != -1:
                        print(
                            "Dequeue Error!: Received non-null value from out reg:",
                            tmpVal, tmpPtrs)

                    # Read left neighbor
                    self.nodes_r_in_pipe.put(dqL)
                    llVal, llHsp, llMdp, llLvl, llR, llL, llU, llD = yield self.nodes_r_out_pipe.get(
                    )
                    # Connect left neighbor to tail
                    self.nodes_w_in_pipe.put(
                        (dqL, [llVal, llHsp, llMdp, llLvl, t, llL, llU, llD]))
                    yield self.nodes_w_out_pipe.get()
                    self.nodes_w_in_pipe.put(
                        (t, [tVal, tHsp, tMdp, tLvl, tR, dqL, tU, tD]))
                    yield self.nodes_w_out_pipe.get()

                    # Clear node and return it to free list
                    self.nodes_w_in_pipe.put(
                        (tL, [-1, -1, -1, -1, -1, -1, -1, -1]))
                    yield self.nodes_w_out_pipe.get()
                    self.free_node_list.push(tL)

                    # Loop to free any nodes above
                    while dqU != -1 and dqLvl <= self.currMaxLevel:
                        # Read up neighbor
                        self.nodes_r_in_pipe.put(dqU)
                        uVal, uHsp, uMdp, uLvl, uR, uL, uU, uD = yield self.nodes_r_out_pipe.get(
                        )
                        # Read tail connected to this node
                        self.nodes_r_in_pipe.put(uR)
                        tVal, tHsp, tMdp, tLvl, tR, tL, tU, tD = yield self.nodes_r_out_pipe.get(
                        )
                        # Read left neighbor
                        self.nodes_r_in_pipe.put(uL)
                        lVal, lHsp, lMdp, lLvl, lR, lL, lU, lD = yield self.nodes_r_out_pipe.get(
                        )
                        # Connect left neighbor to tail
                        self.nodes_w_in_pipe.put(
                            (uL, [lVal, lHsp, lMdp, lLvl, uR, lL, lU, lD]))
                        self.nodes_w_out_pipe.get()
                        self.nodes_w_in_pipe.put(
                            (uR, [tVal, tHsp, tMdp, tLvl, tR, uL, tU, tD]))
                        self.nodes_w_out_pipe.get()
                        # Clear node and return it to free list
                        self.nodes_w_in_pipe.put(
                            (dqU, [-1, -1, -1, -1, -1, -1, -1, -1]))
                        yield self.nodes_w_out_pipe.get()
                        self.free_node_list.push(dqU)
                        # Move up
                        dqU = uU

                    # Adjust max level
                    maxLevel = int(
                        math.log(
                            self.num_entries - self.outreg.num_entries + 1, 2))
                    # if levels decreased, remove any nodes left in the top level
                    if maxLevel < self.currMaxLevel:
                        h = self.head[self.currMaxLevel]
                        t = self.tail[self.currMaxLevel]
                        self.nodes_r_in_pipe.put(h)
                        val, hsp, mdp, lvl, r, l, u, d = yield self.nodes_r_out_pipe.get(
                        )
                        # Connect head and tail in vacated level
                        self.nodes_w_in_pipe.put((h, [
                            POS_INF, -1, -1, lvl, t, -1, -1, self.head[lvl - 1]
                        ]))
                        yield self.nodes_w_out_pipe.get()
                        self.nodes_w_in_pipe.put((t, [
                            NEG_INF, -1, -1, lvl, -1, h, -1, self.tail[lvl - 1]
                        ]))
                        yield self.nodes_w_out_pipe.get()
                        self.currMaxLevel = maxLevel
                        # Walk through all nodes at that level and free them
                        while (r != t):
                            # Read right node
                            self.nodes_r_in_pipe.put(r)
                            rVal, rHsp, rMdp, rLvl, rR, rL, rU, rD = yield self.nodes_r_out_pipe.get(
                            )

                            # Null out up ptrs in nodes below
                            self.nodes_r_in_pipe.put(rD)
                            dVal, dHsp, dMdp, dLvl, dR, dL, dU, dD = yield self.nodes_r_out_pipe.get(
                            )
                            self.nodes_w_in_pipe.put(
                                (rD, [dVal, dHsp, dMdp, dLvl, dR, dL, -1, dD]))
                            yield self.nodes_w_out_pipe.get()

                            # Clear node and free it
                            self.nodes_w_in_pipe.put(
                                (r, [-1, -1, -1, -1, -1, -1, -1, -1]))
                            self.nodes_w_out_pipe.get()
                            self.free_node_list.push(r)
                            # Move right
                            r = rR

                    deq_nclks = self.env.now - t1
                    self.bg_deq_nclks_list.append(deq_nclks)
                    self.busy = 0

            except simpy.Interrupt as i:
                #                print ("deq_sl stopped")
                break

    def dequeue(self):
        while True:
            # Wait for dequeue command
            yield self.deq_in_pipe.get()
            t1 = self.env.now
            # Send remove request to out reg
            self.outreg_rem_in_pipe.put(True)
            (retVal, (retHsp, retMdp)) = yield self.outreg_rem_out_pipe.get()
            self.num_entries -= 1
            # Output deq result
            deq_nclks = self.env.now - t1
            self.deq_out_pipe.put((retVal, retHsp, retMdp, deq_nclks))
Exemplo n.º 2
0
class SkipList(HW_sim_object):
    def __init__(self, env, period, size, outreg_width, enq_fifo_depth,
                 rd_latency, wr_latency, outreg_latency):
        super(SkipList, self).__init__(env, period)

        self.env = env
        self.period = period
        self.outreg_width = outreg_width
        self.outreg_latency = outreg_latency
        self.enq_fifo_depth = enq_fifo_depth

        # Process communication pipes
        self.search_in_pipe = simpy.Store(env)
        self.search_out_pipe = simpy.Store(env)
        self.enq_in_pipe = simpy.Store(env)
        self.enq_out_pipe = simpy.Store(env)
        self.deq_in_pipe = simpy.Store(env)
        self.deq_out_pipe = simpy.Store(env)
        self.outreg_ins_in_pipe = simpy.Store(env)
        self.outreg_ins_out_pipe = simpy.Store(env)
        self.outreg_rem_in_pipe = simpy.Store(env)
        self.outreg_rem_out_pipe = simpy.Store(env)
        self.nodes_r_in_pipe = simpy.Store(env)
        self.nodes_r_out_pipe = simpy.Store(env)
        self.nodes_w_in_pipe = simpy.Store(env)
        self.nodes_w_out_pipe = simpy.Store(env)

        # Block RAM for node memory
        depth = size
        self.nodes = BRAM(self.env, period, self.nodes_r_in_pipe,
                          self.nodes_r_out_pipe, self.nodes_w_in_pipe,
                          self.nodes_w_out_pipe, depth, wr_latency, rd_latency)
        # FIFO for free node list
        self.free_node_list = Fifo(size)
        # Output register on dequeue side
        self.outreg = out_reg(self.env, period, self.outreg_ins_in_pipe,
                              self.outreg_ins_out_pipe,
                              self.outreg_rem_in_pipe,
                              self.outreg_rem_out_pipe, outreg_width,
                              outreg_latency)
        # FIFO for enqueing into the skip list
        self.enq_fifo = Fifo(enq_fifo_depth)

        # Set current size and max level to zero
        self.num_entries = 0
        self.currMaxLevel = 0

        # Push all free nodes in free list FIFO
        for addr in range(size):
            self.free_node_list.push(addr)

        # log2_size is max height skip list will grow to
        self.log2_size = int(math.log(size, 2))

        # Head and tail pointers for each level, representing -inf and +inf
        self.head = (self.log2_size + 1) * [0]
        self.tail = (self.log2_size + 1) * [0]

        # Busy flag
        self.busy = 1

        # Next value to be output
        self.next_val = None

        # Lists to store time measurements
        self.bg_search_nclks_list = []
        self.bg_enq_nclks_list = []
        self.bg_deq_nclks_list = []

        # register processes for simulation
        self.run(env)

    def run(self, env):
        self.env.process(self.initSkipList())
        self.env.process(self.search())
        self.env.process(self.enqueue())
        self.env.process(self.dequeue())
        self.enq_sl_proc = self.env.process(self.enq_sl())
        self.deq_sl_proc = self.env.process(self.deq_sl())

    def __str__(self):
        outStr = ""
        # Level 0 head and tail
        h0 = self.head[0]
        t0 = self.tail[0]

        # Loop through all levels in descending order
        for i in range(self.currMaxLevel, -1, -1):
            val0, hsp0, mdp0, lvl0, r0, l0, u0, d0 = self.nodes.mem[h0]
            # +inf
            outStr += "+oo --"
            # For every node in level 0...
            while r0 != t0:
                val0, hsp0, mtp0, lvl0, r0, l0, u0, d0 = self.nodes.mem[r0]
                # Print value in level i if it exists in level 0
                j = 0
                u = u0
                while j < i:
                    if u == -1:
                        # print dashes if no connection between level 0 and level i
                        if val0 < 10:
                            outStr += "---"
                        elif val0 < 100:
                            outStr += "----"
                        else:
                            outStr += "-----"
                        break
                    else:
                        val, hsp, mdp, lvl, r, l, u, d = self.nodes.mem[u]
                        j += 1
                if i == j:
                    outStr += str(val0) + "--"
            # -inf
            outStr += " -oo\n"
        return outStr

    def initSkipList(self):
        prev_h = -1
        prev_t = -1
        # Initialize head and tail pointers up to log2(maxsize) levels
        for i in range(self.log2_size + 1):
            h = self.free_node_list.pop()
            t = self.free_node_list.pop()
            self.head[i] = h
            self.tail[i] = t

            if i > 0:
                # Read head from lower level
                # Send read request
                self.nodes_r_in_pipe.put(prev_h)
                # Wait for response
                prev_val, prev_hsp, prev_mdp, prev_lvl, prev_r, prev_l, prev_u, prev_d = yield self.nodes_r_out_pipe.get(
                )
                # Write back w/ up ptrs set to this level
                self.nodes_w_in_pipe.put((prev_h, [
                    prev_val, prev_hsp, prev_mdp, prev_lvl, prev_r, prev_l, h,
                    prev_d
                ]))
                yield self.nodes_w_out_pipe.get()
                # Read tail from lower level
                self.nodes_r_in_pipe.put(prev_t)
                prev_val, prev_hsp, prev_mdp, prev_lvl, prev_r, prev_l, prev_u, prev_d = yield self.nodes_r_out_pipe.get(
                )
                # Write back w/ up ptrs set to this level
                self.nodes_w_in_pipe.put((prev_t, [
                    prev_val, prev_hsp, prev_mdp, prev_lvl, prev_r, prev_l, t,
                    prev_d
                ]))
                yield self.nodes_w_out_pipe.get()

            # Write current level's head/tail
            self.nodes_w_in_pipe.put(
                (h, [POS_INF, -1, -1, i, t, -1, -1, prev_h]))
            yield self.nodes_w_out_pipe.get()
            self.nodes_w_in_pipe.put(
                (t, [NEG_INF, -1, -1, i, -1, h, -1, prev_t]))
            yield self.nodes_w_out_pipe.get()

            prev_h = h
            prev_t = t

            self.busy = 0
#            print ("sl init done @", self.env.now)

# Search for insertion point for new value

    def search(self):
        while True:
            # wait for search command
            value = yield self.search_in_pipe.get()
            t1 = self.env.now
            level = self.currMaxLevel
            n = self.head[level]
            u = self.head[level + 1]
            # Loop until bottom level
            while level >= 0:
                print("level:", level)
                cons_nodes = 0
                # Read node n
                self.nodes_r_in_pipe.put(n)
                nVal, nHsp, nMdp, nLvl, nR, nL, nU, nD = yield self.nodes_r_out_pipe.get(
                )
                d, dVal, dHsp, dMdp, dLvl, dR, dL, dU, dD = n, nVal, nHsp, nMdp, nLvl, nR, nL, nU, nD
                # While traversing this level searcing for consecutive nodes...
                while nR != -1:
                    # Store current node in l
                    l, lVal, lHsp, lMdp, lLvl, lR, lL, lU, lD = n, nVal, nHsp, nMdp, nLvl, nR, nL, nU, nD
                    print("curr node: @", n, "val:", nVal, "level", nLvl)
                    # Move right
                    n = nR
                    self.nodes_r_in_pipe.put(n)
                    nVal, nHsp, nMdp, nLvl, nR, nL, nU, nD = yield self.nodes_r_out_pipe.get(
                    )
                    # Save the node at which we will drop down
                    if nVal > value:
                        d, dVal, dHsp, dMdp, dLvl, dR, dL, dU, dD = n, nVal, nHsp, nMdp, nLvl, nR, nL, nU, nD
                    # Exit if reached a higher node stack
                    if nU != -1:
                        break
                    # Count consecutive nodes except for head
                    cons_nodes += 1
                    print("cons_nodes:", cons_nodes)
                    # If max number of consecutive nodes found
                    if cons_nodes == MAX_CONS_NODES:
                        # Insert new node one level above
                        # Read node in level above
                        self.nodes_r_in_pipe.put(u)
                        uVal, uHsp, uMdp, uLvl, uR, uL, uU, uD = yield self.nodes_r_out_pipe.get(
                        )
                        print("read u: @", u, "val:", uVal)
                        # Get new node
                        m = self.free_node_list.pop()
                        # Connect new node
                        self.nodes_w_in_pipe.put(
                            (m, [lVal, lHsp, lMdp, level + 1, uR, u, -1, l]))
                        yield self.nodes_w_out_pipe.get()
                        print("adding node: @", m, "val:", lVal, "level:",
                              level + 1, "r:", uR, "l:", u, "u:", -1, "d:", l)
                        # Connect right neighbor to new node
                        # Read right neighbor of upper node
                        self.nodes_r_in_pipe.put(uR)
                        uRVal, uRHsp, uRMdp, uRLvl, uRR, uRL, uRU, uRD = yield self.nodes_r_out_pipe.get(
                        )
                        print("read uR: @", uR, "val:", uRVal)
                        # Write back
                        self.nodes_w_in_pipe.put(
                            (uR,
                             [uRVal, uRHsp, uRMdp, uRLvl, uRR, m, uRU, uRD]))
                        yield self.nodes_w_out_pipe.get()
                        print("write uR: @", uR, "val:", uRVal, "l:", m)

                        # Connect left neighbor to new node
                        self.nodes_w_in_pipe.put(
                            (u, [uVal, uHsp, uMdp, uLvl, m, uL, uU, uD]))
                        yield self.nodes_w_out_pipe.get()
                        print("write u: @", u, "val:", uVal, "r:", m)

                        # Connect node below to new node
                        self.nodes_w_in_pipe.put(
                            (l, [lVal, lHsp, lMdp, lLvl, lR, lL, m, lD]))
                        yield self.nodes_w_out_pipe.get()
                        print("write l: @", l, "val:", lVal, "u:", m)

                        # Increment current level if we added a new level
                        if level + 1 > self.currMaxLevel:
                            self.currMaxLevel += 1
                            print("incr currMaxLev:", self.currMaxLevel)
                            break

                u = d
                n = dD
                level -= 1
            # Output result
            nclks = self.env.now - t1
            self.search_out_pipe.put(
                ((d, dVal, dHsp, dMdp, dLvl, dR, dL, dU, dD), nclks))

    def enq_sl(self):
        while True:
            try:
                yield self.env.timeout(self.period)
                # If enq_fifo not empty and there's room in skip list, process entry
                if self.enq_fifo.fill_level(
                ) > 0 and self.free_node_list.fill_level() >= (
                        self.currMaxLevel + 1) and self.busy == 0:
                    #print ("enq_sl:", self.env.now)
                    self.busy = 1
                    t1 = self.env.now
                    (value, (hsp, mdp)) = self.enq_fifo.pop()

                    # Find insertion point
                    self.search_in_pipe.put(value)
                    ((n, nVal, nHsp, nMdp, nLvl, nR, nL, nU, nD),
                     search_nclks) = yield self.search_out_pipe.get()

                    # Insert new node at level 0
                    m = self.free_node_list.pop()
                    # Connect new node to neighbors on same level
                    self.nodes_w_in_pipe.put(
                        (m, [value, hsp, mdp, 0, nR, n, -1, -1]))
                    yield self.nodes_w_out_pipe.get()

                    # Connect right neighbor to new node
                    # Read right neighbor
                    self.nodes_r_in_pipe.put(nR)
                    nRVal, nRHsp, nRMdp, nRLvl, nRR, nRL, nRU, nRD = yield self.nodes_r_out_pipe.get(
                    )
                    # Write back
                    self.nodes_w_in_pipe.put(
                        (nR, [nRVal, nRHsp, nRMdp, nRLvl, nRR, m, nRU, nRD]))
                    yield self.nodes_w_out_pipe.get()

                    # Connect left neighbor
                    self.nodes_w_in_pipe.put(
                        (n, [nVal, nHsp, nMdp, nLvl, m, nL, nU, nD]))
                    yield self.nodes_w_out_pipe.get()

                    # Write time measurements to lists
                    self.bg_search_nclks_list.append(search_nclks)
                    enq_nclks = self.env.now - t1 - search_nclks
                    self.bg_enq_nclks_list.append(enq_nclks)
                    self.busy = 0

            except simpy.Interrupt as i:
                #                print ("enq_sl stopped")
                break

    def enqueue(self):
        while True:
            # Wait for enqueue command
            (value, hsp, mdp) = yield self.enq_in_pipe.get()
            t1 = self.env.now
            # Wait if out reg and enqueue FIFO are full
            while (self.outreg.num_entries == self.outreg.width
                   and self.enq_fifo.fill_level()
                   == self.enq_fifo_depth) or self.outreg.busy == 1:
                yield self.env.timeout(self.period)
            # Insert into output reg
            self.outreg_ins_in_pipe.put((value, [hsp, mdp]))
            (out_reg_val, out_reg_ptrs) = yield self.outreg_ins_out_pipe.get()
            if out_reg_val != -1:
                # out reg insert returned an entry (either same new entry or one that was evicted from out reg)
                # push entry into enqueue FIFO
                self.enq_fifo.push((out_reg_val, out_reg_ptrs))

            enq_nclks = self.env.now - t1
            self.enq_out_pipe.put((0, enq_nclks))
            self.num_entries += 1

    def deq_sl(self):
        while True:
            try:
                # Wait one clock
                yield self.env.timeout(self.period)
                # If there's room in out reg and there are entries in skip list and it's not busy
                if (
                        self.outreg.num_entries < self.outreg.width
                ) and self.num_entries > self.outreg.num_entries and self.busy == 0:
                    t1 = self.env.now
                    self.busy = 1
                    # Point to tail node in level 0
                    t = self.tail[0]
                    # Read tail
                    self.nodes_r_in_pipe.put(t)
                    tVal, tHsp, tMdp, tLvl, tR, tL, tU, tD = yield self.nodes_r_out_pipe.get(
                    )
                    # Read node to dequeue
                    self.nodes_r_in_pipe.put(tL)
                    dqVal, dqHsp, dqMdp, dqLvl, dqR, dqL, dqU, dqD = yield self.nodes_r_out_pipe.get(
                    )

                    # Send dequeued value to out reg
                    self.outreg.ins_in_pipe.put((dqVal, [dqHsp, dqMdp]))
                    (tmpVal, tmpPtrs) = yield self.outreg_ins_out_pipe.get()
                    # tmpVal should be -1 because there was room available in out reg
                    if tmpVal != -1:
                        print(
                            "Dequeue Error!: Received non-null value from out reg:",
                            tmpVal, tmpPtrs)

                    # Read left neighbor
                    self.nodes_r_in_pipe.put(dqL)
                    llVal, llHsp, llMdp, llLvl, llR, llL, llU, llD = yield self.nodes_r_out_pipe.get(
                    )
                    # Connect left neighbor to tail
                    self.nodes_w_in_pipe.put(
                        (dqL, [llVal, llHsp, llMdp, llLvl, t, llL, llU, llD]))
                    yield self.nodes_w_out_pipe.get()
                    self.nodes_w_in_pipe.put(
                        (t, [tVal, tHsp, tMdp, tLvl, tR, dqL, tU, tD]))
                    yield self.nodes_w_out_pipe.get()

                    # Clear node and return it to free list
                    self.nodes_w_in_pipe.put(
                        (tL, [-1, -1, -1, -1, -1, -1, -1, -1]))
                    yield self.nodes_w_out_pipe.get()
                    self.free_node_list.push(tL)

                    # Loop to free any nodes above
                    while dqU != -1:
                        # Read up neighbor
                        self.nodes_r_in_pipe.put(dqU)
                        uVal, uHsp, uMdp, uLvl, uR, uL, uU, uD = yield self.nodes_r_out_pipe.get(
                        )
                        # Read tail connected to this node
                        self.nodes_r_in_pipe.put(uR)
                        tVal, tHsp, tMdp, tLvl, tR, tL, tU, tD = yield self.nodes_r_out_pipe.get(
                        )
                        # Read left neighbor
                        self.nodes_r_in_pipe.put(uL)
                        lVal, lHsp, lMdp, lLvl, lR, lL, lU, lD = yield self.nodes_r_out_pipe.get(
                        )
                        # Connect left neighbor to tail
                        self.nodes_w_in_pipe.put(
                            (uL, [lVal, lHsp, lMdp, lLvl, uR, lL, lU, lD]))
                        self.nodes_w_out_pipe.get()
                        self.nodes_w_in_pipe.put(
                            (uR, [tVal, tHsp, tMdp, tLvl, tR, uL, tU, tD]))
                        self.nodes_w_out_pipe.get()
                        # If level is empty, decrement current max level
                        if tLvl == self.currMaxLevel and uL == self.head[
                                self.currMaxLevel]:
                            self.currMaxLevel -= 1
                        # Clear node and return it to free list
                        self.nodes_w_in_pipe.put(
                            (dqU, [-1, -1, -1, -1, -1, -1, -1, -1]))
                        yield self.nodes_w_out_pipe.get()
                        self.free_node_list.push(dqU)
                        # Move up
                        dqU = uU

                    deq_nclks = self.env.now - t1
                    self.bg_deq_nclks_list.append(deq_nclks)
                    self.busy = 0

            except simpy.Interrupt as i:
                #                print ("deq_sl stopped")
                break

    def dequeue(self):
        while True:
            # Wait for dequeue command
            yield self.deq_in_pipe.get()
            t1 = self.env.now
            # Send remove request to out reg
            self.outreg_rem_in_pipe.put(True)
            (retVal, (retHsp, retMdp)) = yield self.outreg_rem_out_pipe.get()
            self.num_entries -= 1
            # Output deq result
            deq_nclks = self.env.now - t1
            self.deq_out_pipe.put((retVal, retHsp, retMdp, deq_nclks))