Chapter 9. Facet Basics

Thus far, our exploration of Rosetta has concentrated on the Rosetta expression language used to define Rosetta items and state mathematical properties over those items. Although the expression language is useful in its own right, it does not provide a mechanism for defining models where multiple properties hold simultaneously over multiple system observers. Nor does it provide mechanisms for modeling how properties change over time.

We look now at the facet language for defining the fundamental Rosetta modeling construct used to define system and component models. A facet is a parameterized, declarative structure that defines the properties of a system. Each facet definition consists of four major elements:(i)adomain,(ii)parameters,(iii)local declarations, and (iv)terms. A facet’sdomain identifies the vocabulary and model of computation used as a basis for the facet model. Facet parameters define a model’s visible interface and provide a mechanism for customization and instantiation. Facet declarations provide local items defining state and local functions. Facet terms declaratively define system properties by defining properties of parameters and local variables. By defining facets, specifiers define system properties from a particular perspective. By combining facets, specifiers define multiple perspectives for a given system.

Facets differ from constructs in traditional modeling and programming languages in that they use declarative constructs, and the underlying model of computation varies from model to model. Rather than defining a program or executable model exhibiting system properties, facets define those properties directly. This allows Rosetta to be far more expressive and general as compared to an executable specification language or programming language. Abstract properties are easily defined without overspecifying implementation details. Incomplete specifications are allowed and can be refined during the design process. Rather than infer properties by observing system execution, properties are defined directly.

The domain identified by a facet specifies its vocabulary and computational model. Where a state-based model may be appropriate for one facet, a continuous time or frequency domain model may be appropriate for another. Rather than force a single modeling semantics on every facet, Rosetta allows the user to choose vocabulary and semantics for each model individually. Later we will see how these heterogeneous models are composed to define complete systems.

A First Model—An AM Modulator

The facet am_mod constructed in this section defines a simple model for an AM modulator that exhibits the basic features of a facet model. The AM modulator is a continuous time model that outputs an input baseband signal multiplied by a sinusoidal carrier whose amplitude and frequency are specified as input parameters. This is a classical model of AM modulation, typically expressed mathematically as:

   s(t) = f(t) cos(ωct)

where s(t) is the output signal, f(t) is the signal, and the cos term is the carrier.

A facet embodying a modulator that implements a system exhibiting these properties must define a signature, domain, and terms. The signature defines the parameters and local items needed to define the system and consists of a facet name, parameters, and declarations. The domain identifies the underlying computation model used to describe the system. Terms define properties over the items defined in the signature and domain. Defining a facet thus becomes instantiating a standard syntactic template identifying its components:

 facet F([[ parameters ]] ) ::domain is
   [[ declarations ]]
 begin
   [[ terms ]]
 end facet F ;

Defining a facet begins with its interface. When defining an interface, we must decide what quantities associated with the facet will be visible. Specifically, we must identify inputs, outputs, and design parameters. Inputs and outputs represent quantities input and output through parameters at the facet interface. Design parameters define static parameters used to configure a component. The am_mod component will minimally input a baseband signal, f (t), and output a modulated signal, s(t). To make this model customizable, we will also add a design parameter specifying the carrier frequency. Because the modulator has no memory, no local variables are needed.

The facet’s name, parameters, and declarations together define its signature. The parameter list defines inputs, outputs, and design parameters while the declarative region contains local declarations. Beginning the facet declaration we have:

facet am_mod(f::input real; s::output real;
            w::design real) ::domain is
begin
  [[ terms ]]
end facet am_mod;

We have named our facet am_mod and defined three parameters corresponding to quantities in the earlier equation. Dependent variables become outputs while independent variables become inputs. Each parameter is named with an associated direction and type. Specifically, f is the input signal, s is the output signal, and w is the carrier frequency. All are taken directly from the specification equation. There are no local definitions, thus the declarative region contains no declarations.

The selection of a domain dictates how and when observations of system state are made. As presented thus far, a variable has a label, type, and potential value. When observing a system over time, observable quantities, such as a system’s state and parameters, have a trajectory. As they change, variable values can be traced and sequences of values observed. This is the basis of most simulation and model-checking analysis tools. The domain specifies when observations can be made and how those observations are sequenced. Informally, if a variable’s value is graphed using a Cartesian coordinate system, the variable value forms the Y axis while the domain specifies the type of the X axis. For example, in the discrete_time domain, quantities are observed at discrete time intervals and the X axis is a discrete time line. In contrast, in the continuous_time domain, quantities are observed continuously over time and the X axis is a continuous time line.

In the original equation, the quantities f and s are functions of a variable t. In the signal processing domain, t typically represents continuous time. Specifically, the quantities f(t) and s(t) can be observed with respect to any continuous time value. To model this in the am_mod facet, we select the continuous_time domain. In this domain, each variable is a mapping from the continuous time line to a value. Thus, any parameter or variable can be observed with respect to a particular instant in time by saying v@t, where v is an item name and t is a time value. If v is specified without t, then the current time is assumed. We will explore this concept further in subsequent chapters and define a general structure for defining domains. For now, it is sufficient to understand that the selection of a domain defines the temporal properties of observations. Continuing to define the am_mod facet, the domain can now be specified:

facet am_mod(f::input real; s::output real;
           w::design real) ::continuous_time is
begin
  [[ terms ]]
end facet am_mod;

The remaining task is defining properties over quantities that describe the modulator. The modulation equation gives us precisely the information we need for the definition. By choosing the continuous_time domain, the parameters f and s correspond with the functions f(t) and s(t), respectively. The continuous_time domain provides a variable t that corresponds to the current time value. The single term labeled mixer in the completed facet below defines a single system property specifying a relationship between input and output parameters. Specifically, the output parameter, s, is equal to the input signal, f, times a carrier:

facet am_mod(f::input real; s::;output real;
           w::design real) ::continuous_time is
begin
   mixer: s=f*cos(w*t);
end facet am_mod;

The notation s=f*cos(w*t) specifies that s is equal to f*cos(w*t). The term constrains the value of s by making it equal to a defined quantity. This is not an assignment any more than the equality assertion in the mathematical equation is assignment. The equality operator defines an equivalence relationship between the variable and the quantity.

Other properties can be added to define constraints and additional functionality within the facet. If we would like to specify that the output voltage of the device modeled by the facet should not be greater than 5 volts, we simply add a term indicating this constraint:

facet am_mod(f::input real; s::;output real;
           w::design real) ::continuous_time is
begin
   mixer: s=f*cos(w*t);
   out_limit: abs(s) =< 5;
   in_limit: abs(f) =< 10e-3;
end facet am_mod;

The term out_limit asserts that the absolute value of the output signal, s, must always be between -5 and 5 volts. The term in_limit asserts that the input voltage can never exceed 10 mV. With the addition of these terms, properties defining both behavior and constraint information are present in the specifications.

This facet definition process demonstrates several important characteristics of Rosetta definitions and facets. First, the specification is declarative. Each term defines a property that must hold over a collection of items, not an executable statement. Second, a domain is used to define how the system will be defined and observed. Traditional specification and simulation languages use a single domain or a fixed collection of domains. As we shall see later, Rosetta provides a variety of domains and allows users to define their own. Finally, a system specification parallels the style used in the application domain. The mixer equation is virtually identical to the mathematical formula, making the Rosetta specification easy to read and write. As we learn more about domains, we shall see how they contribute to this Rosetta capability.

Composing Models—Adding Constraints

Other domains define computation differently, providing Rosetta with a heterogeneous definition capability. We can modify the AM modulator definition to separate constraints and functional description into two specifications:

facet am_mod_fn(f::input real; s::output real;
                w::design real)::continuous_time is
begin
   mixer: s=f*cos(w*t);
end facet am_mod;

facet am_mod_const(f::input real; s::output real)::static is

begin
   out_limit : abs(s) =< 5;
   in_limit : abs(s) =< 10e-3;
end facet am_mod;

am_mod(f::input real; s::output real; w::design real) ::static is
     am_mod_fn(f,s,w) *  am_mod_const(f,s);

The am_mod_fn facet is identical to the original facet defining the AM modulator. The am_mod_const facet contains only the constraints specified for the AM modulator in the second mode. In this case, the domain is static, reflecting that the constraints are invariant—specifically, that regardless of any concept of time, the magnitude of the input and output voltages must never exceed 10 mV and 5 V, respectively. The final definition specifies a new version of the am_mod specification that is the product of the AM modulator’s functional specification and associated constraints. The result is a new model that reflects properties specified in both original models. The domain of this model is static, reflecting the fact that this is the only common domain available for this composition.

This example reflects the compositional nature of Rosetta specifications. Functional requirements and constraints are represented in separate facets using separate semantic domains. The facet product defines a new model that must exhibit both the specified functional behavior and operational constraints. The concept of facet product is exceptionally useful when describing heterogeneous system aspects and will be discussed in detail in Chapter 15.

Combinational Circuits—A Simple Adder

We have seen facets define continuous systems and static constraints. State-based specifications, like digital components, are just as easily described using the same techniques and constructs. The following equations define digital equations for a full adder:

         z = xy ⊕ cin
      cout  = (xy + xcin  + ycin)

Using identical techniques, we can define a facet representing the function of the full adder. As before, we start with the standard facet template:

facet F( [[ parameters ]] ) ::domain is
  [[ declarations ]]
begin
  [[ terms ]]
end facet F;

We will call the model adder_fcn to reflect that it specifies the basic function of an adder. From the preceding equations, we identify z and co as outputs, with x, y, and ci as inputs. Instantiating the facet template with this information results in:

facet adder_fcn(x,y,ci::input bit;
                 z,co::output bit) :: domain is
begin
  [[ terms ]]
end facet adder_fcn;

We must now select an underlying computation model and instantiate the domain. The continuous_time model used for the AM modulator can certainly be used here. In exactly the same manner, the preceding equations can be used as terms in the model definition:

facet adder_fcn(x,y,ci::input bit ;
                 z,co::output bit) :: continuous_time is
begin
  sum: z = x xor y xor ci;
  carry: co = (x and y) or (x and ci) or (y and ci);
end facet adder_fcn;

In this definition, values are specified for both the sum and carry values. The mathematical expressions are translated into Rosetta syntax, but the semantics remains the same. The terms state that at any time, the sum and carry outputs are equal to the values specified by their respective terms.

The first specification of the adder’s function is ideal. The relationship between the output parameters and their equations holds constantly for any time. We know that such circuits do not exist—there is always some delay in the circuit. Ideally, we should be able to say that the defined relationships hold between current values of inputs and output values sometime in the future. Because the continuous_time domain makes time explicit, defining such a specification is relatively simple:

facet adder_fcn(x,y,ci::input bit ;
                 z,co::output bit) :: continuous_time is
begin
  sum: z@(t+5e-6) = x xor y xor ci ;
  carry: co@(t+6e-6) = (x and y) or (x and ci) or (y and ci);
end facet adder_fcn;

The difference is the use of the @ operator to specify the value of a variable at some time in the future or past. Understanding the new terms is a simple matter of reading the specification as “z at time t+5e-6 is equal to ...”. What the term says is that at some time 5 microseconds in the future, the value of z is specified by the equation. Note that when the “@” operator is omitted, the current time or state is assumed. So, 5 microseconds in the future, the value of z will be equal to x xor y xor z for all values in the current state. The delay through the circuit differs for the two outputs, but is explicitly specified in the definition.

If desired, we can use a design parameter to allow reuse of the facet specification:

facet adder_fcn(x,y,ci::input bit ;
                 z,co::output bit)
                 delay ::continuous_time is
begin
  sum: z@(t+delay) = x xor y xor ci;
  carry: co@(t+delay) = (x and y) or (x and ci) or (y and ci);
end facet adder_fcn;

When the facet is instantiated, the actual parameter associated with delay specifies the delay time through the circuit.

An alternate definition of the counter uses the state_based domain to allow for non-ideal behavior without choosing a specific time model:

facet adder_fcn(x,y,ci::input bit ;
                 z,co::output bit) ::state_based is
begin
  sum: z' = x xor y xor ci;
  carry: co' = (x and y) or (x and ci) or (y and ci);
end facet adder_fcn;

The delay parameter is gone along with references to actual time values or increments in the specification. The notations z’ and co’ reference values in the next state, but do not indicate how the next state is obtained or how it is observed. The specification simply states that there is a next state, acknowledging that instantaneous change does not occur.

The advantage of this specification is its abstract nature. Time is held abstract, allowing function to be specified before details of time are known. This is precisely how most digital designers define initial system specifications.

Defining State—A 2-bit Counter

Thus far, each defined facet is a stateless mapping of inputs to outputs requiring no representation of internal state. To demonstrate the use of internal state, we will now define requirements for a simple 2-bit counter with a single reset input. The equations for such a device are:

    s′ = (s + 1) mod 4
      o = s

Where s′ is the state, s is the next state, and o is the output.

Again, we start with the same facet template:

facet F( [[ parameters ]] ) ::domain is
  [[ declarations ]]
begin
  [[ terms ]]
end facet F;

We select a name, counter, and define reset and clock inputs, and a value output. We use the parameterized word type to define a 2-bit value:

facet counter(reset::input bit ;
             value::output word(2);
             clk::input bit) ::domain is
  [[ declarations ]]
begin
  [[ terms ]]
end facet counter;

The counter differs from previous definitions. First, it is stateful—the value of the output is dependent on inputs and on the previous output value. Thus, we need to define an internal state for the mode. Second, it is a finite state system—a 2-bit counter has exactly four states. Based on these observations, we can select a domain and define an internal state.

Remember that a domain defines the ordering of computations. Because state and order are intimately tied, the domain and state definition are also dependent. Rosetta provides a special domain called finite_state for finite state systems. This domain takes a single parameter that defines the type of the state. If that type is finite, then the state set is finite. For our counter, the state set is defined by all 2-bit values, precisely the set provided by word(2). This observation allows us to define the facet’s domain:

      facet counter(reset::input bit; value::output word(2);
                  clk::input bit) ::finite_state(word(2)) is
       [[ declarations ]]
       begin
       [[ terms ]]
   end facet counter;

In the same way that continuous_time provided a current time value, the finite_state domain provides a current state value named s and a next state function that maps one state to another, next (s::state)::state. Our task is to provide terms defining these quantities from the earlier equations.

The facet in Figure 9.1 defines the complete counter model. The next_state term defines the next state function for the counter. In the state-based domains, the tick decoration indicates the value of a symbol in the next state. Using this notation, the next_state term declares that s’ is state 00 if reset is high, else it is s mod 4 if the clock is rising. Two local functions are defined to calculate the next state in binary and identify a rising edge. The next state function is a direct application of a case statement defining the next state by extension. The function rising uses the built-in predicate event(x) to detect a change in the value of its parameter and the equivalence x=1 to determine if the parameter is high.

Example 9.1. A 2-bit counter with reset.

    facet counter(reset::input bit ; value::output word(2);
              clk::input bit )::finite_state (word(2)) is
  inc_mod4(x::word(2))::word(2) is
    case x is
      {b"00"} -> b"01" |
      {b"01"} -> b"10" |
      {b"10"} -> b"11" |
      {b"11"} -> b"00"
    end case;

  rising(s::bit )::boolean is event(s) and s=1;

begin
  next_state: s' = if reset=1
                      then b"00"
                      else if rising(clk)
                              then inc_mod4(s)
                              else s
                           end if;
                    end if;
 output: value = s;
end facet counter;

Defining Structure—A 2-bit Adder

