📄 mate-manual.tex
字号:
\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:\vspace{0.1in}{\scriptsize\begin{tabular}{|l|c|c|c|l|} \hlineComponent & Width & Name & Embedded & Description\\ \hlineOPrand & 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\\ \hline\end{tabular}}\vspace{0.1in}Functions are always one byte wide and never have embeddedoperands. In the above example, neither {\tt pushc} nor {\tt jumps}are available as library functions; they are actually primitives thatcompose part of what TinyScript compiles to.\subsection{Primitives}Primitives are operation components that a language compiles to. Theycannot be included in a VM in the same way functions can: they have noODF which VMBuilder can use to include them. The only way to includethem is to use a language that compiles to them. Operations such asbranches, memory access, and arithmetic are examples of \mateprimitives.Primitives provide language storage abstractions, such asvariables. For example, TinyScript has the notion of sixteen variablesthat are shared between all handlers, and eight variables which areprivate to each handler. The operations {\tt getvar4} and {\ttsetvar4} implement the former, while the operations {\tt getlocal3}and {\tt setlocal3} implement the latter.The implementations of the two abstractions are very similar. A singlecomponent implements {\tt getvar4} and {\tt setvar4}, providing two{\tt MateBytecode} interfaces. Sixteen opcodes map to each instance of{\tt MateBytecode}, for the four bits of embedded operand. Thecomponent allocates sixteen shared variables in its frame. Todetermine which variable a program is accessing, the implementationsubtracts the executed opcode from the base opcode, Additionally, theimplementation checks that the program holds the lock to the sharedvariable:{\scriptsize\begin{verbatim} MateStackVariable heap[16]; command result_t Get.execute(uint8_t instr, MateContext* context) { uint8_t arg = instr - OPgetvar4; uint8_t lock = varToLock(arg); if ((lock == 255) || !call Locks.isHeldBy(lock, context)) { call Error.error(context, MATE_ERROR_INVALID_ACCESS); return FAIL; } dbg(DBG_USR1, "VM (%i): Executing getvar (%i).\n", (int)context->which, (int)arg); call Stacks.pushOperand(context, &heap[arg]); return SUCCESS; }\end{verbatim}}{\tt getlocal3} and {\tt setlocal3} are similar, except thereare only eight variables, and there's no need to check locks. However,the component that implements the two primitives has to allocate statefor each context:{\scriptsize\begin{verbatim} MateStackVariable vars[MATE_CONTEXT_NUM][NUM_VARS]; command result_t Get.execute(uint8_t instr, MateContext* context) { uint8_t arg = instr - OPgetlocal3; dbg(DBG_USR1, "VM (%i): OPgetlocal3 (%i).\n", (int)context->which, (int)arg); call Stacks.pushOperand(context, &vars[context->which][arg]); return SUCCESS; }\end{verbatim}}Some primitives are more than one byte wide. For example, {\ttOP2jumps10}, the basic branch instruction is two bytes wide. The {\ttbyteLength()} command of the {\tt MateBytecode} interface must returnthe byte width of an instruction, so the scheduler knows how much toincrement the program counter by. {\tt It increments the programcounter before executing the instruction.} The component implementingthe primitive is responsible for getting the extra bytes. For example,this is part of {\tt OP2jumps10M}:{\scriptsize\begin{verbatim} command uint8_t MateBytecode.byteLength() {return 2;} command result_t MateBytecode.execute(uint8_t instr, MateContext* context) { uint16_t addr = (instr - OP2jumps10) << 8; MateStackVariable* cond = call Stacks.popOperand(context); addr |= call Store.getOpcode[context->currentHandler](context->pc-1);\end{verbatim}}It generates the 10-bit jump address by taking the bottom two bits ofthe opcode and incorporating the next byte. Since the VM has alreadyincremented the program counter, the next byte is at {\tt context-$>$pc-1}.The \mate tutorials briefly mention a requirement when using the \mateoperand stack. When a component pops operands off the stack with {\ttpopOperand}, the call returns a pointer to a stack variable. This is apointer into the stack data structure. If a component then pushessomething onto the stack, that push can modify the region of the stackthe pointer refers to. There are situations when it is safe to accesspopped operands after pushes, however.Elements on the operand stack have a fixed size. For example, a buffer(which stores a pointer) is the same size as a value. An operand stackis an array of operands and a stack pointer, which indicates the nextfree element. Pushing something onto the stack fills the next freeelement, and increments the stack pointer. Popping something off thestack decrements the stack pointer, and returns a pointer to thatelement. The following three snippets of pseudocode are examples ofthe resulting behavior:\begin{verbatim} op1 = pop(); op2 = pop(); push(4); // op2 is now invalidated push(op2); // BUG, unless you want to copy the top of the operand stack op1 = pop(); op2 = pop(); push(op2); // SAFE; returns op2 to operand stack push(op1); // same as above op1 = pop(); op2 = pop(); push(op1); // invalidates op2, which is now the same as op1 push(4); // invalidates op1\end{verbatim}\section{Languages}\label{sec:language}A language is defined by the set of primitives it compiles to. ALANGUAGE element in a VM specification file causes VMBuilder to searchfor a language description file ({\tt .ldf}). An LDF must have aLANGUAGE element with the NAME and DESC tags. Additionally, it shouldhave a series of PRIMITIVE elements. PRIMITIVE elements have onerequired tag, OPCODE. They also have the optional field LOCKS, whichspecifies if the primitive encapsulates a shared resources which theconcurrency manager must arbitrate access to. For example, this is asnippet of {\tt tscript.ldf}:{\scriptsize\begin{verbatim}<LANGUAGE name="TinyScript" desc="A simple, BASIC-like language."><PRIMITIVE opcode="halt"><PRIMITIVE opcode="2pushc10"><PRIMITIVE opcode="2jumps10"><PRIMITIVE opcode="getlocal3"><PRIMITIVE opcode="setlocal3"><PRIMITIVE opcode="bpush3" locks=true><PRIMITIVE opcode="getvar4" locks=true><PRIMITIVE opcode="setvar4" locks=true><PRIMITIVE opcode="or"><PRIMITIVE opcode="and"><PRIMITIVE opcode="not"><PRIMITIVE opcode="eq"><PRIMITIVE opcode="gte"><PRIMITIVE opcode="gt">\end{verbatim}}VMBuilder interprets PRIMITIVE elements in a language file; it doesnot load any additional files in response to them. It uses the OPCODEtag to refer to the primitive's component when wiring the instructionset. If the LOCKS tag exists (the value is ignored), the VMBuilderalso has MateTopLevel wire the primitive component to the concurrencymanager.The LANGUAGE element has a single optional tag,FIRSTORDERFUNCTIONS. If the LANGAUGE element has this tag (whose valueis ignored), the VMBuilder includes support for first orderfunctions. It does so by wiring functions to the {\tt FunctionImpls}interface of {\tt MateEngine} and generating a set of functionidentifiers of the form fn\_name, where {\it name} is the name of thefunction. These indentifiers are in an enum in {\tt MateConstants.h},and can therefore can be accessed through the VM Java constants file.\section{VM Options}Tutorial 4 presents how to specify the language, events, and functionsthat a \mate VM supports. More advanced users can also modify variousVM options. These options can be set with one or more {\tt OPTION}elements. The supported options are:\vspace{1ex}{\scriptsize\begin{tabular}{|l|l|l|}\hlineName & Type & Description \\ \hlineOPDEPTH & integer & Sets the maximum depth of a context operand stack. \\ & & The default value is 8.\\ & & Changing this value will change the maximum length of TinyScript statements.\\ & & Larger values increase RAM utilization.\\\hlineBUF\_LEN & integer & Sets the maximum size of a data buffer.\\ & & The default value is 10.\\ & & Larger values will allow you to manage larger buffers. \\ & & However, this will increase the RAM allocated for each buffer.\\ & & If made much larger, it increase packet size, {\bf greatly} increasing RAM utilization.\\ & & Decreasing this value will not reduce the RAM utilization of message buffers.\\ \hlineCAPSULE\_SIZE & integer & Sets the maximum size of a code capsule.\\ & & The default value is 128.\\ & & Changing this value will affect how large a program you can write.\\ & & Larger values increase RAM utilization.\\ \hline\end{tabular}}\vspace{1ex}For example, to change some options, you could add either a single element\begin{verbatim}<OPTION OPDEPTH=6 CAPSULE_SIZE=64>\end{verbatim}or multiple elements\begin{verbatim}<OPTION OPDEPTH=6><OPTION CAPSULE_SIZE=64>\end{verbatim}\section{Java Toolchain}When VMBuilder generates a Makefile for a \mate VM, it includes rulesfor building a few Java classes that the \mate toolchain uses. Theforemost of these is a constants class, which it generates with the{\tt ncg} tool. This class contains all of the mappings betweenhandler names and IDs, context names and IDs, instructions and theirbytecodes, error codes, and data types. It also generates a set ofmessage classes, for interacting with a VM.\subsection{Constants Class}Every VM has a constants class. The name of the class is VM-specific,to prevent users from accidentally loading the wrong constantsfile. For example, the Bombilla constants class name isBombillaConstants. The class contains all of the constants containedin MateConstants.h as public variables. The \mate toolchain has aclass, named {\tt net.tinyos.script.ConstantMapper}, for easilyaccessing these variables through the Java reflection API.The constructor to ConstantMapper takes two arguments, a class nameand a prefix. The prefix acts as a filter on the constants itconsiders. For example, if you instantiate a ConstantMapper like so:\begin{verbatim}ConstantMapper map = new ConstantMapper("BombillaConstants", "OP");\end{verbatim}then all of its accessor functions will operate on the public fieldsof a class named {\tt BombillaConstants} whose name begins with {\ttOP}, in this case the instructions of the Bombilla VM.ConstantMapper provides three basic methods for fetching constants:{\tt codeToName()} {\tt nameToCode()}, and {\tt names()}. For example,the TinyScript compiler takes a TinyScript program and producesassembly code for it. The ScriptAssembler then takes each instructionand determines its bytecode with the {\tt nameToCode()} method. Incontrast, when the Scripter displays a VM error, it reads the binaryvalues of the error condition, context, and handler, and produceshuman-readable names for them with the {\tt codeToName()}method. Finally, the ScripterGUI determines the set of valid handlersone can write scripts for by using {\tt names()}, then translatesthose names to IDs with {\tt nameToCode()}.Obtaining the name of a VM's constants file requires reading in the VMdescription file that VMBuilder produces. Every VM applicationdirectory has a file named {\tt vm.vmdf}, which describes the VM. TheJava class {\tt net.tinyos.script.Configuration} automatically loadsand parses VM descroption files, extracting the important fields. Ittakes the path to the file as an argument in its constructor. This iswhy you must run Scripter from the VM application directory: itinstantiates a Configuration with the name {\tt vm.vmdf}. This willalso ensure that the Java loader will find the right constantsfile. Configuration has accessor functions to get at the importantinstances of ConstantMapper, such as capsule names, opcodes, and errorcodes. You can also get the constant class name, if you need to buildother ConstantMappers, with the {\tt constantClassName()} method.\subsection{Message Classes}By default, the Makefile VMBuilder generates builds several Javamessage classes. These classes are all built in the {\tt vm\_specific}subdirectory. They allow Scripters to generate the proper packets fortransmitting code to a mote, as well as read mote data output.The first class, code transmission, has three kinds of messages: {\ttCapsuleMsg}, {\tt CapsuleStatusMsg}, and {\ttCapsuleChunkMsg}. CapsuleMsg is for generating a full \mate capsulethat matches the on-mote memory layout. CapsuleStatusMsg is so toolscan monitor download status as it occurs. CapsuleChunkMsg is so theScripters can send the chunks that make up a capsule along the serialport to a mote.The second class, data transmission, has three kinds of messages: {\ttBufferBCastMsg}, {\tt BufferUARTMsg}, and {\tt MultiHopMsg}. The firsttwo are the format the {\tt bcast} and {\tt uart} functions send: a\mate data buffer in the payload of a packet. MultiHopMsg is forpackets sent through a multihop routing layer: it includes multihopheader fields, as well as the \mate buffer. The tool {\ttnet.tinyos.script.VMBufferReader} listens for all three of these kindsof packets and outputs information on them. It is a good place tostart if you want to read data from \mate into a Java application.\section{Conclusion}\mate is under active development. Bugs, questions, contexts, andfunctions can be sent to Phil Levis ({\tt pal@cs.berkeley.edu}).\end{document}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -