1. Field of the Invention
This invention relates to a processor simulation environment. It enables single and multiple processor systems to be simulated by running code on a host processor.
2. Description of the Prior Art
The increasing complexity of the system code (software) running on processors has forced processors to have different execution privilege levels or modes. There can be many modes such as ‘user’, ‘system’, ‘hypervisor’. These modes allow the processor to separate the context that the system code runs in for each mode.
The increasing use of multiple processors in the design of electronic systems has forced changes in the style of programming used. The parallel operation of multiple processors requires careful management of the instructions which require any READ or WRITE from the system memory. This is due to the fact that the memory is accessible to all the processors in the system and it is important that any such accesses occur in the correct order to maintain the integrity of data.
Early designs used TEST and SET instructions which controlled the read or write of a single location, verifying that the data was valid before it was passed to the part of the system that had requested it. The main drawback of this technique was that the TEST instructions set a semaphore which prevented all other processors accessing the memory until the SET instruction released it. This resulted in the entire system waiting for the semaphore to be released. Since data clashes in parallel systems are quite infrequent, this resulted in a lot of time spent waiting where there was no danger of data corruption.
A more recent and better technique is the use of SPECULATE and COMMIT instructions (see Hammond, L., Carlstrom, B., Wong, V., Hertzberg. B., Chen, M., Kozyrakis, C. and Olukotun, K., “Programming with Transactional Coherence and Consistency” ASPLOS'04, Boston, Mass., U.S.A., October 2004.). These instructions, like the TEST and SET ones, mark an area where memory access requires careful management. However, they differ in two ways: firstly, instead of dealing with a simple READ or WRITE instruction from a single location, the region between the SPECULATE and COMMIT instructions may include a long sequence of instructions which depend on the data from memory and, secondly, the SPECULATE instruction does not set a semaphore which prevents other processors from accessing the memory as the TEST instruction does. The full section of code, known as a transaction, within a SPECULATE/COMMIT region is atomic, i.e. it must either complete in its entirety or it will be deemed to have failed, resulting in a software exception.
The SPECULATE/COMMIT process relies on the use of local cache, known as the level 1 cache (FIG. 1) to store data while the series of instructions that make up the single transaction within a SPECULATE/COMMIT region is being completed. Each processor in the multi processor system has its own level 1 cache. When a SPECULATE instruction is encountered within the code, that instruction directs the processor to change the way that all data access is handled; all subsequent writes must remain within the level 1 cache and not propagate to shared memory or shared cache (FIG. 2). When a COMMIT instruction is encountered, all of the data which has been written to the level 1 cache within the SPECULATE/COMMIT region, known as speculatively written data, is propagated to more distant shared caches or memories within the multiprocessor system in a single atomic action (FIG. 3).
In the cases where a read or a write occurs within a SPECULATE/COMMIT region, the full line of data is copied from shared memory into the cache first (FIG. 4). This is to ensure that when a partial write occurs from the processor, the final data written back to shared memory can be guaranteed to be correct. If the data were not copied first from shared memory to local cache then the transfer of partial writes could lead to errors. This is because the parts of the data, which have not been updated in the cache, will retain previous values which are then subsequently passed back to the shared memory (FIG. 5). Copying the data from shared memory first and then overwriting guarantees the integrity of the data which is subsequently written back to shared memory (FIG. 4).
There are a two main reasons why a write will fail in this SPECULATE/COMMIT process. In SPECULATE mode, a write by a processor will fail if the write cannot be performed without causing another speculatively written cache line to be ejected from the level 1 cache. This occurs because the associativity of the cache, i.e. number of locations where data from any given address in memory may be stored, has been exceeded. A COMMIT instruction will fail if any of the speculatively written data would conflict with changes in the shared cache. This occurs if one of the other processors in the system writes to the same cache line address as the one which would have already been changed by the speculatively written data had that data been written direct to shared cache rather than level 1 cache. In some systems this may be detected by hardware and the exception taken immediately that the conflicting write occurs; in other systems, the exception may be deferred until the subsequent commit is encountered. Either type of failure will cause an exception to be taken. If this happens all the accumulated speculatively written data will be discarded.
Use of the SPECULATE/COMMIT instructions therefore results in faster processing as the delays associated with waiting for the semaphore to clear in the TEST/SET style have been removed. The only disadvantage is that, when the code in the SPECULATE/COMMIT region fails to complete properly, the exception handling needs to be more complex to deal with the fact that this is a block of multiple instructions and not a single memory access which has failed.
Simulating multi-processor systems requires changes to existing techniques in order to ensure maximum efficiency and hence simulation speed. One of the more commonly used techniques is that of code-morphing where the simulated processor code is run on the host processor of the machine on which the simulation runs. This is done by means of a code dictionary which, at run time, translates or morphs the code from its original form to the one required for it to run on the host processor.