📄 mate-manual.tex
字号:
fires, it increments {\tt current}. When Receive triggers, it tests tosee if {\tt current} is greater than {\tt max}; if so, it sets {\ttmax} to {\tt current} and resets {\tt current} to zero.\begin{quotation}\begin{verbatim}Timer: Receive:shared current; shared current;shared max current = 0;current = current + 1;if (current > max) then max = current;end if\end{verbatim}\end{quotation}In this program, an error could occur if Timer and Receive run at thesame time. Specifically, if the Receive statement {\tt current = 0;}runs just after {\tt if (current $>$ max) then} but before {\tt max =current}, then max could be set to zero. This is clearly incorrect.This form of program error is commonly known as a race condition, asthe result of the computation is dependent on the race between the twohandlers to modify their variables.In \mate programs, these sorts of race conditions cannot occur. When aVM installs a program, it analyzes the code to determine all of thevariables the program uses. It doesn't allow two handlers that couldhave race conditions to run in parallel. In the above example, Timerand Receive would not run at the same time; if one were running, theother would wait. However, if two handlers can run safely at the sametime, the VM lets them do so. This is useful if one of the handlershas, say, along loop. If, for example, the Receive handler had to waitfor this long loop to complete, then multiple packets may arrive,causing the handler to drop some of them.\section{Domain Experts: Building an ALR}\label{sec:building}\mate ALRs can be built in two ways: with a GUI, and with a file. TheVM building tool is VMBuilder:\begin{verbatim} java net.tinyos.script.VMBuilder [options] options: -t=<directory> Load all contexts and primitives from subdirectories contexts and opcodes -d=<directory> Load all contexts and primitives from directory -l=<language file> Use the language specified in this file -f=<file> Load context or opcode from file -nw <vmfile> Do not start the GUI; load VM specification from file\end{verbatim}There are sample VM specification files in lib/VM/samples, as well asa README describing the file format. For now, we suggest you use buildVMs from files. However, if you want to browse the available contextsand library functions, then run VMBuilder with -t=$<$dir$>$, where diris the path to lib/VM.\section{Expert Users: Extending \mate}\label{sec:extending}\mate has a small set of events and library functions, but it is also designedto allow users to add new ones. In this section, we step though the processof adding an event or library function to \mate so you can incorporate itinto your ALR>\subsection{Library Functions}For VMBuilder to recognize a library function, there must be an OpcodeDescription File (.odf). The library function component should be aconfiguration, which wires the actual module to all of its neededservices. We'll use {\tt OPid} as a running example. All libraryfunctions provide the MateBytecode interface, which is how the VMengine executes them.OPid provides the id() library call, which pushes the mote ID onto theoperand stack. There are three files: OPidM.nc, OPid.nc, andOPid.odf. OidM is a module, that actually implements the libraryfunction:\begin{verbatim}includes Mate;module OPidM { provides interface MateBytecode; uses { interface MateStacks as Stacks; }}implementation { command result_t MateBytecode.execute(uint8_t instr, MateContext* context) { dbg(DBG_USR1, "VM (%i): Pushing local address: %i.\n", (int)context->which, (int)TOS_LOCAL_ADDRESS); call Stacks.pushValue(context, TOS_LOCAL_ADDRESS); return SUCCESS; }}\end{verbatim}It just takes TOS\_LOCAL\_ADDRESS and pushed it onto the operand stack,where it can be used as a parameter to another function, stored in avariable, or modified with arithmetic.OPid.nc is a configuration that wires OPidM to all of its needed services:\begin{verbatim}includes Mate;configuration OPid { provides interface MateBytecode;}implementation { components OPidM, BStacksProxy; MateBytecode = OPidM; OPidM.Stacks -> BStacksProxy;}\end{verbatim}This approach means that, merely by incorporating OPid in thecomponent list of the VM, it will be wired to everything it needs. TheProxy components are a way of having many opcodes share animplementation of an interface without specifing what it is. Thetop-level VM wires all of the Proxy components to specificimplementations. This means, for example, that the implementation ofthe operand stacks can be changed without needing to change anyopcodes. There is a Proxy for each major interface. The full set is:\begin{itemize}\item BBufferProxy\item BContextSynchProxy\item BErrorProxy\item BLocksProxy\item BQueueProxy\item BStacksProxy\item BVirusProxy\end{itemize}The final file is the description. ODF files have a single XML-likeelement in them, which describes the function.\begin{verbatim}<PRIMITIVE name=ID opcode=id numparams=0 returnval=TRUE desc="Returns the mote's ID.">\end{verbatim}They have five required elements: name, opcode, numparams, returnval,and desc. Name defines the function name exported toTinyScript. Opcode defines the name the primitive opcode takes to theassembler. These must both be the same as the component name (e.g.,``id''); name should be all upper case, and opcode should be all lowercase. The numparams element states how many parameters the functiontakes: id takes none. The returnval element specifies whether theprimitive returns a value. The id() function, for example, does, whilethe send() function does not. Finally, the desc element is thedescription provided in the Scripter interface. It should describewhat parameters the function expects, what it does, and whether it hasa return value.The best way to learn how to write a new function is to look at a fewof the existing ones.\subsection{Synchronization}Some instructions represent shared resources. For example, {\ttbpush1} and {\tt getvar4} access shared variables. For race freeprogram, the VM execution engine must be aware of this and controlscheduling appropriately.All of the VM context synchronization is handled in the BContextSynchcomponent. It keeps track of shared resources through theMateBytecodeLock interface. If an instruction manages a sharedresource, then it must provide this interface. Additionally, in its ODF,it must have the optional element ``locks'' set to true.For example, let's look at OPbpush1. This instruction pushes the twoshared buffers, buffer0 and buffer1, onto the operand stack. It is nota library function; instead, it is an element of the instruction set thatTinyScript compiles to. In tscript.ldf, it lists\begin{verbatim}<PRIMITIVE opcode="bpush1" locks=true>\end{verbatim}Then, in OPbpush1M:\begin{verbatim}module OPbpush1M { provides interface MateBytecode; provides interface MateBytecodeLock;}\end{verbatim}MateBytecodeLock has a single command:\begin{verbatim}interface MateBytecodeLock { command int16_t lockNum(uint8_t instr);}\end{verbatim}This takes a instruction opcode and returns a unique lock number. The idea isthat certain opcodes have a lock associated with them. bpush1, for example,has one bit of embedded operand, for the two buffers. If {\tt bpush1 0}is passed to {\tt OPbpush1M.nc}, then it returns the lock number forbuffer zero, while {\tt bpush1 1} will return the lock number forbuffer one.The full {\tt OPbpush1M.nc} logic:\begin{verbatim}module OPbpush1M { ... provides interface MateBytecodeLock; ...}implementation { typedef enum { BOMB_BUF_LOCK_1_0 = unique("MateLock"), BOMB_BUF_LOCK_1_1 = unique("MateLock"), } BufLockNames; ... command int16_t MateBytecodeLock.lockNum(uint8_t instr) { if (instr & 1) { return BOMB_BUF_LOCK_1_1; } else { return BOMB_BUF_LOCK_1_0; } } ...}\end{verbatim}It declares two unique lock numbers with the nesC unique function. WhenlockNum() is called, it returns the lock number associated with thecorresponding buffer. Then, each context has\begin{verbatim} uint8_t heldSet[(BOMB_HEAPSIZE + 7) / 8]; uint8_t releaseSet[(BOMB_HEAPSIZE + 7) / 8]; uint8_t acquireSet[(BOMB_HEAPSIZE + 7) / 8];\end{verbatim}where BOMB\_HEAPSIZE is defined to be uniqueCount("MateLock");\subsubsection{Opcode Component Naming Convention}Library function components have the following naming convention:\begin{verbatim} OP<width><name><operand>.nc\end{verbatim}Width and operand are both numbers. Width specifies how many byteswide the instruction is. If no width is specified, the default isone. Operand specifies how many bytes of embedded operand thereare. If no operand is specified, the default is zero. Note that, afterconsidering width and operand, the instruction must have an opcode inits first byte. That is, the instruction cannot be two bytes wide andhave no embedded operand; as the \mate scheduler dispatches on thefirst byte of the opcode, it would not be able to distinguish thisinstruction from other ones.Here are a few examples:\begin{tabular}{lcccl}Component & Width & Name & Embedded & Description\\OPrand & 1 & rand & 0 & Generates a random number\\OPpushc6 & 1 & pushc & 6 & Push a constant onto the stack\\OP2jumps10& 2 & jumps & 10 & Jump to a 10-bit address\\\end{tabular}Generally, library functions are always one byte wide and never haveembedded operands. In the above example, neither {\tt pushc} nor {\tt jumps}are available as library functions; they are actually opcodes that compose part of what TinyScript compiles to. So, all library functions youwrite should just be of the form OP$<name>$.The library function component must be a configuration, which wiresthe actual module to all of its needed services. We'll use {\ttOPrand} as a running example.The $name$ field must be the name of the function as it is called froma scripting language.\subsection{Events}All \mate event components use the interfaces MateContextSynch,MateEngineControl, MateCapsuleStore, and MateAnalysis. We describeeach in turn.\vspace{1ex}\noindent{\bf MateContextSynch:} This interfaceis how the handler component communicates with the \mate scheduler. Theonly event the component must implement is makeRunnable().\vspace{1ex}\noindent{\bf MateEngineControl:} This interface is howthe handler component interacts with the main execution engine. Usingit, a context registers its execution capsule and can ask the VM toreboot (necessary when new code is installed).\vspace{1ex}\noindent{\bf MateCapsuleStore:} This interface is how handler components obtain references to their current capsule. Thecomponent that implements MateCapsuleStore is responsible for managingcode storage across different program dissemination formats. It signals a{\tt capsuleChanged} event when new code is fully installed.\vspace{1ex}\noindent{\bf MateAnalysis:} This interface is howthe context requests a program analysis to compute lock setsfor parallel execution.The exact working and interactions of all of these interfaces aresomewhat complex. If you want to write a new event handler, Irecommend taking an existing, simple one (e.g., ClockContext), copyingit, and modifying it as need be. Cleaning up this aspect of the VM isdefinitely a thing I plan to do, but not just yet.Event handler contexts have component naming conventions similar tolibrary functions. There is a configuration, which must be$<name$>Context.nc, a module, and a context description file (CDF). Inaddition to a CONTEXT entry, a CDF can contain PRIMITIVE entries,which represent library functions the handler enables. For example,{\tt Timer1Context.cdf} is so:\begin{verbatim}<CONTEXT name="Timer1" desc="A periodic timer."><PRIMITIVE name=SETTIMER1 opcode=settimer1 numparams=1 param1=1 returnval=FALSEdesc="Takes a single parameter, the interval (in tenths of a second) betweentimer firings for the Timer 1 context. Calling with a time of zero stops thetimer.">\end{verbatim}Node that settimer1() does not have an ODF file in lib/opcodes. Infact, the opcode module makes calls on Timer1Context.nc, tochange the timer firing rate:\begin{verbatim}configuration OPsettimer1 { provides interface MateBytecode;}implementation { components OPsettimer1M, Timer1Context, MStacksProxy; MateBytecode = OPsettimer1M; OPsettimer1M.Stacks -> MStacksProxy; OPsettimer1M.Types -> MStacksProxy; OPsettimer1M.Timer -> Timer1Context;}\end{verbatim}\end{document}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -