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.
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.
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.
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.
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;
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 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.
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.
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
.
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 |
---|---|---|
|
| Base language constructs |
|
| Unicode definitions and functions |
|
| Base domain definitions |
|
| Lexical constructs |
|
| Abstract syntax constructs |
|
| Semantic definitions for abstract syntax elements |
|
| Derived form definitions |
|
| 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.
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.
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:
assumptions ∧definitions ⇒implications
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.
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.
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.
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.
3.133.119.62