Пример #1
0
def test_bypassed_basic():
    run_test_vector_sim(RegisterFile(8, 4, 1, 1, True, True), [
        ('read_addr[0] read_data[0]* write_addr[0] write_data[0] write_call[0]'
         ),
        (0, 255, 0, 255, 1),
        (0, 255, 0, 0, 0),
    ],
                        dump_vcd=None,
                        test_verilog=test_verilog)
Пример #2
0
def test_dump_basic():
    run_test_vector_sim(RegisterFile(8, 2, 1, 1, False, False), [
        ('read_addr[0] read_data[0]* write_addr[0] write_data[0] write_call[0] dump_out[0]* dump_out[1]* set_in_[0] set_in_[1] set_call'
         ),
        (0, 0, 0, 5, 1, '?', '?', 0, 0, 0),
        (0, 5, 1, 3, 1, '?', '?', 0, 0, 0),
        (0, 5, 0, 0, 0, 5, 3, 0, 0, 0),
        (0, 5, 0, 0, 0, 5, 3, 4, 2, 1),
        (0, 4, 0, 0, 0, 4, 2, 0, 0, 0),
        (0, 4, 0, 5, 1, 4, 2, 4, 2, 1),
        (0, 4, 0, 0, 0, 4, 2, 0, 0, 0),
    ],
                        dump_vcd=None,
                        test_verilog=test_verilog)
Пример #3
0
    def __init__(s,
                 nslots,
                 num_alloc_ports,
                 num_free_ports,
                 nsnapshots,
                 used_slots_initial=0,
                 freelist_impl=FreeList):
        UseInterface(
            s,
            SnapshottingFreeListInterface(nslots, num_alloc_ports,
                                          num_free_ports, nsnapshots))

        s.free_list = freelist_impl(nslots,
                                    num_alloc_ports,
                                    num_free_ports,
                                    free_alloc_bypass=False,
                                    release_alloc_bypass=False,
                                    used_slots_initial=used_slots_initial)

        s.connect_m(s.alloc, s.free_list.alloc)
        s.connect_m(s.free, s.free_list.free)
        s.connect_m(s.set, s.free_list.set)
        s.connect_m(s.get_state, s.free_list.get_state)

        s.snapshots = [
            RegisterFile(Bits(1),
                         nslots,
                         0,
                         num_alloc_ports,
                         write_read_bypass=False,
                         write_dump_bypass=True) for _ in range(nsnapshots)
        ]

        # pack the dump ports (arrays of 1 bit ports) into bit vectors to make
        # them easier to manage
        s.snapshot_packers = [
            Packer(Bits(1), nslots) for _ in range(nsnapshots)
        ]

        s.dump_mux = Mux(Bits(nslots), nsnapshots)

        for i in range(nsnapshots):
            for j in range(num_alloc_ports):
                # Write a 1 into every snapshot for every allocated entry
                s.connect(s.alloc_index[j], s.snapshots[i].write_addr[j])
                s.connect(s.snapshots[i].write_data[j], 1)
                s.connect(s.alloc_call[j], s.snapshots[i].write_call[j])

            for j in range(nslots):
                s.connect(s.snapshots[i].dump_out[j],
                          s.snapshot_packers[i].pack_in_[j])
                s.connect(s.snapshots[i].set_in_[j], 0)

            @s.combinational
            def handle_reset_alloc_tracking_set_call(i=i):
                if s.reset_alloc_tracking_call and s.reset_alloc_tracking_target_id == i:
                    s.snapshots[i].set_call.v = 1
                else:
                    s.snapshots[i].set_call.v = 0

            s.connect(s.dump_mux.mux_in_[i], s.snapshot_packers[i].pack_packed)

        # Pick from a snapshot to revert
        s.connect(s.dump_mux.mux_select, s.revert_allocs_source_id)
        s.connect(s.free_list.release_mask, s.dump_mux.mux_out)

        # if reset_alloc_tracking and revert_allocs are called in the same cycle
        # with the same target_id, then revert_allocs should revert the new
        # value, which is nothing
        @s.combinational
        def handle_revert_alloc():
            if s.reset_alloc_tracking_call and s.revert_allocs_call and s.reset_alloc_tracking_target_id == s.revert_allocs_source_id:
                s.free_list.release_call.v = 0
            else:
                s.free_list.release_call.v = s.revert_allocs_call
