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)
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)
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
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)
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)
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)