Chapter 10. Defining Facets

Facet definitions nearly always take one of two forms: (i) item declarations or (ii) direct definitions. Item declarations use the same form as used with other Rosetta items. Specifically, a label, type, and value are used to create an item declaration of the form:

      f ::T is v

where f names the new facet, T is the facet’s type, and v is the facet’s value. This style is used frequently when the value of a facet is calculated from other facets using the facet algebra or when defining a facet variable whose value is unknown.

Like functions, this definition style can be cumbersome and difficult to read in many circumstances. Thus, a special syntax is provided to define facets directly. Called direct definition, this syntax defines a template over facet declaration elements that results in the common facet definition style seen in previous examples. This template takes the following general form:

     facet f(parameters) ::T is
        decls
     begin
        terms
     end facet f;

Using the direct definition style makes defining components simple and readable. The following definition for inc uses this style to define a simple state_based model that increments its input and outputs the result:

       facet inc(x::input integer; z::output integer) ::state_based is
       begin
         update: z' = x+1;
       end facet inc;

Direct Facet Definition

In Chapter 9 we saw several examples of prototypical facet definitions. Each of these definitions uses the most common mechanism for defining facets, the special syntax for direct definition. Like function direct definition, facet direct definition creates a new item and assigns a value to it in one syntactic expression. Here, the item being defined is a facet and the value is a facet value.

The general syntax for a direct facet definition is:

 [[ use P ; ]]*
 facet F [[ [ variables ] ]][[ ( parameters) ]] ::domain is
    [[ [[ export (all | exports) ]]
   declarations ]]
 begin
    [[ terms ]]
 end facet F ;

where F is the facet name, variables is an optional list of universally quantified parameters, parameters is a list of parameter declarations, declarations is an optional set of declarations preceded by an optional export clause defining visibility outside the facet, domain is the modeling domain, and terms is an optional collection of terms. The facet definition is opened by the facet keyword and terminated by end facet and F, where name must be the same in both locations. The facet definition may be preceded by a collection of use clauses that specify packages to be included in the definition. The scope of all declarations, parameters, and imported packages is the region between the facet and end keywords.

Parameters

Facet parameter declarations have the following form:

   names ::[[ kind ]] T

where names is a comma-separated list of one or more parameter names, T is the parameter type, and the optional kind describes a constraint on the parameter. A parameter list is a semicolon-separated list of declarations. The is clause is not allowed in parameter declarations.

The kind qualifier has three built-in values, input, output, and design. The input and output kinds are used to label system inputs and outputs, respectively. The design kind identifies a parameter much like a generic parameter in traditional design languages. The distinction is that input and output parameters are observed and driven by the system during its operation. Design parameters are system settings that do not vary during operation. The specific semantics of each kind varies with the domain in question.

It is possible to define a parameter without a kind. Such parameters are unconstrained and may be used as inputs, outputs, design parameters, or any other system parameter. Such parameters are useful when direction is not meaningful in describing a parameter or the parameter may represent bi-directional flow. Designers are encouraged to use kinds whenever possible to simplify both specification and analysis.

Universally Quantified Parameters

A universally quantified facet parameter declaration has the same syntax as a parameter has, except no kind can be specified. Parameters and items defined as universally quantified parameters differ in that the latter need not be instantiated when the facet is used. Instead, their values can be determined using type inference and unification techniques during facet instantiation and type checking. Consider the following definition of a component that adds two values and outputs the result:

    facet adder
       [a::subtype(number)]
       (x,y::input a; z::output a)::state_based is
    begin
       z' = x+y;
    end facet adder;

In this definition, the type of the input and output parameters is the parameter, a, defined to be a sub type of number. When the facet is instantiated in another facet definition, the type of a must be determined. For example:

      facet intAdder(x,y::integer; z::integer)::state_based is
      begin
         adderc : adder(x,y,z);
      end facet intAdder;

In this declaration, type checking would determine that the value of a must be integer through the unification process. Because integer is in fact a subtype of number, it is a legal instantiation of a. Here, the type associated with a assures that an addition operator exists for the actual value.

Although the following facet looks legal, we cannot establish its correctness:

       facet wordAdder(x,y::word(8); z::word(8))::state_based is
       begin
         adderc : adder(x,y,z);
       end facet wordAdder;

The inference process can determine that the value of a must be word(8). However, word(8) is not a subtype of number and cannot be a value of a. In this situation, the adder can be instantiated only with number types.

When directly instantiated, universally quantified parameters behave like traditional facet parameters. If a specification frequently instantiates a universally quantified parameter, making it a traditional parameter should be considered. A universally quantified variable is needed only when the specifier needs the type inference system to determine a type automatically.

Declarations

Local items are defined within the declaration section along with an optional export clause. Local item declarations follow the format described in Chapter 4. A list of item names is followed by a declaration:

    names ::T [[ is expression ]] ;

Any item type may be defined within the declarative section, including variables and constants, functions, types, packages, and other facets.

Immediately following the domain and preceding the collection of declarations is an optional export clause having the following format:

    export [[ names | all ]]

The export clause defines what internal facet definitions can be seen from outside the facet. The export keyword is followed either by a list of item names or the keyword all. If the export clause appears with the all keyword, then all declarations defined in the facet are visible. This includes declared items and labeled terms. If the export keyword appears with an export list, then only items listed in names are visible. Any item, parameter, or term defined in the specification, not just those in the declarative region, is eligible for export, including term names. If the export clause is omitted, then no items defined within the facet are visible outside the definition.

The export definition is strict, with no mechanism for overriding the export clause when the facet is instantiated. Specifically, if an item is not exported from a facet, there is no mechanism for referencing the item in the including scope. If a need exists to see an item declared in a facet, then it must be explicitly exported or implicitly exported using the all keyword.

To access definitions within a facet, the common “dot” notation is used. Given a facet name and an item name within the facet, the notation:

    facet.item

is used to reference the internal item. Similarly:

     facet0.facet1.facet2.item

refers to an item declared three levels deep within a definition. It is important to note that at each level, the referenced item must be exported. If item is not exported from facet2, then the reference chain breaks down, as the item cannot be referenced.

Domain

The required domain identifier is used to identify the facet’s underlying semantics by specifying units of semantics, model of computation, and vocabulary. The units of semantics define the basic constructs over which computation is defined. Two units of semantics common in Rosetta specifications are state based and signal based. The state-based unit of semantics specifies that each facet using it must define the concepts of state and next state. In contrast, the signal-based unit of semantics requires the specifier to define events and associated values. One way of thinking about the units of semantics is as a domain of discourse for specifications using the domain.

The model of computation uses the units of semantics to define how computation is performed. Domains such as finite_state and continuous_time specialize the state_based units of semantics to define finite, discrete state computation, and infinite, continuous computation. Both domains achieve this by assigning specific properties to the state type and next state functions provided by the state_based unit of semantics domain.

The vocabulary provided by a domain provides definitions for common quantities used in the domain. For example, the state_based domain provides a definition for event(x) that specifies when an item changes value moving from one state to the next. The continuous_time domain defines an item, t, that indicates the current time. Where the units of semantics and model of computation remain anonymous in most specifications, specifiers use definitions provided in the vocabulary extensively.

The domain associated with a facet is also referred to as the facet’s type. Thus, the declaration:

   f ::state_based;

defines a new facet using the state_based domain. Like any other item declaration, we will read this as “f of type state based.” The type associated with a domain is defined as all possible facets written by extending the domain. Thus, any two facets that use the same domain are of the same type. When one domain extends another, that domain is a subtype of the original domain. Because finite_state extends state_based, finite_state is a sub type of state_based.

The domains defined in the extended Rosetta domain set are shown in Figure 10.1. Domains are shown in a semi-lattice structure that is called the domain semi-lattice. The semi-lattice is arranged hierarchically, with arrows representing extensions. For example, the discrete domain is defined by adding definitions, or extending, the state_based domain. Extensions define subtypes, thus discrete is a subtype of state_based and state_based is a supertype of discrete. Relationships are transitive, thus finite_state is also a subtype of state_based. Domains, their uses, and semantics are discussed extensively in Chapter 12.

Domains defined for the base Rosetta language.

Figure 10.1. Domains defined for the base Rosetta language.

Terms

Terms specify facet behavior by either defining a facet’s properties or constituent components. When defining properties directly, terms describe everything from high-level requirements and constraints to fully executable descriptions. When defining components, terms instantiate, rename, and interconnect other facets to form structural definitions. Because terms are declarative, they are not ordered and define a set of properties. Also, terms are not necessarily executable; however, in some restricted cases terms are executable. In the most general case, terms simply define true properties and components.

A facet may contain any number of terms having the following form:

   [[ l : ]] e ;

where l is the term’s name and e is either a boolean valued expression or a facet valued expression. If the term is a boolean property, the label may be omitted. If the term is a facet, the label must be present. Like all Rosetta declarations, term declarations define items. In this case, the item type is either boolean or facet, the item value is defined by e, and the term can be referenced by l.

For example, the following term states that inc(3) is equal to 4:

  l1: inc(3) == 4;

The term label is l1 and the expression is inc(3)==4. The semicolon terminates the term definition; however, it does not indicate any type of sequencing of terms. Term order in a facet is immaterial.

The label and semicolon define the term’s scope. The label opens the scope while the semicolon terminates the labeled expression. Thus, the specification fragment:

   l1: inc(3) == 4;
   l2: 1 in {1,2,3};

defines two terms with labels l1 and l2 and term expressions inc(3)==4 and 1 in {1,2,3}, respectively. It is equivalent to:

   l2: 1 in {1,2,3};
   l1: inc(3) == 4;

because terms are simply boolean declarations. The specification fragment:

   l1: inc(3) == 3;
   l2: 0 in {1,2,3};

is also a statically legal term list even though it is inconsistent. In contrast:

   l1: inc(3) == 4
   l2: 1 in {1,2,3};

is not legal because the first term is missing a terminating semicolon.

Semantically, the semicolon behaves as a conjunction. Terms delineated by semicolons in the body of a specification are simultaneously true and form the set of terms associated with the facet. A facet is consistent if and only if its domain, terms, and declarations, including type conditions, are mutually consistent.

Facet definitions may seem quite similar, but with proper interpretation mean quite different things. The following examples demonstrate this fact by showing how similarly defined terms have different semantics based on the definition domain used by the excluding face.

