Chapter 11. Packages, Libraries, and Components

Rosetta provides a collection of capabilities for grouping declarations into composite structures. A package is a parameterized Rosetta construct used to group definitions together into reusable collections. Packages represent containers for various related declarations. A library defines a location for packages by associating a universal resource indicator (URI) with a local name. A component is a collection of definitions that allow inclusion of usage assumptions and verification conditions along with a specification in a single construct. Components allow information, including design rationale, correctness connections, and assumptions for correct usage, to be encapsulated with a facet in a standard manner.

Packages

Semantically, a package is basically a facet with no terms and whose items are all exported. Every Rosetta definition that is not itself a package must be defined within a package. Thus, packages are the basic element of specification organization. They are used to represent entire projects, component libraries, abstract data types, and collections of related functions and data types. While a facet represents a model, a package represents a related collection of models and other items.

As an example, the following package represents an abstract data type for binary trees:

  package tree(T::type)::static  is
    TreeType(T:: type):: type is data
      nil ::empty |
      node(lt::TreeType,v::T,rt::TreeType) ::nonempty;
  end data ;

    size(t::TreeType)::natural  is
     if nil(t) then 0 else 1+size(lt(t))+size(rt(t))  end if;
  end package tree;

This package defines a new constructed type called TreeType and a function size that evaluates to the size of a tree. The package is parameterized over a type, T, allowing the tree to contain any Rosetta type. To use this package, a use clause would include and instantiate the package as follows:

    use tree(integer);

This use clause includes the tree package and instantiates the tree element type with integer. Thus, the package where the inclusion occurs now has access to a new tree type that contains integers and whose size can be determined.

The use clause must appear immediately before the outermost declaration that includes it. Typically, the use clauses associated with a construct appear immediately before its definition. For example:

   use tree(integer);
   facet multiplier
       ...
   end facet multiplier;

includes package elements in the multiplier facet. However, the declarations in tree are not visible beyond the end facet. The rationale behind this notation is that frequently data structures and functions provided by a package will be used in a construct’s parameter list. This is indicated implicitly by including the use clause immediately prior to the construct.

Example 11.1. Defining a Package Providing RTL Components and Using the Package in a Simple Definition

This simple Rosetta specification defines a package containing two basic RTL components. The package is defined in the static domain; however, the two facets defined within are both state_based facets.

  package rtl()::static  is
  facet mux2
     (i0,i1:: input bit; z::output bit;
      c::input bit)::state_based is
   mux1 : z' = if c=0 then i0 else i1 end if;
  end facet mux2;

  facet demux2
    (e::input bit; z0,z1::output bit;
     c::input bit)::state_based is
    demux0: z0' = if c=0    then e  else 0  end if;
    demux1: z1' = if c=1    then e  else 0  end if;
  end facet demux2;
 end package rtl;

To access the package, the use clause specifies the package prior to the construct where it is referenced:

  use rtl();
  facet mux4(i0,i1,i2,i3:: input bit; z:: output bit;
            c0,c1:: input bit)::state_based  is
 begin
   m1: mux2(i0,i1,x1,c0);
   m2: mux2(i2,i3,x2,c0);
   m3: mux2(x1,x2,c1,z);
end facet mux4;

The mux4 specification defines a 4-1 multiplexer structurally from three 2-1 multiplexers defined in the rtl package previously.

Defining Packages

All package definitions have the following format:

    [[  use P ; ]]*
     package P ( [[   parameters]]) :: D is
    [[ export ]]
    [[ declarations ]]
  end package P ;

The package keyword identifies the declaration of a package, while P names the package. The optional parameters is a collection of parameter definitions. These definitions differ in that all parameters to a package are considered design parameters. Typically, such parameters are used to customize characteristics of items provided by the package. The optional export allows visibility control over package declarations. By default, all package declarations are exported (unlike a facet, where no declarations are exported).

Like facets, the domain, D, provides a vocabulary and language for package specification. Most packages are defined in the static domain to provide maximum flexibility in their use. Note that other definitions within the package do not need to use the same domain as a basis. The domain of the including construct must be a subtype of the included package. This requirement assures that all items defined in the included package domain have meaning in the including domain. For example, it is not possible to include a package of continuous_time functions in a discrete_time domain.

Packages differ from facets in that they have no terms, and all defined items are exported by default. The package body appears between its signature and associated end keyword. The begin keyword does not appear because no terms will ever follow it. If an export clause does not appear in a package definition, all declarations are exported by default. Including an export clause results in only those items specified being exported. Using the export all notation is the same as having no export clause.

Separable Definitions

Any package definition may be separated into an interface and a body. Like other structures, the interface specifies the visible portion of the package, while the body adds specification detail. Package interfaces have the following syntax:

   [[ use P ; ]]*
     package interface P [[ parameters ]] :: D [[ with body ]] is
     [[ export ]]
     [[ declarations ]]
  end package interface P ;

