Model Components¶
An overview of what's in model/, and how the pieces relate. See each
subdirectory's own README.md for full detail. This page is the map.
model/
cpp_common_code/ SparcCore, the core itself. Timing-agnostic.
cpp_model/ SparcStateMachine, 0-delay driver, no Sitar.
sitar_model/ Top/Core/SparcThread/MemoryInterface/MainMemory,
Sitar-timed driver, cycle-level (non-pipelined).
cpp_common_code/: the core itself¶
SparcCore is a pure C++ class implementing SPARC V8 instruction
semantics. This includes decode dispatch, ALU/FPU operations, trap logic
(checkExternalTraps(), checkInstructionException(), executeTraps(),
following Appendix C's reference algorithms closely, with section
citations throughout the source), and the register file (Registers.h,
including windowed integer registers, f0-f31, and all state
registers). It has no notion of cycles, timing, or a driving loop of its
own. A driver calls its methods in the right order and supplies memory
access itself. This is deliberate: it's the one place ISA semantics live,
shared unmodified by both models below, so a bug fix or new instruction
benefits both.
Also here: Decoder (instruction word -> Opcode), MemCore (flat,
byte-addressed functional memory, used directly by cpp_model and wrapped
with timing by sitar_model's MainMemory), FloatingPointFunctions.h
(IEEE-754 single/double/quad-precision arithmetic, quad via
libquadmath), and CoreLogger (SparcCore::logger, which formats and,
depending on how a driver configures it, emits this core's state as a
trace of architectural events). Both drivers below produce the identical
trace format, viewable in the log viewer.
cpp_model/: the 0-delay functional driver¶
SparcStateMachine drives SparcCore through an ordinary
fetch-decode-execute-trap loop, with zero modeled latency. Every
instruction "completes" in the same iteration it starts, and that
iteration, one complete instruction execution, is counted and reported
as 1 "cycle" by sparc_sim_cpp (see --max-cycles/its halt message).
This is purely an iteration count, with no notion of how long a real
instruction or memory access would actually take, unlike the Sitar model
below where a cycle is an actual elapsed clock cycle. No Sitar dependency
at all. This is the fast, simple reference to check ISA-level correctness
against. It's what validation/run_tests.py uses by default.
sitar_model/: the cycle-timed driver¶
The same SparcCore, driven through Sitar with a simple, non-pipelined
cycle-level timing model. Structurally:
behavior
[
run sparcThread;
||
run mainMemory;
||
wait until (sparcThread.HALT.VALUE);
wait until (this_phase==1);
$
log<<endl<<"Core halted.";
log<<endl<<sparcThread.printInfo();
$;
stop simulation;
]
end behavior
This is Core.sitar's top-level behavior: sparcThread (the state
machine, which owns SparcCore plus five memory-interface threads for
ifetch/read/write/atomic/flush) and mainMemory (a persistent procedure
owning the actual MemCore storage) run as two branches of one parallel
block, communicating through a shared request/response struct, alongside
a third branch that watches for sparcThread halting (its own procedure
never returns, it loops forever across RESET/EXECUTE/ERROR by
design) and stops the simulation once it does. sparcThread and
mainMemory are procedures on branches of the same parallel block, not
modules connected by ports/nets. Because of that, their handshake can
complete within a single phase, meaning zero added cycles, before either
side's own configured latency is even applied.
Three independent, additive latency knobs, all ordinary int >= 0 values
with no special-casing at zero (see
Performance Modeling for where to set them and
how to observe the effect):
- Opcode latency. Charged by
SparcThreadfor every instruction. MemoryInterface.delay. Per memory-access channel, on the requester side (models interconnect/cache latency).MainMemory.delay. The memory's own service time, shared by all requesters.
Connecting other components¶
Stub
This section is a placeholder. This project models the core only (see
Models). A
worked example of connecting an external component to it, a cache
sitting between SparcThread's memory-interface threads and
MainMemory, is planned for this page. It will demonstrate the
general pattern for building a larger Sitar model around this core.
Until then, the shape of the pattern is already visible in how
MemoryInterface and MainMemory connect today. They share a
MemAccessInterface struct plus a request/response valid-bit
handshake (see model/sitar_model/src/cpp_code/MemAccessInterface.h's
header comment for the exact protocol). An interposed cache would be
another MainMemory-like persistent procedure, itself acting as a
requester to a further MemoryInterface/MainMemory pair behind it.