The following term l1 asserts that x is equal to f(x):

    facet inconsistent::static is
      x::integer;
      f(i::integer)::integer = i+1;
   begin
      l1: x = f(x);
    end facet inconsistent;

The domain for this term is static, referring to Rosetta’s basic mathematical system. There is no concept of state, time, or change in this domain. Thus, x=f(x) is an assertion about x that must always hold. This domain is frequently termed the monotonic domain because change is not defined; f(x) is never equal to x because no integer is equal its successor. Thus, this term is inconsistent and the specification is in error.

In the following facet, the terms and declarations remain the same. However, the domain is changed to state_based. This domain allows for values to change by defining concepts of state and next state:

    facet inconsistent::state_based is
      x::integer;
      f(i::integer)::integer = i+1;
    begin
      l1: x = f(x);
    end facet inconsistent;

Unfortunately, the inconsistency remains because x refers specifically to x in the current state. l1 asserts that x in the current state is equal to f(x) in the current state. The specification is semantically no different than the first.

The following facet fixes the problem by taking advantage of the state_based domain:

  facet consistent::state_based is
     x::integer;
     f(i::integer)::integer = i+1;
  begin
     l1: x' = f(x);
  end facet consistent;

The notation x’ is defined by the state_based domain to refer to x in the next state. Term l1 now asserts that x in the next state is equal to x in the current state plus 1. This is the semantics that x=f(x) has in a traditional imperative language and the semantics we want here.

With some experience, inconsistencies such as those just described are relatively easy to find by hand or with tool assistance. Finding inconsistencies automatically is an exceptionally hard problem with no known solutions. Thus, one must explore specifiations by hand or by guiding tools manually.

The following facet asserts that x at current time plus 5ms is equal to f(x) in the current state:

   facet sample::continuous_time is
     x::integer;
     f(i::integer)::integer = i+1;
   begin
     l1: x@(t+5e-3) = f(x);
   end facet sample;

This specification is quite similar to the previous specification in that the value of x in some future state is equal to f(x). It differs in that the specific state is referenced using the @ operator and it uses the continuous_time domain. The notation x@t refers to the value of x in some state t. Specifically, the term l1 asserts that in the state associated with 5ms in the future, x will have the value associated with f(x) where the argument to f is the value of x in the current state.

The definition of x’ from facet consistent is a shorthand for the definition x@next(s). The relationship between x and x’ used across computer science and engineering is that x refers to the current state while x’ refers to the next state. The definition of x’ using next provides a precise semantics for this common shorthand.

Using the continuous_time domain introduces a different semantics for state. The state_based domain in the previous facet defines state and change of state, but does not assign specific semantics to state. The continuous_time domain goes farther, specifying that state is observed as a continuous value associated with time.

In addition to demonstrating what a domain is, these examples illustrate a fundamental feature of the Rosetta language. Where traditional programming and design languages embed the semantics of the computation model in the language definition, Rosetta exposes it to the user. Furthermore, the specifier may use different underlying computation semantics when developing system models. This capability is a fundamental step toward true system-level modeling.

Example 10.1. Constant Definition Shorthand

The standard definition syntax for constant items is actually a shorthand whose meaning is defined in terms of function declarations and terms. For example, the following definition can be used to define a constant value for pi:

   pi ::real is 3.1416;

The definition is a shorthand form of:

   facet pi-definition::static is
     export all;
     pi::real;
   begin
     pi_def: pi = 3.1416;
   end facet pi-definition;

Constant functions are defined similarly. The definition of inc is:

   inc(x::integer)::integer is x+1;

An alternative, semantically equivalent definition assigns a specific function to a function variable:

    facet inc-definition::static is
      export all;
       inc(x::integer)::integer;
    begin
       incdef: inc =} <* (x::integer)::integer is x + 1  *>;
    end facet inc-definition;

This definition is identical to the standard definition above. However, the shorthand definition of constants and constant functions is more readable and easier for machines to recognize and process. Furthermore, it extends the definition conservatively, assuring consistency of any resulting definition. Whenever possible, the shorthand notation should be used for defining constant values and functions.

The universal quantifier can also be used to provide function definitions. The following example defines a function variable and uses universal quantification to define the function’s behavior:

    facet comprehension::static is
      export inc;
      inc(x::integer)::integer;
    begin
      incdef:forall (x::integer | inc(x) = x + 1);
    end facet comprehension;

The definition states that for every x taken from integer, inc(x)=x+1. This is semantically equivalent to previous definitions and provides significant flexibility. However, it is exceptionally difficult for machine interpreters to determine when functions defined this way are constant.

In addition to the let expression, the facet term definition language provides a let form for sharing declarations across multiple terms. Consider the following definition:

    let x::integer be g(a) in
      l1: f(x,5);
      l2: g(x)
    end let;

Although syntactically similar to the let expression, the term let is semantically quite different. It behaves more like a statement than an expression. When the previous let term is evaluated, the following terms result:

   l1: f(g(a),5);
   l2: g(g(a));

The item declared in the let term is simply replaced by its value in the enclosed terms. Like the let expression, multiple variable declarations and nested let forms are allowed.

The let term used in the terms section defines items local to a collection of terms not visible outside the let’s scope. In the previous example, the item x defined in the let term cannot be referenced in any terms other than l1 and l2. Furthermore, the required presence of the be clause implies that x is a constant and cannot be used to define communication between components. For example, the following definition of a 2-bit adder is not legal:

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

The local definition, ci, is defined in the let term, but no be clause is provided to define a value. If this problem is corrected by providing a value, c1 cannot be used to communicate because its value will be constant.

Formally, the let term has the following syntax when used to define local variables for terms:

   let name0 ::T0 be e0
      [[ ; namek  ::Tk be ek  ]]* in
    [[ term ]]*
  end let;

Note that unlike the let expression, multiple names in a single declaration are not allowed. The semantics of be is identical to is and the be clause must be included in each declaration.

Separable Definitions

Rosetta provides a mechanism for separating the definition of any facet into its interface and body. The interface defines parameters, a domain, and potentially exported declarations. In addition, use clauses associated with the interface define required packages for the specification. The body defines terms and declarations that will not be exported. No new facet parameters, domains, declarations, or use clauses may be defined with in a package body.

As an example, the following definition for a half-adder can be split into an interface and body:

  facet halfAdder
     (x,y::input bit; z,cout::output bit)::state_based is
  begin
     sum: z' = x or y;
     carry: cout' = x and y;
  end facet halfAdder;

The interface of this definition allows the user to see a black box representation of the device. Enough information must be provided to allow a user to include the device in a design without reference to the body. The interface definition provides usage information without reference to the complete specification:

   facet interface halfAdder
     (x,y::input bit; z,cout::output bit)::state_based is
   end facet interface halfAdder;

The domain and parameters are declared, allowing the user to connect the adder to other facet definitions.

The facet body provides terms to complete the facet definition. Only terms are added by the body, making it dependent on the interface declaration for parameter and domain declarations:

     facet halfAdder body is
     begin
        sum: z' = x or y;
        carry: cout' = x and y;
    end facet body halfAdder;

Together, the half-adder interface and body provide the same component as provided by the original facet. The distinction is that specifics of the definition are isolated from the user. Furthermore, the half-adder interface can be defined well before the body, allowing its inclusion in designs without requiring detailed specification.

The syntax of a facet interface declares the facet by specifying its signature and associated use clauses. It borrows heavily from the traditional facet declaration syntax:

    [[ use P ; ]]*
    facet interface F [[ [[ variables ]] ]][[ ( parameters) ]] ::domain is
      [[ [[ export ( all | exports) ]]
        declarations ]]
    end facet interface F ;

The only distinction between the interface definition and a full facet definition is the exclusion of the begin keyword and associated terms. The interface keyword is added to the declaration to assert that only the interface is being defined. Any item visible in the interface, including those resulting from using packages and including domains, is automatically visible in the facet body. There is no need to re-use packages or domains, or redeclare parameters or local definitions.

The syntax of a facet body simply adds terms and other local definitions to the interface:

     [[ use P ; ]]*
     facet body F is
        [[ declarations ]]
     begin
        [[ terms ]]
     end facet body F ;

Definitions of parameters, domains, and exported declarations are excluded from the body definition. However, use clauses can be included to use packages not specified by the interface. Unlike use clauses associated with the interface, use clauses associated with the body do not extend over the interface. This allows the body to locally specify packages that need not be visible to the facet user.

A facet interface does not require the presence of a facet body. If no body is visible when the facet is used, only information from the interface is available for analysis. Effectively, it is like defining a facet with no terms. Because Rosetta is not executable, this situation is perfectly acceptable. Many kinds of static analysis can be performed knowing only information available in the interface.

The declaration of a facet body requires the presence of an associated interface. Because the interface defines items and includes packages used in the body definition, the interface must be present. Only one body can be defined in the scope of any interface. Rosetta has no mechanism for distinguishing between body definitions.

Example 10.2. Using Facet Interfaces to Define a Black Box view of a TDMA Signal Processing Component

Among the most common tasks in systems design is designing block-diagram-level specifications. Virtually every systems design begins with a block diagram describing interactions such as information flow between system components. Using facet interfaces is an excellent way to define such diagrams formally without including too much detail.

     facet interface resampler
       (i::input complex; o::output complex; clk::input bit)::discrete_time is
     end facet interface resampler;

     facet interface carrierRecovery
       (i::input complex; o::output complex; clk::input bit)::discrete_time is
     end facet interface carrierRecovery;

     facet interface decimator
       (i::input complex; o::output complex; clk::input bit)::discrete_time is
     end facet interface decimator;

     facet interface bitSynchronization
       (i::input complex; o::output complex; clk::input bit)::discrete_time is
     facet interface bitSynchronization;

     facet interface errorCorrection
       (i::input complex; o::output complex; clk::input bit)::discrete_time is
     end facet interface errorCorrection;

     facet interface messageProcessor
       (i::input complex; o::output complex; clk::input bit)::discrete_time is
     end facet interface messageProcessor;