In addition to directly defining system behaviors, terms can describe the structure of a system using facet instantiation and renaming. The following definition constructs a 2-bit adder from the full adder defined previously:

    facet adder2_fcn(x0,x1,y0,y1::input bit, co::output bit,
                 z0,z1::output bit)::continuous_time is
  ci ::bit;
begin
  b0: adder_fcn(x0,y0,0,z0,ci);
  b1: adder_fcn(x1,y1,ci,z1,co);
end facet adder2_fcn

The 2-bit adder functional model is a structural model and differs substantially from the monolithic, 1-bit adder model. Instead of defining facet properties directly, the 2-bit adder is defined structurally by instantiating and interconnecting facet models. Two 1-bit adders are used in a notation that is quite similar to structural VHDL. In this case, the terms b0 and b1 are facets representing 1-bit adders rather than boolean values.

The 1-bit adder facets communicate and interact with their environment through parameter instantiation. The internal variable ci defines the internal carry signal that communicates carry out from the first adder to carry in from the second. The facets interact with the external environment by instantiating parameters with system inputs and outputs. The adder is instantiated with the first bit of the 2-bit input while the second adder is instantiated with the second.

It is important to note that the adder models within the 2-bit adder are unique instances of the 1-bit adder. A technique called relabeling is used to create fresh copies of the 1-bit adder component within the 2-bit adder. Reference to the 1-bit adder is maintained to ensure that when the 1-bit adder model changes, the 2-bit adder will change to reflect the new model.

Specification Reuse—Using Packages

The Rosetta use clause allows specifiers to include packaged definitions in a facet. A package is a special facet that contains declarations. The syntax:

        use name;

immediately preceding a facet declaration includes the items exported from name in the scope of the facet. For example, the following facet uses two packages to define a simple instruction interpreter model for a CPU:

use cpu_utils(8);
use clk_utils;
facet instruction_interpreter
   (clk::input bit; datain::input word(8);
    address,dataout::output word(8))::state_based is
  registers ::cpu_utils.regfile;
begin
  if clk_utils.rising(clk)
     then...
     else...
  end if;
end facet instruction_interpreter ;

The use clauses import definitions from cpu_utils and clk_utils, respectively. Within the facet, package instances are included in the declarations and terms section. The term fragment uses the definition of rising to monitor the clock input for a rising edge. The dot notation, clk_utils.rising, is used to make certain the definition from package clk_utils is used. If no other definition of rising is provided in the scope of the term, the qualifier may safely be omitted.

The declaration of registers uses the type cpu_utils.regfile to define a register file for the definition. Note that the use clause including cpu_utils has an actual parameter that specifies the size of the structures defined in the package. In this case, the parameter defines 8-bit devices. Packages are parameterized like facets except that all parameters must be of kind design, implying that they do not change with respect to state or time.

Using package parameters, we can parameterize the word size of the entire instruction interpreter model:

package instruction_interpreter_pkg
   (width::natural)::state_based
use cpu_utils(width);
use clk_utils;
facet instruction_interpreter
   (clk::input bit; datain::input word(width);
    address,dataout::output word(width))::state_based is
  registers ::cpu_utils.regfile;
begin
   if clk_utils.rising(clk)
      then...
      else...
   end if;
end facet instruction_interpreter;

end package instruction_interpreter_pkg;

The package instruction_interpreter_pkg defines a single parameter, width, used to specify the word width of the model. The use clause:

    use instruction_interpreter_pkg (8);

instantiates the package parameter so that we have the same model as the original definition.

Packages, components, and other library constructs are discussed fully in Chapter 11. Numerous standard packages are defined for Rosetta that allow functions ranging from simple mathematical definitions through manipulating Rosetta constructs using reflective operations.

Abstract Specification—Architecture Definition

One area where abstract specification is important is definition of general-purpose architectures. Such architectures are defined using facets with facet-type parameters. This advanced definition demonstrates numerous Rosetta capabilities that will be defined in subsequent chapters. It can safely be skipped if desired. The following facet defines a simple architecture that connects two models in sequence:

facet sequential[Ti,To,Ty::type]
                (f1,f2::design state_based,
                 x::input Ti,
                 z::output To)::state_based is
  y ::Ty;
begin
  c1: f1(x,y);
  c2: f2(y,z);
end facet sequential;

The sequential facet uses a collection of higher-order facet features to define a high-level architecture along with type constraints. Specifically, this is the first facet presented in detail that uses (i) universally quantified parameters, (ii) facets as parameters, and (iii) type parameters.

Most of the interesting work goes on in the facet signature. At first examination, sequential seems to have two parameter lists that differ only in their delimiters—the first using square brackets and the second using parentheses. The first defines universally quantified type parameters while the second defines traditional parameters. The first two parameters in the parameter list, f1 and f2, are defined to be of type state_based, which is also the name of a Rosetta domain. The domain of a facet is also known as its type. Thus, the parameter declaration defines two parameters that must be facets whose domain is state_based.

The facet then uses structural techniques to define a configuration of the two facets passed as parameters. Specifically, f1 consumes system inputs and produces output values that are in turn consumed by f2 and transformed into system outputs. The terms c1 and c2 structurally define the system using the two input facets. Component c1 is instantiated with the system input and component c2 is instantiated with the system output. They communicate via an internal variable, y.

What makes this definition interesting are the types associated with component inputs and outputs as well as the item y that allows components to communicate. When defining a general-purpose architecture, input and output types should not be overspecified. Before facets are assigned to f1 and f2, the types of inputs, outputs, and interconnections are not known. Because the components can exchange literally any item type, our first tendency might be to make their types universal, the supertype of all Rosetta items. However, using the type top provides no type information. Once instantiated, we will not be able to check type properties or safely assert anything about the result.

Universally quantified parameters provide a mechanism for achieving the desired result. These parameters serve as place holders for information that will be specified on inferred at a later time when the facet is instantiated. In sequential, three universally quantified parameters are defined to represent the system input type and output type, and the information exchanged between components. None of these values is known when defining the architecture. When the architecture is instantiated with a facet, such information will be available. For the specification to be consistent, values must be found, for each universally quantified parameter, that satisfy type correctness conditions. Consider the following specification fragments:

facet absolute(x::input integer; y::output natural)::state_based is
begin
  y' = abs(x);
end facet absolute;

facet square_root(x::input integer; y::output integer)::state_based is
begin
  y' = sqrt(x);
end facet square_root;
posroot(x::input integer; y::output integer) ::state_based is
  sequential(absolute,square_root,x,y);

The facets absolute and square_root calculate the absolute value and square roots of their arguments, respectively. The facet posroot uses the sequential architecture to interconnect the two specifications to define a new facet that finds the square root of its input after assuring that the value is positive. Note that in the definition of posroot, only the traditional parameters are instantiated and then only the facet values are instantiated. Universally quantified parameters cannot be instantiated directly—the type-checking system must determine their values automatically.

Knowing the values for f1 and f2, we can now infer values for Ti, To, and Ty. Ti and To are easy because they are constrained in only one place. Ti is the type associated with the sequential facet input used to instantiate the first parameter of f1. Because the type of fi’s first parameter is integer, Ti is also integer. The same logic applies when determining To is also integer.

The interesting case is determining a value for Ty, the type of the interconnection between the facets. Both absolute’s second parameter and square_root’s first parameter provide type constraints on this value. Specifically, y must be both a natural and an integer. A unifying type must be found for Ty that accounts for all outputs from absolute and can be handled by square_root. In this case, integer is the desired type and inferring the type of Ty is trivial.

The process described informally here is known as unification. If no satisfying value can be found for a universally quantified parameter following instantiation, then an error results. Such a case occurs if the first component in the example were to output a character value rather than a natural value. In this case, no type exists for Ty that is compatible with both character and integer other than top. The ability to discover such inconsistencies is vital to system level design, where many parameters may not be known during early, abstract design stages.

 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.124.83