VP Requirements & Abstraction Level
Defining Loosely Timed (LT) vs Approximately Timed (AT) models and gathering requirements for a Virtual Platform.
VP Requirements & Abstraction Level
When architecting a Virtual Platform (VP), the first question an Electronic System Level (ESL) architect must answer is: What is the abstraction level?
If the goal is to boot an operating system (Linux, Android) as fast as possible to develop software early, you need Loosely Timed (LT) models. If the goal is to analyze bus contention, memory bandwidth, and cache hit ratios, you need Approximately Timed (AT) models.
Loosely Timed (LT) vs Approximately Timed (AT)
The IEEE 1666 TLM 2.0 standard explicitly defines these two coding styles.
Loosely Timed (LT)
- Goal: Maximum simulation speed.
- Mechanism: Uses the
b_transportblocking interface. A transaction executes in a single C++ function call. Time is passed as a reference (sc_core::sc_time& delay) and accumulated, utilizing Temporal Decoupling to avoid expensive scheduler context switches. - Use Case: Software development, firmware validation, functional verification.
Approximately Timed (AT)
- Goal: Cycle-approximate performance analysis.
- Mechanism: Uses the
nb_transport_fwandnb_transport_bwnon-blocking interfaces. A single transaction is broken into multiple phases (BEGIN_REQ,END_REQ,BEGIN_RESP,END_RESP) traversing back and forth through the router, accurately modeling pipeline stages and bus arbitration. - Use Case: Architectural exploration, performance bottleneck analysis.
End-to-End LT Initiator Example
In a Doulos Simple Bus compliant VP, we predominantly use LT to boot software. Here is a perfect LT initiator utilizing Temporal Decoupling.
#include <systemc>
#include <tlm>
#include <tlm_utils/simple_initiator_socket.h>
SC_MODULE(LT_CPU_Model) {
tlm_utils::simple_initiator_socket<LT_CPU_Model> socket;
SC_CTOR(LT_CPU_Model) : socket("socket") {
SC_THREAD(execute_instructions);
}
void execute_instructions() {
tlm::tlm_generic_payload trans;
sc_core::sc_time local_delay = sc_core::SC_ZERO_TIME;
uint32_t data = 0;
// Temporal Decoupling: Accumulate time locally without yielding to the SystemC scheduler
for (int i = 0; i < 1000; i++) {
trans.set_command(tlm::TLM_READ_COMMAND);
trans.set_address(0x1000);
trans.set_data_ptr(reinterpret_cast<unsigned char*>(&data));
trans.set_data_length(4);
trans.set_response_status(tlm::TLM_INCOMPLETE_RESPONSE);
socket->b_transport(trans, local_delay);
// Add internal CPU instruction execution latency
local_delay += sc_core::sc_time(10, sc_core::SC_NS);
// Sync with global time only when quantum is exceeded (e.g., 1000 ns)
if (local_delay >= sc_core::sc_time(1000, sc_core::SC_NS)) {
wait(local_delay); // Expensive context switch
local_delay = sc_core::SC_ZERO_TIME;
}
}
}
};
int sc_main(int argc, char* argv[]) {
// Boilerplate for standalone compilation
return 0;
}By explicitly gathering requirements upfront, you avoid the disastrous mistake of writing slow, cycle-accurate models when the software team just needs a fast functional platform.
Comments and Corrections