The first collection of specifications defines the interfaces for each processor component. The types of the interface ports are defined, and the facets are named and given domains. However, specification details are omitted.

    facet TDMAstruct
      (i::input complex; o::output complex; clk::input bit)::discrete_time is
        export power;
        power ::real;
        ec2mp,bs2ec,d2bs,cr2d,r2cr ::complex;
    begin
      c1: resampler(i,r2cr,clk);
      c2: carrierRecovery(r2cr,cr2d,clk);
      c3: decimator(cr2d,d2bs,clk);
      c4: bitSynchronization(d2bs,bs2ec,clk);
      c5: errorCorrection(bs2ec,ec2mp,clk);
      c6: messageProcessor(ec2mp,o,clk);
    end facet TDMAstruct;

The TDMAstruct facet defines interconnections between the previously defined facet interface definitions. Information flow between components is defined without committing to particular specifications.

Example 10.3. Using a Facet Body to Define a Specification for a TDMA Signal Processing Component

Details for elements of the structural TDMA definition can be added by associating a body with any of the facets comprising the specification. The following example is a trivial Rosetta facet body describing the decimator component:

      facet body decimator is
      begin
        o' = if event(clk)
                then decimate(i)
                else o
             end if;
      end facet body decimator;

The decimator body is immediately associated with the decimator interface defined previously. The specification body can be changed and further refined without impact to the interface specification. As the design is refined, the specification can be analyzed to determine if refinements violate system-level specifications.

Facets and Hardware Description Languages

Rosetta terms are unordered and declarative, meaning that there is no notion of execution or execution order. Although this concept is strange to many designers, its embodiment in traditional hardware description languages is quite comfortable. Consider the following signal assignments from VHDL:

         s <= s+1 after 5ns;
         r <= r+1 after 4ns;

Although these terms are evaluated in order, they affect their associated signals in the order specified by their associated timing delays. The value of s in the simulation is updated after the value of r as specified by their associated delays. Interestingly, reversing the evaluation order has no affect on the signals. Specifically:

         r <= r+1 after 4ns;
         s <= s+1 after 5ns;

results in the same effect on the modified signals.

The equivalent Rosetta specification has the following form:

        s_update: s@t+5e-6 = s+1;
        r_update: r@t+4e-6 = r+1;

The distinction between these definitions is in the mechanism used to specify the next state. In the VHDL model, a single time model is used implicitly. In Rosetta, the timing model can be exposed and utilized. The code fragment above simply makes the time value and its manipulation explicit. Like VHDL, this term pair has the same effect regardless of ordering because of the explicit definition of the next state.

Facet terms can be used to define structural representation of components by including and instantiating other facets. Assuming that adderl defines a full adder, terms may be used structurally to define a 2-bit adder as follows:

         bit0: adder1(x0,y0,0,z0,c);
         bit1: adder1(x1,y1,c,z1,co);

In this definition, bit0 and bit1 are names for two adder1 facets included in the facet definition. In this case, facet parameters are instantiated to provide external communication through external ports x0 and z0 or communication between devices within the component through shared parameters such as c. The 2-bit adder will be explored extensively in the discussion of structural definition that follows. For this discussion, it is enough to understand the basic concept of structural definition.

Unlike traditional hardware specification languages, single models may exhibit both structural and behavioral characteristics. It is possible to mix terms defined by boolean expressions and terms defined by facet expressions. Remember that both sets of terms must be simultaneously true. This capability unique to Rosetta allows users to define both the structure and the properties of a system in the same facet. It will become clear shortly that although powerful, this technique is not always appropriate. This is particularly true when properties are defined in distinct domains such as design constraints and functional definitions.

Facet Styles

When a facet contains only boolean-valued terms, the facet is defined by defining properties directly. Such definitions are referred to as property-based specifications and correspond to behavioral modeling in traditional specification languages. When a facet contains only facet-valued terms, the facet is defining components and interconnections. Such definitions are referred to as structural specifications and correspond to architecture or structural specification in traditional specification languages. When the facet contains both boolean-valued and facet-valued terms, the definition is referred to as a mixed definition, where both structural and property based definitions apply. Because Rosetta is not interpreted, asserting properties mixed with structural specifications does not present the same kinds of issues as presented by operational specification languages.

Property-Based Facets

When facets contain terms that are labeled boolean expressions, the terms are simply assertions of properties over items. For a facet to be consistent, the terms defined in the facet and the terms defined in its domain must be mutually consistent. Specifically, false should never be derivable from a term collection.

Consider the term defining a relational property over an item:

    t1: x =< 5.0;

This definition simply asserts that the value of x is less than or equal to 5.0. It is not conditional nor does it represent an assignment or executable statement. It simply states that the value of x is less than or equal to 5.0 in the context of the definition. In the facet definition:

     facet f(x::input real, z::output boolean,
             d::real design)::state_based is
     begin
       t1: x =< 5.0;
     end facet f;

the term states that the input parameter x must be less than or equal to 5.0, effectively defining a usage assumption over x. If an input greater than 5.0 appears on x, this term cannot be true. Thus, the composition of this facet with any facet that causes a value greater than 5 to appear on input x is inconsistent. Even when both original facets are consistent, the composition may be inconsistent.

A similar term uses equality to constrain the value of x in a different manner. The term:

     t2: x = 5.0;

states that x is equal to the value 5.0. This is not an assignment statement, but simply asserts that x is mathematically equal to 5.0. Any where in the context of this definition, x can be replaced by the value 5.0. Using this definition, the following pair of terms is inconsistent:

     t2: x = 5.0;
     bad: x = 6.0;

