Mohit Paul T, NXP Semiconductors Pvt. Ltd.
Availability of hardware for embedded software development is generally delayed as the hardware development cycle takes longer to complete. An integrated setup of an executable specification of the hardware with a software debugger can facilitate the starting of software development even before the hardware is available. This paper describes the guidelines for integration of a hardware modeling environment with debugger to enable the use of hardware models in embedded software development. SystemC is used as the platform for hardware modeling.
The integrated setup caters to two primary use cases – software development and performance analysis. Integrated setup provides platform for software development where, system-level contexts for both hardware (e.g. MMIO registers) as well as software (e.g. variables) are visible from the debugger GUI. Integrated setup can also be used for performance analysis using cycle accurate SystemC models.
This paper brings-out generic guidelines for debugger integration with SystemC which can be used for other such similar activities. The paper describes how to spawn the SystemC simulation kernel from the debugger context to ensure the control of the simulation by the debugger. The synchronization of debugger control operations like run, step, stop, etc with the SystemC simulation is described. Breakpoint management for the debugger in the SystemC simulation is also described as part of the paper. The access and updating of the hardware resources like the processor and peripheral registers, and the memories from the debugger view are addressed in the paper. The paper also specifies the generic interfaces and data structures needed for debugger integration with hardware executable specification.
System-on-chip (SoC) designs are ever increasing in complexity, resulting in higher challenges for architects, hardware designers, application developers, etc. Time-to- Market (TtM) pressure forces the software development to start much earlier than the hardware availability. This is only possible with the use of ESL tools which provide an early prototype of hardware that can be used for the software development much before the availability of hardware.
Hardware models and virtual prototypes provide other benefits like architectural exploration and act as golden reference in RTL verification.
With all the benefits put together and viewed in the context of Time-to-Market (TtM) pressures, virtual prototypes provide a compelling solution to the myriad of design challenges faced today.
Once an executable specification of the hardware is available, the next big challenge is how to enable software development on the executable specification. Software development will require the ability to load program, view and modify the variables, putting breakpoints, etc. The debugging efforts can also be greatly aided by the ability to view and change the processor registers, peripheral registers and the memories.
By integrating the executable specification of the hardware with a suitable debugger with support for program loading and managing program control flow, it will be possible for software development to commence earlier. The paper brings out design guidelines for integration of a SystemC based hardware executable specification with a generic debugger.
SYSTEMC IN GENERAL AS A HARDWARE MODELING LANGUAGE
Early growth of ESL within semiconductor companies resulted in development and use of proprietary solutions. This created barriers for exchange of high-level models across industry. Lack of Industry standard language was keenly felt and SystemC was created to address this gap.
SystemC is C++ based industry standard language used for high level model development. A reference simulator implementing SystemC is available for free from  and enabled the wide adoption of SystemC within industry and academia.
Many commercial solutions that support SystemC are available from various vendors. Generic SystemC IP models are available from independent IP vendors.
Thus there exist an eco-system of tool provides, IP developers, service providers and academic work which makes SystemC language of choice for high-level modeling. Using SystemC, models can be developed at various abstraction levels like Un-timed (UT), Loosely Timed (LT), etc to suite various requirements like software development, architectural exploration etc.
TRADITIONAL EMBEDDED SOFTWARE DEVELOPMENT FLOW
Traditionally, embedded software was developed only after the availability of hardware board. This means, both hardware and software development are sequential and delays the availability of complete system.
This situation can be improved considerably by using the virtual prototype environment (VPE) which enables the early software development before the actual availability of hardware.
Net result is the early availability of the complete system and reduced Time-to-Market.
Figure 1 from  captures the gain in time to market by the usage of virtual prototype as opposed to traditional product development cycle.
Fig. 1 Traditional flow v/s virtual prototype flow TTM gains
EARLY SW DEVELOPMENT USING SYSTEMC MODELS
The embedded software development is typically done within the Integrated-Development-Environment (IDE) of the specific vendor. Embedded software development is facilitated by tools like debuggers which facilitate the development and debugging of embedded software.
In-order to enable the usage of virtual prototype for embedded software development, it is essential that virtual prototypes are integrated with the debugger.
From the embedded software developer usage perspective, virtual prototype with debugger integration provides the identical interface as development with board. Thus, there is no need of additional learning needed to use the virtual prototype.
This paper specifically covers the integration of debugger with virtual prototype and discusses the design choices used to integrate the virtual prototype with debugger.
Figure 2 shows the integrated setup with the debugger interacting with the hardware specification model.
Fig. 2 Integrated setup of Hardware executable specification with Debugger
This section covers the design issues encountered during the integration effort.
A. How to spawn complete SystemC kernel
The debugger is integrated with the processor model in SystemC. Execution of the instructions on the processor model is controlled by the debugger using commands like – ‘Start’, ‘Stop’, ‘SetBreakpoint’ etc.
For example, ‘Start’ command from debugger would mean to begin execution of instructions by the processor model. Similarly ‘Stop’ command would indicate to stop the execution and return the control back to the debugger. SystemC kernel should only be created after debugger invocation and not when the debugger editing section is going on. SystemC simulation kernel should stop after the user issues a ‘Stop’ command from debugger.
SystemC model should be created in such a way that it is usable with debugger and also in standalone mode. For the debugger to be able to control the SystemC simulation, it is necessary that the SystemC kernel is outside the debugger thread. So it is required to spawn SystemC kernel as a separate OS thread which is created when the debug session is started.
This would mean that the whole of the SystemC including the kernel and the different SC_THREADs are all part of one standalone OS thread. Thus controlling this OS thread is sufficient to implement various debugger commands. It may be noted that as a result of this, if any single SC_THREAD is blocked on an OS resource, then the entire SystemC kernel is blocked, ensuring that the timing is maintained.
Figure 3, using UML notation , captures ‘start’ and ‘creation’ mechanisms using sequence diagram when the debugger is invoked. As shown the entry point for the spawned thread is the sc_main() function of the SystemC. Thus the whole of the SystemC kernel will be running within the context of the spawned OS thread. The SystemC kernel and hence the simulation time can be controlled by controlling any SC_THREAD.
Fig. 3 Sequence Diagram for Synchronization between SystemC and Debugger during invocation of debugger
Figure 4 shows the spawning of the SystemC thread from the debug control.
Fig. 4 Spawning of the SystemC thread by the Debugger
The synchronization between SystemC and debugger is explained in the following section.
B. How to synchronize SystemC and debugger
It is required that the debugger is able to stop and start the simulation as per the user control. The debugger must also be able to stop the simulation on hitting breakpoints.
Synchronization points between debugger and the SystemC kernel are
- ‘Start’ command
- ‘Stop’ command
Since the entire SystemC is spawned as a separate OS thread it is possible to use mutex for the OS thread synchronization.
As soon as the SystemC is created as a separate OS thread, it is ensured that this thread is blocked on a mutex. The debug controller takes the mutex at the start, thereby blocking the SystemC kernel. The SystemC OS thread is unblocked once the debugger releases the mutex.
The following section describes the handling of the synchronization points in more detail.
1) Starting the simulation:
After initialization the SystemC OS thread is blocked on a mutex which is held by the debug control. For starting the simulation, the debug control updates a shared conditional variable to state RUN, and releases the mutex. This unblocks the SystemC OS thread, which after examining the state of the shared variable, takes the mutex and starts execution of the instructions. The execution continues as long as the shared conditional access variable’s value is RUN.
The debug controller waits on the mutex which is now held by the SystemC kernel OS thread.
As shown in Figure 5 below, on receiving the go command from the debugger, the mutex is released by the debug control. The SystemC control thread is unblocked by the mutex, which then checks for the state control variable. The state control variable being in the RUN state causes the SystemC simulation to be unblocked and the simulation starts execution.
2) When simulation stopped from debugger:
To stop the simulation from the debugger, the debug controller updates the state of shared conditional variable to STOP and waits for mutex.
The SystemC OS kernel which examines the state of shared condition variable at the boundary of every instruction notices the change in the state of shared variable to STOP and releases the mutex.
The released mutex is taken over by the debug controller. In Figure 5, the ‘stop’ command is issued from the debugger. On the boundary of instruction execution, the SystemC control thread checks for the state variable value. As this variable is set to STOP, the control thread relinquishes the control of the mutex, which in turn is obtained by the debug control.
3) When simulation stopped from SystemC:
Simulation can be stopped from SystemC in case of a breakpoint hit or in case of error by updating the shared conditional variable and releasing the mutex thereby unblocking the debug controller.
In case of error, a data structure is provided which indicates the reason for stop from the SystemC to the debug controller. Detailed handling of breakpoint is described in the following section.
C. Breakpoint Handling
The debug controller provides the address on which the user wants a software breakpoint. This address is passed to the SystemC through a shared global variable. Once the address specified for a software breakpoint is reached (PC equals this address) in SystemC, then the SystemC OS thread updates the shared condition variable and releases the mutex. This unblocks the debug control thread.
For speed optimization, a boolean array with size equal to the code memory size, with each location indicating whether the current address has a breakpoint or not, is used.
D. Stepping N instructions
When the simulation needs to run for N steps, the number of steps to be executed is passed to the SystemC simulation control. The simulation maintains and updates this information internally ensuring that on executing the required number of instructions the control will be returned from SystemC as described in B.3: When simulation stopped from SystemC above.
E. Exiting simulation
When the debugger is disconnected, the debug controller calls the sc_stop() which causes the SystemC to give up control and return from sc_main(). After the return from sc_main, the OS thread is deleted.
To stop the simulation from the debugger when the simulation is running, the debug controller updates the state of shared conditional variable to EXIT. The SystemC OS kernel which examines the state of shared condition variable at the boundary of every instruction notices the change in the state of shared variable to EXIT, releases the mutex and calls sc_stop().
Figure 5 captures ‘start’ and ‘stop’ synchronization mechanisms using sequence diagram.
The prototypes for the interface between the hardware executable specification and the debugger and their descriptions are given below.
Fig. 5 Sequence Diagram for Synchronization between SystemC and Debugger
The resources like registers and memory are accessed using the following functions.
1. UL32 ISS_Read(unsigned int ResID, BYTE *pB, DWORD nAdr, DWORD nMany)
This function is used to read a particular resource identified by ResID. The address and number of bytes to be read are also passed. The read data is updated in the pB register. This function will return a status or error info as the return value.
2. UL32 ISS_Write(unsigned int ResID, BYTE *pB, DWORD nAdr, DWORD nMany)
This function is used to write to a particular resource identified by ResID. The address and number of bytes to be written are also passed. The data is passed in the pB register. This function will return a status or error info as the return value.
3. UL32 ISS_ReadPC(void)
This function can be used to get the current Program Counter (PC) address.
4. void ISS_WritePC (UL32 nPC)
This function can be used to update the Program Counter (PC) address.
5. void ISS_GetRegs(void *pR)
This function can be used to get the complete register set which can be predefined to have the relevant processor registers and important peripheral registers.
6. void ISS_SetRegs(void *pR)
This function can be used to update the complete register set which can be predefined to have the relevant processor registers and important peripheral registers.
The additional functions for breakpoint handling and memory loading are given below.
7. ISS_SetClrBP(bool flag, unsigned int Addr)
This function is used to set or clear a breakpoint at the specified address.
8. ISS_SetNoOfSteps(unsigned int Steps)
This function is used to indicate the number of steps to execute before the simulation control is returned to the debugger.
9. ISS_MemProgram(DWORD nAdr BYTE *pB, DWORD nCnt, DWORD *eAdr)
This function is used to load the program image into the simulation memory.
The data structures used for controlling of simulation is described below.
1. unsigned int CurrRunState
This variable specifies the current state of debugger request for simulation control. The supported values are enumerated below.
The initial value of this variable is set to SYSC_STOP which forces the SystemC simulation to be blocked during start-up.
2. boost::mutex sysc_mutex
This boost mutex is used for synchronization, blocking and notification of the SystemC and debugger environments.
3. boost::condition sysc_run
This boost condition along with the mutex above is used for synchronization, blocking and notification of the SystemC and debugger environments
4. unsigned int SysCStopReason
This variable should be updated by the SystemC simulation to indicate the reason for returning control to the debugger. The valid values for this variable are as follows
The variable is initialized to SYSC_R_INIT.
The integration of a SystemC kernel with third party debuggers can help in early software development leading to reduced time to market. The software development can be started using the SystemC model of the SoC, thus avoiding the delay caused by non-availability of hardware. The debugging effort on the hardware is considerably reduced due to the early availability of the debugger integrated hardware model.
The possibility of seeing the peripheral registers along with the software ‘variables’ improves the efficiency and speed of development and debugging. The development of low level software drivers will particularly be benefited with such a view where the hardware register and software variables are coexisting.
 IEEE 1666 LRM: SystemC Language Referance Manual available at http://www.systemc.org/
 Keil Application note: Application Note 145 Rev. 3 (apnt_145.chm)
 “Virtual Prototyping Environment for Multi-core SoC Hardware and Software Development” by Syed Saif Abrar, Aravinda Thimmapuram; IP 07, Grenoble