Пример #4
0
    def __init__(s,
                 dtype,
                 nregs,
                 num_read_ports,
                 num_write_ports,
                 write_read_bypass,
                 write_snapshot_bypass,
                 nsnapshots,
                 reset_values=None):
        UseInterface(
            s,
            SnapshottingRegisterFileInterface(dtype, nregs, num_read_ports,
                                              num_write_ports,
                                              write_read_bypass,
                                              write_snapshot_bypass,
                                              nsnapshots))

        # Note that write_dump_bypass is set with write_snapshot_bypass
        # To bypass the result of a write into a snapshot, the internal
        # registerfile must dump the result of a write
        s.regs = RegisterFile(dtype,
                              nregs,
                              num_read_ports,
                              num_write_ports,
                              write_read_bypass,
                              write_snapshot_bypass,
                              reset_values=reset_values)

        s.snapshots = [
            RegisterFile(dtype, nregs, 0, 0, False, False)
            for _ in range(nsnapshots)
        ]

        # Forward read and writes to register file
        s.connect_m(s.read, s.regs.read)
        s.connect_m(s.write, s.regs.write)

        # Connect the dump data from the primary register file
        # to the set port on each snapshot
        for i in range(nsnapshots):
            for j in range(nregs):
                s.connect(s.snapshots[i].set_in_[j], s.regs.dump_out[j])

            # Write to a given snapshot if it matches the target and snapshot was called
            @s.combinational
            def handle_snapshot_save(i=i):
                s.snapshots[
                    i].set_call.v = s.snapshot_call and s.snapshot_target_id == i

        s.snapshot_muxes = [Mux(dtype, nsnapshots) for _ in range(nregs)]
        for j in range(nregs):
            s.connect(s.snapshot_muxes[j].mux_select, s.restore_source_id)
            for i in range(nsnapshots):
                s.connect(s.snapshot_muxes[j].mux_in_[i],
                          s.snapshots[i].dump_out[j])

        # Restore by writing data from the snapshot back into the register file
        # set port.
        # But:
        # (1) If snapshot and restore are called in the same cycle on the same snapshot:
        #  (a) write_snapshot_bypass is False: we snapshot, write, and restore.
        #      It must appear as if the write never happened, so we restore from
        #      s.regs.dump_out
        #  (b) write_snapshot_bypass is True: we write, snapshot, and restore.
        #      It must appear as if the restore never happened, so we do not restore
        # (2) If restore and set are called in the same cycle, the restore doesn't matter.
        #     Execute the set, and do not restore.

        # Compute the restore vector for case 1
        s.restore_vector = [Wire(dtype) for _ in range(nregs)]
        s.should_restore = Wire(1)
        if not write_snapshot_bypass:
            s.connect(s.should_restore, s.restore_call)
            for j in range(nregs):

                @s.combinational
                def compute_restore_vector(j=j):
                    if s.snapshot_call and s.restore_call and s.snapshot_target_id == s.restore_source_id:
                        s.restore_vector[j].v = s.regs.dump_out[j]
                    else:
                        s.restore_vector[j].v = s.snapshot_muxes[j].mux_out
        else:

            @s.combinational
            def compute_should_restore():
                if s.snapshot_call and s.restore_call and s.snapshot_target_id == s.restore_source_id:
                    s.should_restore.v = 0
                else:
                    s.should_restore.v = s.restore_call

            for j in range(nregs):
                s.connect(s.restore_vector[j], s.snapshot_muxes[j].mux_out)

        # Handle the restore-set conflict for case 2
        s.set_vector = [Wire(dtype) for _ in range(nregs)]
        s.should_set = Wire(1)
        s.set_muxes = [Mux(dtype, 2) for _ in range(nregs)]

        @s.combinational
        def handle_should_set():
            s.should_set.v = s.should_restore | s.set_call

        for j in range(nregs):
            s.connect(s.set_muxes[j].mux_in_[0], s.restore_vector[j])
            s.connect(s.set_muxes[j].mux_in_[1], s.set_in_[j])
            s.connect(s.set_muxes[j].mux_select, s.set_call)
            s.connect(s.set_muxes[j].mux_out, s.regs.set_in_[j])

        s.connect(s.regs.set_call, s.should_set)