To determine why this pair of terms is inconsistent simply recall that (i) terms are unordered in a facet and (ii) equals asserts equality and is not an assignment. Borrowing from traditional programming languages, one might assert that these two statements assign 5.0 and 6.0 to x, resulting in a final value of 6.0. However, these two terms state that x is equal to 5.0 and 6.0, respectively. Simple substitution then generates the equality 5.0=6.0, which is known to be false. Thus, the term pair is inconsistent and cannot be used in a facet definition.

A similar problem is introduced with the term:

    bad: x = x+1;

This common programming structure assigns the value x+1 to the variable x following its execution. However, in Rosetta, equals is not assignment but an assertion that two values are the same. In this case the term states that the value of x is equivalent to x+1, a provably false statement. Unlike traditional programming and hardware description languages, Rosetta forces the designer to indicate when an equivalence must hold. The previous bad term interpreted in languages such as C implicitly indicates that x is incremented in the next state. Rosetta simply requires that specifiers explicitly indicate when symbols are interpreted as being in the current or next state. The “tick” decoration used earlier is a shorthand notation for “in the next state.” Thus, the new and correct term can be expressed:

   good: x'= x+1;

which is interpreted as “x in the next state is equal to x in the current state plus 1.” Using this simple notation, Rosetta terms can be used to define relationships between the current and next state within a facet. This extremely powerful capability allows consistent and convenient definitions of properties within a facet. Whether working in discrete, continuous, or spatial domains, the concepts of the current and next state are powerful and meaningful in the specification.

Structural Facets

Structural definition in Rosetta occurs when all terms are facets. In the following definition, two adder components are composed within a facet to create a 2-bit adder:

    facet adder2(x0,x1,y0,y1,ci::input bit,
                 z0,z1,co::output bit)::static is
      c::bit;
    begin
      b0: adder1(x0,y0,ci,z0,c);
      b1: adder1(x1,y1,c,z1,co);
    end facet adder2;

In this definition, b0 and b1 relabel adder1 facets that perform addition on two bits. The first adder’s data inputs are instantiated with facet parameters x0 and y0 while its output is instantiated with facet parameter z0. Its carry-in value is instantiated with facet parameter ci and its carry out is instantiated with the internal variable c. The effect is defining interconnections between the internal components and the enclosing facet’s interface.

The two terms include and rename two copies of the adder1 facet that functionally represents the behavior of a 1-bit adder. The term:

    b0:adder1(x0,y0,ci,z0,c);

creates a copy of the adder1 facet, renames it b0, and instantiates it with system parameters and an internal variable. This process is called relabeling and allows the inclusion of the same facet by giving each copy its own name. In this example, two 1-bit adders are included in the definition, but each adder is distinct, as it is a renamed copy of the original definition. Using the previously defined notation, adder2.b1 refers to the adder named b1 inside adder2. Because the 1-bit adder is renamed, the notation adder2.adder1 is not defined.

Figure 10.2 graphically shows the structure of the 2-bit adder defined in the previous example. Parameters from the adder2 facet interface are used to instantiate parameters of the two 1-bit adders. This instantiation causes the 1-bit adders to share symbols with the interface. Thus, when x0 is instantiated when adder2 is used in a definition, the parameter in b0 is instantiated with the same item. Thus, inputs to b0 and b1 change when their associated parameters in the adder2 interface change. Likewise, when the outputs of b0 and b1 change, adder2 outputs instantiated with the same item also change. In this way, facet input and output values are communicated to internal facet interface parameters.

A structurally defined 2-bit adder.

Figure 10.2. A structurally defined 2-bit adder.

The internal variable, c, is used to facilitate communication of a carry value from b0 to b1. It works in a manner similar to that of interface parameters, in that when one facet constrains the port instantiated with c, the constraint information is immediately available to the other facet. Specifically, when b0 sets the carry value, b1 sees the result because it shares a the item, c.

Mixed Facets

As the name implies, mixed definitions contain both structural and property-based terms. Where structural and behavioral definition with the same block in VHDL is illegal, Rosetta takes an approach closer to Verilog, allowing structural and behavioral constructs in the same specicification. Combining structural and property-based specification within the same facet in Rosetta is encouraged and useful for defining properties of facet assemblies. Specifically, the properties specified by boolean terms are simply assertions over the same items as used by the structural definition. Thus, the property-based assertions can be thought of as defining conditions that must hold over the assembly of facets. Consider the definition of a power constraint over a component assembly using a mixed definition:

       facet adder2_const::static is
         export p;
         p ::real;
       begin
         b0: adder1_const;
         b1: adder1_const;
         c: p = b0.p+b1.p;
       end facet adder2_const;

In this facet definition, two instances of the adder1_const facet are included and renamed to represent power constraints defined for the two 1-bit adders. The structural terms b0 and b1 structurally define the power model of the 2-bit adder. The boolean term, c, defines the power of the structural component as the sum of the component powers.

Scoping Rules

Rosetta is a statically scoped language, thus the scope of a definition and the item referred to by an item reference can be determined by the syntax of a specification. To understand scoping rules, one must understand the four sources of definition information. The local scope, domain scope, including scope, and use clauses all provide declarations that can be referenced in a facet, package, or component definition.

