Engineers assigned to integrate hardware and software early in the design process confront several less-than-optimal alternatives. They can write software models of the hardware in C or C++ to start software testing; they can approximate software behavior in the hardware simulation environment with a bus functional model; or, for single-chip designs that contain a microprocessor and other custom logic, they can use a fully functional model.
But, while running actual software in the hardware simulation environment works well, the price of simulating a full-hardware model of a processor (usually a known-good element) can sometimes be too high to pay. This choice also forces the programmer to debug in an unfamiliar environment using hardware simulation tools like waveform viewers.
Despite these and other drawbacks associated with the three different approaches, designers must start integrating hardware and software before they have silicon. The ideal solution would allow engineers to use existing tools and models with minimal modifications. This would enable developers to work in familiar development and debug environments.
The same software tools could be used for both the simulation environment and the target system, allowing the programmer to have the same view of the hardware, and ensuring a smooth transition from simulation to integration. This approach, which can be done without special modifications to the embedded software for the cosimulation environment, is employed by the Virtual-CPU (V-CPU) environment (see the figure).
Coverification In Use
In the coverification environment, hardware simulation uses existing logic simulation and debug tools. Early simulation may take place in an event-driven simulator using either Verilog or VHDL. Later in the design flow, cycle-based simulation, acceleration, or hardware emulation may provide the hardware execution environment. Using this simulated representation of the hardware, the embedded system software can be run in two modes, host-code or target-code. Host-code mode uses the native workstation tools to compile and debug the software, while target-code mode uses the tools for the target processor.
The embedded system memory map is the underlying control structure that translates software accesses to hardware simulation or software functional models. When hardware models of peripherals are not yet available, or have already been fully tested, software functional models allow for higher performance by handling transactions in the software environment instead of the hardware simulation environment.
The coverification environment is complete when the software and hardware models are interfaced via an application programming interface (API). Host-code software uses the API to initialize the environment and to interface instruction set simulators (ISSs) for target code execution. The hardware simulation interface can be used to translate software instructions from the coverification environment to the design-specific bus functional model (BFM). The BFM then becomes the interface to the hardware simulation.
Each interface has a common transaction model—the set of primitive actions that decide how software interacts with hardware. These are bus-oriented transactions, and include instruction fetches, reads, writes, and interrupt checks. The transactions are broadly defined to allow a wide range of buses to be modeled. They include varying size accesses, cache line reads and writes, and user-defined options.
As an extension of the bus functional model and testbench approach, this methodology uses the same BFMs, and replaces the testbench with embedded system software that generates bus transactions. In addition to the read, write, and interrupt transaction model, a "back-door" interface is provided. This interface allows the user to develop hardware-side tasks to perform functions outside those possible with bus protocols. Typical uses of the back-door interface include loading HDL memories, injecting signal stimulus to the design, and controlling hardware-side debug tracing.
This coverification environment was used to develop device drivers for a design based on the Advanced RISC Machines', Cambridge, U.K. architecture. The ARM7TDMI is a 32-bit RISC microprocessor used for a wide range of applications, including digital cell phones and pagers, set-top boxes, printers, and hard disk drives. These are high-performance and cost-sensitive embedded-control applications with particular emphasis on reduced system power consumption.
Features of the ARM architecture include both the 32-bit ARM instruction set and the 16-bit Thumb instruction set; a three-stage execution pipeline that remains hidden from application software; single-cycle instruction execution for most instructions; two cycle branches and memory access instructions; support for byte, half word, and word accesses; fast and normal interrupt support; on-chip emulation support; and a fully static design for minimizing power consumption.
Integrating the ARM7TDMI with the coverification environment was straightforward. For the core bus, there was an existing bus functional model available, developed by ARM. This model was produced using the same tools used by ARM for all of the fully functional design simulation models (DSMs). The model runs with both Verilog and VHDL simulators, and integrates seamlessly into the logic simulation environment. No additional HDL modules need to be instantiated in the design. And, for some logic simulators, only a simple environment variable change is needed to switch from the fully functional DSM to the bus-functional DSM.
A popular instruction-set simulator was also integrated for running native ARM code. This simulator, called the ARMulator, employs a memory interface that maps address spaces into a C API. Code was developed to interface this API to the V-CPU API. The combination of the ARM bus-functional model, the ARMulator integration, and the V-CPU API provided the programmer with a means of using both host-code and target-code modes for fast development and debugging of diagnostics and drivers.
This methodology was used in a device driver for a serial interface that can be used to connect to peripherals using a small number of signals. The serial interface was part of a highly integrated chip that was still in development. The device driver was developed employing the system specification, and simulated with a coverification environment using the hardware models and the RTL code for the design. This allowed early access to the hardware for the software engineers, and provided real-world stimulus to the hardware design.
Bus Functional Model Used
Preparing the RTL model for co-verification was a matter of replacing the fully functional model of the core processor with the BFM, which has the same interface ports as the fully functional core model, and drove the core bus in a similar fashion. This project used a VHDL simulator that dynamically loads either the fully functional model or the BFM, based on an environment variable. Both simulation models are written in C for optimal performance. This allows users to switch between the two with no recompilation of the HDL design.
After the BFM substitution was made, debugging and maintenance of the RTL simulation environment was the same that of the real hardware models under development. Standard simulators and debugging tools were used, and integration of new hardware was the same as the simulation environment with the fully functional core model.
Before developing software for the embedded system, the programmer's view of the system must be described. That view includes simulation information and a description of the memory map. Below is a sample configuration file that describes the overall environment and one sample memory map item.
main host_name = tiffany port_number = 6500 debug_level = 0 check_level = 1 synch mode = access_done delay_valid = off zero_delay = off typ_delay = 0 min_delay = 0 max_delay = 10 map valid = 1 type = mem start_addr = 0xa0000000 end_addr = 0xa000ffff
The main stanza describes the simulation environment, including where the hardware simulation may be found, and parameters for error checking and printing. The synch stanza describes the synchronization mechanism.
In this example configuration file, access_done indicates free-running synchronization. That means that the hardware and software simulations will proceed independently until interaction (read, write, or interrupt) is necessary between them. The map stanza indicates the address space corresponding to the serial peripheral. For this system configuration, the control and data registers lie between the specified addresses (0xa0000000 to 0xa000ffff). Any software access in this range of memory space "belongs" to the serial peripheral, and will be handled by the hardware simulation environment.
Initial development of the device driver was performed in a host code environment. The device driver was coded using the same techniques as would normally be used for a C-language software. Native compilers are used, and code is debugged with techniques familiar to the programmer, from print statements to full-featured graphical debuggers such as gdb and dbx.
For host-code programs, some environment-specific initialization code is required. This defines the software simulation environment, and starts the connection with the hardware simulator. After this initialization code is executed, V-CPU's implicit access mechanism allows the remainder of the device driver software to be coded in the same fashion as the real hardware. Without this feature, host-code mode would be much more difficult to use (see Listings 1 and 2).
Notice that calls to the hardware are made implicitly by dereferencing pointers. Because these pointers exist in a coverification-mapped space, the access to the hardware is accomplished without the programmer having to specifically instrument the code for the coverification environment.
Interrupt handling in the host-code-mode driver was accomplished by using the InterruptConnect API call to register an interrupt service routine (ISR) with the coverification environment. This call specifies an ISR that will be called without further program interaction when an interrupt is detected by the bus functional model in the hardware simulation.
For target-code execution, the software was cross-compiled for the ARM7 architecture, and was run on the ARMulator instruction-set simulator. Target-code mode allows assembly language programming and simulation of the software at the architectural level, with visibility into the working structures of the microprocessor.
The target code was cross-compiled and linked using the ARM software development toolkit (SDT), and run in the ARMulator. The ARMulator allows a connection to the coverification environment through its memory interface. It also provides source-level debugging, stack backtrace, register display, source-code disassembly, and watch capability. The ARMulator uses a configuration file to specify which memory interface should be used for the simulation.
The following is a section of configuration file used to connect the ARMulator to the coverification environment:
; Comments begin with a ; ;; Default is the flat memory map ; Default = Flat Default = VCPU VCPUconfigfile = dev.cfg
The device driver code developed for target-code mode was largely the same code as used for host-code mode, because only minor modifications were required for the coverification environment. Because target-code mode must be written (or cross-compiled) to the architectural level, additional initialization code is required for the interrupt vector. In addition, the interrupt service routine is slightly different from the host-code equivalent to reflect architectural interrupt handling and return requirements.
See Listing 3 for the example target-code mode interrupt initialization code.
Using a coverification methodology, engineers can accurately verify hardware and software, even though they lack working silicon. This particular approach has proven successful in diagnostic and device driver development projects, including designs using the ARM7TDMI microprocessor.