So far our instruction tree contains only empty instruction data structures. SIM_instruction_proceed is used both to initialize these data structures and to move the instruction between the phases of instruction operation. The defined instruction phases are: initiated, fetched, decoded, executed, retired, and committed. More phases may be added in the future. These phases do not map directly to a modern processor pipeline but define the atomic phases an instruction can be broken down to inside Simics. Somewhere along the pipeline an instruction need to pass through some or all of these phases.
The user can define which phases to proceed to by calling SIM_instruction_set_stop_phase. This function takes a phase and a boolean value. SIM_instruction_proceed will then stop after the next phase that is set to true. For convenience there are predefined functions that combine setting a stop phase and proceeding. These shortcuts are: SIM_instruction_fetch, SIM_instruction_decode, SIM_instruction_execute, SIM_instruction_retire, and SIM_instruction_commit.
Instructions can be proceeded to the decoded phase before they are inserted in the instruction tree, this is useful if the tree location for a decoded instruction depends on its type (the type of an instruction can be determined when it is decoded). For example the instruction following a delay slot have a different parent depending on whether the delay slot is annulled or not.
During the fetch phase an instruction is copied from memory into the internal instruction data structures created by SIM_instruction_insert. As this is a memory operation, any memory hierarchy attached to Simics will be called during the phase. The memory hierarchy may indicate that the memory access should stall the CPU. If this occurs SIM_instruction_proceed or its shortcuts will return a status value to notify the caller about this. SIM_instruction_remaining_stall_time will return the number of cycles remaining. Once the stall has completed SIM_instruction_remaining_stall_time will return zero and the instruction can be proceeded to the next phase. If SIM_instruction_proceed or any of its shortcuts is called before the stall time has completed they will return the same status value again.
Fetching an instruction may also generate an exception. In this case another status value is returned. Exception handling is described in section 4.3.6.
A program counter value is necessary to fetch an instruction. Simics can look for the value in several places. If the instruction is at the head of the instruction tree or not yet inserted in the tree, Simics will read the architectural state. For instructions at other locations in the tree, Simics will look in the output of the parent instruction. If the parent has not reached the executed phase the program counter output field will be invalid and the instruction will not be able to proceed to fetched. However the user can also override the default program counter value with the function SIM_instruction_write_input_reg. This is the only way to fetch instructions whose parent has not yet executed. This will also mark the instruction as speculative. As soon as the parent instruction is executed the child will become non-speculative if the produced value of the program counter matches the one set (if the parent was non-speculative). When the written program counter does not match the generated program counter the instruction is incorrect and will eventually need to be discarded. See section 4.4.
When simulating the SPARC architecture the above applies to nPC as well.
When an instruction is fetched SIM_instruction_opcode can be used to retrieve the opcode.
During the decode phase Simics translates the fetched instruction into the internal data structures that are used during execution. It is then possible to use SIM_instruction_type to determine the type of the instruction and SIM_instruction_get_reg_info to find the registers used by the instruction. These registers can then be read and written with SIM_instruction_read_input_reg and SIM_instruction_write_input_reg.
Under special circumstances the instruction cannot be decoded until the value of a certain register that determines the instruction is known. For example on the SPARC architecture the value of the asi register can change the instruction from a normal memory operation into a block operation. In this case SIM_instruction_proceed and SIM_instruction_decode will return an error code until the value of the asi register is known.
Instruction semantics are modeled during the execution phase. Thus input values are used to produce output values. The input values used are collected from previously executed instructions by reading from the pool of internal registers or from the architectural register state. The output values are written to the internal registers. As stated before Simics has no restrictions on the number of internal registers.
Before an instruction can execute all the input values must have been produced, i.e. all dependences must be fulfilled. This is done either by executing all instructions that the instruction depends on or by explicitly setting the value of an input register with SIM_instruction_write_input_reg. As with the program counter the latter case will also make the instruction speculative until an earlier instruction produces the same value. This is how value speculation can be handled.
Instructions that access memory may stall if the connected memory hierarchy returns a stall time. SIM_instruction_proceed and SIM_instruction_execute will then return a status code to signal this.
As with the fetch and decode phases, a status code will be returned if the execution phase does not complete due to an exception.
During the Retire phase, all stores that were speculatively executed and kept in the LSQ are sent to memory, one by one and in order if the instruction executed several.
Before an instruction can retire it must have become non-speculative. Retiring stores can be done out-of-order since the LSQ will take care of reordering stores that are conflicting. Note that starting the Retire phase makes the instruction unsquashable since the architectural state will be modified.
An instruction may stall during the Retire phase since memory operations are sent to the memory hierarchy. The status codes are the same as for the Execute phase. A retiring store may trigger an exception (like a memory parity error). In this case, it will be marked as faulting with status executed.
Instruction output is written back to the architectural state during the commit phase. When committed, the instruction is automatically removed from the instruction tree, but the data structures remain until explicitly deallocated by calling SIM_instruction_end. Instructions marked as speculative cannot be committed. However the user can force speculative commit by calling SIM_instruction_force_correct to remove the speculative status before committing. This is strongly discouraged since it may lead to incorrect execution. In addition, an instruction can only be committed if it is at the root of the instruction tree and it has only one child instruction. The one child limit is to avoid the creation of two separate instruction trees.
Committed instructions cannot be discarded since their output have been copied to architectural state and thus cannot be rolled back.
Exceptions may occur at any phase of instruction operation. Exceptions are signaled by a return code from SIM_instruction_proceed or its shortcuts. Once an exception has been signaled the instruction cannot proceeded to further phases. The exception should be handled by calling SIM_instruction_handle_exception on the faulting instruction. This call will set the program counter to the first instruction in the corresponding exception handler. Currently this function requires that the instruction tree contains only the faulting instruction. All earlier instructions must have been committed and all later instructions need to be discarded. All previous instruction must be committed as Simics can only service exception handlers in order and non-speculatively. This cannot be guaranteed unless the instruction with the exception is the oldest instruction in the tree.
Interrupts differ from exceptions in that they are inherently asynchronous. Thus while the handling mechanism is similar the signaling mechanism is quite different. Simics signals that an interrupt has occurred by raising an Asynchronous_Trap hap. The user must install a handler for this hap. The suggested operation is that the hap handler set a flag visible to the cycle handler. Once the cycle handler is aware of an interrupt it may choose to service the interrupt by calling SIM_instruction_handle_interrupt at any time, subject to one restriction - the instruction tree must be empty. When SIM_instruction_handle_interrupt is called Simics will cache the current value for the next instruction and replace it with the address of the first instruction in the interrupt vector. When the interrupt completes the cached address will be used to return control to the interrupted location.