Defining the symbols available in a facet’s scope begins with the domain. Each facet extends its domain with new declarations and terms. Thus, the declarations and terms defined in a domain are there when the facet definition begins. For example, the state_based domain defines a collection of items that are common to all stateful systems:

     domain state_based::static is
       state ::type;
       s ::state;
       next(s::state)::state;
     begin
        ...
     end domain state_based;

Any facet defined using the state_based domain starts with definitions of the state type (state), the current state (s), and the next state function (next). They are part of the local scope and can be referenced within any state_based facet. Thus, the following definition represents a correct model:

     facet incrementer
       (vi::input integer; vo::output integer)::state_based is
     begin
       vo@next(s) = vi+1;
     end facet incrementer;

Although s and next are not defined directly in the facet, the declarations are included from the state_based domain.

The inclusion of domains in this manner provides a primitive kind of inheritance among domains and facets. The state_based domain defined here has an associated domain, in this case the static domain. This implies that any declarations in the static domain are also included in the state_based domain and any facet that uses the state_based domain. The discrete and continuous domains inherit from the state_based domain and thus include definitions of state, s, and next. As the domain collection is formed in this way, the type associated with each domain forms a subtype of its domain. For example, the state_based domain type is a subtype of the static domain because the static domain is its domain.

The next addition to symbols in the facet’s scope is the local declarative region. This includes the universally quantified parameter list, parameter list, and local declarations between the is and begin keywords. Any declarations made in this region are visible throughout the facet definition. A second declaration of incrementer uses a local function to implement the increment action:

      facet incrementer
        (vi::input integer; vo::output integer)::state_based is
        inc(x::integer)::integer is x+1;
      begin
        vo@next(s) = inc(vi);
      end facet incrementer;

Within the facet term, the inc function is visible and is used to define the increment property. Also, items from the parameter list declaration are used in the term to reference elements of the facet interface.

A common mistake with writing facets is attempting to redefine items declared in the facet’s domain. Items in the domain are treated as part of the facet and any attempt to redeclare the item in the local scope is treated as a second, illegal definition. For example, in the previous facet definition, if s and next are defined locally, an error results because the new declarations are repeated declarations in the same scope:

   facet incrementer
     (vi::input integer; vo::output integer)::state_based is
     inc(x::integer)::integer is x+1;
     s ::state;
     next(s::state)::state;
   begin
     vo@next(s) = inc(vi);
   end facet incrementer;

The local definitions of s and next conflict with the definitions from the state_based domain, resulting in a redeclaration error. It is still possible to constrain items defined in the domain using terms. The type state and the function next are not interpretable in the current definition. They can be given values using terms; however, this practice is discouraged in favor of using a more concrete domain definition. More will be said about this in Chapter 12.

As seen earlier in this chapter, the use clause provides a mechanism for including definitions from packages. When a package is referenced in a use clause, all declarations from the package are included in the succeeding declaration, qualified with the package name. The definition of the increment function from the previous example can be moved into a package and included using a use clause as follows:

    package inc_package is
      inc(x::integer)::integer is x+1;
    end package inc_package;

    use inc_package;
    facet incrementer

      (vi::input integer; vo::output integer)::state_based is
    begin
     vo@next(s) = inc_package.inc(vi);
    end facet incrementer;

In this definition, the use clause includes the definition of inc in the facet definition. The quantified name, inc_package.inc, specifies the definition of inc from the included package. Because there is only one definition of inc in the facet scope, the qualified name may be omitted and the simple name inc used instead.

If a local definition of inc appears in the facet definition, the local definition hides the package definition unless the qualified name is used. This example defines a local version of increment in addition to the definition from inc_package:

  use inc_package;
  facet incrementer
    (vi:: input integer; vo:: output integer)::state_based  is
    inc(x::integer)::integer  is x+2;
  begin
    vo@next(s) = inc(vi);
  end facet incrementer;

Because the term uses an unqualified name, the local definition of inc is used and the facet model increments its input by 2 in each state. The definition from inc_package is still present and may be referenced using its qualified name:

  use inc_package;
  facet incrementer
    (vi:: input integer; vo:: output integer)::state_based  is
    inc(x::integer)::integer  is x+2;
  begin
    vo@next(s) = inc_package.inc(vi);
  end facet incrementer;

Note that if two or more used packages provide a declaration for the same item, the qualified name must always be used unless a local definition is present. If a local definition is present, then the unqualified name always refers to it.

An interesting example makes the local definition equivalent to the packaged definition:

  use inc_package;
  facet incrementer
    (vi:: input integer; vo:: output integer)::state_based  is
    inc::<*(x::integer)::integer*>  is inc_package.inc;
  begin
    vo@next(s) = inc_package(vi);
  end facet incrementer;

Here the value of the local definition is defined to be the value from the packaged definition. Now the local, unqualified definition refers specifically to the definition from inc_package.

It should be noted that the use clause appears before the facet declaration. The reason for this is to make clear that declarations from a used package can be referenced in the parameter list and universally quantified parameter list. This is particularly useful when packages contain types that may be used parameter declarations.

