This all changes when you get to the control logic. Control logic is the opposite of process: it is time dependent. The results, the output of the block, depend on when
the inputs arrive. There are two types of control logic, as far as this discussion is concerned: reactive and non-reactive.
By definition, reactive control logic needs to respond to something. It involves tight interactions between blocks in the design, as in, for example, a request-response type of interaction. When a control block is triggered to do something and needs to come back with a response almost immediately, concurrency is required. The two processes need to run separately so you can model how one reacts to the other.
Now that time is part of the functional spec, it is part of what defines correctness, so it needs to be in the source code, and you need to model it. Therefore, although sequential C++ can be used for non-reactive control logic in processing pipelines—such as arbiters—SystemC is a more natural choice for reactive control because it allows explicit modeling of timing and concurrency. This has the added benefit of allowing designers to model the types of non-deterministic behavior common to reactive control.
Illustrating the usefulness of SystemC for expressing reactive control designs does not require a complex example. The key difference, in this case, between SystemC and untimed C++ is that SystemC allows the testbench to execute as a parallel thread to the DUT, enabling the testbench to react
to any control requests coming from the DUT. The example shown below is a simple averaging filter that averages two values of x and writes the output to y. The average
, or DUT, module provides a static base address and offset to the testbench, which is used to look up the values of x from some location in memory. The reading of x will block
until the testbench provides the data using the address written to addr
. In SystemC this is not an issue since the testbench is running concurrent to the DUT. When the DUT stalls, control is transferred back to the testbench, allowing it to react to the address supplied by the DUT. In sequential C++ the blocking read of x would stall the DUT but not return control to the testbench, leading to a simulation deadlock.