Tech03 01 Oop
Tech03 01 Oop
Neil Vachharajani
David I. August
ABSTRACT
Modeling systems in which users construct models by specifying
the interconnect between concurrently executing components are
a natural fit for hardware modeling. These concurrent-structural
modeling systems allow the specification of the model to parallel
that of the hardware making specifications clear, easy to understand, and straight-forward to build and modify.
Unfortunately, many concurrent-structural modeling systems provide few mechanisms to create flexible-reusable components. Others have built these modeling systems on object-oriented programming (OOP) languages in the hope that the OOP features will allow the creation of useful flexible reusable components for structural modeling. Unfortunately, simply adopting OOP features is
insufficient for this task. By drawing an analogy between OOP
programs and concurrent-structural models, it is possible to understand why. Furthermore, this analogy highlights what would be
needed in a concurrent-structural system to gain the same benefits
seen in OOP systems. This report compares the features necessary
to obtain reuse in concurrent-structural modeling systems by drawing this analogy between object-oriented programming constructs
and concurrent-structural modeling constructs.
1.
INTRODUCTION
2.
CONCURRENT-STRUCTURAL MODELING
3.
The object-oriented programming (OOP) paradigm eases the development of reusable software components by providing the programmer encapsulation, polymorphism, and hierarchy. These techniques make producing a single reusable component easier, allow
easy customization of existing components, and allow the creation
of components which are more powerful since they are more generic.
In this section, the concurrent-structural analogues of OOP techniquess will be identified. The techniques important for reuse identified in this section can serve as the foundation for the design of
a structural specification language that supports component-based
reuse.
3.1
The foundation of the analogy between object-oriented programming and concurrent-structural modeling is the structural analogues
of the object, member variables, and member functions. It is obvious that the analogue of objects are components. Components are
3.1.1
Constructors
3.1.2
Pure Functions
A pure member function, one that does not side-effect internal object state, represents a computation path that takes input arguments
and internal state and computes the functions return value. Objects typically contain many member functions and a client may
call none, some, or all of the functions. If certain object behavior
is not needed, there is no obligation to invoke a particular member
function. Conversely, if a computation is needed multiple times, a
member function may be invoked multiple times even with different input arguments.
Unlike an object, a hardware component does not perform com-
putation on demand. Instead the component is constantly computing all of its outputs concurrently. The component reacts to value
changes on its input ports and responds by altering values on its
output ports. It is logical to compare an objects pure functions
with the computation paths that exist inside of a hardware component model. In this comparison, function arguments are replaced
with values on input ports and return values with values on output
ports.
Notice that there is a loss in flexibility when consuming a components computation in the concurrent-structural domain. First, all
the computation paths of the component are constantly enabled and
thus, there is a responsibility to provide the component with all its
inputs during each clock cycle. Additionally, a particular computation cannot be invoked twice during the same cycle since the values
on the input ports must be fixed for the whole cycle.
To obtain reusability similar to that obtained in object-oriented programming, a concurrent-structural modeling system must remove
these limitations on flexibility. The modeling system needs to introduce concepts analoguous to not invoking a function and to invoking a function multiple times with distinct input arguments.
3.1.3
State-Updating Functions
Many member functions are not pure functions like those described
in the previous section. Instead, these functions alter the internal
object state and thus the sequence in which they are invoked relative
to all other function invocations is significant.
Conversely, in a hardware component, due to the systems concurrency, there typically does not exist a well defined order between
the computations occurring in a single cycle. To avoid race conditions, all state updates are typically synchronized to the rising or
falling edge of a clock signal. Thus, we can compare state-updating
functions to those computation paths whose outputs are sampled at
the rising or falling edge of a clock. This behavior can be encapsulated in a state element component (e.g. a component which represents a buffer or a queue) or can be internal to a more complex
component (e.g. a branch predictor or cache memory).
3.2
Polymorphism
3.2.1
Parametric Polymorphism
Parametric polymorphism allows the definition of a class or function to be abstract with respect to some set of types. The class or
function abstractions (sometimes called templates or generics) can
be instantiated with a set of concrete types to make a concrete class
or function. This polymorphism is particularly useful when defining abstract data type objects such as lists, sets, hash maps, etc. The
behavior of these objects is type neutral and thus allowing the type
stored in the object to be specified parametrically greatly increases
their reusability.
Similarly, in structural modeling, certain components behaviors
are type neutral, and thus parametric polymorphism can greatly increase the reuse these objects will experience. For example, queues,
memories, and routing components are typically type neutral. Forcing reimplementation of the components for various data types is
a significant, unnecessary burden that can be avoided by a structural modeling language that supports parametric polymorphism on
component state, computation, and interfaces.
While increasing the reusability of components, parametric polymorphism may also cause programs to become cluttered with type
instantiations. To avoid this problem, many programming languages
employ type inference to automatically infer the types with which
functions must be instantiated. This convenience, which allows
parametric polymorphism to be used with no additional overhead,
is particularly important in strucutral modeling due to the potentially large number of parametrically polymorphic components.
3.2.2
Function Overloading
A second type of polymorphism used in object-oriented programming languages is function overloading. With parametric polymorphism, a single function definition could be used with arguments of
varying types. While this type of polymorphism is useful in many
cases, it is often necessary to tailor the computation of a function
based on the types of its arguments. Function overloading allows a
single function to be defined with multiple signatures and bodies,
thus allowing custom behavior based on the types of arguments.
In the structural domain, the natural extension of overloading is to
allow a component to define various type signatures for its ports,
and to implement different behaviors based on the types actually
used. Just as in the object-oriented case, this can improve reusability by increasing the components applicability.
3.2.3
Subtyping
The third type of polymorphism available in object-oriented programming languages is subtyping. Subtyping allows a value of one
type to be used where a value of another type is required, provided
that first type is a subtype of the latter. Thus in function invocation,
rather than supplying arguments of the types specified in the function signature, one could alternatively provide data whose types are
subtypes of the arguments types.
The languages subtype relation defines which types are subtypes
of which other types. While some portion of the subtype relation is
fixed by the programming language, the relation can be extended
via subclassing. Subclassing will be discussed in section 3.3.2,
however its relation to subtyping will be discussed here.
In structural modeling, it is natural to allow subtyping when making
connections between components ports. Note, however, that when
leveraging subtyping in this way, the augmented subtype relation
is irrelevant. Components are not being sent along connections between components, and thus, only the language defined subtyping
relation is relevant.
In object-oriented programming, the use of subtyping is not restricted to computation function invocation, but can also be used
when invoking an objects constructor. When an object is passed to
another objects constructor, typically, the receiving object stores
the passed object in a member variable, and the member functions
act as clients of the passed object. The subtype relation guarantees
that the passed object will conform to a specific interface, however,
since the actual type of the argument can vary, the behavior the
passed object provides is polymorphic.
Component C
3
Component B
Component B
Component B
3
6
1
Component A
Component A
Component A
3.3
Component A
2
Component B
Component C
3.3.2
Inheritance
5.
In practice, one-off inheritance is very common in hardware modeling. For example, it is extremely convenient to have a single
component handle arbitration with inheritance used to customize
the arbitration policy. Since arbitration policies are often very specific to a particular situation, the inherited component will probably only be instantiated once and not be useful in other designs.
A similar situation occurs when writing event handlers to process
Java AWT events. In Java, it is common to use anonymous inner
classes which inherit from event adapters to reduce the syntactic
overhead of full-fledged inheritance and to prevent namespace clutter. A structural modeling language needs a similar mechanism to
eliminate any overhead associated with inheritance.
3.4
Aspect-Oriented Programming
Despite the success of the object-oriented programming in producing reusable code, certain kinds of reuse patterns are still hard to
achieve. Specifically, the inheritance mechanism, while allowing a
user to augment the behavior of one object, does little to help the
user augment system-wide behavior or implement behavior which
crosses object boundaries. For example adding logging behavior to
an object-oriented system would typically involve modifying every
object in the system.
Another programming methodology, aspect-oriented programming [1],
alleviates this burden by allowing code designed for a specific purpose (e.g. logging) to be grouped together and have the code be
automatically weaved together with the base program in order to
implement the complete desired behavior.
In hardware modeling, gathering statistics about a simulation is a
cross-cutting concern. It involves almost all the components in the
system and depending on the statistics desired, the code necessary
to gather the data can vary dramatically. Thus, incorporating code
to gather statistics is very similar to inserting code into objects to
handle logging. For a component to be reusable and a user to have
control over the statistics gathered. An aspect-oriented approach to
data collection is necessary and should be supported by a hardware
modeling language.
4.
CONCLUSION
REFERENCES