The **ICache (Instruction Cache)** and **DCache (Data Cache)** are implemented as separate instances of the same `Cache` class, each with identical configurations (size, block size, associativity) but distinct roles. The **ICache** handles read-only instruction fetches (`op == 'r' && type == 'I'`), while the **DCache** manages both reads and writes for data (`op == 'r'/'w' && type == 'D'`).
The `ICache` in this simulator is implemented as an instance of the general `Cache` class, specialized for instruction access. Its design enforces **read-only access**, aligning with typical hardware behavior where the instruction cache is never written to. In the simulation logic, any attempt to write to the ICache explicitly triggers an error and aborts execution. This design ensures strict separation of responsibilities between instruction and data paths.
For **parallel access simulation**, the split-cache mode creates two independent cache instances: `iCache` for instructions and `dCache` for data. These caches are accessed based on the operation type parsed from the memory trace. Instead of simulating cycle-by-cycle interleaving, the simulator **approximates parallelism** by dispatching accesses to both caches independently and **accumulates statistics separately**. At the end of the simulation, the total execution time is estimated by taking the **maximum of the total cycles** used by either the ICache or DCache. This approach captures the parallel nature of split caches, where instruction and data fetches can proceed concurrently, and avoids overestimating the execution time as would happen if their latencies were summed. Though simplified, this method effectively reflects the performance benefits of parallel access in split cache architectures.