Interfacing Hardware and Software Using C++ Class Libraries Dinesh Ramanathan Ray Roth Dept. of Info. & Computer Science CynApps Inc University of California 2520 Mission College Blvd. Irvine, CA 92697 Santa Clara CA 95054
[email protected] [email protected]
Abstract As chip capacity increases and system-on-a-chip becomes more than just a catch phrase, hardware and system design are being driven in new directions. Systems are designed not just as hardware, but as a tightly coupled combination of both hardware and software. C++, extended with class libraries, is emerging as the way to design such complex systems. This paper proposes methods to specify and refine designs from a purely software description at a functional level to a level where the hardware components are encapsulated as objects and their interfaces clearly defined. The entire system functionality is described in C++ using some of the commercially available class libraries like SystemC from Synopsys and Cynlib from CynApps. We propose a methodology where a designer can migrate software functionality to hardware by describing its interface to the software portion of the system. We start with a simple handshaking interface and refine it to more complex functionality. We also show that the software driver for the hardware device is generated as a side-effect of the interface refinement process. We demonstrate our methodology on the design of a fax machine from a purely software description of the system. We refine the design by implementing its decoder functionality in hardware and interfacing it with the software encoder.
1. Introduction In today’s design flow, hardware and system design methods are stretched to solve increasingly larger problems in order to overcome increased complexity. As the size of the design increases, the abstraction at which these designs are represented and implemented becomes important. The Presently with CynApps Inc.
Rajesh Gupta Dept. of Info. & Computer Science University of California Irvine, CA 92697
[email protected]
quality of results and the time to market for the overall system, both hardware and software, are now become the driving metrics of success for a project, replacing the quality of results and the time to market for simple ASICs or chips. A cell phone’s functional description, for instance, consists of roughly one million lines of C code; some portion of that exists as software and some portion of that is reduced down to hardware. The commercial success of a cell phone is determined not by how well the hardware works, instead by how well the overall system works and how quickly the product is brought to market versus competitors. As a result, early exploration and debugging of the overall system has become a critical factor for product success. Early investigation into hardware-software partitioning can lead to a faster time-to-market when it can be used to determine the minimal amount of hardware necessary to produce reasonable speed. Debugging of the overall system must begin before hardware design is complete, which means that the software designers must have a model with which they must be allowed to test and debug their software while the hardware is designed independently. As a result, hardware and software design environments cannot remain separate. Current hardware description languages (HDLs) such as Verilog and VHDL were initially centered around designs at the RT-level. The ability to use higher levels of abstractions in these hardware description languages is limited at best. The ability to perform HW/SW partitioning in a flexible manner is critical to the system’s architectural state space exploration. Since current hardware description environments are different from software description environments, the ability to perform such architectural exploration is hard and convoluted. We believe the correct way to design complex systems is by utilizing general purpose programming languages, extended with hardware description primitives. More specifically, we believe C++ class libraries [7, 8] will become the method of choice for higher level design. This is even more so since we would like to design the system in a unified en-
this step, the hardware components can be refined into RTL with the interface determining how the hardware and the software components communicate with each other. Since the hardware components are also designed using the C++ class libraries a system level simulation can be performed at every stage in the downstream design process. The focus of this work is on functional and structural composition rather than temporal aspects for which a significant body of prior research exists [9, 10, 11, 12, 13]. This paper is organized as follows: Section 2 discusses the problem of taking a purely software model and implementing parts of its functionality in hardware using the C++ class libraries. Section 3 discusses the design of fax machine. We refine a purely software description of the fax machine by implementing its decode component in hardware. Finally, section 4 summarizes the paper.
vironment and then explore the design space to map various pieces of the design into hardware and software. Recently, we have seen two class libraries become available in the market place: Cynlib [4, 5] from CynApps Inc. and SystemC [3, 2] from Synopsys. Both approaches have added concurrency and reactivity to C++ via class library extensions. They have also added a rich set of data types that allow a user to specify fixed point signed and unsigned integers. These libraries also contain a cycle-accurate simulation kernel. All hierarchical C++ classes that have been derived from a library specific base class operate concurrently. These libraries provide the notion of a clock (and other signals) which is used for synchronization among the concurrent classes. These extensions, along with the objectoriented mechanisms built into C++, enable the encapsulation of hardware in C++. Because C++ was created as a general purpose programming language, this extended language is rich in both high level constructs as well as allows low level hardware description. This paper does not intend to compare Cynlib with SystemC: the aim of this paper is to show how describing hardware using C++ classes either in SystemC or Cynlib enables hardware-software codesign. One of the central forces behind moving the design process to C++ is that most system level designs begin their life as either a C or a C++ model. These models are used by system designers to tradeoff various design considerations at a very high level of abstraction [7, 8, 4, 2]. Further, they have to identify portions of the system that need to be implemented in hardware as well as software in order to achieve the specified system performance. After the partitioning decision has been made, the partitioned subsystem specifications are handed over to the hardware and software designers. They then design the hardware subsystems in either a hardware description language (typically Verilog or VHDL) and the software subsystems in C or C++. As a result, the starting specification for the hardware designer and the software designer is a C or C++ function. If the hardware designer uses an HDL for the subsystem design, interfacing the hardware model with the software components becomes a cumbersome task since the HDL typically has different semantics and syntax from the software. Further, changes in the specification cannot be quickly moved between the hardware and the software partition. Also, system level simulation is quite slow since the hardware and the software are in different environments. As a result, this methodology leads to interface mismatches and a slowdown in simulation speed when the hardware descriptions are being combined with the software to verify the complete system. In this paper, we propose methods to specify and refine designs from a purely software description at a functional level to a level where the hardware components are encapsulated as objects with their interfaces clearly defined. After
2. Component Creation One of the most common problems in the design of hardware from a software specification is the creation of a hardware object that implements the functionality of its software counterpart. Typically, system designers model the initial system as a pure functional model using either C or C++. This specification then needs to be partitioned and then refined into hardware and software components. Each component is then implemented with its interface to either software or hardware. In this section, we address the problem of taking a simple sequential program and mapping a part of its functionality in hardware using freely available C++ class libraries. int main() { data_structure data; F1(&data); F2(&data); F3(&data); }
Figure 1. Initial simple system level C program Consider the simple program in Figure 1. It contains a data structure that is operated upon by functions F1, F2 and F3. Now, consider the function F2. If this function is implemented in hardware, F2’s functionality must be encapsulated into a hardware object. This object should provide the interface that enables communication between it and the software implementation of functions F1 and F3. The primary reason for providing such an interface between 2
it. Further, we assume that the first level of interface between the hardware component implementing function F2 is a simple hand-shaking protocol. Figure 2 show the protocol being implemented in Cynlib and in SystemC. It consists of two signals, a request signal and a done signal, that are created as part of hand-shake. Once function F1 has completed, we enable the request signal and tick the clock till the done signal is received from the hardware component indicating that is has finished performing its function (F2) on data.
the hardware and the software is to model the synchronization that is needed initially between function F1 implemented in software and function F2 implemented in hardware and finally between function F2 and function F3. This synchronization is required to provide information about when a particular resource is ready for use, in this case access to the data structure data. Note that in this example, we consider only a sequential implementation of functions F2 and F3. As a result, function F2 must not operate on data before F1 has performed its transformation on it and similarly function F3 must know when hardware component F2 has finished its operation on data.
class hardware : public CynModule { public: hardware(char *h, In m_clk, In m_req, Out done, InST m_data) : CynModule(h) { /* execute function F2 on positive edge of signal clk */ execute_on(&edge, "+", m_clk); }
int main() { Uint request, done; Struct data; hardware *h = new hardware("h", CynClock0, request, done, data); F1(&data); HW INTERFACE request = 1; do { CynTick(); HW INTERFACE DRIVER } while (!done); request = 0; F3(&data); }
};
void edge() { if (m_req) { F2(m_data); done