The final source of item declarations in a facet is the scope in which the facet is defined. Declarations in the including scope may be referenced in the internal facet’s definitions. For example, if the incrementer facet is defined locally within another facet, definitions from the outer facet are visible:

    facet outer::state_based is
      inc(x::integer)::integer is x+2;

    use inc_package;
    facet incrementer
      (vi:: input integer; vo:: output integer)::state_based  is

    begin
      vo@next(s) = inc(vi);
    end facet incrementer;

    begin
      ...
    end facet outer;

In this case, the definition of inc comes from the outer facet. If a packaged declaration of the same name is available, then the unqualified name refers to the declaration from the outer context. Thus, the use clause in the previous definition is not referenced in the facet declaration. If a local definition is included, it overrides the definition from the outer context, much like a packaged definition. However, there is no qualified name for the declaration from the outer context and it cannot be referenced in the scope of the new declaration.

Basics of Facet Semantics

To this point, little has been said about the semantics underlying facet definitions. The details of that discussion are beyond the scope of this book. However, a brief discussion of how that semantics is defined is included here. The mapping of a facet to its underlying co-algebra is presented followed by a discussion of type semantics. This section may be safely skipped by those simply interested in learning about writing Rosetta specifications.

Facet Semantics

Facet semantics are denoted by mapping terms and item declarations to a co-algebra. Co-algebraic semantics is chosen over the more popular algebraic semantics due to the reactive nature of Rosetta facets. Each facet observes its inputs and defines a response to changes in those inputs, defining a kind of stream transformer. Additionally, the abstract nature of the co-algebra’s state makes defining many different state observations much easier.

A co-algebra is defined by an abstract state, χ, observations on the abstract state, Facet Semantics, and types associated with each observation, Facet Semantics. The signature of a co-algebra defines a collection of typed observations over an abstract state:

Facet Semantics

where Facet Semantics defines a vector of observations, χ is the abstract state, and Facet Semantics is a vector defining observer types.

Denoting a Rosetta facet as a co-algebra maps declared items to observations, item types to observation types, and terms to co-algebra terms defining properties of observations. If the abstract state is viewed as system state, each Rosetta facet defines a different observation of the system.

Given the following facet defining a simple increment operation:

Example 10.4. Denotation of a Simple Rosetta Facet as a Co-algebra Structure

  facet increment(o::output integer; clk:: input bit)::state_based(integer)  is
    inc(x::integer)::integer is x+1;
  begin
    update: if rising(clk) then s' = inc(s) else s' = s end if;
    out: o' = s';
  end facet increment

the corresponding co-algebra signature is:

     <o,o',clk,inc,s,s',next> ::
      χ -> <integer,integer,bit,integer->integer,state,state,state->state>

The co-algebra defines a mapping χ to a vector of values associated with the specified type; o is an integer, inc is a mapping from integer to integer, and so forth. Given the co-algebra signature, terms are defined over signature elements:

   <o,o',clk,inc,s,s',next> ::
      χ -> <integer,integer,bit,integer->integer,state,state,state->state>
      if rising(clk)then s' = inc(s)else s' = s end if ;
      o' = s';

All items, including the facet state and next state function, correspond to observations of the abstract co-algebra state. Thus, the state Item, s, in a facet does not correspond to the abstract state, χ, in the co-algebra. It is a simple observation identical in every way to any other observation. Thus, the system state can be consistently observed in different ways by different facets. A digital system can be observed as having both an analog and digital state by defining facets where s is of type real and bitvector, respectively. Because these are simply observations of the state and not the state itself, the observation types are not inconsistent.

An interaction between domains relates observations in different facets. If the state in any facet is simply an observation of system state, it is possible to define relationships between these observations. In Rosetta, such relationships are defined using interactions and they move information from one domain to another. Because state is defined at the domain level, interactions can also be defined at the domain level, supporting general interaction styles.

Many specification languages sharing the style used by Rosetta define their semantics algebraically. The denotation of such languages is quite similar to Rosetta mapping declarations in the language to structures in the semantic system. However, algebraic specifications use concrete type for state representation unless a hidden algebra is defined. When using algebraic semantics, the state has a concrete type, making definition of multiple observation types more difficult. The chosen concrete state type will always be biased to one particular state representation. The co-algebraic semantics only defines observations of state, not a concrete state type, avoiding bias caused by a concrete state type.

Facets and Type Semantics

All facets must well-typed, implying that the facet must be properly structured and each declaration must be well-typed. Examining the facet’s structure reveals the kinds of checks performed during static analysis. The counter facet defined in Figure 10.3 exemplifies the kind of static analysis necessary to determine if a facet is well-typed.

Example 10.3. Specification of 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;

All declarations in counter must themselves be well-typed, starting with the internal declaration of inc_mod4 and rising. Each declaration defines any function that can be checked using rules defined in Chapter 6. The expression associated with each function must be well-typed and have the same type as its specified return type.

Terms are declarations and must also be well-typed. The expressions that define each term must well-typed and must be of type boolean or a facet type. In each case, counter terms define boolean expressions that are in fact well-typed.

Finally, the domain instance must be well typed and be of some domain type. The conditions for a domain instance being well-typed are the same as for a facet being well-typed. Specifically, the type of each actual parameter must be a subtype of its associated formal parameter. In the counter example, the domain used is finite_state, whose only argument is a type, or set of values. Because word(2) defines the set of two-element bitvectors, the domain instance is well-typed.

 

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

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