Пример #5
0
    def __init__(s, interface, MemMsg):
        UseInterface(s, interface)

        s.require(
            MethodSpec(
                'mb_send',
                args={'msg': MemMsg.req},
                rets=None,
                call=True,
                rdy=True,
            ),
            MethodSpec(
                'mb_recv',
                args=None,
                rets={'msg': MemMsg.resp},
                call=True,
                rdy=True,
            ),
        )

        s.store_address_table = RegisterFile(
            AddrSizePair(s.interface.Addr.nbits, s.interface.Size.nbits),
            s.interface.nslots, 1, 1, False, False)
        s.address_valid_table = RegisterFile(Bits(1), s.interface.nslots, 0, 2,
                                             False, False,
                                             [0] * s.interface.nslots)
        s.store_data_table = RegisterFile(s.interface.Data, s.interface.nslots,
                                          1, 1, False, False)
        s.data_valid_table = RegisterFile(Bits(1), s.interface.nslots, 1, 2,
                                          False, False,
                                          [0] * s.interface.nslots)
        s.overlap_checkers = [
            OverlapChecker(
                OverlapCheckerInterface(s.interface.Addr.nbits,
                                        s.interface.max_size))
            for _ in range(s.interface.nslots)
        ]
        s.memory_arbiter = MemoryArbiter(
            MemoryArbiterInterface(s.interface.Addr, s.interface.Size,
                                   s.interface.Data), MemMsg)
        s.connect_m(s.memory_arbiter.mb_send, s.mb_send)
        s.connect_m(s.memory_arbiter.mb_recv, s.mb_recv)
        s.connect_m(s.memory_arbiter.store_acks_outstanding,
                    s.store_acks_outstanding)

        s.overlapped_and_live = [Wire(1) for _ in range(s.interface.nslots)]
        # PYMTL_BROKEN for some reason reduce_or verilates, but then the C++ fails to compile
        s.or_ = Or(LogicOperatorInterface(s.interface.nslots))
        for i in range(s.interface.nslots):
            s.connect(s.overlap_checkers[i].check_base_a, s.store_pending_addr)
            s.connect(s.overlap_checkers[i].check_size_a, s.store_pending_size)
            s.connect(s.overlap_checkers[i].check_base_b,
                      s.store_address_table.dump_out[i].addr)
            s.connect(s.overlap_checkers[i].check_size_b,
                      s.store_address_table.dump_out[i].size)

            s.connect(s.or_.op_in_[i], s.overlapped_and_live[i])

            @s.combinational
            def check_overlapped(i=i):
                s.overlapped_and_live[i].v = not s.overlap_checkers[
                    i].check_disjoint and s.store_pending_live_mask[
                        i] and s.address_valid_table.dump_out[i]

        s.connect(s.store_pending_pending, s.or_.op_out)

        s.connect(s.address_valid_table.write_call[0], s.register_store_call)
        s.connect(s.address_valid_table.write_addr[0], s.register_store_id_)
        s.connect(s.address_valid_table.write_data[0], 0)
        s.connect(s.data_valid_table.write_call[0], s.register_store_call)
        s.connect(s.data_valid_table.write_addr[0], s.register_store_id_)
        s.connect(s.data_valid_table.write_data[0], 0)

        s.connect(s.store_address_table.write_call[0],
                  s.enter_store_address_call)
        s.connect(s.store_address_table.write_addr[0],
                  s.enter_store_address_id_)
        s.connect(s.store_address_table.write_data[0].addr,
                  s.enter_store_address_addr)
        s.connect(s.store_address_table.write_data[0].size,
                  s.enter_store_address_size)
        s.connect(s.address_valid_table.write_call[1],
                  s.enter_store_address_call)
        s.connect(s.address_valid_table.write_addr[1],
                  s.enter_store_address_id_)
        s.connect(s.address_valid_table.write_data[1], 1)

        s.connect(s.store_data_table.write_call[0], s.enter_store_data_call)
        s.connect(s.store_data_table.write_addr[0], s.enter_store_data_id_)
        s.connect(s.store_data_table.write_data[0], s.enter_store_data_data)
        s.connect(s.data_valid_table.write_call[1], s.enter_store_data_call)
        s.connect(s.data_valid_table.write_addr[1], s.enter_store_data_id_)
        s.connect(s.data_valid_table.write_data[1], 1)

        s.connect_m(s.memory_arbiter.recv_load, s.recv_load)
        s.connect_m(s.memory_arbiter.send_load, s.send_load)
        s.connect(s.store_address_table.read_addr[0], s.send_store_id_)
        s.connect(s.store_data_table.read_addr[0], s.send_store_id_)
        s.connect(s.memory_arbiter.send_store_addr,
                  s.store_address_table.read_data[0].addr)
        s.connect(s.memory_arbiter.send_store_size,
                  s.store_address_table.read_data[0].size)
        s.connect(s.memory_arbiter.send_store_data,
                  s.store_data_table.read_data[0])
        s.connect(s.memory_arbiter.send_store_call, s.send_store_call)
        s.connect(s.send_store_rdy, s.memory_arbiter.send_store_rdy)

        s.connect(s.data_valid_table.read_addr[0], s.store_data_available_id_)
        s.connect(s.store_data_available_ret, s.data_valid_table.read_data[0])

        s.connect(s.store_address_table.set_call, 0)
        for port in s.store_address_table.set_in_:
            s.connect(port, 0)
        s.connect(s.address_valid_table.set_call, 0)
        for port in s.address_valid_table.set_in_:
            s.connect(port, 0)
        s.connect(s.store_data_table.set_call, 0)
        for port in s.store_data_table.set_in_:
            s.connect(port, 0)
        s.connect(s.data_valid_table.set_call, 0)
        for port in s.data_valid_table.set_in_:
            s.connect(port, 0)
Пример #6
0
    def __init__(s, dflow_interface):
        UseInterface(s, dflow_interface)
        dlen = s.interface.DataLen
        naregs = s.interface.NumAregs
        npregs = s.interface.NumPregs
        nsnapshots = s.interface.NumSnapshots
        nstore_queue = s.interface.NumStoreQueue
        num_src_ports = s.interface.NumSrcPorts
        num_dst_ports = s.interface.NumDstPorts
        num_is_ready_ports = s.interface.NumIsReadyPorts
        num_forward_ports = s.interface.NumForwardPorts

        # used to allocate snapshot IDs
        s.snapshot_allocator = SnapshottingFreeList(nsnapshots, 1, 1,
                                                    nsnapshots)

        # Reserve the highest tag for x0
        # Free list with 1 alloc ports, 1 free port, and AREG_COUNT - 1 used slots
        # initially
        s.free_regs = SnapshottingFreeList(npregs - 1,
                                           num_dst_ports,
                                           num_dst_ports,
                                           nsnapshots,
                                           used_slots_initial=naregs - 1)
        s.store_ids = SnapshottingFreeList(nstore_queue, num_dst_ports,
                                           num_dst_ports, nsnapshots)
        # arch_used_pregs tracks the physical registers used by the current architectural state
        # arch_used_pregs[i] is 1 if preg i is used by the arf, and 0 otherwise
        # on reset, the ARF is backed by pregs [0, naregs - 1]
        arch_used_pregs_reset = [Bits(1, 0) for _ in range(npregs - 1)]
        for i in range(naregs):
            arch_used_pregs_reset[i] = Bits(1, 1)

        s.arch_used_pregs = RegisterFile(
            Bits(1),
            npregs - 1,
            0,  # no read ports needed, only a dump port
            num_dst_ports *
            2,  # have to write twice for every instruction that commits
            False,  # no read ports, so we don't need a write-read bypass
            True,  # use a write-dump bypass to reset after commit
            reset_values=arch_used_pregs_reset)
        # Zero out the set
        s.connect(s.arch_used_pregs.set_call, 0)
        for i in range(npregs - 1):
            s.connect(s.arch_used_pregs.set_in_[i], 0)

        # Build the initial rename table.
        # x0 -> don't care (will use 0)
        # xn -> n-1
        initial_map = [0] + [x for x in range(naregs - 1)]
        s.rename_table = RenameTable(naregs, npregs, num_src_ports,
                                     num_dst_ports, nsnapshots, True,
                                     initial_map)
        s.ZERO_TAG = s.rename_table.ZERO_TAG

        # The physical register file, which stores the values
        # and ready states
        # Number of read ports is the same as number of source ports
        # Number of write ports is the same as number of dest ports
        # Writes are bypassed before reads, and the dump/set is not used
        s.preg_file = AsynchronousRAM(
            AsynchronousRAMInterface(
                Bits(dlen),
                npregs,
                num_is_ready_ports,
                num_dst_ports,
                True,
            ),
            reset_values=0,
        )
        # 2 write ports are needed for every dst port:
        # The second set to update all the destination states during issue,
        # (get_dst), and the first set to write the computed value
        # (write)
        # We give write the first set since it is sequenced before get_dst
        # In practice, there should be no conflicts (or the processor is doing
        # something dumb). But, we must adhere to the interface even if the
        # user does something dumb and tries to write to a bogus tag which
        # is currently free.
        # The ready table is not bypassed; is_ready comes before all the the writes
        # (which are in get_dst and write)
        s.ready_table = AsynchronousRAM(
            AsynchronousRAMInterface(
                Bits(1),
                npregs,
                num_is_ready_ports,
                num_dst_ports * 2,
                False,
            ),
            reset_values=1,
        )
        # The arhitectural areg -> preg mapping
        # Written and read only in commit
        # No write-read bypass
        # Yes write_dump bypass to allow restores to state after commits
        s.areg_file = RegisterFile(
            s.interface.Preg,
            naregs,
            num_dst_ports,
            num_dst_ports,
            False,
            True,
            reset_values=initial_map,
        )
        # Zero out the set
        s.connect(s.areg_file.set_call, 0)
        for i in range(naregs):
            s.connect(s.areg_file.set_in_[i], 0)

        # commit
        @s.combinational
        def compute_valid_store_mask():
            s.valid_store_mask_mask.v = ~s.store_ids.get_state_state

        s.is_commit_not_zero_tag = [Wire(1) for _ in range(num_dst_ports)]
        for i in range(num_dst_ports):
            # Determine if the commit is not the zero tag
            @s.combinational
            def check_commit(i=i):
                s.is_commit_not_zero_tag[i].v = (
                    s.commit_tag[i] != s.ZERO_TAG) and s.commit_call[i]

            # Read the preg currently associated with this areg
            s.connect(s.areg_file.read_addr[i], s.commit_areg[i])
            # Free the preg currently backing this areg
            s.connect(s.free_regs.free_index[i], s.areg_file.read_data[i])
            # Only free if not the zero tag
            s.connect(s.free_regs.free_call[i], s.is_commit_not_zero_tag[i])
            # Free the store ID from the committing instruction
            s.connect(s.store_ids.free_index[i], s.free_store_id_id_[i])
            # Only free if the commit is valid
            s.connect(s.store_ids.free_call[i], s.free_store_id_call[i])

            # Write into the ARF the new preg
            s.connect(s.areg_file.write_addr[i], s.commit_areg[i])
            s.connect(s.areg_file.write_data[i], s.commit_tag[i])
            # Only write if not the zero tag
            s.connect(s.areg_file.write_call[i], s.is_commit_not_zero_tag[i])

            # Mark the old preg used by the ARF as free
            s.connect(s.arch_used_pregs.write_addr[i],
                      s.areg_file.read_data[i])
            s.connect(s.arch_used_pregs.write_data[i], 0)
            s.connect(s.arch_used_pregs.write_call[i],
                      s.is_commit_not_zero_tag[i])
            # Mark the new preg used by the ARF as used
            s.connect(s.arch_used_pregs.write_addr[i + num_dst_ports],
                      s.commit_tag[i])
            s.connect(s.arch_used_pregs.write_data[i + num_dst_ports], 1)
            s.connect(s.arch_used_pregs.write_call[i + num_dst_ports],
                      s.is_commit_not_zero_tag[i])

        # write
        s.is_write_not_zero_tag = [Wire(1) for _ in range(num_dst_ports)]
        for i in range(num_dst_ports):
            # Determine if the write is not the zero tag
            @s.combinational
            def check_write(i=i):
                s.is_write_not_zero_tag[i].v = (s.write_tag[i] !=
                                                s.ZERO_TAG) and s.write_call[i]

            # All operations on the preg file are on the first set of write ports
            # at the offset 0
            # Write the value and ready into the preg file
            s.connect(s.preg_file.write_addr[i], s.write_tag[i])
            s.connect(s.preg_file.write_data[i], s.write_value[i])
            # Only write if not the zero tag
            s.connect(s.preg_file.write_call[i], s.is_write_not_zero_tag[i])

            s.connect(s.ready_table.write_addr[i], s.write_tag[i])
            s.connect(s.ready_table.write_data[i], 1)
            # Only write if not the zero tag
            s.connect(s.ready_table.write_call[i], s.is_write_not_zero_tag[i])

        # get_updated
        s.is_forward_not_zero_tag = [Wire(1) for _ in range(num_forward_ports)]
        for i in range(num_forward_ports):

            @s.combinational
            def check_forward(i=i):
                s.is_forward_not_zero_tag[i].v = (
                    s.forward_tag[i] != s.ZERO_TAG) and s.forward_call[i]

        # Unify the write and forward ports
        # Note that forward occurs after write
        for i in range(num_dst_ports):
            s.connect(s.get_updated_valid[i], s.is_write_not_zero_tag[i])
            s.connect(s.get_updated_tags[i], s.write_tag[i])
        for i in range(num_forward_ports):
            s.connect(s.get_updated_valid[num_dst_ports + i],
                      s.is_forward_not_zero_tag[i])
            s.connect(s.get_updated_tags[num_dst_ports + i], s.forward_tag[i])

        # get_src
        s.connect_m(s.get_src, s.rename_table.lookup)

        # get_dst
        s.get_dst_need_writeback = [Wire(1) for _ in range(num_dst_ports)]
        for i in range(num_dst_ports):
            s.connect(s.get_dst_rdy[i], s.free_regs.alloc_rdy[i])
            s.connect(s.get_store_id_rdy[i], s.store_ids.alloc_rdy[i])
            s.connect(s.store_ids.alloc_call[i], s.get_store_id_call[i])
            s.connect(s.get_store_id_store_id[i], s.store_ids.alloc_index[i])

            @s.combinational
            def handle_get_dst_allocate(i=i):
                if s.get_dst_areg[i] == 0:
                    # zero register
                    s.free_regs.alloc_call[i].v = 0
                    s.get_dst_preg[i].v = s.ZERO_TAG
                    s.get_dst_need_writeback[i].v = 0
                elif s.free_regs.alloc_rdy[i]:
                    # allocate a register from the freelist
                    s.free_regs.alloc_call[i].v = s.get_dst_call[i]
                    s.get_dst_preg[i].v = s.free_regs.alloc_index[i]
                    s.get_dst_need_writeback[i].v = s.get_dst_call[i]
                else:
                    # free list is full
                    s.free_regs.alloc_call[i].v = 0
                    s.get_dst_preg[i].v = s.ZERO_TAG
                    s.get_dst_need_writeback[i].v = 0

            # Update the rename table
            s.connect(s.rename_table.update_areg[i], s.get_dst_areg[i])
            s.connect(s.rename_table.update_preg[i], s.get_dst_preg[i])
            s.connect(s.rename_table.update_call[i],
                      s.get_dst_need_writeback[i])
            s.connect(s.ready_table.write_addr[i + num_dst_ports],
                      s.get_dst_preg[i])
            s.connect(s.ready_table.write_data[i + num_dst_ports], 0)
            s.connect(s.ready_table.write_call[i + num_dst_ports],
                      s.get_dst_need_writeback[i])

        # is_ready
        s.read_muxes_ready = [
            Mux(Bits(1), 2) for _ in range(num_is_ready_ports)
        ]
        s.is_ready_is_zero_tag = [Wire(1) for _ in range(num_is_ready_ports)]
        for i in range(num_is_ready_ports):

            @s.combinational
            def handle_read(i=i):
                s.is_ready_is_zero_tag[i].v = s.is_ready_tag[i] == s.ZERO_TAG

            s.connect(s.is_ready_tag[i], s.ready_table.read_addr[i])
            s.connect(s.read_muxes_ready[i].mux_in_[0],
                      s.ready_table.read_data[i])
            s.connect(s.read_muxes_ready[i].mux_in_[1], 1)
            s.connect(s.read_muxes_ready[i].mux_select,
                      s.is_ready_is_zero_tag[i])
            s.connect(s.is_ready_ready[i], s.read_muxes_ready[i].mux_out)

        # read
        s.read_forwarded_data = [
            Wire(dlen)
            for _ in range(num_is_ready_ports * (num_forward_ports + 1))
        ]
        for i in range(num_is_ready_ports):

            @s.combinational
            def handle_read_forwarded_data_0(i=i):
                s.read_forwarded_data[i].v = s.preg_file.read_data[i]

            for j in range(num_forward_ports):

                @s.combinational
                def handle_read_forwarded_data(last=num_is_ready_ports * j + i,
                                               now=num_is_ready_ports *
                                               (j + 1) + i,
                                               i=i,
                                               j=j):
                    if s.forward_call[j] and s.forward_tag[j] == s.read_tag[i]:
                        s.read_forwarded_data[now].v = s.forward_value[j]
                    else:
                        s.read_forwarded_data[now].v = s.read_forwarded_data[
                            last]

        s.read_muxes_value = [
            Mux(Bits(dlen), 2) for _ in range(num_is_ready_ports)
        ]
        s.read_is_zero_tag = [Wire(1) for _ in range(num_is_ready_ports)]
        for i in range(num_is_ready_ports):

            @s.combinational
            def handle_read(i=i):
                s.read_is_zero_tag[i].v = s.read_tag[i] == s.ZERO_TAG

            s.connect(s.read_tag[i], s.preg_file.read_addr[i])
            # We take data after the forwarding has been handled
            s.connect(
                s.read_muxes_value[i].mux_in_[0],
                s.read_forwarded_data[num_is_ready_ports * num_forward_ports +
                                      i])
            s.connect(s.read_muxes_value[i].mux_in_[1], 0)
            s.connect(s.read_muxes_value[i].mux_select, s.read_is_zero_tag[i])
            s.connect(s.read_value[i], s.read_muxes_value[i].mux_out)

        # snapshot
        # ready if a snapshot ID is available
        s.connect(s.snapshot_rdy, s.snapshot_allocator.alloc_rdy[0])
        # snapshot ID is allocated by the allocator and returned
        s.connect(s.snapshot_id_, s.snapshot_allocator.alloc_index[0])
        s.connect(s.snapshot_call, s.snapshot_allocator.alloc_call[0])
        # snapshot the snapshot allocator into itself
        s.connect(s.snapshot_allocator.reset_alloc_tracking_target_id,
                  s.snapshot_id_)
        s.connect(s.snapshot_allocator.reset_alloc_tracking_call,
                  s.snapshot_call)
        # snapshot the freelist
        s.connect(s.free_regs.reset_alloc_tracking_target_id, s.snapshot_id_)
        s.connect(s.free_regs.reset_alloc_tracking_call, s.snapshot_call)
        s.connect(s.store_ids.reset_alloc_tracking_target_id, s.snapshot_id_)
        s.connect(s.store_ids.reset_alloc_tracking_call, s.snapshot_call)
        # snapshot the rename table
        s.connect(s.rename_table.snapshot_target_id, s.snapshot_id_)
        s.connect(s.rename_table.snapshot_call, s.snapshot_call)

        # free_snapshot
        # just free it in the snapshot allocator, all the various
        # snapshots will eventually be overwritten
        s.connect(s.snapshot_allocator.free_index[0], s.free_snapshot_id_)
        s.connect(s.snapshot_allocator.free_call[0], s.free_snapshot_call)

        # restore
        # restore the snapshot allocator
        s.connect(s.snapshot_allocator.revert_allocs_source_id,
                  s.restore_source_id)
        s.connect(s.snapshot_allocator.revert_allocs_call, s.restore_call)
        # restore the free list
        s.connect(s.free_regs.revert_allocs_source_id, s.restore_source_id)
        s.connect(s.free_regs.revert_allocs_call, s.restore_call)
        s.connect(s.store_ids.revert_allocs_source_id, s.restore_source_id)
        s.connect(s.store_ids.revert_allocs_call, s.restore_call)
        # restore the rename table
        s.connect(s.rename_table.restore_source_id, s.restore_source_id)
        s.connect(s.rename_table.restore_call, s.restore_call)

        # rollback
        # set the snapshot allocator to the architectural state of no snapshots
        # meaning everything is free (all ones)
        s.connect(s.snapshot_allocator.set_state,
                  (~Bits(nsnapshots, 0)).uint())
        s.connect(s.snapshot_allocator.set_call, s.rollback_call)
        # set the free list to arch_used_pregs (note that write_dump_bypass
        # is true)
        # use a packer to collect all the bits
        s.arch_used_pregs_packer = Packer(Bits(1), npregs - 1)
        for i in range(npregs - 1):
            s.connect(s.arch_used_pregs_packer.pack_in_[i],
                      s.arch_used_pregs.dump_out[i])
        # set the free regs to the complement
        @s.combinational
        def handle_rollback_free_regs_set():
            s.free_regs.set_state.v = ~s.arch_used_pregs_packer.pack_packed

        s.connect(s.free_regs.set_call, s.rollback_call)
        s.connect(s.store_ids.set_state, (~Bits(nstore_queue, 0)).uint())
        s.connect(s.store_ids.set_call, s.rollback_call)
        # set the rename table to the areg_file (again write_dump_bypass is true)
        for i in range(naregs):
            s.connect(s.rename_table.set_in_[i], s.areg_file.dump_out[i])
        s.connect(s.rename_table.set_call, s.rollback_call)