lh-l4v/camkes/adl-spec/Types_CAMKES.thy

333 lines
13 KiB
Plaintext

(*
* Copyright 2014, NICTA
*
* This software may be distributed and modified according to the terms of
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
* See "LICENSE_GPLv2.txt" for details.
*
* @TAG(NICTA_GPL)
*)
chapter {* \label{sec:types}Types *}
(*<*)
theory Types_CAMKES
imports Main
begin
(*>*)
text {*
This section describes the types of entities that appear in the CAmkES
Interface Definition Language (IDL) and Architecture Description Language
(ADL). The model assumes that all syntactic elements are available in a single
environment; that is, all @{term import} inclusion statements have already
been processed and all references are to be resolved within this context.
*}
subsection {* \label{subsec:symbols}Symbols *}
text {*
CAmkES has two types of symbols that exist in separate namespaces. Symbols of
the first type are used in IDL descriptions to identify method names and
parameters. These symbols are used during code generation and therefore need
to be distinct within a specific interface.
*}
type_synonym idl_symbol = string
text {*
The second type of symbols are used in ADL descriptions to identify components,
connectors and other architecture-level entities. These symbols are also used
during code generation, but are lexically scoped. E.g. Instantiated interfaces
within separate components can have an identical name.
*}
type_synonym adl_symbol = string
text {*
Although both symbols map to the same underlying type, these have different
constraints (e.g. IDL symbols become direct substrings of code-level symbols
and hence need to respect symbol naming restrictions for the target
language(s)).
*}
subsection {* \label{subsec:methods}Methods *}
text {*
Methods are the elements that make up a CAmkES procedure (described below).
Each method within a CAmkES procedure has a list of parameters defined by a
type, direction and symbol name. Each method also has an optional return
value type. The valid types for method parameters and return values include
a set of high level types designed to abstract the types available in a
general programming language. By using only these types in a procedure
description, the interface can be implemented in any valid target language.
In high-level languages, arrays may have attached size information, while in
C this information is passed as an extra parameter to their containing
method. Arrays are parameterised with the underlying type of
their elements. Similar to primitive types, using a high-level type for the
elementary type of an array allows it to be implemented or used in any
component, while using a C-specific type prevents implementing or using it in
a component not written in C. Arrays of arrays and multidimensional arrays are
not supported.
We also support talking about arbitrary C types. These include fixed-width
integers, floats, structs, etc. For these types, our model just passes their
names around and they are expected to behave as simple value types
(without embedded pointers).
*}
datatype number =
\<comment> \<open>High level types\<close>
UnsignedInteger
| Integer
| Real
| Boolean
datatype textual =
\<comment> \<open>High level types\<close>
Character
| String
\<comment> \<open>C-specific types\<close>
| char
datatype primitive =
Numerical number
| Textual textual
datatype array =
SizedArray primitive
| TerminatedArray primitive
datatype param_type =
Primitive primitive
| Array array
| CType string
text {*
Rather than having a single return value per procedure method, each
method parameter can be an input parameter, an output parameter, or both.
*}
datatype param_direction =
InParameter (* also covers 'refin' *)
| OutParameter
| InOutParameter
text {*
Each procedure comprises a collection of methods that each have an
optional return type,
identifier and a list of parameters. Each parameter has a type and an
identifier.
*}
record parameter =
p_type :: param_type
p_direction :: param_direction
p_name :: idl_symbol
record "method" =
m_return_type :: "param_type option"
m_name :: idl_symbol
m_parameters :: "parameter list"
text {*
The translation from procedure methods in IDL to their representation in
Isabelle is straightforward. The CAmkES method
\begin{verbatim}
int foo(in string s);
\end{verbatim}
is translated to the Isabelle representation
*}
(*<*)value(*>*)
"\<lparr>m_return_type = Some (Primitive (Numerical Integer)),
m_name = ''foo'',
m_parameters = [
\<lparr>p_type = Primitive (Textual String),
p_direction = InParameter,
p_name = ''s''\<rparr>
]\<rparr>"
text {*
More examples are given in \autoref{sec:examples}.
*}
subsection {* \label{subsec:interfaces}Interfaces *}
text {*
Connections between two components are made from one interface to another.
CAmkES distinguishes between
interfaces that consist of a list of function calls and interfaces
that have other patterns of interaction.
There are three basic types of supported interfaces. The first, @{text procedure},
is used for modelling traditional caller-callee semantics of interaction. The
second, @{text event}, is used for asynchronous notifications such as interrupts.
Finally, @{text dataport}, is used to model shared memory communication.
*}
type_synonym procedure = "method list"
type_synonym event = nat \<comment> \<open>ID\<close>
type_synonym dataport = "string option" \<comment> \<open>type\<close>
datatype interface =
Procedure procedure
| Event event
| Dataport dataport
subsection {* \label{subsec:connectors}Connectors *}
text {*
Two components are connected via a connector. The type of a connector is an
abstraction of the underlying communication mechanism. Connectors come in three
distinct types, native connectors, hardware connectors and export connectors.
Native connectors map directly to implementation mechanisms. These are the
types of connectors that are found in almost all component platform models. The
event-style connector, @{text AsynchronousEvent}, is
used to model communication consisting of an identifier with no associated message
data.
*}
datatype native_connector_type =
AsynchronousEvent \<comment> \<open>an asynchronous notification\<close>
| RPC \<comment> \<open>a synchronous channel\<close>
| SharedData \<comment> \<open>a shared memory region\<close>
text {*
Recalling that hardware devices are modelled as components in CAmkES, hardware
connectors are used to connect the interface of a device to the interface of a
software component. Devices must be connected using the connector type that
corresponds to the mode of interaction with the device.
*}
datatype hardware_connector_type =
HardwareMMIO \<comment> \<open>memory mapped IO\<close>
| HardwareInterrupt \<comment> \<open>device interrupts\<close>
| HardwareIOPort \<comment> \<open>IA32 IO ports\<close>
text {*
Export connectors are used when
specifying a compound component. A compound component has a set of interfaces
that are a subset of the unconnected interfaces of its constituent components.
The exposed interfaces of the compound component are defined by using export
connectors to map these to the interfaces of the internal components.
Export connectors are purely an architectural-level entity and do not exist at
runtime. During code generation connections through exported interfaces are
flattened. That is, a connection from interface A to exported interface B that
exports interface C is instantiated as a connection from interface A to interface
C would be. They can be thought of as a design-level convenience.
\begin{figure}[h]
\begin{center}
\caption{\label{fig:export}An export connector}
\includegraphics{imgs/composite-passthrough}
\end{center}
\end{figure}
*}
datatype export_connector_type =
ExportAsynchronous
| ExportRPC
| ExportData
datatype connector_type =
Native native_connector_type
| Hardware hardware_connector_type
| Export export_connector_type
text {*
Connectors are distinguished by the mode of interaction they enable. The
reason for this will become clearer in \autoref{subsec:wconnectors}.
*}
datatype connector =
SyncConnector connector_type
| AsyncConnector connector_type
| MemoryConnector connector_type
subsection {* \label{subsec:components}Components *}
text {*
Functional entities in a CAmkES system are represented as components. These
are re-usable collections of source code with explicit descriptions of the
exposed methods of interaction (@{term interfaces} described above).
Components have three distinct modes of communication:
\begin{enumerate}
\item Synchronous communication over procedures. This communication is
analogous to a function call and happens over a channel established from
a @{text requires} interface to a @{text provides} interface.
\item Asynchronous communication using events. This is suitable for things
like interrupts and happens over a channel from an @{text emits} interface to
a @{text consumes} interface.
\item Bidirectional communication via shared memory. This is suitable for
passing large data between components. It happens over a channel between
two @{text dataports}.
\end{enumerate}
*}
record component =
control :: bool
requires :: "(adl_symbol \<times> procedure) list"
provides :: "(adl_symbol \<times> procedure) list"
dataports :: "(adl_symbol \<times> dataport) list"
emits :: "(adl_symbol \<times> event) list"
consumes :: "(adl_symbol \<times> event) list"
attributes :: "(adl_symbol \<times> param_type) list"
subsection {* \label{subsec:assembling}Assembling a System *}
text {*
A complete system is formed by instantiating component types that have been
defined, interconnecting these instances and specifying a system
configuration. Connections are specified by the two interfaces they connect
and the communication mechanism in use.
*}
record connection =
conn_type :: connector
conn_from :: "(adl_symbol \<times> adl_symbol) list"
conn_to :: "(adl_symbol \<times> adl_symbol) list"
text {*
A composition block is used to contain all components of the system and the
connections that define their communication with each other.
*}
record composition =
components :: "(adl_symbol \<times> component) list"
connections :: "(adl_symbol \<times> connection) list"
text {*
Configurations are used as a way of adding extra information to a component
or specialising the component in a particular context. The attributes
available to set are specified in the definition of the component, as
indicated above. These attributes are accessible to the component at the
code level at runtime.
*}
type_synonym configuration =
"(adl_symbol \<times> adl_symbol \<times> string) list"
text {*
Finally the system specification is expressed at the top level as an
assembly. This extra level of abstraction allows more flexible re-use of
compositions and configurations.
*}
record assembly =
composition :: "composition"
configuration :: "configuration option"
subsection {* \label{subsec:future}Future Work *}
subsubsection {* \label{subsubsec:composites}Component Hierarchy *}
text {*
Some component platforms support the notion of explicit composite components.
This allows a composition of components to be re-used as a component itself.
In the context of the model presented above, this would allow a
@{term composition} element to appear anywhere a @{term component} element
is expected. Flexibility like this is desirable to avoid repeatedly
constructing common design patterns involving fine grained components. There
are plans to extend CAmkES to add this functionality.
*}
subsubsection {* \label{subsubsec:iarrays}Interface Arrays *}
text {*
When specifying a more complicated dynamic component, it can be desirable to
define an array of interfaces. For example, a component that
provides an arbitrary number of copies of a specified procedure. This would
be implemented by the size of the array (the number of such copies) being
made available to the component at runtime and an index being provided with
each procedure method invocation. An example of how this could be useful is
discussed in \autoref{subsec:terminal}.
Extending this further, allowing the specification of interface arrays that
can be resized at runtime, by adding or removing connections, enables even
greater flexibility. Supporting this kind of dynamism in a system requires
meta functions (for modifying the interface array) and introduces further
complexity in handling failures of these.
*}
(*<*)end(*>*)