Use clauses specified with the package interface extend to the package body when the body is present. The package name, parameter list, domain, and declarations are identical to the unified declaration syntax. Items not appearing in the interface cannot be exported and all items are exported by default.

The special syntactic element, with body, appearing before the is keyword indicates that the interface being defined requires a body. Because packages only provide declarations to other specification units, they do not have associated terms. Thus, an interface that only contains declarations may represent the entire package. If a body should be associated with the interface, the optional with body keyword should be included. If an interface is complete without a body, then the keyword should be omitted.

Package bodies have the following syntax:

    [[  use P ]]*
    package body P is
       [[ declarations ]]
    end package body P;

Like other structure bodies, no new parameters, domain elements, or exported declarations can be added. Any packages used by the body are not visible in the interface. Although an interface can be used without a body, a package body cannot be used without its associated interface.

Using Packages

The format of the use clause is:

    use packages;

where packages is a comma-separated list of instantiated packages.

All exported symbols from packages in packages are visible by default in the scope of the use clause without the use of the name.label notation. If an exported symbol conflicts with symbols defined in scope or by other included packages, the dot notation is used to indicate the appropriate item. Specifically, if two used packages provide the same symbol, the dot notation is used to resolve the actual symbol used. If a symbol is defined in the construct using it or its associated domain, then the local definition overrides any package definition. If the package definition is required, then the dot notation may be used to reference the specific definition.

It is also possible to include multiple copies of the same package with different parameterization. In this case, dereferencing a name requires the parameters of the package used to be included. For example:

     use tree(integer);
     use tree(float);

requires each reference to any symbol in tree to include parameters when dereferencing. Specifically:

   tree(integer) .nil

refers to the integer tree’s nil value, while:

    tree(float).nil

refers to the float tree’s nil value. If package names are sufficient to determine the instance associated with a name, then the parameters are unnecessary when dereferencing.

Example 11.2. Package Inclusion and Results of Name Resolution

The following package and facet declarations exemplify some name resolution properties:

  package example1()::static is
    a,b,c::integer;
  end package example1;

  package example1()::static is
      export c;
    c,d::integer;
  end package example;

  use example1();
  use example2();
  facet exampleImport()::static  is
    a ::bit;
begin
  a == 1;   // local a
  example1.a == 1; // a from example1
  b == 2;   // b from example1
  c == 3;   //  cannot resolve
  example1.c == 4; // c from example1
  example2.c == 5; // c from example2
  d == 6; //  cannot resolve
  example2.d == 7; // d from example2
end facet exampleImport;

The Working Package

All Rosetta items must be declared in a package. However, the package is not always explicitly referenced in the specification model. When Rosetta constructs are defined without explicitly being included in a package, they are included by default in the working package. There is no physical package associated with the working package. Instead, the contents of the working package are located in the current working directory. There is no need to export anything from the working package, as it will always be the outermost defining package.

Libraries

Libraries define virtual locations for packages and other compilation units. A library declaration defines a library name and may associate it with a physical location using a standard universal resource indicator. The library itself is not a location, but rather is a name or alias for a location that is used to reference a collection of compilation units. By using URIs, library locations may be local to the machine or distributed throughout the network.

Library Definition

Libraries are defined in the same manner as other top-level constructs are. The library keyword declares a library name and the optional is clause provides a value for the library:

    library name [[ is string ]] end library name;

The string value provides a location for the library contents and takes the form of a standard URI pointing to the base library. Some examples of library declarations include:

 library design_lib  end library design_lib;

 library local  is
     "file:///usr/local/rosetta/local"
 end library local;

 library rosetta.lang  is
     "http://www.rosetta.com/usr/lib/rosetta/lang"
 end library rosetta.lang;

The library design_lib defines a library that is known to exist, but whose location is not known. The local library defines a library on the local machine located in:

    /usr/local/rosetta/local

Finally, rosetta.lang defines a remote library located on www.rosetta.com in the directory:

    /usr/lib/rosetta/lang

Defining libraries in this manner allows distributed system definitions. This is vital for large systems, where design teams are physically distributed over large distances. Using URIs allows Rosetta to take advantage of standard location definition techniques and access methods.

Referencing Library Elements

Library elements are referenced using the standard dot notation. For example:

   use local.data_types;

Uses the package data_types located in the local library. If a package appears in a use clause without a library qualifier, the working package is assumed with the current location used as the library. For example:

   use data_types;

looks for a package named data_types in the current working directory. This is the equivalent of the definition:

   use working.data_types;

Here, working is not a library, but rather is the name of the package containing data_types.

Predefined Libraries

Several predefined libraries must exist for any Rosetta installation (Table 11.1). The rosetta.lang library provides basic language definition packages. The prelude package contains the Rosetta prelude, defining all constructs defined in the static domain. If the null domain is extended with the prelude package, the static domain results. The unicode package defines characters and operators for standard Unicode manipulation. Finally, the domains package contains definitions for the base Rosetta domain set.

Table 11.1. Built-in Rosetta libraries and associated packages

Library

Package

Contents

rosetta.lang

prelude

Base language constructs

 

unicode

Unicode definitions and functions

 

domains

Base domain definitions

rosetta.lang.reflect

lexical

Lexical constructs

 

abstract_syntax

Abstract syntax constructs

 

semantics

Semantic definitions for abstract syntax elements

 

simplification

Derived form definitions

 

name_expansion

Functions for expanding simple names

The rosetta.lang.reflect library provides reflection operators used to define and manipulate Rosetta language constructs. The lexical and abstract_syntax packages contain definitions for lexical constructs and data structures representing abstract syntax, respectively. The semantics, simplification, and name_expansion packages contain semantic definitions, derived forms, and rules for fully expanding abbreviated names into their full forms.

Components

A component groups a traditional facet declaration with usage assumptions and correctness conditions. Each component specifies assumptions defining usage assumptions, definitions defining the component and playing the same role as facet terms, and implications defining correctness conditions. Each of these elements is specified as a collection of terms identical to a set of facet terms.

To illustrate component definition, the following specification defines a binary search component, augmenting the normal functional specification with usage assumptions and correctness conditions:

    component bin_search(x::input sequence(integer);
                          k:: input integer;
                          z:: output boolean) ::state_based  is
   begin
      assumptions
        a1: ordered(x);
    end assumptions ;
    definitions
      d1: z'=binSearchFn(k,x);
    end definitions ;
    implications
      i1: z'=k in ~(x);
    end implications ;
  end bin_search;

This component specifies that the output in the next state is equal to using the function binSearchFn to search the input sequence. Defined in the definitions section, this requirement is the basic functional specification for the component. At the same time, the component makes the assumption that its input is ordered using a term in the assumptions section. This is an appropriate guard condition for using a binary search function that requires its input to be ordered. Finally, the component implies that the output in the next state be equivalent to determining if the key is included in the elements of the input sequence. This term appearing in the implications section defines a requirement for the binary search component that should be derivable from the system definition. In this way, the definition serves as a correctness condition. It could also serve as the systems’s functional requirement if a more abstract specifiation were desired.

Before this component is used in a specification, implications must be supported by assumptions and definitions to ensure correctness. When this component is used in a system specification, the assumptions must be supported by the usage environment while the definitions and implications define correct behavior. The implications can be treated as definitions when usage assumptions are justified because they depend only on local definitions. In a sense, they annotate the component definition, adding details that could be otherwise derived.

Defining Components

The syntax of component definitions has the following form:

  component C [[  parameters ]]:: D is
     [[  export exports ]]
     [[  declarations ]]
    begin
      assumptions
        [[ assumptions ]]
    end assumptions ;
    definitions
     [[  definitions ]]
    end definitions ;
    implications
     [[  implications ]]
    end implications ;
 end component C;

where C names the component, parameters is a list of parameters, declarations is a set of declarations, exports is an export clause, and D is the domain of the component. Each of these declarations is identical to the declarations in a facet and plays the same role in the component definition. Where a component differs from a facet is term definition in the body of the specification. The assumptions, definitions, and implications are all collections of terms exactly like those appearing in traditional facet definitions.

The assumptions section defines a collection of terms that are assumed to be true in the component definition. They are not asserted, but instead define usage conditions for the component. Whenever the defined component is included in a definition, its assumptions must hold true in the including system or correct behavior cannot be guaranteed. Assumptions play a role very similar to that of preconditions in an axiomatic specification and record usage conditions for the component.

The definitions section plays the same role in a component as it does in a facet. Specifically, the definitions section declares a collection of terms that define the facet model. As such, they are true within the component and define the component’s behavior when used in other systems.

The implications section defines a collection of correctness conditions for the definitions and assumptions. They define terms that must follow from the union of assumptions and definitions and are treated as definition terms by systems using the component. Implications are used when desirable properties should be defined that do not exhibit basic component definitions. Implications are quite useful when performing verification, because they allow verified terms to be included without requiring re-verification.

The following relationship must hold between assumptions, definitions, and implications for a component to be semantically correct:

   assumptionsdefinitionsimplications

Implications define properties that must be supported by definitions and assumptions. Although ideally this relationship is formal proof, any form of support is acceptable. This reflects the nature of systems engineering, where many reasons can be used to justify a design decision or conclusion. Certainly formal proof is acceptable, but Rosetta semantics does not demand this.

When usage assumptions, definitions, and implications cannot support a formal proof, their inclusion in specifications adds clarity by recording design intent. Many correctness conclusions are made through empirical analysis that are not captured in formal proof. In many cases, conclusions are supported simply by the experiences of the designer. When a design is revisited, understanding assumptions and how they are supported proves vital to successful re-engineering and component replacement.

A shorthand notation is provided for defining a component around an existing facet. Assuming that the facet binSearch existed prior to writing the bin_search component, it could be reused in the component definition as follows:

    component bin_search(x::input sequence(integer);
                   k:: input integer;
                   z:: output boolean) ::state_based  is
   begin
   assumptions
        a1: ordered(x);
   end assumptions ;
   definitions = binSearch(x,k,z);
   end definitions ;
   implications
        i1: z'=k  in ran(x);
   end implications ;
 end bin_search;

Rather than list terms in the definitions section, the binSearch facet is used by associating it with definitions using the notation:

    definitions = binSearch(x,k,z);
    end definitions;

The binSearch facet is simply copied into the definitions section without modification. The assumptions and implications sections can be treated similarly.

Separable Definitions

Like facets, component interfaces may be specified separately from their associated bodies. The syntax for such specifications is virtually identical to that for facet specifications. Specifically, the interface defines use clauses, parameters, the domain, and exported declarations. The body defines terms and local declarations that cannot be exported. The syntax for component interface specification is:

     [[  use P; ]]*
     component interface C [[  parameters ]]::D is
       [[  export exports ]]
       [[  declarations ]]
     end component interface C;

Like the facet interface, the component interface parallels the full component specification, except that term specifications are omitted. Use clauses specified over the interface are similarly specified over the body. Thus, all definitions visible in the interface are visible in the body as well.

The component body specification uses the following syntax:

    [[  use P  ; ]]*
    component body C is
       [[  declarations ]]
    begin
      assumptions
        [[  assumptions]]
      end assumptions;
      definitions
        [[  definitions]]
      end definitions;
      implications
       [[  implications]]
      end implications;
   end component body C  ;

Like the facet body specification, the component body specification defines terms over items declared in the component interface. Use clauses specified over the body only do not extend over the interface. Thus, the user need not know about packages associated with the body definition only.

Accessing Component Elements

There are times when it is desirable to reference a declaration or term in a component that is a part of the assumptions or implications definitions. To support this kind of access, the component definition can be thought of as shorthand for the following facet definition:

  facet C [[  parameters ]]:: D is
    [[   declarations ]]
    export implications, assumptions,  exports;

    facet assumptions ::D is
    begin
     [[  assumptions ]]
    end assumptions ;

    facet implications :: D is
    begin
      [[  implications ]]
    end implications ;

  begin
    [[ definitions]]
  end facet C;

Using this equivalence, it is possible to access the assumptions and implications associated with a component by using the notations C.assumptions and C.implications. Because these collections of terms are treated as internal facets, outside references can be treated as facet references. Specifically, their terms, parameters, and declarations can be accessed using meta-functions and they can be manipulated using facet expressions and functions.

Using Components

Components are used exactly like facets in structural designs and in facet composition operations. When a component is instantiated, the instantiation is treated as a facet formed from it’s definitions. Implications and assumptions are treated as local declarations, but can be referenced using the dot notation as described previously. The reason for allowing this is to support definitions that may include properties from the component that are not explicitly defined as axioms. Because implications are supported by proof or other evidence, they can be referenced and used in other definitions.

Example 11.3. Using the binSearch Component to Define a Larger Model

The following Rosetta component uses the binSearch defined earlier and a second component, bubbleSort, to structurally model a simple component that finds an integer in a sequence. The bubbleSort component accepts a sequence f integers, sorts it, and outputs the sorted result. This component makes no assumptions about its input, but asserts that its output will be ordered.

  component bubbleSort(x::input sequence(integer);
                        y::output sequence(integer));
   begin
       assumptions
         true;
       end assumptions;
       definitions
         d1: y'=bubbleSortFn(x);
       end definitions;
       implications
         i1: ordered(y'),
       end implications;
   end component bubbleSort;

When the bubbleSort component is connected in series with a binSearch component, a search component that does not require sorted input results. Further, we can be confident about the connection between components because the implication that the bubbleSort output will be sorted satisfies the assumption that the binSearch input will be sorted.

  component find(x::input sequence(integer); k:: input integer;
                      z:: output boolean)::state_based  is
    a :: sequence(integer);
  begin
    assumptions
      true;
    end assumptions;
    definitions
      sort: bubbleSort(x,a);
      search: binSearch(a,k,z);
    end definitions;
    implications
      i1: z' = k in x;
    end implications;
 end facet find;

The find system can be defined as a facet if the assumptions and implications are left out. However, the use of a component records further information about the resulting system. Whether a component or facet is used is up to the user’s discretion and often depends on the situation.

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

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