diff --git a/site/404.html b/site/404.html index 79a7a1b..d8ff238 100644 --- a/site/404.html +++ b/site/404.html @@ -842,7 +842,7 @@ - EM&•Mark Results + EM•Mark Results diff --git a/site/advancing/cli/index.html b/site/advancing/cli/index.html index ef42dd0..4297cf4 100644 --- a/site/advancing/cli/index.html +++ b/site/advancing/cli/index.html @@ -876,7 +876,7 @@ - EM&•Mark Results + EM•Mark Results diff --git a/site/advancing/coremark/index.html b/site/advancing/coremark/index.html index ef15136..52a8eba 100644 --- a/site/advancing/coremark/index.html +++ b/site/advancing/coremark/index.html @@ -978,7 +978,7 @@ - EM&•Mark Results + EM•Mark Results diff --git a/site/advancing/emmark/index.html b/site/advancing/emmark/index.html index 8dab0c6..5241ee2 100644 --- a/site/advancing/emmark/index.html +++ b/site/advancing/emmark/index.html @@ -20,7 +20,7 @@ -
Welcome to the world of EM \u2013 a novel programming language and runtime environment targeting resource-constrained embedded systems. To increase your understanding of the language and its runtime, this site documents all aspects of the EM software platform.
If you haven't already done so, we strongly suggest starting with Introducing EM which offers a 10,000' overview of the EM language and runtime \u2013 even if you just can't wait to install the EM software and start hacking some code\u2009!!!\u00a0\u00a0 Besides rationalizing the need for EM, this overview introduces concepts as well as defines terminology used throughout the site.
Building upon this introductory overview of EM, we've then organized the remaining documents found at this site as follows:
Installing EM software and hardware required to build and run EM programs Using EM language immersion through a graduated series of portable EM examples Porting EM steps required for (re-)targeting EM to a specific HW/SW environment Advancing EM deep-dives into specific facets of the EM platform Click here to see more (less) detailsWe'll often insert additional commentary on the material at hand in this collapsible format, providing deeper insights into some aspect of EM. While (hopefully) insightful, feel free to skip these comments at any time.
So follow me, and let the journey begin\u2009....
"},{"location":"discuss/","title":"General comments and specific issues","text":"In my humble opinion\u2009...
Created for the benefit of the broader embedded software community, we value your general feedback \u2013 comments, questions, ideas \u2013 about The EM Programming Language as presented throughout these documents.
To add your thoughts about EM, use the discussion forum in our em-foundation/em-sdk GitHub repository.\u00a0 You can join here\u2009, if you don't already have a GitHub account.
Just keep in mind \u2013 nothing has been decided, and everything still matters.
If you've downloaded the EM SDK and have begun coding, you can report any specific issues with the software in the same em-foundation/em-sdk repository.
"},{"location":"edu/","title":"Academia \u2013 say \"hello world\" to EM","text":"The EM software platform comprises a novel programming language and runtime which targets resource-constrained MCUs. Originally developed in 2010, EM has evolved over the past decade through a series of commercial deployments in low-power, low-cost wireless IoT applications. To encourage broader adoption of this technology, The EM Foundation (a non-profit formed in 2023) now makes the language and its runtime openly\u2009/\u2009freely available.
While EM leverages modern software constructs like interface inheritance and component composition, novel optimization techniques employed by the underlying language translator enable EM programs to invariably outperform their hand-crafted C counterparts in terms of time and (especially) space.
Often targeting MCUs with \u2264\u200932\u2009K of memory, real-world applications \u2013 including a BLE wireless stack \u2013 can comfortably fit within these constraints.\u00a0 A 5X\u2009-\u200910X size reduction in typical embedded applications can also drive comparable savings in energy consumption as well as overall system cost.
EM \u2013 a higher-level programming language AND a higher-level of program performance
"},{"location":"edu/#conceived-in-the-university","title":"Conceived in the university","text":"Before addressing some opportunities for academic collaboration, you should make a quick pass through Introducing EM to understand a little more about the language and its run\u00adtime. The Tiny code \u2192 Tiny chips subsection already takes us to some fertile grounds for future investigation; EM's initial engagement with the RISC-V community should also spark interest within university environments.(1)
While you can certainly skip the technical overivew of the language and its runtime for now, we strongly encourage you to read The history of EM subsection \u2013 and do note the seminal role played by UC Santa Barbara in the birthing and early development of EM.\u2009 In retrospect, EM would not exist today without a nuturing university environment.
"},{"location":"edu/#managing-open-source-projects","title":"Managing open-source projects","text":"With an abundance of open-source software ranging from host tooling to target drivers (plus open-source hardware ranging from Arduino boards to RISC-V MCUs), CS/CE university programs should (in our humble opinion) arm their students with knowledge of open-source best practices \u2013 enabling them to not only participate in established projects, but also to create their own open-source initiatives which they could lead and manage.
Given that the transition of the EM technology into the open-source domain has only just begun, \"ground-floor\" opportunities await enterprising CS/CE departments motivated to assume a leadership role in the process. Said another way, The EM Foundation seeks help from universities to support its mission \u2013 promoting, sustaining, and evolving the EM programming language and runtime for use by the broader embedded systems community.
"},{"location":"edu/#areas-for-technical-contribution","title":"Areas for technical contribution","text":"From an academic perspective, the depth\u2009/\u2009duration of potential technical contributions to EM can range from (say) a single-semester undergraduate project to multi-year graduate-level research culminating in a thesis.\u2009 Some areas of mutual interest might include:
Overall management of EM repositories housed at GitHub \u2013 coordinate the transition of existing (private) sources to public repos; introduce processes defined by open-source maturity models.
Evolution of the (command-line) EM translator plus its companion VS Code extension \u2013 relatively stable, quite compact (~12\u2009K lines of TypeScript), but with many opportunities for adding new features.
Perpetual expansion of the EM language runtime \u2013 ports to different MCUs, device drivers for sensors\u2009/\u2009controllers, wired\u2009/\u2009wireless communication stacks, machine-learning algorithms, etc.
New MCU architectures tailored for EM \u2013 take on the Tiny code \u2192 Tiny chips challenge; leverage RISC-V IP targeting FPGAs for proof-of-concept; add application-specific CPU instructions, etc.
"},{"location":"edu/#call-to-action-contact-us","title":"Call to action \u2013 contact us","text":"If EM appears to align with the interests of your department, don't hesitate to message The EM Foundation on Linkedin with any questions you may have.
In the interim, continue to explore the material at docs.openem.org as well as read our weekly posts at blog.openem.org.
And if you really feel motivated, consider visiting Installing EM and actually downloading the EM SDK \u2013 though perhaps you'll leave this task as an \"exercise for the student\"\u2009.
"},{"location":"install/","title":"Getting started with EM","text":"Using the EM language and its runtime requires a cross-development environment, in which hosted tooling will build\u2009/\u2009load executable EM programs onto target hardware. For the host, you'll use a PC running Windows, Linux, or MacOS(1); for the target, you can choose any MCU board for which an em$distro
package already exists.
Before turning to the EM SDK (described next), you should first install\u2009/\u2009upgrade the following tooling environments on your host PC:
Node.js version 16.3.0 or later executenode
--version
to verify VS Code version 1.80.0 or later execute code
--version
to verify Windows If you don't already have a recent version of the Git Bash shell, you should also install Git for Windows.\u00a0 To verify your setup, ensure that the node
and code
commands from the previous table operate correctly under Git Bash.
Do not proceed forward if these verification checks should fail\u2009!!!
"},{"location":"install/#software-development-kit","title":"Software development kit","text":"The EM SDK comprises two distinct components:
\u2003a VS Code extension named EM Builder with which you'll interact directly; and
\u2003a special \u00abEM-SDK\u00bb
folder automatically managed by , which you can largely ignore.
Under the \u00abEM-SDK\u00bb
folder you'll find a sub-folder named tools
, which will contain C/C++ compilers as well as emulation utilities \u2013 all used internally by EM to build\u2009/\u2009load executable target programs.\u00a0 Already found in a well-known place on your PC(1), EM requires no further action on your part to setup these tools.
\u00abEM-SDK\u00bb
\u00a0=\u2002~/em-sdk
Looking inside your \u00abEM-SDK\u00bb
folder, you'll also find some package*.json
files as well as a node_modules
sub-folder \u2013 characteristic of an npm
package.\u00a0 Unless instructed otherwise, please leave these (and any other\u2009!!\u2009) artifacts housed in \u00abEM-SDK\u00bb
undisturbed.
Unless instructed otherwise, do NOT disturb the contents of your \u2009\u00abEM-SDK\u00bb\u2009 folder\u2009!!!
"},{"location":"install/#target-mcu-hardware","title":"Target MCU hardware","text":"The EM SDK will contain all of the tools required to build\u2009/\u2009load EM programs targeting any of the following boards; you won't need to download any additional, vendor-specific software.\u00a0 While we encourage you to purchase one (or more) of these boards, you can still learn a great deal about EM without target hardware \u2013 by taking the **FAST-TRACK**
option.
The Texas Instruments CC2340R5 wireless MCU features an Arm Cortex-M0+ CPU together with a familiar suite of peripherals \u2013 including a generic 2.4\u2009GHz radio with BLE 5.x support. Texas Instruments also offers an inexpensive LP-EM-CC2340R5 evaluation board in their familiar LaunchPad format \u2013 available from TI as well as their distributors.
You should also purchase this emulator board from TI \u2013 unless you already own a \"classic\" TI LaunchPad with on-board XDS110 support. In that case, you can easily connect this legacy LP to your new LP-EM-CC2340R5 board using a cable supplied by TI. If you haven't used an XDS110 before, run the one_time_setup
script found in \u00abEM-SDK\u00bb/tools/ti-uniflash
.
The ti.cc23xx
distro supports the following Setups for the EM-SDK tools:
ti.cc23xx/gcc
GCC 10.3, optimized for space ti.cc23xx/gcc_sram
GCC 10.3, optimized for space, code\u2009+\u2009consts in SRAM ti.cc23xx/segger
CLANG\u2009/\u2009LLVM 14.0, optimized for space ti.cc23xx/segger_sram
CLANG\u2009/\u2009LLVM 14.0, optimized for space, code\u2009+\u2009consts in SRAM ti.cc23xx/default
== ti.cc23xx/segger
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
Nothing to buy, of course\u2009!!!\u00a0 You can still build executable programs using any em$distro
package within the EM SDK; you just can't load these programs onto target hardware.
Familiarize yourself, however, with the Setups available for one (or more) of the other hardware tracks enumerated above.
As we pivot to installing EM Builder, we'll soon illustrate how to select one of the Setups associated with the distros supporting these hardware options.
Which MCU board(s) would you want to see EM support\u2009???
"},{"location":"install/#em-builder-extension","title":"EM Builder extension","text":"At this stage, we can launch VS Code and install its EM Builder extension. To begin, first create an empty folder anywhere on your PC (outside of \u00abEM-SDK\u00bb
); this folder will serve as your initial VS Code workspace.
Next, enter the command code
workspace
to launch VS Code (where workspace
represents a path to the folder created above).\u00a0 Unless you've already installed EM Builder, you'll see something like the default Welcome
layout shown within the following screen capture:
The installation process actually begins by importing a VS Code profile named EM, which will configure a variety of user-settings as well as download the latest (stable) version of the EM Builder extension and its dependencies. To illustrate the process, watch the Importing EM Profile animation above by clicking the button atop this screen capture.
As seen in the animation, you'll use a special URL generated by VS Code to fetch the EM profile from the cloud.\u00a0 When ready, copy this URL to your clipboard and follow the same steps [\u2009Profiles\u2009>\u2009Import Profiles...\u2009] within your own installation of VS Code \u2013 pasting this URL into the input box.
https://vscode.dev/profile/github/d42fc7e6ff05708f910babc909fce416\n
If necessary, you can rewind the animation by clicking the button whenever it appears atop the screen capture.\u00a0 Hint \u2013 once playing, you should click on the animation itself to enlarge the view.
With the EM profile in place, we'll now activate the EM Builder extension for the first time as illustrated in the following animation:
Activating EM Builder Activating EM BuilderThe [\u2009Command Palette\u2009>\u2009Reload Window\u2009] sequence effectively restarts VS Code which in turn will activate EM Builder.\u2009 Always checking the status of the \u00abEM-SDK\u00bb
folder, the extension will prompt you to install the EM SDK components whenever absent or outdated.(1)
The EM Builder extension also requires that you select one of the Setups supported by your target MCU hardware, as the following animation will illustrate:
Selecting EM Setups Selecting EM SetupsYou should select a \"default\" configuration when the EM Setup dropdown appears \u2013 even if you've chosen the **\u2009FAST-TRACK\u2009** option for your target MCU hardware. Note that this process implicitly binds a default target board associated with your selection.
If you do plan to connect a target board, you'll also need to assign an appropriate value to the ConsolePort
parameter found in the workspace local.properties
file as follows:
Finding the actual value for the ConsolePort
parameter depends, of course, on utilities specific to your PC's operating system as well as some help from your board's documentation.
In summary:
\u2003entercode
workspace
from the shell to launch VS Code \u2003execute the \u2009Profiles\u2009>\u2009Import Profiles...\u2009 command sequence \u2003paste the special URL given above into the input box \u2003select Create Profile and then Create when prompted \u2003execute the Developer: Reload Window command \u2003select Install... if prompted to install EM SDK components \u2003select Select... if prompted to bind EM Setups \u2003choose a \"default\" configuration from the EM Setup dropdown \u2003update the ConsolePort
parameter in local.properties
as appropriate"},{"location":"install/#ready-set-go","title":"Ready, Set, Go\u00a0!!!","text":"With everything in place, the time has come to start Using EM\u2009.Happy coding\u2009!!! \u2002
"},{"location":"advancing/","title":"Deep-dive into different facets of EM","text":"This document comprises a collection of independent articles focusing on different facets of the EM language and runtime. So pick a topic of interest and then dive right in\u2009!!!
Command-line interface Managing EM from within the shell CoreMark\u00ae Reimagined Transforming legacy C code into EM EM\u2022Mark Results Standard benchmarks for EM Energy Consumption Optimizing power over time Language Syntax Navigating EM syntax diagramsWhat other aspects of EM should we tackle next\u2009???
"},{"location":"advancing/cli/","title":"Managing EM from within the shell","text":"Under Construction \u2014 estimated completion in 1Q24 \u2009
"},{"location":"advancing/coremark/","title":"Transforming legacy C code into EM","text":"CoreMark\u00ae has emerged as the premier industry benchmark for measuring CPU performance within embedded systems. Managed through EEMBC\u2009, virtually every MCU vendor has certified and published CoreMark scores for a broad portfolio of their processors. Running the benchmark code also serves as a \"typical workload\" used when characterizing active power consumption [\u2009\u03bcW\u2009/\u2009Mhz\u2009] of a particular MCU.
The workload introduced by CoreMark encompasses four algorithms reflecting the variety of software functions often implemented within embedded application programs:
list processing find and remove elements, generalized sorting matrix manipulation add and multiply by a scalar, vector, or matrix state machine scan a string for a variety of numeric formats cyclic redundancy check checksum over a sequence of 16\u2009/\u200932-bit valuesBesides adding to the workload, CoreMark uses algorithm to validate the final results of running the benchmark program \u2013 comparing a checksum over the list elements used in algorithm against an expected value. CoreMark also checksums the matrix data produced by algorithm as well as the state machine transitions encountered by algorithm .
You'll find the CoreMark sources on GitHub, together with instructions for building\u2009/\u2009running the benchmark program. To ensure the integrity of the benchmark, you cannot modify any of its (portable) C source files \u2013 with the exception of core_portme.[ch]
, used to adapt CoreMark to a particular hardware platform.
Needless to say, your choice of C compiler along with specific options for controlling program optimization remain on the table. While primarily intended for comparing different MCUs, CoreMark also provides a known codebase useful for \"apples-to-apples\" comparisons between different compilers [GCC, IAR, Keil, LLVM] targeting the same MCU.
CoreMark \u2013 a \"typical\" C program in more ways than one
We sense that very few software practitioners have actually studied the CoreMark source files themselves. As long as \"someone else\" can actually port\u2009/\u2009build\u2009/\u2009run the benchmark on the MCU of interest, good enough\u2009!!
In our humble opinion, the CoreMark sources would not serve as the best textbook example of well-crafted C code:\u00a0 insufficent separation of concerns, excessive coupling among compilation units, plus other deficiencies.
Said another way, CoreMark typifies the design\u2009/\u2009implementation of much of the legacy embedded C code we've encountered for decades within industry and academia alike.\u00a0 But therein lies an opportunity to showcase EM.
"},{"location":"advancing/coremark/#coremark-emmark","title":"CoreMark \u21d2 EM\u2022Mark","text":"In reality, none of the official CoreMark sources (written in C) will survive their transformation into EM\u2022Mark \u2013 a new codebase (re-)written entirely in EM.\u2009 At the same time, applying the same CoreMark algorithms to the same input data must yield the same results in EM.
The input data used by EM\u2022Mark (like CoreMark) ultimately derives from a handful of seed\u2009 variables, statically-initialized with prescribed values.\u2009 Declared volatile
in EM as well as C, the integrity of the benchmark requires that the underlying compiler cannot know the initial values of these seed variables and potentially perform overly-aggressive code optimizations.
At the same time, the CoreMark sources do make use of C preprocessor #define
directives to efficiently propogate constants and small (inline) functions during compilation.\u2009 EM\u2022Mark not only achieves the same effect automatically via whole-program optimization, but also leverages the full power of EM meta-programming to initialize internal data structures at build-time \u2013 resulting in a far-more compact program image at run-time.
If necessary, review the material on program configuration and compilation to fully appreciate the opportunities that EM affords for build-time optimization.
"},{"location":"advancing/coremark/#high-level-design","title":"High-level design","text":"The EM\u2022Mark sources (found in the em.coremark package within the em.bench
bundle) consist of ten EM modules and two EM interfaces, organized as follows:
The ActiveRunnerP and SleepyRunnerP programs on top of this hierarchy both execute the same core benchmark algorithms, albeit in two very different contexts:
ActiveRunnerP
performs multiple\u2009 benchmark iterations, much like the legacy CoreMark program
SleepyRunnerP
performs a single\u2009 benchmark iteration, awakening every second from deep-sleep
The CoreBench
module (imported by both of these programs) coordinates both configuration as well as execution of the list processing, matrix manipulation, and state machine algorithms; we'll have more to say about its implementation in a little while.
To capture behavioral commonality between CoreBench
and the algorithm modules it uses internally [\u2009ListBench
, MatrixBench
, StateBench
\u2009], our EM\u2022Mark design introduces the abstract em.coremark/BenchAlgI
interface:
package em.coremark\n\nimport Utils\n\ninterface BenchAlgI\n\n config memSize: uint16\n\n function dump()\n function kind(): Utils.Kind\n function print()\n function run(arg: uarg_t = 0): Utils.sum_t\n function setup()\n\nend\n
Of the handful of functions specified by this interface, two of these play a central role in the implementation of each benchmark algorithm:
BenchAlgI.setup
, which initializes the algorithm's input data using volatile
seed variables
BenchAlgI.run
, which executes one pass of the benchmark algorithm and returns a CRC value
Taking a quick peek inside CoreBench
, you'll notice how this module's implementation of the BenchI
interface simply delegates to the other algorithm modules \u2013 which in turn implement the same interface:
def em$construct()\n Utils.bindSeedH(1, 0x0)\n Utils.bindSeedH(2, 0x0)\n Utils.bindSeedH(3, 0x66)\nend\n\ndef dump()\n ListBench.dump()\n MatrixBench.dump()\n StateBench.dump()\nend\n\ndef kind()\n return Utils.Kind.FINAL\nend\n\ndef print()\n ListBench.print()\n MatrixBench.print()\n StateBench.print()\nend\n\ndef run(arg)\n auto crc = ListBench.run(1)\n Utils.setCrc(Utils.Kind.FINAL, Crc.add16(<int16>crc, Utils.getCrc(Utils.Kind.FINAL)))\n crc = ListBench.run(-1)\n Utils.setCrc(Utils.Kind.FINAL, Crc.add16(<int16>crc, Utils.getCrc(Utils.Kind.FINAL)))\n Utils.bindCrc(Utils.Kind.LIST, Utils.getCrc(Utils.Kind.FINAL))\n return Utils.getCrc(Utils.Kind.FINAL)\nend\n\ndef setup()\n ListBench.setup()\n MatrixBench.setup()\n StateBench.setup()\nend\n
CoreBench
also uses public get
\u2009/\u2009set
functions provided by the Utils module to fetch\u2009/\u2009store designated CRC and seed values.
more code ahead \u2013 free free to scroll down to the Summary
Each of the benchmark algorithms will call the Crc.add16
or Crc.addU32
functions to fold a new data value into a particular checksum. Looking at the implementation of the Crc module, both of these function definitions ultimately call Crc.update
\u2013 a private function that effectively mimics the crcu8
routine found in the legacy CoreMark source code:
ee_u16\ncrcu8(ee_u8 data, ee_u16 crc)\n{\n ee_u8 i = 0, x16 = 0, carry = 0;\n\n for (i = 0; i < 8; i++)\n {\n x16 = (ee_u8)((data & 1) ^ ((ee_u8)crc & 1));\n data >>= 1;\n\n if (x16 == 1)\n {\n crc ^= 0x4002;\n carry = 1;\n }\n else\n carry = 0;\n crc >>= 1;\n if (carry)\n crc |= 0x8000;\n else\n crc &= 0x7fff;\n }\n return crc;\n}\n
Finally, CoreBench
defines a pair of config
params [\u2009TOTAL_DATA_SIZE
, NUM_ALGS
\u2009] used to bind the BenchAlgI.memSize
parameter associated with the other algorithms; refer to CoreBench.em$configure
defined here for further details.\u2009 Initialized to values tracking the legacy CoreMark code, CoreBench
assigns \u230a2000/3\u230b\u2009\u2261\u2009666
bytes per algorithm.(1)
CoreBench.em$configure
after we explore the three benchmark algorithms in more detail.Pivoting to the simplest of the three benchmark algorithms administered by CoreBench
, the MatrixBench module implements each (public) function specified by the BenchAlgI
interface; and most of the MatrixBench
private functions defined inside the module [\u2009addVal
, mulVec
, clip
, etc\u2009] correspond to legacy C functions\u2009/\u2009macros found in core_matrix.c
\u2009.
Internally, MatrixBench
operates upon three matrices [\u2009matA
, matB
, matC
\u2009] dimensioned at build-time by the module's em$construct
function \u2013 which uses the BenchI.memSize
parameter (bound previously in CoreBench.em$configure
) when calculating a value for dimN
:
module MatrixBench: BenchAlgI\n\nprivate:\n\n type matdat_t: int16\n type matres_t: int32\n\n config dimN: uint8\n\n var matA: matdat_t[]\n var matB: matdat_t[]\n var matC: matres_t[]\n
em.coremark/MatrixBench.em [exc]def em$construct()\n auto i = 0\n auto j = 0\n while j < memSize\n i += 1\n j = i * i * 2 * 4\n end\n dimN = i - 1\n matA.length = matB.length = matC.length = dimN * dimN\nend\n
The MatrixBench.setup
function initializes \"input\" matrices [\u2009matA
, matB
\u2009] at run-time, using values derived from two of the volatile
seed variables prescribed by legacy CoreMark:
def setup()\n auto s32 = <uint32>Utils.getSeed(1) | (<uint32>Utils.getSeed(2) << 16)\n auto sd = <matdat_t>s32\n sd = 1 if sd == 0\n auto order = <matdat_t>1\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n sd = <int16>((order * sd) % 65536)\n auto val = <matdat_t>(sd + order)\n val = clip(val, false)\n matB[i * dimN + j] = val\n val += order\n val = clip(val, true)\n matA[i * dimN + j] = val\n order += 1\n end\n end\nend\n
MatrixBench.run
finally executes the benchmark algorithm itself \u2013 calling a sequence of private matrix manipulation functions and then returning a checksum that captures intermediate results of these operations:
def run(arg)\n auto crc = <Crc.sum_t>0\n auto val = <matdat_t>arg\n auto clipval = enlarge(val)\n #\n addVal(val)\n mulVal(val)\n crc = Crc.add16(sumDat(clipval), crc)\n #\n mulVec()\n crc = Crc.add16(sumDat(clipval), crc)\n #\n mulMat()\n crc = Crc.add16(sumDat(clipval), crc)\n #\n mulMatBix()\n crc = Crc.add16(sumDat(clipval), crc)\n #\n addVal(-val)\n return Crc.add16(<int16>crc, Utils.getCrc(Utils.Kind.FINAL))\nend\n
Once again, the [EM] implementations of private functions like addVal
and mulMat
track their [C] counterparts found in the CoreMark core_matrix.c
source file.
The StateBench module \u2013 which also conforms to the BenchAlgI
interface \u2013 scans an internal array [\u2009memBuf
\u2009] for text matching a variety of numeric formats.\u2009 Similar to what we've seen in MatrixBench
, the build-time em$construct
function sizes memBuf
as well as initializes some private config
parameters used as run-time constants:
config intPat: string[4] = [\n \"5012\", \"1234\", \"-874\", \"+122\"\n ]\n config fltPat: string[4] = [\n \"35.54400\", \".1234500\", \"-110.700\", \"+0.64400\"\n ]\n config sciPat: string[4] = [\n \"5.500e+3\", \"-.123e-2\", \"-87e+832\", \"+0.6e-12\"\n ]\n config errPat: string[4] = [\n \"T0.3e-1F\", \"-T.T++Tq\", \"1T3.4e4z\", \"34.0e-T^\"\n ]\n\n config intPatLen: uint16\n config fltPatLen: uint16\n config sciPatLen: uint16\n config errPatLen: uint16\n\n var memBuf: char[]\n
em.coremark/StateBench.em [exc]def em$construct()\n memBuf.length = memSize\n intPatLen = intPat[0].length\n fltPatLen = fltPat[0].length\n sciPatLen = sciPat[0].length\n errPatLen = errPat[0].length\nend\n
The StateBench.setup
function uses the xxxPat
and xxxPatLen
config
parameters in combination with a local seed
variable to initializing the memBuf
characters at run-time:
def setup()\n auto seed = Utils.getSeed(1)\n auto p = &memBuf[0]\n auto total = 0\n auto pat = \"\"\n auto plen = 0\n while (total + plen + 1) < (memSize - 1)\n if plen\n for auto i = 0; i < plen; i++\n *p++ = pat[i]\n end\n *p++ = ','\n total += plen + 1\n end\n switch ++seed & 0x7\n case 0\n case 1\n case 2\n pat = intPat[(seed >> 3) & 0x3]\n plen = intPatLen\n break\n case 3\n case 4\n pat = fltPat[(seed >> 3) & 0x3]\n plen = fltPatLen\n break\n case 5\n case 6\n pat = sciPat[(seed >> 3) & 0x3]\n plen = sciPatLen\n break\n case 7\n pat = errPat[(seed >> 3) & 0x3]\n plen = errPatLen\n break\n end\n end\nend\n
Details aside, StateBench.run
calls a private scan
function which in turn drives the algorithm's state machine; run
also calls a private scramble
function to \"corrupt\" memBuf
contents ahead of the next scanning cycle:
def run(arg)\n arg = 0x22 if arg < 0x22\n var finalCnt: uint32[NUM_STATES]\n var transCnt: uint32[NUM_STATES]\n for auto i = 0; i < NUM_STATES; i++\n finalCnt[i] = transCnt[i] = 0\n end\n scan(finalCnt, transCnt)\n scramble(Utils.getSeed(1), arg)\n scan(finalCnt, transCnt)\n scramble(Utils.getSeed(2), arg)\n auto crc = Utils.getCrc(Utils.Kind.FINAL)\n for auto i = 0; i < NUM_STATES; i++\n crc = Crc.addU32(finalCnt[i], crc)\n crc = Crc.addU32(transCnt[i], crc)\n end\n return crc\nend\n\ndef scan(finalCnt, transCnt)\n for auto str = &memBuf[0]; *str;\n auto state = nextState(&str, transCnt)\n finalCnt[ord(state)] += 1\n end\nend\n\ndef scramble(seed, step)\n for auto str = &memBuf[0]; str < &memBuf[memSize]; str += <uint16>step\n *str ^= <uint8>seed if *str != ','\n end\nend\n
The crc
returned by StateBench.run
effectively summarizes the number of transitory and finals states encountered when scanning.
even more\u2009 code ahead \u2013 free free to scroll down to the Summary
"},{"location":"advancing/coremark/#list-processing","title":"List processing","text":"Unlike its peer benchmark algorithms, the ListBench module introduces some new design elements into the EM\u2022Mark hierarchy depicted earlier:
the ComparatorI
abstraction, used by ListBench
to generalize its internal implementation of list sorting through a function-valued parameter that compares element values
the ValComparator
module, an implementation of ComparatorI
which invokes the other\u2009 benchmark algorithms (through a proxy
) in a data-dependent fashion
The ComparatorI
interface names just a single function [\u2009compare
\u2009]\u2009; the ListBench
module in turn specifies the signature of this function through a public type [\u2009Comparator
\u2009]\u2009:\u2009(1)
@FunctionalInterface
annotation or a C# delegate
object package em.coremark\n\nimport ListBench\n\ninterface ComparatorI\n\n function compare: ListBench.Comparator\n\nend\n
em.coremark/ListBench.em [exc]module ListBench: BenchAlgI\n\n type Data: struct\n val: int16\n idx: int16\n end\n\n type Comparator: function(a: Data&, b: Data&): int32\n\n config idxCompare: Comparator\n config valCompare: Comparator\n
CoreBench.em$configure
(which we'll examine shortly) performs build-time binding of conformant Comparator
functions to the pair of ListBench
config
parameters declared above. But first, let's look at some private declarations within the ListBench
module:
private:\n\n type Elem: struct\n next: Elem&\n data: Data&\n end\n\n function find(list: Elem&, data: Data&): Elem&\n function pr(list: Elem&, name: string)\n function remove(item: Elem&): Elem&\n function reverse(list: Elem&): Elem&\n function sort(list: Elem&, cmp: Comparator): Elem&\n function unremove(removed: Elem&, modified: Elem&)\n\n config maxElems: uint16\n\n var curHead: Elem&\n\nend\n
The Elem
struct
supports the conventional representation of a singly-linked list, with the ListBench
private functions manipulating references to objects of this type. The maxElems
parameter effectively sizes the pool of Elem
objects, while the curHead
variable references a particular Elem
object that presently anchors the list.
Similar to the other BenchAlgI
modules we've seen, ListBench
cannot fully initialize its internal data structures until setup
fetches a volatile
seed at run-time. Nevertheless, we still can perform a sizeable amount of build-time initialization within em$construct
:
def em$construct()\n auto itemSize = 16 + sizeof<Data>\n maxElems = Math.round(memSize / itemSize) - 3\n curHead = new<Elem>\n curHead.data = new<Data>\n auto p = curHead\n for auto i = 0; i < maxElems - 1; i++\n auto q = p.next = new<Elem>\n q.data = new<Data>\n p = q\n end\n p.data = new<Data>\n p.next = null\nend\n
Like all EM config
params, maxElems
behaves like a var
at build-time but like a const
at run-time; and the value assigned by em$construct
will itself depend on other build-time parameters and variables [\u2009itemSize
, memSize
\u2009].\u2009 In theory, initialization of maxElem
could have occurred at run-time \u2013 and with EM code that looks virtually identical to what we see here.
But by executing this EM code at build-time\u2009, we'll enjoy higher-levels of performance at run-time\u2009.
Taking this facet of EM one step further,(1)em$construct
\"wires up\" a singly-linked chain of newly allocated\u2009/\u2009initialized Elem
objects anchored by the curHead
variable \u2013 a programming idiom you've learned in Data Structures 101\u2009.\u2009 Notice how each Elem.data
field similarly references a newly-allocated (but uninitialized\u2009) Data
object.
Turning now to ListBench.setup
, the pseudo-random values assigned to each element's e.data.val
and e.data.idx
fields originate with one of the volatile
seed variables prescribed by CoreMark.\u2009 Before returning, the private sort
function (which we'll visit shortly) re-orders the list elements by comparing their e.data.idx
fields:
def setup()\n auto seed = Utils.getSeed(1)\n auto ki = 1\n auto kd = maxElems - 3\n auto e = curHead\n e.data.idx = 0\n e.data.val = 0x8080\n for e = e.next; e.next; e = e.next\n auto pat = <uint16>(seed ^ kd) & 0xf\n auto dat = (pat << 3) | (kd & 0x7)\n e.data.val = <int16>((dat << 8) | dat)\n kd -= 1\n if ki < (maxElems / 5)\n e.data.idx = ki++\n else\n pat = <uint16>(seed ^ ki++)\n e.data.idx = <int16>(0x3fff & (((ki & 0x7) << 8) | pat))\n end\n end\n e.data.idx = 0x7fff\n e.data.val = 0xffff\n curHead = sort(curHead, idxCompare)\nend\n
Finally, the following implementation of ListBench.run
calls many private functions [\u2009find
, remove
, reverse
, \u2026\u2009] to continually rearrange the list elements; ListBench.run
also uses another volatile
seed as well as calls sort
with two different Comparator
functions:
def run(arg)\n auto list = curHead\n auto finderIdx = <int16>arg\n auto findCnt = Utils.getSeed(3)\n auto found = <uint16>0\n auto missed = <uint16>0\n auto retval = <Crc.sum_t>0\n var data: Data\n data.idx = finderIdx\n for auto i = 0; i < findCnt; i++\n data.val = <int16>(i & 0xff)\n auto elem = find(list, data)\n list = reverse(list)\n if elem == null\n missed += 1\n retval += <uint16>(list.next.data.val >> 8) & 0x1\n else\n found += 1\n if <uint16>elem.data.val & 0x1\n retval += (<uint16>(elem.data.val >> 9)) & 0x1\n end\n if elem.next != null\n auto tmp = elem.next\n elem.next = tmp.next\n tmp.next = list.next\n list.next = tmp\n end\n end\n data.idx += 1 if data.idx >= 0\n end\n retval += found * 4 - missed\n list = sort(list, valCompare) if finderIdx > 0\n auto remover = remove(list.next)\n auto finder = find(list, &data)\n finder = list.next if !finder\n while finder\n retval = Crc.add16(list.data.val, retval)\n finder = finder.next\n end\n unremove(remover, list.next)\n list = sort(list, idxCompare)\n for auto e = list.next; e; e = e.next\n retval = Crc.add16(list.data.val, retval)\n end\n return retval\nend\n
Refer to ListBench for the definitions of the internal functions called by ListBench.run
\u2009.
As already illustrated, the ListBench.sort
accepts a cmp
argument of type Comparator
\u2013 invoked when merging Data
objects from a pair of sorted sub-lists: (1)
core_list_mergesort
function found in the legacy core_list_join.c
source file.def sort(list, cmp)\n auto insize = <int32>1\n var q: Elem&\n var e: Elem&\n for ;;\n auto p = list\n auto tail = list = null\n auto nmerges = <int32>0 # count number of merges we do in this pass\n while p\n nmerges++ # there exists a merge to be done\n # step `insize' places along from p\n q = p\n auto psize = 0\n for auto i = 0; i < insize; i++\n psize++\n q = q.next\n break if !q\n end\n # if q hasn't fallen off end, we have two lists to merge\n auto qsize = insize\n # now we have two lists; merge them\n while psize > 0 || (qsize > 0 && q)\n # decide whether next element of merge comes from p or q\n if psize == 0\n # p is empty; e must come from q\n e = q\n q = q.next\n qsize--\n elif qsize == 0 || !q\n # q is empty; e must come from p.\n e = p\n p = p.next\n psize--\n elif cmp(p.data, q.data) <= 0\n # First element of p is lower (or same); e must come from p.\n e = p\n p = p.next\n psize--\n else\n # First element of q is lower; e must come from q.\n e = q\n q = q.next\n qsize--\n end\n # add the next element to the merged list\n if tail\n tail.next = e\n else\n list = e\n end\n tail = e\n end\n # now p has stepped `insize' places along, and q has too\n p = q\n end\n tail.next = null\n # If we have done only one merge, we're finished\n break if nmerges <= 1 # allow for nmerges==0, the empty list case\n # Otherwise repeat, merging lists twice the size\n insize *= 2\n end\n return list\nend\n
Looking first at the IdxComparator
module, you couldn't imagine a simpler implementation of its ComparatorI.compare
function \u2013 which returns the signed difference of the idx
fields after scrambling the val
fields:
module IdxComparator: ComparatorI\n\nend\n\ndef compare(a, b)\n a.val = <int16>((<uint16>a.val & 0xff00) | (0x00ff & <uint16>(a.val >> 8)))\n b.val = <int16>((<uint16>b.val & 0xff00) | (0x00ff & <uint16>(b.val >> 8)))\n return a.idx - b.idx\nend\n
Turning now to the ValComparator
module, you couldn't imagine a more convoluted\u2009 implementation of ComparatorI.compare
\u2013 which returns the signed difference of values computed by the private calc
function:\u2009(1)
calc_func
found in the legacy core_list_join.c
source filemodule ValComparator: ComparatorI\n\n proxy Bench0: BenchAlgI\n proxy Bench1: BenchAlgI\n\nprivate:\n\n function calc(pval: int16*): int16\n\nend\n\ndef calc(pval)\n auto val = <uint16>*pval\n auto optype = <uint8>(val >> 7) & 1\n return <int16>(val & 0x007f) if optype\n auto flag = val & 0x7\n auto vtype = (val >> 3) & 0xf\n vtype |= vtype << 4\n var ret: uint16\n switch flag\n case 0\n ret = Bench0.run(<uarg_t>vtype)\n Utils.bindCrc(Bench0.kind(), ret)\n break\n case 1\n ret = Bench1.run(<uarg_t>vtype)\n Utils.bindCrc(Bench1.kind(), ret)\n break\n default\n ret = val\n break\n end\n auto newcrc = Crc.add16(<int16>ret, Utils.getCrc(Utils.Kind.FINAL))\n Utils.setCrc(Utils.Kind.FINAL, Crc.add16(<int16>ret, Utils.getCrc(Utils.Kind.FINAL)))\n ret &= 0x007f\n *pval = <int16>((val & 0xff00) | 0x0080 | ret) ## cache the result\n return <int16>ret\nend\n\ndef compare(a, b)\n auto val1 = calc(&a.val)\n auto val2 = calc(&b.val)\n return val1 - val2\nend\n
Besides scrambling the contents of a val
field reference passed as its argument, calc
actually runs other benchmark algorithms via a pair of BenchAlgI
proxies [\u2009Bench0
, Bench1
\u2009]\u2009.
Having visited most of the individual modules found in the EM\u2022Mark design hierarchy, let's return to CoreBench
and review its build-time configuration functions:
module CoreBench: BenchAlgI\n\n config TOTAL_DATA_SIZE: uint16 = 2000\n config NUM_ALGS: uint8 = 3\n\nend\n\ndef em$configure()\n memSize = Math.floor(TOTAL_DATA_SIZE / NUM_ALGS)\n ListBench.idxCompare ?= IdxComparator.compare\n ListBench.valCompare ?= ValComparator.compare\n ListBench.memSize ?= memSize\n MatrixBench.memSize ?= memSize\n StateBench.memSize ?= memSize\n ValComparator.Bench0 ?= StateBench\n ValComparator.Bench1 ?= MatrixBench\nend\n\ndef em$construct()\n Utils.bindSeedH(1, 0x0)\n Utils.bindSeedH(2, 0x0)\n Utils.bindSeedH(3, 0x66)\nend\n
In addition to calculating and assigning the memSize
config
parameter for each of the benchmarks, CoreBench.em$configure
binds a pair of Comparator
functions to ListBench
as well as binds the StateBench
and MatrixBench
modules to the ValComparator
proxies.
CoreBench.em$construct
completes build-time configuration by binding a prescribed set of values to the volatile
seed variables accessed at run-time by the individual benchmarks.
Whether you've arrived here by studying (or skipping\u2009!!) all of that EM code, let's summarize some key takeaways from the exercise of transforming CoreMark into EM\u2022Mark\u2009:
The CoreMark source code \u2013 written in C with \"plenty of room for improvement\" \u2013 typifies much of the legacy software targeting resource-constrained MCUs.
The high-level design of EM\u2022Mark (depicted here) showcases many aspects of the EM langage \u2013 separation of concerns, client-supplier decoupling, build-time configuration, etc.
The ActiveRunnerP and SleepyRunnerP programs can run on any\u2009 MCU for which an em$distro
package exists \u2013 making EM\u2022Mark ideal for benchmarking MCU performance.
Besides embodying a higher-level of programming, EM\u2022Mark also outperforms\u2009 legacy CoreMark.
To prove our claim about programming in EM, let's move on to the EM\u2022Mark results and allow the numbers to speak for themselves.
"},{"location":"advancing/emmark/","title":"Standard benchmarks for EM","text":"Armed with an understanding of the CoreMark \u21d2 EM\u2022Mark transformation, you'll further appreciate the significance of the EM benchmarks presented below. While CoreMark focuses heavily on measuring CPU performance, EM\u2022Mark takes a more balanced approach that also considers program image size, active power consumption, and overall energy efficiency.
With an emphasis on maximizing execution time, CoreMark programs overwhelmingly employ the most aggressive \"optimize-for-speed\" options when compiling the benchmark sources for a particular MCU \u2013 resulting in excessively large program images.
By way of contrast, EM\u2022Mark starts from the premise that minimizing program size when targeting resource-constrained MCUs trumps other performance factors.\u00a0 Said another way, most embedded firmware developers in practice will usually choose the most aggressive \"optimize-for-size\" option when compiling their code.
LP-EM-CC2340R5Board #2Board #3Texas Instruments reports a score of 2.1 CoreMarks\u2009/\u2009MHz for their CC2340R5 wireless MCU, which features a 48\u2009MHz Cortex-M0+ CPU.\u00a0 TI used the IAR C/C++ compiler [\u2009v9.32.2\u2009] to generate an ~18\u2009K program image, optimized for high-speed with no size constraints.
The legacy CoreMark results we'll report below used TI's LLVM-based Arm compiler [\u2009v2.1.3\u2009] with its \"optimize-for-size\" [\u2009-Oz
\u2009] option. To satisfy our curiousity, building the same Core\u00adMark program using the compiler's \"optimize-for-speed\" [\u2009-Os
\u2009] option yielded comparable results to those reported with IAR.\u2009(1)
All of the EM\u2022Mark results reported below use the ti.cc23xx distro bundle delivered with the EM-SDK plus the following pair of Setups when building program images:\u2009(1)
ti.cc23xx/segger
CLANG\u2009/\u2009LLVM 14.0, optimized for space, code\u2009+\u2009consts in Flash ti.cc23xx/segger_sram
CLANG\u2009/\u2009LLVM 14.0, optimized for space, code\u2009+\u2009consts in SRAM TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
"},{"location":"advancing/emmark/#program-size","title":"Program size","text":"Much like the legacy CoreMark C code, the ActiveRunnerP
EM program performs multiple iterations of the benchmark algorithms and displays results when finished:
package em.coremark\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.mcu import Common\n\nimport CoreBench\nimport Utils\n\nmodule ActiveRunnerP\n\n config ITERATIONS: uint16 = 10\n\nend\n\ndef em$startup()\n CoreBench.setup()\nend\n\ndef em$run()\n AppLed.on()\n Common.BusyWait.wait(250000)\n AppLed.off()\n Common.UsCounter.start()\n %%[d+]\n for auto i = 0; i < ITERATIONS; i++\n CoreBench.run()\n end\n %%[d-]\n auto usecs = Common.UsCounter.stop()\n AppLed.on()\n Common.BusyWait.wait(250000)\n AppLed.off()\n printf \"usecs = %d\\n\", usecs\n printf \"list crc = %04x\\n\", Utils.getCrc(Utils.Kind.LIST)\n printf \"matrix crc = %04x\\n\", Utils.getCrc(Utils.Kind.MATRIX)\n printf \"state crc = %04x\\n\", Utils.getCrc(Utils.Kind.STATE)\n printf \"final crc = %04x\\n\", Utils.getCrc(Utils.Kind.FINAL)\nend\n
Recalling the central role played by the CoreBench
module in the EM\u2022Mark high-level design, the implementation of its setup
and run
functions dominate the code\u2009/\u2009data sizes of the ActiveRunnerP
program image:
EM\u2022Mark Image Size
By way of comparison, the legacy CoreMark program built with TI's compiler weighs in with the following image size:
text (8798)
const (3777)
data (286)
bss (2372)
CoreMark Image Size
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
"},{"location":"advancing/emmark/#execution-time","title":"Execution time","text":"We've used a Saleae Logic Analyzer to capture logic traces of the legacy CoreMark and ActiveRunnerP
programs executing ten iterations of the benchmark. To help measure execution time, both programs blink appLed
for 250\u2009ms before\u2009/\u2009after the main benchmark loop:
Legacy Logic Capture \u2013 Flash
ActiveRunnerP Logic Capture \u2013 Flash
ActiveRunnerP Logic Capture \u2013 SRAM
CoreMark text\u2009+\u2009const [\u2009Flash\u2009] 176\u2009ms EM\u2022Mark text\u2009+\u2009const [\u2009Flash\u2009] 151\u2009ms EM\u2022Mark text\u2009+\u2009const [\u2009SRAM\u2009] 124\u2009msExecution Times \u2013 Summary
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
"},{"location":"advancing/emmark/#active-power","title":"Active power","text":"We've used a Joulescope JS220 to capture power profiles of legacy CoreMark and ActiveRunnerP
executing ten iterations of the benchmark. To help measure power consumption, both programs blink appLed
for 250\u2009ms before\u2009/\u2009after the main benchmark loop:
Legacy Power Capture \u2013 Flash
ActiveRunnerP Power Capture \u2013 Flash
ActiveRunnerP Power Capture \u2013 SRAM
CoreMark text\u2009+\u2009const [\u2009Flash\u2009] 1.368\u2009mJ EM\u2022Mark text\u2009+\u2009const [\u2009Flash\u2009] 1.034\u2009mJ EM\u2022Mark text\u2009+\u2009const [\u2009SRAM\u2009] 0.634\u2009mJExecution Times \u2013 Summary
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
"},{"location":"advancing/emmark/#energy-efficiency","title":"Energy efficiency","text":"Applications targeting resource-constrained (ultra-low-power) MCUs often spend most of their time in deep-sleep \u2013 awakening at rates from once-per-second down to once-per-day, and actively executing for time windows measured in just milliseconds.
The CPU-centric nature of legacy CoreMark (and hence ActiveRunnerP
) doesn't necessarily reflect \"real-world\" duty-cycled applications targeting ULP MCUs, where maximizing energy efficiency becomes paramount.
In addition to legacy CoreMark, the EEMBC organization offers ULPMark \u2013 a benchmark suite that quantifies many aspects of ultra-low-power MCUs. One of the profiles in the suite in fact measures active power consumption, using CoreMark as the workload; other profiles quantify the true energy cost of deep-sleep.
Unlike CoreMark, however, ULPMark requires a paid license to access its source code \u2014 an obstacle for purely inquisitive engineers working with ULP MCUs.\u2009 EM\u2022Mark attempts to fill this niche with a complementary pair of portable programs for benchmarking code size and execution time, as well as power consumption.
To that end, EM\u2022Mark also incorporates the SleepyRunnerP
program \u2013 which executes the same underlying benchmark algorithms as ActiveRunnerP
, but in a very different setting:
package em.coremark\n\nfrom em$distro import BoardC\n\nfrom em.utils import FiberMgr\nfrom em.utils import TickerMgr\n\nimport CoreBench\n\nmodule SleepyRunnerP\n\nprivate:\n\n var ticker: TickerMgr.Ticker&\n\n var count: uint8 = 10\n\n function tickCb: TickerMgr.TickCallback\n\nend\n\ndef em$construct()\n ticker = TickerMgr.createH()\nend\n\ndef em$startup()\n CoreBench.setup()\nend\n\ndef em$run()\n ticker.start(256, tickCb)\n FiberMgr.run()\nend\n\ndef tickCb()\n %%[d+]\n auto crc = CoreBench.run()\n %%[d-]\n printf \"crc = %04x\\n\", crc\n return if --count\n ticker.stop()\n halt\nend\n
Here too, SleepyRunnerP
calls CoreBench.setup
at startup; but instead of the \"main loop\" seen earlier in ActiveRunnerP
, we now make a single\u2009 call to CoreBench.run
just once-per-second. Relying only on modules found in the em.core
bundle [\u2009FiberMgr
, TickerMgr
\u2009], SleepyRunnerP
can execute on any target MCU for which an em$distro
package exists.\u2009(1)
The following sets of Saleae logic-captures first show SleepyRunner
awakening once-per-second, and then zoom-in to view the execution time of a single active cycle:
SleepRunnerP Logic Capture \u2013 SRAM
SleepRunnerP Logic Capture \u2013 SRAM\u2002[\u2009zoom\u2009]
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
Note that measurement M0
reflects the total active time as framed by dbgB
(managed automatically by EM), whereas measurement M1
reflects the actual benchmark interval between the dbgD
toggles seen earlier in SleepyRunnerP
.\u2009 Also note that the latter measurement does not\u2009 include the time to format\u2009/\u2009output the \"crc = 72be\\n\"
character string.
The following sets of Joulescope power-captures report the total amount of energy consumed over a one-second interval, as well as the amount of energy consumed when the SleepyRunnerP
program awakens and executes a single iteration of the benchmark:
SleepRunnerP Power Capture \u2013 SRAM
SleepRunnerP Power Capture \u2013 SRAM\u2002[\u2009zoom\u2009]
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
As expected, a single call to the CoreBench.run
function takes only milliseconds to execute and yet consumes most of the energy over the one-second cycle.
Improvements in EM\u2022Mark program size and execution time compared with legacy CoreMark hopefully supports EM's claim of higher-level programming and higher-levels of performance\u2009.
Building EM\u2022Mark with the most aggressive \"optimize-for-size\" options passed to the underlying C/C++ compiler reflects the reality of targeting resource-constrained MCUs.
Placing runtime code and constants into SRAM (versus Flash) not only improves execution time but also reduces active power consumption \u2014 a corrolary of optimizing for program size.
While ActiveRunnerP
maintains the same focus as legacy CoreMark (inviting side-by-side comparison), the SleepyRunnerP
benchmark more accurately quantifies the energy efficiency of ULP MCUs.
We put forth a rather bold proposition in Using EM:\u2003 If you can't see the problem, you can't fix it\u2009!!!\u00a0 As we toured the EM runtime, logic captures for each example program gave you an important perspective \u2013 that of program state over the course of time. Informally termed \"real-time debug\", this kind of logic trace proves invaluable to both quantify program execution time at sub-\u03bcs resolution, as well as to verify proper sequencing of program execution.
This article expands this proposition into the domain of energy consumption \u2013 by now tracing MCU power over the course of time. With many resource-constrained MCUs targeting always-on applications that run using batteries and\u2009/\u2009or harvested energy, power profiles captured by a precision energy analyzer nicely complement the \"real-time debug\" traces captured by a logic-state analyzer.
Until recently, the cost of a high-quality energy analyzer would often exceed $10,000 \u2013 far beyond most of our budgets. The recent arrival of the Joulescope JS220 precision analyzer does, however, afford order-of-magnitude relief on pricing. And the STM32 Power Shield offers an even more affordable option at <\u2009$100.
Help wanted \u2013 someone familiar with the STM32 Power Shield
For now, though, we'll stick with the Joulescope JS220 as our energy analyzer. And even if you don't own a JS220, we recommend downloading the (free) Joulescope UI software as well as the original power capture files presented throughout this article.
"},{"location":"advancing/energy/#mcu-power-modes","title":"Mcu power modes","text":"The Tour 10 \u2013 Logic Capture seen here in Using EM introduced the terms \"lite-sleep\" and \"deep-sleep\" corresponding to distinct marks on dbgB
. Recalling the Alarm1P example, this program enters the MCU's deep-sleep mode %%[b:2]
after calling alarm.wakeup
. But after calling AppLed.wink
\u2013 which internally pauses execution for a much shorter period of time \u2013 the program instead enters the MCU's lite-sleep mode %%[b:1]
.
While each vendor often has their own jargon for these power modes (IDLE
, PAUSE
, SLEEP
, STOP
, SUSPEND
, \u2026\u2009), we'll uniformally use the following terminology when measuring power across different MCUs supported by EM:
ACTIVE
CPU core running \u2013 MCU peripherals powered on when needed by CPU PAUSE
CPU core idling \u2013 MCU peripherals powered on when needed for CPU wakeup SLEEP
CPU + most peripherals powered off \u2013 wakeup via special \"always-on\" peripherals HIBERNATE
entire MCU powered off \u2013 CPU \"reset\" interrupt triggered by external HW devices only As application software transitions amongst these modes, the power required to operate the MCU can range from milliwatts [ACTIVE
] to microwatts [SLEEP
] \u2013 and even down to nanowatts [HIBERNATE
] if the application wishes to suspend execution indefintely.
To quantify these MCU power modes on your target board, we'll use the Button3P program highlighted in Tour\u200906 to obtain these readings. The Button3P Power Capture image below marks four distinct points during program execution, where we'll use the JS220 to measure the amperage instanteneously drawn by the MCU.
\u00a0 We've pressed appBut
for almost 4\u2009s, with the program testing appBut
every 100\u2009ms. [PAUSE
]
\u00a0 Crossing the 4\u2009s threshold, the program now blinks sysLed
for 40\u2009ms using a busy-wait loop. [ACTIVE
]
\u00a0 The program sleeps until the next button event, even though appBut
itself remains pressed. [SLEEP
]
\u00a0 With the button now released, the program remains asleep while drawing even less current. [SLEEP
]
Button3P Power Capture
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
Because the JS200 samples at a relatively slow 1\u2009MHz rate (compared with the CPU clock), the capture around marker shows a series of ripples spaced 100\u2009ms apart which in fact correspond to very brief CPU wakeups from PAUSE
to sample appBut
; a similar train of blips occurs around markers and , which here represent an internal duty-cycled recharge of the MCU's DC/DC converter or LDO regulator.
By in large, these current measurements align with specifications found in the MCU vendor's datasheet. Do note, however, that the ACTIVE
reading recorded at marker includes current drawn by the board's LED as well as the CPU itself.
While important, MCU power specifications such as [SLEEP
=
1.5
\u03bcA
] or even more generalized forms such as [ACTIVE
=
53
\u03bcA
/
MHz
] say absolutely nothing about the overall energy efficiency of an ultra-low-power embedded system built around this particular MCU. To gain this perspective, we must simultaneously consider the software running on the MCU and answer critical questions such as:
Once awakened, how quickly can our application software go back to sleep\u2009?!?!
By knowing the amount of time a program spends in the various MCU power modes, we can begin to quantify the overall energy efficiency of an embedded system. To illustrate the methodology we'll apply to measure energy consumption, consider the following JS220 capture of the Alarm1P example, which complements the logic capture found here:
LP-EM-CC2340R5Board #2Board #3Alarm1P Power Capture \u2013 Flash
EM Setup:\u00a0ti.cc23xx/segger_default
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
Alarm1P
spends the majority of its time in SLEEP
, typical of many embedded applications.
\u00a0 Once ACTIVE
, the program calls AppLed.wink
and then enters PAUSE
mode for 100\u2009ms;
\u00a0 awoken from PAUSE
, the program calls alarm.wakeup
and moves from ACTIVE
to SLEEP
mode.
\u00a0 The energy (in millijoules) consumed during this 100+\u2009ms SLEEP
\u2009-\u2009ACTIVE
\u2009-\u2009PAUSE
\u2009-\u2009ACTIVE
\u2009-\u2009SLEEP
interval.
\u00a0 The energy (in millijoules) consumed over an arbitrary 10\u2009s interval encompassing six wakeups from SLEEP
.
While time intervals and differ by a factor of 100, the total energy [\u2009mJ
\u2009] consumed over these intervals differ by only a factor of \u2248\u20096. Needless to say, decreasing the number of wakeups over a given timeframe will always improve overall energy efficiency. Often, though, application requirements will dictate the frequency of these wakeup intervals \u2013 as high as once per second in many embedded systems.
Introducing EM hypothesized that reducing code size could have a potentially dramatic impact on the size, power, and cost of future MCU silicon. Focusing on the dimension of power for now \u2013 and targeting legacy MCUs \u2013 we can already quantify the relationship between \"less code\" and \"less energy\".
Needless to say, minimizing the number of CPU instructions executed while ACTIVE
will only reduce overall energy consumption \u2013 assuming our software still meets a given set of application requirements. With its uncanny ability to reduce code size, the EM language and runtime should benchmark quite favorably against more conventional C/C++ RTOS platforms such as Zephyr.
Help wanted \u2013 embedded programmer familiar with Zephyr
Setting aside the larger \"EM vs C/C++\" discussion for now, we'll focus instead on a very effective technique for reducing energy consumption using the same Alarm1P program executing on the same target MCU board.\u00a0 Quite simply, the EM runtime will automatically copy the\u2009.text
and\u2009.const
program sections into fast, on-chip SRAM at startup \u2013 rather than leaving this readonly code\u2009+\u2009data in Flash memory, where they conventionally reside.
To quantify the impact of this change, compare the following Alarm1P Power Capture with our earlier baseline \u2013 paying close attention to the total energy [\u2009mJ
\u2009] consumed at intervals and in each capture.
Alarm1P Power Capture \u2013 SRAM
EM Setup:\u00a0ti.cc23xx/segger_sram
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
While we do see a modest 10% gain in energy efficiency here, don't forget that the Alarm1P
program actually fetches very few instructions \u2013 with the MCU remaining in a (lower-power) PAUSE
mode for most of interval .\u00a0 Imagine, though, a duty-cycled \"sleepy\" application that executes a non-trivial mix of math functions and control code when ACTIVE
:\u00a0 significant improvements during interval would also lower overall energy consumption reported at .
With modern MCUs clocked at \u2248\u200950\u2009-\u2009150\u2009MHz\u2009, these architectures invariably employ a HW cache to mitigate wait-states which would otherwise stall the CPU when fetching instructions or constants directly from slower flash memory. But SRAM has no such limitations, as this class of memory can easily sustain read rates in excess of 500\u2009MHz\u2009. The CPU hence runs at maximum efficiency, allowing the application to re-enter SLEEP
that much sooner.
Assuming the program image can actually fit within SRAM \u2013 a far more scarce resource than flash \u2013 the EM distro for your target MCU board can actually disable the flash memory and its HW cache during em$startup
to further reduce ambient power. Paradoxically, running the CPU at its highest possible clock rate will often decrease overall energy consumption when executing \u2013 again, by minimizing the amount of time spent in the ACTIVE
mode.
In the future, we'll have additional articles under Advancing EM that benchmark more sophisticated EM applications that nevertheless can execute entirely from on-chip SRAM.\u00a0 In the meanwhile, ponder the following question:
Knowing EM applications need only a small SRAM to run, how might we architect future MCUs\u2009???
"},{"location":"advancing/syntax/","title":"Navigating EM syntax diagrams","text":"The following \"railroad track\" diagram captures the complete syntax of EM. Automatically generated from the actual grammar used by the language translator, you can click on any of the blue rectangles (such as Unit
or Decl
) and navigate to the definition of these non-terminal elements; the cyan ovals represent terminal tokens scanned by an underlying lexer.
Hint
To quickly jump to the major sections of the grammar, click through the blue non-terminals within the special definitions at the top of the diagram [\u2009$start
, Decl_$
, Expr_$
, Stmt_$
, Type_$
\u2009]\u2009.
Your browser will not maintain history as you navigate through this diagram. To save time scrolling back to the top of diagram, simply click on the Language Syntax link to reload the entire window.
"},{"location":"cargo/","title":"Index","text":""},{"location":"cargo/#em-bundles","title":"EM bundles","text":""},{"location":"cargo/em.bench/","title":"Index","text":""},{"location":"cargo/em.bench/#bundle-embench","title":"bundle em.bench","text":""},{"location":"cargo/em.bench/em.coremark/","title":"Index","text":""},{"location":"cargo/em.bench/em.coremark/#package-emcoremark","title":"package em.coremark","text":""},{"location":"cargo/em.bench/em.coremark/ActiveRunnerP/","title":"ActiveRunnerP","text":""},{"location":"cargo/em.bench/em.coremark/ActiveRunnerP/#unit-activerunnerp","title":"unit ActiveRunnerP","text":"em.coremark/ActiveRunnerP.empackage em.coremark\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.mcu import Common\n\nimport CoreBench\nimport Utils\n\nmodule ActiveRunnerP\n\n config ITERATIONS: uint16 = 10\n\nend\n\ndef em$startup()\n CoreBench.setup()\nend\n\ndef em$run()\n AppLed.on()\n Common.BusyWait.wait(250000)\n AppLed.off()\n Common.UsCounter.start()\n %%[d+]\n for auto i = 0; i < ITERATIONS; i++\n CoreBench.run()\n end\n %%[d-]\n auto usecs = Common.UsCounter.stop()\n AppLed.on()\n Common.BusyWait.wait(250000)\n AppLed.off()\n printf \"usecs = %d\\n\", usecs\n printf \"list crc = %04x\\n\", Utils.getCrc(Utils.Kind.LIST)\n printf \"matrix crc = %04x\\n\", Utils.getCrc(Utils.Kind.MATRIX)\n printf \"state crc = %04x\\n\", Utils.getCrc(Utils.Kind.STATE)\n printf \"final crc = %04x\\n\", Utils.getCrc(Utils.Kind.FINAL)\nend\n
"},{"location":"cargo/em.bench/em.coremark/BenchAlgI/","title":"BenchAlgI","text":""},{"location":"cargo/em.bench/em.coremark/BenchAlgI/#unit-benchalgi","title":"unit BenchAlgI","text":"em.coremark/BenchAlgI.empackage em.coremark\n\nimport Utils\n\ninterface BenchAlgI\n\n config memSize: uint16\n\n function dump()\n function kind(): Utils.Kind\n function print()\n function run(arg: uarg_t = 0): Utils.sum_t\n function setup()\n\nend\n
"},{"location":"cargo/em.bench/em.coremark/ComparatorI/","title":"ComparatorI","text":""},{"location":"cargo/em.bench/em.coremark/ComparatorI/#unit-comparatori","title":"unit ComparatorI","text":"em.coremark/ComparatorI.empackage em.coremark\n\nimport ListBench\n\ninterface ComparatorI\n\n function compare: ListBench.Comparator\n\nend\n
"},{"location":"cargo/em.bench/em.coremark/CoreBench/","title":"CoreBench","text":""},{"location":"cargo/em.bench/em.coremark/CoreBench/#unit-corebench","title":"unit CoreBench","text":"em.coremark/CoreBench.empackage em.coremark\n\nfrom em.lang import Math\n\nimport BenchAlgI\nimport Crc\nimport ListBench\nimport IdxComparator\nimport MatrixBench\nimport StateBench\nimport Utils\nimport ValComparator\n\nmodule CoreBench: BenchAlgI\n\n config TOTAL_DATA_SIZE: uint16 = 2000\n config NUM_ALGS: uint8 = 3\n\nend\n\ndef em$configure()\n memSize = Math.floor(TOTAL_DATA_SIZE / NUM_ALGS)\n ListBench.idxCompare ?= IdxComparator.compare\n ListBench.valCompare ?= ValComparator.compare\n ListBench.memSize ?= memSize\n MatrixBench.memSize ?= memSize\n StateBench.memSize ?= memSize\n ValComparator.Bench0 ?= StateBench\n ValComparator.Bench1 ?= MatrixBench\nend\n\ndef em$construct()\n Utils.bindSeedH(1, 0x0)\n Utils.bindSeedH(2, 0x0)\n Utils.bindSeedH(3, 0x66)\nend\n\ndef dump()\n ListBench.dump()\n MatrixBench.dump()\n StateBench.dump()\nend\n\ndef kind()\n return Utils.Kind.FINAL\nend\n\ndef print()\n ListBench.print()\n MatrixBench.print()\n StateBench.print()\nend\n\ndef run(arg)\n auto crc = ListBench.run(1)\n Utils.setCrc(Utils.Kind.FINAL, Crc.add16(<int16>crc, Utils.getCrc(Utils.Kind.FINAL)))\n crc = ListBench.run(-1)\n Utils.setCrc(Utils.Kind.FINAL, Crc.add16(<int16>crc, Utils.getCrc(Utils.Kind.FINAL)))\n Utils.bindCrc(Utils.Kind.LIST, Utils.getCrc(Utils.Kind.FINAL))\n return Utils.getCrc(Utils.Kind.FINAL)\nend\n\ndef setup()\n ListBench.setup()\n MatrixBench.setup()\n StateBench.setup()\nend\n
"},{"location":"cargo/em.bench/em.coremark/Crc/","title":"Crc","text":""},{"location":"cargo/em.bench/em.coremark/Crc/#unit-crc","title":"unit Crc","text":"em.coremark/Crc.empackage em.coremark\n\nimport Utils\n\nmodule Crc\n\n type sum_t: Utils.sum_t\n\n function add16(val: int16, crc: sum_t): sum_t\n function addU32(val: uint32, crc: sum_t): sum_t\n\nprivate:\n\n function update(data: uint8, crc: sum_t): sum_t\n\nend\n\ndef add16(val, crc)\n auto v = <uint16>val\n crc = update(<uint8>v, crc)\n crc = update(<uint8>(v >> 8), crc)\n return crc\nend\n\ndef addU32(val, crc)\n crc = add16(<int16>val, crc)\n crc = add16(<int16>(val >> 16), crc)\n return crc\nend\n\ndef update(data, crc)\n auto i = <uint8>0\n auto x16 = <uint8>0\n auto carry = <uint8>0\n for auto i = 0; i < 8; i++\n x16 = <uint8>((data & 1) ^ (<uint8>crc & 1))\n data >>= 1\n if x16 == 1\n crc ^= 0x4002\n carry = 1\n else\n carry = 0\n end\n crc >>= 1\n if carry\n crc |= 0x8000\n else\n crc &= 0x7fff\n end\n end\n return crc\nend\n
"},{"location":"cargo/em.bench/em.coremark/IdxComparator/","title":"IdxComparator","text":""},{"location":"cargo/em.bench/em.coremark/IdxComparator/#unit-idxcomparator","title":"unit IdxComparator","text":"em.coremark/IdxComparator.empackage em.coremark\n\nimport ComparatorI\n\nmodule IdxComparator: ComparatorI\n\nend\n\ndef compare(a, b)\n a.val = <int16>((<uint16>a.val & 0xff00) | (0x00ff & <uint16>(a.val >> 8)))\n b.val = <int16>((<uint16>b.val & 0xff00) | (0x00ff & <uint16>(b.val >> 8)))\n return a.idx - b.idx\nend\n
"},{"location":"cargo/em.bench/em.coremark/ListBench/","title":"ListBench","text":""},{"location":"cargo/em.bench/em.coremark/ListBench/#unit-listbench","title":"unit ListBench","text":"em.coremark/ListBench.empackage em.coremark\n\nfrom em.lang import Math\n\nimport BenchAlgI\nimport Crc\nimport Utils\n\n# patterned after core_list_join.c\n\nmodule ListBench: BenchAlgI\n\n type Data: struct\n val: int16\n idx: int16\n end\n\n type Comparator: function(a: Data&, b: Data&): int32\n\n config idxCompare: Comparator\n config valCompare: Comparator\n\nprivate:\n\n type Elem: struct\n next: Elem&\n data: Data&\n end\n\n function find(list: Elem&, data: Data&): Elem&\n function pr(list: Elem&, name: string)\n function remove(item: Elem&): Elem&\n function reverse(list: Elem&): Elem&\n function sort(list: Elem&, cmp: Comparator): Elem&\n function unremove(removed: Elem&, modified: Elem&)\n\n config maxElems: uint16\n\n var curHead: Elem&\n\nend\n\ndef em$construct()\n auto itemSize = 16 + sizeof<Data>\n maxElems = Math.round(memSize / itemSize) - 3\n curHead = new<Elem>\n curHead.data = new<Data>\n auto p = curHead\n for auto i = 0; i < maxElems - 1; i++\n auto q = p.next = new<Elem>\n q.data = new<Data>\n p = q\n end\n p.data = new<Data>\n p.next = null\nend\n\ndef dump()\n for auto e = curHead; e; e = e.next\n %%[a+]\n %%[>e.data.idx]\n %%[>e.data.val]\n %%[a-]\n end\nend\n\ndef find(list, data)\n auto elem = list\n if data.idx >= 0\n while elem && elem.data.idx != data.idx\n elem = elem.next\n end\n else\n while elem && <int16>(<uint16>elem.data.val & 0xff) != data.val\n elem = elem.next\n end\n end\n return elem\nend\n\ndef kind()\n return Utils.Kind.LIST\nend\n\ndef pr(list, name)\n auto sz = 0\n printf \"%s\\n[\", name\n for auto e = list; e; e = e.next\n auto pre = (sz++ % 8) == 0 ? \"\\n \" : \"\"\n printf \"%s(%04x,%04x)\", <iarg_t>pre, e.data.idx, e.data.val\n end\n printf \"\\n], size = %d\\n\", sz\nend\n\ndef print()\n pr(curHead, \"current\")\nend\n\ndef remove(item)\n auto ret = item.next\n auto tmp = item.data\n item.data = ret.data\n ret.data = tmp\n item.next = item.next.next\n ret.next = null\n return ret\nend\n\ndef reverse(list)\n auto next = <Elem&>null\n while list\n auto tmp = list.next\n list.next = next\n next = list\n list = tmp\n end\n return next\nend\n\n def run(arg)\n auto list = curHead\n auto finderIdx = <int16>arg\n auto findCnt = Utils.getSeed(3)\n auto found = <uint16>0\n auto missed = <uint16>0\n auto retval = <Crc.sum_t>0\n var data: Data\n data.idx = finderIdx\n for auto i = 0; i < findCnt; i++\n data.val = <int16>(i & 0xff)\n auto elem = find(list, data)\n list = reverse(list)\n if elem == null\n missed += 1\n retval += <uint16>(list.next.data.val >> 8) & 0x1\n else\n found += 1\n if <uint16>elem.data.val & 0x1\n retval += (<uint16>(elem.data.val >> 9)) & 0x1\n end\n if elem.next != null\n auto tmp = elem.next\n elem.next = tmp.next\n tmp.next = list.next\n list.next = tmp\n end\n end\n data.idx += 1 if data.idx >= 0\n end\n retval += found * 4 - missed\n list = sort(list, valCompare) if finderIdx > 0\n auto remover = remove(list.next)\n auto finder = find(list, &data)\n finder = list.next if !finder\n while finder\n retval = Crc.add16(list.data.val, retval)\n finder = finder.next\n end\n unremove(remover, list.next)\n list = sort(list, idxCompare)\n for auto e = list.next; e; e = e.next\n retval = Crc.add16(list.data.val, retval)\n end\n return retval\nend\n\ndef setup()\n auto seed = Utils.getSeed(1)\n auto ki = 1\n auto kd = maxElems - 3\n auto e = curHead\n e.data.idx = 0\n e.data.val = 0x8080\n for e = e.next; e.next; e = e.next\n auto pat = <uint16>(seed ^ kd) & 0xf\n auto dat = (pat << 3) | (kd & 0x7)\n e.data.val = <int16>((dat << 8) | dat)\n kd -= 1\n if ki < (maxElems / 5)\n e.data.idx = ki++\n else\n pat = <uint16>(seed ^ ki++)\n e.data.idx = <int16>(0x3fff & (((ki & 0x7) << 8) | pat))\n end\n end\n e.data.idx = 0x7fff\n e.data.val = 0xffff\n curHead = sort(curHead, idxCompare)\nend\n\ndef sort(list, cmp)\n auto insize = <int32>1\n var q: Elem&\n var e: Elem&\n for ;;\n auto p = list\n auto tail = list = null\n auto nmerges = <int32>0 # count number of merges we do in this pass\n while p\n nmerges++ # there exists a merge to be done\n # step `insize' places along from p\n q = p\n auto psize = 0\n for auto i = 0; i < insize; i++\n psize++\n q = q.next\n break if !q\n end\n # if q hasn't fallen off end, we have two lists to merge\n auto qsize = insize\n # now we have two lists; merge them\n while psize > 0 || (qsize > 0 && q)\n # decide whether next element of merge comes from p or q\n if psize == 0\n # p is empty; e must come from q\n e = q\n q = q.next\n qsize--\n elif qsize == 0 || !q\n # q is empty; e must come from p.\n e = p\n p = p.next\n psize--\n elif cmp(p.data, q.data) <= 0\n # First element of p is lower (or same); e must come from p.\n e = p\n p = p.next\n psize--\n else\n # First element of q is lower; e must come from q.\n e = q\n q = q.next\n qsize--\n end\n # add the next element to the merged list\n if tail\n tail.next = e\n else\n list = e\n end\n tail = e\n end\n # now p has stepped `insize' places along, and q has too\n p = q\n end\n tail.next = null\n # If we have done only one merge, we're finished\n break if nmerges <= 1 # allow for nmerges==0, the empty list case\n # Otherwise repeat, merging lists twice the size\n insize *= 2\n end\n return list\nend\n\ndef unremove(removed, modified)\n auto tmp = removed.data\n removed.data = modified.data\n modified.data = tmp\n removed.next = modified.next\n modified.next = removed\nend\n
"},{"location":"cargo/em.bench/em.coremark/MatrixBench/","title":"MatrixBench","text":""},{"location":"cargo/em.bench/em.coremark/MatrixBench/#unit-matrixbench","title":"unit MatrixBench","text":"em.coremark/MatrixBench.empackage em.coremark\n\nimport BenchAlgI\nimport Crc\nimport Utils\n\n# patterned after core_matrix.c\n\nmodule MatrixBench: BenchAlgI\n\nprivate:\n\n type matdat_t: int16\n type matres_t: int32\n\n config dimN: uint8\n\n var matA: matdat_t[]\n var matB: matdat_t[]\n var matC: matres_t[]\n\n function addVal(val: matdat_t)\n function mulVal(val: matdat_t)\n function mulMat()\n function mulMatBix()\n function mulVec()\n function sumDat(clipval: matdat_t): matdat_t\n\n function bix(res: matres_t, lower: uint8, upper: uint8): matres_t\n function clip(d: matdat_t, b: bool): matdat_t\n function enlarge(val: matdat_t): matdat_t\n\n function prDat(lab: string, mat: matdat_t[])\n function prRes(lab: string)\n\nend\n\ndef em$construct()\n auto i = 0\n auto j = 0\n while j < memSize\n i += 1\n j = i * i * 2 * 4\n end\n dimN = i - 1\n matA.length = matB.length = matC.length = dimN * dimN\nend\n\ndef addVal(val)\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n matA[i * dimN + j] += val\n end\n end\nend\n\ndef bix(res, lower, upper)\n auto r = <uint32>res\n auto l = <uint32>lower\n auto u = <uint32>upper\n return <matres_t>((r >> l) & (~(0xffffffff << u)))\nend\n\ndef clip(d, b)\n auto x = <uint16>d\n return <matdat_t>(x & (b ? 0x0ff : 0x0ffff))\nend\n\ndef dump()\n ## TODO -- implement\nend\n\ndef enlarge(val)\n auto v = <uint16>val\n return <matdat_t>(0xf000 | v)\nend\n\ndef kind()\n return Utils.Kind.MATRIX\nend\n\ndef mulVal(val)\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n matC[i * dimN + j] = <matres_t>matA[i * dimN + j] * <matres_t>val\n end\n end\nend\n\ndef mulMat()\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n matC[i * dimN + j] = 0\n for auto k = 0; k < dimN; k++\n matC[i * dimN + j] += <matres_t>matA[i * dimN + k] * <matres_t>matB[k * dimN + j]\n end\n end\n end\nend\n\ndef mulMatBix()\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n matC[i * dimN + j] = 0\n for auto k = 0; k < dimN; k++\n auto tmp = <matres_t>matA[i * dimN + k] * <matres_t>matB[k * dimN + j]\n matC[i * dimN + j] += bix(tmp, 2, 4) * bix(tmp, 5, 7)\n end\n end\n end\nend\n\ndef mulVec()\n for auto i = 0; i < dimN; i++\n matC[i] = 0\n for auto j = 0; j < dimN; j++\n matC[i] += <matres_t>matA[i * dimN + j] * <matres_t>matB[j]\n end\n end\nend\n\ndef print()\n prDat(\"A\", matA)\n prDat(\"B\", matB)\nend\n\ndef prDat(lab, mat)\n printf \"\\n%s:\\n \", lab\n for auto i = 0; i < dimN; i++\n auto sep = \"\"\n for auto j = 0; j < dimN; j++\n printf \"%s%d\", sep, mat[i * dimN + j]\n sep = \",\"\n end\n printf \"\\n \"\n end\nend\n\ndef prRes(lab)\n printf \"\\n%s:\\n \", lab\n for auto i = 0; i < dimN; i++\n auto sep = \"\"\n for auto j = 0; j < dimN; j++\n printf \"%s%d\", sep, matC[i * dimN + j]\n sep = \",\"\n end\n printf \"\\n \"\n end\nend\n\ndef run(arg)\n auto crc = <Crc.sum_t>0\n auto val = <matdat_t>arg\n auto clipval = enlarge(val)\n #\n addVal(val)\n mulVal(val)\n crc = Crc.add16(sumDat(clipval), crc)\n #\n mulVec()\n crc = Crc.add16(sumDat(clipval), crc)\n #\n mulMat()\n crc = Crc.add16(sumDat(clipval), crc)\n #\n mulMatBix()\n crc = Crc.add16(sumDat(clipval), crc)\n #\n addVal(-val)\n return Crc.add16(<int16>crc, Utils.getCrc(Utils.Kind.FINAL))\nend\n\ndef setup()\n auto s32 = <uint32>Utils.getSeed(1) | (<uint32>Utils.getSeed(2) << 16)\n auto sd = <matdat_t>s32\n sd = 1 if sd == 0\n auto order = <matdat_t>1\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n sd = <int16>((order * sd) % 65536)\n auto val = <matdat_t>(sd + order)\n val = clip(val, false)\n matB[i * dimN + j] = val\n val += order\n val = clip(val, true)\n matA[i * dimN + j] = val\n order += 1\n end\n end\nend\n\ndef sumDat(clipval)\n auto cur = <matres_t>0\n auto prev = <matres_t>0\n auto tmp = <matres_t>0\n auto ret = <matdat_t>0\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n cur = matC[i * dimN + j]\n tmp += cur\n if tmp > clipval\n ret += 10\n tmp = 0\n else\n ret += (cur > prev) ? 1 : 0\n end\n prev = cur\n end\n end\n return ret\nend\n
"},{"location":"cargo/em.bench/em.coremark/SleepyRunnerP/","title":"SleepyRunnerP","text":""},{"location":"cargo/em.bench/em.coremark/SleepyRunnerP/#unit-sleepyrunnerp","title":"unit SleepyRunnerP","text":"em.coremark/SleepyRunnerP.empackage em.coremark\n\nfrom em$distro import BoardC\n\nfrom em.utils import FiberMgr\nfrom em.utils import TickerMgr\n\nimport CoreBench\n\nmodule SleepyRunnerP\n\nprivate:\n\n var ticker: TickerMgr.Ticker&\n\n var count: uint8 = 10\n\n function tickCb: TickerMgr.TickCallback\n\nend\n\ndef em$construct()\n ticker = TickerMgr.createH()\nend\n\ndef em$startup()\n CoreBench.setup()\nend\n\ndef em$run()\n ticker.start(256, tickCb)\n FiberMgr.run()\nend\n\ndef tickCb()\n %%[d+]\n auto crc = CoreBench.run()\n %%[d-]\n printf \"crc = %04x\\n\", crc\n return if --count\n ticker.stop()\n halt\nend\n
"},{"location":"cargo/em.bench/em.coremark/StateBench/","title":"StateBench","text":""},{"location":"cargo/em.bench/em.coremark/StateBench/#unit-statebench","title":"unit StateBench","text":"em.coremark/StateBench.empackage em.coremark\n\nimport BenchAlgI\nimport Crc\nimport Utils\n\n# patterned after core_state.c\n\nmodule StateBench: BenchAlgI\n\nprivate:\n\n const NUM_STATES: uint8 = 8\n\n type StringBuf: char*\n\n type State: enum\n START,\n INVALID,\n S1,\n S2,\n INT,\n FLOAT,\n EXPONENT,\n SCIENTIFIC,\n end\n\n config intPat: string[4] = [\n \"5012\", \"1234\", \"-874\", \"+122\"\n ]\n config fltPat: string[4] = [\n \"35.54400\", \".1234500\", \"-110.700\", \"+0.64400\"\n ]\n config sciPat: string[4] = [\n \"5.500e+3\", \"-.123e-2\", \"-87e+832\", \"+0.6e-12\"\n ]\n config errPat: string[4] = [\n \"T0.3e-1F\", \"-T.T++Tq\", \"1T3.4e4z\", \"34.0e-T^\"\n ]\n\n config intPatLen: uint16\n config fltPatLen: uint16\n config sciPatLen: uint16\n config errPatLen: uint16\n\n var memBuf: char[]\n\n function isDigit(ch: char): bool\n function nextState(pStr: StringBuf*, transCnt: uint32[]): State\n function ord(state: State): uint8\n function scan(finalCnt: uint32[], transCnt: uint32[])\n function scramble(seed: Utils.seed_t, step: uarg_t)\n\nend\n\ndef em$construct()\n memBuf.length = memSize\n intPatLen = intPat[0].length\n fltPatLen = fltPat[0].length\n sciPatLen = sciPat[0].length\n errPatLen = errPat[0].length\nend\n\ndef dump()\n ## TODO -- implement\nend\n\ndef isDigit(ch)\n return ch >= '0' && ch <= '9'\nend\n\ndef kind()\n return Utils.Kind.STATE\nend\n\ndef nextState(pStr, transCnt)\n auto str = *pStr\n auto state = State.START\n for ; *str && state != State.INVALID; str++\n auto ch = *str\n if ch == ','\n str++\n break\n end\n switch state\n case State.START\n if isDigit(ch)\n state = State.INT\n elif ch == '+' || ch == '-'\n state = State.S1\n elif ch == '.'\n state = State.FLOAT\n else\n state = State.INVALID\n transCnt[ord(State.INVALID)] += 1\n end\n transCnt[ord(State.START)] += 1\n break\n case State.S1\n if isDigit(ch)\n state = State.INT\n transCnt[ord(State.S1)] += 1\n elif ch == '.'\n state = State.FLOAT\n transCnt[ord(State.S1)] += 1\n else\n state = State.INVALID\n transCnt[ord(State.S1)] += 1\n end\n break\n case State.INT\n if ch == '.'\n state = State.FLOAT\n transCnt[ord(State.INT)] += 1\n elif !isDigit(ch)\n state = State.INVALID\n transCnt[ord(State.INT)] += 1\n end\n break\n case State.FLOAT\n if ch == 'E' || ch == 'e'\n state = State.S2\n transCnt[ord(State.FLOAT)] += 1\n elif !isDigit(ch)\n state = State.INVALID\n transCnt[ord(State.FLOAT)] += 1\n end\n break\n case State.S2\n if ch == '+' || ch == '-'\n state = State.EXPONENT\n transCnt[ord(State.S2)] += 1\n else\n state = State.INVALID\n transCnt[ord(State.S2)] += 1\n end\n break\n case State.EXPONENT\n if isDigit(ch)\n state = State.SCIENTIFIC\n transCnt[ord(State.EXPONENT)] += 1\n else\n state = State.INVALID\n transCnt[ord(State.EXPONENT)] += 1\n end\n break\n case State.SCIENTIFIC\n if !isDigit(ch)\n state = State.INVALID\n transCnt[ord(State.INVALID)] += 1\n end\n break\n end\n end\n *pStr = str\n return state\nend\n\ndef ord(state)\n return <uint8>state\nend\n\ndef print()\n auto p = &memBuf[0]\n auto cnt = 0\n printf \"\\n%c\", '\"'\n while *p\n if (cnt++ % 8) == 0\n printf \"\\n \"\n end\n var c: char\n while (c = *p++) != ','\n printf \"%c\", c\n end\n printf \", \"\n end\n printf \"\\n%c, count = %d\\n\", '\"', cnt\nend\n\ndef run(arg)\n arg = 0x22 if arg < 0x22\n var finalCnt: uint32[NUM_STATES]\n var transCnt: uint32[NUM_STATES]\n for auto i = 0; i < NUM_STATES; i++\n finalCnt[i] = transCnt[i] = 0\n end\n scan(finalCnt, transCnt)\n scramble(Utils.getSeed(1), arg)\n scan(finalCnt, transCnt)\n scramble(Utils.getSeed(2), arg)\n auto crc = Utils.getCrc(Utils.Kind.FINAL)\n for auto i = 0; i < NUM_STATES; i++\n crc = Crc.addU32(finalCnt[i], crc)\n crc = Crc.addU32(transCnt[i], crc)\n end\n return crc\nend\n\ndef scan(finalCnt, transCnt)\n for auto str = &memBuf[0]; *str;\n auto state = nextState(&str, transCnt)\n finalCnt[ord(state)] += 1\n end\nend\n\ndef scramble(seed, step)\n for auto str = &memBuf[0]; str < &memBuf[memSize]; str += <uint16>step\n *str ^= <uint8>seed if *str != ','\n end\nend\n\ndef setup()\n auto seed = Utils.getSeed(1)\n auto p = &memBuf[0]\n auto total = 0\n auto pat = \"\"\n auto plen = 0\n while (total + plen + 1) < (memSize - 1)\n if plen\n for auto i = 0; i < plen; i++\n *p++ = pat[i]\n end\n *p++ = ','\n total += plen + 1\n end\n switch ++seed & 0x7\n case 0\n case 1\n case 2\n pat = intPat[(seed >> 3) & 0x3]\n plen = intPatLen\n break\n case 3\n case 4\n pat = fltPat[(seed >> 3) & 0x3]\n plen = fltPatLen\n break\n case 5\n case 6\n pat = sciPat[(seed >> 3) & 0x3]\n plen = sciPatLen\n break\n case 7\n pat = errPat[(seed >> 3) & 0x3]\n plen = errPatLen\n break\n end\n end\nend\n
"},{"location":"cargo/em.bench/em.coremark/Utils/","title":"Utils","text":""},{"location":"cargo/em.bench/em.coremark/Utils/#unit-utils","title":"unit Utils","text":"em.coremark/Utils.empackage em.coremark\n\nmodule Utils\n\n const NUM_SEEDS: uint8 = 5\n\n type Kind: enum\n FINAL, LIST, MATRIX, STATE, ZZZ_\n end\n\n type seed_t: uint16 volatile\n type sum_t: uint16\n\n function bindCrc(kind: Kind, crc: sum_t)\n function getCrc(kind: Kind): sum_t\n function setCrc(kind: Kind, crc: sum_t)\n\n host function bindSeedH(idx: uint8, val: seed_t)\n function getSeed(idx: uint8): seed_t\n\nprivate:\n\n var crcTab: sum_t[]\n var seedTab: seed_t[NUM_SEEDS]\n\nend\n\ndef em$construct()\n crcTab.length = <uint16>Kind.ZZZ_\nend\n\ndef bindCrc(kind, crc)\n auto p = &crcTab[<uint16>kind]\n *p = crc if *p == 0\nend\n\ndef bindSeedH(idx, val)\n seedTab[idx - 1] = val\nend\n\ndef getCrc(kind)\n return crcTab[<uint16>kind]\nend\n\ndef getSeed(idx)\n return seedTab[idx - 1]\nend\n\ndef setCrc(kind, crc)\n crcTab[<uint16>kind] = crc\nend\n
"},{"location":"cargo/em.bench/em.coremark/ValComparator/","title":"ValComparator","text":""},{"location":"cargo/em.bench/em.coremark/ValComparator/#unit-valcomparator","title":"unit ValComparator","text":"em.coremark/ValComparator.empackage em.coremark\n\nimport BenchAlgI\nimport ComparatorI\nimport Crc\nimport Utils\n\nmodule ValComparator: ComparatorI\n\n proxy Bench0: BenchAlgI\n proxy Bench1: BenchAlgI\n\nprivate:\n\n function calc(pval: int16*): int16\n\nend\n\ndef calc(pval)\n auto val = <uint16>*pval\n auto optype = <uint8>(val >> 7) & 1\n return <int16>(val & 0x007f) if optype\n auto flag = val & 0x7\n auto vtype = (val >> 3) & 0xf\n vtype |= vtype << 4\n var ret: uint16\n switch flag\n case 0\n ret = Bench0.run(<uarg_t>vtype)\n Utils.bindCrc(Bench0.kind(), ret)\n break\n case 1\n ret = Bench1.run(<uarg_t>vtype)\n Utils.bindCrc(Bench1.kind(), ret)\n break\n default\n ret = val\n break\n end\n auto newcrc = Crc.add16(<int16>ret, Utils.getCrc(Utils.Kind.FINAL))\n Utils.setCrc(Utils.Kind.FINAL, Crc.add16(<int16>ret, Utils.getCrc(Utils.Kind.FINAL)))\n ret &= 0x007f\n *pval = <int16>((val & 0xff00) | 0x0080 | ret) ## cache the result\n return <int16>ret\nend\n\ndef compare(a, b)\n auto val1 = calc(&a.val)\n auto val2 = calc(&b.val)\n return val1 - val2\nend\n
"},{"location":"cargo/em.core/","title":"Index","text":""},{"location":"cargo/em.core/#bundle-emcore","title":"bundle em.core","text":""},{"location":"cargo/em.core/em.hal/","title":"Index","text":""},{"location":"cargo/em.core/em.hal/#package-emhal","title":"package em.hal","text":""},{"location":"cargo/em.core/em.hal/BusyWaitI/","title":"BusyWaitI","text":""},{"location":"cargo/em.core/em.hal/BusyWaitI/#unit-busywaiti","title":"unit BusyWaitI","text":"em.hal/BusyWaitI.empackage em.hal\n\ninterface BusyWaitI\n # ^| abstraction of a spin-loop\n function wait(usecs: uint32)\n # ^| enter a spin-loop\n # ^| @usecs - duration in microseconds\nend\n
"},{"location":"cargo/em.core/em.hal/BusyWaitN/","title":"BusyWaitN","text":""},{"location":"cargo/em.core/em.hal/BusyWaitN/#unit-busywaitn","title":"unit BusyWaitN","text":"em.hal/BusyWaitN.empackage em.hal\n\nimport BusyWaitI\n\nmodule BusyWaitN: BusyWaitI\n # ^| Nil implementation of the BusyWaitI interface\nend\n\ndef wait(usecs)\nend\n
"},{"location":"cargo/em.core/em.hal/ButtonI/","title":"ButtonI","text":""},{"location":"cargo/em.core/em.hal/ButtonI/#unit-buttoni","title":"unit ButtonI","text":"em.hal/ButtonI.empackage em.hal\n\ninterface ButtonI\n # ^| abstraction of a pressable button\n type OnPressedCB: function()\n # ^| signature of a button's callback function\n function isPressed(): bool\n # ^| test whether this button is currently pressed\n function onPressed(cb: OnPressedCB, minDurationMs: uint16 = 100, maxDurationMs: uint16 = 4000)\n # ^| bind a callback to this button\n # ^| @cb - callback function, executed when this button is pressed\n # ^| @minDurationMs - minimum time in millisecs before executing this button's callback\n # ^| @maxDurationMs - maximum time in millisecs, after which this button's callback is executed\n\nend\n
"},{"location":"cargo/em.core/em.hal/ButtonN/","title":"ButtonN","text":""},{"location":"cargo/em.core/em.hal/ButtonN/#unit-buttonn","title":"unit ButtonN","text":"em.hal/ButtonN.empackage em.hal\n\nimport ButtonI\n\nmodule ButtonN: ButtonI\n # ^| Nil implementation of the ButtonI interface\nend\n\ndef isPressed()\n return false\nend\n\ndef onPressed(cb, minDurationMs, maxDurationMs)\nend\n
"},{"location":"cargo/em.core/em.hal/ConsoleUartI/","title":"ConsoleUartI","text":""},{"location":"cargo/em.core/em.hal/ConsoleUartI/#unit-consoleuarti","title":"unit ConsoleUartI","text":"em.hal/ConsoleUartI.empackage em.hal\n\ninterface ConsoleUartI \n\n host function setBaudH(rate: uint32)\n\n function flush()\n function put(data: uint8)\n\nend\n
"},{"location":"cargo/em.core/em.hal/ConsoleUartN/","title":"ConsoleUartN","text":""},{"location":"cargo/em.core/em.hal/ConsoleUartN/#unit-consoleuartn","title":"unit ConsoleUartN","text":"em.hal/ConsoleUartN.empackage em.hal\n\nimport ConsoleUartI\n\nmodule ConsoleUartN: ConsoleUartI\n # ^| Nil implementation of the ConsoleUartI interface\nend\n\ndef setBaudH(rate)\nend\n\ndef flush()\nend\n\ndef put(data)\nend\n
"},{"location":"cargo/em.core/em.hal/CopierI/","title":"CopierI","text":""},{"location":"cargo/em.core/em.hal/CopierI/#unit-copieri","title":"unit CopierI","text":"em.hal/CopierI.empackage em.hal\n\ninterface CopierI\n\n function exec(dst: ptr_t, src: ptr_t, cnt: uint16)\n\nend\n
"},{"location":"cargo/em.core/em.hal/FlashI/","title":"FlashI","text":""},{"location":"cargo/em.core/em.hal/FlashI/#unit-flashi","title":"unit FlashI","text":"em.hal/FlashI.empackage em.hal\n\ninterface FlashI\n\n host function getSectorSizeH(): uint32\n host function getWriteChunkH(): uint32\n\n function erase(addr: addr_t, upto: addr_t = 0)\n function write(addr: addr_t, data: ptr_t, len: uint32): addr_t\n\nend\n
"},{"location":"cargo/em.core/em.hal/FlashN/","title":"FlashN","text":""},{"location":"cargo/em.core/em.hal/FlashN/#unit-flashn","title":"unit FlashN","text":"em.hal/FlashN.empackage em.hal\n\nimport FlashI\n\nmodule FlashN: FlashI\n # ^| Nil implementation of the FlashI interface\nend\n\ndef getSectorSizeH()\n return 0\nend\n\ndef getWriteChunkH()\n return 0\nend\n\ndef erase(addr, upto)\nend\n\ndef write(addr, data, len)\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/GlobalInterruptsI/","title":"GlobalInterruptsI","text":""},{"location":"cargo/em.core/em.hal/GlobalInterruptsI/#unit-globalinterruptsi","title":"unit GlobalInterruptsI","text":"em.hal/GlobalInterruptsI.empackage em.hal\n\n#! Interface implemented by a GlobalInterrupts module for a device.\n\ninterface GlobalInterruptsI \n\n type Key: uarg_t\n\n #! Disables interrupts and saves state\n function disable(): Key\n\n #! Enables interrupts\n function enable()\n\n #! Restores interrupts to previous state\n function restore(key: Key)\nend\n
"},{"location":"cargo/em.core/em.hal/GlobalInterruptsN/","title":"GlobalInterruptsN","text":""},{"location":"cargo/em.core/em.hal/GlobalInterruptsN/#unit-globalinterruptsn","title":"unit GlobalInterruptsN","text":"em.hal/GlobalInterruptsN.empackage em.hal\n\nimport GlobalInterruptsI\n\nmodule GlobalInterruptsN: GlobalInterruptsI\n # ^| Nil implementation of the GlobalInterruptsI interface\nend\n\ndef disable()\n return 0\nend\n\ndef enable()\nend\n\ndef restore(key)\nend\n
"},{"location":"cargo/em.core/em.hal/GpioEdgeDetectMinI/","title":"GpioEdgeDetectMinI","text":""},{"location":"cargo/em.core/em.hal/GpioEdgeDetectMinI/#unit-gpioedgedetectmini","title":"unit GpioEdgeDetectMinI","text":"em.hal/GpioEdgeDetectMinI.empackage em.hal\n\nimport GpioI\n\ninterface GpioEdgeDetectMinI: GpioI \n # ^| extends the GpioI abstraction with edge-detection features\n type Handler: function ()\n # ^| signature of an edge-detection function\n host function setDetectHandlerH(h: Handler)\n # ^| bind a handler to this GPIO at build-time\n function clearDetect()\n # ^| clear (acknowledge) any edge-detection by this GPIO\n function disableDetect() \n # ^| disable edge-detection by this GPIO\n function enableDetect()\n # ^| enable edge-detection by this GPIO\n function setDetectFallingEdge()\n # ^| detect high-to-low transitions by this GPIO\n function setDetectRisingEdge()\n # ^| detect low-to-high transitions by this GPIO\nend\n
"},{"location":"cargo/em.core/em.hal/GpioEdgeDetectMinN/","title":"GpioEdgeDetectMinN","text":""},{"location":"cargo/em.core/em.hal/GpioEdgeDetectMinN/#unit-gpioedgedetectminn","title":"unit GpioEdgeDetectMinN","text":"em.hal/GpioEdgeDetectMinN.empackage em.hal\n\nimport GpioEdgeDetectMinI\n\nmodule GpioEdgeDetectMinN: GpioEdgeDetectMinI\n # ^| Nil implementation of the GpioEdgeDetectMinI interface\nend\n\ndef set()\nend\n\ndef clear()\nend\n\ndef toggle()\nend\n\ndef get()\n return false\nend\n\ndef makeInput()\nend\n\ndef isInput()\n return false\nend\n\ndef makeOutput()\nend\n\ndef isOutput()\n return false\nend\n\ndef functionSelect(select)\nend\n\ndef setInternalPullup(state)\nend\n\ndef pinId()\n return 0\nend\n\ndef reset()\nend\n\ndef enableDetect()\nend\n\ndef disableDetect()\nend\n\ndef clearDetect()\nend\n\ndef setDetectRisingEdge()\nend\n\ndef setDetectFallingEdge()\nend\n\ndef setDetectHandlerH(h)\nend\n
"},{"location":"cargo/em.core/em.hal/GpioI/","title":"GpioI","text":""},{"location":"cargo/em.core/em.hal/GpioI/#unit-gpioi","title":"unit GpioI","text":"em.hal/GpioI.empackage em.hal\n\ninterface GpioI\n # ^| abstraction of a GPIO pin\n function clear()\n # ^| clear the value of this GPIO (low)\n function functionSelect (select: uint8)\n # ^| select an alternative function of this GPIO\n function get(): bool\n # ^| get the value of this GPIO\n function isInput(): bool\n # ^| test if this GPIO is an input pin\n function isOutput(): bool\n # ^| test if this GPIO is an output pin\n function makeInput()\n # ^| make this GPIO an input pin\n function makeOutput()\n # ^| make this GPIO an output pin\n function pinId(): int16\n # ^| Return the pin ID of this GPIO\n function reset()\n # ^| Reset this GPIO\n function set()\n # ^| set the value of this GPIO (high)\n function setInternalPullup (state: bool)\n # ^| enable/disable the internalpullup for this GPIO\n function toggle()\n # ^| toggle the value of this GPIO\n\nend\n
"},{"location":"cargo/em.core/em.hal/GpioN/","title":"GpioN","text":""},{"location":"cargo/em.core/em.hal/GpioN/#unit-gpion","title":"unit GpioN","text":"em.hal/GpioN.empackage em.hal\n\nimport GpioI\n\nmodule GpioN: GpioI\n # ^| Nil implementation of the GpioI interface\nend\n\ndef set()\nend\n\ndef clear()\nend\n\ndef toggle()\nend\n\ndef get()\n return false\nend\n\ndef makeInput()\nend\n\ndef isInput()\n return false\nend\n\ndef makeOutput()\nend\n\ndef isOutput()\n return false\nend\n\ndef functionSelect(select)\nend\n\ndef setInternalPullup(state)\nend\n\ndef pinId()\n return 0\nend\n\ndef reset()\nend\n
"},{"location":"cargo/em.core/em.hal/HostUartI/","title":"HostUartI","text":""},{"location":"cargo/em.core/em.hal/HostUartI/#unit-hostuarti","title":"unit HostUartI","text":"em.hal/HostUartI.empackage em.hal\n\nimport ConsoleUartI\n\ninterface HostUartI: ConsoleUartI\n\n type RxHandler: function(b: uint8)\n\n function disable()\n function enable()\n function get(): uint8\n\n host function setRxHandlerH(handler: RxHandler)\n\nend\n
"},{"location":"cargo/em.core/em.hal/HostUartN/","title":"HostUartN","text":""},{"location":"cargo/em.core/em.hal/HostUartN/#unit-hostuartn","title":"unit HostUartN","text":"em.hal/HostUartN.empackage em.hal\n\nimport HostUartI\n\nmodule HostUartN: HostUartI\n # ^| Nil implementation of the HostUartI interface\nend\n\ndef setBaudH(rate)\n ## TODO -- implement\nend\n\ndef flush()\n ## TODO -- implement\nend\n\ndef put(data)\n ## TODO -- implement\nend\n\ndef disable()\n ## TODO -- implement\nend\n\ndef enable()\n ## TODO -- implement\nend\n\ndef get()\n ## TODO -- implement\n return 0\nend\n\ndef setRxHandlerH(handler)\n ## TODO -- implement\nend\n
"},{"location":"cargo/em.core/em.hal/IdleI/","title":"IdleI","text":""},{"location":"cargo/em.core/em.hal/IdleI/#unit-idlei","title":"unit IdleI","text":"em.hal/IdleI.empackage em.hal\n\n#! Should be implemented by an Idle module\n\ninterface IdleI \n\n #! This function defines how the system idles\n function exec()\n\n #! This function is called during \"warm\" wakeups\n function wakeup()\nend\n
"},{"location":"cargo/em.core/em.hal/IdleN/","title":"IdleN","text":""},{"location":"cargo/em.core/em.hal/IdleN/#unit-idlen","title":"unit IdleN","text":"em.hal/IdleN.empackage em.hal\n\nimport IdleI\n\nmodule IdleN: IdleI\n # ^| Nil implementation of the IdleI interface\nend\n\ndef exec()\nend\n\ndef wakeup()\nend\n
"},{"location":"cargo/em.core/em.hal/InterruptSourceI/","title":"InterruptSourceI","text":""},{"location":"cargo/em.core/em.hal/InterruptSourceI/#unit-interruptsourcei","title":"unit InterruptSourceI","text":"em.hal/InterruptSourceI.empackage em.hal\n\n#! Generally implemented by an Interrupt Template used to \"create\" interrupts\n\ninterface InterruptSourceI \n\n type Handler: function()\n\n #! Sets the handler function for a particular interrupt\n host function setHandlerH(h: Handler)\n\n #! Enables a particular interrupt\n function enable()\n\n #! Disables a particular interrupt\n function disable()\n\n #! Clears the interrupt flag \n function clear()\n\n #! True if the interrupt is enabled\n function isEnabled(): bool\nend\n
"},{"location":"cargo/em.core/em.hal/IntrVecI/","title":"IntrVecI","text":""},{"location":"cargo/em.core/em.hal/IntrVecI/#unit-intrveci","title":"unit IntrVecI","text":"em.hal/IntrVecI.empackage em.hal\n\ninterface IntrVecI\n\n type ExceptionHandler: function(vecNum: uint32, retAddr: addr_t)\n\n host function bindExceptionHandlerH(handler: ExceptionHandler)\n\nend\n
"},{"location":"cargo/em.core/em.hal/LedI/","title":"LedI","text":""},{"location":"cargo/em.core/em.hal/LedI/#unit-ledi","title":"unit LedI","text":"em.hal/LedI.empackage em.hal\n\ninterface LedI\n # ^| abstraction of an LED\n function isOn(): bool\n # ^| test if the LED is on\n function off()\n # ^| turn the LED off\n function on()\n # ^| turn the LED on\n function toggle()\n # ^| toggle the LED\n function wink(msecs: uint16)\n # ^| turn the LED on/off\n # ^| @msecs duration in milliseconds\n end\n
"},{"location":"cargo/em.core/em.hal/LedN/","title":"LedN","text":""},{"location":"cargo/em.core/em.hal/LedN/#unit-ledn","title":"unit LedN","text":"em.hal/LedN.empackage em.hal\n\nimport LedI\n\nmodule LedN: LedI\n # ^| Nil implementation of the LedI interface\nend\n\ndef isOn()\n return false\nend\n\ndef on()\nend\n\ndef off()\nend\n\ndef toggle()\nend\n\ndef wink(usecs)\nend\n
"},{"location":"cargo/em.core/em.hal/McuI/","title":"McuI","text":""},{"location":"cargo/em.core/em.hal/McuI/#unit-mcui","title":"unit McuI","text":"em.hal/McuI.empackage em.hal\n\n#! Implemented by an Mcu module\n\ninterface McuI \n\n const ADMIN_RESET: int8 = -1\n const HOST_RESET: int8 = -2\n const COLD_RESET: int8 = -3\n const FIRST_RESET: int8 = -4\n\n config mclkFrequency: uint32\n\n function getResetCode(): int8\n function getStashAddr(): ptr_t\n function isWarm(): bool\n function readEui48(dst: uint8*)\n\n #! Perform startup and shutdown operations specific for a particular Mcu\n function reset(code: int8 = 0)\n function startup()\n function shutdown()\nend\n
"},{"location":"cargo/em.core/em.hal/McuInfoI/","title":"McuInfoI","text":""},{"location":"cargo/em.core/em.hal/McuInfoI/#unit-mcuinfoi","title":"unit McuInfoI","text":"em.hal/McuInfoI.empackage em.hal\n\ninterface McuInfoI\n\n function readBatMv(): uint16\n function readTempC(): int8\n\nend\n
"},{"location":"cargo/em.core/em.hal/McuInfoN/","title":"McuInfoN","text":""},{"location":"cargo/em.core/em.hal/McuInfoN/#unit-mcuinfon","title":"unit McuInfoN","text":"em.hal/McuInfoN.empackage em.hal\n\nimport McuInfoI\n\nmodule McuInfoN: McuInfoI\n # ^| Nil implementation of the McuInfoI interface\nend\n\ndef readBatMv()\n return 0\nend\n\ndef readTempC()\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/McuN/","title":"McuN","text":""},{"location":"cargo/em.core/em.hal/McuN/#unit-mcun","title":"unit McuN","text":"em.hal/McuN.empackage em.hal\n\nimport McuI\n\nmodule McuN: McuI\n\nend\n\ndef getResetCode()\n return 0\nend\n\ndef getStashAddr()\n return null\nend\n\ndef isWarm()\n return false\nend\n\ndef readEui48(dst)\nend\n\ndef reset(code)\nend\n\ndef startup()\nend\n\ndef shutdown()\nend\n
"},{"location":"cargo/em.core/em.hal/MsCounterI/","title":"MsCounterI","text":""},{"location":"cargo/em.core/em.hal/MsCounterI/#unit-mscounteri","title":"unit MsCounterI","text":"em.hal/MsCounterI.empackage em.hal\n\ninterface MsCounterI\n\n function start()\n function stop(): uint32\n\nend\n
"},{"location":"cargo/em.core/em.hal/MsCounterN/","title":"MsCounterN","text":""},{"location":"cargo/em.core/em.hal/MsCounterN/#unit-mscountern","title":"unit MsCounterN","text":"em.hal/MsCounterN.empackage em.hal\n\nimport MsCounterI\n\nmodule MsCounterN: MsCounterI\n # ^| Nil implementation of the MsCounterI interface\nend\n\ndef start()\nend\n\ndef stop()\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/OneShotMilliI/","title":"OneShotMilliI","text":""},{"location":"cargo/em.core/em.hal/OneShotMilliI/#unit-oneshotmillii","title":"unit OneShotMilliI","text":"em.hal/OneShotMilliI.em package em.hal\n\ninterface OneShotMilliI\n # ^| abstraction of a one-shot timer with millisecond resolution\n type Handler: function(arg: ptr_t)\n # ^| handler function signature\n function disable()\n # ^| disables the timer\n function enable(msecs: uint32, handler: Handler, arg: ptr_t = null) \n # ^| enables the timer to expire in msecs milliseconds\n # ^| @msecs - duration in millisecs before expiration\n # ^| @handler - handler function called upon expiration \n # ^| @arg - optional value passed to the handler\nend\n
"},{"location":"cargo/em.core/em.hal/OneShotMilliN/","title":"OneShotMilliN","text":""},{"location":"cargo/em.core/em.hal/OneShotMilliN/#unit-oneshotmillin","title":"unit OneShotMilliN","text":"em.hal/OneShotMilliN.empackage em.hal\n\nimport OneShotMilliI\n\nmodule OneShotMilliN: OneShotMilliI\n # ^| Nil implementation of the OneShotMilliI interface\nend\n\ndef disable()\nend\n\ndef enable(msecs, handler, arg)\nend\n
"},{"location":"cargo/em.core/em.hal/PollerI/","title":"PollerI","text":""},{"location":"cargo/em.core/em.hal/PollerI/#unit-polleri","title":"unit PollerI","text":"em.hal/PollerI.empackage em.hal\n\ninterface PollerI\n # ^| abstration of periodic polling\n type PollFxn: function(): bool\n # ^| signature of a boolean-valued polling function\n function poll(rateMs: uint16, count: uint16, fxn: PollFxn): uint16 \n # ^| initiates a polling sequence\n # ^| @rateMs - idle time in milliseconds between pollings\n # ^| @count - maximum number of polling attempts\n # ^| @fxn - the polling function itself\n # ^| @return - the number of polling attempts remaining (success if >0)\nend\n
"},{"location":"cargo/em.core/em.hal/PollerN/","title":"PollerN","text":""},{"location":"cargo/em.core/em.hal/PollerN/#unit-pollern","title":"unit PollerN","text":"em.hal/PollerN.empackage em.hal\n\nimport PollerI\n\nmodule PollerN: PollerI\n # ^| Nil implementation of the PollerI interface\nend\n\ndef poll(rateMs, count, fxn)\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/RandI/","title":"RandI","text":""},{"location":"cargo/em.core/em.hal/RandI/#unit-randi","title":"unit RandI","text":"em.hal/RandI.empackage em.hal\n\ninterface RandI\n\n function gen(): uint32\n\nend\n
"},{"location":"cargo/em.core/em.hal/RandN/","title":"RandN","text":""},{"location":"cargo/em.core/em.hal/RandN/#unit-randn","title":"unit RandN","text":"em.hal/RandN.empackage em.hal\n\nimport RandI\n\nmodule RandN: RandI\n # ^| Nil implementation of the RandI interface\nend\n\ndef gen()\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/SpiMasterI/","title":"SpiMasterI","text":""},{"location":"cargo/em.core/em.hal/SpiMasterI/#unit-spimasteri","title":"unit SpiMasterI","text":"em.hal/SpiMasterI.empackage em.hal\n\ninterface SpiMasterI\n\n function activate()\n function deactivate()\n function flush()\n function get(): uint8\n function put(data: uint8)\n\nend\n
"},{"location":"cargo/em.core/em.hal/TimeoutI/","title":"TimeoutI","text":""},{"location":"cargo/em.core/em.hal/TimeoutI/#unit-timeouti","title":"unit TimeoutI","text":"em.hal/TimeoutI.empackage em.hal\n\ninterface TimeoutI\n\n function active(): bool\n function cancel()\n function set(msecs: uint32)\n\nend\n
"},{"location":"cargo/em.core/em.hal/TimeoutN/","title":"TimeoutN","text":""},{"location":"cargo/em.core/em.hal/TimeoutN/#unit-timeoutn","title":"unit TimeoutN","text":"em.hal/TimeoutN.empackage em.hal\n\nimport TimeoutI\n\nmodule TimeoutN: TimeoutI\n # ^| Nil implementation of the TimeoutI interface\nend\n\ndef active()\n return false\nend\n\ndef cancel()\nend\n\ndef set(msecs)\nend\n
"},{"location":"cargo/em.core/em.hal/UptimerI/","title":"UptimerI","text":""},{"location":"cargo/em.core/em.hal/UptimerI/#unit-uptimeri","title":"unit UptimerI","text":"em.hal/UptimerI.empackage em.hal\n\ninterface UptimerI\n\n type Time: struct\n secs: uint32\n subs: uint32\n ticks: uint32\n end\n\n function calibrate(secs256: uint32, ticks: uint32): uint16\n function read(): Time&\n function resetSync()\n function trim(): uint16\n\nend\n
"},{"location":"cargo/em.core/em.hal/UptimerN/","title":"UptimerN","text":""},{"location":"cargo/em.core/em.hal/UptimerN/#unit-uptimern","title":"unit UptimerN","text":"em.hal/UptimerN.empackage em.hal\n\nimport UptimerI\n\nmodule UptimerN: UptimerI\n # ^| Nil implementation of the UptimerI interface\nend\n\ndef calibrate(secs256, ticks)\n return 0\nend\n\ndef read()\n return null\nend\n\ndef resetSync()\nend\n\ndef trim()\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/UsCounterI/","title":"UsCounterI","text":""},{"location":"cargo/em.core/em.hal/UsCounterI/#unit-uscounteri","title":"unit UsCounterI","text":"em.hal/UsCounterI.empackage em.hal\n\ninterface UsCounterI\n\n function start()\n function stop(): uint32\n\nend\n
"},{"location":"cargo/em.core/em.hal/UsCounterN/","title":"UsCounterN","text":""},{"location":"cargo/em.core/em.hal/UsCounterN/#unit-uscountern","title":"unit UsCounterN","text":"em.hal/UsCounterN.empackage em.hal\n\nimport UsCounterI\n\nmodule UsCounterN: UsCounterI\n\nend\n\ndef start()\nend\n\ndef stop()\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/UsThreshI/","title":"UsThreshI","text":""},{"location":"cargo/em.core/em.hal/UsThreshI/#unit-usthreshi","title":"unit UsThreshI","text":"em.hal/UsThreshI.empackage em.hal\n\ninterface UsThreshI\n\n function pause()\n function set(usecs: uint16)\n\nend\n
"},{"location":"cargo/em.core/em.hal/WakeupTimerI/","title":"WakeupTimerI","text":""},{"location":"cargo/em.core/em.hal/WakeupTimerI/#unit-wakeuptimeri","title":"unit WakeupTimerI","text":"em.hal/WakeupTimerI.empackage em.hal\n\ninterface WakeupTimerI\n # ^| abstraction of a free-running wakeup-timer (RTC)\n type Handler: function()\n # ^| handler function signature\n function disable()\n # ^| disables any pending wakeup from the timer\n function enable(thresh: uint32, handler: Handler)\n # ^| enables a future wakeup from the timer\n # ^| @thresh - an internal timer threshold value\n # ^| @handler - the function called when reaching the threshold\n function secs256ToTicks(secs256: uint32): uint32\n # ^| converts secs256 to logical timer ticks\n function ticksToThresh(ticks: uint32): uint32\n # ^| converts timer ticks to an internal timer threshold value\n function timeToTicks(secs: uint32, subs: uint32): uint32\n # ^| converts secs+subs time value to logical timer ticks\n # ^| @secs - the seconds component of the time value\n # ^| @subs - the sub-seconds component of the time value\n # ^| @return - time value represented as logic timer ticks\nend\n
"},{"location":"cargo/em.core/em.hal/WakeupTimerN/","title":"WakeupTimerN","text":""},{"location":"cargo/em.core/em.hal/WakeupTimerN/#unit-wakeuptimern","title":"unit WakeupTimerN","text":"em.hal/WakeupTimerN.empackage em.hal\n\nimport WakeupTimerI\n\nmodule WakeupTimerN: WakeupTimerI\n # ^| Nil implementation of the WakeupTimerI interface\nend\n\ndef disable()\nend\n\ndef enable(thresh, handler)\nend\n\ndef secs256ToTicks(secs256)\n return 0\nend\n\ndef ticksToThresh(ticks)\n return 0\nend\n\ndef timeToTicks(secs, subs)\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/WatchdogI/","title":"WatchdogI","text":""},{"location":"cargo/em.core/em.hal/WatchdogI/#unit-watchdogi","title":"unit WatchdogI","text":"em.hal/WatchdogI.empackage em.hal\n\ninterface WatchdogI\n\n type Handler: function()\n\n function didBite(): bool\n function disable()\n function enable(secs: uint16, handler: Handler)\n function pet()\n\nend\n
"},{"location":"cargo/em.core/em.hal/WatchdogN/","title":"WatchdogN","text":""},{"location":"cargo/em.core/em.hal/WatchdogN/#unit-watchdogn","title":"unit WatchdogN","text":"em.hal/WatchdogN.empackage em.hal\n\nimport WatchdogI\n\nmodule WatchdogN: WatchdogI\n # ^| Nil implementation of the WatchdogI interface\nend\n\ndef didBite()\n return false\nend\n\ndef disable()\nend\n\ndef enable(secs, handler)\nend\n\ndef pet()\nend\n
"},{"location":"cargo/em.core/em.lang/","title":"Index","text":""},{"location":"cargo/em.core/em.lang/#package-emlang","title":"package em.lang","text":""},{"location":"cargo/em.core/em.lang/Assert/","title":"Assert","text":""},{"location":"cargo/em.core/em.lang/Assert/#unit-assert","title":"unit Assert","text":"em.lang/Assert.empackage em.lang\n\nimport AssertProviderI\nimport AssertProviderN\n\nmodule Assert\n\n proxy Provider: AssertProviderI\n\n function enabled(): bool\n function trigger(upath: atom_t, line: uint16, msg: atom_t = 0, arg1: iarg_t = 0, arg2: iarg_t = 0)\n\nend\n\ndef em$configure()\n Provider ?= AssertProviderN\nend\n\ndef enabled()\n return Provider.enabled()\nend\n\ndef trigger(upath, line, msg, arg1, arg2)\n Provider.trigger(upath, line, msg, arg1, arg2) if enabled()\nend\n
"},{"location":"cargo/em.core/em.lang/AssertProviderI/","title":"AssertProviderI","text":""},{"location":"cargo/em.core/em.lang/AssertProviderI/#unit-assertprovideri","title":"unit AssertProviderI","text":"em.lang/AssertProviderI.empackage em.lang\n\ninterface AssertProviderI\n\n function enabled(): bool\n function trigger(upath: atom_t, line: uint16, msg: atom_t, arg1: iarg_t, arg2: iarg_t)\n\nend\n
"},{"location":"cargo/em.core/em.lang/AssertProviderN/","title":"AssertProviderN","text":""},{"location":"cargo/em.core/em.lang/AssertProviderN/#unit-assertprovidern","title":"unit AssertProviderN","text":"em.lang/AssertProviderN.empackage em.lang\n\nimport AssertProviderI\n\nmodule AssertProviderN: AssertProviderI\n\nend\n\ndef enabled()\n return false\nend\n\ndef trigger(upath, line, msg, arg1, arg2)\nend\n
"},{"location":"cargo/em.core/em.lang/Atom/","title":"Atom","text":""},{"location":"cargo/em.core/em.lang/Atom/#unit-atom","title":"unit Atom","text":"em.lang/Atom.empackage em.lang\n\nmodule Atom\n\n config NULL_A: atom_t = @\"<<null>>\"\n config UNDEFINED_A: atom_t = @\"<<undefined>>\"\n\n function fromString(str: string, oatom: atom_t*): bool\n function hasTable(): bool\n function toString(atom: atom_t): string\n\nprivate:\n\n const MASK: addr_t = 0x80000000\n\n config tableFlag: bool\n\nend\n\ndef em$construct()\n tableFlag = ^^!!em$props.get(em$session.PROP_ATOM_TABLE)^^\nend\n\ndef fromString(str, oatom)\n auto saddr = <addr_t>str\n return false if tableFlag || (saddr & MASK) == 0\n *oatom = <atom_t>saddr\n return true\nend\n\ndef hasTable()\n return tableFlag\nend\n\ndef toString(atom)\n return tableFlag ? ^^em$atoms[atom]^^ : <string>((<addr_t>atom) | MASK)\nend\n
"},{"location":"cargo/em.core/em.lang/BuildC/","title":"BuildC","text":""},{"location":"cargo/em.core/em.lang/BuildC/#unit-buildc","title":"unit BuildC","text":"em.lang/BuildC.empackage em.lang\n\ncomposite BuildC\n\n config arch: string\n config bootFlash: bool\n config bootLoader: bool\n config compiler: string\n config cpu: string\n config jlinkDev: string\n config mcu: string\n config optimize: string\n\nprivate:\n\n var curPval: string\n\n function getProp(pname: string): string\n\nend\n\ndef em$preconfigure()\n arch ?= curPval if getProp(\"em.build.Arch\")\n bootFlash ?= true if getProp(\"em.build.BootFlash\")\n bootLoader ?= true if getProp(\"em.lang.BootLoader\")\n compiler ?= curPval if getProp(\"em.build.Compiler\")\n cpu ?= curPval if getProp(\"em.build.Cpu\")\n jlinkDev ?= curPval if getProp(\"em.build.JlinkDev\")\n mcu ?= curPval if getProp(\"em.build.Mcu\")\n optimize ?= curPval if getProp(\"em.build.Optimize\")\nend\n\ndef getProp(pname)\n return curPval = ^^em$props.get^^(pname, null)\nend\n
"},{"location":"cargo/em.core/em.lang/BuilderI/","title":"BuilderI","text":""},{"location":"cargo/em.core/em.lang/BuilderI/#unit-builderi","title":"unit BuilderI","text":"em.lang/BuilderI.empackage em.lang\n\nhost interface BuilderI\n\n type CompileInfo: struct\n errMsgs: string[]\n imageSizes: string\n procStat: int8\n end\n\n type TypeInfoDesc: uint8[2]\n\n type TypeInfo: struct\n ARG: TypeInfoDesc\n CHAR: TypeInfoDesc\n INT: TypeInfoDesc\n INT8: TypeInfoDesc\n INT16: TypeInfoDesc\n INT32: TypeInfoDesc\n LONG: TypeInfoDesc\n PTR: TypeInfoDesc\n SHORT: TypeInfoDesc\n SIZE: TypeInfoDesc\n end\n\n function compile(buildDir: string): CompileInfo&\n function getTypeInfo(): TypeInfo&\n function populate(buildDir: string, sysFlag: bool)\n\nend\n
"},{"location":"cargo/em.core/em.lang/CompositeI/","title":"CompositeI","text":""},{"location":"cargo/em.core/em.lang/CompositeI/#unit-compositei","title":"unit CompositeI","text":"em.lang/CompositeI.empackage em.lang\n\ninterface CompositeI\n\n host function em$configure()\n host function em$preconfigure()\n\nend\n
"},{"location":"cargo/em.core/em.lang/Console/","title":"Console","text":""},{"location":"cargo/em.core/em.lang/Console/#unit-console","title":"unit Console","text":"em.lang/Console.empackage em.lang\n\nimport ConsoleProviderI\n\nmodule Console\n\n proxy Provider: ConsoleProviderI\n\n config noPrint: bool\n\n function print(fmt: string, a1: iarg_t = 0, a2: iarg_t = 0, a3: iarg_t = 0, a4: iarg_t = 0, a5: iarg_t = 0, a6: iarg_t = 0)\n\n function wrC(data: char)\n function wrN(data: num_t)\n function wrP(data: ptr_t)\n\n function wrT(data: string)\n\n function wrIA(data: iarg_t)\n function wrUA(data: uarg_t)\n\n function wrI8(data: int8)\n function wrI16(data: int16)\n function wrI32(data: int32)\n\n function wrU8(data: uint8)\n function wrU16(data: uint16)\n function wrU32(data: uint32)\n\nend\n\ndef em$generateCode(prefix)\n |-> #define em$print em_lang_Console::print\nend\n\ndef print(fmt, a1, a2, a3, a4, a5, a6)\n if !noPrint\n Provider.print(fmt, a1, a2, a3, a4, a5, a6)\n end\nend\n\ndef wrC(data)\n Provider.put(data)\nend\n\ndef wrN(data)\n Provider.put(0x8F)\n auto ba = <uint8[]>(&data)\n for auto i = 0; i < sizeof<num_t>; i++\n Provider.put(ba[i])\n end\n Provider.flush()\nend\n\ndef wrP(data)\n wrU32(<uint32>data)\nend\n\ndef wrT(data)\n Provider.put(0x80)\n auto cp = <char*>data\n for ;;\n auto ch = *cp++\n wrC(ch)\n return if ch == 0 \n end\nend\n\ndef wrIA(data)\n wrU16(<uint16>data)\nend\n\ndef wrUA(data)\n wrU16(<uint16>data)\nend\n\ndef wrI8(data)\n wrU8(<uint8>data)\nend\n\ndef wrI16(data)\n wrU16(<uint16>data)\nend\n\ndef wrI32(data)\n wrU32(<uint32>data)\nend\n\ndef wrU8(data)\n Provider.put(0x81)\n Provider.put(data)\n Provider.flush()\nend\n\ndef wrU16(data)\n Provider.put(0x82)\n auto b = <uint8> ((data >> 8) & 0xFF)\n Provider.put(b)\n b = <uint8> ((data >> 0) & 0xFF)\n Provider.put(b)\n Provider.flush()\nend\n\ndef wrU32(data)\n Provider.put(0x84)\n auto b = <uint8> ((data >> 24) & 0xFF)\n Provider.put(b)\n b = <uint8> ((data >> 16) & 0xFF)\n Provider.put(b)\n b = <uint8> ((data >> 8) & 0xFF)\n Provider.put(b)\n b = <uint8> ((data >> 0) & 0xFF)\n Provider.put(b)\n Provider.flush()\nend\n
"},{"location":"cargo/em.core/em.lang/ConsoleProviderI/","title":"ConsoleProviderI","text":""},{"location":"cargo/em.core/em.lang/ConsoleProviderI/#unit-consoleprovideri","title":"unit ConsoleProviderI","text":"em.lang/ConsoleProviderI.empackage em.lang\n\ninterface ConsoleProviderI\n\n function flush()\n function print(fmt: string, a1: iarg_t = 0, a2: iarg_t = 0, a3: iarg_t = 0, a4: iarg_t = 0, a5: iarg_t = 0, a6: iarg_t = 0)\n function put(data: uint8)\n\nend\n
"},{"location":"cargo/em.core/em.lang/ConsoleProviderN/","title":"ConsoleProviderN","text":""},{"location":"cargo/em.core/em.lang/ConsoleProviderN/#unit-consoleprovidern","title":"unit ConsoleProviderN","text":"em.lang/ConsoleProviderN.empackage em.lang\n\nimport ConsoleProviderI\n\nmodule ConsoleProviderN: ConsoleProviderI\n\nend\n\ndef flush()\nend\n\ndef print(fmt, a1, a2, a3, a4, a5, a6)\nend\n\ndef put(data)\nend\n
"},{"location":"cargo/em.core/em.lang/Debug/","title":"Debug","text":""},{"location":"cargo/em.core/em.lang/Debug/#unit-debug","title":"unit Debug","text":"em.lang/Debug.empackage em.lang\n\nimport DebugPinI\n\nmodule Debug\n\n proxy Pin_a: DebugPinI\n proxy Pin_b: DebugPinI\n proxy Pin_c: DebugPinI\n proxy Pin_d: DebugPinI\n\n function getNumPins(): uint8\n function sleepEnter()\n function sleepLeave()\n function startup()\n\nprivate:\n\nend\n\ndef em$configure()\n em$used ?= true\nend\n\ndef getNumPins()\n return 4\nend\n\ndef sleepEnter()\n Pin_a.reset()\n Pin_b.reset()\n Pin_c.reset()\n Pin_d.reset()\nend\n\ndef sleepLeave()\n startup()\nend\n\ndef startup()\n Pin_a.startup()\n Pin_b.startup()\n Pin_c.startup()\n Pin_d.startup()\nend\n
"},{"location":"cargo/em.core/em.lang/DebugPinI/","title":"DebugPinI","text":""},{"location":"cargo/em.core/em.lang/DebugPinI/#unit-debugpini","title":"unit DebugPinI","text":"em.lang/DebugPinI.empackage em.lang\n\ninterface DebugPinI\n\n function clear()\n function get(): bool\n function set()\n function toggle()\n function pulse()\n function mark(k: uint8 = 0)\n function reset()\n function startup()\n\nend\n
"},{"location":"cargo/em.core/em.lang/DebugPinN/","title":"DebugPinN","text":""},{"location":"cargo/em.core/em.lang/DebugPinN/#unit-debugpinn","title":"unit DebugPinN","text":"em.lang/DebugPinN.empackage em.lang\n\nimport DebugPinI\n\nmodule DebugPinN: DebugPinI\n\nend\n\ndef clear()\nend\n\ndef get()\n return false\nend\n\ndef set()\nend\n\ndef toggle()\nend\n\ndef pulse()\nend\n\ndef mark(k)\nend\n\ndef reset()\nend\n\ndef startup()\nend\n
"},{"location":"cargo/em.core/em.lang/Math/","title":"Math","text":""},{"location":"cargo/em.core/em.lang/Math/#unit-math","title":"unit Math","text":"em.lang/Math.empackage em.lang\n\n#! This module is only available for use on the host.\n#! It contains some standard math functions.\n\nhost module Math\n\n config PI: num_t\n\n #! Returns the absolute value of x\n function abs(x: num_t): num_t\n\n #! Returns the smallest integer greater than or equal to x\n function ceil(x: num_t): num_t\n\n #! Returns the cos of x\n function cos(x: num_t): num_t\n\n #! Returns the largest integer less than or equal to x\n function floor(x: num_t): num_t\n\n #! Returns the logarithm (base 10) of x\n function log2(x: num_t): num_t\n\n #! Returns the logarithm (base 10) of x\n function log10(x: num_t): num_t\n\n #! Returns the value of x to the y power\n function pow(x: num_t, y: num_t): num_t\n\n #! Returns the rounded value of x\n function round(x: num_t): num_t\n\n #! Returns the sin of x\n function sin(x: num_t): num_t\n\nend\n\ndef em$configure()\n PI ?= ^^global.Math.PI^^\nend\n\ndef abs(x)\n return ^^global.Math.abs(x)^^\nend\n\n# If x = 19.1 this function will return 20\ndef ceil(x)\n return ^^global.Math.ceil(x)^^\nend\n\ndef cos(x)\n return ^^global.Math.cos(x)^^\nend\n\n# If x = 19.6 this function will return 19\ndef floor(x)\n return ^^global.Math.floor(x)^^\nend\n\ndef log2(x)\n return ^^global.Math.log2(x)^^\nend\n\ndef log10(x)\n return ^^global.Math.LOG10E * global.Math.log(x)^^\nend\n\ndef pow(x, y)\n return ^^global.Math.pow(x, y)^^\nend\n\ndef round(x)\n return ^^global.Math.round(x)^^\nend\n\ndef sin(x)\n return ^^global.Math.sin(x)^^\nend\n
"},{"location":"cargo/em.core/em.lang/ModuleI/","title":"ModuleI","text":""},{"location":"cargo/em.core/em.lang/ModuleI/#unit-modulei","title":"unit ModuleI","text":"em.lang/ModuleI.empackage em.lang\n\ninterface ModuleI\n\n type io32_t: uint32 volatile*\n\n host config em$exclude: bool\n host config em$export: bool\n host config em$traceGrp: string\n host config em$used: bool\n\n config em$tracePri: uint8\n\n host function em$configure()\n host function em$construct()\n template em$generateCode(prefix: string)\n\n function em$fail()\n function em$halt()\n function em$reset()\n function em$run()\n function em$shutdown()\n function em$startup()\n function em$startupDone()\n\n host function em$uses__()\n\nend\n
"},{"location":"cargo/em.core/em.lang/RunC/","title":"RunC","text":""},{"location":"cargo/em.core/em.lang/RunC/#unit-runc","title":"unit RunC","text":"em.lang/RunC.empackage em.lang\n\nimport Console\nimport ConsoleProviderN\n\nimport Debug\nimport DebugPinN\n\ncomposite RunC\n\nend\n\ndef em$configure()\n Console.em$used ?= true\n Console.Provider ?= ConsoleProviderN\n Debug.em$used ?= true\n Debug.Pin_a ?= DebugPinN\n Debug.Pin_b ?= DebugPinN\n Debug.Pin_c ?= DebugPinN\n Debug.Pin_d ?= DebugPinN\nend\n
"},{"location":"cargo/em.core/em.lang/TemplateI/","title":"TemplateI","text":""},{"location":"cargo/em.core/em.lang/TemplateI/#unit-templatei","title":"unit TemplateI","text":"em.lang/TemplateI.empackage em.lang\n\nhost interface TemplateI \n\n function em$cacheDirty(ctimeMs: num_t): bool\n template em$generateUnit(pkgName: string, unitName: string)\n\nend\n
"},{"location":"cargo/em.core/em.mcu/","title":"Index","text":""},{"location":"cargo/em.core/em.mcu/#package-emmcu","title":"package em.mcu","text":""},{"location":"cargo/em.core/em.mcu/Common/","title":"Common","text":""},{"location":"cargo/em.core/em.mcu/Common/#unit-common","title":"unit Common","text":"em.mcu/Common.empackage em.mcu\n\nfrom em.hal import BusyWaitI\nfrom em.hal import GlobalInterruptsI\nfrom em.hal import IdleI\nfrom em.hal import McuI\nfrom em.hal import MsCounterI\nfrom em.hal import RandI\nfrom em.hal import UsCounterI\nfrom em.hal import WatchdogI\n\nmodule Common\n # ^| collection of proxies implementing MCU abstractions\n proxy BusyWait: BusyWaitI\n proxy GlobalInterrupts: GlobalInterruptsI\n proxy Idle: IdleI \n proxy Mcu: McuI \n proxy MsCounter: MsCounterI \n proxy Rand: RandI\n proxy UsCounter: UsCounterI \n proxy Watchdog: WatchdogI\n\nend\n
"},{"location":"cargo/em.core/em.mcu/CommonC/","title":"CommonC","text":""},{"location":"cargo/em.core/em.mcu/CommonC/#unit-commonc","title":"unit CommonC","text":"em.mcu/CommonC.empackage em.mcu\n\nfrom em.hal import BusyWaitN\nfrom em.hal import GlobalInterruptsN\nfrom em.hal import IdleN\nfrom em.hal import McuN\nfrom em.hal import MsCounterN\nfrom em.hal import RandN\nfrom em.hal import UsCounterN\nfrom em.hal import WatchdogN\n\nfrom em.mcu import Common\n\ncomposite CommonC\n\nend\n\ndef em$configure()\n Common.BusyWait ?= BusyWaitN\n Common.GlobalInterrupts ?= GlobalInterruptsN\n Common.Idle ?= IdleN\n Common.Mcu ?= McuN\n Common.MsCounter ?= MsCounterN\n Common.Rand ?= RandN\n Common.UsCounter ?= UsCounterN\n Common.Watchdog ?= WatchdogN\nend\n
"},{"location":"cargo/em.core/em.mcu/ConsoleUart/","title":"ConsoleUart","text":""},{"location":"cargo/em.core/em.mcu/ConsoleUart/#unit-consoleuart","title":"unit ConsoleUart","text":"em.mcu/ConsoleUart.empackage em.mcu\n\nfrom em.hal import ConsoleUartI\nfrom em.hal import ConsoleUartN\n\nmodule ConsoleUart: ConsoleUartI\n\n proxy Impl: ConsoleUartI\n\nend\n\ndef em$configure()\n Impl ?= ConsoleUartN\nend\n\ndef setBaudH(rate)\n Impl.setBaudH(rate)\nend\n\ndef flush()\n Impl.flush()\nend\n\ndef put(data)\n Impl.put(data)\nend\n
"},{"location":"cargo/em.core/em.mcu/Copier/","title":"Copier","text":""},{"location":"cargo/em.core/em.mcu/Copier/#unit-copier","title":"unit Copier","text":"em.mcu/Copier.empackage em.mcu\n\nfrom em.hal import CopierI\n\nmodule Copier: CopierI\n\n proxy Impl: CopierI\n\nend\n\ndef exec(dst, src, cnt)\n Impl.exec(dst, src, cnt)\nend\n
"},{"location":"cargo/em.core/em.mcu/Info/","title":"Info","text":""},{"location":"cargo/em.core/em.mcu/Info/#unit-info","title":"unit Info","text":"em.mcu/Info.empackage em.mcu\n\nfrom em.hal import McuInfoI\n\nmodule Info: McuInfoI\n\n proxy Impl: McuInfoI\n\nend\n\ndef readBatMv()\n return Impl.readBatMv()\nend\n\ndef readTempC()\n return Impl.readTempC()\nend\n
"},{"location":"cargo/em.core/em.mcu/Poller/","title":"Poller","text":""},{"location":"cargo/em.core/em.mcu/Poller/#unit-poller","title":"unit Poller","text":"em.mcu/Poller.empackage em.mcu\n\nfrom em.hal import PollerN\n\nfrom em.hal import PollerI\n\nmodule Poller: PollerI\n\n proxy Impl: PollerI\n\n function pause(timeMs: uint16)\n\nend\n\ndef em$configure()\n Impl ?= PollerN\nend\n\ndef pause(timeMs)\n Impl.poll(timeMs, 1, null)\nend\n\ndef poll(rateMs, count, fxn)\n return Impl.poll(rateMs, count, fxn)\nend\n
"},{"location":"cargo/em.core/em.mcu/Timeout/","title":"Timeout","text":""},{"location":"cargo/em.core/em.mcu/Timeout/#unit-timeout","title":"unit Timeout","text":"em.mcu/Timeout.empackage em.mcu\n\nfrom em.hal import TimeoutI\n\nmodule Timeout: TimeoutI\n\n proxy Impl: TimeoutI\n\nend\n\ndef active()\n return Impl.active()\nend\n\ndef cancel()\n Impl.cancel()\nend\n\ndef set(msecs)\n Impl.set(msecs)\nend\n
"},{"location":"cargo/em.core/em.utils/","title":"Index","text":""},{"location":"cargo/em.core/em.utils/#package-emutils","title":"package em.utils","text":""},{"location":"cargo/em.core/em.utils/AlarmMgr/","title":"AlarmMgr","text":""},{"location":"cargo/em.core/em.utils/AlarmMgr/#unit-alarmmgr","title":"unit AlarmMgr","text":"em.utils/AlarmMgr.empackage em.utils\n\nfrom em.hal import WakeupTimerI\n\nimport EpochTime\nimport FiberMgr\n\nmodule AlarmMgr\n # ^|\n proxy WakeupTimer: WakeupTimerI\n # ^|\n type Alarm: opaque\n # ^| \n host function initH(fiber: FiberMgr.Fiber&)\n # ^| \n function active(): bool\n # ^| \n function cancel()\n # ^| \n function wakeup(secs256: uint32)\n # ^| \n function wakeupAt(secs256: uint32)\n # ^| \n end\n\n host function createH(fiber: FiberMgr.Fiber&): Alarm&\n # ^|\nprivate:\n\n def opaque Alarm\n fiber: FiberMgr.Fiber&\n thresh: uint32\n ticks: uint32\n function setup(ticks: uint32)\n end\n\n function update(deltaTicks: uint32)\n function wakeupHandler: WakeupTimer.Handler\n\n var alarmTab: Alarm[..]\n var curAlarm: Alarm&\n\nend\n\ndef createH(fiber)\n var alarm: Alarm& = alarmTab[alarmTab.length++]\n alarm.initH(fiber)\n return alarm\nend\n\ndef update(deltaTicks)\n WakeupTimer.disable()\n auto nxtAlarm = <Alarm&>null\n var maxTicks: uint32 = ~0 # largest uint32\n for a in alarmTab\n continue if a.ticks == 0 # inactive alarm\n a.ticks -= deltaTicks\n if a.ticks == 0 # expired alarm\n a.fiber.post()\n elif a.ticks < maxTicks\n nxtAlarm = a\n maxTicks = a.ticks \n end\n end\n return if nxtAlarm == null # no active alarms\n curAlarm = nxtAlarm\n WakeupTimer.enable(curAlarm.thresh, wakeupHandler)\nend\n\ndef wakeupHandler()\n update(curAlarm.ticks)\nend\n\ndef Alarm.initH(fiber)\n this.fiber = fiber\n this.ticks = 0\nend\n\ndef Alarm.active()\n return this.ticks != 0\nend\n\ndef Alarm.cancel()\n this.ticks = 0\n update(0)\nend\n\ndef Alarm.setup(ticks)\n this.thresh = WakeupTimer.ticksToThresh(ticks)\n this.ticks = ticks\n update(0)\nend\n\ndef Alarm.wakeup(secs256)\n auto ticks = WakeupTimer.secs256ToTicks(secs256)\n this.setup(ticks)\nend\n\ndef Alarm.wakeupAt(secs256)\n var etSubs: uint32\n auto etSecs = EpochTime.getCurrent(&etSubs)\n auto etTicks = WakeupTimer.timeToTicks(etSecs, etSubs)\n auto ticks = WakeupTimer.secs256ToTicks(secs256)\n this.setup(ticks - (etTicks % ticks))\nend\n
"},{"location":"cargo/em.core/em.utils/AppControl/","title":"AppControl","text":""},{"location":"cargo/em.core/em.utils/AppControl/#unit-appcontrol","title":"unit AppControl","text":"em.utils/AppControl.empackage em.utils\n\nfrom em.mcu import Common\n\nimport BoardController\nimport EpochTime\n\nmodule AppControl\n\n function restart(status: int8)\n\nprivate:\n\n type Stash: struct\n secs: uint32\n subs: uint32\n end\n\n function getStash(): Stash&\n function doReset(code: int8)\n\nend\n\ndef em$startup()\n return if Common.Mcu.isWarm() || Common.Mcu.getResetCode() < 0\n auto stash = getStash()\n EpochTime.setCurrent(stash.secs, stash.subs, false)\nend\n\ndef em$fail()\n BoardController.em$fail()\nend\n\ndef em$halt()\n BoardController.em$halt()\nend\n\ndef doReset(code)\n auto stash = getStash()\n stash.secs = EpochTime.getCurrent(&stash.subs)\n Common.Mcu.reset(code) \nend\n\ndef getStash()\n return Common.Mcu.getStashAddr()\nend\n\ndef restart(status)\n doReset(status)\nend\n
"},{"location":"cargo/em.core/em.utils/AssertProvider/","title":"AssertProvider","text":""},{"location":"cargo/em.core/em.utils/AssertProvider/#unit-assertprovider","title":"unit AssertProvider","text":"em.utils/AssertProvider.empackage em.utils\n\nfrom em.lang import AssertProviderI\n\nimport Error\nimport Logger\n\nmodule AssertProvider: AssertProviderI\n\nprivate:\n\n config assertMsgE: Logger.EventKind&\n config assertSiteE: Logger.EventKind&\n\nend\n\ndef em$construct()\n assertMsgE = Logger.declareEventH(\"ASSERT: $F\", \"*--*\")\n assertSiteE = Logger.declareEventH(\"ASSERT: $a, line %d\", \"*--*\")\nend\n\ndef enabled()\n return Logger.POLICY != Logger.Policy.NIL\nend\n\ndef trigger(upath, line, msg, arg1, arg2)\n assertSiteE.log(<addr_t>upath, line)\n assertMsgE.log(<addr_t>msg, <addr_t>arg1, <addr_t>arg2) if msg\n Error.raise(Error.Kind.ASSERTION, 0, 0)\nend\n
"},{"location":"cargo/em.core/em.utils/BasicListManager/","title":"BasicListManager","text":""},{"location":"cargo/em.core/em.utils/BasicListManager/#unit-basiclistmanager","title":"unit BasicListManager","text":"em.utils/BasicListManager.empackage em.utils\n\nimport ListManagerI\n\n#! This module implements ListManagerI.\n#! It contains all the functions necessary to maintain a list of objects.\n\nmodule BasicListManager: ListManagerI\n\nprivate:\n\n # Representation of Element\n def opaque Element \n next: Element& volatile\n end\n\n # Representation of List\n def opaque List \n first: Element& volatile\n last: Element& volatile\n end\nend\n\n# Initially an Element is not a member of a List\ndef Element.init() \n this.next = null\nend\n\n# Initially an Element is not a member of a List\ndef Element.initH() \n this.next = null\nend\n\ndef Element.isActive() \n return <uarg_t> this.next\nend\n\n# Creates a head and tail pointer\ndef List.init() \n this.first = this.last = <Element&> &this.first\nend\n\n# Creates a head and tail pointer\ndef List.initH() \n this.first = this.last = <Element&> &this.first;\nend\n\n# The very first Element is pointed to by this.first and this.last.\n# Subsequent Elements are added to the end of the list by setting the\n# next pointer of the current last Element to the added Element then moving\n# the last pointer. \ndef List.add(elem)\n this.last.next = elem\n this.last = elem\n elem.next = <Element&> this\nend\n\n# Elements are removed from the front of the list by\n# setting a pointer equal to the this.first pointer,\n# then moving the this.first pointer to the next Element.\n# If the pointer then points to the List then that was the last\n# Element and reinitialize the list for adding Elements.\n# Otherwise just remove the Element. \ndef List.get() \n auto elem = this.first\n this.last = <Element&>this if (this.first = elem.next) == <Element&>this\n elem.next = null\n return elem\nend\n\ndef List.getAt(index)\n auto elem = this.first\n auto i = 0\n if this.hasElements()\n for ;;\n break if i++ == index \n elem = elem.next \n break if elem == <Element&> this\n end\n elem = null if elem == <Element&> this\n else \n elem = null\n end\n return elem\nend\n\n# This function returns the Element after the given Element\n# in the List, but does not remove it from the List.\ndef List.getNext(elem) \n return elem == this.last ? null : elem.next\nend\n\ndef List.hasElements() \n return (<uarg_t> this.first) ^ <uarg_t> this\nend\n\n# This function prints the index and address of each Element in the List\ndef List.print() \n auto elem = this.first\n auto i = 0\n if this.hasElements()\n for ;;\n printf \"elem%d %p\\n\", i++, elem\n elem = elem.next\n break if elem == <Element&> this\n end\n else \n printf \"list empty\\n\"\n end\n printf \"\\n\" \nend\n\n# Performs a linear search through the list to find the Element\n# then removes it, updating the appropriate pointers.\ndef List.remove(elem)\n auto e = this.first\n if this.hasElements()\n if elem == this.first\n this.first = this.first.next\n else \n for ;; \n if e.next == elem\n e.next = elem.next \n break \n end \n break if e == <Element&> this\n end\n end\n end\nend\n
"},{"location":"cargo/em.core/em.utils/BoardController/","title":"BoardController","text":""},{"location":"cargo/em.core/em.utils/BoardController/#unit-boardcontroller","title":"unit BoardController","text":"em.utils/BoardController.empackage em.utils\n\nfrom em.hal import ConsoleUartI\nfrom em.hal import LedI\n\nfrom em.mcu import Common\nfrom em.mcu import ConsoleUart\n\nimport ConsoleProtocol\n\nmodule BoardController\n\n proxy Led: LedI\n proxy Uart: ConsoleUartI\n\n config blinkRate: uint32 = 50000\n\nprivate: \n\n function blink(times: uint8, usecs: uint32)\n\nend\n\ndef em$configure()\n Uart ?= ConsoleUart\nend\n\ndef em$reset()\n Common.Mcu.startup() \nend\n\ndef em$startupDone()\n return if Common.Mcu.isWarm()\n Led.off()\n blink(2, blinkRate)\n Uart.flush()\n Uart.put(0)\n Uart.put(0)\n for auto i = 0; i < ConsoleProtocol.SOT_COUNT; i++\n Uart.put(ConsoleProtocol.SOT_BYTE)\n end\n Uart.flush()\nend\n\ndef em$halt()\n Uart.put(ConsoleProtocol.EOT_BYTE)\n Common.GlobalInterrupts.disable()\n Led.on()\n Common.Mcu.shutdown()\nend\n\ndef em$fail()\n Common.Mcu.shutdown()\n Common.GlobalInterrupts.disable()\n while true\n blink(2, blinkRate)\n for auto i = 0; i < 800; i++\n Common.BusyWait.wait(100)\n end\n end\nend\n\ndef blink(times, usecs)\n auto k = (times * 2)\n while --k\n Led.toggle()\n Common.BusyWait.wait(usecs) \n end\n Led.toggle()\nend\n
"},{"location":"cargo/em.core/em.utils/BoardDriverI/","title":"BoardDriverI","text":""},{"location":"cargo/em.core/em.utils/BoardDriverI/#unit-boarddriveri","title":"unit BoardDriverI","text":"em.utils/BoardDriverI.empackage em.utils\n\ninterface BoardDriverI\n\n host function bindParamsH(p: ptr_t)\n\n template genImpl(dn: string)\n template genSpec(dn: string)\n\nend\n
"},{"location":"cargo/em.core/em.utils/BoardDriversGenT/","title":"BoardDriversGenT","text":""},{"location":"cargo/em.core/em.utils/BoardDriversGenT/#unit-boarddriversgent","title":"unit BoardDriversGenT","text":"em.utils/BoardDriversGenT.empackage em.utils\n\nimport BoardInfo\n\ntemplate BoardDriversGenT\n\n config driversPkg: string\n\nend\n\ndef em$generateUnit(pn, un)\n auto brdRec = BoardInfo.readRecordH()\n auto drvPkg = driversPkg\n |->package `pn`\n |-> \n for dd in brdRec.drvDescs\n |->from `drvPkg` import `dd.driver` as `dd.name`\n end\n |->\n |->module `un`\n |-> \n |->end\nend\n
"},{"location":"cargo/em.core/em.utils/BoardInfo/","title":"BoardInfo","text":""},{"location":"cargo/em.core/em.utils/BoardInfo/#unit-boardinfo","title":"unit BoardInfo","text":"em.utils/BoardInfo.empackage em.utils\n\nfrom em$distro import BoardMeta as Meta\n\nhost module BoardInfo\n\n const PROP_BOARD_CHAIN: string = \"em.lang.BoardChain_\"\n const PROP_BOARD_KIND: string = \"em.lang.BoardKind\"\n\n type PinMap: Meta.PinMap\n type DrvDesc: Meta.DrvDesc\n type Record: Meta.Record\n\n function getKind(): string\n function readRecordH(): Record&\n\nprivate:\n\n function cacheGet(kind: string): Record&\n function cacheSet(kind: string, rec: Record&)\n\n function collapse(kind: string, db: ptr_t, set: ptr_t): ptr_t\n\n function init()\n\n function mergeBrd(baseBrd: ptr_t, extBrd: ptr_t)\n function mergeDb(baseDb: ptr_t, extDb: ptr_t)\n\n config baseFileLoc: string\n config localFileLoc: string\n\n config attrs: string[]\n config pins: string[]\n\n config boardKind: string\n\n var initFlg: bool\n var recCache: ptr_t\n\nend\n\ndef em$configure()\n readRecordH()\nend\n\ndef cacheGet(kind)\n ^^if (!BoardInfo.recCache) BoardInfo.recCache = {}^^\n return ^^BoardInfo.recCache[kind]^^\nend\n\ndef cacheSet(kind, rec)\n ^^BoardInfo.recCache[kind] = rec^^\nend\n\ndef collapse(kind, db, set)\n return ^^db.$DEFAULTS^^ if ^^!kind || kind == '$DEFAULTS'^^\n var ext: ptr_t = ^^db[kind]^^\n if !ext\n printf \"*** unknown board kind: '%s'\\n\", kind\n fail\n end\n if ^^set.has(kind)^^\n printf \"*** circular inheritance chain: '%s'\\n\", kind\n fail\n end\n ^^set.add(kind)^^\n var base: ptr_t = collapse(^^ext.$inherits^^, db, set)\n mergeBrd(base, ext)\n return base\nend\n\ndef getKind()\n init()\n return boardKind\nend\n\ndef init()\n return if initFlg\n initFlg = true\n attrs = Meta.attrNames\n pins = Meta.pinNames\n baseFileLoc = Meta.baseFileLoc\n auto path = ^^$Path.join(em$session.getRootDir(), 'em-boards-local')^^\n localFileLoc = ^^$Fs.existsSync(path) ? path : null^^\n boardKind = ^^em$props.get^^(PROP_BOARD_KIND)\nend\n\ndef mergeBrd(baseBrd, extBrd)\n ^^var aTab = []^^\n ^^for (a in extBrd) { aTab[a] = true; aTab.push(a) }^^\n for a in attrs\n ^^baseBrd[a] = extBrd[a]^^ if ^^a in extBrd^^\n ^^aTab[a] = false^^\n end\n for i: uint8 = 0; i < ^^aTab.length^^; i++\n var an: string = ^^aTab[i]^^\n if an != \"pins\" && an != \"drvDescs\" && an != \"nvsFiles\" && ^^aTab[an]^^\n printf \"*** unknown attribute name: %s\\n\", an\n fail\n end\n end\n #\n ^^var pTab = []^^\n ^^for (p in extBrd.pins) { pTab[p] = true; pTab.push(p) }^^\n ^^baseBrd.pins = {}^^ if ^^!baseBrd.pins^^\n for p in pins\n ^^baseBrd.pins[p] = extBrd.pins[p]^^ if ^^extBrd.pins && p in extBrd.pins^^\n ^^pTab[p] = false^^\n end\n for i: uint8 = 0; i < ^^pTab.length^^; i++\n var pn: string = ^^pTab[i]^^\n continue if ^^pn[0] == '$'^^\n if ^^pTab[pn]^^\n printf \"*** unknown pin name: %s\\n\", pn\n fail\n end\n end\n #\n ^^baseBrd.drvDescs = extBrd.drvDescs^^ if ^^extBrd.drvDescs^^\n ^^baseBrd.nvsFiles = extBrd.nvsFiles^^ if ^^extBrd.nvsFiles^^\nend\n\ndef mergeDb(baseDb, extDb)\n ^^var brdSet = {}^^\n ^^var brdArr = []^^\n ^^for (b in baseDb) brdSet[b] = true^^\n ^^for (b in extDb) brdSet[b] = true^^\n ^^for (b in brdSet) brdArr.push(b)^^\n for i: uint8 = 0; i < ^^brdArr.length^^; i++\n ^^var brdKind = brdArr[i]^^\n ^^var baseBrd = baseDb[brdKind]^^\n ^^var extBrd = extDb[brdKind]^^\n if ^baseBrd && ^extBrd\n if ^^extBrd.$overrides^^\n mergeBrd(^baseBrd, ^extBrd)\n else\n printf \"*** missing '$overrides' for %s\\n\", ^brdKind\n fail\n end\n elif ^extBrd\n if ^^extBrd.$overrides^^\n printf \"*** no platform definition for '$overrides' for %s\\n\", ^brdKind\n fail\n else\n ^^baseDb[brdKind] = extDb[brdKind]^^\n end\n end\n end\nend\n\ndef readRecordH()\n init()\n if boardKind == null\n printf \"*** null board kind\\n\"\n fail\n end\n var rec: Record& = cacheGet(boardKind)\n return rec if rec\n var baseLoc: string = ^em$find(baseFileLoc)\n var baseDb: ptr_t = ^^$Yaml.load($Fs.readFileSync(baseLoc), 'utf-8')^^\n if localFileLoc != null\n var localDb: ptr_t = ^^$Yaml.load($Fs.readFileSync(localFileLoc), 'utf-8')^^\n mergeDb(baseDb, localDb) \n end\n var set: ptr_t = ^^new Set^^\n var base: ptr_t = collapse(boardKind, baseDb, set)\n var chain: ptr_t = ^^Array.from(set.values()).join('--')^^\n ^^em$props.set^^(PROP_BOARD_CHAIN, chain)\n rec = new <Record>\n rec.pinMap = new <PinMap>\n for a in attrs\n ^^rec[a] = base[a]^^\n end\n for p in pins\n ^^rec.pinMap[p] = base.pins[p]^^\n end\n ^^for (n in base.drvDescs) {^^\n ^^var o = base.drvDescs[n]^^\n rec.drvDescs[rec.drvDescs.length++] = new <DrvDesc> {\n name: ^n, driver: ^^o.driver^^, params: ^^o.params^^\n }\n ^^}^^\n cacheSet(boardKind, rec)\n return rec\nend\n
"},{"location":"cargo/em.core/em.utils/BoardMeta/","title":"BoardMeta","text":""},{"location":"cargo/em.core/em.utils/BoardMeta/#unit-boardmeta","title":"unit BoardMeta","text":"em.utils/BoardMeta.empackage em.utils\n\nhost module BoardMeta\n\n type PinMap: struct\n pin: int8\n end\n\n type DrvDesc: struct\n name: string\n driver: string\n params: ptr_t\n end \n\n type Record: struct\n attr: bool\n pinMap: PinMap&\n drvDescs: DrvDesc&[]\n end\n\n config baseFileLoc: string = \"biz.biosbob.distro.axm0f343/em-boards\"\n\n config attrNames: string[] = [\n \"$inherits\",\n \"$overrides\",\n \"attr\",\n ]\n\n config pinNames: string[] = [\n \"pin\",\n ] \n\nend\n
"},{"location":"cargo/em.core/em.utils/Bootup/","title":"Bootup","text":""},{"location":"cargo/em.core/em.utils/Bootup/#unit-bootup","title":"unit Bootup","text":"em.utils/Bootup.empackage em.utils\n\nmodule Bootup\n\n type Fxn: function()\n\n host function addFxnH(fxn: Fxn)\n\n function exec()\n\nprivate:\n\n config FXNTAB: Fxn volatile[]\n config fxnCnt: uint8\n\nend\n\ndef em$construct()\n FXNTABootMemory = true\nend\n\ndef addFxnH(fxn)\n FXNTAB[fxnCnt++] = fxn\nend\n\ndef exec()\n for auto i = 0; i < fxnCnt; i++\n FXNTAB[i]()\n end\nend\n
"},{"location":"cargo/em.core/em.utils/BufPrint/","title":"BufPrint","text":""},{"location":"cargo/em.core/em.utils/BufPrint/#unit-bufprint","title":"unit BufPrint","text":"em.utils/BufPrint.empackage em.utils\n\nmodule BufPrint\n\n config enabled: bool\n\n function bswap(ptr: ptr_t, cnt: uint16, lab: string = null)\n function bytes(ptr: ptr_t, cnt: uint16, lab: string = null)\n function words(ptr: ptr_t, cnt: uint16, lab: string = null)\n\nprivate:\n\n function label(lab: string)\n\nend\n\ndef bswap(ptr, cnt, lab)\n if enabled\n label(lab) if lab\n printf \"[\"\n auto pb = <uint8*>ptr\n auto wc = ((cnt + 0x3) & ~0x3) / 4\n auto sep = \"\"\n while wc--\n printf \"%s\", sep\n sep = \", \"\n var buf: uint8[4]\n buf[0] = *pb++\n buf[1] = *pb++\n buf[2] = *pb++\n buf[3] = *pb++\n auto nb = cnt >= 4 ? 4 : cnt\n cnt -= nb\n auto idx = 3\n while nb--\n printf \"%02x\", buf[idx--]\n end\n end\n printf \"]\\n\"\n end\nend\n\ndef bytes(ptr, cnt, lab)\n if enabled\n label(lab) if lab\n auto pb = <uint8*>ptr\n while cnt--\n printf \"%02x\", *pb++\n end\n printf \"\\n\"\n end\nend\n\ndef label(lab)\n printf \"%s = \", lab\nend\n\ndef words(ptr, cnt, lab)\n if enabled\n label(lab) if lab\n printf \"[\"\n auto pw = <uint32*>ptr\n auto sep = \"\"\n while cnt--\n printf \"%s%08x\", sep, *pw++\n sep = \", \"\n end\n printf \"]\\n\"\n end\nend\n
"},{"location":"cargo/em.core/em.utils/ButtonT/","title":"ButtonT","text":""},{"location":"cargo/em.core/em.utils/ButtonT/#unit-buttont","title":"unit ButtonT","text":"em.utils/ButtonT.empackage em.utils\n\ntemplate ButtonT\n\nend\n\ndef em$generateUnit(pn, un)\n|->>>\n ## ---- generated by em.utils/ButtonT ---- ##\n package `pn`\n\n from em.hal import ButtonI\n from em.hal import GpioEdgeDetectMinI\n\n from em.mcu import Poller\n\n from em.utils import FiberMgr\n\n module `un`: ButtonI\n # ^| implements the ButtonI interface\n proxy Edge: GpioEdgeDetectMinI\n # ^| a GPIO with edge-detection capabilities\n private:\n\n function buttonHandler: Edge.Handler\n function debounceFB: FiberMgr.FiberBodyFxn\n\n config debounceF: FiberMgr.Fiber&\n\n var curDuration: uint16\n var curCb: OnPressedCB\n var maxDur: uint16\n var minDur: uint16\n\n end\n\n def em$construct()\n Edge.setDetectHandlerH(buttonHandler)\n debounceF = FiberMgr.createH(debounceFB)\n end \n\n def em$startup()\n Edge.makeInput()\n Edge.setInternalPullup(true)\n Edge.setDetectFallingEdge()\n end\n\n def buttonHandler()\n Edge.clearDetect()\n debounceF.post() if curCb\n end\n\n def debounceFB(arg)\n curDuration = 0\n for ;;\n Poller.pause(minDur)\n return if curDuration == 0 && !isPressed()\n curDuration += minDur\n break if !isPressed() || curDuration >= maxDur\n end\n curCb()\n end\n\n def isPressed()\n return !Edge.get()\n end\n\n def onPressed(cb, minDurationMs, maxDurationMs)\n curCb = cb\n maxDur = maxDurationMs\n minDur = minDurationMs\n if cb == null\n Edge.disableDetect()\n else\n Edge.enableDetect()\n end\n end\n|-<<<\nend\n
"},{"location":"cargo/em.core/em.utils/Checksum/","title":"Checksum","text":""},{"location":"cargo/em.core/em.utils/Checksum/#unit-checksum","title":"unit Checksum","text":"em.utils/Checksum.empackage em.utils\n\n# Fletcher-16 checksum\n\nmodule Checksum\n\n type Obj: opaque\n function addData(buf: uint8*, len: uint16)\n function clear()\n function getSum8(): uint8\n function getSum16(): uint16\n end\n\nprivate:\n\n def opaque Obj\n sum1: uint16\n sum2: uint16\n end\n\nend\n\ndef Obj.addData(ptr, len)\n auto tl = <uint8>0\n while len\n tl = len >= 20 ? 20 : len\n len -= tl\n for ;;\n this.sum2 += this.sum1 += *ptr++\n tl -= 1\n break if tl == 0\n end\n this.sum1 = (this.sum1 & 0xFF) + (this.sum1 >> 8)\n this.sum2 = (this.sum2 & 0xFF) + (this.sum2 >> 8)\n end\n this.sum1 = (this.sum1 & 0xFF) + (this.sum1 >> 8)\n this.sum2 = (this.sum2 & 0xFF) + (this.sum2 >> 8)\n#/*\n for i: uint16 = 0; i < len; i++\n this.sum1 += *ptr++\n this.sum1 -= 255 if this.sum1 >= 255;\n this.sum2 += this.sum1;\n this.sum2 -= 255 if this.sum2 >= 255;\n end\n#*/ \nend\n\ndef Obj.clear()\n this.sum1 = this.sum2 = 0xFF\nend\n\ndef Obj.getSum8()\n return <uint8>(this.sum1 ^ this.sum2)\nend\n\ndef Obj.getSum16()\n return this.sum2 << 8 | this.sum1\nend\n
"},{"location":"cargo/em.core/em.utils/ConsoleProtocol/","title":"ConsoleProtocol","text":""},{"location":"cargo/em.core/em.utils/ConsoleProtocol/#unit-consoleprotocol","title":"unit ConsoleProtocol","text":"em.utils/ConsoleProtocol.empackage em.utils\n\nmodule ConsoleProtocol\n\n const SOT_BYTE: uint8 = 0x3\n const SOT_COUNT: uint8 = 13\n\n const EOT_BYTE: uint8 = 0x4\n\nend\n
"},{"location":"cargo/em.core/em.utils/Copier/","title":"Copier","text":""},{"location":"cargo/em.core/em.utils/Copier/#unit-copier","title":"unit Copier","text":"em.utils/Copier.empackage em.utils\n\nfrom em.hal import CopierI\n\nmodule Copier: CopierI\n\nend\n\ndef exec(dst, src, cnt)\n ^memcpy(dst, src, cnt)\nend\n
"},{"location":"cargo/em.core/em.utils/Crc16/","title":"Crc16","text":""},{"location":"cargo/em.core/em.utils/Crc16/#unit-crc16","title":"unit Crc16","text":"em.utils/Crc16.empackage em.utils\n\nmodule Crc16\n\n type Obj: opaque\n function addByte(b: uint8): uint8\n function addData(src: uint8*, len: uint8)\n function getSum(): uint16\n function getSumLsb(): uint8\n function getSumMsb(): uint8\n function init()\n end\n\n host function createH(): Obj&\n\nprivate:\n\n const POLY: uint16 = 0x8005\n\n def opaque Obj\n sum: uint16\n function update(b: uint8)\n end\n\nend\n\ndef createH()\n return new<Obj>\nend\n\ndef Obj.addByte(b)\n this.update(b)\n return b\nend\n\ndef Obj.addData(src, len)\n while len--\n this.update(*src++)\n end\nend\n\ndef Obj.getSum()\n return this.sum\nend\n\ndef Obj.getSumLsb()\n return <uint8>(this.sum & 0xFF)\nend\n\ndef Obj.getSumMsb()\n return <uint8>(this.sum >> 8)\nend\n\ndef Obj.init()\n this.sum = 0xFFFF\nend\n\ndef Obj.update(b)\n auto tot = this.sum\n for auto i = 0; i < 8; i++\n if ((tot & 0x8000) >> 8) ^ (b & 0x80)\n tot = (tot << 1) ^ POLY\n else\n tot = (tot << 1)\n end\n b <<= 1\n end\n this.sum = tot\nend\n
"},{"location":"cargo/em.core/em.utils/DebugPinAux/","title":"DebugPinAux","text":""},{"location":"cargo/em.core/em.utils/DebugPinAux/#unit-debugpinaux","title":"unit DebugPinAux","text":"em.utils/DebugPinAux.empackage em.utils\n\nfrom em.mcu import Common\n\nmodule DebugPinAux\n\n type ToggleFxn: function()\n\n config pulseDelay: int32 = -1\n\n function pulse(toggleFxn: ToggleFxn)\n function mark(toggleFxn: ToggleFxn, k: uint8)\n\nprivate:\n\n function delay()\n\nend\n\ndef delay()\n ## TODO -- finer granularity\n Common.BusyWait.wait(<uint32>(-pulseDelay))\nend\n\ndef pulse(toggleFxn)\n toggleFxn()\n delay()\n toggleFxn()\n delay()\n\nend\n\ndef mark(toggleFxn, k)\n while k--\n pulse(toggleFxn)\n end\nend\n
"},{"location":"cargo/em.core/em.utils/DebugPinT/","title":"DebugPinT","text":""},{"location":"cargo/em.core/em.utils/DebugPinT/#unit-debugpint","title":"unit DebugPinT","text":"em.utils/DebugPinT.empackage em.utils\n\ntemplate DebugPinT\n\nend\n\ndef em$generateUnit(pn, un)\n|->>>\npackage `pn`\n\nfrom em.utils import DebugPinAux as Aux\nfrom em.lang import DebugPinI\nfrom em.hal import GpioI\n\nmodule `un`: DebugPinI\n\n proxy Pin: GpioI\n\nend\n\ndef clear()\n Pin.set()\nend\n\ndef get()\n return Pin.get() != 0\nend\n\ndef set()\n Pin.clear()\nend\n\ndef toggle()\n Pin.toggle()\nend\n\ndef pulse()\n Aux.pulse(<Aux.ToggleFxn>toggle)\nend\n\ndef mark(k)\n Aux.mark(<Aux.ToggleFxn>toggle, k)\nend\n\ndef reset()\n Pin.reset()\nend\n\ndef startup()\n Pin.makeOutput()\n Pin.set()\nend\n|-<<<\nend\n
"},{"location":"cargo/em.core/em.utils/EpochTime/","title":"EpochTime","text":""},{"location":"cargo/em.core/em.utils/EpochTime/#unit-epochtime","title":"unit EpochTime","text":"em.utils/EpochTime.empackage em.utils\n\nfrom em.hal import UptimerI\n\nmodule EpochTime\n\n proxy Uptimer: UptimerI\n\n type UpdateFxn: function(esecs: uint32, esubs: uint32)\n\n host function bindUpdateFxnH(fxn: UpdateFxn)\n\n function getCurrent(oSubs: uint32* = null): uint32\n function getRaw(oSubs: uint32* = null): uint32\n function mkSecs256(secs: uint32, subs: uint32): uint32\n function setCurrent(eSecs: uint32, eSubs: uint32 = 0, syncFlag: bool = false)\n\nprivate:\n\n config updateFxn: UpdateFxn\n\n var deltaSecs: uint32\n var deltaSubs: uint32\n\n var lastSecs256: uint32\n var lastTicks: uint32\n\n function compute(time: Uptimer.Time&, oSubs: uint32*): uint32\n\nend\n\ndef bindUpdateFxnH(fxn)\n updateFxn = fxn\nend\n\ndef compute(time, oSubs)\n auto eSubs = time.subs + deltaSubs\n auto inc = eSubs < time.subs ? 1 : 0\n (*oSubs) = eSubs if oSubs\n return time.secs + deltaSecs + inc\nend\n\ndef getCurrent(oSubs)\n return compute(Uptimer.read(), oSubs)\nend\n\ndef getRaw(oSubs)\n auto time = Uptimer.read()\n *oSubs = time.subs if oSubs\n return time.secs\nend\n\ndef mkSecs256(secs, subs)\n return (secs << 8) | (subs >> 24)\nend\n\ndef setCurrent(eSecs, eSubs, syncFlag)\n auto time = Uptimer.read()\n deltaSubs = eSubs - time.subs\n auto dec = deltaSubs > eSubs ? 1 : 0\n deltaSecs = eSecs - time.secs - dec\n auto eSecs256 = mkSecs256(eSecs, eSubs)\n if syncFlag\n Uptimer.calibrate(eSecs256 - lastSecs256, time.ticks - lastTicks) if lastSecs256\n lastSecs256 = eSecs256\n lastTicks = time.ticks\n else\n lastSecs256 = lastTicks = 0\n end\n var subs: uint32\n auto secs = compute(time, &subs)\n updateFxn(eSecs, eSubs) if updateFxn\nend\n
"},{"location":"cargo/em.core/em.utils/Error/","title":"Error","text":""},{"location":"cargo/em.core/em.utils/Error/#unit-error","title":"unit Error","text":"em.utils/Error.empackage em.utils\n\nimport Logger\n\nmodule Error\n\n type Kind: enum\n APPLICATION, ASSERTION, EXCEPTION, EXPIRATION, WATCHDOG\n end\n\n type RaiseFxn: function (kind: Kind, infoA: iarg_t, infoB: iarg_t)\n\n host function bindOnRaiseH(fxn: RaiseFxn)\n function raise: RaiseFxn\n\nprivate:\n\n config KIND_ATOM: atom_t[] = [\n @\"APPLICATION\", @\"ASSERTION\", @\"EXCEPTION\", @\"EXPIRATION\", @\"WATCHDOG\"\n ]\n\n config errorE: Logger.EventKind&\n config onRaiseFxn: RaiseFxn\n\nend\n\ndef em$construct()\n ## TODO -- atomize error kind\n errorE = Logger.declareEventH(\"ERROR: $a [0x%08x, 0x%08x]\", \"*--*\")\nend\n\ndef bindOnRaiseH(fxn)\n onRaiseFxn = fxn\nend\n\ndef raise(kind, infoA, infoB)\n errorE.log(<addr_t>KIND_ATOM[<uint8>kind], <addr_t>infoA, <addr_t>infoB)\n onRaiseFxn(kind, infoA, infoB) if onRaiseFxn\nend\n
"},{"location":"cargo/em.core/em.utils/FftC32/","title":"FftC32","text":""},{"location":"cargo/em.core/em.utils/FftC32/#unit-fftc32","title":"unit FftC32","text":"em.utils/FftC32.empackage em.utils\n\nfrom em.lang import Math\n\nmodule FftC32\n\n type Complex: class\n re: int16\n im: int16\n function get(): uint32\n function set(w: uint32)\n end\n\n config fftSize: uint16 = 128\n config shift: uint8 = 1\n\n function exec(buf: Complex[])\n\nprivate:\n\n config N_WAVE: uint16\n config N_WAVE_LOG2: uint8\n\n config SINE_WAVE: int16[]\n\n function fixMul(a: int16, b: int16): int16\n\nend\n\ndef em$construct()\n N_WAVE = fftSize\n N_WAVE_LOG2 = Math.log2(N_WAVE)\n auto numHalf = N_WAVE / 2\n auto numQtr = N_WAVE / 4\n SINE_WAVE.length = N_WAVE - numQtr\n auto rng = Math.PI / 2\n for auto i = 0; i < SINE_WAVE.length; i++\n if i <= numQtr\n auto sx = Math.sin((rng / numQtr) * i)\n SINE_WAVE[i] = Math.round(sx * 32767) >> shift\n elif i < numHalf\n SINE_WAVE[i] = SINE_WAVE[numHalf - i]\n else\n SINE_WAVE[i] = -(SINE_WAVE[i - numHalf])\n end\n end\nend\n\ndef exec(buf)\n auto mr = 0\n for auto m = 1; m < fftSize; m++\n auto l = fftSize\n for ;;\n l >>= 1\n continue if mr + l > fftSize - 1\n break\n end\n mr = (mr & (l - 1)) + l\n continue if mr <= m\n auto t = buf[m].get()\n buf[m].set(buf[mr].get())\n buf[mr].set(t)\n end\n auto stage = 1\n auto sineStep = N_WAVE_LOG2 - 1\n while stage < fftSize\n auto twiddleStep = stage << 1\n for auto grp = 0; grp < stage; grp++\n auto idx = grp << sineStep\n auto wr = SINE_WAVE[idx + N_WAVE / 4]\n auto wi = -SINE_WAVE[idx]\n for auto i = grp; i < fftSize; i += twiddleStep\n auto j = i + stage\n auto fci = &buf[i]\n auto fcj = &buf[j]\n auto tr = fixMul(wr, fcj.re) - fixMul(wi, fcj.im)\n auto ti = fixMul(wr, fcj.im) + fixMul(wi, fcj.re)\n auto qr = fci.re >> shift\n auto qi = fci.im >> shift\n fcj.re = qr - tr\n fcj.im = qi - ti\n fci.re = qr + tr\n fci.im = qi + ti\n end\n end\n sineStep -= 1\n stage = twiddleStep\n end\nend\n\n\ndef fixMul(a, b)\n auto c = (<int32>a * <int32>b) >> 14\n b = <int16>(<uint32>c & 0x01)\n a = <int16>((c >> 1) + b)\n return a\nend\n\ndef Complex.get()\n return *(<uint32*>this)\nend\n\ndef Complex.set(w)\n *(<uint32*>this) = w\nend\n
"},{"location":"cargo/em.core/em.utils/FftQ15/","title":"FftQ15","text":""},{"location":"cargo/em.core/em.utils/FftQ15/#unit-fftq15","title":"unit FftQ15","text":"em.utils/FftQ15.empackage em.utils\n\nfrom em.lang import Math\n\nmodule FftQ15\n\n config fftSize: uint16 = 128\n config shift: uint8 = 1\n\n function exec(fr: int16[], fi: int16[])\n\nprivate:\n\n config N_WAVE: uint16\n config N_WAVE_LOG2: uint8\n\n config SINE_WAVE: int16[]\n\n function fixMul(a: int16, b: int16): int16\n\nend\n\ndef em$construct()\n N_WAVE = fftSize\n N_WAVE_LOG2 = Math.log2(N_WAVE)\n auto numHalf = N_WAVE / 2\n auto numQtr = N_WAVE / 4\n SINE_WAVE.length = N_WAVE - numQtr\n auto rng = Math.PI / 2\n for auto i = 0; i < SINE_WAVE.length; i++\n if i <= numQtr\n auto sx = Math.sin((rng / numQtr) * i)\n SINE_WAVE[i] = Math.round(sx * 32767) >> shift\n elif i < numHalf\n SINE_WAVE[i] = SINE_WAVE[numHalf - i]\n else\n SINE_WAVE[i] = -(SINE_WAVE[i - numHalf])\n end\n end\nend\n\ndef exec(fr, fi)\n auto mr = 0\n for auto m = 1; m < fftSize; m++\n auto l = fftSize\n for ;;\n l >>= 1\n continue if mr + l > fftSize - 1\n break\n end\n mr = (mr & (l - 1)) + l\n continue if mr <= m\n auto tr = fr[m]\n fr[m] = fr[mr]\n fr[mr] = tr\n auto ti = fi[m]\n fi[m] = fi[mr]\n fi[mr] = ti\n end\n auto stage = 1\n auto sineStep = N_WAVE_LOG2 - 1\n while stage < fftSize\n auto twiddleStep = stage << 1\n for auto grp = 0; grp < stage; grp++\n auto idx = grp << sineStep\n auto wr = SINE_WAVE[idx + N_WAVE / 4]\n auto wi = -SINE_WAVE[idx]\n for auto i = grp; i < fftSize; i += twiddleStep\n auto j = i + stage\n auto tr = fixMul(wr, fr[j]) - fixMul(wi, fi[j])\n auto ti = fixMul(wr, fi[j]) + fixMul(wi, fr[j])\n auto qr = fr[i] >> shift\n auto qi = fi[i] >> shift\n fr[j] = qr - tr\n fi[j] = qi - ti\n fr[i] = qr + tr\n fi[i] = qi + ti\n end\n end\n sineStep -= 1\n stage = twiddleStep\n end\nend\n\ndef fixMul(a, b)\n auto c = (<int32>a * <int32>b) >> 14\n b = <int16>(<uint32>c & 0x01)\n a = <int16>((c >> 1) + b)\n return a\nend\n
"},{"location":"cargo/em.core/em.utils/FiberMgr/","title":"FiberMgr","text":""},{"location":"cargo/em.core/em.utils/FiberMgr/#unit-fibermgr","title":"unit FiberMgr","text":"em.utils/FiberMgr.empackage em.utils\n\nfrom em.mcu import Common\nfrom em.utils import ListMgr\n\nmodule FiberMgr\n # ^| manages opaque fiber objects\n type FiberBodyFxn: function(arg: uarg_t)\n # ^| function signature of fiber body\n type Fiber: opaque\n # ^| opaque fiber object - public specification\n host function initH(fxn: FiberBodyFxn, arg: uarg_t = 0)\n # ^| initialize this fiber and bind its function and argument\n function getArg(): uarg_t\n # ^| get this fiber's body function argument\n function getFxn(): FiberBodyFxn\n # ^| get this fiber's body function\n function post()\n # ^| make this fiber ready-to-run\n function setArg(a: uarg_t)\n # ^| set this fiber's body function argument\n function setFxn(f: FiberBodyFxn)\n # ^| set this fiber's body function\n end\n\n host function createH(fxn: FiberBodyFxn, arg: uarg_t = 0): Fiber&\n # ^| allocate and initialize a fiber; see Fiber.initH\n function run()\n # ^| initiate dispatch of ready-to-run fibers\nprivate:\n\n def opaque Fiber\n elem: ListMgr.Element\n fxn_: FiberBodyFxn\n arg_: uarg_t\n end\n\n function dispatch()\n\n var fiberTab: Fiber[]\n var readyList: ListMgr.List\n\nend\n\ndef em$construct() \n readyList.initH()\nend\n\ndef createH(fxn, arg)\n var fiber: Fiber& = fiberTab[fiberTab.length++]\n fiber.initH(fxn, arg)\n return fiber\nend\n\ndef dispatch()\n for ;;\n break if readyList.hasElements() == 0\n auto fiber = <Fiber&>readyList.get()\n Common.GlobalInterrupts.enable()\n auto fxn = fiber.fxn_\n fxn(fiber.arg_)\n Common.GlobalInterrupts.disable()\n end \nend\n\ndef run()\n Common.Idle.wakeup()\n Common.GlobalInterrupts.enable()\n for ;;\n Common.GlobalInterrupts.disable()\n dispatch()\n Common.Idle.exec()\n end\nend\n\ndef Fiber.initH(fxn, arg)\n this.elem.initH()\n this.fxn_ = fxn\n this.arg_ = arg\nend\n\ndef Fiber.post()\n auto key = Common.GlobalInterrupts.disable()\n readyList.add(this.elem) if !this.elem.isActive()\n Common.GlobalInterrupts.restore(key)\nend\n\ndef Fiber.getArg()\n return this.arg_\nend\n\ndef Fiber.setArg(a)\n this.arg_ = a\nend\n\ndef Fiber.setFxn(f)\n this.fxn_ = f\nend\n\ndef Fiber.getFxn()\n return this.fxn_\nend\n
"},{"location":"cargo/em.core/em.utils/Formatter/","title":"Formatter","text":""},{"location":"cargo/em.core/em.utils/Formatter/#unit-formatter","title":"unit Formatter","text":"em.utils/Formatter.empackage em.utils\n\nfrom em.lang import Console\n\nmodule Formatter\n\n function print(fmt: string, a1: iarg_t = 0, a2: iarg_t = 0, a3: iarg_t = 0, a4: iarg_t = 0, a5: iarg_t = 0, a6: iarg_t = 0)\n function puts(s: string)\n\nprivate:\n\n const OUTMAX: uint8 = ((32 + 2) / 3) + 5\n\n function c2d(c: char): uint8\n function formatNum(buf: char*, num: uint32, base: uint8, pad: char, len: uint8): char*\n function isDigit(c: char): bool\n\n config hexDigs: string = \"0123456789abcdef\"\n\nend\n\ndef c2d(c)\n return c - '0'\nend\n\ndef formatNum(buf, num, base, pad, len)\n auto cnt = len\n *(--buf) = 0\n for ;;\n *(--buf) = hexDigs[<uint8> (num % base)]\n num /= base\n break if len > 0 && --cnt == 0\n break if num == 0\n end\n while cnt-- > 0\n *(--buf) = pad\n end\n return buf\nend\n\ndef isDigit(c)\n return c >= '0' && c <= '9'\nend\n\ndef print(fmt, a1, a2, a3, a4, a5, a6)\n var ch: char\n var buf: char[OUTMAX]\n var args: iarg_t[6]\n var argp: iarg_t* = &args[0]\n args[0] = a1\n args[1] = a2\n args[2] = a3\n args[3] = a4\n args[4] = a5\n args[5] = a6\n while (ch = *fmt++) != 0\n auto pad = ' '\n auto len = 0\n if (ch != '%') \n Console.wrC(ch)\n continue\n end\n ch = *fmt++\n if ch == '0'\n pad = '0'\n ch = *fmt++\n end\n while isDigit(ch)\n len = (len * 10) + c2d(ch)\n ch = *fmt++\n end\n var out: char*\n if ch == 'd'\n var dn: int32 = <int32> *argp++\n if dn < 0\n Console.wrC('-')\n dn = -dn\n end\n out = formatNum(&buf[OUTMAX], <uint32> dn, 10, pad, len)\n elif ch == 'x' \n var xn: uint32 = <uint32> *argp++\n out = formatNum(&buf[OUTMAX], xn, 16, pad, len)\n elif ch == 's'\n out = <char*> *argp++\n else\n Console.wrC(ch == 'c' ? <char> *argp++ : ch)\n continue\n end\n puts(<string>out)\n end\nend\n\ndef puts(s)\n var cp: char* = <char*>s\n var ch: char\n while (ch = *cp++) != 0\n Console.wrC(ch)\n end\nend\n
"},{"location":"cargo/em.core/em.utils/FormattingConsole/","title":"FormattingConsole","text":""},{"location":"cargo/em.core/em.utils/FormattingConsole/#unit-formattingconsole","title":"unit FormattingConsole","text":"em.utils/FormattingConsole.empackage em.utils\n\nfrom em.lang import ConsoleProviderI\n\nfrom em.mcu import ConsoleUart\n\nimport Formatter\n\nmodule FormattingConsole: ConsoleProviderI\n\nend\n\ndef flush()\n ConsoleUart.flush()\nend\n\ndef print(fmt, a1, a2, a3, a4, a5, a6)\n Formatter.print(fmt, a1, a2, a3, a4, a5, a6)\nend\n\ndef put(data)\n ConsoleUart.put(data)\nend\n
"},{"location":"cargo/em.core/em.utils/HeapMem/","title":"HeapMem","text":""},{"location":"cargo/em.core/em.utils/HeapMem/#unit-heapmem","title":"unit HeapMem","text":"em.utils/HeapMem.empackage em.utils\n\nfrom em.mcu import Common\nfrom em.mcu import Copier\n\nmodule HeapMem\n\n config baseAddr: addr_t\n config maxBytes: uint16\n\n function alloc(size: uint16): ptr_t\n function avail(): uint16\n function mark()\n function release()\n\nprivate:\n\n var curTopPtr: uint32*\n var savTopPtr: uint32*\n\nend\n\ndef em$startup()\n return if Common.Mcu.isWarm()\n curTopPtr = baseAddr\nend\n\ndef alloc(size)\n auto ptr = curTopPtr\n auto wc = ((size + 3) & ~0x3) / 4\n curTopPtr += wc\n return ptr\nend\n\ndef avail()\n return <uint16>((baseAddr + maxBytes) - <uint32>curTopPtr)\nend\n\ndef mark()\n *curTopPtr = <uint32>savTopPtr\n savTopPtr = curTopPtr++\nend\n\ndef release()\n fail if !savTopPtr\n curTopPtr = savTopPtr\n savTopPtr = <uint32*>*curTopPtr\n\n\nend\n
"},{"location":"cargo/em.core/em.utils/HeapStatic/","title":"HeapStatic","text":""},{"location":"cargo/em.core/em.utils/HeapStatic/#unit-heapstatic","title":"unit HeapStatic","text":"em.utils/HeapStatic.empackage em.utils\n\nfrom em.mcu import Common\n\nmodule HeapStatic\n\n config baseAddr: addr_t\n config maxBytes: uint16\n\n host function allocH(size: uint16): ptr_t\n\n function getTopAddr(): addr_t\n host function getTopAddrH(): addr_t\n\nprivate:\n\n const MASK: uint32 = 0x3\n config topAddr: addr_t\nend\n\ndef em$construct()\n return if baseAddr || maxBytes == 0\n printf \"*** HeapStatic: baseAddr == 0\\n\"\n fail\nend\n\ndef em$startup()\n return if Common.Mcu.isWarm()\n ^memset(<ptr_t>baseAddr, 0, topAddr - baseAddr) if topAddr && Common.Mcu.getResetCode() <= Common.Mcu.COLD_RESET\nend\n\ndef allocH(size)\n topAddr = baseAddr if topAddr == 0\n auto p = <ptr_t>topAddr\n topAddr += size\n topAddr = (topAddr + MASK) & ~MASK\n return p if (topAddr - baseAddr) < maxBytes\n printf \"*** HeapStatic.allocH: maxBytes = %d, size = %d\\n\", maxBytes, size\n fail\nend\n\ndef getTopAddr()\n return topAddr\nend\n\ndef getTopAddrH()\n return topAddr\nend\n
"},{"location":"cargo/em.core/em.utils/LedBlinkerI/","title":"LedBlinkerI","text":""},{"location":"cargo/em.core/em.utils/LedBlinkerI/#unit-ledblinkeri","title":"unit LedBlinkerI","text":"em.utils/LedBlinkerI.empackage em.utils\n\nfrom em.hal import LedI\n\ninterface LedBlinkerI: LedI\n\n function blink(count: uint16, rateSecs: uint16 = 1, rateMs: uint16 = 0)\n\nend\n
"},{"location":"cargo/em.core/em.utils/LedBlinkerT/","title":"LedBlinkerT","text":""},{"location":"cargo/em.core/em.utils/LedBlinkerT/#unit-ledblinkert","title":"unit LedBlinkerT","text":"em.utils/LedBlinkerT.empackage em.utils\n\ntemplate LedBlinkerT\n\nend\n\ndef em$generateUnit(pn, un)\n|->>>\npackage `pn`\n\nfrom em.hal import LedI\n\nfrom em.utils import LedBlinkerAux\nfrom em.utils import LedBlinkerI\n\nmodule `un`: LedBlinkerI\n\n proxy Led: LedI\n\nend\n\ndef isOn()\n return Led.isOn()\nend\n\ndef on()\n Led.on()\nend\n\ndef off()\n Led.off()\nend\n\ndef toggle()\n Led.toggle()\nend\n\ndef blink(count, rateSecs, rateMs)\n LedBlinkerAux.setFxns(Led.on, Led.off)\n LedBlinkerAux.blink(count, rateSecs, rateMs)\nend\n\ndef wink(onMs, offMs)\n LedBlinkerAux.setFxns(Led.on, Led.off)\n LedBlinkerAux.wink(onMs, offMs)\nend\n|-<<<\nend\n
"},{"location":"cargo/em.core/em.utils/LedT/","title":"LedT","text":""},{"location":"cargo/em.core/em.utils/LedT/#unit-ledt","title":"unit LedT","text":"em.utils/LedT.empackage em.utils\n\ntemplate LedT \n\nend\n\ndef em$generateUnit(pn, un)\n|->>>\n package `pn`\n\n from em.hal import LedI\n from em.hal import GpioI\n\n from em.mcu import Poller\n\n module `un`: LedI\n proxy Pin: GpioI\n config activeLow: bool = false\n end\n\n def em$startup()\n Pin.makeOutput()\n if activeLow\n Pin.set() \n else \n Pin.clear() \n end\n end\n\n def on()\n if activeLow\n Pin.clear() \n else \n Pin.set() \n end \n end\n\n def off()\n if activeLow\n Pin.set() \n else \n Pin.clear() \n end \n end\n\n def toggle()\n Pin.toggle()\n end\n\n def isOn()\n return activeLow ? !Pin.get() : Pin.get()\n end\n\n def wink(msecs)\n on()\n Poller.pause(msecs)\n off()\n end \n|-<<<\nend\n
"},{"location":"cargo/em.core/em.utils/ListManagerI/","title":"ListManagerI","text":""},{"location":"cargo/em.core/em.utils/ListManagerI/#unit-listmanageri","title":"unit ListManagerI","text":"em.utils/ListManagerI.empackage em.utils\n\n#! Implemented by a List Manager module\n#! Element and List declarations.\n\ninterface ListManagerI \n\n type Element: opaque \n\n #! Target and Host function to initialize an Element\n function init()\n host function initH()\n\n #! Returns non-null if the Element is currently a member of a List\n function isActive(): uarg_t\n end\n\n type List: opaque \n\n #! Target and Host function to initialize a List\n function init()\n host function initH()\n\n #! Adds an element to the List as defined by the List Manager\n function add(elem: Element&)\n\n #! Returns a reference to an Element and removes it from the List\n function get(): ref_t\n\n #! Returns a reference to the Element at the specified index\n function getAt(index: uint8): ref_t\n\n #! Returns a reference to the next Element from the specified Element\n function getNext(elem: Element&): ref_t\n\n #! Returns a non-zero value if the List is not empty\n function hasElements(): uarg_t\n\n #! Displays information about the List\n function print()\n\n #! Removes the specified Element from the List\n function remove(elem: Element&)\n end\nend\n
"},{"location":"cargo/em.core/em.utils/ListMgr/","title":"ListMgr","text":""},{"location":"cargo/em.core/em.utils/ListMgr/#unit-listmgr","title":"unit ListMgr","text":"em.utils/ListMgr.empackage em.utils\n\nmodule ListMgr\n\n type Element: opaque \n\n function init()\n host function initH()\n\n function isActive(): uarg_t\n end\n\n type List: opaque \n\n function init()\n host function initH()\n\n function add(elem: Element&)\n function get(): ref_t\n function getAt(index: uint8): ref_t\n function getNext(elem: Element&): ref_t\n function hasElements(): uarg_t\n function print()\n function remove(elem: Element&)\n end\n\nprivate:\n\n def opaque Element \n next: Element& volatile\n end\n\n def opaque List \n first: Element& volatile\n last: Element& volatile\n end\nend\n\ndef Element.init() \n this.next = null\nend\n\ndef Element.initH() \n this.next = null\nend\n\ndef Element.isActive() \n return <uarg_t> this.next\nend\n\ndef List.init() \n this.first = this.last = <Element&> &this.first\nend\n\ndef List.initH() \n this.first = this.last = <Element&> &this.first;\nend\n\ndef List.add(elem)\n this.last.next = elem\n this.last = elem\n elem.next = <Element&> this\nend\n\ndef List.get() \n auto elem = this.first\n this.last = <Element&>this if (this.first = elem.next) == <Element&>this\n elem.next = null\n return elem\nend\n\ndef List.getAt(index)\n auto elem = this.first\n auto i = 0\n if this.hasElements()\n for ;;\n break if i++ == index \n elem = elem.next \n break if elem == <Element&> this\n end\n elem = null if elem == <Element&> this\n else \n elem = null\n end\n return elem\nend\n\ndef List.getNext(elem) \n return elem == this.last ? null : elem.next\nend\n\ndef List.hasElements() \n return (<uarg_t> this.first) ^ <uarg_t> this\nend\n\ndef List.print() \n auto elem = this.first\n auto i = 0\n if this.hasElements()\n for ;;\n printf \"elem%d %p\\n\", i++, elem\n elem = elem.next\n break if elem == <Element&> this\n end\n else \n printf \"list empty\\n\"\n end\n printf \"\\n\" \nend\n\ndef List.remove(elem)\n auto e = this.first\n if this.hasElements()\n if elem == this.first\n this.first = this.first.next\n else \n for ;; \n if e.next == elem\n e.next = elem.next \n break \n end \n break if e == <Element&> this\n end\n end\n end\nend\n
"},{"location":"cargo/em.core/em.utils/LoaderAuxI/","title":"LoaderAuxI","text":""},{"location":"cargo/em.core/em.utils/LoaderAuxI/#unit-loaderauxi","title":"unit LoaderAuxI","text":"em.utils/LoaderAuxI.empackage em.utils\n\ninterface LoaderAuxI\n\n function jumpTo(codeAddr: addr_t)\n\nend\n
"},{"location":"cargo/em.core/em.utils/Logger/","title":"Logger","text":""},{"location":"cargo/em.core/em.utils/Logger/#unit-logger","title":"unit Logger","text":"em.utils/Logger.empackage em.utils\n\nfrom em.hal import FlashI\nfrom em.hal import FlashN\n\nfrom em.lang import Assert\nfrom em.mcu import Common\nfrom em.lang import Console\n\nimport EpochTime\nimport Formatter\nimport HeapStatic\n\nmodule Logger\n\n proxy Flash: FlashI\n\n type Accum: opaque\n function add(val: uint32 = 0)\n function clear()\n function getBin(idx: uint8): uint32\n function getCount(): uint32\n function getMax(): uint32\n function print()\n end\n\n type EventKind: opaque\n function cache(a1: addr_t = 0, a2: addr_t = 0, a3: addr_t = 0)\n function log(a1: addr_t = 0, a2: addr_t = 0, a3: addr_t = 0)\n function print(a1: addr_t = 0, a2: addr_t = 0, a3: addr_t = 0)\n end\n\n type Policy: enum\n NIL, QUIET, PRINT, STORE\n end\n\n config POLICY: Policy = Policy.NIL\n config ENTRY_COUNT: uint16 = 16\n config STORE_SECTOR: uint8 = 0\n\n host function createAccumH(lab: string, grp: string, lims: uint32[] = null): Accum&\n host function declareEventH(msg: string, grp: string): EventKind&\n\n function flush()\n function isEmpty(): bool\n function mkTime(): uint32\n function print()\n function putBytes(bp: uint8*, cnt: uint16)\n function putc(b: uint8)\n function store()\n\nprivate:\n\n const PKT_CODE: uint8 = 0xFE\n const EVT_CODE: uint8 = 0xFD\n const ACC_CODE: uint8 = 0xFC\n const BEG_CODE: uint8 = 0xFB\n const END_CODE: uint8 = 0xFA\n\n type Group: class\n chars: char[4]\n host function initH(gs: string)\n end\n\n def opaque Accum\n data: uint32[]\n metaIdx: uint8\n function getMeta(): AccumMeta&\n end\n\n type AccumMeta: struct\n lab: string\n lims: uint32[]\n hasMax: bool\n limCnt: uint8\n group: Group\n size: uint8\n end\n\n def opaque EventKind\n msg: string\n kidx: uint16\n group: Group\n end\n\n type EventInst: class\n time: uint32\n a1: iarg_t\n a2: iarg_t\n a3: iarg_t\n kidx: uint16\n function put()\n end\n\n type Cursor: class\n idx: uint16\n function next(): EventInst&\n end\n\n config accMetaTab: AccumMeta[]\n\n config sectBeg: addr_t\n config sectEnd: addr_t\n\n config totalAccSize: uint16\n\n var accTab: Accum&[..]\n var evkTab: EventKind&[..]\n\n var buf: EventInst[]\n var curs: Cursor&\n\nend\n\ndef em$configure()\n Flash ?= FlashN\nend\n\ndef em$construct()\n buf = HeapStatic.allocH(sizeof<EventInst> * ENTRY_COUNT) if POLICY != Policy.NIL\n curs = HeapStatic.allocH(sizeof<Cursor>) if POLICY != Policy.NIL\n sectBeg = <addr_t>(Flash.getSectorSizeH() * STORE_SECTOR)\n sectEnd = sectBeg + Flash.getSectorSizeH()\n end\n\ndef createAccumH(lab, grp, lims)\n return null if POLICY == Policy.NIL\n auto accMeta = <AccumMeta&>accMetaTab[accMetaTab.length++]\n accMeta.lab = lab\n accMeta.group.initH(grp)\n accMeta.hasMax = (lims != null)\n accMeta.lims = lims if accMeta.hasMax\n accMeta.limCnt = <uint8>lims.length if accMeta.hasMax\n accMeta.size = (1 + (!accMeta.hasMax ? 0 : (1 + accMeta.limCnt))) * sizeof<uint32>\n auto acc = new<Accum>\n accTab[accTab.length++] = acc\n acc.data = HeapStatic.allocH(accMeta.size)\n acc.metaIdx = <uint8>accMetaTab.length\n totalAccSize += accMeta.size\n return acc\nend\n\ndef declareEventH(msg, grp)\n return null if POLICY == Policy.NIL\n auto ek = new<EventKind>\n evkTab[evkTab.length++] = ek\n ek.kidx = evkTab.length\n ek.msg = <string>msg\n ek.group.initH(grp)\n return ek\nend\n\ndef flush()\n print() if POLICY == Policy.PRINT\n store() if POLICY == Policy.QUIET || POLICY == Policy.STORE\nend\n\ndef isEmpty()\n if POLICY != Policy.NIL\n for acc in accTab\n return false if acc.data[0]\n end\n for auto i = 0; i < ENTRY_COUNT; i++\n return false if buf[i].kidx != 0\n end\n end\n return true\nend\n\ndef mkTime()\n var subs: uint32\n auto secs = EpochTime.getRaw(&subs)\n return (secs << 8) | (subs >> 24)\nend\n\ndef print()\n if POLICY > Policy.QUIET\n putc(PKT_CODE)\n putc(BEG_CODE)\n for acc in accTab\n acc.print()\n end\n for auto i = 0; i < ENTRY_COUNT; i++\n auto evt = curs.next()\n continue if evt.kidx == 0\n evt.put()\n end\n putc(PKT_CODE)\n putc(END_CODE)\n end\nend\n\ndef putBytes(bp, cnt)\n while cnt--\n auto b = *bp++\n putc(PKT_CODE) if b == PKT_CODE\n putc(b)\n end\nend\n\ndef putc(b)\n Console.Provider.put(b)\nend\n\ndef store()\n if POLICY != Policy.NIL && STORE_SECTOR > 0\n auto saddr = sectBeg\n Flash.erase(saddr)\n var et: uint32 = EpochTime.getCurrent()\n saddr = Flash.write(saddr, &et, sizeof<uint32>)\n for acc in accTab\n saddr = Flash.write(saddr, &acc.data[0], acc.getMeta().size)\n end\n for auto i = 0; i < ENTRY_COUNT; i++\n auto evt = curs.next()\n continue if evt.kidx == 0\n saddr = Flash.write(saddr, evt, sizeof<EventInst>)\n evt.kidx = 0\n end\n end\nend\n\ndef Accum.add(val)\n auto accMeta = this.getMeta()\n this.data[0] += 1\n return if !accMeta.hasMax\n this.data[1] = val if val > this.data[1]\n for auto i = 0; i < accMeta.limCnt; i++\n this.data[2 + i] += 1 if val < accMeta.lims[i]\n end\nend\n\ndef Accum.clear()\n auto accMeta = this.getMeta()\n auto words = accMeta.hasMax ? (2 + accMeta.limCnt) : 1\n ^memset(&this.data[0], 0, words * sizeof<uint32>)\nend\n\ndef Accum.getBin(idx)\n return this.data[idx + 2]\nend\n\ndef Accum.getCount()\n return this.data[0]\nend\n\ndef Accum.getMax()\n return this.data[1]\nend\n\ndef Accum.getMeta()\n return <AccumMeta&>(&accMetaTab[this.metaIdx-1])\nend\n\ndef Accum.print()\n if POLICY > Policy.QUIET\n auto accMeta = this.getMeta()\n putc(PKT_CODE)\n putc(ACC_CODE)\n putc(this.metaIdx)\n putBytes(<uint8*>&this.data[0], accMeta.size)\n end\nend\n\ndef Cursor.next()\n auto evt = &buf[this.idx++]\n this.idx = 0 if this.idx >= ENTRY_COUNT\n return evt\nend\n\ndef EventInst.put()\n putc(PKT_CODE)\n putc(EVT_CODE)\n putBytes(<uint8*>this, sizeof<EventInst> - sizeof<uint16>)\nend\n\ndef EventKind.cache(a1, a2, a3)\n if POLICY != Policy.NIL && ENTRY_COUNT > 0\n auto evt = curs.next()\n evt.time = mkTime()\n evt.kidx = this.kidx\n evt.a1 = <iarg_t>a1\n evt.a2 = <iarg_t>a2\n evt.a3 = <iarg_t>a3\n end\nend\n\ndef EventKind.log(a1, a2, a3)\n this.cache(a1, a2, a3) if POLICY == Policy.STORE || POLICY == Policy.QUIET\n this.print(a1, a2, a3) if POLICY == Policy.PRINT\nend\n\ndef EventKind.print(a1, a2, a3)\n if POLICY > Policy.QUIET\n var evt: EventInst\n evt.time = mkTime()\n evt.kidx = this.kidx\n evt.a1 = <iarg_t>a1\n evt.a2 = <iarg_t>a2\n evt.a3 = <iarg_t>a3\n evt.put()\n end\nend\n\ndef Group.initH(gs)\n for auto i = 0; i < this.chars.length; i++\n this.chars[i] = ^^gs && gs[i] ? gs.charCodeAt(i) : \" \".charCodeAt(0)^^\n end\nend\n
"},{"location":"cargo/em.core/em.utils/MemDump/","title":"MemDump","text":""},{"location":"cargo/em.core/em.utils/MemDump/#unit-memdump","title":"unit MemDump","text":"em.utils/MemDump.empackage em.utils\n\nmodule MemDump\n\n function print32(addr: ptr_t, count: uint8)\n\nend\n\ndef print32(addr, count)\n auto p32 = <uint32*>addr\n while count--\n auto v = *p32\n printf \"%08x: %08x\\n\", p32, v\n p32 += 1\n end\nend\n
"},{"location":"cargo/em.core/em.utils/MinConsole/","title":"MinConsole","text":""},{"location":"cargo/em.core/em.utils/MinConsole/#unit-minconsole","title":"unit MinConsole","text":"em.utils/MinConsole.empackage em.utils\n\nfrom em.lang import ConsoleProviderI\n\nfrom em.mcu import ConsoleUart\n\nimport Formatter\n\nmodule MinConsole: ConsoleProviderI\n\nend\n\ndef flush()\n ConsoleUart.flush()\nend\n\ndef print(fmt, a1, a2, a3, a4, a5, a6)\nend\n\ndef put(data)\n ConsoleUart.put(data)\nend\n
"},{"location":"cargo/em.core/em.utils/MsCounterUptimer/","title":"MsCounterUptimer","text":""},{"location":"cargo/em.core/em.utils/MsCounterUptimer/#unit-mscounteruptimer","title":"unit MsCounterUptimer","text":"em.utils/MsCounterUptimer.empackage em.utils\n\nfrom em.hal import MsCounterI\nfrom em.hal import UptimerI\n\nmodule MsCounterUptimer: MsCounterI\n\n proxy Uptimer: UptimerI\n\nprivate:\n\n var t0: uint32\n\n function readMsecs(): uint32\n\nend\n\ndef readMsecs()\n auto time = Uptimer.read()\n return ((time.secs & 0xFF) << 16) + (time.subs >> 16)\nend\n\ndef start()\n t0 = readMsecs()\nend\n\ndef stop()\n return 0 if t0 == 0\n auto dt = readMsecs() - t0\n t0 = 0\n return (dt * 1000) >> 16\nend\n
"},{"location":"cargo/em.core/em.utils/PollerAux/","title":"PollerAux","text":""},{"location":"cargo/em.core/em.utils/PollerAux/#unit-polleraux","title":"unit PollerAux","text":"em.utils/PollerAux.empackage em.utils\n\nfrom em.hal import OneShotMilliI\nfrom em.hal import PollerI\n\nfrom em.mcu import Common\n\nmodule PollerAux: PollerI\n\n proxy OneShot: OneShotMilliI\n\n config pauseOnly: bool = false\n\nprivate:\n\n function handler: OneShot.Handler\n function pause(msecs: uint32)\n\n var doneFlag: bool volatile\n\nend\n\ndef handler(arg)\n doneFlag = true\nend\n\ndef pause(msecs)\n return if msecs == 0\n doneFlag = false\n OneShot.enable(msecs, handler, null)\n while !doneFlag\n Common.Idle.exec()\n end\nend\n\ndef poll(rate, count, fxn)\n if pauseOnly\n pause(rate)\n return 1\n else\n count = 0 if rate == 0\n while count\n pause(rate)\n count -= 1\n break if fxn && fxn()\n end\n return count \n end\nend\n
"},{"location":"cargo/em.core/em.utils/ProcMgr/","title":"ProcMgr","text":""},{"location":"cargo/em.core/em.utils/ProcMgr/#unit-procmgr","title":"unit ProcMgr","text":"em.utils/ProcMgr.empackage em.utils\n\nfrom em.mcu import Common\nfrom em.utils import BasicListManager\n\nmodule ProcMgr\n\n type ProcFxn: function(arg: uarg_t)\n\n type Proc: opaque\n host function declareStartH()\n host function initH(fxn: fxn_t, arg: uarg_t = 0)\n function arg(a: uarg_t)\n function fxn(f: fxn_t)\n function getArg(): uarg_t\n function getFxn(): fxn_t\n function post()\n end\n\n host function createH(fxn: fxn_t, arg: uarg_t = 0): Proc&\n\n function run()\n\nprivate:\n\n def opaque Proc\n elem: BasicListManager.Element\n fxn_: fxn_t\n arg_: uarg_t\n end\n\n function dispatch()\n\n config startP: Proc&\n\n var procList: BasicListManager.List\n\nend\n\ndef em$construct() \n procList.initH()\nend\n\ndef em$startup()\n startP.post() if startP && !Common.Mcu.isWarm()\nend\n\ndef createH(fxn, arg)\n auto proc = new<Proc>\n proc.initH(fxn, arg)\n return proc\nend\n\ndef dispatch()\n for ;;\n break if procList.hasElements() == 0\n auto proc = <Proc&>procList.get()\n Common.GlobalInterrupts.enable()\n auto fxn = <ProcFxn>proc.fxn_\n fxn(proc.arg_)\n Common.GlobalInterrupts.disable()\n end \nend\n\ndef run()\n Common.Idle.wakeup()\n Common.GlobalInterrupts.enable()\n for ;;\n Common.GlobalInterrupts.disable()\n dispatch()\n Common.Idle.exec()\n end\nend\n\ndef Proc.declareStartH()\n startP = this\nend\n\ndef Proc.initH(fxn, arg)\n this.elem.initH()\n this.fxn_ = fxn\n this.arg_ = arg\nend\n\ndef Proc.arg(a)\n this.arg_ = a\nend\n\ndef Proc.fxn(f)\n this.fxn_ = f\nend\n\ndef Proc.getArg()\n return this.arg_\nend\n\ndef Proc.getFxn()\n return this.fxn_\nend\n\ndef Proc.post()\n auto key = Common.GlobalInterrupts.disable()\n procList.add(this.elem) if !this.elem.isActive()\n Common.GlobalInterrupts.restore(key)\nend\n
"},{"location":"cargo/em.core/em.utils/SoftUart/","title":"SoftUart","text":""},{"location":"cargo/em.core/em.utils/SoftUart/#unit-softuart","title":"unit SoftUart","text":"em.utils/SoftUart.empackage em.utils\n\nfrom em.hal import ConsoleUartI\nfrom em.hal import GpioI\nfrom em.hal import UsThreshI\n\nfrom em.lang import Math\n\nfrom em.mcu import Common\n\nmodule SoftUart: ConsoleUartI\n\n proxy TxPin: GpioI\n proxy UsThresh: UsThreshI\n\nprivate:\n\n config baudRate: uint32 = 115200\n config bitTime: uint16 = 7\n\nend\n\ndef em$startup()\n TxPin.makeOutput()\n TxPin.set()\nend\n\ndef setBaudH(rate)\n ## TODO -- implement\nend\n\ndef flush()\nend\n\ndef put(data)\n var bitCnt: uint8 = 10 # Load Bit counter, 8data + ST/SP/SP\n var txByte: uint16 = (data << 1) | 0x600 # Add mark stop bits and space start bit\n var key: uarg_t = Common.GlobalInterrupts.disable()\n for ;;\n UsThresh.set(bitTime)\n if bitCnt-- == 0\n TxPin.set()\n break\n else\n if txByte & 0x01\n TxPin.set()\n else\n TxPin.clear()\n end\n txByte = txByte >> 1 # shift next bit\n end\n UsThresh.pause()\n end\n Common.GlobalInterrupts.restore(key)\nend\n
"},{"location":"cargo/em.core/em.utils/TickerMgr/","title":"TickerMgr","text":""},{"location":"cargo/em.core/em.utils/TickerMgr/#unit-tickermgr","title":"unit TickerMgr","text":"em.utils/TickerMgr.empackage em.utils\n\nimport AlarmMgr\nimport FiberMgr\n\nmodule TickerMgr\n # ^|\n type TickCallback: function()\n # ^|\n type Ticker: opaque\n # ^|\n host function initH()\n # ^|\n function start(rate256: uint32, tickCb: TickCallback)\n # ^| \n function stop()\n # ^| \n end\n\n host function createH(): Ticker&\n # ^|\nprivate:\n\n def opaque Ticker\n alarm: AlarmMgr.Alarm&\n fiber: FiberMgr.Fiber&\n rate256: uint32\n tickCb: TickCallback\n end\n\n function alarmFB: FiberMgr.FiberBodyFxn\n\n var tickerTab: Ticker[]\n\nend\n\ndef createH()\n var ticker: Ticker& = tickerTab[tickerTab.length++]\n ticker.initH()\n return ticker\nend\n\ndef Ticker.initH()\n this.fiber = FiberMgr.createH(alarmFB, ^^this.$$cn^^)\n this.alarm = AlarmMgr.createH(this.fiber)\nend\n\ndef alarmFB(arg)\n auto ticker = <Ticker&>arg\n return if ticker.tickCb == null\n ticker.tickCb() \n ticker.alarm.wakeupAt(ticker.rate256)\nend\n\ndef Ticker.start(rate256, tickCb)\n this.rate256 = rate256\n this.tickCb = tickCb\n this.alarm.wakeupAt(rate256)\nend\n\ndef Ticker.stop()\n this.alarm.cancel()\n this.tickCb = null\nend\n
"},{"location":"cargo/em.core/em.utils/TimeoutAux/","title":"TimeoutAux","text":""},{"location":"cargo/em.core/em.utils/TimeoutAux/#unit-timeoutaux","title":"unit TimeoutAux","text":"em.utils/TimeoutAux.empackage em.utils\n\nfrom em.hal import OneShotMilliI\nfrom em.hal import TimeoutI\n\nmodule TimeoutAux: TimeoutI\n\n proxy OneShot: OneShotMilliI\n\nprivate:\n\n var flag: bool volatile\n\n function handler: OneShot.Handler\n\nend\n\ndef active()\n return flag\nend\n\ndef cancel()\n flag = false\n OneShot.disable()\nend\n\ndef handler(arg)\n cancel()\nend\n\ndef set(msecs)\n flag = true\n OneShot.enable(msecs, handler, null)\nend\n
"},{"location":"cargo/em.docs/","title":"Index","text":""},{"location":"cargo/em.docs/#bundle-emdocs","title":"bundle em.docs","text":""},{"location":"cargo/em.docs/em.examples.basic/","title":"Index","text":""},{"location":"cargo/em.docs/em.examples.basic/#package-emexamplesbasic","title":"package em.examples.basic","text":""},{"location":"cargo/em.docs/em.examples.basic/Alarm1P/","title":"Alarm1P","text":""},{"location":"cargo/em.docs/em.examples.basic/Alarm1P/#unit-alarm1p","title":"unit Alarm1P","text":"em.examples.basic/Alarm1P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.utils import AlarmMgr\nfrom em.utils import FiberMgr\n\nmodule Alarm1P\n\nprivate:\n\n function blinkFB: FiberMgr.FiberBodyFxn\n\n config alarm: AlarmMgr.Alarm&\n config blinkF: FiberMgr.Fiber&\n\n var counter: uint32\n\nend\n\ndef em$construct()\n blinkF = FiberMgr.createH(blinkFB)\n alarm = AlarmMgr.createH(blinkF)\nend\n\ndef em$run()\n blinkF.post()\n FiberMgr.run()\nend\n\ndef blinkFB(arg)\n %%[c]\n AppLed.wink(100) # 100ms\n counter += 1\n if counter & 0x1\n alarm.wakeup(512) # 2s\n else\n alarm.wakeup(192) # 750ms\n end\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/Alarm2P/","title":"Alarm2P","text":""},{"location":"cargo/em.docs/em.examples.basic/Alarm2P/#unit-alarm2p","title":"unit Alarm2P","text":"em.examples.basic/Alarm2P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.utils import AlarmMgr\nfrom em.utils import FiberMgr\n\nmodule Alarm2P\n\nprivate:\n\n function blinkFB: FiberMgr.FiberBodyFxn\n\n config alarm: AlarmMgr.Alarm&\n config blinkF: FiberMgr.Fiber&\n\n var counter: uint32\n\nend\n\ndef em$construct()\n blinkF = FiberMgr.createH(blinkFB)\n alarm = AlarmMgr.createH(blinkF)\nend\n\ndef em$run()\n blinkF.post()\n FiberMgr.run()\nend\n\ndef blinkFB(arg)\n %%[c]\n counter += 1\n if counter & 0x1\n AppLed.wink(100) # 100ms\n else\n AppLed.wink(5) # 5ms\n end\n alarm.wakeupAt(384) # 1.5s window \nend\n
"},{"location":"cargo/em.docs/em.examples.basic/BlinkerDbgP/","title":"BlinkerDbgP","text":""},{"location":"cargo/em.docs/em.examples.basic/BlinkerDbgP/#unit-blinkerdbgp","title":"unit BlinkerDbgP","text":"em.examples.basic/BlinkerDbgP.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.mcu import Common\n\nmodule BlinkerDbgP\n\n config dbgFlag: bool = true\n\n config minCnt: uint16 = 1000\n config maxCnt: uint16 = 1020 \n\nend\n\ndef em$run()\n AppLed.on()\n for cnt: uint16 = minCnt; cnt < maxCnt; cnt++\n %%[d+]\n Common.BusyWait.wait(500 * 1000L)\n %%[d-]\n AppLed.toggle()\n continue if !dbgFlag\n fail if cnt > ((minCnt + maxCnt) / 2)\n %%[>cnt]\n var bits11: uint8 = cnt & 0b0011\n %%[>bits11]\n %%[c:bits11]\n printf \"cnt = %d (0x%04x), bits11 = %d\\n\", cnt, cnt, bits11\n end\n AppLed.off()\n halt\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/BlinkerP/","title":"BlinkerP","text":""},{"location":"cargo/em.docs/em.examples.basic/BlinkerP/#unit-blinkerp","title":"unit BlinkerP","text":"em.examples.basic/BlinkerP.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.mcu import Common\n\nmodule BlinkerP\n\nend\n\ndef em$run()\n AppLed.on()\n for auto i = 0; i < 10; i++\n Common.BusyWait.wait(500 * 1000L)\n AppLed.toggle()\n end\n AppLed.off()\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/Button1P/","title":"Button1P","text":""},{"location":"cargo/em.docs/em.examples.basic/Button1P/#unit-button1p","title":"unit Button1P","text":"em.examples.basic/Button1P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em$distro import McuC\nfrom McuC import AppButEdge\n\nfrom em.mcu import Common\n\nmodule Button1P\n\nprivate:\n\n function handler: AppButEdge.Handler\n\nend\n\ndef em$construct()\n AppButEdge.setDetectHandlerH(handler)\nend\n\ndef em$startup()\n AppButEdge.makeInput()\n AppButEdge.setInternalPullup(true)\n AppButEdge.setDetectFallingEdge()\nend\n\ndef em$run()\n Common.GlobalInterrupts.enable()\n for ;;\n AppButEdge.enableDetect()\n Common.Idle.exec()\n end\nend\n\ndef handler()\n %%[c]\n AppButEdge.clearDetect()\n AppLed.on()\n Common.BusyWait.wait(5000)\n AppLed.off()\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/Button2P/","title":"Button2P","text":""},{"location":"cargo/em.docs/em.examples.basic/Button2P/#unit-button2p","title":"unit Button2P","text":"em.examples.basic/Button2P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em$distro import McuC\nfrom McuC import AppButEdge\n\nfrom em.mcu import Common\n\nfrom em.utils import FiberMgr\n\nmodule Button2P\n\nprivate:\n\n function blinkFB: FiberMgr.FiberBodyFxn\n function handler: AppButEdge.Handler\n\n config blinkF: FiberMgr.Fiber&\n\nend\n\ndef em$construct()\n AppButEdge.setDetectHandlerH(handler)\n blinkF = FiberMgr.createH(blinkFB)\nend\n\ndef em$startup()\n AppButEdge.makeInput()\n AppButEdge.setInternalPullup(true)\n AppButEdge.setDetectFallingEdge()\nend\n\ndef em$run()\n AppButEdge.enableDetect()\n FiberMgr.run()\nend\n\ndef blinkFB(arg)\n %%[d]\n AppLed.on()\n Common.BusyWait.wait(5000)\n AppLed.off()\n AppButEdge.enableDetect()\nend\n\ndef handler()\n %%[c]\n AppButEdge.clearDetect()\n blinkF.post()\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/Button3P/","title":"Button3P","text":""},{"location":"cargo/em.docs/em.examples.basic/Button3P/#unit-button3p","title":"unit Button3P","text":"em.examples.basic/Button3P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppBut\nfrom BoardC import AppLed\nfrom BoardC import SysLed\n\nfrom em.mcu import Common\nfrom em.utils import FiberMgr\n\nmodule Button3P\n\nprivate:\n\n function onPressedCB: AppBut.OnPressedCB\n\nend\n\ndef em$run()\n AppBut.onPressed(onPressedCB)\n FiberMgr.run()\nend\n\ndef onPressedCB()\n %%[c]\n if AppBut.isPressed()\n SysLed.on()\n Common.BusyWait.wait(40000) # 40ms\n SysLed.off()\n else\n AppLed.on()\n Common.BusyWait.wait(5000) # 5ms\n AppLed.off()\n end\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/FiberP/","title":"FiberP","text":""},{"location":"cargo/em.docs/em.examples.basic/FiberP/#unit-fiberp","title":"unit FiberP","text":"em.examples.basic/FiberP.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.mcu import Common\nfrom em.utils import FiberMgr\n\nmodule FiberP\n\nprivate:\n\n function blinkFB: FiberMgr.FiberBodyFxn\n\n config blinkF: FiberMgr.Fiber&\n\n var count: uint8 = 5\n\nend\n\ndef em$construct()\n blinkF = FiberMgr.createH(blinkFB)\nend\n\ndef em$run()\n blinkF.post()\n FiberMgr.run()\nend\n\ndef blinkFB(arg)\n %%[d]\n halt if --count == 0\n AppLed.on()\n Common.BusyWait.wait(100000)\n AppLed.off()\n Common.BusyWait.wait(100000)\n blinkF.post()\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/HelloP/","title":"HelloP","text":""},{"location":"cargo/em.docs/em.examples.basic/HelloP/#unit-hellop","title":"unit HelloP","text":"em.examples.basic/HelloP.empackage em.examples.basic\n\nfrom em$distro import BoardC\n\nmodule HelloP\n\nend\n\ndef em$run()\n printf \"hello world\\n\"\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/OneShot1P/","title":"OneShot1P","text":""},{"location":"cargo/em.docs/em.examples.basic/OneShot1P/#unit-oneshot1p","title":"unit OneShot1P","text":"em.examples.basic/OneShot1P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em$distro import McuC\nfrom McuC import OneShotMilli\n\nfrom em.mcu import Common\n\nmodule OneShot1P\n\nprivate:\n\n function handler: OneShotMilli.Handler\n\n var doneFlag: bool volatile = true\n\nend\n\ndef em$run()\n Common.GlobalInterrupts.enable()\n for auto i = 0; i < 5; i++\n %%[d]\n AppLed.on()\n Common.BusyWait.wait(5000)\n AppLed.off()\n doneFlag = false\n OneShotMilli.enable(100, handler)\n while !doneFlag\n Common.Idle.exec()\n end\n end\nend\n\ndef handler(arg)\n %%[c]\n doneFlag = true\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/OneShot2P/","title":"OneShot2P","text":""},{"location":"cargo/em.docs/em.examples.basic/OneShot2P/#unit-oneshot2p","title":"unit OneShot2P","text":"em.examples.basic/OneShot2P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em$distro import McuC\nfrom McuC import OneShotMilli\n\nfrom em.mcu import Common\nfrom em.utils import FiberMgr\n\nmodule OneShot2P\n\nprivate:\n\n function blinkFB: FiberMgr.FiberBodyFxn\n function handler: OneShotMilli.Handler\n\n config blinkF: FiberMgr.Fiber&\n var count: uint8 = 5\n\nend\n\ndef em$construct()\n blinkF = FiberMgr.createH(blinkFB)\nend\n\ndef em$run()\n blinkF.post()\n FiberMgr.run()\nend\n\ndef blinkFB(arg)\n %%[d]\n AppLed.on()\n Common.BusyWait.wait(5000)\n AppLed.off()\n halt if --count == 0\n OneShotMilli.enable(100, handler, null)\nend\n\ndef handler(arg)\n %%[c]\n blinkF.post()\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/PollerP/","title":"PollerP","text":""},{"location":"cargo/em.docs/em.examples.basic/PollerP/#unit-pollerp","title":"unit PollerP","text":"em.examples.basic/PollerP.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.mcu import Common\nfrom em.mcu import Poller\n\nmodule PollerP\n\nend\n\ndef em$run()\n Common.GlobalInterrupts.enable()\n auto k = 5\n while k--\n Poller.pause(100) # 100ms\n AppLed.wink(5) # 5ms\n end\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/TickerP/","title":"TickerP","text":""},{"location":"cargo/em.docs/em.examples.basic/TickerP/#unit-tickerp","title":"unit TickerP","text":"em.examples.basic/TickerP.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\nfrom BoardC import SysLed\n\nfrom em.utils import FiberMgr\nfrom em.utils import TickerMgr\n\nmodule TickerP\n\nprivate:\n\n function appTickCb: TickerMgr.TickCallback\n function sysTickCb: TickerMgr.TickCallback\n\n config appTicker: TickerMgr.Ticker&\n config sysTicker: TickerMgr.Ticker&\n\nend\n\ndef em$construct()\n appTicker = TickerMgr.createH()\n sysTicker = TickerMgr.createH()\nend\n\ndef em$run()\n appTicker.start(256, appTickCb)\n sysTicker.start(384, sysTickCb)\n FiberMgr.run()\nend\n\ndef appTickCb()\n %%[c]\n AppLed.wink(100)\nend\n\ndef sysTickCb()\n %%[d]\n SysLed.wink(100)\nend\n
"},{"location":"cargo/ti.cc23xx/","title":"Index","text":""},{"location":"cargo/ti.cc23xx/#bundle-ticc23xx","title":"bundle ti.cc23xx","text":""},{"location":"cargo/ti.cc23xx/ti.build.cc23xx/","title":"Index","text":""},{"location":"cargo/ti.cc23xx/ti.build.cc23xx/#package-tibuildcc23xx","title":"package ti.build.cc23xx","text":""},{"location":"cargo/ti.cc23xx/ti.build.cc23xx/GccBuilder/","title":"GccBuilder","text":""},{"location":"cargo/ti.cc23xx/ti.build.cc23xx/GccBuilder/#unit-gccbuilder","title":"unit GccBuilder","text":"ti.build.cc23xx/GccBuilder.empackage ti.build.cc23xx\n\nfrom em.build.misc import UniFlash\nfrom em.build.misc import Utils\n\nfrom em.build.gcc import BuilderBase as Base\n\nfrom em.lang import BuilderI\nfrom em.lang import BuildC\n\nmodule GccBuilder: BuilderI\n\nend\n\ndef em$configure()\n Base.gccFlav ?= \"arm-none-eabi\"\n if BuildC.bootFlash\n Base.dmemBase ?= 0x20005000\n Base.dmemSize ?= 0x4000\n Base.imemBase ?= 0x20000000\n Base.imemSize ?= 0x5000\n Base.lmemBase ?= 0x00000000\n Base.lmemSize ?= 0x80000\n else\n Base.dmemBase ?= 0x20000000\n Base.dmemSize ?= 0x9000\n Base.imemBase ?= 0x00000000\n Base.imemSize ?= 0x80000\n end\n Base.vectSize ?= 0x90\n Utils.addInclude(\"com.ti/devices/cc23x0r5\")\n Utils.addSection(0x4e020000, 0x800, \"FLASH_CCFG\", \".ccfg\")\nend\n\ndef compile(buildDir)\n return <CompileInfo&>Base.compile(buildDir)\nend\n\ndef getTypeInfo()\n return <TypeInfo&>Base.getTypeInfo()\nend\n\ndef populate(buildDir, sysFlag)\n Base.populate(buildDir, sysFlag)\n Utils.copy(buildDir, \"CC2340R5.ccxml\", \"ti.build.cc23xx\")\n UniFlash.genLoadScript(buildDir, \"CC2340R5\")\nend\n
"},{"location":"cargo/ti.cc23xx/ti.build.cc23xx/SeggerBuilder/","title":"SeggerBuilder","text":""},{"location":"cargo/ti.cc23xx/ti.build.cc23xx/SeggerBuilder/#unit-seggerbuilder","title":"unit SeggerBuilder","text":"ti.build.cc23xx/SeggerBuilder.empackage ti.build.cc23xx\n\nfrom em.build.misc import UniFlash\nfrom em.build.misc import Utils\n\nfrom em.build.segger import BuilderBase as Base\n\nfrom em.lang import BuilderI\nfrom em.lang import BuildC\n\nmodule SeggerBuilder: BuilderI\n\nend\n\ndef em$configure()\n if BuildC.bootFlash\n Base.dmemBase ?= 0x20005000\n Base.dmemSize ?= 0x4000\n Base.imemBase ?= 0x20000000\n Base.imemSize ?= 0x5000\n Base.lmemBase ?= 0x00000000\n Base.lmemSize ?= 0x80000\n else\n Base.dmemBase ?= 0x20000000\n Base.dmemSize ?= 0x9000\n Base.imemBase ?= 0x00000000\n Base.imemSize ?= 0x80000\n end\n Base.vectSize ?= 0x90\n Utils.addInclude(\"com.ti/devices/cc23x0r5\")\n Utils.addInclude(\"com.ti/devices/cc23x0r5/cmsis/core\")\n Utils.addSection(0x4e020000, 0x800, \"FLASH_CCFG\", \".ccfg\")\nend\n\ndef compile(buildDir)\n return <CompileInfo&>Base.compile(buildDir)\nend\n\ndef getTypeInfo()\n return <TypeInfo&>Base.getTypeInfo()\nend\n\ndef populate(buildDir, sysFlag)\n Base.populate(buildDir, sysFlag)\n Utils.copy(buildDir, \"CC2340R5.ccxml\", \"ti.build.cc23xx\")\n UniFlash.genLoadScript(buildDir, \"CC2340R5\")\nend\n
"},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/","title":"Index","text":""},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/#package-tidistrocc23xx","title":"package ti.distro.cc23xx","text":""},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/BoardC/","title":"BoardC","text":""},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/BoardC/#unit-boardc","title":"unit BoardC","text":"ti.distro.cc23xx/BoardC.empackage ti.distro.cc23xx\n\nfrom em.lang import Atom # force ordering\n\nimport McuC\nfrom McuC import AppButEdge\nfrom McuC import AppLedPin\nfrom McuC import AppOutPin\nfrom McuC import AppOutUart\nfrom McuC import SysDbgA\nfrom McuC import SysDbgB\nfrom McuC import SysDbgC\nfrom McuC import SysDbgD\nfrom McuC import SysLedPin\nfrom McuC import OneShotMilli\nfrom McuC import Uptimer\nfrom McuC import WakeupTimer\n\nfrom em.lang import Console\nfrom em.lang import Debug\n\nfrom em.mcu import ConsoleUart\nfrom em.mcu import Poller\n\nfrom em.utils import AlarmMgr\nfrom em.utils import BoardController\nfrom em.utils import BoardInfo\nfrom em.utils import EpochTime\nfrom em.utils import FormattingConsole\nfrom em.utils import PollerAux\n\nfrom em.utils import DebugPinT {} as DbgA\nfrom em.utils import DebugPinT {} as DbgB\nfrom em.utils import DebugPinT {} as DbgC\nfrom em.utils import DebugPinT {} as DbgD\n\nfrom em.utils import LedT {} as AppLed\nfrom em.utils import LedT {} as SysLed\n\nfrom em.utils import ButtonT {} as AppBut\n\nexport AppBut\nexport AppLed\nexport SysLed\n\ncomposite BoardC\n\nend\n\ndef em$configure()\n auto brdRec = BoardInfo.readRecordH()\n auto pm = brdRec.pinMap\n AlarmMgr.WakeupTimer ?= WakeupTimer\n AppBut.Edge ?= AppButEdge\n AppLed.activeLow ?= brdRec.activeLowLeds\n AppLed.em$used ?= true\n AppLed.Pin ?= AppLedPin\n AppOutUart.TxPin ?= AppOutPin\n BoardController.em$used ?= true\n BoardController.Led ?= SysLed\n Console.em$used ?= true\n Console.Provider ?= FormattingConsole\n ConsoleUart.Impl ?= AppOutUart\n DbgA.Pin ?= SysDbgA\n DbgB.Pin ?= SysDbgB\n DbgC.Pin ?= SysDbgC\n DbgD.Pin ?= SysDbgD\n Debug.Pin_a ?= DbgA\n Debug.Pin_b ?= DbgB\n Debug.Pin_c ?= DbgC\n Debug.Pin_d ?= DbgD\n EpochTime.Uptimer ?= Uptimer\n Poller.Impl ?= PollerAux\n PollerAux.OneShot ?= OneShotMilli\n SysLed.activeLow ?= brdRec.activeLowLeds\n SysLed.em$used ?= true\n SysLed.Pin ?= SysLedPin\nend\n
"},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/BoardMeta/","title":"BoardMeta","text":""},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/BoardMeta/#unit-boardmeta","title":"unit BoardMeta","text":"ti.distro.cc23xx/BoardMeta.empackage ti.distro.cc23xx\n\nfrom em.utils import BoardMeta as BaseMeta\n\nhost module BoardMeta\n\n type DrvDesc: BaseMeta.DrvDesc\n\n type PinMap: struct\n appBut: int8\n appLed: int8\n appOut: int8\n extFlashCS: int8\n extFlashCLK: int8\n extFlashPICO: int8\n extFlashPOCI: int8\n sysDbgA: int8\n sysDbgB: int8\n sysDbgC: int8\n sysDbgD: int8\n sysLed: int8\n end\n\n type Record: struct\n activeLowLeds: bool\n baudRate: uint32\n clockFreq: uint32\n extFlashDisable: bool\n lfXtalEnable: bool\n pinMap: PinMap&\n drvDescs: DrvDesc&[]\n end\n\n config baseFileLoc: string = \"ti.distro.cc23xx/em-boards\"\n\n config attrNames: string[] = [\n \"$inherits\",\n \"$overrides\",\n \"activeLowLeds\",\n \"baudRate\",\n \"clockFreq\",\n \"extFlashDisable\",\n \"lfXtalEnable\",\n ]\n\n config pinNames: string[] = [\n \"appBut\",\n \"appLed\",\n \"appOut\",\n \"extFlashCS\",\n \"extFlashCLK\",\n \"extFlashPICO\",\n \"extFlashPOCI\",\n \"sysDbgA\",\n \"sysDbgB\",\n \"sysDbgC\",\n \"sysDbgD\",\n \"sysLed\",\n ] \n\nend\n
"},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/McuC/","title":"McuC","text":""},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/McuC/#unit-mcuc","title":"unit McuC","text":"ti.distro.cc23xx/McuC.empackage ti.distro.cc23xx\n\nfrom ti.mcu.cc23xx import Regs # force ordering\n\nfrom ti.mcu.cc23xx import EdgeDetectGpioT {} as AppButEdge\nfrom ti.mcu.cc23xx import GpioT {} as AppLedPin\nfrom ti.mcu.cc23xx import GpioT {} as AppOutPin\nfrom ti.mcu.cc23xx import GpioT {} as SysDbgA\nfrom ti.mcu.cc23xx import GpioT {} as SysDbgB\nfrom ti.mcu.cc23xx import GpioT {} as SysDbgC\nfrom ti.mcu.cc23xx import GpioT {} as SysDbgD\nfrom ti.mcu.cc23xx import GpioT {} as SysLedPin\n\nfrom ti.mcu.cc23xx import ConsoleUart0 as AppOutUart\nfrom ti.mcu.cc23xx import BusyWait\nfrom ti.mcu.cc23xx import ExtFlashDisabler\nfrom ti.mcu.cc23xx import GlobalInterrupts\nfrom ti.mcu.cc23xx import Idle\nfrom ti.mcu.cc23xx import IntrVec\nfrom ti.mcu.cc23xx import Mcu\nfrom ti.mcu.cc23xx import MsCounter\nfrom ti.mcu.cc23xx import OneShotGpt3 as OneShotMilli\nfrom ti.mcu.cc23xx import Uptimer\nfrom ti.mcu.cc23xx import UsCounter\nfrom ti.mcu.cc23xx import WakeupTimer\n\nfrom em.mcu import Common\nfrom em.mcu import CommonC\n\nfrom em.utils import BoardInfo\n\nexport AppButEdge\nexport AppLedPin\nexport AppOutPin\nexport AppOutUart\nexport OneShotMilli\nexport SysDbgA\nexport SysDbgB\nexport SysDbgC\nexport SysDbgD\nexport SysLedPin\nexport Uptimer\nexport WakeupTimer\n\ncomposite McuC\n\nend\n\ndef em$preconfigure()\n auto brdRec = BoardInfo.readRecordH()\n auto pm = brdRec.pinMap\n AppButEdge.pin ?= pm.appBut\n AppLedPin.pin ?= pm.appLed\n AppOutPin.pin ?= pm.appOut\n AppOutUart.setBaudH(brdRec.baudRate)\n ExtFlashDisabler.em$used ?= brdRec.extFlashDisable\n ExtFlashDisabler.CLK_pin ?= pm.extFlashCLK\n ExtFlashDisabler.CS_pin ?= pm.extFlashCS\n ExtFlashDisabler.PICO_pin ?= pm.extFlashPICO\n ExtFlashDisabler.POCI_pin ?= pm.extFlashPOCI\n IntrVec.em$used ?= true\n Mcu.hasLfXtal ?= brdRec.lfXtalEnable\n Mcu.mclkFrequency ?= brdRec.clockFreq\n Regs.em$used ?= true\n SysDbgA.pin ?= pm.sysDbgA\n SysDbgB.pin ?= pm.sysDbgB\n SysDbgC.pin ?= pm.sysDbgC\n SysDbgD.pin ?= pm.sysDbgD\n SysLedPin.pin ?= pm.sysLed\nend\n\ndef em$configure()\n Common.BusyWait ?= BusyWait\n Common.GlobalInterrupts ?= GlobalInterrupts\n Common.Idle ?= Idle\n Common.Mcu ?= Mcu\n Common.MsCounter ?= MsCounter\n Common.UsCounter ?= UsCounter\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/","title":"Index","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/#package-timcucc23xx","title":"package ti.mcu.cc23xx","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/BusyWait/","title":"BusyWait","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/BusyWait/#unit-busywait","title":"unit BusyWait","text":"ti.mcu.cc23xx/BusyWait.empackage ti.mcu.cc23xx\n\nfrom em.hal import BusyWaitI\n\nmodule BusyWait: BusyWaitI\n\nend\n\ndef wait(usecs)\n ^HapiWaitUs(usecs)\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/BusyWaitSysTick/","title":"BusyWaitSysTick","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/BusyWaitSysTick/#unit-busywaitsystick","title":"unit BusyWaitSysTick","text":"ti.mcu.cc23xx/BusyWaitSysTick.empackage ti.mcu.cc23xx\n\nfrom em.hal import BusyWaitI\n\nmodule BusyWaitSysTick: BusyWaitI\n\nend\n\ndef wait(usecs)\n ^^SysTick->VAL^^ = 0\n ^^SysTick->LOAD^^ = usecs\n ^^SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk^^\n while ^^SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk^^ != 0\n %%[d]\n end\n ^^SysTick->CTRL^^ = 0\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/ConsoleUart0/","title":"ConsoleUart0","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/ConsoleUart0/#unit-consoleuart0","title":"unit ConsoleUart0","text":"ti.mcu.cc23xx/ConsoleUart0.empackage ti.mcu.cc23xx\n\nfrom em.hal import ConsoleUartI\nfrom em.hal import GpioI\n\nfrom em.lang import Math\n\nimport Idle\nimport Mcu\n\nmodule ConsoleUart0: ConsoleUartI\n\n proxy TxPin: GpioI\n\nprivate:\n\n config baud: uint32\n config fbrd: uint32\n config ibrd: uint32\n\n function sleepEnter: Idle.Callback\n function sleepLeave: Idle.Callback\n\nend\n\ndef em$construct()\n Idle.addSleepEnterCbH(sleepEnter)\n Idle.addSleepLeaveCbH(sleepLeave)\n auto brd = <num_t>(Mcu.mclkFrequency / (baud * 16))\n ibrd = Math.floor(brd)\n fbrd = Math.round((brd - ibrd) * 64)\nend\n\ndef em$startup()\n sleepLeave()\nend\n\ndef setBaudH(rate)\n baud = rate\nend\n\ndef sleepEnter()\n ^^HWREG(CLKCTL_BASE + CLKCTL_O_CLKENCLR0)^^ = ^CLKCTL_CLKENSET0_UART0\n TxPin.reset()\nend\n\ndef sleepLeave()\n ^^HWREG(CLKCTL_BASE + CLKCTL_O_CLKENSET0)^^ = ^CLKCTL_CLKENSET0_UART0\n TxPin.makeOutput()\n TxPin.set()\n TxPin.functionSelect(2)\n ^^HWREG(UART0_BASE + UART_O_CTL)^^ &= ~^UART_CTL_UARTEN\n ^^HWREG(UART0_BASE + UART_O_IBRD)^^ = ibrd\n ^^HWREG(UART0_BASE + UART_O_FBRD)^^ = fbrd\n ^^HWREG(UART0_BASE + UART_O_LCRH)^^ = ^UART_LCRH_WLEN_BITL8\n ^^HWREG(UART0_BASE + UART_O_CTL)^^ |= ^UART_CTL_UARTEN\nend\n\ndef flush()\n while (^^HWREG(UART0_BASE + UART_O_FR)^^ & ^UART_FR_BUSY) != 0\n end\nend\n\ndef put(data)\n ^^HWREG(UART0_BASE + UART_O_DR)^^ = data\n flush()\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/EdgeDetectGpioAux/","title":"EdgeDetectGpioAux","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/EdgeDetectGpioAux/#unit-edgedetectgpioaux","title":"unit EdgeDetectGpioAux","text":"ti.mcu.cc23xx/EdgeDetectGpioAux.empackage ti.mcu.cc23xx\n\nimport InterruptT { name: \"GPIO_COMB\" } as Intr\n\nmodule EdgeDetectGpioAux\n\n type Handler: function ()\n\n type HandlerInfo: struct\n link: HandlerInfo&\n mask: uint32\n handler: Handler\n end\n\n function addHandler(hi: HandlerInfo&)\n\n private:\n\n var handlerList: HandlerInfo&\n function edgeIsr: Intr.Handler\n\nend\n\ndef em$construct()\n Intr.setHandlerH(edgeIsr)\nend\n\ndef em$startup()\n Intr.enable()\nend\n\ndef addHandler(hi)\n hi.link = handlerList\n handlerList = hi\nend\n\ndef edgeIsr()\n auto mis = <uint32>^^HWREG(GPIO_BASE + GPIO_O_MIS)^^\n for hi: HandlerInfo& = handlerList; hi != null; hi = hi.link\n hi.handler() if (mis & hi.mask) && hi.handler\n end\n ^^HWREG(GPIO_BASE + GPIO_O_ICLR)^^ = 0xffffffff\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/EdgeDetectGpioT/","title":"EdgeDetectGpioT","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/EdgeDetectGpioT/#unit-edgedetectgpiot","title":"unit EdgeDetectGpioT","text":"ti.mcu.cc23xx/EdgeDetectGpioT.empackage ti.mcu.cc23xx\n\ntemplate EdgeDetectGpioT\n\n const UNDEF: int8 = -1\n\n config pin: int8 = UNDEF\n\nend\n\ndef em$generateUnit(pn, un)\n auto bq = \"`\"\n auto p = pin\n auto pre = ^^pn.replace(/[.]/g, '_') + '_' + un + '__'^^\n|->>>\n package `pn`\n\n from ti.mcu.cc23xx import EdgeDetectGpioAux as Aux\n from ti.mcu.cc23xx import GpioT {} as Pin\n\n from em.hal import GpioEdgeDetectMinI\n\n module `un`: GpioEdgeDetectMinI\n\n config pin: int16 = `p`\n\n private:\n\n config isDef: bool\n config mask: uint32 \n\n var info: Aux.HandlerInfo\n\n end\n\n def em$configure()\n Pin.pin ?= pin\n end\n\n def em$construct()\n isDef = pin != `UNDEF`\n mask = isDef ? <uint32>(1 << <uint8>pin) : 0\n info.mask = mask\n end\n\n def em$startup()\n Aux.addHandler(info)\n end \n\n def clear() \n Pin.clear()\n end\n\n def set()\n Pin.set()\n end\n\n def get()\n return Pin.get()\n end\n\n def toggle()\n Pin.toggle()\n end\n\n def isInput()\n return Pin.isInput()\n end\n\n def isOutput()\n return Pin.isOutput()\n end\n\n def makeInput()\n Pin.makeInput()\n end\n\n def makeOutput()\n Pin.makeOutput()\n end\n\n def functionSelect(select)\n Pin.functionSelect(select)\n end\n\n def setInternalPullup(enable)\n Pin.setInternalPullup(enable)\n end\n\n def pinId()\n return pin\n end\n\n def reset()\n Pin.reset()\n end\n\n def enableDetect()\n ^^HWREG(GPIO_BASE + GPIO_O_IMSET)^^ = mask if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ |= ^IOC_IOC0_WUENSB if isDef\n end\n\n def disableDetect()\n ^^HWREG(GPIO_BASE + GPIO_O_IMCLR)^^ = mask if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ &= ~^IOC_IOC0_WUENSB if isDef\n end\n\n def clearDetect()\n ^^HWREG(GPIO_BASE + GPIO_O_ICLR)^^ = mask if isDef\n end\n\n def setDetectRisingEdge()\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ &= ~^IOC_IOC0_EDGEDET_M if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ |= ^IOC_IOC0_EDGEDET_EDGE_POS if isDef\n end\n\n def setDetectFallingEdge()\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ &= ~^IOC_IOC0_EDGEDET_M if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ |= ^IOC_IOC0_EDGEDET_EDGE_NEG if isDef\n end\n\n def setDetectHandlerH(h)\n info.handler = <Aux.Handler>h\n end\n|-<<<\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/ExtFlashDisabler/","title":"ExtFlashDisabler","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/ExtFlashDisabler/#unit-extflashdisabler","title":"unit ExtFlashDisabler","text":"ti.mcu.cc23xx/ExtFlashDisabler.empackage ti.mcu.cc23xx\n\nimport GpioT {} as CS\nimport GpioT {} as CLK\nimport GpioT {} as PICO\nimport GpioT {} as POCI\n\nfrom em.mcu import Common\n\nmodule ExtFlashDisabler\n\n config CS_pin: int8\n config CLK_pin: int8\n config PICO_pin: int8\n config POCI_pin: int8\n\nprivate:\n\n const SD_CMD: uint8 = 0xb9\n\nend\n\ndef em$construct()\n CS.pin = CS_pin\n CLK.pin = CLK_pin\n PICO.pin = PICO_pin\n POCI.pin = POCI_pin\nend\n\ndef em$startup()\n %%[c+]\n CS.makeOutput()\n CLK.makeOutput()\n PICO.makeOutput()\n POCI.makeInput()\n # attention\n CS.set()\n Common.BusyWait.wait(1)\n CS.clear()\n Common.BusyWait.wait(1)\n CS.set()\n Common.BusyWait.wait(50)\n # shutdown command\n CS.clear()\n for auto i = 0; i < 8; i++\n CLK.clear()\n if ((SD_CMD >> (7 - i)) & 0x01) == 0\n PICO.clear()\n else\n PICO.set()\n end\n CLK.set()\n Common.BusyWait.wait(1)\n end\n CLK.clear()\n CS.set()\n Common.BusyWait.wait(50)\n #\n CS.reset()\n CLK.reset()\n PICO.reset()\n POCI.reset()\n %%[c-]\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/GlobalInterrupts/","title":"GlobalInterrupts","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/GlobalInterrupts/#unit-globalinterrupts","title":"unit GlobalInterrupts","text":"ti.mcu.cc23xx/GlobalInterrupts.empackage ti.mcu.cc23xx\n\nfrom em.hal import GlobalInterruptsI\n\nmodule GlobalInterrupts: GlobalInterruptsI\n\nend\n\ndef disable()\n auto key = <uarg_t>(^^__get_PRIMASK()^^)\n ^^__set_PRIMASK(1)^^\n return key\nend\n\ndef enable()\n ^^__set_PRIMASK(0)^^\nend\n\ndef restore(key)\n ^^__set_PRIMASK(key)^^\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/GpioT/","title":"GpioT","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/GpioT/#unit-gpiot","title":"unit GpioT","text":"ti.mcu.cc23xx/GpioT.empackage ti.mcu.cc23xx\n\ntemplate GpioT\n\n const UNDEF: int16 = -1\n\n config pin: int16 = UNDEF\n\nend\n\ndef em$generateUnit(pn, un)\n auto bq = \"`\"\n auto pre = ^^pn.replace(/[.]/g, '_') + '_' + un + '__'^^\n|->>>\n package `pn`\n\n from em.hal import GpioI\n\n module `un`: GpioI\n\n config pin: int16 = `pin`\n\n function pinMask(): uint32 \n\n private:\n\n config isDef: bool\n config mask: uint32\n\n end\n\n def em$construct()\n isDef = pin != `UNDEF`\n mask = isDef ? <uint32>(1 << <uint8>pin) : 0\n end\n\n def clear() \n ^^HWREG(GPIO_BASE + GPIO_O_DOUTCLR31_0)^^ = mask if isDef\n end\n\n def set()\n ^^HWREG(GPIO_BASE + GPIO_O_DOUTSET31_0)^^ = mask if isDef\n end\n\n def get()\n return 0 if !isDef\n return isInput() ? ((^^HWREG(GPIO_BASE + GPIO_O_DIN31_0)^^ & mask) != 0) : ((^^HWREG(GPIO_BASE + GPIO_O_DOUT31_0)^^ & mask) != 0)\n end\n\n def toggle()\n ^^HWREG(GPIO_BASE + GPIO_O_DOUTTGL31_0)^^ = mask if isDef\n end\n\n def isInput()\n return isDef && (^^HWREG(GPIO_BASE + GPIO_O_DOE31_0)^^ & mask) == 0\n end\n\n def isOutput()\n return isDef && (^^HWREG(GPIO_BASE + GPIO_O_DOE31_0)^^ & mask) != 0\n end\n\n def makeInput()\n ^^HWREG(GPIO_BASE + GPIO_O_DOECLR31_0)^^ = mask if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ |= ^IOC_IOC0_INPEN if isDef\n end\n\n def makeOutput()\n ^^HWREG(GPIO_BASE + GPIO_O_DOESET31_0)^^ = mask if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ &= ~^IOC_IOC0_INPEN if isDef\n end\n\n def functionSelect(select)\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ = select if isDef\n end\n\n def setInternalPullup(enable)\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ |= ^IOC_IOC0_PULLCTL_PULL_UP if isDef && enable\n end\n\n def pinId()\n return pin\n end\n\n def pinMask()\n return mask\n end\n\n def reset()\n ^^HWREG(GPIO_BASE + GPIO_O_DOECLR31_0)^^ = mask if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ |= (^IOC_IOC0_IOMODE_M | ^IOC_IOC0_PULLCTL_M) if isDef\n end\n|-<<<\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Idle/","title":"Idle","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Idle/#unit-idle","title":"unit Idle","text":"ti.mcu.cc23xx/Idle.empackage ti.mcu.cc23xx\n\nfrom em.hal import IdleI\nfrom em.lang import Debug\n\nmodule Idle: IdleI\n\n type Callback: function()\n\n host function addSleepEnterCbH(cb: Callback) \n host function addSleepLeaveCbH(cb: Callback) \n\n function doSleep()\n function doWait()\n\n function setWaitOnly(val: bool)\n\nprivate:\n\n config sleepEnterCbTab: Callback[..]\n config sleepLeaveCbTab: Callback[..]\n\n var waitOnly: bool\n\nend\n\ndef em$startup()\n %%[b+]\n ^^HWREG(PMCTL_BASE + PMCTL_O_VDDRCTL)^^ = ^PMCTL_VDDRCTL_SELECT # LDO\n ^^HWREG(EVTULL_BASE + EVTULL_O_WKUPMASK)^^ = ^EVTULL_WKUPMASK_AON_RTC_COMB | ^EVTULL_WKUPMASK_AON_IOC_COMB\nend\n\ndef addSleepEnterCbH(cb)\n sleepEnterCbTab[sleepEnterCbTab.length++] = cb\nend\n\ndef addSleepLeaveCbH(cb)\n sleepLeaveCbTab[sleepLeaveCbTab.length++] = cb\nend\n\ndef doSleep()\n for cb in sleepEnterCbTab\n cb()\n end\n %%[b:2]\n %%[b-]\n Debug.sleepEnter()\n ^^HWREG(CKMD_BASE + CKMD_O_LDOCTL)^^ = 0x0\n ^^__set_PRIMASK(1)^^\n ^^HapiEnterStandby(NULL)^^\n Debug.sleepLeave()\n %%[b+]\n for cb in sleepLeaveCbTab\n cb()\n end\n ^^__set_PRIMASK(0)^^\nend\n\ndef doWait()\n %%[b:1]\n %%[b-]\n ^^__set_PRIMASK(1)^^\n ^^asm(\"wfi\")^^\n %%[b+]\n ^^__set_PRIMASK(0)^^\nend\n\n\ndef exec()\n if waitOnly\n doWait()\n else\n doSleep()\n end\nend\n\ndef setWaitOnly(val)\n waitOnly = val\nend\n\ndef wakeup()\n ## TODO -- implement\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/InterruptT/","title":"InterruptT","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/InterruptT/#unit-interruptt","title":"unit InterruptT","text":"ti.mcu.cc23xx/InterruptT.empackage ti.mcu.cc23xx\n\ntemplate InterruptT\n\n config name: string\n\nend\n\ndef em$generateUnit(pn, un) \n auto intrName = name\n auto handlerNameQ = \"`\" + un + \".handlerName`\"\n|->>>\n package `pn`\n\n from ti.mcu.cc23xx import IntrVec\n\n from em.hal import InterruptSourceI\n\n module `un`: InterruptSourceI \n\n private:\n host var handlerName: string\n end\n\n def em$construct()\n IntrVec.addIntrH(\"`intrName`\")\n end\n\n def em$generateCode( prefix )\n if `un`.handlerName\n |-> void `intrName`_Handler() {\n |-> `handlerNameQ`();\n |-> }\n end\n end\n\n def setHandlerH(h)\n handlerName = h ? ^^String(h).substring(1)^^ : null\n end\n\n def enable() \n ^^NVIC_EnableIRQ(`intrName`_IRQn)^^\n end\n\n def disable() \n ^^NVIC_DisableIRQ(`intrName`_IRQn)^^\n end\n\n def clear()\n ^^NVIC_ClearPendingIRQ(`intrName`_IRQn)^^\n end\n\n def isEnabled() \n return ^^NVIC_GetEnableIRQ(`intrName`_IRQn)^^\n end\n|-<<<\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/IntrVec/","title":"IntrVec","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/IntrVec/#unit-intrvec","title":"unit IntrVec","text":"ti.mcu.cc23xx/IntrVec.empackage ti.mcu.cc23xx\n\nfrom em.hal import IntrVecI\n\nmodule IntrVec: IntrVecI\n\n host function addIntrH(name: string)\n\nprivate:\n\n const HARD_FAULT: uint32 = 3\n\n type IsrFxn: function()\n\n host config nameTab: string[] = [\n \"NMI\",\n \"HardFault\",\n null,\n null,\n null,\n null,\n null,\n null,\n null,\n \"SVC\",\n null,\n null,\n \"PendSV\",\n \"SysTick\",\n \"CPUIRQ0\",\n \"CPUIRQ1\",\n \"CPUIRQ2\",\n \"CPUIRQ3\",\n \"CPUIRQ4\",\n \"GPIO_COMB\", \n \"LRFD_IRQ0\", \n \"LRFD_IRQ1\", \n \"DMA_DONE_COMB\", \n \"AES_COMB\", \n \"SPI0_COMB\", \n \"UART0_COMB\", \n \"I2C0_IRQ\", \n \"LGPT0_COMB\", \n \"LGPT1_COMB\", \n \"ADC0_COMB\", \n \"CPUIRQ16\", \n \"LGPT2_COMB\", \n \"LGPT3_COMB\",\n ]\n\n host config usedTab: string[]\n\n config excHandler: ExceptionHandler\n\n function nullIsr()\n\nend\n\ndef em$generateCode(prefix)\n|->>>\ntypedef void( *intfunc )( void );\ntypedef union { intfunc fxn; void* ptr; } intvec_elem;\n\n|-<<<\n for n in nameTab\n continue if n == null\n |-> extern void `n`_Handler( void );\n |-> #define `n`_ISR `prefix`::nullIsr\n end \n|->>>\n\n|-<<<\n for u in usedTab\n |-> #undef `u`_ISR\n |-> #define `u`_ISR `u`_Handler\n end \n|->>>\n\nextern em_uint32 __stack_top__;\nextern \"C\" void __em_program_start( void );\nextern \"C\" const intvec_elem __attribute__((section(\".intvec\"))) __vector_table[] = {\n { .ptr = (void*)&__stack_top__ },\n { .fxn = __em_program_start },\n|-<<<\n for n in nameTab\n if n == null\n |-> 0,\n else\n |-> { .fxn = `n`_ISR },\n end\n end \n |-> };\n|->>>\n\n|-<<<\nend\n\ndef em$startup()\n ^^SCB->VTOR = (uint32_t)(&__vector_table)^^\nend\n\ndef addIntrH(name)\n usedTab[usedTab.length++] = name\nend\n\ndef bindExceptionHandlerH(handler)\n excHandler = handler\nend\n\ndef nullIsr()\n auto vecNum = <uint32>(^^__get_IPSR()^^)\n %%[b:4]\n %%[><uint8>vecNum]\n auto frame = <uint32[]>(^^__get_MSP()^^)\n %%[><uint32>&frame[0]]\n for auto i = 0; i < 8; i++\n %%[b]\n %%[>frame[i]]\n end\n fail\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Mcu/","title":"Mcu","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Mcu/#unit-mcu","title":"unit Mcu","text":"ti.mcu.cc23xx/Mcu.empackage ti.mcu.cc23xx\n\nfrom em.hal import McuI\n\nfrom em.lang import Debug\n\nmodule Mcu: McuI\n\n config noCache: bool\n config hasLfXtal: bool\n\nend\n\ndef getResetCode()\n ## TODO -- implement\n return 0\nend\n\ndef getStashAddr()\n ## TODO -- implement\n return null\nend\n\ndef isWarm()\n ## TODO -- implement\n return false\nend\n\ndef readEui48(dst)\n ## TODO -- implement\nend\n\ndef reset(code)\n ## TODO -- implement\nend\n\ndef startup()\n Debug.startup()\n if hasLfXtal\n ^^HWREG(CKMD_BASE + CKMD_O_LFINCOVR) = 0x001E8480 | CKMD_LFINCOVR_OVERRIDE_M^^\n ^^HWREG(CKMD_BASE + CKMD_O_LFCLKSEL) = CKMD_LFCLKSEL_MAIN_LFXT^^\n ^^HWREG(CKMD_BASE + CKMD_O_LFXTCTL) = CKMD_LFXTCTL_EN^^\n ^^HWREG(CKMD_BASE + CKMD_O_IMSET) = CKMD_IMASK_LFCLKGOOD^^\n else\n ^^HWREG(CKMD_BASE + CKMD_O_TRIM1) |= CKMD_TRIM1_NABIAS_LFOSC^^\n ^^HWREG(CKMD_BASE + CKMD_O_LFCLKSEL) = CKMD_LFCLKSEL_MAIN_LFOSC^^\n ^^HWREG(CKMD_BASE + CKMD_O_LFOSCCTL) = CKMD_LFOSCCTL_EN^^\n ^^HWREG(CKMD_BASE + CKMD_O_LFINCCTL) &= ~CKMD_LFINCCTL_PREVENTSTBY_M^^\n ^^HWREG(CKMD_BASE + CKMD_O_IMSET) = CKMD_IMASK_LFCLKGOOD^^\n end\n ^^HWREG(CLKCTL_BASE + CLKCTL_O_IDLECFG)^^ = 1 if noCache\n ^^HWREG(VIMS_BASE + VIMS_O_CCHCTRL)^^ = 0 if noCache\nend\n\ndef shutdown()\n ## TODO -- implement\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/MsCounter/","title":"MsCounter","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/MsCounter/#unit-mscounter","title":"unit MsCounter","text":"ti.mcu.cc23xx/MsCounter.empackage ti.mcu.cc23xx\n\nfrom em.hal import MsCounterI\n\nimport Rtc\n\nmodule MsCounter: MsCounterI\n\nprivate:\n\n var t0: uint32\n\nend\n\ndef start()\n t0 = Rtc.getMsecs()\nend\n\ndef stop()\n return 0 if t0 == 0\n auto t1 = Rtc.getMsecs()\n auto dt = (t1 > t0) ? (t1 - t0) : (t0 - t1)\n t0 = 0\n return dt\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/OneShotGpt3/","title":"OneShotGpt3","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/OneShotGpt3/#unit-oneshotgpt3","title":"unit OneShotGpt3","text":"ti.mcu.cc23xx/OneShotGpt3.empackage ti.mcu.cc23xx\n\nimport InterruptT { name: \"LGPT3_COMB\" } as Intr\n\nimport BusyWait\nimport Idle\nimport Mcu\n\nfrom em.hal import OneShotMilliI\n\nmodule OneShotGpt3: OneShotMilliI\n\nprivate:\n\n var curArg: ptr_t\n var curFxn: Handler\n\n function isr: Intr.Handler\n\nend\n\ndef em$construct()\n Intr.setHandlerH(isr)\nend\n\ndef disable()\n curFxn = null\n Idle.setWaitOnly(false)\n Intr.disable()\n ^^HWREG(LGPT3_BASE + LGPT_O_ICLR) = LGPT_ICLR_TGT^^\nend\n\ndef enable(msecs, handler, arg)\n curFxn = handler\n curArg = arg\n Idle.setWaitOnly(true)\n Intr.enable()\n ^^HWREG(CLKCTL_BASE + CLKCTL_O_CLKENSET0)^^ = ^CLKCTL_CLKENSET0_LGPT3\n ^^HWREG(LGPT3_BASE + LGPT_O_IMSET) = LGPT_IMSET_TGT^^\n ^^HWREG(LGPT3_BASE + LGPT_O_TGT)^^ = msecs * (Mcu.mclkFrequency / 1000)\n ^^HWREG(LGPT3_BASE + LGPT_O_CTL) = LGPT_CTL_MODE_UP_ONCE | LGPT_CTL_C0RST^^\nend\n\ndef isr()\n auto fxn = curFxn\n disable()\n fxn(curArg) if fxn\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/OneShotSysTick/","title":"OneShotSysTick","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/OneShotSysTick/#unit-oneshotsystick","title":"unit OneShotSysTick","text":"ti.mcu.cc23xx/OneShotSysTick.empackage ti.mcu.cc23xx\n\nimport InterruptT { name: \"SysTick\" } as Intr\n\nimport Idle\nimport Mcu\n\nfrom em.hal import OneShotMilliI\n\nmodule OneShotSysTick: OneShotMilliI\n\nprivate:\n\n var curArg: ptr_t\n var curFxn: Handler\n\n function isr: Intr.Handler\n\nend\n\ndef em$construct()\n Intr.setHandlerH(isr)\nend\n\ndef em$startup()\n ^^SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk^^\nend\n\ndef disable()\n curFxn = null\n Idle.setWaitOnly(false)\n Intr.disable()\n ^^SysTick->CTRL &= ~(SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk)^^\nend\n\ndef enable(msecs, handler, arg)\n curFxn = handler\n curArg = arg\n Idle.setWaitOnly(true)\n Intr.enable()\n ^^SysTick->LOAD^^ = msecs * (Mcu.mclkFrequency / 1000)\n ^^SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk^^\nend\n\ndef isr()\n auto fxn = curFxn\n disable()\n fxn(curArg) if fxn\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/OneShotSysTim0/","title":"OneShotSysTim0","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/OneShotSysTim0/#unit-oneshotsystim0","title":"unit OneShotSysTim0","text":"ti.mcu.cc23xx/OneShotSysTim0.empackage ti.mcu.cc23xx\n\nimport InterruptT { name: \"CPUIRQ1\" } as Intr\n\nimport Idle\nimport Mcu\n\nfrom em.hal import OneShotMilliI\n\nmodule OneShotSysTim0: OneShotMilliI\n\nprivate:\n\n var curArg: ptr_t\n var curFxn: Handler\n\n function isr: Intr.Handler\n\nend\n\ndef em$construct()\n Intr.setHandlerH(isr)\nend\n\ndef disable()\n curFxn = null\n Idle.setWaitOnly(false)\n Intr.disable()\n ^^HWREG(SYSTIM_BASE + SYSTIM_O_ICLR) = SYSTIM_ICLR_EV0^^\nend\n\ndef enable(msecs, handler, arg)\n curFxn = handler\n curArg = arg\n Idle.setWaitOnly(true)\n Intr.clear()\n Intr.enable()\n ^^HWREG(EVTSVT_BASE + EVTSVT_O_CPUIRQ1SEL) = EVTSVT_CPUIRQ1SEL_PUBID_SYSTIM0^^\n ^^HWREG(SYSTIM_BASE + SYSTIM_O_IMSET) = SYSTIM_IMSET_EV0^^\n auto time1u = <uint32>(^^HWREG(SYSTIM_BASE + SYSTIM_O_TIME1U)^^)\n auto thresh = time1u + (msecs * 1000)\n ^^HWREG(SYSTIM_BASE + SYSTIM_O_CH0CC)^^ = thresh\n printf \"time1u = %d, thresh = %d\\n\", time1u, thresh\nend\n\ndef isr()\n %%[a]\n auto fxn = curFxn\n disable()\n fxn(curArg) if fxn\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Regs/","title":"Regs","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Regs/#unit-regs","title":"unit Regs","text":"ti.mcu.cc23xx/Regs.empackage ti.mcu.cc23xx\n\nmodule Regs\n\nend\n\ndef em$generateCode(prefix)\n|->>>\n #include \"cmsis/cc23x0r5.h\"\n #include \"cmsis/core/core_cm0plus.h\"\n\n #include \"driverlib/hapi.h\"\n\n #include \"inc/hw_memmap.h\"\n #include \"inc/hw_types.h\"\n\n #include \"inc/hw_ckmd.h\"\n #include \"inc/hw_clkctl.h\"\n #include \"inc/hw_evtull.h\"\n #include \"inc/hw_evtsvt.h\"\n #include \"inc/hw_gpio.h\"\n #include \"inc/hw_ioc.h\"\n #include \"inc/hw_lgpt.h\"\n #include \"inc/hw_lgpt3.h\"\n #include \"inc/hw_pmctl.h\"\n #include \"inc/hw_rtc.h\"\n #include \"inc/hw_systim.h\"\n #include \"inc/hw_uart.h\"\n #include \"inc/hw_vims.h\"\n\n #include \"inc/hw_ccfg.h\"\n\n#if 0\n\n extern \"C\" const ccfg_t __ccfg __attribute__((section(\".ccfg\"), used)) = {\n\n .bootCfg.pBldrVtor = XCFG_BC_PBLDR_UNDEF,\n\n .bootCfg.bldrParam.serialRomBldrParamStruct.bldrEnabled = XCFG_BC_BLDR_DIS,\n .bootCfg.bldrParam.serialRomBldrParamStruct.serialIoCfgIndex = 0,\n .bootCfg.bldrParam.serialRomBldrParamStruct.pinTriggerDio = 0,\n .bootCfg.bldrParam.serialRomBldrParamStruct.pinTriggerEnabled = XCFG_BC_PINTRIG_DIS,\n .bootCfg.bldrParam.serialRomBldrParamStruct.pinTriggerLevel = XCFG_BC_PINTRIG_LEVEL_LO,\n .bootCfg.pAppVtor = (void*)0x0,\n\n .hwOpts = {0xffffffff, 0xffffffff},\n\n .permissions.allowDebugPort = CCFG_PERMISSION_ALLOW,\n .permissions.allowEnergyTrace = CCFG_PERMISSION_ALLOW,\n .permissions.allowFlashVerify = CCFG_PERMISSION_ALLOW,\n .permissions.allowFlashProgram = CCFG_PERMISSION_ALLOW,\n .permissions.allowChipErase = CCFG_PERMISSION_ALLOW,\n .permissions.allowToolsClientMode = CCFG_PERMISSION_ALLOW,\n .permissions.allowFakeStby = CCFG_PERMISSION_ALLOW,\n .permissions.allowReturnToFactory = CCFG_PERMISSION_ALLOW,\n\n .misc.saciTimeoutOverride = 1U,\n .misc.saciTimeoutExp = 7,\n\n .flashProt.writeEraseProt.mainSectors0_31 = 0xffffffff,\n .flashProt.writeEraseProt.mainSectors32_255 = 0xffffffff,\n\n .flashProt.writeEraseProt.ccfgSector = 0,\n .flashProt.writeEraseProt.fcfgSector = 0,\n .flashProt.writeEraseProt.engrSector = 0,\n\n .flashProt.res = 0xFFFFFFFFU,\n\n .flashProt.chipEraseRetain.mainSectors0_31 = 0x0,\n .flashProt.chipEraseRetain.mainSectors32_255 = 0x0,\n\n .debugCfg.authorization = CCFG_DBGAUTH_DBGOPEN,\n .debugCfg.allowBldr = CCFG_DBGBLDR_ALLOW,\n .debugCfg.pwdId = {0x01, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0d, 0x15},\n .debugCfg.pwdHash = {0x6d, 0xd7, 0xe4, 0x36, 0xeb, 0xf4, 0x31, 0xdf,\n 0x95, 0xae, 0x15, 0xee, 0x03, 0xba, 0x8e, 0xe4,\n 0xc4, 0xc6, 0x3f, 0xd8, 0x45, 0x3f, 0x67, 0x5e,\n 0x74, 0xd7, 0xc2, 0x01, 0x2c, 0x90, 0x58, 0xe5},\n };\n\n#else\n\n extern \"C\" const uint32_t __ccfg[] __attribute__((section(\".ccfg\"), used)) = {\n 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,\n 0xFFFFFFFF, 0xFFFFFFFF, 0xAAAAAAAA, 0x0000000F,\n 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x0000A55A, 0x03020101, 0x150D0805, 0x36E4D76D,\n 0xDF31F4EB, 0xEE15AE95, 0xE48EBA03, 0xD83FC6C4,\n 0x5E673F45, 0x01C2D774, 0xE558902C, 0x00000000,\n };\n#endif\n|-<<<\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Rtc/","title":"Rtc","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Rtc/#unit-rtc","title":"unit Rtc","text":"ti.mcu.cc23xx/Rtc.empackage ti.mcu.cc23xx\n\nimport InterruptT { name: \"CPUIRQ0\" } as Intr\n\nmodule Rtc\n\n type Handler: function()\n\n function disable()\n function enable(thresh: uint32, handler: Handler)\n function getMsecs(): uint32\n function getRaw(oSubs: uint32*): uint32\n function toThresh(ticks: uint32): uint32\n function toTicks(secs256: uint32): uint32\n\nprivate:\n\n const MSECS_SCALAR: uint16 = 1000 / 8\n const RES_BITS: uint8 = 20\n\n var curHandler: Handler\n\n function isr: Intr.Handler\n\nend\n\ndef em$construct()\n Intr.setHandlerH(isr)\nend\n\ndef em$startup()\n ^^HWREG(CKMD_BASE + CKMD_O_LFINCOVR)^^ = 0x80000000 + (1 << RES_BITS)\n ^^HWREG(RTC_BASE + RTC_O_CTL)^^ = ^RTC_CTL_RST\n ^^HWREG(EVTSVT_BASE + EVTSVT_O_CPUIRQ0SEL) = EVTSVT_CPUIRQ0SEL_PUBID_AON_RTC_COMB^^\n Intr.enable()\nend\n\ndef disable()\n curHandler = null\n ^^HWREG(RTC_BASE + RTC_O_IMCLR)^^ = ^RTC_IMCLR_EV0\nend\n\ndef enable(thresh, handler)\n curHandler = handler\n ^^HWREG(RTC_BASE + RTC_O_CH0CC8U)^^ = thresh\n ^^HWREG(RTC_BASE + RTC_O_IMSET)^^ = ^RTC_IMSET_EV0\nend\n\ndef getMsecs()\n auto ticks = <uint32>^^HWREG(RTC_BASE + RTC_O_TIME8U)^^\n return (ticks * MSECS_SCALAR) >> (RES_BITS - 7)\nend\n\ndef getRaw(oSubs)\n var lo: uint32\n var hi: uint32\n for ;;\n lo = ^^HWREG(RTC_BASE + RTC_O_TIME8U)^^\n hi = ^^HWREG(RTC_BASE + RTC_O_TIME524M)^^\n break if lo == ^^HWREG(RTC_BASE + RTC_O_TIME8U)^^\n end\n *oSubs = lo << 16\n return hi\nend\n\ndef isr()\n ^^HWREG(RTC_BASE + RTC_O_ICLR)^^ = ^RTC_ICLR_EV0\n curHandler() if curHandler\nend\n\n\ndef toThresh(ticks)\n return ^^HWREG(RTC_BASE + RTC_O_TIME8U)^^ + ticks\nend\n\ndef toTicks(secs256)\n return secs256 << 8\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Uptimer/","title":"Uptimer","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Uptimer/#unit-uptimer","title":"unit Uptimer","text":"ti.mcu.cc23xx/Uptimer.empackage ti.mcu.cc23xx\n\nimport Rtc\n\nfrom em.hal import UptimerI\n\nmodule Uptimer: UptimerI\n\nprivate:\n\n var curTime: Time\n\nend\n\ndef calibrate(secs256, ticks)\n ## TODO -- implement\n return 0\nend\n\ndef read()\n curTime.secs = Rtc.getRaw(&curTime.subs)\n return curTime\nend\n\ndef resetSync()\n ## TODO -- implement\nend\n\ndef trim()\n ## TODO -- implement\n return 0\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/UsCounter/","title":"UsCounter","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/UsCounter/#unit-uscounter","title":"unit UsCounter","text":"ti.mcu.cc23xx/UsCounter.empackage ti.mcu.cc23xx\n\nfrom em.hal import UsCounterI\n\nimport Mcu\n\nmodule UsCounter: UsCounterI\n\nend\n\ndef start()\n ^^SysTick->CTRL = (1 << SysTick_CTRL_CLKSOURCE_Pos) | (1 << SysTick_CTRL_ENABLE_Pos)^^\n ^^SysTick->LOAD = 0xFFFFFF^^\n ^^SysTick->VAL = 0^^\nend\n\ndef stop()\n auto lr = <uint32>^^SysTick->LOAD^^\n auto vr = <uint32>^^SysTick->VAL^^\n auto dt = (((lr - vr) << 1) / (Mcu.mclkFrequency / 1000000)) >> 1\n ^^SysTick->CTRL = 0^^\n return dt\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/WakeupTimer/","title":"WakeupTimer","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/WakeupTimer/#unit-wakeuptimer","title":"unit WakeupTimer","text":"ti.mcu.cc23xx/WakeupTimer.empackage ti.mcu.cc23xx\n\nfrom em.hal import WakeupTimerI\n\nimport Rtc\n\nmodule WakeupTimer: WakeupTimerI\n\nend\n\ndef disable()\n Rtc.disable()\nend\n\ndef enable(secs256, handler)\n Rtc.enable(secs256, <Rtc.Handler>handler)\nend\n\ndef secs256ToTicks(secs256)\n return secs256 << 8\nend\n\ndef ticksToThresh(ticks)\n return Rtc.toThresh(ticks)\nend\n\ndef timeToTicks(secs, subs)\n return (secs << 16) | (subs >> 16)\nend\n
"},{"location":"intro/","title":"Why another language\u2009???","text":"For the past fifty years, the C Programming Language has significantly streamlined software development for resource-constrained embedded microcontrollers [MCUs]. Already coming into maturity by 1980, C had proven itself as a practical \u201cmedium-level\u201d language for many of the first 8\u2009/\u200916\u2009/\u200932-bit MCUs \u2013 enabling full entitlement to the underlying silicon, while offering developers greater software productivity and portability over native assembly language.
As a testimony to its staying power, C continues to this day as the dominant programming language for resource-constrained MCUs; and given that we still write embedded software targeting 8\u2009/\u200916\u2009/\u200932-bit processors \u2013 often with less than 32K of memory \u2013 do we really have a practical alternative to C\u2009???\u00a0\u00a0 Yes, we do \u2013 EM.
Other embedded programming languagesBesides C and assembler, practitioners cite C++, Java, and Python as other languages of choice for embedded software development \u2013 with interest in Go and Rust starting to grow. For a variety of reasons, none of these languages come close to overtaking the dominant position of C:\u00a0 developers often fear a steep learning curve with a new language, offsetting any potential gain in productivity; and the language itself might introduce additional runtime overhead in time and (especially) space that render it impractical for the most resource-constrained MCUs.
"},{"location":"intro/#the-big-picture","title":"The big picture","text":"The EM software platform comprises a novel programming language and run-time environment which targets resource-constrained embedded hardware \u2013 on the low-end, systems potentially managed by 8-bit MCUs using as little as 4\u2009K of program memory and 256\u2009B of data. Compared with C, EM features a higher-level programming paradigm centered around highly-reusable software modules which embody the principles of encapsulation, abstraction, and composition.
At the same time, higher-level language constructs introduced by EM don't necessarily cause higher-levels of run-time overhead; modular EM applications designed using modern software techniques will often outperform comparable C programs written in a more conventional style. As you'll learn later on, the EM language translator will in fact generate monolithic C/C++ programs from a set of EM source modules \u2013 leveraging the maturity and ubiquity of optimizing C/C++compilers for embedded MCUs.
More than a programming language, the EM platform also features a modular run-time environment (written in EM, of course\u2009!!!) which streamlines developer productivity through software re-use as well as increases application portability through abstraction \u2013 all without compromising overall system performance. The EM run-time modules include basic services which handle and dispatch real-time application events, as well as device-drivers which manage hardware peripherals typically found within embedded MCUs.
Write once, run anywhereNot unlike other software platforms ranging from Java and Linux to Ruby and Python, EM offers up a \"write once, run anywhere\u2009...\" value-proposition for its application developers. The difference, needless to say, lies in the underlying hardware configurations targeted by EM \u2013 memory-constrained MCUs otherwise incapable of supporting Java or Linux (let alone Ruby or Python), for which C has remained the dominant programming environment for over a half-century.
"},{"location":"intro/#tiny-code-tiny-chips","title":"Tiny code \u2192 Tiny chips","text":"EM programs typically consume less memory than their C counterparts \u2013 a critical feature when targeting resource-constrained MCUs. With 3\u2009X\u2009\u2013\u20095\u2009X reductions in program size not uncommon in practice, many real-world EM applications can comfortably fit in (say) a 32K memory space.
The latest 32-bit MCUs deployed in edge-computing applications will often feature generous amounts of flash memory [\u2009\u2265\u2009512K\u2009] for storing program code, as well as large blocks of SRAM [\u2009\u2265\u200964K\u2009] for reading\u2009+\u2009writing program data; these MCUs also feature processors with sophisticated pipelines as well as advanced memory caches \u2013 all boosting the performance of large (and ever-growing\u2009!!!) bodies of legacy C code used in today's applications.
But what if our EM-based applications really did require only 32\u2009K of memory\u2009???
We could take conventional MCU designs \"over-the-edge\" by embracing a radically simple set of hardware featues which collectively would define the fringe of embedded processing:
\u00a0entry-level CPU core\u00a0 \u2009 no larger than Arm Cortex-M0+ \u00a0tightly-coupled memories (TCMs)\u00a0 \u2009code\u2009+\u2009data; \u2264\u200932K per bank; zero wait-state SRAM \u00a0bulk flash and NO cache\u00a0 \u2009TCMs loaded under firmware control \u00a0rudimentary peripherals\u00a0 \u2009 no specialized auxiliary CPU cores \u00a0always-on domain\u00a0 \u2009 wakeup events from deep-sleep power modes \u00a0\u2265\u2009100MHz system clock\u00a0 \u2009no TCM wait-states \u2192 maximize CPU throughputFeatures through would collectively minimize the number of logic gates and memory cells required to implement this MCU in silicon \u2013 resulting in a smaller chip die compared with a more fully-featured design, which (holding other factors constant) forecasts lower manufacturing costs as well as lower leakage power.
To support \"sleepy applications\" with short active-duty cycles, feature enables our MCU to execute for years on small batteries \u2013 or even indefinitely using harvested energy. Because of its relatively small die-size, our MCU could likely consume \u2264\u20091\u03bcW of power when sleeping; and by retaining all program state in the (small) TCMs of , our MCU can quickly return to its fully-powered active mode when awoken by an event.
Feature , however, seems at odds with achieving an energy-efficient design \u2013 faster clocks proportionally increase switching (dynamic) power consumption; but since the CPU can access its TCMs without stalling, increased clock speed also results in proportionally faster software execution \u2013 enabling our duty-cycled application to spend more time in deep-sleep. In practice, this approach actually improves overall energy(1)utilization by reducing the (static) overhead of leakage power during active execution periods.
Given that feature enables faster execution, the peripherals of can now leverage software running on the main CPU core to perform functions that would otherwise require additional hardware \u2013 such as advanced encryption modes or the upper layers of a comm stack. Unlike conventional cryto or radio blocks (which often contain their own dedicated cores) our peripherals embody a \"RISC-like\" approach which efficiently implements only rudimentary primitives in hardware and then relegates higher-levels functions to software.
EM programs that target our idealized MCU will often exhibit a simple, cyclic structure:
wake-up from deep-sleep
acquire data from the environment
analyze this data using an algorithm
transmit results (wirelessly) to the edge
re-enter deep-sleep
Because of their single-threaded design, these programs would only require a single CPU core to maximize application throughput; software functions normally relegated (say) to an auxiliary cyrto or radio core can now execute exclusively on the main CPU. Consolidating all software onto a single core not only maximizes re-use of MCU logic elements, but can further reduce cost and power by minimizing the silicon footprint of certain peripherals.(1)
So thanks to EM \u2013 10\u2009X fewer bytes, 10\u2009X fewer transistors, 10\u2009X lower power, 10\u2009X lower cost\u2009!!!
The promise of RISC-VThe emergence of RISC-V as an open instruction-set architecture has triggered an abundance of innovation in MCU design; you can literally find dozens of open-source projects containing synthesizable cores expressed in languages like Verilog and VHDL. While much of the RISC-V community has set its sights on high-performance computing from the edge up to the cloud, some practitioners have focused on \"tiny cores\" suitable for low-power, low-cost applications on the fringe; visit the X-HEEP and NEORV32 projects as examples.
With more degrees of freedom when implementing the entry-level RV32ICM instruction set \u2013 often compared against the ARM Cortex-M0+ \u2013 MCU designers can truly innovate when realizing our earlier feature \u2013 all in the interest of further shrinking silicon and software:
by using an internal 8-bit or 16-bit ALU, optionally increasing to maintain throughput;
by locating \u2013 in a compact (17-bit) address-space, further improving RV32ICM code-density; or
by adding specialized instructions to accelerate higher-level software functions supporting
Here again, the tiny-code of EM serves as a catalyst which can drive novel RISC-V tiny-chips.
"},{"location":"intro/#technical-overview","title":"Technical overview","text":"Building upon the tiny\u00a0code\u2009\u2192\u2009tiny\u00a0chips premise behind EM, let's dive into some technical details.\u00a0 The following chapters each focus on a particular aspect of the language and its runtime enviorment, and collectively provide a technical overview of the EM platform:
1)\u2003 EM modules & interfaces \u2022\u2022\u2022 The client/server dichotomy\u2003\u2003 2)\u2003 EM composites & templates \u2022\u2022\u2022 Assembling application elements\u2003\u2003 3)\u2003 EM program life-cycle \u2022\u2022\u2022 From build-time to run-time\u2003\u2003 4)\u2003 EM runtime bundles \u2022\u2022\u2022 Software platform content\u2003\u2003We encourage you to read these chapters in sequence, as each subsequent chapter builds on material covered by its predecessors. At the same time, we recognize that this document introduces a lot of (new) information about a (new) programming environment; feel free to proceed iteratively, skimming the content on your first pass before circling back for a more thorough reading.
The technical overivew also includes exemplary source-code fragments, written in the EM programming language and formatted as follows:
Hello.em# we've omitted a few details here\n# but you should get the basic idea\n\nmodule Hello\nend\n\ndef em$run()\n printf \"Hello world\\n\"\nend\n
Even if you don't plan to (initially) install and use EM, these fragments should give you an intuitive sense of the language \u2013 which relies upon familiar programming constructs (such as seen at line 8 above) that you've likely encountered elsewhere.
So with that, let's move onward to Chapter 1 and begin our technical overview of EM.
"},{"location":"intro/#the-history-of-em","title":"The history of EM","text":"EM's origin story
The EM programming language first appeared at UC Santa Barbara in 2010, where undergraduate students taking CS190C\u2009/\u2009ECE1940 would develop \u201creal-world\u201d embedded applications targeting resource-constrained MCUs \u2013 with all software written in EM, of course, using this (now outdated) language primer as a guide.
The EM language had emerged through deep discussion and intense interaction with Amichai Amar \u2013 a UCSB PhD candidate at that time. Consult his thesis for more details on the outcome of our undergraduate course, as well as a more comprehensive exposition of programming resource-constrained MCUs using EM.
But the true EM backstory actually began decades before its debut at UCSB. In 1998, Texas Instruments acquired Spectron Microsystems \u2013 whose SPOX and DSP\u2009/\u2009BIOS products had already emerged as de facto industry standards. This milestone validated the importance of software technology in further solidifying TI's leadership as a DSP silicon vendor, and in fact triggered a flurry of similar acquisitions during the dot-com boom.
Once inside TI, the Spectron team broadened the application of its patented configuration technology \u2013 which had already enabled DSP\u2009/\u2009BIOS to fit comfortably within the 2\u2009K boot ROM of broadly-deployed, low-power DSPs. This effort culminated in the open-source RTSC project, hosted at the Eclipse Foundation beginning in 2007 and still used today within TI software products. But RTSC fell a bit short in fulfilling its vision \u2013 and provided some much needed impetus for the birth of EM a few years later.
EM's coming-of-age began in 2011 with the founding of Emmoco \u2013 an early player offering an embedded\u2009\u2194\u2009mobile connectivity stack targeting new TI wireless MCUs which supported the emerging BLE standard. Acquired by Shelfbucks in late 2015, the Emmoco stack ultimately evolved to support long-range, low-power subGHz radios in which just one cloud-connected HUB could interact with (say) 10,000 TAGs in a 500,000 sq ft venue.
After Shelfbucks ceased operations in 2019 \u2013 and thanks to some legacy licensing agreements \u2013 EM found its way onto other low-power wireless MCUs from vendors such as NXP, Analog Devices, and ON Semiconductor; EM's foray into the world of RISC-V (as detailed in an earlier note) also began in this time frame. Targeting very high-volume applications for over a decade now, EM's uncanny ability to reduce firmware footprint proved critical in keeping system size, power, and cost in check.
As of today, EM has supported more than twenty 8\u2009/\u200916\u2009/\u200932-bit MCUs from almost a dozen silicon vendors. The EM language translator \u2013 which ultimately outputs ANSI C/C++ code for portability \u2013 has also targeted the most popular toolchains for embedded development [GCC, IAR, Keil, LLVM].\u00a0 Thanks to a recent rewrite of the translator into TypeScript, EM now enjoys robust language support within the VS Code IDE.
More important, perhaps, just a handful of EM programmers have developed thousands of EM modules used (and often re-used\u2009) across a broad spectrum of IoT applications targeting these MCUs. But due to the proprietary nature of these applications, the EM language and its runtime has remained closed \u2013 until now\u2009!!!
"},{"location":"intro/to-1/","title":"The client/supplier dichotomy","text":"EM revolves around the concept of a concrete module
, a programmatic construct that plays a seminal role within the language comparable to the position held by a class within C++ or Java. Not unlike classes, each EM module defines a programmatic boundary between its clients \u2013 users of the module \u2013 and the supplier of the module itself.
To minimize direct coupling between clients and suppliers \u2013 and therefore to increase software re-use \u2013 EM also supports module abstraction through an interface
construct which clients in turn can leverage through a module proxy
.
Reflecting a basic dichotomy between module clients and module suppliers, consider the overall organization of a sample EM module named Mod1
, whose source code will reside in a file named Mod1.em
:
package bob.pkg\n\nmodule Mod1 # client-visible feature declarations\n const C: ...\n type T: ...\n function f( ... )\n # etc\n\nprivate: # supplier-proprietary feature declarations\n var x: ...\n function g( ... )\n # etc\nend\n\ndef f(...) # supplier-proprietary function definitions\n # body of 'f'\nend\n\ndef g(...)\n # body of 'g'\nend\n\n# etc...\n
Starting from the top, each EM module lives within the logical scope of a package
which physically corresponds to a file-system directory of the same name. As in Java or Python, an EM package will generally bear a globally-unique qualified name that identifies its supplier \u2013 bob.pkg
, bob.pkg.test
, dave.pkg
, and so forth. By extension, all EM modules have a (globally-unique) fully-qualified canonical name \u2013 bob.pkg/Mod1
in the current example \u2013 though in practice you'll invariably refer to modules using simple names like Mod1
.
But unlike Java or Python, where the contents of a package named bob.pkg
would actually reside in a nested directory structure with the path bob/pkg
, EM employs a flat organization in which the logical package-name and physical directory-name must match exactly.
Moving on, the declarations beginning at line 3 comprise the public specification of this module \u2013 a coherent collection of constants, types, and functions (what others might term an \"API\"\u2009) available for direct use by clients of Mod1
. Taken together, these externally-visible features of the module constitute a programmatic contract in which future changes on the part of the supplier should (hopefully!!!\u2009) not violate prior client assumptions.
By contrast, features of Mod1
declared beginning at line 9 along with all definitions of its public and private functions beginning at line 15 remain hidden from any clients of this module. The supplier of Mod1
can consequently change the internal implementation of this module \u2013 optimizing performance or improving robustness \u2013 while maintaining a measure of \"plug-compatibility\" from its clients' perspective.
By enforcing crisp boundaries between clients and suppliers, EM modules encourage a software \"best-practice\" known as separation of concerns \u2013 organizing application functionality into discrete programmatic elements that encapsulate specific implementation decisions. Besides helping us digest software-rich systems in \"bite-sized\" chunks (where each EM module becomes a small world unto itself), our ability to manage change throughout the software life-cycle emerges as the most enduring benefit of modularity in general.
"},{"location":"intro/to-1/#importing-modules","title":"Importing modules","text":"To gain access to public features of bob.pkg/Mod1
, clients must explicitly import
this module within their own\u2009.em
files prior to any direct usage. As an illustration, consider a sample module named dave.pkg/Mod2
:
package dave.pkg\n\nfrom bob.pkg import Mod1\n\nmodule Mod2 # client-visible feature declarations\n function f( ... )\n\nprivate: # supplier-proprietary feature declarations\n var t: Mod1.T\nend\n\ndef f(...) # supplier-proprietary function definitions\n Mod1.f( ... )\nend\n
The directive at line 3 effectively adds the identifier Mod1
to this file's top-level namespace, which already includes all public\u2009/\u2009private feature names of Mod2
; an optional trailing as
clause can resolve name conflicts, should they arise. Through their import
directives, EM modules organize themselves into a static hierarchy of clients and suppliers. As a rule, this client-supplier relation must remain acyclic; an EM module can neither directly nor indirectly import itself.
After importing Mod1
, the example accesses this module's public type T
at line 9 followed by its public function f
at line 13 using a qualified name of the form Mod1.feature
. This syntax enables client modules using Mod1
to (coincidentally) declare their own features with identical names, such as the function f
defined here within the scope of Mod2
; unqualified identifiers always refer to features defined within the current module.
By implementing a special (intrinsic) function named em$run
, any EM module can potentially serve as the entry-point for an executable application \u2013 a common pattern in many modern languages. Said another way, EM has no inherent notion of a \"main-program\"; instead, application developers will designate a particular module as the top of a client-supplier hierarchy defined via import
directives.
At the end of the day, EM application programs comprise a set of concrete modules \u2013 each contributing some measure of encapsulated code and data to the final executable image. In the example above, where we directly imported bob.pkg/Mod1
, this module will auto\u00admatically and unconditionally become an element of any application program that directly or indirectly uses dave.pkg/Mod2
.
As an alternative to this mode of direct coupling between modules, EM introduces a language construct known as a proxy
that adds a level of indirection between clients and suppliers \u2013 abstracting a particular supplier's identity from the client's perspective. Akin to polymorphism within object-oriented programming, EM proxies can enhance application flexibility and improve software re-use by further decoupling clients from suppliers; the same client module, as you'll soon see, can effectively (re-)use different supplier imple\u00admentations of otherwise common functionality.
And even more important than re-use, we can better manage change\u2009!!!
By not having to modify client modules that employ proxies, our software becomes more resilient and malleable when \u2013 and not if \u2013 application requirements evolve over time.
To capture commonality amongst a family of \"plug-compatible\" modules, EM enables us to separately publish a set of client-visible features as an interface
\u2013 a public specification independent of any particular supplier implementation. As an illustration, let's refactor the bob.pkg/Mod1
module presented earlier.
bob.pkg/ModI.em
package bob.pkg\n\ninterface ModI # public specification only \n const C: ...\n type T: ...\n function f( ... )\nend\n
bob.pkg/Mod1.empackage bob.pkg\n\nfrom bob.pkg import ModI\n\nmodule Mod1: ModI # public specification \n # additional features\n\nprivate: # internal implementation\n # same as before\nend\n\ndef f()\n # body of 'f'\nend\n\n# etc...\n
The declarations beginning at line 3 mimic the earlier public specification of bob.pkg/Mod1
; but unlike a concrete module, an abstract EM interface cannot have an internal implementation. Our new rendition of Mod1
now inherits its public specification from the interface ModI
at line 3, while declaring any additional (public) features unique to this module; the private portion of Mod1
beginning at line 8 remains unchanged from before.
In practice, abstract interfaces and implementing modules will often reside in different packages: for instance, the EM runtime contains a package named em.hal holding ConsoleUartI
, GpioI
, WakeupTimerI
, and other interfaces; packages such as ti.mcu.cc23xx then hold concrete implementations of these abstract interfaces targeting a particular MCU architecture.
Returning to the matter at hand \u2013 abstracting suppliers \u2013 we'll now refactor our original dave.pkg/Mod2
client module to remove its direct dependence on bob.pkg/Mod1
and instead leverage a local proxy
implementing the ModI
interface.
package dave.pkg\n\nfrom bob.pkg import ModI\n\nmodule Mod2 # client-visible feature declarations\n proxy ModX: ModI\n function f( ... )\n\nprivate: # supplier-proprietary feature declarations\n var t: ModX.T\nend\n\ndef f(...) # supplier-proprietary function definitions\n ModX.f( ... )\nend\n
The syntax at line 6 adds the name ModX
to the top-level scope of Mod2
, as well as declares that the proxy ModX
provides all client features specified within the interface ModI
; access to the public type T
and the public function f
at lines 10 and 14 respectively mirror earlier direct usage of bob.pkg/Mod1
.
Once again, we should emphasize that client Mod2
has no overt coupling to the concrete module Mod1
; instead, Mod2
only knows about the abstract interface ModI
\u2013 which would admit an unbounded number of alternate implementations beyond that provided by Mod1
.
The pattern exemplified here occurs extensively within the EM runtime, and largely holds the key to maintaining platform portability. As a case in point, the package em.utils contains only portable modules such as AlarmMgr
which declares a local proxy implementing the em.hal/WakeupTimerI interface. Client application modules likewise desiring hardware independence can simply follow suit, using the proxy
\u2009\u2013\u2009interface
pattern at each point of potential variability.
Moving on to Chapter 2, we'll now explore the process of binding the Mod2
.
ModX
proxy to the Mod1
module.
By leveraging the proxy
\u2009\u2013\u2009interface
design pattern, EM modules exhibit component-like qualities \u2013 enabling third-party integrators to effectively compose a set of modules that otherwise have no direct knowledge of one another.
To facilitate general aggregation and assembly of discrete modules into larger application entities, the EM language introduces a multi-faceted construct known as a composite
to service these needs.
Like modules and interfaces, each EM composite resides under a named package and will import other units \u2013 modules, interfaces, even composites \u2013 into its top-level namespace. Consistent with their role as higher-level collection points for discrete modules, EM composites will often import surprisingly large numbers of modules in practice.(1)
In their most elementary form, EM composites will selectively export
a group of concrete modules under logical names known to higher-level clients.
package bob.pkg\n\nimport Mod1 as ModX # omit redundant 'from' clause\n\nexport ModX # a logical module\n\ncomposite CompC \nend\n
After re-labeling bob.pkg/Mod1
at line 3 using an as
clause, the export
directive at line 5 publicizes the name ModX
; clients importing CompC
can then use the (logical) module named ModX
, otherwise oblivious to its true identity as bob.pkg/Mod1
. While not declared explicitly, ModX
in fact belongs to the ModI
family of modules \u2013 serving as a concrete delegate suitable for binding to an abstract proxy implementing a common programmatic interface.
Programming with components \u2013 which takes modularity, separation of concerns, and resilience in the face of change to entirely new levels \u2013 dictates that (abstract) interfaces define all functional interactions between independently-replaceable elements. As such, each (concrete) software component \"provides\" and \"requires\" services defined by some set of interfaces \u2013 not unlike the standardized \"plugs\" and \"sockets\" found in hardware components. Through disciplined use of export
directives within EM composites and proxy
declarations within EM modules, we can emulate the provides\u2009\u2013\u2009requires paradigm that forms the backbone of all component-oriented systems.
Besides aggregating modules, EM composites will often take on the task of \"wiring\" together a set of modules into a more integrated assembly \u2013 especially modules like our last version of dave.pkg/Mod2
, which utilize local proxies to further decouple themselves from potential suppliers. To illustrate:
package geof.pkg\n\nfrom bob.pkg import Mod1 # concrete delegate\nfrom dave.pkg import Mod2 # exposes abstract proxy\n\ncomposite CompC\n # no features\nend\n\ndef em$configure()\n Mod2.ModX ?= Mod1 # bind proxy to its delegate\nend\n
Reflecting its higher-level position within the application hierarchy, this particular composite reaches across multiple packages starting at line 3 when importing a set of modules. The actual binding of bob.pkg/Mod1
to dave.pkg/Mod2
via the latter's ModX
proxy then occurs at line 11, using a special single-assignment operator [\u2009?=
\u2009] which we'll explain later. We'll also have more to say about the role played by the intrinsic function em$configure
.
To use the EM language, you'll need an EM distro \u2013 a multi-tiered sub-system that melds portable runtime content in packages like em.utils
with hardware-specific content in packages like ti.mcu.cc23xx
. By design, each EM distro will publish a top-level composite with a fully-qualified name like ti.distro.cc23xx/BoardC. This BoardC
composite in turn builds upon other composites \u2013 whether hardware-specific ti.distro.cc23xx/McuC or else portable em.mcu/CommonC.
Just as individual functions may have parameters, EM modules as a whole can publicize a special kind of parameter known as a config
which clients in turn can selectively assign inside of em$configure
. To illustrate typical usage, let's expand our earlier versions of bob.pkg/Mod1
and dave.pkg2/Mod2
to now expose some new client-visible features in the form of configuration parameters.
package bob.pkg\n\nmodule Mod1\n\n config flag: bool\n # ^| Enable some functionality\n # other public features\n\nprivate:\n\n# etc...\n
dave.pkg/Mod2.empackage dave.pkg\n\nmodule Mod2\n\n config count: uint8\n # ^| Establish some threshold\n # other public features\n\nprivate:\n\n# etc...\n
As you might glean from the special documentation comments, the flag
parameter declared at Mod1
6 and the count
parameter declared at Mod2
6 should each render these modules more flexible than before, and hence increase opportunities for clients to (re-)use Mod1
and Mod2
in a wider range of applications. Syntactically similar to const
and var
declarations within the language, config
parameters have a very special semantic property:
An EM config
behaves like an assignable var
at build-time, but like a read-only const
at run-time.
Once we delve into the EM program life-cycle \u2013 from build-time through run-time \u2013 you'll understand the rationale behind this paradox, as well as more fully appreciate the implications of module configuration for resource-constrained embedded applications. For now, suffice it to say that EM composites serve as an ideal site for assigning module config
parameters when assembling application programs. Expanding our earlier example:
package geof.pkg\n\nfrom bob.pkg import Mod1\nfrom dave.pkg import Mod2\n\ncomposite CompC\nend\n\ndef em$configure()\n Mod2.ModX ?= Mod1 # bind proxy to its delegate\n\n Mod1.flag ?= true # assign config parameters\n Mod2.count ?= 100\nend\n
Just as we bound proxies to delegates, the single-assignment statements at lines 12 and 13 bind parameter values consistent with the types used in corresponding config
declarations found at Mod1
6 and Mod2
6. In this light, clients can treat proxies as a special kind of con\u00adfiguration parameter \u2013 assigned module
values implementing a declared interface
type.
The em$preconfigure
and em$configure
functions defined within McuC provide a realistic example of configuration \u2013 starting with a call to BoardInfo.readRecordH
, which returns a data-structure of board-specific parameter values read from a YAML source file. The (portable) implementation of readRecordH
found within the em.utils/BoardInfo module hints at the range of programmability we can bring to bear during configuration.
The EM language provides a general-purpose template
mechanism for synthesizing other source-level artifacts \u2013 from fragments of C code to complete\u2009.em
files \u2013 during the program build process. The latter scenario, which we'll focus on here, enables suppliers to deliver concrete modules in a more generic embodiment \u2013 one that clients will frequently instantiate within the context of an EM composite.
Starting from the client's perspective, the following composite effectively manufactures a pair of new modules \u2013 locally aliased as Clone1
and Clone2
\u2013 by instantiating a common imported template named thom.pkg/GenT
.
package geof.pkg\n\nfrom thom.pkg import GenT {flag: true, count: 100} as Clone1\nfrom thom.pkg import GenT {flag: false, count: 200} as Clone2\n\nexport Clone1\nexport Clone2\n\ncomposite CompC\nend\n\ndef em$configure()\n # assign Clone<n> config parameters\n # bind Clone<n> proxies to delegates\n # delegate other proxies to Clone<n>\nend\n
Using an expanded form of import
directive at lines 3 and 4, this composite works with (synthesized) modules Clone1
and Clone2
no differently than how we employed the bob.pkg/Mod1
or dave.pkg/Mod2
modules defined earlier. Since Clone1
and Clone2
have no prior outside identity, composites will often export
these newly-formed modules for use by higher-level clients; composites may further configure and assemble these synthesized modules within the body of their own em$configure
function.
The flag
and count
values bound at lines at lines 3 and 4 above \u2013 which shape the essential characteristics of the synthesized Clone1
and Clone2
modules \u2013 ultimately correspond to public configuration parameters declared within thom.pkg/GenT
:
package thom.pkg\n\ntemplate GenT\n\n config flag: bool\n # ^| Enable some functionality\n config count: uint8\n # ^| Establish some threshold\nend\n\ndef em$generateUnit(pn, un)\n |-> package `pn`\n |->\n |-> module `un`\n |->\n if flag\n |-> const MAX: Uint8 = `count`\n end\n # generate remainder of this module\nend\n
Like any other config
, the parameters declared after line 5 become assignable variables at program build-time \u2013 in this case, via import
directives beginning at line 3 of CompC
. The GenT
template will then consume these configuration parameters within the body of its em$generateUnit
function, which synthesizes lines of source code using special EM output statements prefixed by the |->
symbol.
The flag
config declared at 6 impacts the generated output by controlling execution flow within em$generateUnit
; by contrast, this function directly interpolates the count
config declared at 9 within the generated output. When invoked, em$generateUnit
receives pn
and un
arguments bound to strings like \"geof.pkg\"
and \"CompC__Clone1\"
\u2013 reflecting the original context in which the geof.pkg/CompC
composite imported and instantiated the GenT
template back on the earlier line 3.
Our McuC composite instantiates a module-per-pin using this GpioT template. Inspecting this template's em$generateUnit
function, note how the pin
config ultimately shapes the synthesized module through interpolation within the |->
output statements. Though not a rule, synthesized modules will often implement an interface that captures their commonality \u2013 em.hal/GpioI in the example at hand.
To further grasp the special role played by composite
and template
units, let's proceed onward to Chapter 3 and explore EM's rather unique build flow.
We turn now to the life-cycle of an EM program \u2013 covering its build-time transformation from a single \"main\" source module into a binary program image comprising multiple modules, as well as its run-time execution phases from hardware reset to system shutdown.
Along the way, you'll understand the role played by EM language intrinsics throughout the program life-cycle \u2013 not only to demarcate execution phases at program run-time (em$run
), but also to enable active participation by content suppliers at various stages during program build-time (em$configure
and em$generateUnit
).
The following figure depicts the four principal phases of the EM program life-cycle, as well as maps out the high-level flow of build-time artifacts \u2013 starting with a ModP.em
source file and ending with a main.out
binary image. The first three of these phases unfold on your host computer, and collectively constitute program build-time; the final phase, needless to say, represents run-time execution of the generated program on target hardware.
Each\u2009.em
source file \u2013 whether a module
, interface
, composite
, or template
\u2013 represents an independent unit of translation within EM. Starting from a designated top-level unit \u2013 in our case, a module named ModP
which would implement the em$run
intrinsic \u2013 EM will (recursively) process an N-element hierarchy of other translation units that ModP
directly or indirectly imports; since the relation defined by import
directives cannot have cycles, translating ModP
effectively yields a top-to-bottom (partial) ordering of its dependent units.
Translating concrete modules such as ModP
will generally produce three corresponding output files, consumed in subsequent phases of the program build process:
ModP.hpp
the public\u2009/\u2009private features of ModP
translated into a C++ header file ModP.cpp
internal function definitions within ModP
translated into equivalent C++ code ModP.js
a JavaScript rendition of ModP
which will contribute during program configuration Translating abstract interfaces such as our earlier ModI
example will only yield a ModI.js
and ModI.hpp
output file. Translating composites or templates such as our earlier CompC
or GenT
examples \u2013 which contribute at build-time but not run-time \u2013 will only yield a CompC.js
or GenT.js
output file.
Finally, all template instantiations encountered en route through import
directives (such as the references to GenT
in CompC
) will trigger immediate execution of the designated template's em$generateUnit
intrinsic \u2013 already translated to JavaScript within the GenT.js
output file. Unit translation of the new\u2009.em
file produced at this step then proceeds recursively.
A top-level module such as ModP
could easily have static dependencies on more than 100 other translation units \u2013 especially when imported composites aggressively instantiate templates managing discrete MCU resources like GPIO pins. To accelerate program build-time, the EM translator maintains an internal cache of all generated files and will only (re-)translate a particular\u2009.em
file when deemed necessary.
The configuration phase of the EM program life-cycle \u2013 still upstream from the final compilation of all generated C++ code into a binary image \u2013 actually entails executing a special hosted version of the program rendered in JavaScript. Labeled main.js
in the earlier figure, this fabricated program basically amalgamates the\u2009.js
files output for each module or composite found within the N-element import
hierarchy rooted at ModP
itself.
Seemingly, any hosted language (Java, Python, Ruby) could provide a suitable execution environment for this phase of the EM program life-cycle. Some might argue the case for Python, as this language already plays a similar role with respect to C/C++ code \u2013 especially in emerging platforms such as TinyML, which deploy machine-learning algorithms (developed in a hosted Python environment) onto embedded target hardware.
As it turns out, JavaScript had already claimed the host language role among EM's predecessors \u2013 notably the Eclipse\u2009/\u2009RTSC project which in turn drew upon earlier DSP/BIOS configuration technology. Given the Java-centricity of the Eclipse IDE, Mozilla's Rhino \u2013 a JavaScript engine written in Java and seemlessly integrated with the JVM runtime \u2013 served as an ideal environment at that point in time.
Indeed, an Eclipse plug-in (written in Java) provided almost a decade of IDE support for the EM language; and Rhino therefore remained our JavaScript platform of choice. But now that language support for EM has migrated to the VS Code IDE \u2013 written in TypeScript and running on the Chromium\u2009/\u2009V8 engine \u2013 Node.js provides an even richer JavaScript platform for hosting the configuration phase of the EM program life-cycle.
During its execution, the prog.js
program makes three top-to-bottom passes over the N-element import
hierarchy rooted in ModP
\u2013 invoking JavaScript translations of certain EM intrinsics on a per-unit basis (if defined).
The 1st pass invokes em$preconfigure
, which only composites may elect to define; public proxies and config parameters bound at this time using the single-assignment operator [\u2009?=
\u2009] become immune to further modification in the next pass.
The 2nd pass invokes em$configure
, which modules as well as composites may elect to define; proxies and configs bound here using the [\u2009?=
\u2009] operator become immune to further modification by lower-level units yet to execute in this pass.
The 3rd pass invokes two intrinsics on modules whose special em$used
config parameter tests true: em$construct
, for initializing private module state; and em$generateCode
, for synthesizing internal C/C++ code using the EM template mechanism illustrated in GenT
.
The [\u2009?=
\u2009] operator, as hinted earlier, implements single-assignment semantics \u2013 sealing the first value assigned to a configurable proxy or parameter, while silently ignoring all sub\u00adsequent assignments to the same feature. With a top-to-bottom ordering imposed on the ModP
import
hierarchy, [\u2009?=
\u2009] operations executed by higher-level modules and composites essentially \"override\" (default) binding decisions made by lower-level units. By implementing em$configure
, ModP
itself can now preempt proxy\u2009/\u2009parameter assignments otherwise made by any modules or composites it may import.
Higher-level modules such as ModP
cannot, however, effect the values of configurable features already bound in the first configuration pass via em$preconfigure
; the latter intrinsic enables suppliers of EM composites to selectively freeze proxy bindings and parameter values, tempering flexibility in the interest of robustly assembling elements for a fixed application setting. As an example, our McuC composite binds physical pin numbers read from a board-specific YAML file \u2013 reflecting the \"hard reality\" of the underlying hardware.
Referring to the earlier figure, one practical consequence of configuration becomes pruning the original (and often large) N\u2013element import
hierarchy into a more tractable M\u2013element subset comprising those modules actually used within the program. In support, each module has an intrinsic em$used
parameter \u2013 automatically bound in most cases, but explicitly con\u00adfigurable if necessary \u2013 that ultimately determines membership in the M\u2013element subset.
The top-level module ModP
has its em$used
parameter automatically set, and is always used within the program.
If module Mod1
is used and Mod1
imports module Mod2
(directly or via a composite), then Mod2
is used as well.
If module Mod1
is used and proxy Mod1.ModX
ultimately delegates to module Mod2
, then Mod2
is used as well.
Otherwise Mod1
is not used in the program, unless some higher-level module or composite explicitly sets Mod1.em$used
.
The final configuration pass gives each used module within the M\u2013element subset an opportunity to focus internally; configuration of all public features of these modules would have already occurred. By defining the em$construct
intrinsic, modules may programmatically initialize their private var
, config
, or even proxy
features at this point within the flow.
But since em$construct
actually executes on your host computer, module suppliers can now implement complex initialization algorithms at build-time that would otherwise prove far too costly to execute at run-time on resource-constrained MCUs.
With language constructs normally used to implement target-side functions like em$run
also available in hosted functions like em$construct
, module suppliers can now migrate (expensive) computations from run-time to build-time with little effort. Said another way, EM can serve as its own meta-language \u2013 synthesizing the final form of a concrete module by statically reflecting upon values assigned to its configurable parameters.
The em$construct
function of ti.mcu.cc23xx/ConsoleUart0 computes values for private configs ibrd
and fbrd
, eventually used to initialize hardware registers defining the UART's baud-rate; a less efficient implementation would perform this computation at run-time. Taking this approach to the next level, the em$construct
function of em.utils/FftC32 initializes a custom sine-wave table at build-time.
Besides executing (costly) math functions, EM meta-programming can also initialize complex, linked data-structures at build-time \u2013 such as the em$construct
function and createH
functions of em.utils/FiberMgr, which in turn call build-time functions of em.utils/ListMgr. As a general rule, any static initialization of data at build-time results in more compact programs at run-time.
Complementing em$construct
\u2013 oriented towards initializing private state \u2013 some modules will also implement the em$generateCode
intrinsic. Using the same form of templatized output statements illustrated earlier in GenT
, module suppliers can inject customized C/C++ code fragments into the final program image \u2013 with public config par\u00adameters typically shaping the synthesized output.
On the low end of the scale, MCU-specific modules like ti.mcu.cc23xx/Regs use the em$generateCode
intrinsic to #include
vendor-supplied header files; modules such as Rtc will then reference symbols and macros defined in these headers using a special ^^
escape token.
Moving up a notch, the ti.mcu.cc23xx/IntrVec module programmatically synthesizes the run-time vector table for this MCU using build-time bindings of interrupt handlers \u2013 complete with compiler-specific directives to control placement in memory. In the limit, em$generateCode
can leverage the full capabilities of JavaScript executing on your host computer.
Referring back to the earlier figure, the ultimate outcome of executing the main.js
(meta-) program within the overall EM build-flow becomes yet another program \u2013 this time, a single C++ program labeled main.cpp
. As suggested earlier, this program only incorporates generated code from the M used modules selected from the original set of N imported units traced back to ModP.em
.
Each module Mod
participating in this consolidated C++ program respectively contributes (in order) the following portions of code, which collectively represents the bulk of the generated main.cpp
file's content:
constant, type, variable, and function declarations from Mod.hpp
, generated during the initial translation of Mod.em
;
static data initializers reflecting the values assigned to public\u2009/\u2009private features of Mod
during the prior configuration phase;
any C/C++ code synthesized by the Mod.em$generateCode
intrinsic, executed during the prior configuration phase; and
definitions of declared and intrinsic functions from Mod.cpp
, generated during the initial translation of Mod.em
.
By merging all generated C/C++ code into a single input file, the underlying compiler for the target MCU can aggressively optimize the program as a whole \u2013 folding away constants, inlining small functions, and eliminating unused code or data. As a case in point, client function calls via abstract proxies to configured delegate modules \u2013 seemingly a double-indirection at run-time \u2013 will usually \"melt-away\" and leave the delegate function body inlined at the client call-site.
Example of whole-program optimizationReturning to FftC32, its exec
function uses three config
parameters at run-time which em$construct
previously initialized by at build-time \u2013 N_WAVE
, N_WAVE_LOG2
, SINE_WAVE
. Knowing the values of these parameters when digesting main.cpp
, the compiler has greater latitude in making time\u2009/\u2009space tradeoffs when generating object code for FftC32.exec
.
As another example, em.utils/FiberMgr makes many function calls via Common.GlobalInterrupts
\u2013 a proxy which conforms to the GlobalInterruptsI interface, and ultimately delegates to a hardware-specific implementation such as ti.cc23xx.mcu/GlobalInterrupts. Knowing this particular proxy\u2009-\u2009delegate binding, the compiler would inline the delegate's (small) functions directly at each Common.GlobalInterrupts
call site.
This final phase of the EM program life-cycle \u2013 which represents the transition from build-time to run-time \u2013 technically commences when you load the executable main.out
image into target memory and reset the MCU. But as you'll see, run-time contributions from the M concrete modules used within this program won't occur until execution reaches main
.
main
The path actually taken from loading the main.out
file to executing the C/C++ main
function can vary widely from one target environment [MCU\u2009+\u2009compiler\u2009+\u2009board] to the next; but fortunately, each distribution of the EM software platform will render this process transparent to the application developer. In practice, each EM distro will leverage much of the tooling infrastructure supporting the underlying MCU \u2013 from flash loaders that operate on standard\u2009.bin
or\u2009.hex
files, to compiler startup files like crt0.s
that manage the transition from MCU reset to C/C++ main
as efficiently as possible.
For the M concrete modules bound within the main.out
image, program run-time actually begins when target execution reaches the C/C++ main
function. Since the un\u00adderlying compiler's own startup file does little more than prepare data memory and initialize critical CPU registers, more comprehensive startup of the target board and the MCU peri\u00adpherals still needs to occur prior to calling the top-level ModP.em$run
intrinsic.
The main
function initially calls a C++ rendition of Modr.em$reset
, where Modr
represents the first module to implement this intrinsic found by a top-to-bottom scan of the M modules used in this program; needless to say, this scan occurs at program build-time, not run-time. In practice, some target-specific module included with your EM distribution will assume responsibility for defining the em$reset
intrinsic; higher-level application modules generally avoid (re-)defining this intrinsic.
The main
function will next call C++ renditions of Modi.em$startup
for each Modi
found to implement this intrinsic; here too, a top-to-bottom scan of all M program modules occurs at build-time. Unlike em$reset
, higher-level application modules down to target-specific driver modules will define this intrinsic in order to perform (run-time) initializations not possible during (build-time) execution of em$construct
.
The main
function then calls a C++ rendition of Mods.em$startupDone
, where Mods
represents the first module found to implement this intrinsic through a top-to-bottom scan of all M modules participating in the program. As with em$reset
, your EM distribution will usually take responsibility for defining em$startupDone
\u2013 which performs any final hardware setup before the application program assumes control.
The main
function finally calls a C++ rendition of ModP.em$run
, which effectively transfers control to the top-level module of this application. Since embedded applications often execute some form of \"run forever\" loop \u2013 whether explicitly coded within the program or else implicitly managed by some run-time task scheduler \u2013 in practice the em$run
intrinsic will not return control back to the calling main
function.
em$startup
Many modules that manage MCU hardware peripherals will define em$startup
\u2013 such as Idle and Rtc found in the ti.mcu.cc23xx
package; clearly, this sort of hardware setup must occur at run-time. By extension, portable modules like em.utils/SoftUart which leverage proxies to interact with underlying hardware may likewise rely upon em$startup
to perform some run-time initialization \u2013 in this case, initializing a GpioI proxy named TxPin
.
Should the top-level ModP.em$run
intrinsic actually return to main
, control then transfers to a distinguished __halt
function \u2013 also generated prior to program compilation \u2013 which supervises an orderly shutdown of the target application. If necessary, any program module can explicitly initiate the shutdown sequence at run-time through a special halt
statement that can appear inside EM function definitions.
The __halt
function will first call C++ renditions of Modi.em$shutdown
for each Modi
found to implement this intrinsic; higher-level applications down to target-specific drivers modules will define this intrinsic in order to perform run-time finalization prior to halting the processor.
The __halt
function then calls a C++ rendition of Modh.em$halt
, where Modh
represents the first module to implement this intrinsic found by a top-to-bottom scan; each EM distro offers a \"default\" version of em$halt
, though higher-level modules may (re-)define this intrinsic in some cases.
Should the implementation of em$halt
happen to return to __halt
, program control would fall-through to a special block of code that simply spins within an infinite loop.
In cases where something goes \"seriously wrong\" within the system and execution should terminate more abruptly, EM also supports a special fail
statement that can appear in any function definition. When executed at run-time, fail
immediately transfers control to an implementation of the em$fail
intrinsic \u2013 often found within the same module implementing em$halt
; should em$fail
return, the program likewise enters an infinite loop.
By design, each EM distro relies upon the portable em.utils/BoardController module which centralizes definitions of the singleton intrinsics em$reset
, em$startupDone
, em$halt
, and em$fail
; configuration of the BoardController
module and its dependents typically occurs within the distro's BoardC composite. In those (rare) circumstances where some higher-level module needs to \"override\" one of these special functions, the higher-level intrinsic definition would likely call the corresponding \"base\" function within BoardController
.
While we've already directed you to browse selected\u2009.em
source files drawn from EM platform runtime, Chapter 4 concludes our technical overview with more comprehensive picture of this environment.
While software design and development focuses on individual\u2009.em
files \u2013 modules, interfaces, composites, templates \u2013 a more coarse-grained construct known as a bundle serves as the unit of software delivery within the EM platform. Like the packages they ultimately contain, each EM bundle bears a globally-unique qualified name suggestive of its publisher and purpose \u2013 though individual\u2009.em
files will only reference packages by name, and never the containing bundle itself.
Representing \"components-in-the-large\", an EM bundle will not only publicize the elements it provides but will also identify other bundles it requires for successful deployment; de\u00adpendent bundles may in turn require other bundles \u2013 reminiscent of the hierarchic import
relation between individual\u2009.em
files. Bundles can also serve as a unit of software versioning within the platform, with dependencies optionally constrained to particular labeled releases.
In the sections that follow, we'll respectively explore these three EM bundles:
em.corehardware-independent packages fundamental to EM
ti.cc23xxhardware-dependent packages comprising a typical EM distro
em.docshardware-independent example programs which use the prior bundles
"},{"location":"intro/to-4/#portable-content","title":"Portable content","text":"As its name suggests, the em.core
bundle incorporates rudimentary and essential content present in all distributions of the EM software platform \u2013 some of which we've already visited earlier in this document. Through aggressive use of the proxy
\u2009\u2013\u2009interface
pattern, a critical subset of the em.core
bundle \u2013 specifically, its em.mcu
and em.utils
packages \u2013 in fact remain 100% portable across all target environments.
This package contains abstract interfaces reflecting the functionality of low-level hardware elements found within embedded MCUs \u2013 ConsoleUartI
, LedI
, WakeupTimerI
, and many others; this package contains also contains \"empty\" implementations of these interfaces (ConsoleUartN
, LedN
, etc), often used as default proxy
bindings. As such, this package serves as a critical hardware abstraction layer (HAL) \u2013 prescribing a fundamental architectural boundary that insulates portable (hardware-independent) content from target-specific (hardware-dependent) content.
This package comprises a somewhat eclectic (and ever-expanding) collection of run-time application services \u2013 ranging from elementary modules like ListMgr
and FiberMgr
up to more sophisticated templates like ButtonT
and LedT
. While mostly supporting program run-time, special host
modules like BoardInfo
and BoardMeta
offer (portable) services used by EM distros at program build-time.
Unlike em.utils
, this package generally limits its contents to what we'll term as global proxies \u2013 a set of well-known modules that implement certain em.hal
interfaces by effectively forwarding function calls to a conformant delegate; ConsoleUart
and Poller
illustrate this pattern. This package also contains the widely-used Common
module, which conveniently aggregates additional global proxies into a single unit.
Invisible to most clients, this package helps bootstrap the implementation of the EM language itself through several distinguished interfaces declaring intrinsic configs and functions:\u00a0 ModuleI
, that all modules or interfaces will inherit; CompositeI
, that all composites will inherit; and TemplateI
, that all templates will inherit. This package also contains the Console
module and its ConsoleProviderI
interface \u2013 supporting printf
statements innate to the language. Finally, this package houses common C/C++ and JavaScript code fragments interpolated during the EM program build-flow.
Using EM will take a more detailed look at the source code of specific\u2009.em
files found in the first three of these packages.
Complementing em.core
and its portable packages, the ti.cc23xx
bundle delivers support for the Texas Instruments CC2340R5 wireless MCU. While specifically targeting a particular MCU family, the organization of the packages found within the ti.cc23xx
bundle follows a pattern generally seen within any EM distro.
This package (and in fact the entire ti.cc23xx
distro bundle) revolves around a single composite conventionally named BoardC
\u2013 responsible for configuring parameters as well as binding proxies exposed by elements of em.core
. This package also contains two other conventionally named units found in any EM distro:\u00a0 the McuC
composite, which (in this case) focuses on the ti.mcu.cc23xx
package; and the special host
BoardMeta
module, which effectively defines the schema of the board-specific YAML file mentioned earlier in the context of configuration and pre-configuration.
Many of the modules in this package provide MCU-specific implementations of abstract interfaces found in em.hal
\u2013 ConsoleUart0
, Idle
, OneShotGpt3
, and others. Likewise, this package features templates such as GpioT
and InterruptT
whose em$generateUnit
intrinsics synthesize MCU-specific implementations of other HAL interfaces at program build-time. Finally, this package contains even lower-level auxiliary modules (eg, Regs
and Rtc
) whose clients typically remain within the distro itself.
This package contains host
modules that control the compiling\u2009/\u2009linking of target EM programs using a particular C/C++ toolchain. The modules typically pass target-specific information such as memory maps or register declarations to generic modules found in a special em.build
bundle, which we'll discuss in a later document.
Porting EM will dive into virtually all of the\u2009.em
source files found in these three packages, as well as explore the em.build
bundle mentioned above.
The em.examples.basic package within the em.docs
bundle contains a curated set of programs described at length in Using EM. A live sequence of \"guided tours\" that view\u2009/\u2009build\u2009/\u2009load these programs revolve around the following modules:
To ensure their portability, none of these programs explicitly reference the \"current\" EM distro package [\u2009ti.distro.cc23xx
\u2009] by name; rather, these programs use a special language intrinsic [\u2009em$distro
\u2009] as a logical name which you will bind to a particular distro package.
If you want to learn more about EM \u2013 but continue to fly at 10,000' for now \u2013 we strongly recommend reading through Using EM to get a sense for the platform's tooling and runtime from a ground-level perspective; a quick pass through the other documents at this site should prove informative as well.
When you want to hit the ground running and start coding, however, you'll definitely need to first work through Installing EM before proceeding onward to Using EM. Both of these documents also accomodate a special **\u2009FAST-TRACK\u2009** option, which enables you to rapidly experience the EM development environment as well as learn more about em.core.
The Porting EM document continues on the ground, exploring the ti.cc23xx bundle in greater detail as well as outlining the steps required to create new EM distros that support other MCUs. Over time, we expect Porting EM will draw upon a broader set of distro bundles to further reinforce the EM platform's architecture for encapsulating target-specific content.
Finally, Advancing EM contains an ever-growing collection of standalone articles addressing a wide-range of topics about EM. While many of these articles expect that you've already spent time on the ground coding, others presume just a 10,000' understanding of Using EM.
Case in point:\u00a0\u00a0 we strongly recommend the Energy Consumption article, which presents further evidence supporting a hypothesis about EM put forth earlier.
Happy coding\u2009!!! \u2002
"},{"location":"porting/","title":"Managing hardware diversity in EM","text":"Under Construction \u2014 estimated completion in 1Q24 \u2009
"},{"location":"using/","title":"Writing portable applications in EM","text":"If you've made it this far, let's hit the ground running and starting coding\u2009!!!\u00a0\u00a0 This document will first familiarize you with the mechanics of viewing, editing, building, and loading EM programs and their constituent units using the EM Builder extension for VS Code. If you haven't worked with VS Code, the web abounds with tutorials\u2009+\u2009references for this ever-popular IDE.
The main portion of this document comprises a series of tours that visit some basic programming examples \u2013 curated to introduce elements of the EM runtime environment, as well as to reinforce common coding idioms of the EM language(1). To support these documentation resources, EM Builder includes a \"tour guide\" utility that walks you through this material in a somewhat specialized fashion.
If you've chosen to bypass Installing EM for now, a quick read through this document would still have benefit \u2013 even if you can't execute real software on real hardware.
If you have elected to install EM Builder \u2013 but haven't received your hardware or else prefer the **\u2009FAST-TRACK\u2009** option \u2013 you can still build the programs featured in any of the tours. Until your board arrives and you can actually load these programs, studying the logic capture(s) associated with each example should help bridge the gap.
These captures in fact reveal subtle timing details otherwise invisible to the naked eye.
We've generated all of the captures presented later in this document using a Saleae Logic Analyzer \u2013 in our opinion, the single most valuable tool for \"debugging\" real-time embedded software running on target MCU hardware. At a minimum, you should download the (free) Saleae Logic 2 software and explore these logic capture files off-line.
If you can't see the problem, you can't fix it\u2009!!!Software debuggers have traditionally focused upon a rich presentation of program state (variables, call stacks, peripherals) \u2013 but only after program execution halts (say) upon reaching a breakpoint. Given the general nature of embedded software, however, a dynamic presentation depicting state-changes over time proves far more helpful for troubleshooting real-world problems.
While modern debuggers attempt to address this need, they often do so by introducing significant amounts of hardware\u2009/\u2009software overhead \u2013 limiting their utility to software development rather than system deployment. These approaches may also preclude the underlying MCU from entering its \"deep-sleep\" mode \u2013 a serious limitation that can obscure real-world, real-time wakeup issues.
But armed with no more than a basic logic analyzer, the EM language provides intrinsic \"real-time debug\" capabilities that enable us to dynamically capture program state without incurring excessive program overhead. In the spirit of \"test what you fly, fly what you test\", these capabilities prove equally valuable during development in the factory and well as during deployment in the field.
At the same time, we also want you to experience the joy of blinking LEDs\u2009!!!\u00a0\u00a0 With that, let's first skill-up on EM Builder and then tour the EM runtime.\u00a0
When finished, circle back to Learning more EM and plan your next adventure.
"},{"location":"using/using-01/","title":"Working with EM Builder","text":"To flatten your learning curve, we'll work exclusively within the VS Code environment \u2013 using the EM Builder extension which you've already installed. Besides introducing some core capabilities of the extension, the material that follows will also verify the integrity of your installation.
But what about the command line\u2009???Strictly speaking, we don't need VS Code to develop EM software:\u00a0 you can create folders\u2009/\u2009files as you always have; you can create\u2009/\u2009modify\u2009.em
sources in your favorite text editor; and you can build\u2009/\u2009load executable EM programs using the em-cli
command-line interface.\u00a0 The implementation of EM Builder and em-cli
, in fact, share quite a bit of common code \u2013 with the former often invoking the latter. The Command-line inteface reference material describes the em-cli
tool in greater detail.
The following animation illustrates the overall organization of your workspace folder when using the EM Builder extension, and picks up from where we left off when Installing EM.\u2009 We also encourage you to launch VS Code by entering code
workspace
from your PC's shell, and to simultaneously explore this environment at your own pace.
The following animation illustrates how to build HelloP.em
using the EM - Build command, selected from this file's (right-click) context menu. Since this program contains a main em$run
function, the EM translator produces a corresponding executable image; see the OUTPUT panel for details.
The following animation illustrates how to load the previously-built HelloP.em
program using the EM - Load command \u2013 also selected from a context menu. Should loading the executable image fail for some reason (eg, you don't even have a target board), an error message would appear in the OUTPUT panel.
The following animation illustrates how to monitor any printf
output from the currently loaded program image, by invoking EM - Open Console within the VS Code Command Palette\u2009. Once the special TERMINAL panel has launched, press the RESET button on your target board and enjoy the show\u2009!!!
The following animation illustrates how to create a workspace bundle using the EM - New Bundle command, likewise found in the Command Palette\u2009. In this case, we've named the new artifact as my.bundle
; the remainder of this document will often refer to this folder as your \"personal\" bundle.
The following animation illustrates how to populate your personal bundle folder with other EM artifacts \u2013 packages, modules, interfaces, etc \u2013 created using commands selected from the (right-click) context menu of the containing folder. In this case, we'll invoke EM - New Package followed by EM - New Test Program to create a unit named my.pkg/Test
.
The following animation illustrates the EM - Clone command, used to populate personal bundles with runtime content delivered by the EM Builder extension and housed under the special\u2009.em-cargo
folder. In this case, we'll first locate em.examples.basic/FiberP
and em.utils/FiberMgr
under\u2009.em-cargo
and then clone these units into my.bundle
.
The following animation illustrates a few of the VS Code language features supported by the EM Builder extension. In this case, we'll hover over EM identifiers and jump between\u2009.em
source files; we'll also use the OUTLINE panel to navigate amongst the declarations\u2009/\u2009definitions contained within individual EM units.
While these capabilities prove helpful when exploring EM source code, the EM Builder extension also provides support for \"smart completion\" (IntelliSense) \u2013 even more helpful when editing your code.\u00a0 At the same time, VS Code language support remains a never-ending journey of incremental improvement.
Help wanted \u2013 VS Code programmer
"},{"location":"using/using-02/","title":"Programming with EM","text":"Turning now to the EM runtime, a series of curated \"guided tours\" will incrementally introduce components of the em.core bundle \u2013 many of which you may have browsed in your reading of Introducing EM.\u2009 By convention, each tour revolves around a specific EM program found in the em.examples.basic package \u2013 with each of these small programming examples motivating us to visit key elements of the EM runtime.
In the material that follows, we'll reference a number of logical MCU \"pins\" configured by the current EM distro and generally accessible through headers on your target board:
\u2003\u2003\u2003\u2003\u2003appBut
application button pin \u2003\u2003\u2003\u2003\u2003appLed
application LED pin (usually green) \u2003\u2003\u2003\u2003\u2003appOut
application console TX pin \u2003\u2003\u2003\u2003\u2003sysDbgA
system debug pin A \u2003\u2003\u2003\u2003\u2003sysDbgB
system debug pin B \u2003\u2003\u2003\u2003\u2003sysDbgC
system debug pin C \u2003\u2003\u2003\u2003\u2003sysDbgD
system debug pin D \u2003\u2003\u2003\u2003\u2003sysLed
system LED pin (usually red) A special YAML file named em-boards
found the em$distro
package folder binds these logical pin names to physical pin numbers of your target board. Porting EM will have more to say about em-boards
, as well as explain the special setup-*.properties
files located in the root of your distro bundle.
The following animation illustrates the EM - Start Tour command, which you'll use to view each of the tours described in the remainder of this document. You'll find these tours in appropriately named\u2009.emtour
files, located inside the\u2009.tours/101_Using_EM
folder delivered with the em.docs
bundle.
With that, go ahead and actually take tour 00_HelloP.emtour
within your VS Code environment. Before proceeding to take Tour 01\u2009\u2013\u2009Tour 12\u2009, however, you should feel comfortable with the UI presented in Tour 00\u2009; and do retake any of these tours whenever necessary.
This tour centers around the BlinkerP program, which toggles the appLed
pin on your target board \u2013 typically connected to a green LED. This tour also visits the BusyWaitI and LedI interfaces, as well as presents the Common module and its constituent proxies.
\u00a0 The EM runtime quickly blinks your board's sysLed
at startup.
\u00a0 The BlinkerP
program toggles appLed
every ~0.5\u2009s.
\u00a0 The EM runtime turns on sysLed
upon normal program termination.
\u00a0 The next capture zooms into the appOut
signal.
\u00a0 The EM runtime outputs this sequence of (non-printable) bytes at startup, after blinking sysLed
.
\u00a0 The EM runtime output this single byte upon normal program termination, before turning on sysLed
.
This tour focuses upon the BlinkerDbgP program, which also toggles the appLed
pin on your target board. This tour highlights a number of capabilities within EM for visualizing and troubleshooting program execution in real-time.
\u00a0 The busy-wait bracketed by %%[d+]
and %%[d-]
measures ~498 ms.
\u00a0 Executing a fail
statement causes sysLed
to blink rapidly.
\u00a0 The %%[>cnt]
statement outputs the (non-printable) 0x82
control byte followed by 2 bytes of payload.
\u00a0 The %%[>bits11]
statement outputs the (non-printable) 0x81
control byte followed by 1 byte of payload.
\u00a0 The %%[c:bits11]
statement causes the dbgC
pin to rapidly toggle 2 times \u2013 since (bits11\u2009==\u20090x1)
\u00a0 The ASCII character output of printf
begins here \u2013 0x63('c')
, 0x6E('n')
, and so on.
This tour centers around the FiberP program, which introduces a very lightweight threading construct termed a fiber. This tour also visits the EM runtime's (portable) implementation of fibers found in the FiberMgr module.
Tour 03 \u2013 Logic Capture \u00a0 The %%[d]
statement in blinkFB
marks each point in time where the blinkF
fiber begins execution.
\u00a0 The ~200 ms between blinkF
cycles includes appLed
on\u2009/\u2009off time plus FiberMgr
dispatch overhead.
This tour centers around the Button1P program, which handles incoming events generated when pressing appBut
on your board. This tour also visits the GpioEdgeDetectMinI interface that in turn extends the GpioI abstraction.
\u00a0 The EM runtime uses dbgB
to mirror the MCU's execution mode [\u2009L \u2013 actively running, H \u2013 awaiting wakeup\u2009]
\u00a0 Pressing your board's button drives the appBut
pin low; appLed
then blinks shortly thereafter.
\u00a0 A 125\u2009ns \"glitch\" on the appBut
signal fired another event at this time \u2013 causing an extra appLed
blink.
\u00a0 You've pressed appBut
at this time, which then begins awakening the MCU from its low-power sleep.
\u00a0 Requiring 34.2\u2009\u03bcs to fully power the MCU, dbgB
's falling edge marks when Button1P
starts actively running.
\u00a0 Control enters handler
within 3.4\u2009\u03bcs, whose initial %%[c]
statement toggles dbgC
for 1.2\u2009\u03bcs
\u00a0 After some housekeeping, Button1P
finally turns-on appLed
\u2013 5.9\u2009\u03bcs since actively running.
This tour centers around the Button2P program, which uses a Fiber
object to better handle incoming events. This tour also analyzes the performance impact of this approach compared with the earlier Button1P program.
dbgB
shows that Button2P
awoke exactly twice in response to pressing appBut
\u2013 no glitches this time\u2009!!!
Button2P
resumes execution in 35.2\u2009\u03bcs; keep in mind that low-power MCU wakeup times can vary slightly.
\u00a0 Control then enters handler
3.4\u2009\u03bcs later \u2013 consistent with the Button1P
capture seen above.
\u00a0 Unlike Button1P
, the Button2P
handler
readies the blinkF
fiber which then gains control 5.3\u2009\u03bcs later.
\u00a0 Finally, the blinkFB
function turns-on appLed
\u2013 with only ~5\u2009\u03bcs of FiberMgr
scheduling overhead.
This tour centers around the Button3P program, which debounces button input signals in a portable fashion. This tour also visits the ButtonI interface along with a module implementing this interface \u2013 the latter generated by the ButtonT template at build-time.
Tour 06 \u2013 Logic Capture \u00a0 Once triggered by pressing your board's button, the EM runtime polls the state of appBut
every 100\u2009ms.
\u00a0 If pressed for \u2265\u2009100\u2009ms but \u2264\u20094\u2009s, Button3P
will blink appLed
within 100\u2009ms of releasing the button.
\u00a0 Hitting the 4\u2009s mark, Button3P
immediately blinks sysLed
\u2013 regardless of how long appBut
remains low.
\u00a0 After polling appBut
for 4\u2009s, the EM runtime invokes Button3P.onPressedCB
which leaves a %%[c]
mark.
\u00a0 Discovering that appBut
remains pressed [\u2009=\u2009L\u2009], onPressedCB
then blinks sysLed
for 40\u2009ms.
\u00a0 Only 7.5\u2009\u03bcs elapses from dbgB
falling to sysLed
rising, which includes scheduling fibers plus 1.2\u2009\u03bcs for %%[c]
.
This tour centers around the OneShot1P program, which handles timer events that awaken the MCU. This tour also visits the OneShotMilliI interface, which abstracts the functionality of a short-term timer with millisecond resolution.
Tour 07 \u2013 Logic Capture \u00a0 The dbgB
signal shows that OneShot1P
awakens from low-power sleep every 500\u2009ms.
\u00a0 Once awake, OneShot1P
toggles dbgC
and dbgD
before blinking appLed
\u2013 like our earlier Button1P
capture
\u00a0 The 7.5\u2009\u03bcs \"wakeup-to-blink\" latency seen here includes the overhead of servicing an on-chip MCU timer.
"},{"location":"using/using-02/#tour-08-timer-fibers","title":"Tour 08 \u2013 timer fibers","text":"This tour centers around the OneShot2P program, which now uses Fiber
objects to enhance robustness when handing timer events. This tour also invites performance comparision with the earlier OneShot1P program.
\u00a0 Fiber scheduling adds the extra 1.1\u2009\u03bcs of latency seen here, compared with our previous OneShot1P
capture.
This tour centers around the PollerP program, which introduces a portable function for pausing execution at run-time. This tour also visits the top-level Poller module as well as the lower-level PollerAux module \u2013 both implementing the PollerI interface.
Tour 09 \u2013 Logic Capture \u00a0 Without %%[c]
and %%[d]
marks, PollerP
further reduces latency compared to OneShot1P
and OneShot2P
.
\u00a0 The [L] dbgB
signal indicates that the PollerP
program has awoken.
PollerP
then calls AppLed.wink
within 2.5\u2009\u03bcs, which then asserts the appLed
pin
\u00a0 Calling AppLed.wink
2.5\u2009\u03bcs later asserts the appLed
pin and then internally invokes Poller.pause
.
Poller.pause
proceeds to suspend program execution for 5\u2009ms \u2013 the duration of the wink.
\u00a0 The EM runtime toggles dbgB
once before awaiting the next wakeup [H]\u2009; we'll explain more in a later tour.
\u00a0 An MCU timer event will eventually awaken the program 5\u2009ms later, with dbgB
now signalling [L]\u2009.
\u00a0 Control unwinds from Poller.pause
back to AppLed.wink
, which will now lower the appLed
pin.
em$run
finally regains control, and then suspends execution for 500\u2009ms by calling Poller.pause
directly.
This tour centers around the Alarm1P program, which uses Alarm
objects to schedule longer-term wakeups with (sub-)second resolution. This tour also visits the AlarmMgr module, which internally uses a proxy implementing the WakeupTimerI interface.
\u00a0 The Alarm1P
program alternately awakens from a low-power deep-sleep of 2\u2009s or else 750\u2009ms in duration.
\u00a0 The program's blinkFB
function winks appLed
for 100\u2009ms, with the MCU idling in the interim.
\u00a0 Once AppLed.wink
returns, Alarm1P
calls alarm.wakeup
to re-enter an extended period of deep-sleep.
\u00a0 The single dbgB
mark indicates the MCU has entered its \"lite-sleep\" mode, after Alarm1P
calls AppLed.wink
\u00a0 The MCU awakens from its lite-sleep within 100\u2009ms, returning to wink
which then turns-off appLed
.
\u00a0 The double dbgB
mark indicates the MCU has entered its \"deep-sleep\" mode, after calling alarm.wakeup
This tour centers around the Alarm2P program, which now aligns Alarm
wakeups with a given time-window. This tour also re-visits the AlarmMgr implementation, seeing how it schedules the next wakeup event as well as the role played by the EpochTime module.
\u00a0 Note how the wakeups from deep-sleep align with a series of 1.5\u2009s windows along the time-line.
\u00a0 Due to startup overhead, the first alarm.wakeup
call deep-sleeps the MCU for only 1.250\u2009s to stay aligned.
\u00a0 After a 5\u2009ms appLed
wink, this alarm.wakeup
call deep-sleeps the MCU for 1.495\u2009s to stay aligned.
\u00a0 But after a 100\u2009ms wink, this alarm.wakeup
call deep-sleeps the MCU for just 1.400\u2009s to stay aligned.
This tour centers around the TickerP program, which takes duty-cycled functions to a higher level. This tour also visits the TickerMgr module, whose implementation of Ticker
objects emulates as well as builds upon the AlarmMgr and FiberMgr studied earlier.
\u00a0 The appTicker.start
call by the TickerP
program initiates a train of 100\u2009ms appLed
winks, spaced 1\u2009s apart.
\u00a0 The sysTicker.start
call by TickerP
then initiates a train of 100\u2009ms sydLed
winks, but spaced 1.5\u2009s apart.
\u00a0 When appTicker
and sysTicker
wakeups coincide, their callbacks run on a first-come, first-served basis.
Welcome to the world of EM \u2013 a novel programming language and runtime environment targeting resource-constrained embedded systems. To increase your understanding of the language and its runtime, this site documents all aspects of the EM software platform.
If you haven't already done so, we strongly suggest starting with Introducing EM which offers a 10,000' overview of the EM language and runtime \u2013 even if you just can't wait to install the EM software and start hacking some code\u2009!!!\u00a0\u00a0 Besides rationalizing the need for EM, this overview introduces concepts as well as defines terminology used throughout the site.
Building upon this introductory overview of EM, we've then organized the remaining documents found at this site as follows:
Installing EM software and hardware required to build and run EM programs Using EM language immersion through a graduated series of portable EM examples Porting EM steps required for (re-)targeting EM to a specific HW/SW environment Advancing EM deep-dives into specific facets of the EM platform Click here to see more (less) detailsWe'll often insert additional commentary on the material at hand in this collapsible format, providing deeper insights into some aspect of EM. While (hopefully) insightful, feel free to skip these comments at any time.
So follow me, and let the journey begin\u2009....
"},{"location":"discuss/","title":"General comments and specific issues","text":"In my humble opinion\u2009...
Created for the benefit of the broader embedded software community, we value your general feedback \u2013 comments, questions, ideas \u2013 about The EM Programming Language as presented throughout these documents.
To add your thoughts about EM, use the discussion forum in our em-foundation/em-sdk GitHub repository.\u00a0 You can join here\u2009, if you don't already have a GitHub account.
Just keep in mind \u2013 nothing has been decided, and everything still matters.
If you've downloaded the EM SDK and have begun coding, you can report any specific issues with the software in the same em-foundation/em-sdk repository.
"},{"location":"edu/","title":"Academia \u2013 say \"hello world\" to EM","text":"The EM software platform comprises a novel programming language and runtime which targets resource-constrained MCUs. Originally developed in 2010, EM has evolved over the past decade through a series of commercial deployments in low-power, low-cost wireless IoT applications. To encourage broader adoption of this technology, The EM Foundation (a non-profit formed in 2023) now makes the language and its runtime openly\u2009/\u2009freely available.
While EM leverages modern software constructs like interface inheritance and component composition, novel optimization techniques employed by the underlying language translator enable EM programs to invariably outperform their hand-crafted C counterparts in terms of time and (especially) space.
Often targeting MCUs with \u2264\u200932\u2009K of memory, real-world applications \u2013 including a BLE wireless stack \u2013 can comfortably fit within these constraints.\u00a0 A 5X\u2009-\u200910X size reduction in typical embedded applications can also drive comparable savings in energy consumption as well as overall system cost.
EM \u2013 a higher-level programming language AND a higher-level of program performance
"},{"location":"edu/#conceived-in-the-university","title":"Conceived in the university","text":"Before addressing some opportunities for academic collaboration, you should make a quick pass through Introducing EM to understand a little more about the language and its run\u00adtime. The Tiny code \u2192 Tiny chips subsection already takes us to some fertile grounds for future investigation; EM's initial engagement with the RISC-V community should also spark interest within university environments.(1)
While you can certainly skip the technical overivew of the language and its runtime for now, we strongly encourage you to read The history of EM subsection \u2013 and do note the seminal role played by UC Santa Barbara in the birthing and early development of EM.\u2009 In retrospect, EM would not exist today without a nuturing university environment.
"},{"location":"edu/#managing-open-source-projects","title":"Managing open-source projects","text":"With an abundance of open-source software ranging from host tooling to target drivers (plus open-source hardware ranging from Arduino boards to RISC-V MCUs), CS/CE university programs should (in our humble opinion) arm their students with knowledge of open-source best practices \u2013 enabling them to not only participate in established projects, but also to create their own open-source initiatives which they could lead and manage.
Given that the transition of the EM technology into the open-source domain has only just begun, \"ground-floor\" opportunities await enterprising CS/CE departments motivated to assume a leadership role in the process. Said another way, The EM Foundation seeks help from universities to support its mission \u2013 promoting, sustaining, and evolving the EM programming language and runtime for use by the broader embedded systems community.
"},{"location":"edu/#areas-for-technical-contribution","title":"Areas for technical contribution","text":"From an academic perspective, the depth\u2009/\u2009duration of potential technical contributions to EM can range from (say) a single-semester undergraduate project to multi-year graduate-level research culminating in a thesis.\u2009 Some areas of mutual interest might include:
Overall management of EM repositories housed at GitHub \u2013 coordinate the transition of existing (private) sources to public repos; introduce processes defined by open-source maturity models.
Evolution of the (command-line) EM translator plus its companion VS Code extension \u2013 relatively stable, quite compact (~12\u2009K lines of TypeScript), but with many opportunities for adding new features.
Perpetual expansion of the EM language runtime \u2013 ports to different MCUs, device drivers for sensors\u2009/\u2009controllers, wired\u2009/\u2009wireless communication stacks, machine-learning algorithms, etc.
New MCU architectures tailored for EM \u2013 take on the Tiny code \u2192 Tiny chips challenge; leverage RISC-V IP targeting FPGAs for proof-of-concept; add application-specific CPU instructions, etc.
"},{"location":"edu/#call-to-action-contact-us","title":"Call to action \u2013 contact us","text":"If EM appears to align with the interests of your department, don't hesitate to message The EM Foundation on Linkedin with any questions you may have.
In the interim, continue to explore the material at docs.openem.org as well as read our weekly posts at blog.openem.org.
And if you really feel motivated, consider visiting Installing EM and actually downloading the EM SDK \u2013 though perhaps you'll leave this task as an \"exercise for the student\"\u2009.
"},{"location":"install/","title":"Getting started with EM","text":"Using the EM language and its runtime requires a cross-development environment, in which hosted tooling will build\u2009/\u2009load executable EM programs onto target hardware. For the host, you'll use a PC running Windows, Linux, or MacOS(1); for the target, you can choose any MCU board for which an em$distro
package already exists.
Before turning to the EM SDK (described next), you should first install\u2009/\u2009upgrade the following tooling environments on your host PC:
Node.js version 16.3.0 or later executenode
--version
to verify VS Code version 1.80.0 or later execute code
--version
to verify Windows If you don't already have a recent version of the Git Bash shell, you should also install Git for Windows.\u00a0 To verify your setup, ensure that the node
and code
commands from the previous table operate correctly under Git Bash.
Do not proceed forward if these verification checks should fail\u2009!!!
"},{"location":"install/#software-development-kit","title":"Software development kit","text":"The EM SDK comprises two distinct components:
\u2003a VS Code extension named EM Builder with which you'll interact directly; and
\u2003a special \u00abEM-SDK\u00bb
folder automatically managed by , which you can largely ignore.
Under the \u00abEM-SDK\u00bb
folder you'll find a sub-folder named tools
, which will contain C/C++ compilers as well as emulation utilities \u2013 all used internally by EM to build\u2009/\u2009load executable target programs.\u00a0 Already found in a well-known place on your PC(1), EM requires no further action on your part to setup these tools.
\u00abEM-SDK\u00bb
\u00a0=\u2002~/em-sdk
Looking inside your \u00abEM-SDK\u00bb
folder, you'll also find some package*.json
files as well as a node_modules
sub-folder \u2013 characteristic of an npm
package.\u00a0 Unless instructed otherwise, please leave these (and any other\u2009!!\u2009) artifacts housed in \u00abEM-SDK\u00bb
undisturbed.
Unless instructed otherwise, do NOT disturb the contents of your \u2009\u00abEM-SDK\u00bb\u2009 folder\u2009!!!
"},{"location":"install/#target-mcu-hardware","title":"Target MCU hardware","text":"The EM SDK will contain all of the tools required to build\u2009/\u2009load EM programs targeting any of the following boards; you won't need to download any additional, vendor-specific software.\u00a0 While we encourage you to purchase one (or more) of these boards, you can still learn a great deal about EM without target hardware \u2013 by taking the **FAST-TRACK**
option.
The Texas Instruments CC2340R5 wireless MCU features an Arm Cortex-M0+ CPU together with a familiar suite of peripherals \u2013 including a generic 2.4\u2009GHz radio with BLE 5.x support. Texas Instruments also offers an inexpensive LP-EM-CC2340R5 evaluation board in their familiar LaunchPad format \u2013 available from TI as well as their distributors.
You should also purchase this emulator board from TI \u2013 unless you already own a \"classic\" TI LaunchPad with on-board XDS110 support. In that case, you can easily connect this legacy LP to your new LP-EM-CC2340R5 board using a cable supplied by TI. If you haven't used an XDS110 before, run the one_time_setup
script found in \u00abEM-SDK\u00bb/tools/ti-uniflash
.
The ti.cc23xx
distro supports the following Setups for the EM-SDK tools:
ti.cc23xx/gcc
GCC 10.3, optimized for space ti.cc23xx/gcc_sram
GCC 10.3, optimized for space, code\u2009+\u2009consts in SRAM ti.cc23xx/segger
CLANG\u2009/\u2009LLVM 14.0, optimized for space ti.cc23xx/segger_sram
CLANG\u2009/\u2009LLVM 14.0, optimized for space, code\u2009+\u2009consts in SRAM ti.cc23xx/default
== ti.cc23xx/segger
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
Nothing to buy, of course\u2009!!!\u00a0 You can still build executable programs using any em$distro
package within the EM SDK; you just can't load these programs onto target hardware.
Familiarize yourself, however, with the Setups available for one (or more) of the other hardware tracks enumerated above.
As we pivot to installing EM Builder, we'll soon illustrate how to select one of the Setups associated with the distros supporting these hardware options.
Which MCU board(s) would you want to see EM support\u2009???
"},{"location":"install/#em-builder-extension","title":"EM Builder extension","text":"At this stage, we can launch VS Code and install its EM Builder extension. To begin, first create an empty folder anywhere on your PC (outside of \u00abEM-SDK\u00bb
); this folder will serve as your initial VS Code workspace.
Next, enter the command code
workspace
to launch VS Code (where workspace
represents a path to the folder created above).\u00a0 Unless you've already installed EM Builder, you'll see something like the default Welcome
layout shown within the following screen capture:
The installation process actually begins by importing a VS Code profile named EM, which will configure a variety of user-settings as well as download the latest (stable) version of the EM Builder extension and its dependencies. To illustrate the process, watch the Importing EM Profile animation above by clicking the button atop this screen capture.
As seen in the animation, you'll use a special URL generated by VS Code to fetch the EM profile from the cloud.\u00a0 When ready, copy this URL to your clipboard and follow the same steps [\u2009Profiles\u2009>\u2009Import Profiles...\u2009] within your own installation of VS Code \u2013 pasting this URL into the input box.
https://vscode.dev/profile/github/d42fc7e6ff05708f910babc909fce416\n
If necessary, you can rewind the animation by clicking the button whenever it appears atop the screen capture.\u00a0 Hint \u2013 once playing, you should click on the animation itself to enlarge the view.
With the EM profile in place, we'll now activate the EM Builder extension for the first time as illustrated in the following animation:
Activating EM Builder Activating EM BuilderThe [\u2009Command Palette\u2009>\u2009Reload Window\u2009] sequence effectively restarts VS Code which in turn will activate EM Builder.\u2009 Always checking the status of the \u00abEM-SDK\u00bb
folder, the extension will prompt you to install the EM SDK components whenever absent or outdated.(1)
The EM Builder extension also requires that you select one of the Setups supported by your target MCU hardware, as the following animation will illustrate:
Selecting EM Setups Selecting EM SetupsYou should select a \"default\" configuration when the EM Setup dropdown appears \u2013 even if you've chosen the **\u2009FAST-TRACK\u2009** option for your target MCU hardware. Note that this process implicitly binds a default target board associated with your selection.
If you do plan to connect a target board, you'll also need to assign an appropriate value to the ConsolePort
parameter found in the workspace local.properties
file as follows:
Finding the actual value for the ConsolePort
parameter depends, of course, on utilities specific to your PC's operating system as well as some help from your board's documentation.
In summary:
\u2003entercode
workspace
from the shell to launch VS Code \u2003execute the \u2009Profiles\u2009>\u2009Import Profiles...\u2009 command sequence \u2003paste the special URL given above into the input box \u2003select Create Profile and then Create when prompted \u2003execute the Developer: Reload Window command \u2003select Install... if prompted to install EM SDK components \u2003select Select... if prompted to bind EM Setups \u2003choose a \"default\" configuration from the EM Setup dropdown \u2003update the ConsolePort
parameter in local.properties
as appropriate"},{"location":"install/#ready-set-go","title":"Ready, Set, Go\u00a0!!!","text":"With everything in place, the time has come to start Using EM\u2009.Happy coding\u2009!!! \u2002
"},{"location":"advancing/","title":"Deep-dive into different facets of EM","text":"This document comprises a collection of independent articles focusing on different facets of the EM language and runtime. So pick a topic of interest and then dive right in\u2009!!!
Command-line interface Managing EM from within the shell CoreMark Reimagined Transforming legacy C code into EM EM\u2022Mark Results Standard benchmarks for EM Energy Consumption Optimizing power over time Language Syntax Navigating EM syntax diagramsWhat other aspects of EM should we tackle next\u2009???
"},{"location":"advancing/cli/","title":"Managing EM from within the shell","text":"Under Construction \u2014 estimated completion in 1Q24 \u2009
"},{"location":"advancing/coremark/","title":"Transforming legacy C code into EM","text":"CoreMark\u00ae has emerged as the premier industry benchmark for measuring CPU performance within embedded systems. Managed through EEMBC\u2009, virtually every MCU vendor has certified and published CoreMark scores for a broad portfolio of their processors. Running the benchmark code also serves as a \"typical workload\" used when characterizing active power consumption [\u2009\u03bcW\u2009/\u2009Mhz\u2009] of a particular MCU.
The workload introduced by CoreMark encompasses four algorithms reflecting the variety of software functions often implemented within embedded application programs:
list processing find and remove elements, generalized sorting matrix manipulation add and multiply by a scalar, vector, or matrix state machine scan a string for a variety of numeric formats cyclic redundancy check checksum over a sequence of 16\u2009/\u200932-bit valuesBesides adding to the workload, CoreMark uses algorithm to validate the final results of running the benchmark program \u2013 comparing a checksum over the list elements used in algorithm against an expected value. CoreMark also checksums the matrix data produced by algorithm as well as the state machine transitions encountered by algorithm .
You'll find the CoreMark sources on GitHub, together with instructions for building\u2009/\u2009running the benchmark program. To ensure the integrity of the benchmark, you cannot modify any of its (portable) C source files \u2013 with the exception of core_portme.[ch]
, used to adapt CoreMark to a particular hardware platform.
Needless to say, your choice of C compiler along with specific options for controlling program optimization remain on the table. While primarily intended for comparing different MCUs, CoreMark also provides a known codebase useful for \"apples-to-apples\" comparisons between different compilers [GCC, IAR, Keil, LLVM] targeting the same MCU.
CoreMark \u2013 a \"typical\" C program in more ways than one
We sense that very few software practitioners have actually studied the CoreMark source files themselves. As long as \"someone else\" can actually port\u2009/\u2009build\u2009/\u2009run the benchmark on the MCU of interest, good enough\u2009!!
In our humble opinion, the CoreMark sources would not serve as the best textbook example of well-crafted C code:\u00a0 insufficent separation of concerns, excessive coupling among compilation units, plus other deficiencies.
Said another way, CoreMark typifies the design\u2009/\u2009implementation of much of the legacy embedded C code we've encountered for decades within industry and academia alike.\u00a0 But therein lies an opportunity to showcase EM.
"},{"location":"advancing/coremark/#coremark-emmark","title":"CoreMark \u21d2 EM\u2022Mark","text":"In reality, none of the official CoreMark sources (written in C) will survive their transformation into EM\u2022Mark \u2013 a new codebase (re-)written entirely in EM.\u2009 At the same time, applying the same CoreMark algorithms to the same input data must yield the same results in EM.
The input data used by EM\u2022Mark (like CoreMark) ultimately derives from a handful of seed\u2009 variables, statically-initialized with prescribed values.\u2009 Declared volatile
in EM as well as C, the integrity of the benchmark requires that the underlying compiler cannot know the initial values of these seed variables and potentially perform overly-aggressive code optimizations.
At the same time, the CoreMark sources do make use of C preprocessor #define
directives to efficiently propogate constants and small (inline) functions during compilation.\u2009 EM\u2022Mark not only achieves the same effect automatically via whole-program optimization, but also leverages the full power of EM meta-programming to initialize internal data structures at build-time \u2013 resulting in a far-more compact program image at run-time.
If necessary, review the material on program configuration and compilation to fully appreciate the opportunities that EM affords for build-time optimization.
"},{"location":"advancing/coremark/#high-level-design","title":"High-level design","text":"The EM\u2022Mark sources (found in the em.coremark package within the em.bench
bundle) consist of ten EM modules and two EM interfaces, organized as follows:
The ActiveRunnerP and SleepyRunnerP programs on top of this hierarchy both execute the same core benchmark algorithms, albeit in two very different contexts:
ActiveRunnerP
performs multiple\u2009 benchmark iterations, much like the legacy CoreMark program
SleepyRunnerP
performs a single\u2009 benchmark iteration, awakening every second from deep-sleep
The CoreBench
module (imported by both of these programs) coordinates both configuration as well as execution of the list processing, matrix manipulation, and state machine algorithms; we'll have more to say about its implementation in a little while.
To capture behavioral commonality between CoreBench
and the algorithm modules it uses internally [\u2009ListBench
, MatrixBench
, StateBench
\u2009], our EM\u2022Mark design introduces the abstract em.coremark/BenchAlgI
interface:
package em.coremark\n\nimport Utils\n\ninterface BenchAlgI\n\n config memSize: uint16\n\n function dump()\n function kind(): Utils.Kind\n function print()\n function run(arg: uarg_t = 0): Utils.sum_t\n function setup()\n\nend\n
Of the handful of functions specified by this interface, two of these play a central role in the implementation of each benchmark algorithm:
BenchAlgI.setup
, which initializes the algorithm's input data using volatile
seed variables
BenchAlgI.run
, which executes one pass of the benchmark algorithm and returns a CRC value
Taking a quick peek inside CoreBench
, you'll notice how this module's implementation of the BenchI
interface simply delegates to the other algorithm modules \u2013 which in turn implement the same interface:
def em$construct()\n Utils.bindSeedH(1, 0x0)\n Utils.bindSeedH(2, 0x0)\n Utils.bindSeedH(3, 0x66)\nend\n\ndef dump()\n ListBench.dump()\n MatrixBench.dump()\n StateBench.dump()\nend\n\ndef kind()\n return Utils.Kind.FINAL\nend\n\ndef print()\n ListBench.print()\n MatrixBench.print()\n StateBench.print()\nend\n\ndef run(arg)\n auto crc = ListBench.run(1)\n Utils.setCrc(Utils.Kind.FINAL, Crc.add16(<int16>crc, Utils.getCrc(Utils.Kind.FINAL)))\n crc = ListBench.run(-1)\n Utils.setCrc(Utils.Kind.FINAL, Crc.add16(<int16>crc, Utils.getCrc(Utils.Kind.FINAL)))\n Utils.bindCrc(Utils.Kind.LIST, Utils.getCrc(Utils.Kind.FINAL))\n return Utils.getCrc(Utils.Kind.FINAL)\nend\n\ndef setup()\n ListBench.setup()\n MatrixBench.setup()\n StateBench.setup()\nend\n
CoreBench
also uses public get
\u2009/\u2009set
functions provided by the Utils module to fetch\u2009/\u2009store designated CRC and seed values.
more code ahead \u2013 free free to scroll down to the Summary
Each of the benchmark algorithms will call the Crc.add16
or Crc.addU32
functions to fold a new data value into a particular checksum. Looking at the implementation of the Crc module, both of these function definitions ultimately call Crc.update
\u2013 a private function that effectively mimics the crcu8
routine found in the legacy CoreMark source code:
ee_u16\ncrcu8(ee_u8 data, ee_u16 crc)\n{\n ee_u8 i = 0, x16 = 0, carry = 0;\n\n for (i = 0; i < 8; i++)\n {\n x16 = (ee_u8)((data & 1) ^ ((ee_u8)crc & 1));\n data >>= 1;\n\n if (x16 == 1)\n {\n crc ^= 0x4002;\n carry = 1;\n }\n else\n carry = 0;\n crc >>= 1;\n if (carry)\n crc |= 0x8000;\n else\n crc &= 0x7fff;\n }\n return crc;\n}\n
Finally, CoreBench
defines a pair of config
params [\u2009TOTAL_DATA_SIZE
, NUM_ALGS
\u2009] used to bind the BenchAlgI.memSize
parameter associated with the other algorithms; refer to CoreBench.em$configure
defined here for further details.\u2009 Initialized to values tracking the legacy CoreMark code, CoreBench
assigns \u230a2000/3\u230b\u2009\u2261\u2009666
bytes per algorithm.(1)
CoreBench.em$configure
after we explore the three benchmark algorithms in more detail.Pivoting to the simplest of the three benchmark algorithms administered by CoreBench
, the MatrixBench module implements each (public) function specified by the BenchAlgI
interface; and most of the MatrixBench
private functions defined inside the module [\u2009addVal
, mulVec
, clip
, etc\u2009] correspond to legacy C functions\u2009/\u2009macros found in core_matrix.c
\u2009.
Internally, MatrixBench
operates upon three matrices [\u2009matA
, matB
, matC
\u2009] dimensioned at build-time by the module's em$construct
function \u2013 which uses the BenchI.memSize
parameter (bound previously in CoreBench.em$configure
) when calculating a value for dimN
:
module MatrixBench: BenchAlgI\n\nprivate:\n\n type matdat_t: int16\n type matres_t: int32\n\n config dimN: uint8\n\n var matA: matdat_t[]\n var matB: matdat_t[]\n var matC: matres_t[]\n
em.coremark/MatrixBench.em [exc]def em$construct()\n auto i = 0\n auto j = 0\n while j < memSize\n i += 1\n j = i * i * 2 * 4\n end\n dimN = i - 1\n matA.length = matB.length = matC.length = dimN * dimN\nend\n
The MatrixBench.setup
function initializes \"input\" matrices [\u2009matA
, matB
\u2009] at run-time, using values derived from two of the volatile
seed variables prescribed by legacy CoreMark:
def setup()\n auto s32 = <uint32>Utils.getSeed(1) | (<uint32>Utils.getSeed(2) << 16)\n auto sd = <matdat_t>s32\n sd = 1 if sd == 0\n auto order = <matdat_t>1\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n sd = <int16>((order * sd) % 65536)\n auto val = <matdat_t>(sd + order)\n val = clip(val, false)\n matB[i * dimN + j] = val\n val += order\n val = clip(val, true)\n matA[i * dimN + j] = val\n order += 1\n end\n end\nend\n
MatrixBench.run
finally executes the benchmark algorithm itself \u2013 calling a sequence of private matrix manipulation functions and then returning a checksum that captures intermediate results of these operations:
def run(arg)\n auto crc = <Crc.sum_t>0\n auto val = <matdat_t>arg\n auto clipval = enlarge(val)\n #\n addVal(val)\n mulVal(val)\n crc = Crc.add16(sumDat(clipval), crc)\n #\n mulVec()\n crc = Crc.add16(sumDat(clipval), crc)\n #\n mulMat()\n crc = Crc.add16(sumDat(clipval), crc)\n #\n mulMatBix()\n crc = Crc.add16(sumDat(clipval), crc)\n #\n addVal(-val)\n return Crc.add16(<int16>crc, Utils.getCrc(Utils.Kind.FINAL))\nend\n
Once again, the [EM] implementations of private functions like addVal
and mulMat
track their [C] counterparts found in the CoreMark core_matrix.c
source file.
The StateBench module \u2013 which also conforms to the BenchAlgI
interface \u2013 scans an internal array [\u2009memBuf
\u2009] for text matching a variety of numeric formats.\u2009 Similar to what we've seen in MatrixBench
, the build-time em$construct
function sizes memBuf
as well as initializes some private config
parameters used as run-time constants:
config intPat: string[4] = [\n \"5012\", \"1234\", \"-874\", \"+122\"\n ]\n config fltPat: string[4] = [\n \"35.54400\", \".1234500\", \"-110.700\", \"+0.64400\"\n ]\n config sciPat: string[4] = [\n \"5.500e+3\", \"-.123e-2\", \"-87e+832\", \"+0.6e-12\"\n ]\n config errPat: string[4] = [\n \"T0.3e-1F\", \"-T.T++Tq\", \"1T3.4e4z\", \"34.0e-T^\"\n ]\n\n config intPatLen: uint16\n config fltPatLen: uint16\n config sciPatLen: uint16\n config errPatLen: uint16\n\n var memBuf: char[]\n
em.coremark/StateBench.em [exc]def em$construct()\n memBuf.length = memSize\n intPatLen = intPat[0].length\n fltPatLen = fltPat[0].length\n sciPatLen = sciPat[0].length\n errPatLen = errPat[0].length\nend\n
The StateBench.setup
function uses the xxxPat
and xxxPatLen
config
parameters in combination with a local seed
variable to initializing the memBuf
characters at run-time:
def setup()\n auto seed = Utils.getSeed(1)\n auto p = &memBuf[0]\n auto total = 0\n auto pat = \"\"\n auto plen = 0\n while (total + plen + 1) < (memSize - 1)\n if plen\n for auto i = 0; i < plen; i++\n *p++ = pat[i]\n end\n *p++ = ','\n total += plen + 1\n end\n switch ++seed & 0x7\n case 0\n case 1\n case 2\n pat = intPat[(seed >> 3) & 0x3]\n plen = intPatLen\n break\n case 3\n case 4\n pat = fltPat[(seed >> 3) & 0x3]\n plen = fltPatLen\n break\n case 5\n case 6\n pat = sciPat[(seed >> 3) & 0x3]\n plen = sciPatLen\n break\n case 7\n pat = errPat[(seed >> 3) & 0x3]\n plen = errPatLen\n break\n end\n end\nend\n
Details aside, StateBench.run
calls a private scan
function which in turn drives the algorithm's state machine; run
also calls a private scramble
function to \"corrupt\" memBuf
contents ahead of the next scanning cycle:
def run(arg)\n arg = 0x22 if arg < 0x22\n var finalCnt: uint32[NUM_STATES]\n var transCnt: uint32[NUM_STATES]\n for auto i = 0; i < NUM_STATES; i++\n finalCnt[i] = transCnt[i] = 0\n end\n scan(finalCnt, transCnt)\n scramble(Utils.getSeed(1), arg)\n scan(finalCnt, transCnt)\n scramble(Utils.getSeed(2), arg)\n auto crc = Utils.getCrc(Utils.Kind.FINAL)\n for auto i = 0; i < NUM_STATES; i++\n crc = Crc.addU32(finalCnt[i], crc)\n crc = Crc.addU32(transCnt[i], crc)\n end\n return crc\nend\n\ndef scan(finalCnt, transCnt)\n for auto str = &memBuf[0]; *str;\n auto state = nextState(&str, transCnt)\n finalCnt[ord(state)] += 1\n end\nend\n\ndef scramble(seed, step)\n for auto str = &memBuf[0]; str < &memBuf[memSize]; str += <uint16>step\n *str ^= <uint8>seed if *str != ','\n end\nend\n
The crc
returned by StateBench.run
effectively summarizes the number of transitory and finals states encountered when scanning.
even more\u2009 code ahead \u2013 free free to scroll down to the Summary
"},{"location":"advancing/coremark/#list-processing","title":"List processing","text":"Unlike its peer benchmark algorithms, the ListBench module introduces some new design elements into the EM\u2022Mark hierarchy depicted earlier:
the ComparatorI
abstraction, used by ListBench
to generalize its internal implementation of list sorting through a function-valued parameter that compares element values
the ValComparator
module, an implementation of ComparatorI
which invokes the other\u2009 benchmark algorithms (through a proxy
) in a data-dependent fashion
The ComparatorI
interface names just a single function [\u2009compare
\u2009]\u2009; the ListBench
module in turn specifies the signature of this function through a public type [\u2009Comparator
\u2009]\u2009:\u2009(1)
@FunctionalInterface
annotation or a C# delegate
object package em.coremark\n\nimport ListBench\n\ninterface ComparatorI\n\n function compare: ListBench.Comparator\n\nend\n
em.coremark/ListBench.em [exc]module ListBench: BenchAlgI\n\n type Data: struct\n val: int16\n idx: int16\n end\n\n type Comparator: function(a: Data&, b: Data&): int32\n\n config idxCompare: Comparator\n config valCompare: Comparator\n
CoreBench.em$configure
(which we'll examine shortly) performs build-time binding of conformant Comparator
functions to the pair of ListBench
config
parameters declared above. But first, let's look at some private declarations within the ListBench
module:
private:\n\n type Elem: struct\n next: Elem&\n data: Data&\n end\n\n function find(list: Elem&, data: Data&): Elem&\n function pr(list: Elem&, name: string)\n function remove(item: Elem&): Elem&\n function reverse(list: Elem&): Elem&\n function sort(list: Elem&, cmp: Comparator): Elem&\n function unremove(removed: Elem&, modified: Elem&)\n\n config maxElems: uint16\n\n var curHead: Elem&\n\nend\n
The Elem
struct
supports the conventional representation of a singly-linked list, with the ListBench
private functions manipulating references to objects of this type. The maxElems
parameter effectively sizes the pool of Elem
objects, while the curHead
variable references a particular Elem
object that presently anchors the list.
Similar to the other BenchAlgI
modules we've seen, ListBench
cannot fully initialize its internal data structures until setup
fetches a volatile
seed at run-time. Nevertheless, we still can perform a sizeable amount of build-time initialization within em$construct
:
def em$construct()\n auto itemSize = 16 + sizeof<Data>\n maxElems = Math.round(memSize / itemSize) - 3\n curHead = new<Elem>\n curHead.data = new<Data>\n auto p = curHead\n for auto i = 0; i < maxElems - 1; i++\n auto q = p.next = new<Elem>\n q.data = new<Data>\n p = q\n end\n p.data = new<Data>\n p.next = null\nend\n
Like all EM config
params, maxElems
behaves like a var
at build-time but like a const
at run-time; and the value assigned by em$construct
will itself depend on other build-time parameters and variables [\u2009itemSize
, memSize
\u2009].\u2009 In theory, initialization of maxElem
could have occurred at run-time \u2013 and with EM code that looks virtually identical to what we see here.
But by executing this EM code at build-time\u2009, we'll enjoy higher-levels of performance at run-time\u2009.
Taking this facet of EM one step further,(1)em$construct
\"wires up\" a singly-linked chain of newly allocated\u2009/\u2009initialized Elem
objects anchored by the curHead
variable \u2013 a programming idiom you've learned in Data Structures 101\u2009.\u2009 Notice how each Elem.data
field similarly references a newly-allocated (but uninitialized\u2009) Data
object.
Turning now to ListBench.setup
, the pseudo-random values assigned to each element's e.data.val
and e.data.idx
fields originate with one of the volatile
seed variables prescribed by CoreMark.\u2009 Before returning, the private sort
function (which we'll visit shortly) re-orders the list elements by comparing their e.data.idx
fields:
def setup()\n auto seed = Utils.getSeed(1)\n auto ki = 1\n auto kd = maxElems - 3\n auto e = curHead\n e.data.idx = 0\n e.data.val = 0x8080\n for e = e.next; e.next; e = e.next\n auto pat = <uint16>(seed ^ kd) & 0xf\n auto dat = (pat << 3) | (kd & 0x7)\n e.data.val = <int16>((dat << 8) | dat)\n kd -= 1\n if ki < (maxElems / 5)\n e.data.idx = ki++\n else\n pat = <uint16>(seed ^ ki++)\n e.data.idx = <int16>(0x3fff & (((ki & 0x7) << 8) | pat))\n end\n end\n e.data.idx = 0x7fff\n e.data.val = 0xffff\n curHead = sort(curHead, idxCompare)\nend\n
Finally, the following implementation of ListBench.run
calls many private functions [\u2009find
, remove
, reverse
, \u2026\u2009] to continually rearrange the list elements; ListBench.run
also uses another volatile
seed as well as calls sort
with two different Comparator
functions:
def run(arg)\n auto list = curHead\n auto finderIdx = <int16>arg\n auto findCnt = Utils.getSeed(3)\n auto found = <uint16>0\n auto missed = <uint16>0\n auto retval = <Crc.sum_t>0\n var data: Data\n data.idx = finderIdx\n for auto i = 0; i < findCnt; i++\n data.val = <int16>(i & 0xff)\n auto elem = find(list, data)\n list = reverse(list)\n if elem == null\n missed += 1\n retval += <uint16>(list.next.data.val >> 8) & 0x1\n else\n found += 1\n if <uint16>elem.data.val & 0x1\n retval += (<uint16>(elem.data.val >> 9)) & 0x1\n end\n if elem.next != null\n auto tmp = elem.next\n elem.next = tmp.next\n tmp.next = list.next\n list.next = tmp\n end\n end\n data.idx += 1 if data.idx >= 0\n end\n retval += found * 4 - missed\n list = sort(list, valCompare) if finderIdx > 0\n auto remover = remove(list.next)\n auto finder = find(list, &data)\n finder = list.next if !finder\n while finder\n retval = Crc.add16(list.data.val, retval)\n finder = finder.next\n end\n unremove(remover, list.next)\n list = sort(list, idxCompare)\n for auto e = list.next; e; e = e.next\n retval = Crc.add16(list.data.val, retval)\n end\n return retval\nend\n
Refer to ListBench for the definitions of the internal functions called by ListBench.run
\u2009.
As already illustrated, the ListBench.sort
accepts a cmp
argument of type Comparator
\u2013 invoked when merging Data
objects from a pair of sorted sub-lists: (1)
core_list_mergesort
function found in the legacy core_list_join.c
source file.def sort(list, cmp)\n auto insize = <int32>1\n var q: Elem&\n var e: Elem&\n for ;;\n auto p = list\n auto tail = list = null\n auto nmerges = <int32>0 # count number of merges we do in this pass\n while p\n nmerges++ # there exists a merge to be done\n # step `insize' places along from p\n q = p\n auto psize = 0\n for auto i = 0; i < insize; i++\n psize++\n q = q.next\n break if !q\n end\n # if q hasn't fallen off end, we have two lists to merge\n auto qsize = insize\n # now we have two lists; merge them\n while psize > 0 || (qsize > 0 && q)\n # decide whether next element of merge comes from p or q\n if psize == 0\n # p is empty; e must come from q\n e = q\n q = q.next\n qsize--\n elif qsize == 0 || !q\n # q is empty; e must come from p.\n e = p\n p = p.next\n psize--\n elif cmp(p.data, q.data) <= 0\n # First element of p is lower (or same); e must come from p.\n e = p\n p = p.next\n psize--\n else\n # First element of q is lower; e must come from q.\n e = q\n q = q.next\n qsize--\n end\n # add the next element to the merged list\n if tail\n tail.next = e\n else\n list = e\n end\n tail = e\n end\n # now p has stepped `insize' places along, and q has too\n p = q\n end\n tail.next = null\n # If we have done only one merge, we're finished\n break if nmerges <= 1 # allow for nmerges==0, the empty list case\n # Otherwise repeat, merging lists twice the size\n insize *= 2\n end\n return list\nend\n
Looking first at the IdxComparator
module, you couldn't imagine a simpler implementation of its ComparatorI.compare
function \u2013 which returns the signed difference of the idx
fields after scrambling the val
fields:
module IdxComparator: ComparatorI\n\nend\n\ndef compare(a, b)\n a.val = <int16>((<uint16>a.val & 0xff00) | (0x00ff & <uint16>(a.val >> 8)))\n b.val = <int16>((<uint16>b.val & 0xff00) | (0x00ff & <uint16>(b.val >> 8)))\n return a.idx - b.idx\nend\n
Turning now to the ValComparator
module, you couldn't imagine a more convoluted\u2009 implementation of ComparatorI.compare
\u2013 which returns the signed difference of values computed by the private calc
function:\u2009(1)
calc_func
found in the legacy core_list_join.c
source filemodule ValComparator: ComparatorI\n\n proxy Bench0: BenchAlgI\n proxy Bench1: BenchAlgI\n\nprivate:\n\n function calc(pval: int16*): int16\n\nend\n\ndef calc(pval)\n auto val = <uint16>*pval\n auto optype = <uint8>(val >> 7) & 1\n return <int16>(val & 0x007f) if optype\n auto flag = val & 0x7\n auto vtype = (val >> 3) & 0xf\n vtype |= vtype << 4\n var ret: uint16\n switch flag\n case 0\n ret = Bench0.run(<uarg_t>vtype)\n Utils.bindCrc(Bench0.kind(), ret)\n break\n case 1\n ret = Bench1.run(<uarg_t>vtype)\n Utils.bindCrc(Bench1.kind(), ret)\n break\n default\n ret = val\n break\n end\n auto newcrc = Crc.add16(<int16>ret, Utils.getCrc(Utils.Kind.FINAL))\n Utils.setCrc(Utils.Kind.FINAL, Crc.add16(<int16>ret, Utils.getCrc(Utils.Kind.FINAL)))\n ret &= 0x007f\n *pval = <int16>((val & 0xff00) | 0x0080 | ret) ## cache the result\n return <int16>ret\nend\n\ndef compare(a, b)\n auto val1 = calc(&a.val)\n auto val2 = calc(&b.val)\n return val1 - val2\nend\n
Besides scrambling the contents of a val
field reference passed as its argument, calc
actually runs other benchmark algorithms via a pair of BenchAlgI
proxies [\u2009Bench0
, Bench1
\u2009]\u2009.
Having visited most of the individual modules found in the EM\u2022Mark design hierarchy, let's return to CoreBench
and review its build-time configuration functions:
module CoreBench: BenchAlgI\n\n config TOTAL_DATA_SIZE: uint16 = 2000\n config NUM_ALGS: uint8 = 3\n\nend\n\ndef em$configure()\n memSize = Math.floor(TOTAL_DATA_SIZE / NUM_ALGS)\n ListBench.idxCompare ?= IdxComparator.compare\n ListBench.valCompare ?= ValComparator.compare\n ListBench.memSize ?= memSize\n MatrixBench.memSize ?= memSize\n StateBench.memSize ?= memSize\n ValComparator.Bench0 ?= StateBench\n ValComparator.Bench1 ?= MatrixBench\nend\n\ndef em$construct()\n Utils.bindSeedH(1, 0x0)\n Utils.bindSeedH(2, 0x0)\n Utils.bindSeedH(3, 0x66)\nend\n
In addition to calculating and assigning the memSize
config
parameter for each of the benchmarks, CoreBench.em$configure
binds a pair of Comparator
functions to ListBench
as well as binds the StateBench
and MatrixBench
modules to the ValComparator
proxies.
CoreBench.em$construct
completes build-time configuration by binding a prescribed set of values to the volatile
seed variables accessed at run-time by the individual benchmarks.
Whether you've arrived here by studying (or skipping\u2009!!) all of that EM code, let's summarize some key takeaways from the exercise of transforming CoreMark into EM\u2022Mark\u2009:
The CoreMark source code \u2013 written in C with \"plenty of room for improvement\" \u2013 typifies much of the legacy software targeting resource-constrained MCUs.
The high-level design of EM\u2022Mark (depicted here) showcases many aspects of the EM langage \u2013 separation of concerns, client-supplier decoupling, build-time configuration, etc.
The ActiveRunnerP and SleepyRunnerP programs can run on any\u2009 MCU for which an em$distro
package exists \u2013 making EM\u2022Mark ideal for benchmarking MCU performance.
Besides embodying a higher-level of programming, EM\u2022Mark also outperforms\u2009 legacy CoreMark.
To prove our claim about programming in EM, let's move on to the EM\u2022Mark results and allow the numbers to speak for themselves.
"},{"location":"advancing/emmark/","title":"Standard benchmarks for EM","text":"Armed with an understanding of the CoreMark \u21d2 EM\u2022Mark transformation, you'll further appreciate the significance of the EM benchmarks presented below. While CoreMark focuses heavily on measuring CPU performance, EM\u2022Mark takes a more balanced approach that also considers program image size, active power consumption, and overall energy efficiency.
With an emphasis on maximizing execution time, CoreMark programs overwhelmingly employ the most aggressive \"optimize-for-speed\" options when compiling the benchmark sources for a particular MCU \u2013 resulting in excessively large program images.
By way of contrast, EM\u2022Mark starts from the premise that minimizing program size when targeting resource-constrained MCUs trumps other performance factors.\u00a0 Said another way, most embedded firmware developers in practice will usually choose the most aggressive \"optimize-for-size\" option when compiling their code.
LP-EM-CC2340R5Board #2Board #3Texas Instruments reports a score of 2.1 CoreMarks\u2009/\u2009MHz for their CC2340R5 wireless MCU, which features a 48\u2009MHz Cortex-M0+ CPU.\u00a0 TI used the IAR C/C++ compiler [\u2009v9.32.2\u2009] to generate an ~18\u2009K program image, optimized for high-speed with no size constraints.
The legacy CoreMark results we'll report below used TI's LLVM-based Arm compiler [\u2009v2.1.3\u2009] with its \"optimize-for-size\" [\u2009-Oz
\u2009] option. To satisfy our curiousity, building the same Core\u00adMark program using the compiler's \"optimize-for-speed\" [\u2009-Os
\u2009] option yielded comparable results to those reported with IAR.\u2009(1)
All of the EM\u2022Mark results reported below use the ti.cc23xx distro bundle delivered with the EM-SDK plus the following pair of Setups when building program images:\u2009(1)
ti.cc23xx/segger
CLANG\u2009/\u2009LLVM 14.0, optimized for space, code\u2009+\u2009consts in Flash ti.cc23xx/segger_sram
CLANG\u2009/\u2009LLVM 14.0, optimized for space, code\u2009+\u2009consts in SRAM TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
"},{"location":"advancing/emmark/#program-size","title":"Program size","text":"Much like the legacy CoreMark C code, the ActiveRunnerP
EM program performs multiple iterations of the benchmark algorithms and displays results when finished:
package em.coremark\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.mcu import Common\n\nimport CoreBench\nimport Utils\n\nmodule ActiveRunnerP\n\n config ITERATIONS: uint16 = 10\n\nend\n\ndef em$startup()\n CoreBench.setup()\nend\n\ndef em$run()\n AppLed.on()\n Common.BusyWait.wait(250000)\n AppLed.off()\n Common.UsCounter.start()\n %%[d+]\n for auto i = 0; i < ITERATIONS; i++\n CoreBench.run()\n end\n %%[d-]\n auto usecs = Common.UsCounter.stop()\n AppLed.on()\n Common.BusyWait.wait(250000)\n AppLed.off()\n printf \"usecs = %d\\n\", usecs\n printf \"list crc = %04x\\n\", Utils.getCrc(Utils.Kind.LIST)\n printf \"matrix crc = %04x\\n\", Utils.getCrc(Utils.Kind.MATRIX)\n printf \"state crc = %04x\\n\", Utils.getCrc(Utils.Kind.STATE)\n printf \"final crc = %04x\\n\", Utils.getCrc(Utils.Kind.FINAL)\nend\n
Recalling the central role played by the CoreBench
module in the EM\u2022Mark high-level design, the implementation of its setup
and run
functions dominate the code\u2009/\u2009data sizes of the ActiveRunnerP
program image:
EM\u2022Mark Image Size
By way of comparison, the legacy CoreMark program built with TI's compiler weighs in with the following image size:
text (8798)
const (3777)
data (286)
bss (2372)
CoreMark Image Size
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
"},{"location":"advancing/emmark/#execution-time","title":"Execution time","text":"We've used a Saleae Logic Analyzer to capture logic traces of the legacy CoreMark and ActiveRunnerP
programs executing ten iterations of the benchmark. To help measure execution time, both programs blink appLed
for 250\u2009ms before\u2009/\u2009after the main benchmark loop:
Legacy Logic Capture \u2013 Flash
ActiveRunnerP Logic Capture \u2013 Flash
ActiveRunnerP Logic Capture \u2013 SRAM
CoreMark text\u2009+\u2009const [\u2009Flash\u2009] 176\u2009ms EM\u2022Mark text\u2009+\u2009const [\u2009Flash\u2009] 151\u2009ms EM\u2022Mark text\u2009+\u2009const [\u2009SRAM\u2009] 124\u2009msExecution Times \u2013 Summary
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
"},{"location":"advancing/emmark/#active-power","title":"Active power","text":"We've used a Joulescope JS220 to capture power profiles of legacy CoreMark and ActiveRunnerP
executing ten iterations of the benchmark. To help measure power consumption, both programs blink appLed
for 250\u2009ms before\u2009/\u2009after the main benchmark loop:
Legacy Power Capture \u2013 Flash
ActiveRunnerP Power Capture \u2013 Flash
ActiveRunnerP Power Capture \u2013 SRAM
CoreMark text\u2009+\u2009const [\u2009Flash\u2009] 1.368\u2009mJ EM\u2022Mark text\u2009+\u2009const [\u2009Flash\u2009] 1.034\u2009mJ EM\u2022Mark text\u2009+\u2009const [\u2009SRAM\u2009] 0.634\u2009mJExecution Times \u2013 Summary
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
"},{"location":"advancing/emmark/#energy-efficiency","title":"Energy efficiency","text":"Applications targeting resource-constrained (ultra-low-power) MCUs often spend most of their time in deep-sleep \u2013 awakening at rates from once-per-second down to once-per-day, and actively executing for time windows measured in just milliseconds.
The CPU-centric nature of legacy CoreMark (and hence ActiveRunnerP
) doesn't necessarily reflect \"real-world\" duty-cycled applications targeting ULP MCUs, where maximizing energy efficiency becomes paramount.
In addition to legacy CoreMark, the EEMBC organization offers ULPMark \u2013 a benchmark suite that quantifies many aspects of ultra-low-power MCUs. One of the profiles in the suite in fact measures active power consumption, using CoreMark as the workload; other profiles quantify the true energy cost of deep-sleep.
Unlike CoreMark, however, ULPMark requires a paid license to access its source code \u2014 an obstacle for purely inquisitive engineers working with ULP MCUs.\u2009 EM\u2022Mark attempts to fill this niche with a complementary pair of portable programs for benchmarking code size and execution time, as well as power consumption.
To that end, EM\u2022Mark also incorporates the SleepyRunnerP
program \u2013 which executes the same underlying benchmark algorithms as ActiveRunnerP
, but in a very different setting:
package em.coremark\n\nfrom em$distro import BoardC\n\nfrom em.utils import FiberMgr\nfrom em.utils import TickerMgr\n\nimport CoreBench\n\nmodule SleepyRunnerP\n\nprivate:\n\n var ticker: TickerMgr.Ticker&\n\n var count: uint8 = 10\n\n function tickCb: TickerMgr.TickCallback\n\nend\n\ndef em$construct()\n ticker = TickerMgr.createH()\nend\n\ndef em$startup()\n CoreBench.setup()\nend\n\ndef em$run()\n ticker.start(256, tickCb)\n FiberMgr.run()\nend\n\ndef tickCb()\n %%[d+]\n auto crc = CoreBench.run()\n %%[d-]\n printf \"crc = %04x\\n\", crc\n return if --count\n ticker.stop()\n halt\nend\n
Here too, SleepyRunnerP
calls CoreBench.setup
at startup; but instead of the \"main loop\" seen earlier in ActiveRunnerP
, we now make a single\u2009 call to CoreBench.run
just once-per-second. Relying only on modules found in the em.core
bundle [\u2009FiberMgr
, TickerMgr
\u2009], SleepyRunnerP
can execute on any target MCU for which an em$distro
package exists.\u2009(1)
The following sets of Saleae logic-captures first show SleepyRunner
awakening once-per-second, and then zoom-in to view the execution time of a single active cycle:
SleepRunnerP Logic Capture \u2013 SRAM
SleepRunnerP Logic Capture \u2013 SRAM\u2002[\u2009zoom\u2009]
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
Note that measurement M0
reflects the total active time as framed by dbgB
(managed automatically by EM), whereas measurement M1
reflects the actual benchmark interval between the dbgD
toggles seen earlier in SleepyRunnerP
.\u2009 Also note that the latter measurement does not\u2009 include the time to format\u2009/\u2009output the \"crc = 72be\\n\"
character string.
The following sets of Joulescope power-captures report the total amount of energy consumed over a one-second interval, as well as the amount of energy consumed when the SleepyRunnerP
program awakens and executes a single iteration of the benchmark:
SleepRunnerP Power Capture \u2013 SRAM
SleepRunnerP Power Capture \u2013 SRAM\u2002[\u2009zoom\u2009]
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
As expected, a single call to the CoreBench.run
function takes only milliseconds to execute and yet consumes most of the energy over the one-second cycle.
Improvements in EM\u2022Mark program size and execution time compared with legacy CoreMark hopefully supports EM's claim of higher-level programming and higher-levels of performance\u2009.
Building EM\u2022Mark with the most aggressive \"optimize-for-size\" options passed to the underlying C/C++ compiler reflects the reality of targeting resource-constrained MCUs.
Placing runtime code and constants into SRAM (versus Flash) not only improves execution time but also reduces active power consumption \u2014 a corrolary of optimizing for program size.
While ActiveRunnerP
maintains the same focus as legacy CoreMark (inviting side-by-side comparison), the SleepyRunnerP
benchmark more accurately quantifies the energy efficiency of ULP MCUs.
We put forth a rather bold proposition in Using EM:\u2003 If you can't see the problem, you can't fix it\u2009!!!\u00a0 As we toured the EM runtime, logic captures for each example program gave you an important perspective \u2013 that of program state over the course of time. Informally termed \"real-time debug\", this kind of logic trace proves invaluable to both quantify program execution time at sub-\u03bcs resolution, as well as to verify proper sequencing of program execution.
This article expands this proposition into the domain of energy consumption \u2013 by now tracing MCU power over the course of time. With many resource-constrained MCUs targeting always-on applications that run using batteries and\u2009/\u2009or harvested energy, power profiles captured by a precision energy analyzer nicely complement the \"real-time debug\" traces captured by a logic-state analyzer.
Until recently, the cost of a high-quality energy analyzer would often exceed $10,000 \u2013 far beyond most of our budgets. The recent arrival of the Joulescope JS220 precision analyzer does, however, afford order-of-magnitude relief on pricing. And the STM32 Power Shield offers an even more affordable option at <\u2009$100.
Help wanted \u2013 someone familiar with the STM32 Power Shield
For now, though, we'll stick with the Joulescope JS220 as our energy analyzer. And even if you don't own a JS220, we recommend downloading the (free) Joulescope UI software as well as the original power capture files presented throughout this article.
"},{"location":"advancing/energy/#mcu-power-modes","title":"Mcu power modes","text":"The Tour 10 \u2013 Logic Capture seen here in Using EM introduced the terms \"lite-sleep\" and \"deep-sleep\" corresponding to distinct marks on dbgB
. Recalling the Alarm1P example, this program enters the MCU's deep-sleep mode %%[b:2]
after calling alarm.wakeup
. But after calling AppLed.wink
\u2013 which internally pauses execution for a much shorter period of time \u2013 the program instead enters the MCU's lite-sleep mode %%[b:1]
.
While each vendor often has their own jargon for these power modes (IDLE
, PAUSE
, SLEEP
, STOP
, SUSPEND
, \u2026\u2009), we'll uniformally use the following terminology when measuring power across different MCUs supported by EM:
ACTIVE
CPU core running \u2013 MCU peripherals powered on when needed by CPU PAUSE
CPU core idling \u2013 MCU peripherals powered on when needed for CPU wakeup SLEEP
CPU + most peripherals powered off \u2013 wakeup via special \"always-on\" peripherals HIBERNATE
entire MCU powered off \u2013 CPU \"reset\" interrupt triggered by external HW devices only As application software transitions amongst these modes, the power required to operate the MCU can range from milliwatts [ACTIVE
] to microwatts [SLEEP
] \u2013 and even down to nanowatts [HIBERNATE
] if the application wishes to suspend execution indefintely.
To quantify these MCU power modes on your target board, we'll use the Button3P program highlighted in Tour\u200906 to obtain these readings. The Button3P Power Capture image below marks four distinct points during program execution, where we'll use the JS220 to measure the amperage instanteneously drawn by the MCU.
\u00a0 We've pressed appBut
for almost 4\u2009s, with the program testing appBut
every 100\u2009ms. [PAUSE
]
\u00a0 Crossing the 4\u2009s threshold, the program now blinks sysLed
for 40\u2009ms using a busy-wait loop. [ACTIVE
]
\u00a0 The program sleeps until the next button event, even though appBut
itself remains pressed. [SLEEP
]
\u00a0 With the button now released, the program remains asleep while drawing even less current. [SLEEP
]
Button3P Power Capture
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
Because the JS200 samples at a relatively slow 1\u2009MHz rate (compared with the CPU clock), the capture around marker shows a series of ripples spaced 100\u2009ms apart which in fact correspond to very brief CPU wakeups from PAUSE
to sample appBut
; a similar train of blips occurs around markers and , which here represent an internal duty-cycled recharge of the MCU's DC/DC converter or LDO regulator.
By in large, these current measurements align with specifications found in the MCU vendor's datasheet. Do note, however, that the ACTIVE
reading recorded at marker includes current drawn by the board's LED as well as the CPU itself.
While important, MCU power specifications such as [SLEEP
=
1.5
\u03bcA
] or even more generalized forms such as [ACTIVE
=
53
\u03bcA
/
MHz
] say absolutely nothing about the overall energy efficiency of an ultra-low-power embedded system built around this particular MCU. To gain this perspective, we must simultaneously consider the software running on the MCU and answer critical questions such as:
Once awakened, how quickly can our application software go back to sleep\u2009?!?!
By knowing the amount of time a program spends in the various MCU power modes, we can begin to quantify the overall energy efficiency of an embedded system. To illustrate the methodology we'll apply to measure energy consumption, consider the following JS220 capture of the Alarm1P example, which complements the logic capture found here:
LP-EM-CC2340R5Board #2Board #3Alarm1P Power Capture \u2013 Flash
EM Setup:\u00a0ti.cc23xx/segger_default
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
Alarm1P
spends the majority of its time in SLEEP
, typical of many embedded applications.
\u00a0 Once ACTIVE
, the program calls AppLed.wink
and then enters PAUSE
mode for 100\u2009ms;
\u00a0 awoken from PAUSE
, the program calls alarm.wakeup
and moves from ACTIVE
to SLEEP
mode.
\u00a0 The energy (in millijoules) consumed during this 100+\u2009ms SLEEP
\u2009-\u2009ACTIVE
\u2009-\u2009PAUSE
\u2009-\u2009ACTIVE
\u2009-\u2009SLEEP
interval.
\u00a0 The energy (in millijoules) consumed over an arbitrary 10\u2009s interval encompassing six wakeups from SLEEP
.
While time intervals and differ by a factor of 100, the total energy [\u2009mJ
\u2009] consumed over these intervals differ by only a factor of \u2248\u20096. Needless to say, decreasing the number of wakeups over a given timeframe will always improve overall energy efficiency. Often, though, application requirements will dictate the frequency of these wakeup intervals \u2013 as high as once per second in many embedded systems.
Introducing EM hypothesized that reducing code size could have a potentially dramatic impact on the size, power, and cost of future MCU silicon. Focusing on the dimension of power for now \u2013 and targeting legacy MCUs \u2013 we can already quantify the relationship between \"less code\" and \"less energy\".
Needless to say, minimizing the number of CPU instructions executed while ACTIVE
will only reduce overall energy consumption \u2013 assuming our software still meets a given set of application requirements. With its uncanny ability to reduce code size, the EM language and runtime should benchmark quite favorably against more conventional C/C++ RTOS platforms such as Zephyr.
Help wanted \u2013 embedded programmer familiar with Zephyr
Setting aside the larger \"EM vs C/C++\" discussion for now, we'll focus instead on a very effective technique for reducing energy consumption using the same Alarm1P program executing on the same target MCU board.\u00a0 Quite simply, the EM runtime will automatically copy the\u2009.text
and\u2009.const
program sections into fast, on-chip SRAM at startup \u2013 rather than leaving this readonly code\u2009+\u2009data in Flash memory, where they conventionally reside.
To quantify the impact of this change, compare the following Alarm1P Power Capture with our earlier baseline \u2013 paying close attention to the total energy [\u2009mJ
\u2009] consumed at intervals and in each capture.
Alarm1P Power Capture \u2013 SRAM
EM Setup:\u00a0ti.cc23xx/segger_sram
TBD \u2013 open for suggestions
TBD \u2013 open for suggestions
While we do see a modest 10% gain in energy efficiency here, don't forget that the Alarm1P
program actually fetches very few instructions \u2013 with the MCU remaining in a (lower-power) PAUSE
mode for most of interval .\u00a0 Imagine, though, a duty-cycled \"sleepy\" application that executes a non-trivial mix of math functions and control code when ACTIVE
:\u00a0 significant improvements during interval would also lower overall energy consumption reported at .
With modern MCUs clocked at \u2248\u200950\u2009-\u2009150\u2009MHz\u2009, these architectures invariably employ a HW cache to mitigate wait-states which would otherwise stall the CPU when fetching instructions or constants directly from slower flash memory. But SRAM has no such limitations, as this class of memory can easily sustain read rates in excess of 500\u2009MHz\u2009. The CPU hence runs at maximum efficiency, allowing the application to re-enter SLEEP
that much sooner.
Assuming the program image can actually fit within SRAM \u2013 a far more scarce resource than flash \u2013 the EM distro for your target MCU board can actually disable the flash memory and its HW cache during em$startup
to further reduce ambient power. Paradoxically, running the CPU at its highest possible clock rate will often decrease overall energy consumption when executing \u2013 again, by minimizing the amount of time spent in the ACTIVE
mode.
In the future, we'll have additional articles under Advancing EM that benchmark more sophisticated EM applications that nevertheless can execute entirely from on-chip SRAM.\u00a0 In the meanwhile, ponder the following question:
Knowing EM applications need only a small SRAM to run, how might we architect future MCUs\u2009???
"},{"location":"advancing/syntax/","title":"Navigating EM syntax diagrams","text":"The following \"railroad track\" diagram captures the complete syntax of EM. Automatically generated from the actual grammar used by the language translator, you can click on any of the blue rectangles (such as Unit
or Decl
) and navigate to the definition of these non-terminal elements; the cyan ovals represent terminal tokens scanned by an underlying lexer.
Hint
To quickly jump to the major sections of the grammar, click through the blue non-terminals within the special definitions at the top of the diagram [\u2009$start
, Decl_$
, Expr_$
, Stmt_$
, Type_$
\u2009]\u2009.
Your browser will not maintain history as you navigate through this diagram. To save time scrolling back to the top of diagram, simply click on the Language Syntax link to reload the entire window.
"},{"location":"cargo/","title":"Index","text":""},{"location":"cargo/#em-bundles","title":"EM bundles","text":""},{"location":"cargo/em.bench/","title":"Index","text":""},{"location":"cargo/em.bench/#bundle-embench","title":"bundle em.bench","text":""},{"location":"cargo/em.bench/em.coremark/","title":"Index","text":""},{"location":"cargo/em.bench/em.coremark/#package-emcoremark","title":"package em.coremark","text":""},{"location":"cargo/em.bench/em.coremark/ActiveRunnerP/","title":"ActiveRunnerP","text":""},{"location":"cargo/em.bench/em.coremark/ActiveRunnerP/#unit-activerunnerp","title":"unit ActiveRunnerP","text":"em.coremark/ActiveRunnerP.empackage em.coremark\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.mcu import Common\n\nimport CoreBench\nimport Utils\n\nmodule ActiveRunnerP\n\n config ITERATIONS: uint16 = 10\n\nend\n\ndef em$startup()\n CoreBench.setup()\nend\n\ndef em$run()\n AppLed.on()\n Common.BusyWait.wait(250000)\n AppLed.off()\n Common.UsCounter.start()\n %%[d+]\n for auto i = 0; i < ITERATIONS; i++\n CoreBench.run()\n end\n %%[d-]\n auto usecs = Common.UsCounter.stop()\n AppLed.on()\n Common.BusyWait.wait(250000)\n AppLed.off()\n printf \"usecs = %d\\n\", usecs\n printf \"list crc = %04x\\n\", Utils.getCrc(Utils.Kind.LIST)\n printf \"matrix crc = %04x\\n\", Utils.getCrc(Utils.Kind.MATRIX)\n printf \"state crc = %04x\\n\", Utils.getCrc(Utils.Kind.STATE)\n printf \"final crc = %04x\\n\", Utils.getCrc(Utils.Kind.FINAL)\nend\n
"},{"location":"cargo/em.bench/em.coremark/BenchAlgI/","title":"BenchAlgI","text":""},{"location":"cargo/em.bench/em.coremark/BenchAlgI/#unit-benchalgi","title":"unit BenchAlgI","text":"em.coremark/BenchAlgI.empackage em.coremark\n\nimport Utils\n\ninterface BenchAlgI\n\n config memSize: uint16\n\n function dump()\n function kind(): Utils.Kind\n function print()\n function run(arg: uarg_t = 0): Utils.sum_t\n function setup()\n\nend\n
"},{"location":"cargo/em.bench/em.coremark/ComparatorI/","title":"ComparatorI","text":""},{"location":"cargo/em.bench/em.coremark/ComparatorI/#unit-comparatori","title":"unit ComparatorI","text":"em.coremark/ComparatorI.empackage em.coremark\n\nimport ListBench\n\ninterface ComparatorI\n\n function compare: ListBench.Comparator\n\nend\n
"},{"location":"cargo/em.bench/em.coremark/CoreBench/","title":"CoreBench","text":""},{"location":"cargo/em.bench/em.coremark/CoreBench/#unit-corebench","title":"unit CoreBench","text":"em.coremark/CoreBench.empackage em.coremark\n\nfrom em.lang import Math\n\nimport BenchAlgI\nimport Crc\nimport ListBench\nimport IdxComparator\nimport MatrixBench\nimport StateBench\nimport Utils\nimport ValComparator\n\nmodule CoreBench: BenchAlgI\n\n config TOTAL_DATA_SIZE: uint16 = 2000\n config NUM_ALGS: uint8 = 3\n\nend\n\ndef em$configure()\n memSize = Math.floor(TOTAL_DATA_SIZE / NUM_ALGS)\n ListBench.idxCompare ?= IdxComparator.compare\n ListBench.valCompare ?= ValComparator.compare\n ListBench.memSize ?= memSize\n MatrixBench.memSize ?= memSize\n StateBench.memSize ?= memSize\n ValComparator.Bench0 ?= StateBench\n ValComparator.Bench1 ?= MatrixBench\nend\n\ndef em$construct()\n Utils.bindSeedH(1, 0x0)\n Utils.bindSeedH(2, 0x0)\n Utils.bindSeedH(3, 0x66)\nend\n\ndef dump()\n ListBench.dump()\n MatrixBench.dump()\n StateBench.dump()\nend\n\ndef kind()\n return Utils.Kind.FINAL\nend\n\ndef print()\n ListBench.print()\n MatrixBench.print()\n StateBench.print()\nend\n\ndef run(arg)\n auto crc = ListBench.run(1)\n Utils.setCrc(Utils.Kind.FINAL, Crc.add16(<int16>crc, Utils.getCrc(Utils.Kind.FINAL)))\n crc = ListBench.run(-1)\n Utils.setCrc(Utils.Kind.FINAL, Crc.add16(<int16>crc, Utils.getCrc(Utils.Kind.FINAL)))\n Utils.bindCrc(Utils.Kind.LIST, Utils.getCrc(Utils.Kind.FINAL))\n return Utils.getCrc(Utils.Kind.FINAL)\nend\n\ndef setup()\n ListBench.setup()\n MatrixBench.setup()\n StateBench.setup()\nend\n
"},{"location":"cargo/em.bench/em.coremark/Crc/","title":"Crc","text":""},{"location":"cargo/em.bench/em.coremark/Crc/#unit-crc","title":"unit Crc","text":"em.coremark/Crc.empackage em.coremark\n\nimport Utils\n\nmodule Crc\n\n type sum_t: Utils.sum_t\n\n function add16(val: int16, crc: sum_t): sum_t\n function addU32(val: uint32, crc: sum_t): sum_t\n\nprivate:\n\n function update(data: uint8, crc: sum_t): sum_t\n\nend\n\ndef add16(val, crc)\n auto v = <uint16>val\n crc = update(<uint8>v, crc)\n crc = update(<uint8>(v >> 8), crc)\n return crc\nend\n\ndef addU32(val, crc)\n crc = add16(<int16>val, crc)\n crc = add16(<int16>(val >> 16), crc)\n return crc\nend\n\ndef update(data, crc)\n auto i = <uint8>0\n auto x16 = <uint8>0\n auto carry = <uint8>0\n for auto i = 0; i < 8; i++\n x16 = <uint8>((data & 1) ^ (<uint8>crc & 1))\n data >>= 1\n if x16 == 1\n crc ^= 0x4002\n carry = 1\n else\n carry = 0\n end\n crc >>= 1\n if carry\n crc |= 0x8000\n else\n crc &= 0x7fff\n end\n end\n return crc\nend\n
"},{"location":"cargo/em.bench/em.coremark/IdxComparator/","title":"IdxComparator","text":""},{"location":"cargo/em.bench/em.coremark/IdxComparator/#unit-idxcomparator","title":"unit IdxComparator","text":"em.coremark/IdxComparator.empackage em.coremark\n\nimport ComparatorI\n\nmodule IdxComparator: ComparatorI\n\nend\n\ndef compare(a, b)\n a.val = <int16>((<uint16>a.val & 0xff00) | (0x00ff & <uint16>(a.val >> 8)))\n b.val = <int16>((<uint16>b.val & 0xff00) | (0x00ff & <uint16>(b.val >> 8)))\n return a.idx - b.idx\nend\n
"},{"location":"cargo/em.bench/em.coremark/ListBench/","title":"ListBench","text":""},{"location":"cargo/em.bench/em.coremark/ListBench/#unit-listbench","title":"unit ListBench","text":"em.coremark/ListBench.empackage em.coremark\n\nfrom em.lang import Math\n\nimport BenchAlgI\nimport Crc\nimport Utils\n\n# patterned after core_list_join.c\n\nmodule ListBench: BenchAlgI\n\n type Data: struct\n val: int16\n idx: int16\n end\n\n type Comparator: function(a: Data&, b: Data&): int32\n\n config idxCompare: Comparator\n config valCompare: Comparator\n\nprivate:\n\n type Elem: struct\n next: Elem&\n data: Data&\n end\n\n function find(list: Elem&, data: Data&): Elem&\n function pr(list: Elem&, name: string)\n function remove(item: Elem&): Elem&\n function reverse(list: Elem&): Elem&\n function sort(list: Elem&, cmp: Comparator): Elem&\n function unremove(removed: Elem&, modified: Elem&)\n\n config maxElems: uint16\n\n var curHead: Elem&\n\nend\n\ndef em$construct()\n auto itemSize = 16 + sizeof<Data>\n maxElems = Math.round(memSize / itemSize) - 3\n curHead = new<Elem>\n curHead.data = new<Data>\n auto p = curHead\n for auto i = 0; i < maxElems - 1; i++\n auto q = p.next = new<Elem>\n q.data = new<Data>\n p = q\n end\n p.data = new<Data>\n p.next = null\nend\n\ndef dump()\n for auto e = curHead; e; e = e.next\n %%[a+]\n %%[>e.data.idx]\n %%[>e.data.val]\n %%[a-]\n end\nend\n\ndef find(list, data)\n auto elem = list\n if data.idx >= 0\n while elem && elem.data.idx != data.idx\n elem = elem.next\n end\n else\n while elem && <int16>(<uint16>elem.data.val & 0xff) != data.val\n elem = elem.next\n end\n end\n return elem\nend\n\ndef kind()\n return Utils.Kind.LIST\nend\n\ndef pr(list, name)\n auto sz = 0\n printf \"%s\\n[\", name\n for auto e = list; e; e = e.next\n auto pre = (sz++ % 8) == 0 ? \"\\n \" : \"\"\n printf \"%s(%04x,%04x)\", <iarg_t>pre, e.data.idx, e.data.val\n end\n printf \"\\n], size = %d\\n\", sz\nend\n\ndef print()\n pr(curHead, \"current\")\nend\n\ndef remove(item)\n auto ret = item.next\n auto tmp = item.data\n item.data = ret.data\n ret.data = tmp\n item.next = item.next.next\n ret.next = null\n return ret\nend\n\ndef reverse(list)\n auto next = <Elem&>null\n while list\n auto tmp = list.next\n list.next = next\n next = list\n list = tmp\n end\n return next\nend\n\n def run(arg)\n auto list = curHead\n auto finderIdx = <int16>arg\n auto findCnt = Utils.getSeed(3)\n auto found = <uint16>0\n auto missed = <uint16>0\n auto retval = <Crc.sum_t>0\n var data: Data\n data.idx = finderIdx\n for auto i = 0; i < findCnt; i++\n data.val = <int16>(i & 0xff)\n auto elem = find(list, data)\n list = reverse(list)\n if elem == null\n missed += 1\n retval += <uint16>(list.next.data.val >> 8) & 0x1\n else\n found += 1\n if <uint16>elem.data.val & 0x1\n retval += (<uint16>(elem.data.val >> 9)) & 0x1\n end\n if elem.next != null\n auto tmp = elem.next\n elem.next = tmp.next\n tmp.next = list.next\n list.next = tmp\n end\n end\n data.idx += 1 if data.idx >= 0\n end\n retval += found * 4 - missed\n list = sort(list, valCompare) if finderIdx > 0\n auto remover = remove(list.next)\n auto finder = find(list, &data)\n finder = list.next if !finder\n while finder\n retval = Crc.add16(list.data.val, retval)\n finder = finder.next\n end\n unremove(remover, list.next)\n list = sort(list, idxCompare)\n for auto e = list.next; e; e = e.next\n retval = Crc.add16(list.data.val, retval)\n end\n return retval\nend\n\ndef setup()\n auto seed = Utils.getSeed(1)\n auto ki = 1\n auto kd = maxElems - 3\n auto e = curHead\n e.data.idx = 0\n e.data.val = 0x8080\n for e = e.next; e.next; e = e.next\n auto pat = <uint16>(seed ^ kd) & 0xf\n auto dat = (pat << 3) | (kd & 0x7)\n e.data.val = <int16>((dat << 8) | dat)\n kd -= 1\n if ki < (maxElems / 5)\n e.data.idx = ki++\n else\n pat = <uint16>(seed ^ ki++)\n e.data.idx = <int16>(0x3fff & (((ki & 0x7) << 8) | pat))\n end\n end\n e.data.idx = 0x7fff\n e.data.val = 0xffff\n curHead = sort(curHead, idxCompare)\nend\n\ndef sort(list, cmp)\n auto insize = <int32>1\n var q: Elem&\n var e: Elem&\n for ;;\n auto p = list\n auto tail = list = null\n auto nmerges = <int32>0 # count number of merges we do in this pass\n while p\n nmerges++ # there exists a merge to be done\n # step `insize' places along from p\n q = p\n auto psize = 0\n for auto i = 0; i < insize; i++\n psize++\n q = q.next\n break if !q\n end\n # if q hasn't fallen off end, we have two lists to merge\n auto qsize = insize\n # now we have two lists; merge them\n while psize > 0 || (qsize > 0 && q)\n # decide whether next element of merge comes from p or q\n if psize == 0\n # p is empty; e must come from q\n e = q\n q = q.next\n qsize--\n elif qsize == 0 || !q\n # q is empty; e must come from p.\n e = p\n p = p.next\n psize--\n elif cmp(p.data, q.data) <= 0\n # First element of p is lower (or same); e must come from p.\n e = p\n p = p.next\n psize--\n else\n # First element of q is lower; e must come from q.\n e = q\n q = q.next\n qsize--\n end\n # add the next element to the merged list\n if tail\n tail.next = e\n else\n list = e\n end\n tail = e\n end\n # now p has stepped `insize' places along, and q has too\n p = q\n end\n tail.next = null\n # If we have done only one merge, we're finished\n break if nmerges <= 1 # allow for nmerges==0, the empty list case\n # Otherwise repeat, merging lists twice the size\n insize *= 2\n end\n return list\nend\n\ndef unremove(removed, modified)\n auto tmp = removed.data\n removed.data = modified.data\n modified.data = tmp\n removed.next = modified.next\n modified.next = removed\nend\n
"},{"location":"cargo/em.bench/em.coremark/MatrixBench/","title":"MatrixBench","text":""},{"location":"cargo/em.bench/em.coremark/MatrixBench/#unit-matrixbench","title":"unit MatrixBench","text":"em.coremark/MatrixBench.empackage em.coremark\n\nimport BenchAlgI\nimport Crc\nimport Utils\n\n# patterned after core_matrix.c\n\nmodule MatrixBench: BenchAlgI\n\nprivate:\n\n type matdat_t: int16\n type matres_t: int32\n\n config dimN: uint8\n\n var matA: matdat_t[]\n var matB: matdat_t[]\n var matC: matres_t[]\n\n function addVal(val: matdat_t)\n function mulVal(val: matdat_t)\n function mulMat()\n function mulMatBix()\n function mulVec()\n function sumDat(clipval: matdat_t): matdat_t\n\n function bix(res: matres_t, lower: uint8, upper: uint8): matres_t\n function clip(d: matdat_t, b: bool): matdat_t\n function enlarge(val: matdat_t): matdat_t\n\n function prDat(lab: string, mat: matdat_t[])\n function prRes(lab: string)\n\nend\n\ndef em$construct()\n auto i = 0\n auto j = 0\n while j < memSize\n i += 1\n j = i * i * 2 * 4\n end\n dimN = i - 1\n matA.length = matB.length = matC.length = dimN * dimN\nend\n\ndef addVal(val)\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n matA[i * dimN + j] += val\n end\n end\nend\n\ndef bix(res, lower, upper)\n auto r = <uint32>res\n auto l = <uint32>lower\n auto u = <uint32>upper\n return <matres_t>((r >> l) & (~(0xffffffff << u)))\nend\n\ndef clip(d, b)\n auto x = <uint16>d\n return <matdat_t>(x & (b ? 0x0ff : 0x0ffff))\nend\n\ndef dump()\n ## TODO -- implement\nend\n\ndef enlarge(val)\n auto v = <uint16>val\n return <matdat_t>(0xf000 | v)\nend\n\ndef kind()\n return Utils.Kind.MATRIX\nend\n\ndef mulVal(val)\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n matC[i * dimN + j] = <matres_t>matA[i * dimN + j] * <matres_t>val\n end\n end\nend\n\ndef mulMat()\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n matC[i * dimN + j] = 0\n for auto k = 0; k < dimN; k++\n matC[i * dimN + j] += <matres_t>matA[i * dimN + k] * <matres_t>matB[k * dimN + j]\n end\n end\n end\nend\n\ndef mulMatBix()\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n matC[i * dimN + j] = 0\n for auto k = 0; k < dimN; k++\n auto tmp = <matres_t>matA[i * dimN + k] * <matres_t>matB[k * dimN + j]\n matC[i * dimN + j] += bix(tmp, 2, 4) * bix(tmp, 5, 7)\n end\n end\n end\nend\n\ndef mulVec()\n for auto i = 0; i < dimN; i++\n matC[i] = 0\n for auto j = 0; j < dimN; j++\n matC[i] += <matres_t>matA[i * dimN + j] * <matres_t>matB[j]\n end\n end\nend\n\ndef print()\n prDat(\"A\", matA)\n prDat(\"B\", matB)\nend\n\ndef prDat(lab, mat)\n printf \"\\n%s:\\n \", lab\n for auto i = 0; i < dimN; i++\n auto sep = \"\"\n for auto j = 0; j < dimN; j++\n printf \"%s%d\", sep, mat[i * dimN + j]\n sep = \",\"\n end\n printf \"\\n \"\n end\nend\n\ndef prRes(lab)\n printf \"\\n%s:\\n \", lab\n for auto i = 0; i < dimN; i++\n auto sep = \"\"\n for auto j = 0; j < dimN; j++\n printf \"%s%d\", sep, matC[i * dimN + j]\n sep = \",\"\n end\n printf \"\\n \"\n end\nend\n\ndef run(arg)\n auto crc = <Crc.sum_t>0\n auto val = <matdat_t>arg\n auto clipval = enlarge(val)\n #\n addVal(val)\n mulVal(val)\n crc = Crc.add16(sumDat(clipval), crc)\n #\n mulVec()\n crc = Crc.add16(sumDat(clipval), crc)\n #\n mulMat()\n crc = Crc.add16(sumDat(clipval), crc)\n #\n mulMatBix()\n crc = Crc.add16(sumDat(clipval), crc)\n #\n addVal(-val)\n return Crc.add16(<int16>crc, Utils.getCrc(Utils.Kind.FINAL))\nend\n\ndef setup()\n auto s32 = <uint32>Utils.getSeed(1) | (<uint32>Utils.getSeed(2) << 16)\n auto sd = <matdat_t>s32\n sd = 1 if sd == 0\n auto order = <matdat_t>1\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n sd = <int16>((order * sd) % 65536)\n auto val = <matdat_t>(sd + order)\n val = clip(val, false)\n matB[i * dimN + j] = val\n val += order\n val = clip(val, true)\n matA[i * dimN + j] = val\n order += 1\n end\n end\nend\n\ndef sumDat(clipval)\n auto cur = <matres_t>0\n auto prev = <matres_t>0\n auto tmp = <matres_t>0\n auto ret = <matdat_t>0\n for auto i = 0; i < dimN; i++\n for auto j = 0; j < dimN; j++\n cur = matC[i * dimN + j]\n tmp += cur\n if tmp > clipval\n ret += 10\n tmp = 0\n else\n ret += (cur > prev) ? 1 : 0\n end\n prev = cur\n end\n end\n return ret\nend\n
"},{"location":"cargo/em.bench/em.coremark/SleepyRunnerP/","title":"SleepyRunnerP","text":""},{"location":"cargo/em.bench/em.coremark/SleepyRunnerP/#unit-sleepyrunnerp","title":"unit SleepyRunnerP","text":"em.coremark/SleepyRunnerP.empackage em.coremark\n\nfrom em$distro import BoardC\n\nfrom em.utils import FiberMgr\nfrom em.utils import TickerMgr\n\nimport CoreBench\n\nmodule SleepyRunnerP\n\nprivate:\n\n var ticker: TickerMgr.Ticker&\n\n var count: uint8 = 10\n\n function tickCb: TickerMgr.TickCallback\n\nend\n\ndef em$construct()\n ticker = TickerMgr.createH()\nend\n\ndef em$startup()\n CoreBench.setup()\nend\n\ndef em$run()\n ticker.start(256, tickCb)\n FiberMgr.run()\nend\n\ndef tickCb()\n %%[d+]\n auto crc = CoreBench.run()\n %%[d-]\n printf \"crc = %04x\\n\", crc\n return if --count\n ticker.stop()\n halt\nend\n
"},{"location":"cargo/em.bench/em.coremark/StateBench/","title":"StateBench","text":""},{"location":"cargo/em.bench/em.coremark/StateBench/#unit-statebench","title":"unit StateBench","text":"em.coremark/StateBench.empackage em.coremark\n\nimport BenchAlgI\nimport Crc\nimport Utils\n\n# patterned after core_state.c\n\nmodule StateBench: BenchAlgI\n\nprivate:\n\n const NUM_STATES: uint8 = 8\n\n type StringBuf: char*\n\n type State: enum\n START,\n INVALID,\n S1,\n S2,\n INT,\n FLOAT,\n EXPONENT,\n SCIENTIFIC,\n end\n\n config intPat: string[4] = [\n \"5012\", \"1234\", \"-874\", \"+122\"\n ]\n config fltPat: string[4] = [\n \"35.54400\", \".1234500\", \"-110.700\", \"+0.64400\"\n ]\n config sciPat: string[4] = [\n \"5.500e+3\", \"-.123e-2\", \"-87e+832\", \"+0.6e-12\"\n ]\n config errPat: string[4] = [\n \"T0.3e-1F\", \"-T.T++Tq\", \"1T3.4e4z\", \"34.0e-T^\"\n ]\n\n config intPatLen: uint16\n config fltPatLen: uint16\n config sciPatLen: uint16\n config errPatLen: uint16\n\n var memBuf: char[]\n\n function isDigit(ch: char): bool\n function nextState(pStr: StringBuf*, transCnt: uint32[]): State\n function ord(state: State): uint8\n function scan(finalCnt: uint32[], transCnt: uint32[])\n function scramble(seed: Utils.seed_t, step: uarg_t)\n\nend\n\ndef em$construct()\n memBuf.length = memSize\n intPatLen = intPat[0].length\n fltPatLen = fltPat[0].length\n sciPatLen = sciPat[0].length\n errPatLen = errPat[0].length\nend\n\ndef dump()\n ## TODO -- implement\nend\n\ndef isDigit(ch)\n return ch >= '0' && ch <= '9'\nend\n\ndef kind()\n return Utils.Kind.STATE\nend\n\ndef nextState(pStr, transCnt)\n auto str = *pStr\n auto state = State.START\n for ; *str && state != State.INVALID; str++\n auto ch = *str\n if ch == ','\n str++\n break\n end\n switch state\n case State.START\n if isDigit(ch)\n state = State.INT\n elif ch == '+' || ch == '-'\n state = State.S1\n elif ch == '.'\n state = State.FLOAT\n else\n state = State.INVALID\n transCnt[ord(State.INVALID)] += 1\n end\n transCnt[ord(State.START)] += 1\n break\n case State.S1\n if isDigit(ch)\n state = State.INT\n transCnt[ord(State.S1)] += 1\n elif ch == '.'\n state = State.FLOAT\n transCnt[ord(State.S1)] += 1\n else\n state = State.INVALID\n transCnt[ord(State.S1)] += 1\n end\n break\n case State.INT\n if ch == '.'\n state = State.FLOAT\n transCnt[ord(State.INT)] += 1\n elif !isDigit(ch)\n state = State.INVALID\n transCnt[ord(State.INT)] += 1\n end\n break\n case State.FLOAT\n if ch == 'E' || ch == 'e'\n state = State.S2\n transCnt[ord(State.FLOAT)] += 1\n elif !isDigit(ch)\n state = State.INVALID\n transCnt[ord(State.FLOAT)] += 1\n end\n break\n case State.S2\n if ch == '+' || ch == '-'\n state = State.EXPONENT\n transCnt[ord(State.S2)] += 1\n else\n state = State.INVALID\n transCnt[ord(State.S2)] += 1\n end\n break\n case State.EXPONENT\n if isDigit(ch)\n state = State.SCIENTIFIC\n transCnt[ord(State.EXPONENT)] += 1\n else\n state = State.INVALID\n transCnt[ord(State.EXPONENT)] += 1\n end\n break\n case State.SCIENTIFIC\n if !isDigit(ch)\n state = State.INVALID\n transCnt[ord(State.INVALID)] += 1\n end\n break\n end\n end\n *pStr = str\n return state\nend\n\ndef ord(state)\n return <uint8>state\nend\n\ndef print()\n auto p = &memBuf[0]\n auto cnt = 0\n printf \"\\n%c\", '\"'\n while *p\n if (cnt++ % 8) == 0\n printf \"\\n \"\n end\n var c: char\n while (c = *p++) != ','\n printf \"%c\", c\n end\n printf \", \"\n end\n printf \"\\n%c, count = %d\\n\", '\"', cnt\nend\n\ndef run(arg)\n arg = 0x22 if arg < 0x22\n var finalCnt: uint32[NUM_STATES]\n var transCnt: uint32[NUM_STATES]\n for auto i = 0; i < NUM_STATES; i++\n finalCnt[i] = transCnt[i] = 0\n end\n scan(finalCnt, transCnt)\n scramble(Utils.getSeed(1), arg)\n scan(finalCnt, transCnt)\n scramble(Utils.getSeed(2), arg)\n auto crc = Utils.getCrc(Utils.Kind.FINAL)\n for auto i = 0; i < NUM_STATES; i++\n crc = Crc.addU32(finalCnt[i], crc)\n crc = Crc.addU32(transCnt[i], crc)\n end\n return crc\nend\n\ndef scan(finalCnt, transCnt)\n for auto str = &memBuf[0]; *str;\n auto state = nextState(&str, transCnt)\n finalCnt[ord(state)] += 1\n end\nend\n\ndef scramble(seed, step)\n for auto str = &memBuf[0]; str < &memBuf[memSize]; str += <uint16>step\n *str ^= <uint8>seed if *str != ','\n end\nend\n\ndef setup()\n auto seed = Utils.getSeed(1)\n auto p = &memBuf[0]\n auto total = 0\n auto pat = \"\"\n auto plen = 0\n while (total + plen + 1) < (memSize - 1)\n if plen\n for auto i = 0; i < plen; i++\n *p++ = pat[i]\n end\n *p++ = ','\n total += plen + 1\n end\n switch ++seed & 0x7\n case 0\n case 1\n case 2\n pat = intPat[(seed >> 3) & 0x3]\n plen = intPatLen\n break\n case 3\n case 4\n pat = fltPat[(seed >> 3) & 0x3]\n plen = fltPatLen\n break\n case 5\n case 6\n pat = sciPat[(seed >> 3) & 0x3]\n plen = sciPatLen\n break\n case 7\n pat = errPat[(seed >> 3) & 0x3]\n plen = errPatLen\n break\n end\n end\nend\n
"},{"location":"cargo/em.bench/em.coremark/Utils/","title":"Utils","text":""},{"location":"cargo/em.bench/em.coremark/Utils/#unit-utils","title":"unit Utils","text":"em.coremark/Utils.empackage em.coremark\n\nmodule Utils\n\n const NUM_SEEDS: uint8 = 5\n\n type Kind: enum\n FINAL, LIST, MATRIX, STATE, ZZZ_\n end\n\n type seed_t: uint16 volatile\n type sum_t: uint16\n\n function bindCrc(kind: Kind, crc: sum_t)\n function getCrc(kind: Kind): sum_t\n function setCrc(kind: Kind, crc: sum_t)\n\n host function bindSeedH(idx: uint8, val: seed_t)\n function getSeed(idx: uint8): seed_t\n\nprivate:\n\n var crcTab: sum_t[]\n var seedTab: seed_t[NUM_SEEDS]\n\nend\n\ndef em$construct()\n crcTab.length = <uint16>Kind.ZZZ_\nend\n\ndef bindCrc(kind, crc)\n auto p = &crcTab[<uint16>kind]\n *p = crc if *p == 0\nend\n\ndef bindSeedH(idx, val)\n seedTab[idx - 1] = val\nend\n\ndef getCrc(kind)\n return crcTab[<uint16>kind]\nend\n\ndef getSeed(idx)\n return seedTab[idx - 1]\nend\n\ndef setCrc(kind, crc)\n crcTab[<uint16>kind] = crc\nend\n
"},{"location":"cargo/em.bench/em.coremark/ValComparator/","title":"ValComparator","text":""},{"location":"cargo/em.bench/em.coremark/ValComparator/#unit-valcomparator","title":"unit ValComparator","text":"em.coremark/ValComparator.empackage em.coremark\n\nimport BenchAlgI\nimport ComparatorI\nimport Crc\nimport Utils\n\nmodule ValComparator: ComparatorI\n\n proxy Bench0: BenchAlgI\n proxy Bench1: BenchAlgI\n\nprivate:\n\n function calc(pval: int16*): int16\n\nend\n\ndef calc(pval)\n auto val = <uint16>*pval\n auto optype = <uint8>(val >> 7) & 1\n return <int16>(val & 0x007f) if optype\n auto flag = val & 0x7\n auto vtype = (val >> 3) & 0xf\n vtype |= vtype << 4\n var ret: uint16\n switch flag\n case 0\n ret = Bench0.run(<uarg_t>vtype)\n Utils.bindCrc(Bench0.kind(), ret)\n break\n case 1\n ret = Bench1.run(<uarg_t>vtype)\n Utils.bindCrc(Bench1.kind(), ret)\n break\n default\n ret = val\n break\n end\n auto newcrc = Crc.add16(<int16>ret, Utils.getCrc(Utils.Kind.FINAL))\n Utils.setCrc(Utils.Kind.FINAL, Crc.add16(<int16>ret, Utils.getCrc(Utils.Kind.FINAL)))\n ret &= 0x007f\n *pval = <int16>((val & 0xff00) | 0x0080 | ret) ## cache the result\n return <int16>ret\nend\n\ndef compare(a, b)\n auto val1 = calc(&a.val)\n auto val2 = calc(&b.val)\n return val1 - val2\nend\n
"},{"location":"cargo/em.core/","title":"Index","text":""},{"location":"cargo/em.core/#bundle-emcore","title":"bundle em.core","text":""},{"location":"cargo/em.core/em.hal/","title":"Index","text":""},{"location":"cargo/em.core/em.hal/#package-emhal","title":"package em.hal","text":""},{"location":"cargo/em.core/em.hal/BusyWaitI/","title":"BusyWaitI","text":""},{"location":"cargo/em.core/em.hal/BusyWaitI/#unit-busywaiti","title":"unit BusyWaitI","text":"em.hal/BusyWaitI.empackage em.hal\n\ninterface BusyWaitI\n # ^| abstraction of a spin-loop\n function wait(usecs: uint32)\n # ^| enter a spin-loop\n # ^| @usecs - duration in microseconds\nend\n
"},{"location":"cargo/em.core/em.hal/BusyWaitN/","title":"BusyWaitN","text":""},{"location":"cargo/em.core/em.hal/BusyWaitN/#unit-busywaitn","title":"unit BusyWaitN","text":"em.hal/BusyWaitN.empackage em.hal\n\nimport BusyWaitI\n\nmodule BusyWaitN: BusyWaitI\n # ^| Nil implementation of the BusyWaitI interface\nend\n\ndef wait(usecs)\nend\n
"},{"location":"cargo/em.core/em.hal/ButtonI/","title":"ButtonI","text":""},{"location":"cargo/em.core/em.hal/ButtonI/#unit-buttoni","title":"unit ButtonI","text":"em.hal/ButtonI.empackage em.hal\n\ninterface ButtonI\n # ^| abstraction of a pressable button\n type OnPressedCB: function()\n # ^| signature of a button's callback function\n function isPressed(): bool\n # ^| test whether this button is currently pressed\n function onPressed(cb: OnPressedCB, minDurationMs: uint16 = 100, maxDurationMs: uint16 = 4000)\n # ^| bind a callback to this button\n # ^| @cb - callback function, executed when this button is pressed\n # ^| @minDurationMs - minimum time in millisecs before executing this button's callback\n # ^| @maxDurationMs - maximum time in millisecs, after which this button's callback is executed\n\nend\n
"},{"location":"cargo/em.core/em.hal/ButtonN/","title":"ButtonN","text":""},{"location":"cargo/em.core/em.hal/ButtonN/#unit-buttonn","title":"unit ButtonN","text":"em.hal/ButtonN.empackage em.hal\n\nimport ButtonI\n\nmodule ButtonN: ButtonI\n # ^| Nil implementation of the ButtonI interface\nend\n\ndef isPressed()\n return false\nend\n\ndef onPressed(cb, minDurationMs, maxDurationMs)\nend\n
"},{"location":"cargo/em.core/em.hal/ConsoleUartI/","title":"ConsoleUartI","text":""},{"location":"cargo/em.core/em.hal/ConsoleUartI/#unit-consoleuarti","title":"unit ConsoleUartI","text":"em.hal/ConsoleUartI.empackage em.hal\n\ninterface ConsoleUartI \n\n host function setBaudH(rate: uint32)\n\n function flush()\n function put(data: uint8)\n\nend\n
"},{"location":"cargo/em.core/em.hal/ConsoleUartN/","title":"ConsoleUartN","text":""},{"location":"cargo/em.core/em.hal/ConsoleUartN/#unit-consoleuartn","title":"unit ConsoleUartN","text":"em.hal/ConsoleUartN.empackage em.hal\n\nimport ConsoleUartI\n\nmodule ConsoleUartN: ConsoleUartI\n # ^| Nil implementation of the ConsoleUartI interface\nend\n\ndef setBaudH(rate)\nend\n\ndef flush()\nend\n\ndef put(data)\nend\n
"},{"location":"cargo/em.core/em.hal/CopierI/","title":"CopierI","text":""},{"location":"cargo/em.core/em.hal/CopierI/#unit-copieri","title":"unit CopierI","text":"em.hal/CopierI.empackage em.hal\n\ninterface CopierI\n\n function exec(dst: ptr_t, src: ptr_t, cnt: uint16)\n\nend\n
"},{"location":"cargo/em.core/em.hal/FlashI/","title":"FlashI","text":""},{"location":"cargo/em.core/em.hal/FlashI/#unit-flashi","title":"unit FlashI","text":"em.hal/FlashI.empackage em.hal\n\ninterface FlashI\n\n host function getSectorSizeH(): uint32\n host function getWriteChunkH(): uint32\n\n function erase(addr: addr_t, upto: addr_t = 0)\n function write(addr: addr_t, data: ptr_t, len: uint32): addr_t\n\nend\n
"},{"location":"cargo/em.core/em.hal/FlashN/","title":"FlashN","text":""},{"location":"cargo/em.core/em.hal/FlashN/#unit-flashn","title":"unit FlashN","text":"em.hal/FlashN.empackage em.hal\n\nimport FlashI\n\nmodule FlashN: FlashI\n # ^| Nil implementation of the FlashI interface\nend\n\ndef getSectorSizeH()\n return 0\nend\n\ndef getWriteChunkH()\n return 0\nend\n\ndef erase(addr, upto)\nend\n\ndef write(addr, data, len)\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/GlobalInterruptsI/","title":"GlobalInterruptsI","text":""},{"location":"cargo/em.core/em.hal/GlobalInterruptsI/#unit-globalinterruptsi","title":"unit GlobalInterruptsI","text":"em.hal/GlobalInterruptsI.empackage em.hal\n\n#! Interface implemented by a GlobalInterrupts module for a device.\n\ninterface GlobalInterruptsI \n\n type Key: uarg_t\n\n #! Disables interrupts and saves state\n function disable(): Key\n\n #! Enables interrupts\n function enable()\n\n #! Restores interrupts to previous state\n function restore(key: Key)\nend\n
"},{"location":"cargo/em.core/em.hal/GlobalInterruptsN/","title":"GlobalInterruptsN","text":""},{"location":"cargo/em.core/em.hal/GlobalInterruptsN/#unit-globalinterruptsn","title":"unit GlobalInterruptsN","text":"em.hal/GlobalInterruptsN.empackage em.hal\n\nimport GlobalInterruptsI\n\nmodule GlobalInterruptsN: GlobalInterruptsI\n # ^| Nil implementation of the GlobalInterruptsI interface\nend\n\ndef disable()\n return 0\nend\n\ndef enable()\nend\n\ndef restore(key)\nend\n
"},{"location":"cargo/em.core/em.hal/GpioEdgeDetectMinI/","title":"GpioEdgeDetectMinI","text":""},{"location":"cargo/em.core/em.hal/GpioEdgeDetectMinI/#unit-gpioedgedetectmini","title":"unit GpioEdgeDetectMinI","text":"em.hal/GpioEdgeDetectMinI.empackage em.hal\n\nimport GpioI\n\ninterface GpioEdgeDetectMinI: GpioI \n # ^| extends the GpioI abstraction with edge-detection features\n type Handler: function ()\n # ^| signature of an edge-detection function\n host function setDetectHandlerH(h: Handler)\n # ^| bind a handler to this GPIO at build-time\n function clearDetect()\n # ^| clear (acknowledge) any edge-detection by this GPIO\n function disableDetect() \n # ^| disable edge-detection by this GPIO\n function enableDetect()\n # ^| enable edge-detection by this GPIO\n function setDetectFallingEdge()\n # ^| detect high-to-low transitions by this GPIO\n function setDetectRisingEdge()\n # ^| detect low-to-high transitions by this GPIO\nend\n
"},{"location":"cargo/em.core/em.hal/GpioEdgeDetectMinN/","title":"GpioEdgeDetectMinN","text":""},{"location":"cargo/em.core/em.hal/GpioEdgeDetectMinN/#unit-gpioedgedetectminn","title":"unit GpioEdgeDetectMinN","text":"em.hal/GpioEdgeDetectMinN.empackage em.hal\n\nimport GpioEdgeDetectMinI\n\nmodule GpioEdgeDetectMinN: GpioEdgeDetectMinI\n # ^| Nil implementation of the GpioEdgeDetectMinI interface\nend\n\ndef set()\nend\n\ndef clear()\nend\n\ndef toggle()\nend\n\ndef get()\n return false\nend\n\ndef makeInput()\nend\n\ndef isInput()\n return false\nend\n\ndef makeOutput()\nend\n\ndef isOutput()\n return false\nend\n\ndef functionSelect(select)\nend\n\ndef setInternalPullup(state)\nend\n\ndef pinId()\n return 0\nend\n\ndef reset()\nend\n\ndef enableDetect()\nend\n\ndef disableDetect()\nend\n\ndef clearDetect()\nend\n\ndef setDetectRisingEdge()\nend\n\ndef setDetectFallingEdge()\nend\n\ndef setDetectHandlerH(h)\nend\n
"},{"location":"cargo/em.core/em.hal/GpioI/","title":"GpioI","text":""},{"location":"cargo/em.core/em.hal/GpioI/#unit-gpioi","title":"unit GpioI","text":"em.hal/GpioI.empackage em.hal\n\ninterface GpioI\n # ^| abstraction of a GPIO pin\n function clear()\n # ^| clear the value of this GPIO (low)\n function functionSelect (select: uint8)\n # ^| select an alternative function of this GPIO\n function get(): bool\n # ^| get the value of this GPIO\n function isInput(): bool\n # ^| test if this GPIO is an input pin\n function isOutput(): bool\n # ^| test if this GPIO is an output pin\n function makeInput()\n # ^| make this GPIO an input pin\n function makeOutput()\n # ^| make this GPIO an output pin\n function pinId(): int16\n # ^| Return the pin ID of this GPIO\n function reset()\n # ^| Reset this GPIO\n function set()\n # ^| set the value of this GPIO (high)\n function setInternalPullup (state: bool)\n # ^| enable/disable the internalpullup for this GPIO\n function toggle()\n # ^| toggle the value of this GPIO\n\nend\n
"},{"location":"cargo/em.core/em.hal/GpioN/","title":"GpioN","text":""},{"location":"cargo/em.core/em.hal/GpioN/#unit-gpion","title":"unit GpioN","text":"em.hal/GpioN.empackage em.hal\n\nimport GpioI\n\nmodule GpioN: GpioI\n # ^| Nil implementation of the GpioI interface\nend\n\ndef set()\nend\n\ndef clear()\nend\n\ndef toggle()\nend\n\ndef get()\n return false\nend\n\ndef makeInput()\nend\n\ndef isInput()\n return false\nend\n\ndef makeOutput()\nend\n\ndef isOutput()\n return false\nend\n\ndef functionSelect(select)\nend\n\ndef setInternalPullup(state)\nend\n\ndef pinId()\n return 0\nend\n\ndef reset()\nend\n
"},{"location":"cargo/em.core/em.hal/HostUartI/","title":"HostUartI","text":""},{"location":"cargo/em.core/em.hal/HostUartI/#unit-hostuarti","title":"unit HostUartI","text":"em.hal/HostUartI.empackage em.hal\n\nimport ConsoleUartI\n\ninterface HostUartI: ConsoleUartI\n\n type RxHandler: function(b: uint8)\n\n function disable()\n function enable()\n function get(): uint8\n\n host function setRxHandlerH(handler: RxHandler)\n\nend\n
"},{"location":"cargo/em.core/em.hal/HostUartN/","title":"HostUartN","text":""},{"location":"cargo/em.core/em.hal/HostUartN/#unit-hostuartn","title":"unit HostUartN","text":"em.hal/HostUartN.empackage em.hal\n\nimport HostUartI\n\nmodule HostUartN: HostUartI\n # ^| Nil implementation of the HostUartI interface\nend\n\ndef setBaudH(rate)\n ## TODO -- implement\nend\n\ndef flush()\n ## TODO -- implement\nend\n\ndef put(data)\n ## TODO -- implement\nend\n\ndef disable()\n ## TODO -- implement\nend\n\ndef enable()\n ## TODO -- implement\nend\n\ndef get()\n ## TODO -- implement\n return 0\nend\n\ndef setRxHandlerH(handler)\n ## TODO -- implement\nend\n
"},{"location":"cargo/em.core/em.hal/IdleI/","title":"IdleI","text":""},{"location":"cargo/em.core/em.hal/IdleI/#unit-idlei","title":"unit IdleI","text":"em.hal/IdleI.empackage em.hal\n\n#! Should be implemented by an Idle module\n\ninterface IdleI \n\n #! This function defines how the system idles\n function exec()\n\n #! This function is called during \"warm\" wakeups\n function wakeup()\nend\n
"},{"location":"cargo/em.core/em.hal/IdleN/","title":"IdleN","text":""},{"location":"cargo/em.core/em.hal/IdleN/#unit-idlen","title":"unit IdleN","text":"em.hal/IdleN.empackage em.hal\n\nimport IdleI\n\nmodule IdleN: IdleI\n # ^| Nil implementation of the IdleI interface\nend\n\ndef exec()\nend\n\ndef wakeup()\nend\n
"},{"location":"cargo/em.core/em.hal/InterruptSourceI/","title":"InterruptSourceI","text":""},{"location":"cargo/em.core/em.hal/InterruptSourceI/#unit-interruptsourcei","title":"unit InterruptSourceI","text":"em.hal/InterruptSourceI.empackage em.hal\n\n#! Generally implemented by an Interrupt Template used to \"create\" interrupts\n\ninterface InterruptSourceI \n\n type Handler: function()\n\n #! Sets the handler function for a particular interrupt\n host function setHandlerH(h: Handler)\n\n #! Enables a particular interrupt\n function enable()\n\n #! Disables a particular interrupt\n function disable()\n\n #! Clears the interrupt flag \n function clear()\n\n #! True if the interrupt is enabled\n function isEnabled(): bool\nend\n
"},{"location":"cargo/em.core/em.hal/IntrVecI/","title":"IntrVecI","text":""},{"location":"cargo/em.core/em.hal/IntrVecI/#unit-intrveci","title":"unit IntrVecI","text":"em.hal/IntrVecI.empackage em.hal\n\ninterface IntrVecI\n\n type ExceptionHandler: function(vecNum: uint32, retAddr: addr_t)\n\n host function bindExceptionHandlerH(handler: ExceptionHandler)\n\nend\n
"},{"location":"cargo/em.core/em.hal/LedI/","title":"LedI","text":""},{"location":"cargo/em.core/em.hal/LedI/#unit-ledi","title":"unit LedI","text":"em.hal/LedI.empackage em.hal\n\ninterface LedI\n # ^| abstraction of an LED\n function isOn(): bool\n # ^| test if the LED is on\n function off()\n # ^| turn the LED off\n function on()\n # ^| turn the LED on\n function toggle()\n # ^| toggle the LED\n function wink(msecs: uint16)\n # ^| turn the LED on/off\n # ^| @msecs duration in milliseconds\n end\n
"},{"location":"cargo/em.core/em.hal/LedN/","title":"LedN","text":""},{"location":"cargo/em.core/em.hal/LedN/#unit-ledn","title":"unit LedN","text":"em.hal/LedN.empackage em.hal\n\nimport LedI\n\nmodule LedN: LedI\n # ^| Nil implementation of the LedI interface\nend\n\ndef isOn()\n return false\nend\n\ndef on()\nend\n\ndef off()\nend\n\ndef toggle()\nend\n\ndef wink(usecs)\nend\n
"},{"location":"cargo/em.core/em.hal/McuI/","title":"McuI","text":""},{"location":"cargo/em.core/em.hal/McuI/#unit-mcui","title":"unit McuI","text":"em.hal/McuI.empackage em.hal\n\n#! Implemented by an Mcu module\n\ninterface McuI \n\n const ADMIN_RESET: int8 = -1\n const HOST_RESET: int8 = -2\n const COLD_RESET: int8 = -3\n const FIRST_RESET: int8 = -4\n\n config mclkFrequency: uint32\n\n function getResetCode(): int8\n function getStashAddr(): ptr_t\n function isWarm(): bool\n function readEui48(dst: uint8*)\n\n #! Perform startup and shutdown operations specific for a particular Mcu\n function reset(code: int8 = 0)\n function startup()\n function shutdown()\nend\n
"},{"location":"cargo/em.core/em.hal/McuInfoI/","title":"McuInfoI","text":""},{"location":"cargo/em.core/em.hal/McuInfoI/#unit-mcuinfoi","title":"unit McuInfoI","text":"em.hal/McuInfoI.empackage em.hal\n\ninterface McuInfoI\n\n function readBatMv(): uint16\n function readTempC(): int8\n\nend\n
"},{"location":"cargo/em.core/em.hal/McuInfoN/","title":"McuInfoN","text":""},{"location":"cargo/em.core/em.hal/McuInfoN/#unit-mcuinfon","title":"unit McuInfoN","text":"em.hal/McuInfoN.empackage em.hal\n\nimport McuInfoI\n\nmodule McuInfoN: McuInfoI\n # ^| Nil implementation of the McuInfoI interface\nend\n\ndef readBatMv()\n return 0\nend\n\ndef readTempC()\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/McuN/","title":"McuN","text":""},{"location":"cargo/em.core/em.hal/McuN/#unit-mcun","title":"unit McuN","text":"em.hal/McuN.empackage em.hal\n\nimport McuI\n\nmodule McuN: McuI\n\nend\n\ndef getResetCode()\n return 0\nend\n\ndef getStashAddr()\n return null\nend\n\ndef isWarm()\n return false\nend\n\ndef readEui48(dst)\nend\n\ndef reset(code)\nend\n\ndef startup()\nend\n\ndef shutdown()\nend\n
"},{"location":"cargo/em.core/em.hal/MsCounterI/","title":"MsCounterI","text":""},{"location":"cargo/em.core/em.hal/MsCounterI/#unit-mscounteri","title":"unit MsCounterI","text":"em.hal/MsCounterI.empackage em.hal\n\ninterface MsCounterI\n\n function start()\n function stop(): uint32\n\nend\n
"},{"location":"cargo/em.core/em.hal/MsCounterN/","title":"MsCounterN","text":""},{"location":"cargo/em.core/em.hal/MsCounterN/#unit-mscountern","title":"unit MsCounterN","text":"em.hal/MsCounterN.empackage em.hal\n\nimport MsCounterI\n\nmodule MsCounterN: MsCounterI\n # ^| Nil implementation of the MsCounterI interface\nend\n\ndef start()\nend\n\ndef stop()\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/OneShotMilliI/","title":"OneShotMilliI","text":""},{"location":"cargo/em.core/em.hal/OneShotMilliI/#unit-oneshotmillii","title":"unit OneShotMilliI","text":"em.hal/OneShotMilliI.em package em.hal\n\ninterface OneShotMilliI\n # ^| abstraction of a one-shot timer with millisecond resolution\n type Handler: function(arg: ptr_t)\n # ^| handler function signature\n function disable()\n # ^| disables the timer\n function enable(msecs: uint32, handler: Handler, arg: ptr_t = null) \n # ^| enables the timer to expire in msecs milliseconds\n # ^| @msecs - duration in millisecs before expiration\n # ^| @handler - handler function called upon expiration \n # ^| @arg - optional value passed to the handler\nend\n
"},{"location":"cargo/em.core/em.hal/OneShotMilliN/","title":"OneShotMilliN","text":""},{"location":"cargo/em.core/em.hal/OneShotMilliN/#unit-oneshotmillin","title":"unit OneShotMilliN","text":"em.hal/OneShotMilliN.empackage em.hal\n\nimport OneShotMilliI\n\nmodule OneShotMilliN: OneShotMilliI\n # ^| Nil implementation of the OneShotMilliI interface\nend\n\ndef disable()\nend\n\ndef enable(msecs, handler, arg)\nend\n
"},{"location":"cargo/em.core/em.hal/PollerI/","title":"PollerI","text":""},{"location":"cargo/em.core/em.hal/PollerI/#unit-polleri","title":"unit PollerI","text":"em.hal/PollerI.empackage em.hal\n\ninterface PollerI\n # ^| abstration of periodic polling\n type PollFxn: function(): bool\n # ^| signature of a boolean-valued polling function\n function poll(rateMs: uint16, count: uint16, fxn: PollFxn): uint16 \n # ^| initiates a polling sequence\n # ^| @rateMs - idle time in milliseconds between pollings\n # ^| @count - maximum number of polling attempts\n # ^| @fxn - the polling function itself\n # ^| @return - the number of polling attempts remaining (success if >0)\nend\n
"},{"location":"cargo/em.core/em.hal/PollerN/","title":"PollerN","text":""},{"location":"cargo/em.core/em.hal/PollerN/#unit-pollern","title":"unit PollerN","text":"em.hal/PollerN.empackage em.hal\n\nimport PollerI\n\nmodule PollerN: PollerI\n # ^| Nil implementation of the PollerI interface\nend\n\ndef poll(rateMs, count, fxn)\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/RandI/","title":"RandI","text":""},{"location":"cargo/em.core/em.hal/RandI/#unit-randi","title":"unit RandI","text":"em.hal/RandI.empackage em.hal\n\ninterface RandI\n\n function gen(): uint32\n\nend\n
"},{"location":"cargo/em.core/em.hal/RandN/","title":"RandN","text":""},{"location":"cargo/em.core/em.hal/RandN/#unit-randn","title":"unit RandN","text":"em.hal/RandN.empackage em.hal\n\nimport RandI\n\nmodule RandN: RandI\n # ^| Nil implementation of the RandI interface\nend\n\ndef gen()\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/SpiMasterI/","title":"SpiMasterI","text":""},{"location":"cargo/em.core/em.hal/SpiMasterI/#unit-spimasteri","title":"unit SpiMasterI","text":"em.hal/SpiMasterI.empackage em.hal\n\ninterface SpiMasterI\n\n function activate()\n function deactivate()\n function flush()\n function get(): uint8\n function put(data: uint8)\n\nend\n
"},{"location":"cargo/em.core/em.hal/TimeoutI/","title":"TimeoutI","text":""},{"location":"cargo/em.core/em.hal/TimeoutI/#unit-timeouti","title":"unit TimeoutI","text":"em.hal/TimeoutI.empackage em.hal\n\ninterface TimeoutI\n\n function active(): bool\n function cancel()\n function set(msecs: uint32)\n\nend\n
"},{"location":"cargo/em.core/em.hal/TimeoutN/","title":"TimeoutN","text":""},{"location":"cargo/em.core/em.hal/TimeoutN/#unit-timeoutn","title":"unit TimeoutN","text":"em.hal/TimeoutN.empackage em.hal\n\nimport TimeoutI\n\nmodule TimeoutN: TimeoutI\n # ^| Nil implementation of the TimeoutI interface\nend\n\ndef active()\n return false\nend\n\ndef cancel()\nend\n\ndef set(msecs)\nend\n
"},{"location":"cargo/em.core/em.hal/UptimerI/","title":"UptimerI","text":""},{"location":"cargo/em.core/em.hal/UptimerI/#unit-uptimeri","title":"unit UptimerI","text":"em.hal/UptimerI.empackage em.hal\n\ninterface UptimerI\n\n type Time: struct\n secs: uint32\n subs: uint32\n ticks: uint32\n end\n\n function calibrate(secs256: uint32, ticks: uint32): uint16\n function read(): Time&\n function resetSync()\n function trim(): uint16\n\nend\n
"},{"location":"cargo/em.core/em.hal/UptimerN/","title":"UptimerN","text":""},{"location":"cargo/em.core/em.hal/UptimerN/#unit-uptimern","title":"unit UptimerN","text":"em.hal/UptimerN.empackage em.hal\n\nimport UptimerI\n\nmodule UptimerN: UptimerI\n # ^| Nil implementation of the UptimerI interface\nend\n\ndef calibrate(secs256, ticks)\n return 0\nend\n\ndef read()\n return null\nend\n\ndef resetSync()\nend\n\ndef trim()\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/UsCounterI/","title":"UsCounterI","text":""},{"location":"cargo/em.core/em.hal/UsCounterI/#unit-uscounteri","title":"unit UsCounterI","text":"em.hal/UsCounterI.empackage em.hal\n\ninterface UsCounterI\n\n function start()\n function stop(): uint32\n\nend\n
"},{"location":"cargo/em.core/em.hal/UsCounterN/","title":"UsCounterN","text":""},{"location":"cargo/em.core/em.hal/UsCounterN/#unit-uscountern","title":"unit UsCounterN","text":"em.hal/UsCounterN.empackage em.hal\n\nimport UsCounterI\n\nmodule UsCounterN: UsCounterI\n\nend\n\ndef start()\nend\n\ndef stop()\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/UsThreshI/","title":"UsThreshI","text":""},{"location":"cargo/em.core/em.hal/UsThreshI/#unit-usthreshi","title":"unit UsThreshI","text":"em.hal/UsThreshI.empackage em.hal\n\ninterface UsThreshI\n\n function pause()\n function set(usecs: uint16)\n\nend\n
"},{"location":"cargo/em.core/em.hal/WakeupTimerI/","title":"WakeupTimerI","text":""},{"location":"cargo/em.core/em.hal/WakeupTimerI/#unit-wakeuptimeri","title":"unit WakeupTimerI","text":"em.hal/WakeupTimerI.empackage em.hal\n\ninterface WakeupTimerI\n # ^| abstraction of a free-running wakeup-timer (RTC)\n type Handler: function()\n # ^| handler function signature\n function disable()\n # ^| disables any pending wakeup from the timer\n function enable(thresh: uint32, handler: Handler)\n # ^| enables a future wakeup from the timer\n # ^| @thresh - an internal timer threshold value\n # ^| @handler - the function called when reaching the threshold\n function secs256ToTicks(secs256: uint32): uint32\n # ^| converts secs256 to logical timer ticks\n function ticksToThresh(ticks: uint32): uint32\n # ^| converts timer ticks to an internal timer threshold value\n function timeToTicks(secs: uint32, subs: uint32): uint32\n # ^| converts secs+subs time value to logical timer ticks\n # ^| @secs - the seconds component of the time value\n # ^| @subs - the sub-seconds component of the time value\n # ^| @return - time value represented as logic timer ticks\nend\n
"},{"location":"cargo/em.core/em.hal/WakeupTimerN/","title":"WakeupTimerN","text":""},{"location":"cargo/em.core/em.hal/WakeupTimerN/#unit-wakeuptimern","title":"unit WakeupTimerN","text":"em.hal/WakeupTimerN.empackage em.hal\n\nimport WakeupTimerI\n\nmodule WakeupTimerN: WakeupTimerI\n # ^| Nil implementation of the WakeupTimerI interface\nend\n\ndef disable()\nend\n\ndef enable(thresh, handler)\nend\n\ndef secs256ToTicks(secs256)\n return 0\nend\n\ndef ticksToThresh(ticks)\n return 0\nend\n\ndef timeToTicks(secs, subs)\n return 0\nend\n
"},{"location":"cargo/em.core/em.hal/WatchdogI/","title":"WatchdogI","text":""},{"location":"cargo/em.core/em.hal/WatchdogI/#unit-watchdogi","title":"unit WatchdogI","text":"em.hal/WatchdogI.empackage em.hal\n\ninterface WatchdogI\n\n type Handler: function()\n\n function didBite(): bool\n function disable()\n function enable(secs: uint16, handler: Handler)\n function pet()\n\nend\n
"},{"location":"cargo/em.core/em.hal/WatchdogN/","title":"WatchdogN","text":""},{"location":"cargo/em.core/em.hal/WatchdogN/#unit-watchdogn","title":"unit WatchdogN","text":"em.hal/WatchdogN.empackage em.hal\n\nimport WatchdogI\n\nmodule WatchdogN: WatchdogI\n # ^| Nil implementation of the WatchdogI interface\nend\n\ndef didBite()\n return false\nend\n\ndef disable()\nend\n\ndef enable(secs, handler)\nend\n\ndef pet()\nend\n
"},{"location":"cargo/em.core/em.lang/","title":"Index","text":""},{"location":"cargo/em.core/em.lang/#package-emlang","title":"package em.lang","text":""},{"location":"cargo/em.core/em.lang/Assert/","title":"Assert","text":""},{"location":"cargo/em.core/em.lang/Assert/#unit-assert","title":"unit Assert","text":"em.lang/Assert.empackage em.lang\n\nimport AssertProviderI\nimport AssertProviderN\n\nmodule Assert\n\n proxy Provider: AssertProviderI\n\n function enabled(): bool\n function trigger(upath: atom_t, line: uint16, msg: atom_t = 0, arg1: iarg_t = 0, arg2: iarg_t = 0)\n\nend\n\ndef em$configure()\n Provider ?= AssertProviderN\nend\n\ndef enabled()\n return Provider.enabled()\nend\n\ndef trigger(upath, line, msg, arg1, arg2)\n Provider.trigger(upath, line, msg, arg1, arg2) if enabled()\nend\n
"},{"location":"cargo/em.core/em.lang/AssertProviderI/","title":"AssertProviderI","text":""},{"location":"cargo/em.core/em.lang/AssertProviderI/#unit-assertprovideri","title":"unit AssertProviderI","text":"em.lang/AssertProviderI.empackage em.lang\n\ninterface AssertProviderI\n\n function enabled(): bool\n function trigger(upath: atom_t, line: uint16, msg: atom_t, arg1: iarg_t, arg2: iarg_t)\n\nend\n
"},{"location":"cargo/em.core/em.lang/AssertProviderN/","title":"AssertProviderN","text":""},{"location":"cargo/em.core/em.lang/AssertProviderN/#unit-assertprovidern","title":"unit AssertProviderN","text":"em.lang/AssertProviderN.empackage em.lang\n\nimport AssertProviderI\n\nmodule AssertProviderN: AssertProviderI\n\nend\n\ndef enabled()\n return false\nend\n\ndef trigger(upath, line, msg, arg1, arg2)\nend\n
"},{"location":"cargo/em.core/em.lang/Atom/","title":"Atom","text":""},{"location":"cargo/em.core/em.lang/Atom/#unit-atom","title":"unit Atom","text":"em.lang/Atom.empackage em.lang\n\nmodule Atom\n\n config NULL_A: atom_t = @\"<<null>>\"\n config UNDEFINED_A: atom_t = @\"<<undefined>>\"\n\n function fromString(str: string, oatom: atom_t*): bool\n function hasTable(): bool\n function toString(atom: atom_t): string\n\nprivate:\n\n const MASK: addr_t = 0x80000000\n\n config tableFlag: bool\n\nend\n\ndef em$construct()\n tableFlag = ^^!!em$props.get(em$session.PROP_ATOM_TABLE)^^\nend\n\ndef fromString(str, oatom)\n auto saddr = <addr_t>str\n return false if tableFlag || (saddr & MASK) == 0\n *oatom = <atom_t>saddr\n return true\nend\n\ndef hasTable()\n return tableFlag\nend\n\ndef toString(atom)\n return tableFlag ? ^^em$atoms[atom]^^ : <string>((<addr_t>atom) | MASK)\nend\n
"},{"location":"cargo/em.core/em.lang/BuildC/","title":"BuildC","text":""},{"location":"cargo/em.core/em.lang/BuildC/#unit-buildc","title":"unit BuildC","text":"em.lang/BuildC.empackage em.lang\n\ncomposite BuildC\n\n config arch: string\n config bootFlash: bool\n config bootLoader: bool\n config compiler: string\n config cpu: string\n config jlinkDev: string\n config mcu: string\n config optimize: string\n\nprivate:\n\n var curPval: string\n\n function getProp(pname: string): string\n\nend\n\ndef em$preconfigure()\n arch ?= curPval if getProp(\"em.build.Arch\")\n bootFlash ?= true if getProp(\"em.build.BootFlash\")\n bootLoader ?= true if getProp(\"em.lang.BootLoader\")\n compiler ?= curPval if getProp(\"em.build.Compiler\")\n cpu ?= curPval if getProp(\"em.build.Cpu\")\n jlinkDev ?= curPval if getProp(\"em.build.JlinkDev\")\n mcu ?= curPval if getProp(\"em.build.Mcu\")\n optimize ?= curPval if getProp(\"em.build.Optimize\")\nend\n\ndef getProp(pname)\n return curPval = ^^em$props.get^^(pname, null)\nend\n
"},{"location":"cargo/em.core/em.lang/BuilderI/","title":"BuilderI","text":""},{"location":"cargo/em.core/em.lang/BuilderI/#unit-builderi","title":"unit BuilderI","text":"em.lang/BuilderI.empackage em.lang\n\nhost interface BuilderI\n\n type CompileInfo: struct\n errMsgs: string[]\n imageSizes: string\n procStat: int8\n end\n\n type TypeInfoDesc: uint8[2]\n\n type TypeInfo: struct\n ARG: TypeInfoDesc\n CHAR: TypeInfoDesc\n INT: TypeInfoDesc\n INT8: TypeInfoDesc\n INT16: TypeInfoDesc\n INT32: TypeInfoDesc\n LONG: TypeInfoDesc\n PTR: TypeInfoDesc\n SHORT: TypeInfoDesc\n SIZE: TypeInfoDesc\n end\n\n function compile(buildDir: string): CompileInfo&\n function getTypeInfo(): TypeInfo&\n function populate(buildDir: string, sysFlag: bool)\n\nend\n
"},{"location":"cargo/em.core/em.lang/CompositeI/","title":"CompositeI","text":""},{"location":"cargo/em.core/em.lang/CompositeI/#unit-compositei","title":"unit CompositeI","text":"em.lang/CompositeI.empackage em.lang\n\ninterface CompositeI\n\n host function em$configure()\n host function em$preconfigure()\n\nend\n
"},{"location":"cargo/em.core/em.lang/Console/","title":"Console","text":""},{"location":"cargo/em.core/em.lang/Console/#unit-console","title":"unit Console","text":"em.lang/Console.empackage em.lang\n\nimport ConsoleProviderI\n\nmodule Console\n\n proxy Provider: ConsoleProviderI\n\n config noPrint: bool\n\n function print(fmt: string, a1: iarg_t = 0, a2: iarg_t = 0, a3: iarg_t = 0, a4: iarg_t = 0, a5: iarg_t = 0, a6: iarg_t = 0)\n\n function wrC(data: char)\n function wrN(data: num_t)\n function wrP(data: ptr_t)\n\n function wrT(data: string)\n\n function wrIA(data: iarg_t)\n function wrUA(data: uarg_t)\n\n function wrI8(data: int8)\n function wrI16(data: int16)\n function wrI32(data: int32)\n\n function wrU8(data: uint8)\n function wrU16(data: uint16)\n function wrU32(data: uint32)\n\nend\n\ndef em$generateCode(prefix)\n |-> #define em$print em_lang_Console::print\nend\n\ndef print(fmt, a1, a2, a3, a4, a5, a6)\n if !noPrint\n Provider.print(fmt, a1, a2, a3, a4, a5, a6)\n end\nend\n\ndef wrC(data)\n Provider.put(data)\nend\n\ndef wrN(data)\n Provider.put(0x8F)\n auto ba = <uint8[]>(&data)\n for auto i = 0; i < sizeof<num_t>; i++\n Provider.put(ba[i])\n end\n Provider.flush()\nend\n\ndef wrP(data)\n wrU32(<uint32>data)\nend\n\ndef wrT(data)\n Provider.put(0x80)\n auto cp = <char*>data\n for ;;\n auto ch = *cp++\n wrC(ch)\n return if ch == 0 \n end\nend\n\ndef wrIA(data)\n wrU16(<uint16>data)\nend\n\ndef wrUA(data)\n wrU16(<uint16>data)\nend\n\ndef wrI8(data)\n wrU8(<uint8>data)\nend\n\ndef wrI16(data)\n wrU16(<uint16>data)\nend\n\ndef wrI32(data)\n wrU32(<uint32>data)\nend\n\ndef wrU8(data)\n Provider.put(0x81)\n Provider.put(data)\n Provider.flush()\nend\n\ndef wrU16(data)\n Provider.put(0x82)\n auto b = <uint8> ((data >> 8) & 0xFF)\n Provider.put(b)\n b = <uint8> ((data >> 0) & 0xFF)\n Provider.put(b)\n Provider.flush()\nend\n\ndef wrU32(data)\n Provider.put(0x84)\n auto b = <uint8> ((data >> 24) & 0xFF)\n Provider.put(b)\n b = <uint8> ((data >> 16) & 0xFF)\n Provider.put(b)\n b = <uint8> ((data >> 8) & 0xFF)\n Provider.put(b)\n b = <uint8> ((data >> 0) & 0xFF)\n Provider.put(b)\n Provider.flush()\nend\n
"},{"location":"cargo/em.core/em.lang/ConsoleProviderI/","title":"ConsoleProviderI","text":""},{"location":"cargo/em.core/em.lang/ConsoleProviderI/#unit-consoleprovideri","title":"unit ConsoleProviderI","text":"em.lang/ConsoleProviderI.empackage em.lang\n\ninterface ConsoleProviderI\n\n function flush()\n function print(fmt: string, a1: iarg_t = 0, a2: iarg_t = 0, a3: iarg_t = 0, a4: iarg_t = 0, a5: iarg_t = 0, a6: iarg_t = 0)\n function put(data: uint8)\n\nend\n
"},{"location":"cargo/em.core/em.lang/ConsoleProviderN/","title":"ConsoleProviderN","text":""},{"location":"cargo/em.core/em.lang/ConsoleProviderN/#unit-consoleprovidern","title":"unit ConsoleProviderN","text":"em.lang/ConsoleProviderN.empackage em.lang\n\nimport ConsoleProviderI\n\nmodule ConsoleProviderN: ConsoleProviderI\n\nend\n\ndef flush()\nend\n\ndef print(fmt, a1, a2, a3, a4, a5, a6)\nend\n\ndef put(data)\nend\n
"},{"location":"cargo/em.core/em.lang/Debug/","title":"Debug","text":""},{"location":"cargo/em.core/em.lang/Debug/#unit-debug","title":"unit Debug","text":"em.lang/Debug.empackage em.lang\n\nimport DebugPinI\n\nmodule Debug\n\n proxy Pin_a: DebugPinI\n proxy Pin_b: DebugPinI\n proxy Pin_c: DebugPinI\n proxy Pin_d: DebugPinI\n\n function getNumPins(): uint8\n function sleepEnter()\n function sleepLeave()\n function startup()\n\nprivate:\n\nend\n\ndef em$configure()\n em$used ?= true\nend\n\ndef getNumPins()\n return 4\nend\n\ndef sleepEnter()\n Pin_a.reset()\n Pin_b.reset()\n Pin_c.reset()\n Pin_d.reset()\nend\n\ndef sleepLeave()\n startup()\nend\n\ndef startup()\n Pin_a.startup()\n Pin_b.startup()\n Pin_c.startup()\n Pin_d.startup()\nend\n
"},{"location":"cargo/em.core/em.lang/DebugPinI/","title":"DebugPinI","text":""},{"location":"cargo/em.core/em.lang/DebugPinI/#unit-debugpini","title":"unit DebugPinI","text":"em.lang/DebugPinI.empackage em.lang\n\ninterface DebugPinI\n\n function clear()\n function get(): bool\n function set()\n function toggle()\n function pulse()\n function mark(k: uint8 = 0)\n function reset()\n function startup()\n\nend\n
"},{"location":"cargo/em.core/em.lang/DebugPinN/","title":"DebugPinN","text":""},{"location":"cargo/em.core/em.lang/DebugPinN/#unit-debugpinn","title":"unit DebugPinN","text":"em.lang/DebugPinN.empackage em.lang\n\nimport DebugPinI\n\nmodule DebugPinN: DebugPinI\n\nend\n\ndef clear()\nend\n\ndef get()\n return false\nend\n\ndef set()\nend\n\ndef toggle()\nend\n\ndef pulse()\nend\n\ndef mark(k)\nend\n\ndef reset()\nend\n\ndef startup()\nend\n
"},{"location":"cargo/em.core/em.lang/Math/","title":"Math","text":""},{"location":"cargo/em.core/em.lang/Math/#unit-math","title":"unit Math","text":"em.lang/Math.empackage em.lang\n\n#! This module is only available for use on the host.\n#! It contains some standard math functions.\n\nhost module Math\n\n config PI: num_t\n\n #! Returns the absolute value of x\n function abs(x: num_t): num_t\n\n #! Returns the smallest integer greater than or equal to x\n function ceil(x: num_t): num_t\n\n #! Returns the cos of x\n function cos(x: num_t): num_t\n\n #! Returns the largest integer less than or equal to x\n function floor(x: num_t): num_t\n\n #! Returns the logarithm (base 10) of x\n function log2(x: num_t): num_t\n\n #! Returns the logarithm (base 10) of x\n function log10(x: num_t): num_t\n\n #! Returns the value of x to the y power\n function pow(x: num_t, y: num_t): num_t\n\n #! Returns the rounded value of x\n function round(x: num_t): num_t\n\n #! Returns the sin of x\n function sin(x: num_t): num_t\n\nend\n\ndef em$configure()\n PI ?= ^^global.Math.PI^^\nend\n\ndef abs(x)\n return ^^global.Math.abs(x)^^\nend\n\n# If x = 19.1 this function will return 20\ndef ceil(x)\n return ^^global.Math.ceil(x)^^\nend\n\ndef cos(x)\n return ^^global.Math.cos(x)^^\nend\n\n# If x = 19.6 this function will return 19\ndef floor(x)\n return ^^global.Math.floor(x)^^\nend\n\ndef log2(x)\n return ^^global.Math.log2(x)^^\nend\n\ndef log10(x)\n return ^^global.Math.LOG10E * global.Math.log(x)^^\nend\n\ndef pow(x, y)\n return ^^global.Math.pow(x, y)^^\nend\n\ndef round(x)\n return ^^global.Math.round(x)^^\nend\n\ndef sin(x)\n return ^^global.Math.sin(x)^^\nend\n
"},{"location":"cargo/em.core/em.lang/ModuleI/","title":"ModuleI","text":""},{"location":"cargo/em.core/em.lang/ModuleI/#unit-modulei","title":"unit ModuleI","text":"em.lang/ModuleI.empackage em.lang\n\ninterface ModuleI\n\n type io32_t: uint32 volatile*\n\n host config em$exclude: bool\n host config em$export: bool\n host config em$traceGrp: string\n host config em$used: bool\n\n config em$tracePri: uint8\n\n host function em$configure()\n host function em$construct()\n template em$generateCode(prefix: string)\n\n function em$fail()\n function em$halt()\n function em$reset()\n function em$run()\n function em$shutdown()\n function em$startup()\n function em$startupDone()\n\n host function em$uses__()\n\nend\n
"},{"location":"cargo/em.core/em.lang/RunC/","title":"RunC","text":""},{"location":"cargo/em.core/em.lang/RunC/#unit-runc","title":"unit RunC","text":"em.lang/RunC.empackage em.lang\n\nimport Console\nimport ConsoleProviderN\n\nimport Debug\nimport DebugPinN\n\ncomposite RunC\n\nend\n\ndef em$configure()\n Console.em$used ?= true\n Console.Provider ?= ConsoleProviderN\n Debug.em$used ?= true\n Debug.Pin_a ?= DebugPinN\n Debug.Pin_b ?= DebugPinN\n Debug.Pin_c ?= DebugPinN\n Debug.Pin_d ?= DebugPinN\nend\n
"},{"location":"cargo/em.core/em.lang/TemplateI/","title":"TemplateI","text":""},{"location":"cargo/em.core/em.lang/TemplateI/#unit-templatei","title":"unit TemplateI","text":"em.lang/TemplateI.empackage em.lang\n\nhost interface TemplateI \n\n function em$cacheDirty(ctimeMs: num_t): bool\n template em$generateUnit(pkgName: string, unitName: string)\n\nend\n
"},{"location":"cargo/em.core/em.mcu/","title":"Index","text":""},{"location":"cargo/em.core/em.mcu/#package-emmcu","title":"package em.mcu","text":""},{"location":"cargo/em.core/em.mcu/Common/","title":"Common","text":""},{"location":"cargo/em.core/em.mcu/Common/#unit-common","title":"unit Common","text":"em.mcu/Common.empackage em.mcu\n\nfrom em.hal import BusyWaitI\nfrom em.hal import GlobalInterruptsI\nfrom em.hal import IdleI\nfrom em.hal import McuI\nfrom em.hal import MsCounterI\nfrom em.hal import RandI\nfrom em.hal import UsCounterI\nfrom em.hal import WatchdogI\n\nmodule Common\n # ^| collection of proxies implementing MCU abstractions\n proxy BusyWait: BusyWaitI\n proxy GlobalInterrupts: GlobalInterruptsI\n proxy Idle: IdleI \n proxy Mcu: McuI \n proxy MsCounter: MsCounterI \n proxy Rand: RandI\n proxy UsCounter: UsCounterI \n proxy Watchdog: WatchdogI\n\nend\n
"},{"location":"cargo/em.core/em.mcu/CommonC/","title":"CommonC","text":""},{"location":"cargo/em.core/em.mcu/CommonC/#unit-commonc","title":"unit CommonC","text":"em.mcu/CommonC.empackage em.mcu\n\nfrom em.hal import BusyWaitN\nfrom em.hal import GlobalInterruptsN\nfrom em.hal import IdleN\nfrom em.hal import McuN\nfrom em.hal import MsCounterN\nfrom em.hal import RandN\nfrom em.hal import UsCounterN\nfrom em.hal import WatchdogN\n\nfrom em.mcu import Common\n\ncomposite CommonC\n\nend\n\ndef em$configure()\n Common.BusyWait ?= BusyWaitN\n Common.GlobalInterrupts ?= GlobalInterruptsN\n Common.Idle ?= IdleN\n Common.Mcu ?= McuN\n Common.MsCounter ?= MsCounterN\n Common.Rand ?= RandN\n Common.UsCounter ?= UsCounterN\n Common.Watchdog ?= WatchdogN\nend\n
"},{"location":"cargo/em.core/em.mcu/ConsoleUart/","title":"ConsoleUart","text":""},{"location":"cargo/em.core/em.mcu/ConsoleUart/#unit-consoleuart","title":"unit ConsoleUart","text":"em.mcu/ConsoleUart.empackage em.mcu\n\nfrom em.hal import ConsoleUartI\nfrom em.hal import ConsoleUartN\n\nmodule ConsoleUart: ConsoleUartI\n\n proxy Impl: ConsoleUartI\n\nend\n\ndef em$configure()\n Impl ?= ConsoleUartN\nend\n\ndef setBaudH(rate)\n Impl.setBaudH(rate)\nend\n\ndef flush()\n Impl.flush()\nend\n\ndef put(data)\n Impl.put(data)\nend\n
"},{"location":"cargo/em.core/em.mcu/Copier/","title":"Copier","text":""},{"location":"cargo/em.core/em.mcu/Copier/#unit-copier","title":"unit Copier","text":"em.mcu/Copier.empackage em.mcu\n\nfrom em.hal import CopierI\n\nmodule Copier: CopierI\n\n proxy Impl: CopierI\n\nend\n\ndef exec(dst, src, cnt)\n Impl.exec(dst, src, cnt)\nend\n
"},{"location":"cargo/em.core/em.mcu/Info/","title":"Info","text":""},{"location":"cargo/em.core/em.mcu/Info/#unit-info","title":"unit Info","text":"em.mcu/Info.empackage em.mcu\n\nfrom em.hal import McuInfoI\n\nmodule Info: McuInfoI\n\n proxy Impl: McuInfoI\n\nend\n\ndef readBatMv()\n return Impl.readBatMv()\nend\n\ndef readTempC()\n return Impl.readTempC()\nend\n
"},{"location":"cargo/em.core/em.mcu/Poller/","title":"Poller","text":""},{"location":"cargo/em.core/em.mcu/Poller/#unit-poller","title":"unit Poller","text":"em.mcu/Poller.empackage em.mcu\n\nfrom em.hal import PollerN\n\nfrom em.hal import PollerI\n\nmodule Poller: PollerI\n\n proxy Impl: PollerI\n\n function pause(timeMs: uint16)\n\nend\n\ndef em$configure()\n Impl ?= PollerN\nend\n\ndef pause(timeMs)\n Impl.poll(timeMs, 1, null)\nend\n\ndef poll(rateMs, count, fxn)\n return Impl.poll(rateMs, count, fxn)\nend\n
"},{"location":"cargo/em.core/em.mcu/Timeout/","title":"Timeout","text":""},{"location":"cargo/em.core/em.mcu/Timeout/#unit-timeout","title":"unit Timeout","text":"em.mcu/Timeout.empackage em.mcu\n\nfrom em.hal import TimeoutI\n\nmodule Timeout: TimeoutI\n\n proxy Impl: TimeoutI\n\nend\n\ndef active()\n return Impl.active()\nend\n\ndef cancel()\n Impl.cancel()\nend\n\ndef set(msecs)\n Impl.set(msecs)\nend\n
"},{"location":"cargo/em.core/em.utils/","title":"Index","text":""},{"location":"cargo/em.core/em.utils/#package-emutils","title":"package em.utils","text":""},{"location":"cargo/em.core/em.utils/AlarmMgr/","title":"AlarmMgr","text":""},{"location":"cargo/em.core/em.utils/AlarmMgr/#unit-alarmmgr","title":"unit AlarmMgr","text":"em.utils/AlarmMgr.empackage em.utils\n\nfrom em.hal import WakeupTimerI\n\nimport EpochTime\nimport FiberMgr\n\nmodule AlarmMgr\n # ^|\n proxy WakeupTimer: WakeupTimerI\n # ^|\n type Alarm: opaque\n # ^| \n host function initH(fiber: FiberMgr.Fiber&)\n # ^| \n function active(): bool\n # ^| \n function cancel()\n # ^| \n function wakeup(secs256: uint32)\n # ^| \n function wakeupAt(secs256: uint32)\n # ^| \n end\n\n host function createH(fiber: FiberMgr.Fiber&): Alarm&\n # ^|\nprivate:\n\n def opaque Alarm\n fiber: FiberMgr.Fiber&\n thresh: uint32\n ticks: uint32\n function setup(ticks: uint32)\n end\n\n function update(deltaTicks: uint32)\n function wakeupHandler: WakeupTimer.Handler\n\n var alarmTab: Alarm[..]\n var curAlarm: Alarm&\n\nend\n\ndef createH(fiber)\n var alarm: Alarm& = alarmTab[alarmTab.length++]\n alarm.initH(fiber)\n return alarm\nend\n\ndef update(deltaTicks)\n WakeupTimer.disable()\n auto nxtAlarm = <Alarm&>null\n var maxTicks: uint32 = ~0 # largest uint32\n for a in alarmTab\n continue if a.ticks == 0 # inactive alarm\n a.ticks -= deltaTicks\n if a.ticks == 0 # expired alarm\n a.fiber.post()\n elif a.ticks < maxTicks\n nxtAlarm = a\n maxTicks = a.ticks \n end\n end\n return if nxtAlarm == null # no active alarms\n curAlarm = nxtAlarm\n WakeupTimer.enable(curAlarm.thresh, wakeupHandler)\nend\n\ndef wakeupHandler()\n update(curAlarm.ticks)\nend\n\ndef Alarm.initH(fiber)\n this.fiber = fiber\n this.ticks = 0\nend\n\ndef Alarm.active()\n return this.ticks != 0\nend\n\ndef Alarm.cancel()\n this.ticks = 0\n update(0)\nend\n\ndef Alarm.setup(ticks)\n this.thresh = WakeupTimer.ticksToThresh(ticks)\n this.ticks = ticks\n update(0)\nend\n\ndef Alarm.wakeup(secs256)\n auto ticks = WakeupTimer.secs256ToTicks(secs256)\n this.setup(ticks)\nend\n\ndef Alarm.wakeupAt(secs256)\n var etSubs: uint32\n auto etSecs = EpochTime.getCurrent(&etSubs)\n auto etTicks = WakeupTimer.timeToTicks(etSecs, etSubs)\n auto ticks = WakeupTimer.secs256ToTicks(secs256)\n this.setup(ticks - (etTicks % ticks))\nend\n
"},{"location":"cargo/em.core/em.utils/AppControl/","title":"AppControl","text":""},{"location":"cargo/em.core/em.utils/AppControl/#unit-appcontrol","title":"unit AppControl","text":"em.utils/AppControl.empackage em.utils\n\nfrom em.mcu import Common\n\nimport BoardController\nimport EpochTime\n\nmodule AppControl\n\n function restart(status: int8)\n\nprivate:\n\n type Stash: struct\n secs: uint32\n subs: uint32\n end\n\n function getStash(): Stash&\n function doReset(code: int8)\n\nend\n\ndef em$startup()\n return if Common.Mcu.isWarm() || Common.Mcu.getResetCode() < 0\n auto stash = getStash()\n EpochTime.setCurrent(stash.secs, stash.subs, false)\nend\n\ndef em$fail()\n BoardController.em$fail()\nend\n\ndef em$halt()\n BoardController.em$halt()\nend\n\ndef doReset(code)\n auto stash = getStash()\n stash.secs = EpochTime.getCurrent(&stash.subs)\n Common.Mcu.reset(code) \nend\n\ndef getStash()\n return Common.Mcu.getStashAddr()\nend\n\ndef restart(status)\n doReset(status)\nend\n
"},{"location":"cargo/em.core/em.utils/AssertProvider/","title":"AssertProvider","text":""},{"location":"cargo/em.core/em.utils/AssertProvider/#unit-assertprovider","title":"unit AssertProvider","text":"em.utils/AssertProvider.empackage em.utils\n\nfrom em.lang import AssertProviderI\n\nimport Error\nimport Logger\n\nmodule AssertProvider: AssertProviderI\n\nprivate:\n\n config assertMsgE: Logger.EventKind&\n config assertSiteE: Logger.EventKind&\n\nend\n\ndef em$construct()\n assertMsgE = Logger.declareEventH(\"ASSERT: $F\", \"*--*\")\n assertSiteE = Logger.declareEventH(\"ASSERT: $a, line %d\", \"*--*\")\nend\n\ndef enabled()\n return Logger.POLICY != Logger.Policy.NIL\nend\n\ndef trigger(upath, line, msg, arg1, arg2)\n assertSiteE.log(<addr_t>upath, line)\n assertMsgE.log(<addr_t>msg, <addr_t>arg1, <addr_t>arg2) if msg\n Error.raise(Error.Kind.ASSERTION, 0, 0)\nend\n
"},{"location":"cargo/em.core/em.utils/BasicListManager/","title":"BasicListManager","text":""},{"location":"cargo/em.core/em.utils/BasicListManager/#unit-basiclistmanager","title":"unit BasicListManager","text":"em.utils/BasicListManager.empackage em.utils\n\nimport ListManagerI\n\n#! This module implements ListManagerI.\n#! It contains all the functions necessary to maintain a list of objects.\n\nmodule BasicListManager: ListManagerI\n\nprivate:\n\n # Representation of Element\n def opaque Element \n next: Element& volatile\n end\n\n # Representation of List\n def opaque List \n first: Element& volatile\n last: Element& volatile\n end\nend\n\n# Initially an Element is not a member of a List\ndef Element.init() \n this.next = null\nend\n\n# Initially an Element is not a member of a List\ndef Element.initH() \n this.next = null\nend\n\ndef Element.isActive() \n return <uarg_t> this.next\nend\n\n# Creates a head and tail pointer\ndef List.init() \n this.first = this.last = <Element&> &this.first\nend\n\n# Creates a head and tail pointer\ndef List.initH() \n this.first = this.last = <Element&> &this.first;\nend\n\n# The very first Element is pointed to by this.first and this.last.\n# Subsequent Elements are added to the end of the list by setting the\n# next pointer of the current last Element to the added Element then moving\n# the last pointer. \ndef List.add(elem)\n this.last.next = elem\n this.last = elem\n elem.next = <Element&> this\nend\n\n# Elements are removed from the front of the list by\n# setting a pointer equal to the this.first pointer,\n# then moving the this.first pointer to the next Element.\n# If the pointer then points to the List then that was the last\n# Element and reinitialize the list for adding Elements.\n# Otherwise just remove the Element. \ndef List.get() \n auto elem = this.first\n this.last = <Element&>this if (this.first = elem.next) == <Element&>this\n elem.next = null\n return elem\nend\n\ndef List.getAt(index)\n auto elem = this.first\n auto i = 0\n if this.hasElements()\n for ;;\n break if i++ == index \n elem = elem.next \n break if elem == <Element&> this\n end\n elem = null if elem == <Element&> this\n else \n elem = null\n end\n return elem\nend\n\n# This function returns the Element after the given Element\n# in the List, but does not remove it from the List.\ndef List.getNext(elem) \n return elem == this.last ? null : elem.next\nend\n\ndef List.hasElements() \n return (<uarg_t> this.first) ^ <uarg_t> this\nend\n\n# This function prints the index and address of each Element in the List\ndef List.print() \n auto elem = this.first\n auto i = 0\n if this.hasElements()\n for ;;\n printf \"elem%d %p\\n\", i++, elem\n elem = elem.next\n break if elem == <Element&> this\n end\n else \n printf \"list empty\\n\"\n end\n printf \"\\n\" \nend\n\n# Performs a linear search through the list to find the Element\n# then removes it, updating the appropriate pointers.\ndef List.remove(elem)\n auto e = this.first\n if this.hasElements()\n if elem == this.first\n this.first = this.first.next\n else \n for ;; \n if e.next == elem\n e.next = elem.next \n break \n end \n break if e == <Element&> this\n end\n end\n end\nend\n
"},{"location":"cargo/em.core/em.utils/BoardController/","title":"BoardController","text":""},{"location":"cargo/em.core/em.utils/BoardController/#unit-boardcontroller","title":"unit BoardController","text":"em.utils/BoardController.empackage em.utils\n\nfrom em.hal import ConsoleUartI\nfrom em.hal import LedI\n\nfrom em.mcu import Common\nfrom em.mcu import ConsoleUart\n\nimport ConsoleProtocol\n\nmodule BoardController\n\n proxy Led: LedI\n proxy Uart: ConsoleUartI\n\n config blinkRate: uint32 = 50000\n\nprivate: \n\n function blink(times: uint8, usecs: uint32)\n\nend\n\ndef em$configure()\n Uart ?= ConsoleUart\nend\n\ndef em$reset()\n Common.Mcu.startup() \nend\n\ndef em$startupDone()\n return if Common.Mcu.isWarm()\n Led.off()\n blink(2, blinkRate)\n Uart.flush()\n Uart.put(0)\n Uart.put(0)\n for auto i = 0; i < ConsoleProtocol.SOT_COUNT; i++\n Uart.put(ConsoleProtocol.SOT_BYTE)\n end\n Uart.flush()\nend\n\ndef em$halt()\n Uart.put(ConsoleProtocol.EOT_BYTE)\n Common.GlobalInterrupts.disable()\n Led.on()\n Common.Mcu.shutdown()\nend\n\ndef em$fail()\n Common.Mcu.shutdown()\n Common.GlobalInterrupts.disable()\n while true\n blink(2, blinkRate)\n for auto i = 0; i < 800; i++\n Common.BusyWait.wait(100)\n end\n end\nend\n\ndef blink(times, usecs)\n auto k = (times * 2)\n while --k\n Led.toggle()\n Common.BusyWait.wait(usecs) \n end\n Led.toggle()\nend\n
"},{"location":"cargo/em.core/em.utils/BoardDriverI/","title":"BoardDriverI","text":""},{"location":"cargo/em.core/em.utils/BoardDriverI/#unit-boarddriveri","title":"unit BoardDriverI","text":"em.utils/BoardDriverI.empackage em.utils\n\ninterface BoardDriverI\n\n host function bindParamsH(p: ptr_t)\n\n template genImpl(dn: string)\n template genSpec(dn: string)\n\nend\n
"},{"location":"cargo/em.core/em.utils/BoardDriversGenT/","title":"BoardDriversGenT","text":""},{"location":"cargo/em.core/em.utils/BoardDriversGenT/#unit-boarddriversgent","title":"unit BoardDriversGenT","text":"em.utils/BoardDriversGenT.empackage em.utils\n\nimport BoardInfo\n\ntemplate BoardDriversGenT\n\n config driversPkg: string\n\nend\n\ndef em$generateUnit(pn, un)\n auto brdRec = BoardInfo.readRecordH()\n auto drvPkg = driversPkg\n |->package `pn`\n |-> \n for dd in brdRec.drvDescs\n |->from `drvPkg` import `dd.driver` as `dd.name`\n end\n |->\n |->module `un`\n |-> \n |->end\nend\n
"},{"location":"cargo/em.core/em.utils/BoardInfo/","title":"BoardInfo","text":""},{"location":"cargo/em.core/em.utils/BoardInfo/#unit-boardinfo","title":"unit BoardInfo","text":"em.utils/BoardInfo.empackage em.utils\n\nfrom em$distro import BoardMeta as Meta\n\nhost module BoardInfo\n\n const PROP_BOARD_CHAIN: string = \"em.lang.BoardChain_\"\n const PROP_BOARD_KIND: string = \"em.lang.BoardKind\"\n\n type PinMap: Meta.PinMap\n type DrvDesc: Meta.DrvDesc\n type Record: Meta.Record\n\n function getKind(): string\n function readRecordH(): Record&\n\nprivate:\n\n function cacheGet(kind: string): Record&\n function cacheSet(kind: string, rec: Record&)\n\n function collapse(kind: string, db: ptr_t, set: ptr_t): ptr_t\n\n function init()\n\n function mergeBrd(baseBrd: ptr_t, extBrd: ptr_t)\n function mergeDb(baseDb: ptr_t, extDb: ptr_t)\n\n config baseFileLoc: string\n config localFileLoc: string\n\n config attrs: string[]\n config pins: string[]\n\n config boardKind: string\n\n var initFlg: bool\n var recCache: ptr_t\n\nend\n\ndef em$configure()\n readRecordH()\nend\n\ndef cacheGet(kind)\n ^^if (!BoardInfo.recCache) BoardInfo.recCache = {}^^\n return ^^BoardInfo.recCache[kind]^^\nend\n\ndef cacheSet(kind, rec)\n ^^BoardInfo.recCache[kind] = rec^^\nend\n\ndef collapse(kind, db, set)\n return ^^db.$DEFAULTS^^ if ^^!kind || kind == '$DEFAULTS'^^\n var ext: ptr_t = ^^db[kind]^^\n if !ext\n printf \"*** unknown board kind: '%s'\\n\", kind\n fail\n end\n if ^^set.has(kind)^^\n printf \"*** circular inheritance chain: '%s'\\n\", kind\n fail\n end\n ^^set.add(kind)^^\n var base: ptr_t = collapse(^^ext.$inherits^^, db, set)\n mergeBrd(base, ext)\n return base\nend\n\ndef getKind()\n init()\n return boardKind\nend\n\ndef init()\n return if initFlg\n initFlg = true\n attrs = Meta.attrNames\n pins = Meta.pinNames\n baseFileLoc = Meta.baseFileLoc\n auto path = ^^$Path.join(em$session.getRootDir(), 'em-boards-local')^^\n localFileLoc = ^^$Fs.existsSync(path) ? path : null^^\n boardKind = ^^em$props.get^^(PROP_BOARD_KIND)\nend\n\ndef mergeBrd(baseBrd, extBrd)\n ^^var aTab = []^^\n ^^for (a in extBrd) { aTab[a] = true; aTab.push(a) }^^\n for a in attrs\n ^^baseBrd[a] = extBrd[a]^^ if ^^a in extBrd^^\n ^^aTab[a] = false^^\n end\n for i: uint8 = 0; i < ^^aTab.length^^; i++\n var an: string = ^^aTab[i]^^\n if an != \"pins\" && an != \"drvDescs\" && an != \"nvsFiles\" && ^^aTab[an]^^\n printf \"*** unknown attribute name: %s\\n\", an\n fail\n end\n end\n #\n ^^var pTab = []^^\n ^^for (p in extBrd.pins) { pTab[p] = true; pTab.push(p) }^^\n ^^baseBrd.pins = {}^^ if ^^!baseBrd.pins^^\n for p in pins\n ^^baseBrd.pins[p] = extBrd.pins[p]^^ if ^^extBrd.pins && p in extBrd.pins^^\n ^^pTab[p] = false^^\n end\n for i: uint8 = 0; i < ^^pTab.length^^; i++\n var pn: string = ^^pTab[i]^^\n continue if ^^pn[0] == '$'^^\n if ^^pTab[pn]^^\n printf \"*** unknown pin name: %s\\n\", pn\n fail\n end\n end\n #\n ^^baseBrd.drvDescs = extBrd.drvDescs^^ if ^^extBrd.drvDescs^^\n ^^baseBrd.nvsFiles = extBrd.nvsFiles^^ if ^^extBrd.nvsFiles^^\nend\n\ndef mergeDb(baseDb, extDb)\n ^^var brdSet = {}^^\n ^^var brdArr = []^^\n ^^for (b in baseDb) brdSet[b] = true^^\n ^^for (b in extDb) brdSet[b] = true^^\n ^^for (b in brdSet) brdArr.push(b)^^\n for i: uint8 = 0; i < ^^brdArr.length^^; i++\n ^^var brdKind = brdArr[i]^^\n ^^var baseBrd = baseDb[brdKind]^^\n ^^var extBrd = extDb[brdKind]^^\n if ^baseBrd && ^extBrd\n if ^^extBrd.$overrides^^\n mergeBrd(^baseBrd, ^extBrd)\n else\n printf \"*** missing '$overrides' for %s\\n\", ^brdKind\n fail\n end\n elif ^extBrd\n if ^^extBrd.$overrides^^\n printf \"*** no platform definition for '$overrides' for %s\\n\", ^brdKind\n fail\n else\n ^^baseDb[brdKind] = extDb[brdKind]^^\n end\n end\n end\nend\n\ndef readRecordH()\n init()\n if boardKind == null\n printf \"*** null board kind\\n\"\n fail\n end\n var rec: Record& = cacheGet(boardKind)\n return rec if rec\n var baseLoc: string = ^em$find(baseFileLoc)\n var baseDb: ptr_t = ^^$Yaml.load($Fs.readFileSync(baseLoc), 'utf-8')^^\n if localFileLoc != null\n var localDb: ptr_t = ^^$Yaml.load($Fs.readFileSync(localFileLoc), 'utf-8')^^\n mergeDb(baseDb, localDb) \n end\n var set: ptr_t = ^^new Set^^\n var base: ptr_t = collapse(boardKind, baseDb, set)\n var chain: ptr_t = ^^Array.from(set.values()).join('--')^^\n ^^em$props.set^^(PROP_BOARD_CHAIN, chain)\n rec = new <Record>\n rec.pinMap = new <PinMap>\n for a in attrs\n ^^rec[a] = base[a]^^\n end\n for p in pins\n ^^rec.pinMap[p] = base.pins[p]^^\n end\n ^^for (n in base.drvDescs) {^^\n ^^var o = base.drvDescs[n]^^\n rec.drvDescs[rec.drvDescs.length++] = new <DrvDesc> {\n name: ^n, driver: ^^o.driver^^, params: ^^o.params^^\n }\n ^^}^^\n cacheSet(boardKind, rec)\n return rec\nend\n
"},{"location":"cargo/em.core/em.utils/BoardMeta/","title":"BoardMeta","text":""},{"location":"cargo/em.core/em.utils/BoardMeta/#unit-boardmeta","title":"unit BoardMeta","text":"em.utils/BoardMeta.empackage em.utils\n\nhost module BoardMeta\n\n type PinMap: struct\n pin: int8\n end\n\n type DrvDesc: struct\n name: string\n driver: string\n params: ptr_t\n end \n\n type Record: struct\n attr: bool\n pinMap: PinMap&\n drvDescs: DrvDesc&[]\n end\n\n config baseFileLoc: string = \"biz.biosbob.distro.axm0f343/em-boards\"\n\n config attrNames: string[] = [\n \"$inherits\",\n \"$overrides\",\n \"attr\",\n ]\n\n config pinNames: string[] = [\n \"pin\",\n ] \n\nend\n
"},{"location":"cargo/em.core/em.utils/Bootup/","title":"Bootup","text":""},{"location":"cargo/em.core/em.utils/Bootup/#unit-bootup","title":"unit Bootup","text":"em.utils/Bootup.empackage em.utils\n\nmodule Bootup\n\n type Fxn: function()\n\n host function addFxnH(fxn: Fxn)\n\n function exec()\n\nprivate:\n\n config FXNTAB: Fxn volatile[]\n config fxnCnt: uint8\n\nend\n\ndef em$construct()\n FXNTABootMemory = true\nend\n\ndef addFxnH(fxn)\n FXNTAB[fxnCnt++] = fxn\nend\n\ndef exec()\n for auto i = 0; i < fxnCnt; i++\n FXNTAB[i]()\n end\nend\n
"},{"location":"cargo/em.core/em.utils/BufPrint/","title":"BufPrint","text":""},{"location":"cargo/em.core/em.utils/BufPrint/#unit-bufprint","title":"unit BufPrint","text":"em.utils/BufPrint.empackage em.utils\n\nmodule BufPrint\n\n config enabled: bool\n\n function bswap(ptr: ptr_t, cnt: uint16, lab: string = null)\n function bytes(ptr: ptr_t, cnt: uint16, lab: string = null)\n function words(ptr: ptr_t, cnt: uint16, lab: string = null)\n\nprivate:\n\n function label(lab: string)\n\nend\n\ndef bswap(ptr, cnt, lab)\n if enabled\n label(lab) if lab\n printf \"[\"\n auto pb = <uint8*>ptr\n auto wc = ((cnt + 0x3) & ~0x3) / 4\n auto sep = \"\"\n while wc--\n printf \"%s\", sep\n sep = \", \"\n var buf: uint8[4]\n buf[0] = *pb++\n buf[1] = *pb++\n buf[2] = *pb++\n buf[3] = *pb++\n auto nb = cnt >= 4 ? 4 : cnt\n cnt -= nb\n auto idx = 3\n while nb--\n printf \"%02x\", buf[idx--]\n end\n end\n printf \"]\\n\"\n end\nend\n\ndef bytes(ptr, cnt, lab)\n if enabled\n label(lab) if lab\n auto pb = <uint8*>ptr\n while cnt--\n printf \"%02x\", *pb++\n end\n printf \"\\n\"\n end\nend\n\ndef label(lab)\n printf \"%s = \", lab\nend\n\ndef words(ptr, cnt, lab)\n if enabled\n label(lab) if lab\n printf \"[\"\n auto pw = <uint32*>ptr\n auto sep = \"\"\n while cnt--\n printf \"%s%08x\", sep, *pw++\n sep = \", \"\n end\n printf \"]\\n\"\n end\nend\n
"},{"location":"cargo/em.core/em.utils/ButtonT/","title":"ButtonT","text":""},{"location":"cargo/em.core/em.utils/ButtonT/#unit-buttont","title":"unit ButtonT","text":"em.utils/ButtonT.empackage em.utils\n\ntemplate ButtonT\n\nend\n\ndef em$generateUnit(pn, un)\n|->>>\n ## ---- generated by em.utils/ButtonT ---- ##\n package `pn`\n\n from em.hal import ButtonI\n from em.hal import GpioEdgeDetectMinI\n\n from em.mcu import Poller\n\n from em.utils import FiberMgr\n\n module `un`: ButtonI\n # ^| implements the ButtonI interface\n proxy Edge: GpioEdgeDetectMinI\n # ^| a GPIO with edge-detection capabilities\n private:\n\n function buttonHandler: Edge.Handler\n function debounceFB: FiberMgr.FiberBodyFxn\n\n config debounceF: FiberMgr.Fiber&\n\n var curDuration: uint16\n var curCb: OnPressedCB\n var maxDur: uint16\n var minDur: uint16\n\n end\n\n def em$construct()\n Edge.setDetectHandlerH(buttonHandler)\n debounceF = FiberMgr.createH(debounceFB)\n end \n\n def em$startup()\n Edge.makeInput()\n Edge.setInternalPullup(true)\n Edge.setDetectFallingEdge()\n end\n\n def buttonHandler()\n Edge.clearDetect()\n debounceF.post() if curCb\n end\n\n def debounceFB(arg)\n curDuration = 0\n for ;;\n Poller.pause(minDur)\n return if curDuration == 0 && !isPressed()\n curDuration += minDur\n break if !isPressed() || curDuration >= maxDur\n end\n curCb()\n end\n\n def isPressed()\n return !Edge.get()\n end\n\n def onPressed(cb, minDurationMs, maxDurationMs)\n curCb = cb\n maxDur = maxDurationMs\n minDur = minDurationMs\n if cb == null\n Edge.disableDetect()\n else\n Edge.enableDetect()\n end\n end\n|-<<<\nend\n
"},{"location":"cargo/em.core/em.utils/Checksum/","title":"Checksum","text":""},{"location":"cargo/em.core/em.utils/Checksum/#unit-checksum","title":"unit Checksum","text":"em.utils/Checksum.empackage em.utils\n\n# Fletcher-16 checksum\n\nmodule Checksum\n\n type Obj: opaque\n function addData(buf: uint8*, len: uint16)\n function clear()\n function getSum8(): uint8\n function getSum16(): uint16\n end\n\nprivate:\n\n def opaque Obj\n sum1: uint16\n sum2: uint16\n end\n\nend\n\ndef Obj.addData(ptr, len)\n auto tl = <uint8>0\n while len\n tl = len >= 20 ? 20 : len\n len -= tl\n for ;;\n this.sum2 += this.sum1 += *ptr++\n tl -= 1\n break if tl == 0\n end\n this.sum1 = (this.sum1 & 0xFF) + (this.sum1 >> 8)\n this.sum2 = (this.sum2 & 0xFF) + (this.sum2 >> 8)\n end\n this.sum1 = (this.sum1 & 0xFF) + (this.sum1 >> 8)\n this.sum2 = (this.sum2 & 0xFF) + (this.sum2 >> 8)\n#/*\n for i: uint16 = 0; i < len; i++\n this.sum1 += *ptr++\n this.sum1 -= 255 if this.sum1 >= 255;\n this.sum2 += this.sum1;\n this.sum2 -= 255 if this.sum2 >= 255;\n end\n#*/ \nend\n\ndef Obj.clear()\n this.sum1 = this.sum2 = 0xFF\nend\n\ndef Obj.getSum8()\n return <uint8>(this.sum1 ^ this.sum2)\nend\n\ndef Obj.getSum16()\n return this.sum2 << 8 | this.sum1\nend\n
"},{"location":"cargo/em.core/em.utils/ConsoleProtocol/","title":"ConsoleProtocol","text":""},{"location":"cargo/em.core/em.utils/ConsoleProtocol/#unit-consoleprotocol","title":"unit ConsoleProtocol","text":"em.utils/ConsoleProtocol.empackage em.utils\n\nmodule ConsoleProtocol\n\n const SOT_BYTE: uint8 = 0x3\n const SOT_COUNT: uint8 = 13\n\n const EOT_BYTE: uint8 = 0x4\n\nend\n
"},{"location":"cargo/em.core/em.utils/Copier/","title":"Copier","text":""},{"location":"cargo/em.core/em.utils/Copier/#unit-copier","title":"unit Copier","text":"em.utils/Copier.empackage em.utils\n\nfrom em.hal import CopierI\n\nmodule Copier: CopierI\n\nend\n\ndef exec(dst, src, cnt)\n ^memcpy(dst, src, cnt)\nend\n
"},{"location":"cargo/em.core/em.utils/Crc16/","title":"Crc16","text":""},{"location":"cargo/em.core/em.utils/Crc16/#unit-crc16","title":"unit Crc16","text":"em.utils/Crc16.empackage em.utils\n\nmodule Crc16\n\n type Obj: opaque\n function addByte(b: uint8): uint8\n function addData(src: uint8*, len: uint8)\n function getSum(): uint16\n function getSumLsb(): uint8\n function getSumMsb(): uint8\n function init()\n end\n\n host function createH(): Obj&\n\nprivate:\n\n const POLY: uint16 = 0x8005\n\n def opaque Obj\n sum: uint16\n function update(b: uint8)\n end\n\nend\n\ndef createH()\n return new<Obj>\nend\n\ndef Obj.addByte(b)\n this.update(b)\n return b\nend\n\ndef Obj.addData(src, len)\n while len--\n this.update(*src++)\n end\nend\n\ndef Obj.getSum()\n return this.sum\nend\n\ndef Obj.getSumLsb()\n return <uint8>(this.sum & 0xFF)\nend\n\ndef Obj.getSumMsb()\n return <uint8>(this.sum >> 8)\nend\n\ndef Obj.init()\n this.sum = 0xFFFF\nend\n\ndef Obj.update(b)\n auto tot = this.sum\n for auto i = 0; i < 8; i++\n if ((tot & 0x8000) >> 8) ^ (b & 0x80)\n tot = (tot << 1) ^ POLY\n else\n tot = (tot << 1)\n end\n b <<= 1\n end\n this.sum = tot\nend\n
"},{"location":"cargo/em.core/em.utils/DebugPinAux/","title":"DebugPinAux","text":""},{"location":"cargo/em.core/em.utils/DebugPinAux/#unit-debugpinaux","title":"unit DebugPinAux","text":"em.utils/DebugPinAux.empackage em.utils\n\nfrom em.mcu import Common\n\nmodule DebugPinAux\n\n type ToggleFxn: function()\n\n config pulseDelay: int32 = -1\n\n function pulse(toggleFxn: ToggleFxn)\n function mark(toggleFxn: ToggleFxn, k: uint8)\n\nprivate:\n\n function delay()\n\nend\n\ndef delay()\n ## TODO -- finer granularity\n Common.BusyWait.wait(<uint32>(-pulseDelay))\nend\n\ndef pulse(toggleFxn)\n toggleFxn()\n delay()\n toggleFxn()\n delay()\n\nend\n\ndef mark(toggleFxn, k)\n while k--\n pulse(toggleFxn)\n end\nend\n
"},{"location":"cargo/em.core/em.utils/DebugPinT/","title":"DebugPinT","text":""},{"location":"cargo/em.core/em.utils/DebugPinT/#unit-debugpint","title":"unit DebugPinT","text":"em.utils/DebugPinT.empackage em.utils\n\ntemplate DebugPinT\n\nend\n\ndef em$generateUnit(pn, un)\n|->>>\npackage `pn`\n\nfrom em.utils import DebugPinAux as Aux\nfrom em.lang import DebugPinI\nfrom em.hal import GpioI\n\nmodule `un`: DebugPinI\n\n proxy Pin: GpioI\n\nend\n\ndef clear()\n Pin.set()\nend\n\ndef get()\n return Pin.get() != 0\nend\n\ndef set()\n Pin.clear()\nend\n\ndef toggle()\n Pin.toggle()\nend\n\ndef pulse()\n Aux.pulse(<Aux.ToggleFxn>toggle)\nend\n\ndef mark(k)\n Aux.mark(<Aux.ToggleFxn>toggle, k)\nend\n\ndef reset()\n Pin.reset()\nend\n\ndef startup()\n Pin.makeOutput()\n Pin.set()\nend\n|-<<<\nend\n
"},{"location":"cargo/em.core/em.utils/EpochTime/","title":"EpochTime","text":""},{"location":"cargo/em.core/em.utils/EpochTime/#unit-epochtime","title":"unit EpochTime","text":"em.utils/EpochTime.empackage em.utils\n\nfrom em.hal import UptimerI\n\nmodule EpochTime\n\n proxy Uptimer: UptimerI\n\n type UpdateFxn: function(esecs: uint32, esubs: uint32)\n\n host function bindUpdateFxnH(fxn: UpdateFxn)\n\n function getCurrent(oSubs: uint32* = null): uint32\n function getRaw(oSubs: uint32* = null): uint32\n function mkSecs256(secs: uint32, subs: uint32): uint32\n function setCurrent(eSecs: uint32, eSubs: uint32 = 0, syncFlag: bool = false)\n\nprivate:\n\n config updateFxn: UpdateFxn\n\n var deltaSecs: uint32\n var deltaSubs: uint32\n\n var lastSecs256: uint32\n var lastTicks: uint32\n\n function compute(time: Uptimer.Time&, oSubs: uint32*): uint32\n\nend\n\ndef bindUpdateFxnH(fxn)\n updateFxn = fxn\nend\n\ndef compute(time, oSubs)\n auto eSubs = time.subs + deltaSubs\n auto inc = eSubs < time.subs ? 1 : 0\n (*oSubs) = eSubs if oSubs\n return time.secs + deltaSecs + inc\nend\n\ndef getCurrent(oSubs)\n return compute(Uptimer.read(), oSubs)\nend\n\ndef getRaw(oSubs)\n auto time = Uptimer.read()\n *oSubs = time.subs if oSubs\n return time.secs\nend\n\ndef mkSecs256(secs, subs)\n return (secs << 8) | (subs >> 24)\nend\n\ndef setCurrent(eSecs, eSubs, syncFlag)\n auto time = Uptimer.read()\n deltaSubs = eSubs - time.subs\n auto dec = deltaSubs > eSubs ? 1 : 0\n deltaSecs = eSecs - time.secs - dec\n auto eSecs256 = mkSecs256(eSecs, eSubs)\n if syncFlag\n Uptimer.calibrate(eSecs256 - lastSecs256, time.ticks - lastTicks) if lastSecs256\n lastSecs256 = eSecs256\n lastTicks = time.ticks\n else\n lastSecs256 = lastTicks = 0\n end\n var subs: uint32\n auto secs = compute(time, &subs)\n updateFxn(eSecs, eSubs) if updateFxn\nend\n
"},{"location":"cargo/em.core/em.utils/Error/","title":"Error","text":""},{"location":"cargo/em.core/em.utils/Error/#unit-error","title":"unit Error","text":"em.utils/Error.empackage em.utils\n\nimport Logger\n\nmodule Error\n\n type Kind: enum\n APPLICATION, ASSERTION, EXCEPTION, EXPIRATION, WATCHDOG\n end\n\n type RaiseFxn: function (kind: Kind, infoA: iarg_t, infoB: iarg_t)\n\n host function bindOnRaiseH(fxn: RaiseFxn)\n function raise: RaiseFxn\n\nprivate:\n\n config KIND_ATOM: atom_t[] = [\n @\"APPLICATION\", @\"ASSERTION\", @\"EXCEPTION\", @\"EXPIRATION\", @\"WATCHDOG\"\n ]\n\n config errorE: Logger.EventKind&\n config onRaiseFxn: RaiseFxn\n\nend\n\ndef em$construct()\n ## TODO -- atomize error kind\n errorE = Logger.declareEventH(\"ERROR: $a [0x%08x, 0x%08x]\", \"*--*\")\nend\n\ndef bindOnRaiseH(fxn)\n onRaiseFxn = fxn\nend\n\ndef raise(kind, infoA, infoB)\n errorE.log(<addr_t>KIND_ATOM[<uint8>kind], <addr_t>infoA, <addr_t>infoB)\n onRaiseFxn(kind, infoA, infoB) if onRaiseFxn\nend\n
"},{"location":"cargo/em.core/em.utils/FftC32/","title":"FftC32","text":""},{"location":"cargo/em.core/em.utils/FftC32/#unit-fftc32","title":"unit FftC32","text":"em.utils/FftC32.empackage em.utils\n\nfrom em.lang import Math\n\nmodule FftC32\n\n type Complex: class\n re: int16\n im: int16\n function get(): uint32\n function set(w: uint32)\n end\n\n config fftSize: uint16 = 128\n config shift: uint8 = 1\n\n function exec(buf: Complex[])\n\nprivate:\n\n config N_WAVE: uint16\n config N_WAVE_LOG2: uint8\n\n config SINE_WAVE: int16[]\n\n function fixMul(a: int16, b: int16): int16\n\nend\n\ndef em$construct()\n N_WAVE = fftSize\n N_WAVE_LOG2 = Math.log2(N_WAVE)\n auto numHalf = N_WAVE / 2\n auto numQtr = N_WAVE / 4\n SINE_WAVE.length = N_WAVE - numQtr\n auto rng = Math.PI / 2\n for auto i = 0; i < SINE_WAVE.length; i++\n if i <= numQtr\n auto sx = Math.sin((rng / numQtr) * i)\n SINE_WAVE[i] = Math.round(sx * 32767) >> shift\n elif i < numHalf\n SINE_WAVE[i] = SINE_WAVE[numHalf - i]\n else\n SINE_WAVE[i] = -(SINE_WAVE[i - numHalf])\n end\n end\nend\n\ndef exec(buf)\n auto mr = 0\n for auto m = 1; m < fftSize; m++\n auto l = fftSize\n for ;;\n l >>= 1\n continue if mr + l > fftSize - 1\n break\n end\n mr = (mr & (l - 1)) + l\n continue if mr <= m\n auto t = buf[m].get()\n buf[m].set(buf[mr].get())\n buf[mr].set(t)\n end\n auto stage = 1\n auto sineStep = N_WAVE_LOG2 - 1\n while stage < fftSize\n auto twiddleStep = stage << 1\n for auto grp = 0; grp < stage; grp++\n auto idx = grp << sineStep\n auto wr = SINE_WAVE[idx + N_WAVE / 4]\n auto wi = -SINE_WAVE[idx]\n for auto i = grp; i < fftSize; i += twiddleStep\n auto j = i + stage\n auto fci = &buf[i]\n auto fcj = &buf[j]\n auto tr = fixMul(wr, fcj.re) - fixMul(wi, fcj.im)\n auto ti = fixMul(wr, fcj.im) + fixMul(wi, fcj.re)\n auto qr = fci.re >> shift\n auto qi = fci.im >> shift\n fcj.re = qr - tr\n fcj.im = qi - ti\n fci.re = qr + tr\n fci.im = qi + ti\n end\n end\n sineStep -= 1\n stage = twiddleStep\n end\nend\n\n\ndef fixMul(a, b)\n auto c = (<int32>a * <int32>b) >> 14\n b = <int16>(<uint32>c & 0x01)\n a = <int16>((c >> 1) + b)\n return a\nend\n\ndef Complex.get()\n return *(<uint32*>this)\nend\n\ndef Complex.set(w)\n *(<uint32*>this) = w\nend\n
"},{"location":"cargo/em.core/em.utils/FftQ15/","title":"FftQ15","text":""},{"location":"cargo/em.core/em.utils/FftQ15/#unit-fftq15","title":"unit FftQ15","text":"em.utils/FftQ15.empackage em.utils\n\nfrom em.lang import Math\n\nmodule FftQ15\n\n config fftSize: uint16 = 128\n config shift: uint8 = 1\n\n function exec(fr: int16[], fi: int16[])\n\nprivate:\n\n config N_WAVE: uint16\n config N_WAVE_LOG2: uint8\n\n config SINE_WAVE: int16[]\n\n function fixMul(a: int16, b: int16): int16\n\nend\n\ndef em$construct()\n N_WAVE = fftSize\n N_WAVE_LOG2 = Math.log2(N_WAVE)\n auto numHalf = N_WAVE / 2\n auto numQtr = N_WAVE / 4\n SINE_WAVE.length = N_WAVE - numQtr\n auto rng = Math.PI / 2\n for auto i = 0; i < SINE_WAVE.length; i++\n if i <= numQtr\n auto sx = Math.sin((rng / numQtr) * i)\n SINE_WAVE[i] = Math.round(sx * 32767) >> shift\n elif i < numHalf\n SINE_WAVE[i] = SINE_WAVE[numHalf - i]\n else\n SINE_WAVE[i] = -(SINE_WAVE[i - numHalf])\n end\n end\nend\n\ndef exec(fr, fi)\n auto mr = 0\n for auto m = 1; m < fftSize; m++\n auto l = fftSize\n for ;;\n l >>= 1\n continue if mr + l > fftSize - 1\n break\n end\n mr = (mr & (l - 1)) + l\n continue if mr <= m\n auto tr = fr[m]\n fr[m] = fr[mr]\n fr[mr] = tr\n auto ti = fi[m]\n fi[m] = fi[mr]\n fi[mr] = ti\n end\n auto stage = 1\n auto sineStep = N_WAVE_LOG2 - 1\n while stage < fftSize\n auto twiddleStep = stage << 1\n for auto grp = 0; grp < stage; grp++\n auto idx = grp << sineStep\n auto wr = SINE_WAVE[idx + N_WAVE / 4]\n auto wi = -SINE_WAVE[idx]\n for auto i = grp; i < fftSize; i += twiddleStep\n auto j = i + stage\n auto tr = fixMul(wr, fr[j]) - fixMul(wi, fi[j])\n auto ti = fixMul(wr, fi[j]) + fixMul(wi, fr[j])\n auto qr = fr[i] >> shift\n auto qi = fi[i] >> shift\n fr[j] = qr - tr\n fi[j] = qi - ti\n fr[i] = qr + tr\n fi[i] = qi + ti\n end\n end\n sineStep -= 1\n stage = twiddleStep\n end\nend\n\ndef fixMul(a, b)\n auto c = (<int32>a * <int32>b) >> 14\n b = <int16>(<uint32>c & 0x01)\n a = <int16>((c >> 1) + b)\n return a\nend\n
"},{"location":"cargo/em.core/em.utils/FiberMgr/","title":"FiberMgr","text":""},{"location":"cargo/em.core/em.utils/FiberMgr/#unit-fibermgr","title":"unit FiberMgr","text":"em.utils/FiberMgr.empackage em.utils\n\nfrom em.mcu import Common\nfrom em.utils import ListMgr\n\nmodule FiberMgr\n # ^| manages opaque fiber objects\n type FiberBodyFxn: function(arg: uarg_t)\n # ^| function signature of fiber body\n type Fiber: opaque\n # ^| opaque fiber object - public specification\n host function initH(fxn: FiberBodyFxn, arg: uarg_t = 0)\n # ^| initialize this fiber and bind its function and argument\n function getArg(): uarg_t\n # ^| get this fiber's body function argument\n function getFxn(): FiberBodyFxn\n # ^| get this fiber's body function\n function post()\n # ^| make this fiber ready-to-run\n function setArg(a: uarg_t)\n # ^| set this fiber's body function argument\n function setFxn(f: FiberBodyFxn)\n # ^| set this fiber's body function\n end\n\n host function createH(fxn: FiberBodyFxn, arg: uarg_t = 0): Fiber&\n # ^| allocate and initialize a fiber; see Fiber.initH\n function run()\n # ^| initiate dispatch of ready-to-run fibers\nprivate:\n\n def opaque Fiber\n elem: ListMgr.Element\n fxn_: FiberBodyFxn\n arg_: uarg_t\n end\n\n function dispatch()\n\n var fiberTab: Fiber[]\n var readyList: ListMgr.List\n\nend\n\ndef em$construct() \n readyList.initH()\nend\n\ndef createH(fxn, arg)\n var fiber: Fiber& = fiberTab[fiberTab.length++]\n fiber.initH(fxn, arg)\n return fiber\nend\n\ndef dispatch()\n for ;;\n break if readyList.hasElements() == 0\n auto fiber = <Fiber&>readyList.get()\n Common.GlobalInterrupts.enable()\n auto fxn = fiber.fxn_\n fxn(fiber.arg_)\n Common.GlobalInterrupts.disable()\n end \nend\n\ndef run()\n Common.Idle.wakeup()\n Common.GlobalInterrupts.enable()\n for ;;\n Common.GlobalInterrupts.disable()\n dispatch()\n Common.Idle.exec()\n end\nend\n\ndef Fiber.initH(fxn, arg)\n this.elem.initH()\n this.fxn_ = fxn\n this.arg_ = arg\nend\n\ndef Fiber.post()\n auto key = Common.GlobalInterrupts.disable()\n readyList.add(this.elem) if !this.elem.isActive()\n Common.GlobalInterrupts.restore(key)\nend\n\ndef Fiber.getArg()\n return this.arg_\nend\n\ndef Fiber.setArg(a)\n this.arg_ = a\nend\n\ndef Fiber.setFxn(f)\n this.fxn_ = f\nend\n\ndef Fiber.getFxn()\n return this.fxn_\nend\n
"},{"location":"cargo/em.core/em.utils/Formatter/","title":"Formatter","text":""},{"location":"cargo/em.core/em.utils/Formatter/#unit-formatter","title":"unit Formatter","text":"em.utils/Formatter.empackage em.utils\n\nfrom em.lang import Console\n\nmodule Formatter\n\n function print(fmt: string, a1: iarg_t = 0, a2: iarg_t = 0, a3: iarg_t = 0, a4: iarg_t = 0, a5: iarg_t = 0, a6: iarg_t = 0)\n function puts(s: string)\n\nprivate:\n\n const OUTMAX: uint8 = ((32 + 2) / 3) + 5\n\n function c2d(c: char): uint8\n function formatNum(buf: char*, num: uint32, base: uint8, pad: char, len: uint8): char*\n function isDigit(c: char): bool\n\n config hexDigs: string = \"0123456789abcdef\"\n\nend\n\ndef c2d(c)\n return c - '0'\nend\n\ndef formatNum(buf, num, base, pad, len)\n auto cnt = len\n *(--buf) = 0\n for ;;\n *(--buf) = hexDigs[<uint8> (num % base)]\n num /= base\n break if len > 0 && --cnt == 0\n break if num == 0\n end\n while cnt-- > 0\n *(--buf) = pad\n end\n return buf\nend\n\ndef isDigit(c)\n return c >= '0' && c <= '9'\nend\n\ndef print(fmt, a1, a2, a3, a4, a5, a6)\n var ch: char\n var buf: char[OUTMAX]\n var args: iarg_t[6]\n var argp: iarg_t* = &args[0]\n args[0] = a1\n args[1] = a2\n args[2] = a3\n args[3] = a4\n args[4] = a5\n args[5] = a6\n while (ch = *fmt++) != 0\n auto pad = ' '\n auto len = 0\n if (ch != '%') \n Console.wrC(ch)\n continue\n end\n ch = *fmt++\n if ch == '0'\n pad = '0'\n ch = *fmt++\n end\n while isDigit(ch)\n len = (len * 10) + c2d(ch)\n ch = *fmt++\n end\n var out: char*\n if ch == 'd'\n var dn: int32 = <int32> *argp++\n if dn < 0\n Console.wrC('-')\n dn = -dn\n end\n out = formatNum(&buf[OUTMAX], <uint32> dn, 10, pad, len)\n elif ch == 'x' \n var xn: uint32 = <uint32> *argp++\n out = formatNum(&buf[OUTMAX], xn, 16, pad, len)\n elif ch == 's'\n out = <char*> *argp++\n else\n Console.wrC(ch == 'c' ? <char> *argp++ : ch)\n continue\n end\n puts(<string>out)\n end\nend\n\ndef puts(s)\n var cp: char* = <char*>s\n var ch: char\n while (ch = *cp++) != 0\n Console.wrC(ch)\n end\nend\n
"},{"location":"cargo/em.core/em.utils/FormattingConsole/","title":"FormattingConsole","text":""},{"location":"cargo/em.core/em.utils/FormattingConsole/#unit-formattingconsole","title":"unit FormattingConsole","text":"em.utils/FormattingConsole.empackage em.utils\n\nfrom em.lang import ConsoleProviderI\n\nfrom em.mcu import ConsoleUart\n\nimport Formatter\n\nmodule FormattingConsole: ConsoleProviderI\n\nend\n\ndef flush()\n ConsoleUart.flush()\nend\n\ndef print(fmt, a1, a2, a3, a4, a5, a6)\n Formatter.print(fmt, a1, a2, a3, a4, a5, a6)\nend\n\ndef put(data)\n ConsoleUart.put(data)\nend\n
"},{"location":"cargo/em.core/em.utils/HeapMem/","title":"HeapMem","text":""},{"location":"cargo/em.core/em.utils/HeapMem/#unit-heapmem","title":"unit HeapMem","text":"em.utils/HeapMem.empackage em.utils\n\nfrom em.mcu import Common\nfrom em.mcu import Copier\n\nmodule HeapMem\n\n config baseAddr: addr_t\n config maxBytes: uint16\n\n function alloc(size: uint16): ptr_t\n function avail(): uint16\n function mark()\n function release()\n\nprivate:\n\n var curTopPtr: uint32*\n var savTopPtr: uint32*\n\nend\n\ndef em$startup()\n return if Common.Mcu.isWarm()\n curTopPtr = baseAddr\nend\n\ndef alloc(size)\n auto ptr = curTopPtr\n auto wc = ((size + 3) & ~0x3) / 4\n curTopPtr += wc\n return ptr\nend\n\ndef avail()\n return <uint16>((baseAddr + maxBytes) - <uint32>curTopPtr)\nend\n\ndef mark()\n *curTopPtr = <uint32>savTopPtr\n savTopPtr = curTopPtr++\nend\n\ndef release()\n fail if !savTopPtr\n curTopPtr = savTopPtr\n savTopPtr = <uint32*>*curTopPtr\n\n\nend\n
"},{"location":"cargo/em.core/em.utils/HeapStatic/","title":"HeapStatic","text":""},{"location":"cargo/em.core/em.utils/HeapStatic/#unit-heapstatic","title":"unit HeapStatic","text":"em.utils/HeapStatic.empackage em.utils\n\nfrom em.mcu import Common\n\nmodule HeapStatic\n\n config baseAddr: addr_t\n config maxBytes: uint16\n\n host function allocH(size: uint16): ptr_t\n\n function getTopAddr(): addr_t\n host function getTopAddrH(): addr_t\n\nprivate:\n\n const MASK: uint32 = 0x3\n config topAddr: addr_t\nend\n\ndef em$construct()\n return if baseAddr || maxBytes == 0\n printf \"*** HeapStatic: baseAddr == 0\\n\"\n fail\nend\n\ndef em$startup()\n return if Common.Mcu.isWarm()\n ^memset(<ptr_t>baseAddr, 0, topAddr - baseAddr) if topAddr && Common.Mcu.getResetCode() <= Common.Mcu.COLD_RESET\nend\n\ndef allocH(size)\n topAddr = baseAddr if topAddr == 0\n auto p = <ptr_t>topAddr\n topAddr += size\n topAddr = (topAddr + MASK) & ~MASK\n return p if (topAddr - baseAddr) < maxBytes\n printf \"*** HeapStatic.allocH: maxBytes = %d, size = %d\\n\", maxBytes, size\n fail\nend\n\ndef getTopAddr()\n return topAddr\nend\n\ndef getTopAddrH()\n return topAddr\nend\n
"},{"location":"cargo/em.core/em.utils/LedBlinkerI/","title":"LedBlinkerI","text":""},{"location":"cargo/em.core/em.utils/LedBlinkerI/#unit-ledblinkeri","title":"unit LedBlinkerI","text":"em.utils/LedBlinkerI.empackage em.utils\n\nfrom em.hal import LedI\n\ninterface LedBlinkerI: LedI\n\n function blink(count: uint16, rateSecs: uint16 = 1, rateMs: uint16 = 0)\n\nend\n
"},{"location":"cargo/em.core/em.utils/LedBlinkerT/","title":"LedBlinkerT","text":""},{"location":"cargo/em.core/em.utils/LedBlinkerT/#unit-ledblinkert","title":"unit LedBlinkerT","text":"em.utils/LedBlinkerT.empackage em.utils\n\ntemplate LedBlinkerT\n\nend\n\ndef em$generateUnit(pn, un)\n|->>>\npackage `pn`\n\nfrom em.hal import LedI\n\nfrom em.utils import LedBlinkerAux\nfrom em.utils import LedBlinkerI\n\nmodule `un`: LedBlinkerI\n\n proxy Led: LedI\n\nend\n\ndef isOn()\n return Led.isOn()\nend\n\ndef on()\n Led.on()\nend\n\ndef off()\n Led.off()\nend\n\ndef toggle()\n Led.toggle()\nend\n\ndef blink(count, rateSecs, rateMs)\n LedBlinkerAux.setFxns(Led.on, Led.off)\n LedBlinkerAux.blink(count, rateSecs, rateMs)\nend\n\ndef wink(onMs, offMs)\n LedBlinkerAux.setFxns(Led.on, Led.off)\n LedBlinkerAux.wink(onMs, offMs)\nend\n|-<<<\nend\n
"},{"location":"cargo/em.core/em.utils/LedT/","title":"LedT","text":""},{"location":"cargo/em.core/em.utils/LedT/#unit-ledt","title":"unit LedT","text":"em.utils/LedT.empackage em.utils\n\ntemplate LedT \n\nend\n\ndef em$generateUnit(pn, un)\n|->>>\n package `pn`\n\n from em.hal import LedI\n from em.hal import GpioI\n\n from em.mcu import Poller\n\n module `un`: LedI\n proxy Pin: GpioI\n config activeLow: bool = false\n end\n\n def em$startup()\n Pin.makeOutput()\n if activeLow\n Pin.set() \n else \n Pin.clear() \n end\n end\n\n def on()\n if activeLow\n Pin.clear() \n else \n Pin.set() \n end \n end\n\n def off()\n if activeLow\n Pin.set() \n else \n Pin.clear() \n end \n end\n\n def toggle()\n Pin.toggle()\n end\n\n def isOn()\n return activeLow ? !Pin.get() : Pin.get()\n end\n\n def wink(msecs)\n on()\n Poller.pause(msecs)\n off()\n end \n|-<<<\nend\n
"},{"location":"cargo/em.core/em.utils/ListManagerI/","title":"ListManagerI","text":""},{"location":"cargo/em.core/em.utils/ListManagerI/#unit-listmanageri","title":"unit ListManagerI","text":"em.utils/ListManagerI.empackage em.utils\n\n#! Implemented by a List Manager module\n#! Element and List declarations.\n\ninterface ListManagerI \n\n type Element: opaque \n\n #! Target and Host function to initialize an Element\n function init()\n host function initH()\n\n #! Returns non-null if the Element is currently a member of a List\n function isActive(): uarg_t\n end\n\n type List: opaque \n\n #! Target and Host function to initialize a List\n function init()\n host function initH()\n\n #! Adds an element to the List as defined by the List Manager\n function add(elem: Element&)\n\n #! Returns a reference to an Element and removes it from the List\n function get(): ref_t\n\n #! Returns a reference to the Element at the specified index\n function getAt(index: uint8): ref_t\n\n #! Returns a reference to the next Element from the specified Element\n function getNext(elem: Element&): ref_t\n\n #! Returns a non-zero value if the List is not empty\n function hasElements(): uarg_t\n\n #! Displays information about the List\n function print()\n\n #! Removes the specified Element from the List\n function remove(elem: Element&)\n end\nend\n
"},{"location":"cargo/em.core/em.utils/ListMgr/","title":"ListMgr","text":""},{"location":"cargo/em.core/em.utils/ListMgr/#unit-listmgr","title":"unit ListMgr","text":"em.utils/ListMgr.empackage em.utils\n\nmodule ListMgr\n\n type Element: opaque \n\n function init()\n host function initH()\n\n function isActive(): uarg_t\n end\n\n type List: opaque \n\n function init()\n host function initH()\n\n function add(elem: Element&)\n function get(): ref_t\n function getAt(index: uint8): ref_t\n function getNext(elem: Element&): ref_t\n function hasElements(): uarg_t\n function print()\n function remove(elem: Element&)\n end\n\nprivate:\n\n def opaque Element \n next: Element& volatile\n end\n\n def opaque List \n first: Element& volatile\n last: Element& volatile\n end\nend\n\ndef Element.init() \n this.next = null\nend\n\ndef Element.initH() \n this.next = null\nend\n\ndef Element.isActive() \n return <uarg_t> this.next\nend\n\ndef List.init() \n this.first = this.last = <Element&> &this.first\nend\n\ndef List.initH() \n this.first = this.last = <Element&> &this.first;\nend\n\ndef List.add(elem)\n this.last.next = elem\n this.last = elem\n elem.next = <Element&> this\nend\n\ndef List.get() \n auto elem = this.first\n this.last = <Element&>this if (this.first = elem.next) == <Element&>this\n elem.next = null\n return elem\nend\n\ndef List.getAt(index)\n auto elem = this.first\n auto i = 0\n if this.hasElements()\n for ;;\n break if i++ == index \n elem = elem.next \n break if elem == <Element&> this\n end\n elem = null if elem == <Element&> this\n else \n elem = null\n end\n return elem\nend\n\ndef List.getNext(elem) \n return elem == this.last ? null : elem.next\nend\n\ndef List.hasElements() \n return (<uarg_t> this.first) ^ <uarg_t> this\nend\n\ndef List.print() \n auto elem = this.first\n auto i = 0\n if this.hasElements()\n for ;;\n printf \"elem%d %p\\n\", i++, elem\n elem = elem.next\n break if elem == <Element&> this\n end\n else \n printf \"list empty\\n\"\n end\n printf \"\\n\" \nend\n\ndef List.remove(elem)\n auto e = this.first\n if this.hasElements()\n if elem == this.first\n this.first = this.first.next\n else \n for ;; \n if e.next == elem\n e.next = elem.next \n break \n end \n break if e == <Element&> this\n end\n end\n end\nend\n
"},{"location":"cargo/em.core/em.utils/LoaderAuxI/","title":"LoaderAuxI","text":""},{"location":"cargo/em.core/em.utils/LoaderAuxI/#unit-loaderauxi","title":"unit LoaderAuxI","text":"em.utils/LoaderAuxI.empackage em.utils\n\ninterface LoaderAuxI\n\n function jumpTo(codeAddr: addr_t)\n\nend\n
"},{"location":"cargo/em.core/em.utils/Logger/","title":"Logger","text":""},{"location":"cargo/em.core/em.utils/Logger/#unit-logger","title":"unit Logger","text":"em.utils/Logger.empackage em.utils\n\nfrom em.hal import FlashI\nfrom em.hal import FlashN\n\nfrom em.lang import Assert\nfrom em.mcu import Common\nfrom em.lang import Console\n\nimport EpochTime\nimport Formatter\nimport HeapStatic\n\nmodule Logger\n\n proxy Flash: FlashI\n\n type Accum: opaque\n function add(val: uint32 = 0)\n function clear()\n function getBin(idx: uint8): uint32\n function getCount(): uint32\n function getMax(): uint32\n function print()\n end\n\n type EventKind: opaque\n function cache(a1: addr_t = 0, a2: addr_t = 0, a3: addr_t = 0)\n function log(a1: addr_t = 0, a2: addr_t = 0, a3: addr_t = 0)\n function print(a1: addr_t = 0, a2: addr_t = 0, a3: addr_t = 0)\n end\n\n type Policy: enum\n NIL, QUIET, PRINT, STORE\n end\n\n config POLICY: Policy = Policy.NIL\n config ENTRY_COUNT: uint16 = 16\n config STORE_SECTOR: uint8 = 0\n\n host function createAccumH(lab: string, grp: string, lims: uint32[] = null): Accum&\n host function declareEventH(msg: string, grp: string): EventKind&\n\n function flush()\n function isEmpty(): bool\n function mkTime(): uint32\n function print()\n function putBytes(bp: uint8*, cnt: uint16)\n function putc(b: uint8)\n function store()\n\nprivate:\n\n const PKT_CODE: uint8 = 0xFE\n const EVT_CODE: uint8 = 0xFD\n const ACC_CODE: uint8 = 0xFC\n const BEG_CODE: uint8 = 0xFB\n const END_CODE: uint8 = 0xFA\n\n type Group: class\n chars: char[4]\n host function initH(gs: string)\n end\n\n def opaque Accum\n data: uint32[]\n metaIdx: uint8\n function getMeta(): AccumMeta&\n end\n\n type AccumMeta: struct\n lab: string\n lims: uint32[]\n hasMax: bool\n limCnt: uint8\n group: Group\n size: uint8\n end\n\n def opaque EventKind\n msg: string\n kidx: uint16\n group: Group\n end\n\n type EventInst: class\n time: uint32\n a1: iarg_t\n a2: iarg_t\n a3: iarg_t\n kidx: uint16\n function put()\n end\n\n type Cursor: class\n idx: uint16\n function next(): EventInst&\n end\n\n config accMetaTab: AccumMeta[]\n\n config sectBeg: addr_t\n config sectEnd: addr_t\n\n config totalAccSize: uint16\n\n var accTab: Accum&[..]\n var evkTab: EventKind&[..]\n\n var buf: EventInst[]\n var curs: Cursor&\n\nend\n\ndef em$configure()\n Flash ?= FlashN\nend\n\ndef em$construct()\n buf = HeapStatic.allocH(sizeof<EventInst> * ENTRY_COUNT) if POLICY != Policy.NIL\n curs = HeapStatic.allocH(sizeof<Cursor>) if POLICY != Policy.NIL\n sectBeg = <addr_t>(Flash.getSectorSizeH() * STORE_SECTOR)\n sectEnd = sectBeg + Flash.getSectorSizeH()\n end\n\ndef createAccumH(lab, grp, lims)\n return null if POLICY == Policy.NIL\n auto accMeta = <AccumMeta&>accMetaTab[accMetaTab.length++]\n accMeta.lab = lab\n accMeta.group.initH(grp)\n accMeta.hasMax = (lims != null)\n accMeta.lims = lims if accMeta.hasMax\n accMeta.limCnt = <uint8>lims.length if accMeta.hasMax\n accMeta.size = (1 + (!accMeta.hasMax ? 0 : (1 + accMeta.limCnt))) * sizeof<uint32>\n auto acc = new<Accum>\n accTab[accTab.length++] = acc\n acc.data = HeapStatic.allocH(accMeta.size)\n acc.metaIdx = <uint8>accMetaTab.length\n totalAccSize += accMeta.size\n return acc\nend\n\ndef declareEventH(msg, grp)\n return null if POLICY == Policy.NIL\n auto ek = new<EventKind>\n evkTab[evkTab.length++] = ek\n ek.kidx = evkTab.length\n ek.msg = <string>msg\n ek.group.initH(grp)\n return ek\nend\n\ndef flush()\n print() if POLICY == Policy.PRINT\n store() if POLICY == Policy.QUIET || POLICY == Policy.STORE\nend\n\ndef isEmpty()\n if POLICY != Policy.NIL\n for acc in accTab\n return false if acc.data[0]\n end\n for auto i = 0; i < ENTRY_COUNT; i++\n return false if buf[i].kidx != 0\n end\n end\n return true\nend\n\ndef mkTime()\n var subs: uint32\n auto secs = EpochTime.getRaw(&subs)\n return (secs << 8) | (subs >> 24)\nend\n\ndef print()\n if POLICY > Policy.QUIET\n putc(PKT_CODE)\n putc(BEG_CODE)\n for acc in accTab\n acc.print()\n end\n for auto i = 0; i < ENTRY_COUNT; i++\n auto evt = curs.next()\n continue if evt.kidx == 0\n evt.put()\n end\n putc(PKT_CODE)\n putc(END_CODE)\n end\nend\n\ndef putBytes(bp, cnt)\n while cnt--\n auto b = *bp++\n putc(PKT_CODE) if b == PKT_CODE\n putc(b)\n end\nend\n\ndef putc(b)\n Console.Provider.put(b)\nend\n\ndef store()\n if POLICY != Policy.NIL && STORE_SECTOR > 0\n auto saddr = sectBeg\n Flash.erase(saddr)\n var et: uint32 = EpochTime.getCurrent()\n saddr = Flash.write(saddr, &et, sizeof<uint32>)\n for acc in accTab\n saddr = Flash.write(saddr, &acc.data[0], acc.getMeta().size)\n end\n for auto i = 0; i < ENTRY_COUNT; i++\n auto evt = curs.next()\n continue if evt.kidx == 0\n saddr = Flash.write(saddr, evt, sizeof<EventInst>)\n evt.kidx = 0\n end\n end\nend\n\ndef Accum.add(val)\n auto accMeta = this.getMeta()\n this.data[0] += 1\n return if !accMeta.hasMax\n this.data[1] = val if val > this.data[1]\n for auto i = 0; i < accMeta.limCnt; i++\n this.data[2 + i] += 1 if val < accMeta.lims[i]\n end\nend\n\ndef Accum.clear()\n auto accMeta = this.getMeta()\n auto words = accMeta.hasMax ? (2 + accMeta.limCnt) : 1\n ^memset(&this.data[0], 0, words * sizeof<uint32>)\nend\n\ndef Accum.getBin(idx)\n return this.data[idx + 2]\nend\n\ndef Accum.getCount()\n return this.data[0]\nend\n\ndef Accum.getMax()\n return this.data[1]\nend\n\ndef Accum.getMeta()\n return <AccumMeta&>(&accMetaTab[this.metaIdx-1])\nend\n\ndef Accum.print()\n if POLICY > Policy.QUIET\n auto accMeta = this.getMeta()\n putc(PKT_CODE)\n putc(ACC_CODE)\n putc(this.metaIdx)\n putBytes(<uint8*>&this.data[0], accMeta.size)\n end\nend\n\ndef Cursor.next()\n auto evt = &buf[this.idx++]\n this.idx = 0 if this.idx >= ENTRY_COUNT\n return evt\nend\n\ndef EventInst.put()\n putc(PKT_CODE)\n putc(EVT_CODE)\n putBytes(<uint8*>this, sizeof<EventInst> - sizeof<uint16>)\nend\n\ndef EventKind.cache(a1, a2, a3)\n if POLICY != Policy.NIL && ENTRY_COUNT > 0\n auto evt = curs.next()\n evt.time = mkTime()\n evt.kidx = this.kidx\n evt.a1 = <iarg_t>a1\n evt.a2 = <iarg_t>a2\n evt.a3 = <iarg_t>a3\n end\nend\n\ndef EventKind.log(a1, a2, a3)\n this.cache(a1, a2, a3) if POLICY == Policy.STORE || POLICY == Policy.QUIET\n this.print(a1, a2, a3) if POLICY == Policy.PRINT\nend\n\ndef EventKind.print(a1, a2, a3)\n if POLICY > Policy.QUIET\n var evt: EventInst\n evt.time = mkTime()\n evt.kidx = this.kidx\n evt.a1 = <iarg_t>a1\n evt.a2 = <iarg_t>a2\n evt.a3 = <iarg_t>a3\n evt.put()\n end\nend\n\ndef Group.initH(gs)\n for auto i = 0; i < this.chars.length; i++\n this.chars[i] = ^^gs && gs[i] ? gs.charCodeAt(i) : \" \".charCodeAt(0)^^\n end\nend\n
"},{"location":"cargo/em.core/em.utils/MemDump/","title":"MemDump","text":""},{"location":"cargo/em.core/em.utils/MemDump/#unit-memdump","title":"unit MemDump","text":"em.utils/MemDump.empackage em.utils\n\nmodule MemDump\n\n function print32(addr: ptr_t, count: uint8)\n\nend\n\ndef print32(addr, count)\n auto p32 = <uint32*>addr\n while count--\n auto v = *p32\n printf \"%08x: %08x\\n\", p32, v\n p32 += 1\n end\nend\n
"},{"location":"cargo/em.core/em.utils/MinConsole/","title":"MinConsole","text":""},{"location":"cargo/em.core/em.utils/MinConsole/#unit-minconsole","title":"unit MinConsole","text":"em.utils/MinConsole.empackage em.utils\n\nfrom em.lang import ConsoleProviderI\n\nfrom em.mcu import ConsoleUart\n\nimport Formatter\n\nmodule MinConsole: ConsoleProviderI\n\nend\n\ndef flush()\n ConsoleUart.flush()\nend\n\ndef print(fmt, a1, a2, a3, a4, a5, a6)\nend\n\ndef put(data)\n ConsoleUart.put(data)\nend\n
"},{"location":"cargo/em.core/em.utils/MsCounterUptimer/","title":"MsCounterUptimer","text":""},{"location":"cargo/em.core/em.utils/MsCounterUptimer/#unit-mscounteruptimer","title":"unit MsCounterUptimer","text":"em.utils/MsCounterUptimer.empackage em.utils\n\nfrom em.hal import MsCounterI\nfrom em.hal import UptimerI\n\nmodule MsCounterUptimer: MsCounterI\n\n proxy Uptimer: UptimerI\n\nprivate:\n\n var t0: uint32\n\n function readMsecs(): uint32\n\nend\n\ndef readMsecs()\n auto time = Uptimer.read()\n return ((time.secs & 0xFF) << 16) + (time.subs >> 16)\nend\n\ndef start()\n t0 = readMsecs()\nend\n\ndef stop()\n return 0 if t0 == 0\n auto dt = readMsecs() - t0\n t0 = 0\n return (dt * 1000) >> 16\nend\n
"},{"location":"cargo/em.core/em.utils/PollerAux/","title":"PollerAux","text":""},{"location":"cargo/em.core/em.utils/PollerAux/#unit-polleraux","title":"unit PollerAux","text":"em.utils/PollerAux.empackage em.utils\n\nfrom em.hal import OneShotMilliI\nfrom em.hal import PollerI\n\nfrom em.mcu import Common\n\nmodule PollerAux: PollerI\n\n proxy OneShot: OneShotMilliI\n\n config pauseOnly: bool = false\n\nprivate:\n\n function handler: OneShot.Handler\n function pause(msecs: uint32)\n\n var doneFlag: bool volatile\n\nend\n\ndef handler(arg)\n doneFlag = true\nend\n\ndef pause(msecs)\n return if msecs == 0\n doneFlag = false\n OneShot.enable(msecs, handler, null)\n while !doneFlag\n Common.Idle.exec()\n end\nend\n\ndef poll(rate, count, fxn)\n if pauseOnly\n pause(rate)\n return 1\n else\n count = 0 if rate == 0\n while count\n pause(rate)\n count -= 1\n break if fxn && fxn()\n end\n return count \n end\nend\n
"},{"location":"cargo/em.core/em.utils/ProcMgr/","title":"ProcMgr","text":""},{"location":"cargo/em.core/em.utils/ProcMgr/#unit-procmgr","title":"unit ProcMgr","text":"em.utils/ProcMgr.empackage em.utils\n\nfrom em.mcu import Common\nfrom em.utils import BasicListManager\n\nmodule ProcMgr\n\n type ProcFxn: function(arg: uarg_t)\n\n type Proc: opaque\n host function declareStartH()\n host function initH(fxn: fxn_t, arg: uarg_t = 0)\n function arg(a: uarg_t)\n function fxn(f: fxn_t)\n function getArg(): uarg_t\n function getFxn(): fxn_t\n function post()\n end\n\n host function createH(fxn: fxn_t, arg: uarg_t = 0): Proc&\n\n function run()\n\nprivate:\n\n def opaque Proc\n elem: BasicListManager.Element\n fxn_: fxn_t\n arg_: uarg_t\n end\n\n function dispatch()\n\n config startP: Proc&\n\n var procList: BasicListManager.List\n\nend\n\ndef em$construct() \n procList.initH()\nend\n\ndef em$startup()\n startP.post() if startP && !Common.Mcu.isWarm()\nend\n\ndef createH(fxn, arg)\n auto proc = new<Proc>\n proc.initH(fxn, arg)\n return proc\nend\n\ndef dispatch()\n for ;;\n break if procList.hasElements() == 0\n auto proc = <Proc&>procList.get()\n Common.GlobalInterrupts.enable()\n auto fxn = <ProcFxn>proc.fxn_\n fxn(proc.arg_)\n Common.GlobalInterrupts.disable()\n end \nend\n\ndef run()\n Common.Idle.wakeup()\n Common.GlobalInterrupts.enable()\n for ;;\n Common.GlobalInterrupts.disable()\n dispatch()\n Common.Idle.exec()\n end\nend\n\ndef Proc.declareStartH()\n startP = this\nend\n\ndef Proc.initH(fxn, arg)\n this.elem.initH()\n this.fxn_ = fxn\n this.arg_ = arg\nend\n\ndef Proc.arg(a)\n this.arg_ = a\nend\n\ndef Proc.fxn(f)\n this.fxn_ = f\nend\n\ndef Proc.getArg()\n return this.arg_\nend\n\ndef Proc.getFxn()\n return this.fxn_\nend\n\ndef Proc.post()\n auto key = Common.GlobalInterrupts.disable()\n procList.add(this.elem) if !this.elem.isActive()\n Common.GlobalInterrupts.restore(key)\nend\n
"},{"location":"cargo/em.core/em.utils/SoftUart/","title":"SoftUart","text":""},{"location":"cargo/em.core/em.utils/SoftUart/#unit-softuart","title":"unit SoftUart","text":"em.utils/SoftUart.empackage em.utils\n\nfrom em.hal import ConsoleUartI\nfrom em.hal import GpioI\nfrom em.hal import UsThreshI\n\nfrom em.lang import Math\n\nfrom em.mcu import Common\n\nmodule SoftUart: ConsoleUartI\n\n proxy TxPin: GpioI\n proxy UsThresh: UsThreshI\n\nprivate:\n\n config baudRate: uint32 = 115200\n config bitTime: uint16 = 7\n\nend\n\ndef em$startup()\n TxPin.makeOutput()\n TxPin.set()\nend\n\ndef setBaudH(rate)\n ## TODO -- implement\nend\n\ndef flush()\nend\n\ndef put(data)\n var bitCnt: uint8 = 10 # Load Bit counter, 8data + ST/SP/SP\n var txByte: uint16 = (data << 1) | 0x600 # Add mark stop bits and space start bit\n var key: uarg_t = Common.GlobalInterrupts.disable()\n for ;;\n UsThresh.set(bitTime)\n if bitCnt-- == 0\n TxPin.set()\n break\n else\n if txByte & 0x01\n TxPin.set()\n else\n TxPin.clear()\n end\n txByte = txByte >> 1 # shift next bit\n end\n UsThresh.pause()\n end\n Common.GlobalInterrupts.restore(key)\nend\n
"},{"location":"cargo/em.core/em.utils/TickerMgr/","title":"TickerMgr","text":""},{"location":"cargo/em.core/em.utils/TickerMgr/#unit-tickermgr","title":"unit TickerMgr","text":"em.utils/TickerMgr.empackage em.utils\n\nimport AlarmMgr\nimport FiberMgr\n\nmodule TickerMgr\n # ^|\n type TickCallback: function()\n # ^|\n type Ticker: opaque\n # ^|\n host function initH()\n # ^|\n function start(rate256: uint32, tickCb: TickCallback)\n # ^| \n function stop()\n # ^| \n end\n\n host function createH(): Ticker&\n # ^|\nprivate:\n\n def opaque Ticker\n alarm: AlarmMgr.Alarm&\n fiber: FiberMgr.Fiber&\n rate256: uint32\n tickCb: TickCallback\n end\n\n function alarmFB: FiberMgr.FiberBodyFxn\n\n var tickerTab: Ticker[]\n\nend\n\ndef createH()\n var ticker: Ticker& = tickerTab[tickerTab.length++]\n ticker.initH()\n return ticker\nend\n\ndef Ticker.initH()\n this.fiber = FiberMgr.createH(alarmFB, ^^this.$$cn^^)\n this.alarm = AlarmMgr.createH(this.fiber)\nend\n\ndef alarmFB(arg)\n auto ticker = <Ticker&>arg\n return if ticker.tickCb == null\n ticker.tickCb() \n ticker.alarm.wakeupAt(ticker.rate256)\nend\n\ndef Ticker.start(rate256, tickCb)\n this.rate256 = rate256\n this.tickCb = tickCb\n this.alarm.wakeupAt(rate256)\nend\n\ndef Ticker.stop()\n this.alarm.cancel()\n this.tickCb = null\nend\n
"},{"location":"cargo/em.core/em.utils/TimeoutAux/","title":"TimeoutAux","text":""},{"location":"cargo/em.core/em.utils/TimeoutAux/#unit-timeoutaux","title":"unit TimeoutAux","text":"em.utils/TimeoutAux.empackage em.utils\n\nfrom em.hal import OneShotMilliI\nfrom em.hal import TimeoutI\n\nmodule TimeoutAux: TimeoutI\n\n proxy OneShot: OneShotMilliI\n\nprivate:\n\n var flag: bool volatile\n\n function handler: OneShot.Handler\n\nend\n\ndef active()\n return flag\nend\n\ndef cancel()\n flag = false\n OneShot.disable()\nend\n\ndef handler(arg)\n cancel()\nend\n\ndef set(msecs)\n flag = true\n OneShot.enable(msecs, handler, null)\nend\n
"},{"location":"cargo/em.docs/","title":"Index","text":""},{"location":"cargo/em.docs/#bundle-emdocs","title":"bundle em.docs","text":""},{"location":"cargo/em.docs/em.examples.basic/","title":"Index","text":""},{"location":"cargo/em.docs/em.examples.basic/#package-emexamplesbasic","title":"package em.examples.basic","text":""},{"location":"cargo/em.docs/em.examples.basic/Alarm1P/","title":"Alarm1P","text":""},{"location":"cargo/em.docs/em.examples.basic/Alarm1P/#unit-alarm1p","title":"unit Alarm1P","text":"em.examples.basic/Alarm1P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.utils import AlarmMgr\nfrom em.utils import FiberMgr\n\nmodule Alarm1P\n\nprivate:\n\n function blinkFB: FiberMgr.FiberBodyFxn\n\n config alarm: AlarmMgr.Alarm&\n config blinkF: FiberMgr.Fiber&\n\n var counter: uint32\n\nend\n\ndef em$construct()\n blinkF = FiberMgr.createH(blinkFB)\n alarm = AlarmMgr.createH(blinkF)\nend\n\ndef em$run()\n blinkF.post()\n FiberMgr.run()\nend\n\ndef blinkFB(arg)\n %%[c]\n AppLed.wink(100) # 100ms\n counter += 1\n if counter & 0x1\n alarm.wakeup(512) # 2s\n else\n alarm.wakeup(192) # 750ms\n end\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/Alarm2P/","title":"Alarm2P","text":""},{"location":"cargo/em.docs/em.examples.basic/Alarm2P/#unit-alarm2p","title":"unit Alarm2P","text":"em.examples.basic/Alarm2P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.utils import AlarmMgr\nfrom em.utils import FiberMgr\n\nmodule Alarm2P\n\nprivate:\n\n function blinkFB: FiberMgr.FiberBodyFxn\n\n config alarm: AlarmMgr.Alarm&\n config blinkF: FiberMgr.Fiber&\n\n var counter: uint32\n\nend\n\ndef em$construct()\n blinkF = FiberMgr.createH(blinkFB)\n alarm = AlarmMgr.createH(blinkF)\nend\n\ndef em$run()\n blinkF.post()\n FiberMgr.run()\nend\n\ndef blinkFB(arg)\n %%[c]\n counter += 1\n if counter & 0x1\n AppLed.wink(100) # 100ms\n else\n AppLed.wink(5) # 5ms\n end\n alarm.wakeupAt(384) # 1.5s window \nend\n
"},{"location":"cargo/em.docs/em.examples.basic/BlinkerDbgP/","title":"BlinkerDbgP","text":""},{"location":"cargo/em.docs/em.examples.basic/BlinkerDbgP/#unit-blinkerdbgp","title":"unit BlinkerDbgP","text":"em.examples.basic/BlinkerDbgP.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.mcu import Common\n\nmodule BlinkerDbgP\n\n config dbgFlag: bool = true\n\n config minCnt: uint16 = 1000\n config maxCnt: uint16 = 1020 \n\nend\n\ndef em$run()\n AppLed.on()\n for cnt: uint16 = minCnt; cnt < maxCnt; cnt++\n %%[d+]\n Common.BusyWait.wait(500 * 1000L)\n %%[d-]\n AppLed.toggle()\n continue if !dbgFlag\n fail if cnt > ((minCnt + maxCnt) / 2)\n %%[>cnt]\n var bits11: uint8 = cnt & 0b0011\n %%[>bits11]\n %%[c:bits11]\n printf \"cnt = %d (0x%04x), bits11 = %d\\n\", cnt, cnt, bits11\n end\n AppLed.off()\n halt\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/BlinkerP/","title":"BlinkerP","text":""},{"location":"cargo/em.docs/em.examples.basic/BlinkerP/#unit-blinkerp","title":"unit BlinkerP","text":"em.examples.basic/BlinkerP.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.mcu import Common\n\nmodule BlinkerP\n\nend\n\ndef em$run()\n AppLed.on()\n for auto i = 0; i < 10; i++\n Common.BusyWait.wait(500 * 1000L)\n AppLed.toggle()\n end\n AppLed.off()\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/Button1P/","title":"Button1P","text":""},{"location":"cargo/em.docs/em.examples.basic/Button1P/#unit-button1p","title":"unit Button1P","text":"em.examples.basic/Button1P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em$distro import McuC\nfrom McuC import AppButEdge\n\nfrom em.mcu import Common\n\nmodule Button1P\n\nprivate:\n\n function handler: AppButEdge.Handler\n\nend\n\ndef em$construct()\n AppButEdge.setDetectHandlerH(handler)\nend\n\ndef em$startup()\n AppButEdge.makeInput()\n AppButEdge.setInternalPullup(true)\n AppButEdge.setDetectFallingEdge()\nend\n\ndef em$run()\n Common.GlobalInterrupts.enable()\n for ;;\n AppButEdge.enableDetect()\n Common.Idle.exec()\n end\nend\n\ndef handler()\n %%[c]\n AppButEdge.clearDetect()\n AppLed.on()\n Common.BusyWait.wait(5000)\n AppLed.off()\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/Button2P/","title":"Button2P","text":""},{"location":"cargo/em.docs/em.examples.basic/Button2P/#unit-button2p","title":"unit Button2P","text":"em.examples.basic/Button2P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em$distro import McuC\nfrom McuC import AppButEdge\n\nfrom em.mcu import Common\n\nfrom em.utils import FiberMgr\n\nmodule Button2P\n\nprivate:\n\n function blinkFB: FiberMgr.FiberBodyFxn\n function handler: AppButEdge.Handler\n\n config blinkF: FiberMgr.Fiber&\n\nend\n\ndef em$construct()\n AppButEdge.setDetectHandlerH(handler)\n blinkF = FiberMgr.createH(blinkFB)\nend\n\ndef em$startup()\n AppButEdge.makeInput()\n AppButEdge.setInternalPullup(true)\n AppButEdge.setDetectFallingEdge()\nend\n\ndef em$run()\n AppButEdge.enableDetect()\n FiberMgr.run()\nend\n\ndef blinkFB(arg)\n %%[d]\n AppLed.on()\n Common.BusyWait.wait(5000)\n AppLed.off()\n AppButEdge.enableDetect()\nend\n\ndef handler()\n %%[c]\n AppButEdge.clearDetect()\n blinkF.post()\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/Button3P/","title":"Button3P","text":""},{"location":"cargo/em.docs/em.examples.basic/Button3P/#unit-button3p","title":"unit Button3P","text":"em.examples.basic/Button3P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppBut\nfrom BoardC import AppLed\nfrom BoardC import SysLed\n\nfrom em.mcu import Common\nfrom em.utils import FiberMgr\n\nmodule Button3P\n\nprivate:\n\n function onPressedCB: AppBut.OnPressedCB\n\nend\n\ndef em$run()\n AppBut.onPressed(onPressedCB)\n FiberMgr.run()\nend\n\ndef onPressedCB()\n %%[c]\n if AppBut.isPressed()\n SysLed.on()\n Common.BusyWait.wait(40000) # 40ms\n SysLed.off()\n else\n AppLed.on()\n Common.BusyWait.wait(5000) # 5ms\n AppLed.off()\n end\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/FiberP/","title":"FiberP","text":""},{"location":"cargo/em.docs/em.examples.basic/FiberP/#unit-fiberp","title":"unit FiberP","text":"em.examples.basic/FiberP.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.mcu import Common\nfrom em.utils import FiberMgr\n\nmodule FiberP\n\nprivate:\n\n function blinkFB: FiberMgr.FiberBodyFxn\n\n config blinkF: FiberMgr.Fiber&\n\n var count: uint8 = 5\n\nend\n\ndef em$construct()\n blinkF = FiberMgr.createH(blinkFB)\nend\n\ndef em$run()\n blinkF.post()\n FiberMgr.run()\nend\n\ndef blinkFB(arg)\n %%[d]\n halt if --count == 0\n AppLed.on()\n Common.BusyWait.wait(100000)\n AppLed.off()\n Common.BusyWait.wait(100000)\n blinkF.post()\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/HelloP/","title":"HelloP","text":""},{"location":"cargo/em.docs/em.examples.basic/HelloP/#unit-hellop","title":"unit HelloP","text":"em.examples.basic/HelloP.empackage em.examples.basic\n\nfrom em$distro import BoardC\n\nmodule HelloP\n\nend\n\ndef em$run()\n printf \"hello world\\n\"\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/OneShot1P/","title":"OneShot1P","text":""},{"location":"cargo/em.docs/em.examples.basic/OneShot1P/#unit-oneshot1p","title":"unit OneShot1P","text":"em.examples.basic/OneShot1P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em$distro import McuC\nfrom McuC import OneShotMilli\n\nfrom em.mcu import Common\n\nmodule OneShot1P\n\nprivate:\n\n function handler: OneShotMilli.Handler\n\n var doneFlag: bool volatile = true\n\nend\n\ndef em$run()\n Common.GlobalInterrupts.enable()\n for auto i = 0; i < 5; i++\n %%[d]\n AppLed.on()\n Common.BusyWait.wait(5000)\n AppLed.off()\n doneFlag = false\n OneShotMilli.enable(100, handler)\n while !doneFlag\n Common.Idle.exec()\n end\n end\nend\n\ndef handler(arg)\n %%[c]\n doneFlag = true\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/OneShot2P/","title":"OneShot2P","text":""},{"location":"cargo/em.docs/em.examples.basic/OneShot2P/#unit-oneshot2p","title":"unit OneShot2P","text":"em.examples.basic/OneShot2P.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em$distro import McuC\nfrom McuC import OneShotMilli\n\nfrom em.mcu import Common\nfrom em.utils import FiberMgr\n\nmodule OneShot2P\n\nprivate:\n\n function blinkFB: FiberMgr.FiberBodyFxn\n function handler: OneShotMilli.Handler\n\n config blinkF: FiberMgr.Fiber&\n var count: uint8 = 5\n\nend\n\ndef em$construct()\n blinkF = FiberMgr.createH(blinkFB)\nend\n\ndef em$run()\n blinkF.post()\n FiberMgr.run()\nend\n\ndef blinkFB(arg)\n %%[d]\n AppLed.on()\n Common.BusyWait.wait(5000)\n AppLed.off()\n halt if --count == 0\n OneShotMilli.enable(100, handler, null)\nend\n\ndef handler(arg)\n %%[c]\n blinkF.post()\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/PollerP/","title":"PollerP","text":""},{"location":"cargo/em.docs/em.examples.basic/PollerP/#unit-pollerp","title":"unit PollerP","text":"em.examples.basic/PollerP.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\n\nfrom em.mcu import Common\nfrom em.mcu import Poller\n\nmodule PollerP\n\nend\n\ndef em$run()\n Common.GlobalInterrupts.enable()\n auto k = 5\n while k--\n Poller.pause(100) # 100ms\n AppLed.wink(5) # 5ms\n end\nend\n
"},{"location":"cargo/em.docs/em.examples.basic/TickerP/","title":"TickerP","text":""},{"location":"cargo/em.docs/em.examples.basic/TickerP/#unit-tickerp","title":"unit TickerP","text":"em.examples.basic/TickerP.empackage em.examples.basic\n\nfrom em$distro import BoardC\nfrom BoardC import AppLed\nfrom BoardC import SysLed\n\nfrom em.utils import FiberMgr\nfrom em.utils import TickerMgr\n\nmodule TickerP\n\nprivate:\n\n function appTickCb: TickerMgr.TickCallback\n function sysTickCb: TickerMgr.TickCallback\n\n config appTicker: TickerMgr.Ticker&\n config sysTicker: TickerMgr.Ticker&\n\nend\n\ndef em$construct()\n appTicker = TickerMgr.createH()\n sysTicker = TickerMgr.createH()\nend\n\ndef em$run()\n appTicker.start(256, appTickCb)\n sysTicker.start(384, sysTickCb)\n FiberMgr.run()\nend\n\ndef appTickCb()\n %%[c]\n AppLed.wink(100)\nend\n\ndef sysTickCb()\n %%[d]\n SysLed.wink(100)\nend\n
"},{"location":"cargo/ti.cc23xx/","title":"Index","text":""},{"location":"cargo/ti.cc23xx/#bundle-ticc23xx","title":"bundle ti.cc23xx","text":""},{"location":"cargo/ti.cc23xx/ti.build.cc23xx/","title":"Index","text":""},{"location":"cargo/ti.cc23xx/ti.build.cc23xx/#package-tibuildcc23xx","title":"package ti.build.cc23xx","text":""},{"location":"cargo/ti.cc23xx/ti.build.cc23xx/GccBuilder/","title":"GccBuilder","text":""},{"location":"cargo/ti.cc23xx/ti.build.cc23xx/GccBuilder/#unit-gccbuilder","title":"unit GccBuilder","text":"ti.build.cc23xx/GccBuilder.empackage ti.build.cc23xx\n\nfrom em.build.misc import UniFlash\nfrom em.build.misc import Utils\n\nfrom em.build.gcc import BuilderBase as Base\n\nfrom em.lang import BuilderI\nfrom em.lang import BuildC\n\nmodule GccBuilder: BuilderI\n\nend\n\ndef em$configure()\n Base.gccFlav ?= \"arm-none-eabi\"\n if BuildC.bootFlash\n Base.dmemBase ?= 0x20005000\n Base.dmemSize ?= 0x4000\n Base.imemBase ?= 0x20000000\n Base.imemSize ?= 0x5000\n Base.lmemBase ?= 0x00000000\n Base.lmemSize ?= 0x80000\n else\n Base.dmemBase ?= 0x20000000\n Base.dmemSize ?= 0x9000\n Base.imemBase ?= 0x00000000\n Base.imemSize ?= 0x80000\n end\n Base.vectSize ?= 0x90\n Utils.addInclude(\"com.ti/devices/cc23x0r5\")\n Utils.addSection(0x4e020000, 0x800, \"FLASH_CCFG\", \".ccfg\")\nend\n\ndef compile(buildDir)\n return <CompileInfo&>Base.compile(buildDir)\nend\n\ndef getTypeInfo()\n return <TypeInfo&>Base.getTypeInfo()\nend\n\ndef populate(buildDir, sysFlag)\n Base.populate(buildDir, sysFlag)\n Utils.copy(buildDir, \"CC2340R5.ccxml\", \"ti.build.cc23xx\")\n UniFlash.genLoadScript(buildDir, \"CC2340R5\")\nend\n
"},{"location":"cargo/ti.cc23xx/ti.build.cc23xx/SeggerBuilder/","title":"SeggerBuilder","text":""},{"location":"cargo/ti.cc23xx/ti.build.cc23xx/SeggerBuilder/#unit-seggerbuilder","title":"unit SeggerBuilder","text":"ti.build.cc23xx/SeggerBuilder.empackage ti.build.cc23xx\n\nfrom em.build.misc import UniFlash\nfrom em.build.misc import Utils\n\nfrom em.build.segger import BuilderBase as Base\n\nfrom em.lang import BuilderI\nfrom em.lang import BuildC\n\nmodule SeggerBuilder: BuilderI\n\nend\n\ndef em$configure()\n if BuildC.bootFlash\n Base.dmemBase ?= 0x20005000\n Base.dmemSize ?= 0x4000\n Base.imemBase ?= 0x20000000\n Base.imemSize ?= 0x5000\n Base.lmemBase ?= 0x00000000\n Base.lmemSize ?= 0x80000\n else\n Base.dmemBase ?= 0x20000000\n Base.dmemSize ?= 0x9000\n Base.imemBase ?= 0x00000000\n Base.imemSize ?= 0x80000\n end\n Base.vectSize ?= 0x90\n Utils.addInclude(\"com.ti/devices/cc23x0r5\")\n Utils.addInclude(\"com.ti/devices/cc23x0r5/cmsis/core\")\n Utils.addSection(0x4e020000, 0x800, \"FLASH_CCFG\", \".ccfg\")\nend\n\ndef compile(buildDir)\n return <CompileInfo&>Base.compile(buildDir)\nend\n\ndef getTypeInfo()\n return <TypeInfo&>Base.getTypeInfo()\nend\n\ndef populate(buildDir, sysFlag)\n Base.populate(buildDir, sysFlag)\n Utils.copy(buildDir, \"CC2340R5.ccxml\", \"ti.build.cc23xx\")\n UniFlash.genLoadScript(buildDir, \"CC2340R5\")\nend\n
"},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/","title":"Index","text":""},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/#package-tidistrocc23xx","title":"package ti.distro.cc23xx","text":""},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/BoardC/","title":"BoardC","text":""},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/BoardC/#unit-boardc","title":"unit BoardC","text":"ti.distro.cc23xx/BoardC.empackage ti.distro.cc23xx\n\nfrom em.lang import Atom # force ordering\n\nimport McuC\nfrom McuC import AppButEdge\nfrom McuC import AppLedPin\nfrom McuC import AppOutPin\nfrom McuC import AppOutUart\nfrom McuC import SysDbgA\nfrom McuC import SysDbgB\nfrom McuC import SysDbgC\nfrom McuC import SysDbgD\nfrom McuC import SysLedPin\nfrom McuC import OneShotMilli\nfrom McuC import Uptimer\nfrom McuC import WakeupTimer\n\nfrom em.lang import Console\nfrom em.lang import Debug\n\nfrom em.mcu import ConsoleUart\nfrom em.mcu import Poller\n\nfrom em.utils import AlarmMgr\nfrom em.utils import BoardController\nfrom em.utils import BoardInfo\nfrom em.utils import EpochTime\nfrom em.utils import FormattingConsole\nfrom em.utils import PollerAux\n\nfrom em.utils import DebugPinT {} as DbgA\nfrom em.utils import DebugPinT {} as DbgB\nfrom em.utils import DebugPinT {} as DbgC\nfrom em.utils import DebugPinT {} as DbgD\n\nfrom em.utils import LedT {} as AppLed\nfrom em.utils import LedT {} as SysLed\n\nfrom em.utils import ButtonT {} as AppBut\n\nexport AppBut\nexport AppLed\nexport SysLed\n\ncomposite BoardC\n\nend\n\ndef em$configure()\n auto brdRec = BoardInfo.readRecordH()\n auto pm = brdRec.pinMap\n AlarmMgr.WakeupTimer ?= WakeupTimer\n AppBut.Edge ?= AppButEdge\n AppLed.activeLow ?= brdRec.activeLowLeds\n AppLed.em$used ?= true\n AppLed.Pin ?= AppLedPin\n AppOutUart.TxPin ?= AppOutPin\n BoardController.em$used ?= true\n BoardController.Led ?= SysLed\n Console.em$used ?= true\n Console.Provider ?= FormattingConsole\n ConsoleUart.Impl ?= AppOutUart\n DbgA.Pin ?= SysDbgA\n DbgB.Pin ?= SysDbgB\n DbgC.Pin ?= SysDbgC\n DbgD.Pin ?= SysDbgD\n Debug.Pin_a ?= DbgA\n Debug.Pin_b ?= DbgB\n Debug.Pin_c ?= DbgC\n Debug.Pin_d ?= DbgD\n EpochTime.Uptimer ?= Uptimer\n Poller.Impl ?= PollerAux\n PollerAux.OneShot ?= OneShotMilli\n SysLed.activeLow ?= brdRec.activeLowLeds\n SysLed.em$used ?= true\n SysLed.Pin ?= SysLedPin\nend\n
"},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/BoardMeta/","title":"BoardMeta","text":""},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/BoardMeta/#unit-boardmeta","title":"unit BoardMeta","text":"ti.distro.cc23xx/BoardMeta.empackage ti.distro.cc23xx\n\nfrom em.utils import BoardMeta as BaseMeta\n\nhost module BoardMeta\n\n type DrvDesc: BaseMeta.DrvDesc\n\n type PinMap: struct\n appBut: int8\n appLed: int8\n appOut: int8\n extFlashCS: int8\n extFlashCLK: int8\n extFlashPICO: int8\n extFlashPOCI: int8\n sysDbgA: int8\n sysDbgB: int8\n sysDbgC: int8\n sysDbgD: int8\n sysLed: int8\n end\n\n type Record: struct\n activeLowLeds: bool\n baudRate: uint32\n clockFreq: uint32\n extFlashDisable: bool\n lfXtalEnable: bool\n pinMap: PinMap&\n drvDescs: DrvDesc&[]\n end\n\n config baseFileLoc: string = \"ti.distro.cc23xx/em-boards\"\n\n config attrNames: string[] = [\n \"$inherits\",\n \"$overrides\",\n \"activeLowLeds\",\n \"baudRate\",\n \"clockFreq\",\n \"extFlashDisable\",\n \"lfXtalEnable\",\n ]\n\n config pinNames: string[] = [\n \"appBut\",\n \"appLed\",\n \"appOut\",\n \"extFlashCS\",\n \"extFlashCLK\",\n \"extFlashPICO\",\n \"extFlashPOCI\",\n \"sysDbgA\",\n \"sysDbgB\",\n \"sysDbgC\",\n \"sysDbgD\",\n \"sysLed\",\n ] \n\nend\n
"},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/McuC/","title":"McuC","text":""},{"location":"cargo/ti.cc23xx/ti.distro.cc23xx/McuC/#unit-mcuc","title":"unit McuC","text":"ti.distro.cc23xx/McuC.empackage ti.distro.cc23xx\n\nfrom ti.mcu.cc23xx import Regs # force ordering\n\nfrom ti.mcu.cc23xx import EdgeDetectGpioT {} as AppButEdge\nfrom ti.mcu.cc23xx import GpioT {} as AppLedPin\nfrom ti.mcu.cc23xx import GpioT {} as AppOutPin\nfrom ti.mcu.cc23xx import GpioT {} as SysDbgA\nfrom ti.mcu.cc23xx import GpioT {} as SysDbgB\nfrom ti.mcu.cc23xx import GpioT {} as SysDbgC\nfrom ti.mcu.cc23xx import GpioT {} as SysDbgD\nfrom ti.mcu.cc23xx import GpioT {} as SysLedPin\n\nfrom ti.mcu.cc23xx import ConsoleUart0 as AppOutUart\nfrom ti.mcu.cc23xx import BusyWait\nfrom ti.mcu.cc23xx import ExtFlashDisabler\nfrom ti.mcu.cc23xx import GlobalInterrupts\nfrom ti.mcu.cc23xx import Idle\nfrom ti.mcu.cc23xx import IntrVec\nfrom ti.mcu.cc23xx import Mcu\nfrom ti.mcu.cc23xx import MsCounter\nfrom ti.mcu.cc23xx import OneShotGpt3 as OneShotMilli\nfrom ti.mcu.cc23xx import Uptimer\nfrom ti.mcu.cc23xx import UsCounter\nfrom ti.mcu.cc23xx import WakeupTimer\n\nfrom em.mcu import Common\nfrom em.mcu import CommonC\n\nfrom em.utils import BoardInfo\n\nexport AppButEdge\nexport AppLedPin\nexport AppOutPin\nexport AppOutUart\nexport OneShotMilli\nexport SysDbgA\nexport SysDbgB\nexport SysDbgC\nexport SysDbgD\nexport SysLedPin\nexport Uptimer\nexport WakeupTimer\n\ncomposite McuC\n\nend\n\ndef em$preconfigure()\n auto brdRec = BoardInfo.readRecordH()\n auto pm = brdRec.pinMap\n AppButEdge.pin ?= pm.appBut\n AppLedPin.pin ?= pm.appLed\n AppOutPin.pin ?= pm.appOut\n AppOutUart.setBaudH(brdRec.baudRate)\n ExtFlashDisabler.em$used ?= brdRec.extFlashDisable\n ExtFlashDisabler.CLK_pin ?= pm.extFlashCLK\n ExtFlashDisabler.CS_pin ?= pm.extFlashCS\n ExtFlashDisabler.PICO_pin ?= pm.extFlashPICO\n ExtFlashDisabler.POCI_pin ?= pm.extFlashPOCI\n IntrVec.em$used ?= true\n Mcu.hasLfXtal ?= brdRec.lfXtalEnable\n Mcu.mclkFrequency ?= brdRec.clockFreq\n Regs.em$used ?= true\n SysDbgA.pin ?= pm.sysDbgA\n SysDbgB.pin ?= pm.sysDbgB\n SysDbgC.pin ?= pm.sysDbgC\n SysDbgD.pin ?= pm.sysDbgD\n SysLedPin.pin ?= pm.sysLed\nend\n\ndef em$configure()\n Common.BusyWait ?= BusyWait\n Common.GlobalInterrupts ?= GlobalInterrupts\n Common.Idle ?= Idle\n Common.Mcu ?= Mcu\n Common.MsCounter ?= MsCounter\n Common.UsCounter ?= UsCounter\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/","title":"Index","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/#package-timcucc23xx","title":"package ti.mcu.cc23xx","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/BusyWait/","title":"BusyWait","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/BusyWait/#unit-busywait","title":"unit BusyWait","text":"ti.mcu.cc23xx/BusyWait.empackage ti.mcu.cc23xx\n\nfrom em.hal import BusyWaitI\n\nmodule BusyWait: BusyWaitI\n\nend\n\ndef wait(usecs)\n ^HapiWaitUs(usecs)\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/BusyWaitSysTick/","title":"BusyWaitSysTick","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/BusyWaitSysTick/#unit-busywaitsystick","title":"unit BusyWaitSysTick","text":"ti.mcu.cc23xx/BusyWaitSysTick.empackage ti.mcu.cc23xx\n\nfrom em.hal import BusyWaitI\n\nmodule BusyWaitSysTick: BusyWaitI\n\nend\n\ndef wait(usecs)\n ^^SysTick->VAL^^ = 0\n ^^SysTick->LOAD^^ = usecs\n ^^SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk^^\n while ^^SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk^^ != 0\n %%[d]\n end\n ^^SysTick->CTRL^^ = 0\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/ConsoleUart0/","title":"ConsoleUart0","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/ConsoleUart0/#unit-consoleuart0","title":"unit ConsoleUart0","text":"ti.mcu.cc23xx/ConsoleUart0.empackage ti.mcu.cc23xx\n\nfrom em.hal import ConsoleUartI\nfrom em.hal import GpioI\n\nfrom em.lang import Math\n\nimport Idle\nimport Mcu\n\nmodule ConsoleUart0: ConsoleUartI\n\n proxy TxPin: GpioI\n\nprivate:\n\n config baud: uint32\n config fbrd: uint32\n config ibrd: uint32\n\n function sleepEnter: Idle.Callback\n function sleepLeave: Idle.Callback\n\nend\n\ndef em$construct()\n Idle.addSleepEnterCbH(sleepEnter)\n Idle.addSleepLeaveCbH(sleepLeave)\n auto brd = <num_t>(Mcu.mclkFrequency / (baud * 16))\n ibrd = Math.floor(brd)\n fbrd = Math.round((brd - ibrd) * 64)\nend\n\ndef em$startup()\n sleepLeave()\nend\n\ndef setBaudH(rate)\n baud = rate\nend\n\ndef sleepEnter()\n ^^HWREG(CLKCTL_BASE + CLKCTL_O_CLKENCLR0)^^ = ^CLKCTL_CLKENSET0_UART0\n TxPin.reset()\nend\n\ndef sleepLeave()\n ^^HWREG(CLKCTL_BASE + CLKCTL_O_CLKENSET0)^^ = ^CLKCTL_CLKENSET0_UART0\n TxPin.makeOutput()\n TxPin.set()\n TxPin.functionSelect(2)\n ^^HWREG(UART0_BASE + UART_O_CTL)^^ &= ~^UART_CTL_UARTEN\n ^^HWREG(UART0_BASE + UART_O_IBRD)^^ = ibrd\n ^^HWREG(UART0_BASE + UART_O_FBRD)^^ = fbrd\n ^^HWREG(UART0_BASE + UART_O_LCRH)^^ = ^UART_LCRH_WLEN_BITL8\n ^^HWREG(UART0_BASE + UART_O_CTL)^^ |= ^UART_CTL_UARTEN\nend\n\ndef flush()\n while (^^HWREG(UART0_BASE + UART_O_FR)^^ & ^UART_FR_BUSY) != 0\n end\nend\n\ndef put(data)\n ^^HWREG(UART0_BASE + UART_O_DR)^^ = data\n flush()\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/EdgeDetectGpioAux/","title":"EdgeDetectGpioAux","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/EdgeDetectGpioAux/#unit-edgedetectgpioaux","title":"unit EdgeDetectGpioAux","text":"ti.mcu.cc23xx/EdgeDetectGpioAux.empackage ti.mcu.cc23xx\n\nimport InterruptT { name: \"GPIO_COMB\" } as Intr\n\nmodule EdgeDetectGpioAux\n\n type Handler: function ()\n\n type HandlerInfo: struct\n link: HandlerInfo&\n mask: uint32\n handler: Handler\n end\n\n function addHandler(hi: HandlerInfo&)\n\n private:\n\n var handlerList: HandlerInfo&\n function edgeIsr: Intr.Handler\n\nend\n\ndef em$construct()\n Intr.setHandlerH(edgeIsr)\nend\n\ndef em$startup()\n Intr.enable()\nend\n\ndef addHandler(hi)\n hi.link = handlerList\n handlerList = hi\nend\n\ndef edgeIsr()\n auto mis = <uint32>^^HWREG(GPIO_BASE + GPIO_O_MIS)^^\n for hi: HandlerInfo& = handlerList; hi != null; hi = hi.link\n hi.handler() if (mis & hi.mask) && hi.handler\n end\n ^^HWREG(GPIO_BASE + GPIO_O_ICLR)^^ = 0xffffffff\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/EdgeDetectGpioT/","title":"EdgeDetectGpioT","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/EdgeDetectGpioT/#unit-edgedetectgpiot","title":"unit EdgeDetectGpioT","text":"ti.mcu.cc23xx/EdgeDetectGpioT.empackage ti.mcu.cc23xx\n\ntemplate EdgeDetectGpioT\n\n const UNDEF: int8 = -1\n\n config pin: int8 = UNDEF\n\nend\n\ndef em$generateUnit(pn, un)\n auto bq = \"`\"\n auto p = pin\n auto pre = ^^pn.replace(/[.]/g, '_') + '_' + un + '__'^^\n|->>>\n package `pn`\n\n from ti.mcu.cc23xx import EdgeDetectGpioAux as Aux\n from ti.mcu.cc23xx import GpioT {} as Pin\n\n from em.hal import GpioEdgeDetectMinI\n\n module `un`: GpioEdgeDetectMinI\n\n config pin: int16 = `p`\n\n private:\n\n config isDef: bool\n config mask: uint32 \n\n var info: Aux.HandlerInfo\n\n end\n\n def em$configure()\n Pin.pin ?= pin\n end\n\n def em$construct()\n isDef = pin != `UNDEF`\n mask = isDef ? <uint32>(1 << <uint8>pin) : 0\n info.mask = mask\n end\n\n def em$startup()\n Aux.addHandler(info)\n end \n\n def clear() \n Pin.clear()\n end\n\n def set()\n Pin.set()\n end\n\n def get()\n return Pin.get()\n end\n\n def toggle()\n Pin.toggle()\n end\n\n def isInput()\n return Pin.isInput()\n end\n\n def isOutput()\n return Pin.isOutput()\n end\n\n def makeInput()\n Pin.makeInput()\n end\n\n def makeOutput()\n Pin.makeOutput()\n end\n\n def functionSelect(select)\n Pin.functionSelect(select)\n end\n\n def setInternalPullup(enable)\n Pin.setInternalPullup(enable)\n end\n\n def pinId()\n return pin\n end\n\n def reset()\n Pin.reset()\n end\n\n def enableDetect()\n ^^HWREG(GPIO_BASE + GPIO_O_IMSET)^^ = mask if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ |= ^IOC_IOC0_WUENSB if isDef\n end\n\n def disableDetect()\n ^^HWREG(GPIO_BASE + GPIO_O_IMCLR)^^ = mask if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ &= ~^IOC_IOC0_WUENSB if isDef\n end\n\n def clearDetect()\n ^^HWREG(GPIO_BASE + GPIO_O_ICLR)^^ = mask if isDef\n end\n\n def setDetectRisingEdge()\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ &= ~^IOC_IOC0_EDGEDET_M if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ |= ^IOC_IOC0_EDGEDET_EDGE_POS if isDef\n end\n\n def setDetectFallingEdge()\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ &= ~^IOC_IOC0_EDGEDET_M if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ |= ^IOC_IOC0_EDGEDET_EDGE_NEG if isDef\n end\n\n def setDetectHandlerH(h)\n info.handler = <Aux.Handler>h\n end\n|-<<<\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/ExtFlashDisabler/","title":"ExtFlashDisabler","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/ExtFlashDisabler/#unit-extflashdisabler","title":"unit ExtFlashDisabler","text":"ti.mcu.cc23xx/ExtFlashDisabler.empackage ti.mcu.cc23xx\n\nimport GpioT {} as CS\nimport GpioT {} as CLK\nimport GpioT {} as PICO\nimport GpioT {} as POCI\n\nfrom em.mcu import Common\n\nmodule ExtFlashDisabler\n\n config CS_pin: int8\n config CLK_pin: int8\n config PICO_pin: int8\n config POCI_pin: int8\n\nprivate:\n\n const SD_CMD: uint8 = 0xb9\n\nend\n\ndef em$construct()\n CS.pin = CS_pin\n CLK.pin = CLK_pin\n PICO.pin = PICO_pin\n POCI.pin = POCI_pin\nend\n\ndef em$startup()\n %%[c+]\n CS.makeOutput()\n CLK.makeOutput()\n PICO.makeOutput()\n POCI.makeInput()\n # attention\n CS.set()\n Common.BusyWait.wait(1)\n CS.clear()\n Common.BusyWait.wait(1)\n CS.set()\n Common.BusyWait.wait(50)\n # shutdown command\n CS.clear()\n for auto i = 0; i < 8; i++\n CLK.clear()\n if ((SD_CMD >> (7 - i)) & 0x01) == 0\n PICO.clear()\n else\n PICO.set()\n end\n CLK.set()\n Common.BusyWait.wait(1)\n end\n CLK.clear()\n CS.set()\n Common.BusyWait.wait(50)\n #\n CS.reset()\n CLK.reset()\n PICO.reset()\n POCI.reset()\n %%[c-]\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/GlobalInterrupts/","title":"GlobalInterrupts","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/GlobalInterrupts/#unit-globalinterrupts","title":"unit GlobalInterrupts","text":"ti.mcu.cc23xx/GlobalInterrupts.empackage ti.mcu.cc23xx\n\nfrom em.hal import GlobalInterruptsI\n\nmodule GlobalInterrupts: GlobalInterruptsI\n\nend\n\ndef disable()\n auto key = <uarg_t>(^^__get_PRIMASK()^^)\n ^^__set_PRIMASK(1)^^\n return key\nend\n\ndef enable()\n ^^__set_PRIMASK(0)^^\nend\n\ndef restore(key)\n ^^__set_PRIMASK(key)^^\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/GpioT/","title":"GpioT","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/GpioT/#unit-gpiot","title":"unit GpioT","text":"ti.mcu.cc23xx/GpioT.empackage ti.mcu.cc23xx\n\ntemplate GpioT\n\n const UNDEF: int16 = -1\n\n config pin: int16 = UNDEF\n\nend\n\ndef em$generateUnit(pn, un)\n auto bq = \"`\"\n auto pre = ^^pn.replace(/[.]/g, '_') + '_' + un + '__'^^\n|->>>\n package `pn`\n\n from em.hal import GpioI\n\n module `un`: GpioI\n\n config pin: int16 = `pin`\n\n function pinMask(): uint32 \n\n private:\n\n config isDef: bool\n config mask: uint32\n\n end\n\n def em$construct()\n isDef = pin != `UNDEF`\n mask = isDef ? <uint32>(1 << <uint8>pin) : 0\n end\n\n def clear() \n ^^HWREG(GPIO_BASE + GPIO_O_DOUTCLR31_0)^^ = mask if isDef\n end\n\n def set()\n ^^HWREG(GPIO_BASE + GPIO_O_DOUTSET31_0)^^ = mask if isDef\n end\n\n def get()\n return 0 if !isDef\n return isInput() ? ((^^HWREG(GPIO_BASE + GPIO_O_DIN31_0)^^ & mask) != 0) : ((^^HWREG(GPIO_BASE + GPIO_O_DOUT31_0)^^ & mask) != 0)\n end\n\n def toggle()\n ^^HWREG(GPIO_BASE + GPIO_O_DOUTTGL31_0)^^ = mask if isDef\n end\n\n def isInput()\n return isDef && (^^HWREG(GPIO_BASE + GPIO_O_DOE31_0)^^ & mask) == 0\n end\n\n def isOutput()\n return isDef && (^^HWREG(GPIO_BASE + GPIO_O_DOE31_0)^^ & mask) != 0\n end\n\n def makeInput()\n ^^HWREG(GPIO_BASE + GPIO_O_DOECLR31_0)^^ = mask if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ |= ^IOC_IOC0_INPEN if isDef\n end\n\n def makeOutput()\n ^^HWREG(GPIO_BASE + GPIO_O_DOESET31_0)^^ = mask if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ &= ~^IOC_IOC0_INPEN if isDef\n end\n\n def functionSelect(select)\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ = select if isDef\n end\n\n def setInternalPullup(enable)\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ |= ^IOC_IOC0_PULLCTL_PULL_UP if isDef && enable\n end\n\n def pinId()\n return pin\n end\n\n def pinMask()\n return mask\n end\n\n def reset()\n ^^HWREG(GPIO_BASE + GPIO_O_DOECLR31_0)^^ = mask if isDef\n ^^HWREG(IOC_BASE + IOC_O_IOC0 + pin * 4)^^ |= (^IOC_IOC0_IOMODE_M | ^IOC_IOC0_PULLCTL_M) if isDef\n end\n|-<<<\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Idle/","title":"Idle","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Idle/#unit-idle","title":"unit Idle","text":"ti.mcu.cc23xx/Idle.empackage ti.mcu.cc23xx\n\nfrom em.hal import IdleI\nfrom em.lang import Debug\n\nmodule Idle: IdleI\n\n type Callback: function()\n\n host function addSleepEnterCbH(cb: Callback) \n host function addSleepLeaveCbH(cb: Callback) \n\n function doSleep()\n function doWait()\n\n function setWaitOnly(val: bool)\n\nprivate:\n\n config sleepEnterCbTab: Callback[..]\n config sleepLeaveCbTab: Callback[..]\n\n var waitOnly: bool\n\nend\n\ndef em$startup()\n %%[b+]\n ^^HWREG(PMCTL_BASE + PMCTL_O_VDDRCTL)^^ = ^PMCTL_VDDRCTL_SELECT # LDO\n ^^HWREG(EVTULL_BASE + EVTULL_O_WKUPMASK)^^ = ^EVTULL_WKUPMASK_AON_RTC_COMB | ^EVTULL_WKUPMASK_AON_IOC_COMB\nend\n\ndef addSleepEnterCbH(cb)\n sleepEnterCbTab[sleepEnterCbTab.length++] = cb\nend\n\ndef addSleepLeaveCbH(cb)\n sleepLeaveCbTab[sleepLeaveCbTab.length++] = cb\nend\n\ndef doSleep()\n for cb in sleepEnterCbTab\n cb()\n end\n %%[b:2]\n %%[b-]\n Debug.sleepEnter()\n ^^HWREG(CKMD_BASE + CKMD_O_LDOCTL)^^ = 0x0\n ^^__set_PRIMASK(1)^^\n ^^HapiEnterStandby(NULL)^^\n Debug.sleepLeave()\n %%[b+]\n for cb in sleepLeaveCbTab\n cb()\n end\n ^^__set_PRIMASK(0)^^\nend\n\ndef doWait()\n %%[b:1]\n %%[b-]\n ^^__set_PRIMASK(1)^^\n ^^asm(\"wfi\")^^\n %%[b+]\n ^^__set_PRIMASK(0)^^\nend\n\n\ndef exec()\n if waitOnly\n doWait()\n else\n doSleep()\n end\nend\n\ndef setWaitOnly(val)\n waitOnly = val\nend\n\ndef wakeup()\n ## TODO -- implement\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/InterruptT/","title":"InterruptT","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/InterruptT/#unit-interruptt","title":"unit InterruptT","text":"ti.mcu.cc23xx/InterruptT.empackage ti.mcu.cc23xx\n\ntemplate InterruptT\n\n config name: string\n\nend\n\ndef em$generateUnit(pn, un) \n auto intrName = name\n auto handlerNameQ = \"`\" + un + \".handlerName`\"\n|->>>\n package `pn`\n\n from ti.mcu.cc23xx import IntrVec\n\n from em.hal import InterruptSourceI\n\n module `un`: InterruptSourceI \n\n private:\n host var handlerName: string\n end\n\n def em$construct()\n IntrVec.addIntrH(\"`intrName`\")\n end\n\n def em$generateCode( prefix )\n if `un`.handlerName\n |-> void `intrName`_Handler() {\n |-> `handlerNameQ`();\n |-> }\n end\n end\n\n def setHandlerH(h)\n handlerName = h ? ^^String(h).substring(1)^^ : null\n end\n\n def enable() \n ^^NVIC_EnableIRQ(`intrName`_IRQn)^^\n end\n\n def disable() \n ^^NVIC_DisableIRQ(`intrName`_IRQn)^^\n end\n\n def clear()\n ^^NVIC_ClearPendingIRQ(`intrName`_IRQn)^^\n end\n\n def isEnabled() \n return ^^NVIC_GetEnableIRQ(`intrName`_IRQn)^^\n end\n|-<<<\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/IntrVec/","title":"IntrVec","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/IntrVec/#unit-intrvec","title":"unit IntrVec","text":"ti.mcu.cc23xx/IntrVec.empackage ti.mcu.cc23xx\n\nfrom em.hal import IntrVecI\n\nmodule IntrVec: IntrVecI\n\n host function addIntrH(name: string)\n\nprivate:\n\n const HARD_FAULT: uint32 = 3\n\n type IsrFxn: function()\n\n host config nameTab: string[] = [\n \"NMI\",\n \"HardFault\",\n null,\n null,\n null,\n null,\n null,\n null,\n null,\n \"SVC\",\n null,\n null,\n \"PendSV\",\n \"SysTick\",\n \"CPUIRQ0\",\n \"CPUIRQ1\",\n \"CPUIRQ2\",\n \"CPUIRQ3\",\n \"CPUIRQ4\",\n \"GPIO_COMB\", \n \"LRFD_IRQ0\", \n \"LRFD_IRQ1\", \n \"DMA_DONE_COMB\", \n \"AES_COMB\", \n \"SPI0_COMB\", \n \"UART0_COMB\", \n \"I2C0_IRQ\", \n \"LGPT0_COMB\", \n \"LGPT1_COMB\", \n \"ADC0_COMB\", \n \"CPUIRQ16\", \n \"LGPT2_COMB\", \n \"LGPT3_COMB\",\n ]\n\n host config usedTab: string[]\n\n config excHandler: ExceptionHandler\n\n function nullIsr()\n\nend\n\ndef em$generateCode(prefix)\n|->>>\ntypedef void( *intfunc )( void );\ntypedef union { intfunc fxn; void* ptr; } intvec_elem;\n\n|-<<<\n for n in nameTab\n continue if n == null\n |-> extern void `n`_Handler( void );\n |-> #define `n`_ISR `prefix`::nullIsr\n end \n|->>>\n\n|-<<<\n for u in usedTab\n |-> #undef `u`_ISR\n |-> #define `u`_ISR `u`_Handler\n end \n|->>>\n\nextern em_uint32 __stack_top__;\nextern \"C\" void __em_program_start( void );\nextern \"C\" const intvec_elem __attribute__((section(\".intvec\"))) __vector_table[] = {\n { .ptr = (void*)&__stack_top__ },\n { .fxn = __em_program_start },\n|-<<<\n for n in nameTab\n if n == null\n |-> 0,\n else\n |-> { .fxn = `n`_ISR },\n end\n end \n |-> };\n|->>>\n\n|-<<<\nend\n\ndef em$startup()\n ^^SCB->VTOR = (uint32_t)(&__vector_table)^^\nend\n\ndef addIntrH(name)\n usedTab[usedTab.length++] = name\nend\n\ndef bindExceptionHandlerH(handler)\n excHandler = handler\nend\n\ndef nullIsr()\n auto vecNum = <uint32>(^^__get_IPSR()^^)\n %%[b:4]\n %%[><uint8>vecNum]\n auto frame = <uint32[]>(^^__get_MSP()^^)\n %%[><uint32>&frame[0]]\n for auto i = 0; i < 8; i++\n %%[b]\n %%[>frame[i]]\n end\n fail\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Mcu/","title":"Mcu","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Mcu/#unit-mcu","title":"unit Mcu","text":"ti.mcu.cc23xx/Mcu.empackage ti.mcu.cc23xx\n\nfrom em.hal import McuI\n\nfrom em.lang import Debug\n\nmodule Mcu: McuI\n\n config noCache: bool\n config hasLfXtal: bool\n\nend\n\ndef getResetCode()\n ## TODO -- implement\n return 0\nend\n\ndef getStashAddr()\n ## TODO -- implement\n return null\nend\n\ndef isWarm()\n ## TODO -- implement\n return false\nend\n\ndef readEui48(dst)\n ## TODO -- implement\nend\n\ndef reset(code)\n ## TODO -- implement\nend\n\ndef startup()\n Debug.startup()\n if hasLfXtal\n ^^HWREG(CKMD_BASE + CKMD_O_LFINCOVR) = 0x001E8480 | CKMD_LFINCOVR_OVERRIDE_M^^\n ^^HWREG(CKMD_BASE + CKMD_O_LFCLKSEL) = CKMD_LFCLKSEL_MAIN_LFXT^^\n ^^HWREG(CKMD_BASE + CKMD_O_LFXTCTL) = CKMD_LFXTCTL_EN^^\n ^^HWREG(CKMD_BASE + CKMD_O_IMSET) = CKMD_IMASK_LFCLKGOOD^^\n else\n ^^HWREG(CKMD_BASE + CKMD_O_TRIM1) |= CKMD_TRIM1_NABIAS_LFOSC^^\n ^^HWREG(CKMD_BASE + CKMD_O_LFCLKSEL) = CKMD_LFCLKSEL_MAIN_LFOSC^^\n ^^HWREG(CKMD_BASE + CKMD_O_LFOSCCTL) = CKMD_LFOSCCTL_EN^^\n ^^HWREG(CKMD_BASE + CKMD_O_LFINCCTL) &= ~CKMD_LFINCCTL_PREVENTSTBY_M^^\n ^^HWREG(CKMD_BASE + CKMD_O_IMSET) = CKMD_IMASK_LFCLKGOOD^^\n end\n ^^HWREG(CLKCTL_BASE + CLKCTL_O_IDLECFG)^^ = 1 if noCache\n ^^HWREG(VIMS_BASE + VIMS_O_CCHCTRL)^^ = 0 if noCache\nend\n\ndef shutdown()\n ## TODO -- implement\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/MsCounter/","title":"MsCounter","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/MsCounter/#unit-mscounter","title":"unit MsCounter","text":"ti.mcu.cc23xx/MsCounter.empackage ti.mcu.cc23xx\n\nfrom em.hal import MsCounterI\n\nimport Rtc\n\nmodule MsCounter: MsCounterI\n\nprivate:\n\n var t0: uint32\n\nend\n\ndef start()\n t0 = Rtc.getMsecs()\nend\n\ndef stop()\n return 0 if t0 == 0\n auto t1 = Rtc.getMsecs()\n auto dt = (t1 > t0) ? (t1 - t0) : (t0 - t1)\n t0 = 0\n return dt\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/OneShotGpt3/","title":"OneShotGpt3","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/OneShotGpt3/#unit-oneshotgpt3","title":"unit OneShotGpt3","text":"ti.mcu.cc23xx/OneShotGpt3.empackage ti.mcu.cc23xx\n\nimport InterruptT { name: \"LGPT3_COMB\" } as Intr\n\nimport BusyWait\nimport Idle\nimport Mcu\n\nfrom em.hal import OneShotMilliI\n\nmodule OneShotGpt3: OneShotMilliI\n\nprivate:\n\n var curArg: ptr_t\n var curFxn: Handler\n\n function isr: Intr.Handler\n\nend\n\ndef em$construct()\n Intr.setHandlerH(isr)\nend\n\ndef disable()\n curFxn = null\n Idle.setWaitOnly(false)\n Intr.disable()\n ^^HWREG(LGPT3_BASE + LGPT_O_ICLR) = LGPT_ICLR_TGT^^\nend\n\ndef enable(msecs, handler, arg)\n curFxn = handler\n curArg = arg\n Idle.setWaitOnly(true)\n Intr.enable()\n ^^HWREG(CLKCTL_BASE + CLKCTL_O_CLKENSET0)^^ = ^CLKCTL_CLKENSET0_LGPT3\n ^^HWREG(LGPT3_BASE + LGPT_O_IMSET) = LGPT_IMSET_TGT^^\n ^^HWREG(LGPT3_BASE + LGPT_O_TGT)^^ = msecs * (Mcu.mclkFrequency / 1000)\n ^^HWREG(LGPT3_BASE + LGPT_O_CTL) = LGPT_CTL_MODE_UP_ONCE | LGPT_CTL_C0RST^^\nend\n\ndef isr()\n auto fxn = curFxn\n disable()\n fxn(curArg) if fxn\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/OneShotSysTick/","title":"OneShotSysTick","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/OneShotSysTick/#unit-oneshotsystick","title":"unit OneShotSysTick","text":"ti.mcu.cc23xx/OneShotSysTick.empackage ti.mcu.cc23xx\n\nimport InterruptT { name: \"SysTick\" } as Intr\n\nimport Idle\nimport Mcu\n\nfrom em.hal import OneShotMilliI\n\nmodule OneShotSysTick: OneShotMilliI\n\nprivate:\n\n var curArg: ptr_t\n var curFxn: Handler\n\n function isr: Intr.Handler\n\nend\n\ndef em$construct()\n Intr.setHandlerH(isr)\nend\n\ndef em$startup()\n ^^SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk^^\nend\n\ndef disable()\n curFxn = null\n Idle.setWaitOnly(false)\n Intr.disable()\n ^^SysTick->CTRL &= ~(SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk)^^\nend\n\ndef enable(msecs, handler, arg)\n curFxn = handler\n curArg = arg\n Idle.setWaitOnly(true)\n Intr.enable()\n ^^SysTick->LOAD^^ = msecs * (Mcu.mclkFrequency / 1000)\n ^^SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk^^\nend\n\ndef isr()\n auto fxn = curFxn\n disable()\n fxn(curArg) if fxn\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/OneShotSysTim0/","title":"OneShotSysTim0","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/OneShotSysTim0/#unit-oneshotsystim0","title":"unit OneShotSysTim0","text":"ti.mcu.cc23xx/OneShotSysTim0.empackage ti.mcu.cc23xx\n\nimport InterruptT { name: \"CPUIRQ1\" } as Intr\n\nimport Idle\nimport Mcu\n\nfrom em.hal import OneShotMilliI\n\nmodule OneShotSysTim0: OneShotMilliI\n\nprivate:\n\n var curArg: ptr_t\n var curFxn: Handler\n\n function isr: Intr.Handler\n\nend\n\ndef em$construct()\n Intr.setHandlerH(isr)\nend\n\ndef disable()\n curFxn = null\n Idle.setWaitOnly(false)\n Intr.disable()\n ^^HWREG(SYSTIM_BASE + SYSTIM_O_ICLR) = SYSTIM_ICLR_EV0^^\nend\n\ndef enable(msecs, handler, arg)\n curFxn = handler\n curArg = arg\n Idle.setWaitOnly(true)\n Intr.clear()\n Intr.enable()\n ^^HWREG(EVTSVT_BASE + EVTSVT_O_CPUIRQ1SEL) = EVTSVT_CPUIRQ1SEL_PUBID_SYSTIM0^^\n ^^HWREG(SYSTIM_BASE + SYSTIM_O_IMSET) = SYSTIM_IMSET_EV0^^\n auto time1u = <uint32>(^^HWREG(SYSTIM_BASE + SYSTIM_O_TIME1U)^^)\n auto thresh = time1u + (msecs * 1000)\n ^^HWREG(SYSTIM_BASE + SYSTIM_O_CH0CC)^^ = thresh\n printf \"time1u = %d, thresh = %d\\n\", time1u, thresh\nend\n\ndef isr()\n %%[a]\n auto fxn = curFxn\n disable()\n fxn(curArg) if fxn\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Regs/","title":"Regs","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Regs/#unit-regs","title":"unit Regs","text":"ti.mcu.cc23xx/Regs.empackage ti.mcu.cc23xx\n\nmodule Regs\n\nend\n\ndef em$generateCode(prefix)\n|->>>\n #include \"cmsis/cc23x0r5.h\"\n #include \"cmsis/core/core_cm0plus.h\"\n\n #include \"driverlib/hapi.h\"\n\n #include \"inc/hw_memmap.h\"\n #include \"inc/hw_types.h\"\n\n #include \"inc/hw_ckmd.h\"\n #include \"inc/hw_clkctl.h\"\n #include \"inc/hw_evtull.h\"\n #include \"inc/hw_evtsvt.h\"\n #include \"inc/hw_gpio.h\"\n #include \"inc/hw_ioc.h\"\n #include \"inc/hw_lgpt.h\"\n #include \"inc/hw_lgpt3.h\"\n #include \"inc/hw_pmctl.h\"\n #include \"inc/hw_rtc.h\"\n #include \"inc/hw_systim.h\"\n #include \"inc/hw_uart.h\"\n #include \"inc/hw_vims.h\"\n\n #include \"inc/hw_ccfg.h\"\n\n#if 0\n\n extern \"C\" const ccfg_t __ccfg __attribute__((section(\".ccfg\"), used)) = {\n\n .bootCfg.pBldrVtor = XCFG_BC_PBLDR_UNDEF,\n\n .bootCfg.bldrParam.serialRomBldrParamStruct.bldrEnabled = XCFG_BC_BLDR_DIS,\n .bootCfg.bldrParam.serialRomBldrParamStruct.serialIoCfgIndex = 0,\n .bootCfg.bldrParam.serialRomBldrParamStruct.pinTriggerDio = 0,\n .bootCfg.bldrParam.serialRomBldrParamStruct.pinTriggerEnabled = XCFG_BC_PINTRIG_DIS,\n .bootCfg.bldrParam.serialRomBldrParamStruct.pinTriggerLevel = XCFG_BC_PINTRIG_LEVEL_LO,\n .bootCfg.pAppVtor = (void*)0x0,\n\n .hwOpts = {0xffffffff, 0xffffffff},\n\n .permissions.allowDebugPort = CCFG_PERMISSION_ALLOW,\n .permissions.allowEnergyTrace = CCFG_PERMISSION_ALLOW,\n .permissions.allowFlashVerify = CCFG_PERMISSION_ALLOW,\n .permissions.allowFlashProgram = CCFG_PERMISSION_ALLOW,\n .permissions.allowChipErase = CCFG_PERMISSION_ALLOW,\n .permissions.allowToolsClientMode = CCFG_PERMISSION_ALLOW,\n .permissions.allowFakeStby = CCFG_PERMISSION_ALLOW,\n .permissions.allowReturnToFactory = CCFG_PERMISSION_ALLOW,\n\n .misc.saciTimeoutOverride = 1U,\n .misc.saciTimeoutExp = 7,\n\n .flashProt.writeEraseProt.mainSectors0_31 = 0xffffffff,\n .flashProt.writeEraseProt.mainSectors32_255 = 0xffffffff,\n\n .flashProt.writeEraseProt.ccfgSector = 0,\n .flashProt.writeEraseProt.fcfgSector = 0,\n .flashProt.writeEraseProt.engrSector = 0,\n\n .flashProt.res = 0xFFFFFFFFU,\n\n .flashProt.chipEraseRetain.mainSectors0_31 = 0x0,\n .flashProt.chipEraseRetain.mainSectors32_255 = 0x0,\n\n .debugCfg.authorization = CCFG_DBGAUTH_DBGOPEN,\n .debugCfg.allowBldr = CCFG_DBGBLDR_ALLOW,\n .debugCfg.pwdId = {0x01, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0d, 0x15},\n .debugCfg.pwdHash = {0x6d, 0xd7, 0xe4, 0x36, 0xeb, 0xf4, 0x31, 0xdf,\n 0x95, 0xae, 0x15, 0xee, 0x03, 0xba, 0x8e, 0xe4,\n 0xc4, 0xc6, 0x3f, 0xd8, 0x45, 0x3f, 0x67, 0x5e,\n 0x74, 0xd7, 0xc2, 0x01, 0x2c, 0x90, 0x58, 0xe5},\n };\n\n#else\n\n extern \"C\" const uint32_t __ccfg[] __attribute__((section(\".ccfg\"), used)) = {\n 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,\n 0xFFFFFFFF, 0xFFFFFFFF, 0xAAAAAAAA, 0x0000000F,\n 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x00000000, 0x00000000, 0x00000000, 0x00000000,\n 0x0000A55A, 0x03020101, 0x150D0805, 0x36E4D76D,\n 0xDF31F4EB, 0xEE15AE95, 0xE48EBA03, 0xD83FC6C4,\n 0x5E673F45, 0x01C2D774, 0xE558902C, 0x00000000,\n };\n#endif\n|-<<<\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Rtc/","title":"Rtc","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Rtc/#unit-rtc","title":"unit Rtc","text":"ti.mcu.cc23xx/Rtc.empackage ti.mcu.cc23xx\n\nimport InterruptT { name: \"CPUIRQ0\" } as Intr\n\nmodule Rtc\n\n type Handler: function()\n\n function disable()\n function enable(thresh: uint32, handler: Handler)\n function getMsecs(): uint32\n function getRaw(oSubs: uint32*): uint32\n function toThresh(ticks: uint32): uint32\n function toTicks(secs256: uint32): uint32\n\nprivate:\n\n const MSECS_SCALAR: uint16 = 1000 / 8\n const RES_BITS: uint8 = 20\n\n var curHandler: Handler\n\n function isr: Intr.Handler\n\nend\n\ndef em$construct()\n Intr.setHandlerH(isr)\nend\n\ndef em$startup()\n ^^HWREG(CKMD_BASE + CKMD_O_LFINCOVR)^^ = 0x80000000 + (1 << RES_BITS)\n ^^HWREG(RTC_BASE + RTC_O_CTL)^^ = ^RTC_CTL_RST\n ^^HWREG(EVTSVT_BASE + EVTSVT_O_CPUIRQ0SEL) = EVTSVT_CPUIRQ0SEL_PUBID_AON_RTC_COMB^^\n Intr.enable()\nend\n\ndef disable()\n curHandler = null\n ^^HWREG(RTC_BASE + RTC_O_IMCLR)^^ = ^RTC_IMCLR_EV0\nend\n\ndef enable(thresh, handler)\n curHandler = handler\n ^^HWREG(RTC_BASE + RTC_O_CH0CC8U)^^ = thresh\n ^^HWREG(RTC_BASE + RTC_O_IMSET)^^ = ^RTC_IMSET_EV0\nend\n\ndef getMsecs()\n auto ticks = <uint32>^^HWREG(RTC_BASE + RTC_O_TIME8U)^^\n return (ticks * MSECS_SCALAR) >> (RES_BITS - 7)\nend\n\ndef getRaw(oSubs)\n var lo: uint32\n var hi: uint32\n for ;;\n lo = ^^HWREG(RTC_BASE + RTC_O_TIME8U)^^\n hi = ^^HWREG(RTC_BASE + RTC_O_TIME524M)^^\n break if lo == ^^HWREG(RTC_BASE + RTC_O_TIME8U)^^\n end\n *oSubs = lo << 16\n return hi\nend\n\ndef isr()\n ^^HWREG(RTC_BASE + RTC_O_ICLR)^^ = ^RTC_ICLR_EV0\n curHandler() if curHandler\nend\n\n\ndef toThresh(ticks)\n return ^^HWREG(RTC_BASE + RTC_O_TIME8U)^^ + ticks\nend\n\ndef toTicks(secs256)\n return secs256 << 8\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Uptimer/","title":"Uptimer","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/Uptimer/#unit-uptimer","title":"unit Uptimer","text":"ti.mcu.cc23xx/Uptimer.empackage ti.mcu.cc23xx\n\nimport Rtc\n\nfrom em.hal import UptimerI\n\nmodule Uptimer: UptimerI\n\nprivate:\n\n var curTime: Time\n\nend\n\ndef calibrate(secs256, ticks)\n ## TODO -- implement\n return 0\nend\n\ndef read()\n curTime.secs = Rtc.getRaw(&curTime.subs)\n return curTime\nend\n\ndef resetSync()\n ## TODO -- implement\nend\n\ndef trim()\n ## TODO -- implement\n return 0\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/UsCounter/","title":"UsCounter","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/UsCounter/#unit-uscounter","title":"unit UsCounter","text":"ti.mcu.cc23xx/UsCounter.empackage ti.mcu.cc23xx\n\nfrom em.hal import UsCounterI\n\nimport Mcu\n\nmodule UsCounter: UsCounterI\n\nend\n\ndef start()\n ^^SysTick->CTRL = (1 << SysTick_CTRL_CLKSOURCE_Pos) | (1 << SysTick_CTRL_ENABLE_Pos)^^\n ^^SysTick->LOAD = 0xFFFFFF^^\n ^^SysTick->VAL = 0^^\nend\n\ndef stop()\n auto lr = <uint32>^^SysTick->LOAD^^\n auto vr = <uint32>^^SysTick->VAL^^\n auto dt = (((lr - vr) << 1) / (Mcu.mclkFrequency / 1000000)) >> 1\n ^^SysTick->CTRL = 0^^\n return dt\nend\n
"},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/WakeupTimer/","title":"WakeupTimer","text":""},{"location":"cargo/ti.cc23xx/ti.mcu.cc23xx/WakeupTimer/#unit-wakeuptimer","title":"unit WakeupTimer","text":"ti.mcu.cc23xx/WakeupTimer.empackage ti.mcu.cc23xx\n\nfrom em.hal import WakeupTimerI\n\nimport Rtc\n\nmodule WakeupTimer: WakeupTimerI\n\nend\n\ndef disable()\n Rtc.disable()\nend\n\ndef enable(secs256, handler)\n Rtc.enable(secs256, <Rtc.Handler>handler)\nend\n\ndef secs256ToTicks(secs256)\n return secs256 << 8\nend\n\ndef ticksToThresh(ticks)\n return Rtc.toThresh(ticks)\nend\n\ndef timeToTicks(secs, subs)\n return (secs << 16) | (subs >> 16)\nend\n
"},{"location":"intro/","title":"Why another language\u2009???","text":"For the past fifty years, the C Programming Language has significantly streamlined software development for resource-constrained embedded microcontrollers [MCUs]. Already coming into maturity by 1980, C had proven itself as a practical \u201cmedium-level\u201d language for many of the first 8\u2009/\u200916\u2009/\u200932-bit MCUs \u2013 enabling full entitlement to the underlying silicon, while offering developers greater software productivity and portability over native assembly language.
As a testimony to its staying power, C continues to this day as the dominant programming language for resource-constrained MCUs; and given that we still write embedded software targeting 8\u2009/\u200916\u2009/\u200932-bit processors \u2013 often with less than 32K of memory \u2013 do we really have a practical alternative to C\u2009???\u00a0\u00a0 Yes, we do \u2013 EM.
Other embedded programming languagesBesides C and assembler, practitioners cite C++, Java, and Python as other languages of choice for embedded software development \u2013 with interest in Go and Rust starting to grow. For a variety of reasons, none of these languages come close to overtaking the dominant position of C:\u00a0 developers often fear a steep learning curve with a new language, offsetting any potential gain in productivity; and the language itself might introduce additional runtime overhead in time and (especially) space that render it impractical for the most resource-constrained MCUs.
"},{"location":"intro/#the-big-picture","title":"The big picture","text":"The EM software platform comprises a novel programming language and run-time environment which targets resource-constrained embedded hardware \u2013 on the low-end, systems potentially managed by 8-bit MCUs using as little as 4\u2009K of program memory and 256\u2009B of data. Compared with C, EM features a higher-level programming paradigm centered around highly-reusable software modules which embody the principles of encapsulation, abstraction, and composition.
At the same time, higher-level language constructs introduced by EM don't necessarily cause higher-levels of run-time overhead; modular EM applications designed using modern software techniques will often outperform comparable C programs written in a more conventional style. As you'll learn later on, the EM language translator will in fact generate monolithic C/C++ programs from a set of EM source modules \u2013 leveraging the maturity and ubiquity of optimizing C/C++compilers for embedded MCUs.
More than a programming language, the EM platform also features a modular run-time environment (written in EM, of course\u2009!!!) which streamlines developer productivity through software re-use as well as increases application portability through abstraction \u2013 all without compromising overall system performance. The EM run-time modules include basic services which handle and dispatch real-time application events, as well as device-drivers which manage hardware peripherals typically found within embedded MCUs.
Write once, run anywhereNot unlike other software platforms ranging from Java and Linux to Ruby and Python, EM offers up a \"write once, run anywhere\u2009...\" value-proposition for its application developers. The difference, needless to say, lies in the underlying hardware configurations targeted by EM \u2013 memory-constrained MCUs otherwise incapable of supporting Java or Linux (let alone Ruby or Python), for which C has remained the dominant programming environment for over a half-century.
"},{"location":"intro/#tiny-code-tiny-chips","title":"Tiny code \u2192 Tiny chips","text":"EM programs typically consume less memory than their C counterparts \u2013 a critical feature when targeting resource-constrained MCUs. With 3\u2009X\u2009\u2013\u20095\u2009X reductions in program size not uncommon in practice, many real-world EM applications can comfortably fit in (say) a 32K memory space.
The latest 32-bit MCUs deployed in edge-computing applications will often feature generous amounts of flash memory [\u2009\u2265\u2009512K\u2009] for storing program code, as well as large blocks of SRAM [\u2009\u2265\u200964K\u2009] for reading\u2009+\u2009writing program data; these MCUs also feature processors with sophisticated pipelines as well as advanced memory caches \u2013 all boosting the performance of large (and ever-growing\u2009!!!) bodies of legacy C code used in today's applications.
But what if our EM-based applications really did require only 32\u2009K of memory\u2009???
We could take conventional MCU designs \"over-the-edge\" by embracing a radically simple set of hardware featues which collectively would define the fringe of embedded processing:
\u00a0entry-level CPU core\u00a0 \u2009 no larger than Arm Cortex-M0+ \u00a0tightly-coupled memories (TCMs)\u00a0 \u2009code\u2009+\u2009data; \u2264\u200932K per bank; zero wait-state SRAM \u00a0bulk flash and NO cache\u00a0 \u2009TCMs loaded under firmware control \u00a0rudimentary peripherals\u00a0 \u2009 no specialized auxiliary CPU cores \u00a0always-on domain\u00a0 \u2009 wakeup events from deep-sleep power modes \u00a0\u2265\u2009100MHz system clock\u00a0 \u2009no TCM wait-states \u2192 maximize CPU throughputFeatures through would collectively minimize the number of logic gates and memory cells required to implement this MCU in silicon \u2013 resulting in a smaller chip die compared with a more fully-featured design, which (holding other factors constant) forecasts lower manufacturing costs as well as lower leakage power.
To support \"sleepy applications\" with short active-duty cycles, feature enables our MCU to execute for years on small batteries \u2013 or even indefinitely using harvested energy. Because of its relatively small die-size, our MCU could likely consume \u2264\u20091\u03bcW of power when sleeping; and by retaining all program state in the (small) TCMs of , our MCU can quickly return to its fully-powered active mode when awoken by an event.
Feature , however, seems at odds with achieving an energy-efficient design \u2013 faster clocks proportionally increase switching (dynamic) power consumption; but since the CPU can access its TCMs without stalling, increased clock speed also results in proportionally faster software execution \u2013 enabling our duty-cycled application to spend more time in deep-sleep. In practice, this approach actually improves overall energy(1)utilization by reducing the (static) overhead of leakage power during active execution periods.
Given that feature enables faster execution, the peripherals of can now leverage software running on the main CPU core to perform functions that would otherwise require additional hardware \u2013 such as advanced encryption modes or the upper layers of a comm stack. Unlike conventional cryto or radio blocks (which often contain their own dedicated cores) our peripherals embody a \"RISC-like\" approach which efficiently implements only rudimentary primitives in hardware and then relegates higher-levels functions to software.
EM programs that target our idealized MCU will often exhibit a simple, cyclic structure:
wake-up from deep-sleep
acquire data from the environment
analyze this data using an algorithm
transmit results (wirelessly) to the edge
re-enter deep-sleep
Because of their single-threaded design, these programs would only require a single CPU core to maximize application throughput; software functions normally relegated (say) to an auxiliary cyrto or radio core can now execute exclusively on the main CPU. Consolidating all software onto a single core not only maximizes re-use of MCU logic elements, but can further reduce cost and power by minimizing the silicon footprint of certain peripherals.(1)
So thanks to EM \u2013 10\u2009X fewer bytes, 10\u2009X fewer transistors, 10\u2009X lower power, 10\u2009X lower cost\u2009!!!
The promise of RISC-VThe emergence of RISC-V as an open instruction-set architecture has triggered an abundance of innovation in MCU design; you can literally find dozens of open-source projects containing synthesizable cores expressed in languages like Verilog and VHDL. While much of the RISC-V community has set its sights on high-performance computing from the edge up to the cloud, some practitioners have focused on \"tiny cores\" suitable for low-power, low-cost applications on the fringe; visit the X-HEEP and NEORV32 projects as examples.
With more degrees of freedom when implementing the entry-level RV32ICM instruction set \u2013 often compared against the ARM Cortex-M0+ \u2013 MCU designers can truly innovate when realizing our earlier feature \u2013 all in the interest of further shrinking silicon and software:
by using an internal 8-bit or 16-bit ALU, optionally increasing to maintain throughput;
by locating \u2013 in a compact (17-bit) address-space, further improving RV32ICM code-density; or
by adding specialized instructions to accelerate higher-level software functions supporting
Here again, the tiny-code of EM serves as a catalyst which can drive novel RISC-V tiny-chips.
"},{"location":"intro/#technical-overview","title":"Technical overview","text":"Building upon the tiny\u00a0code\u2009\u2192\u2009tiny\u00a0chips premise behind EM, let's dive into some technical details.\u00a0 The following chapters each focus on a particular aspect of the language and its runtime enviorment, and collectively provide a technical overview of the EM platform:
1)\u2003 EM modules & interfaces \u2022\u2022\u2022 The client/server dichotomy\u2003\u2003 2)\u2003 EM composites & templates \u2022\u2022\u2022 Assembling application elements\u2003\u2003 3)\u2003 EM program life-cycle \u2022\u2022\u2022 From build-time to run-time\u2003\u2003 4)\u2003 EM runtime bundles \u2022\u2022\u2022 Software platform content\u2003\u2003We encourage you to read these chapters in sequence, as each subsequent chapter builds on material covered by its predecessors. At the same time, we recognize that this document introduces a lot of (new) information about a (new) programming environment; feel free to proceed iteratively, skimming the content on your first pass before circling back for a more thorough reading.
The technical overivew also includes exemplary source-code fragments, written in the EM programming language and formatted as follows:
Hello.em# we've omitted a few details here\n# but you should get the basic idea\n\nmodule Hello\nend\n\ndef em$run()\n printf \"Hello world\\n\"\nend\n
Even if you don't plan to (initially) install and use EM, these fragments should give you an intuitive sense of the language \u2013 which relies upon familiar programming constructs (such as seen at line 8 above) that you've likely encountered elsewhere.
So with that, let's move onward to Chapter 1 and begin our technical overview of EM.
"},{"location":"intro/#the-history-of-em","title":"The history of EM","text":"EM's origin story
The EM programming language first appeared at UC Santa Barbara in 2010, where undergraduate students taking CS190C\u2009/\u2009ECE1940 would develop \u201creal-world\u201d embedded applications targeting resource-constrained MCUs \u2013 with all software written in EM, of course, using this (now outdated) language primer as a guide.
The EM language had emerged through deep discussion and intense interaction with Amichai Amar \u2013 a UCSB PhD candidate at that time. Consult his thesis for more details on the outcome of our undergraduate course, as well as a more comprehensive exposition of programming resource-constrained MCUs using EM.
But the true EM backstory actually began decades before its debut at UCSB. In 1998, Texas Instruments acquired Spectron Microsystems \u2013 whose SPOX and DSP\u2009/\u2009BIOS products had already emerged as de facto industry standards. This milestone validated the importance of software technology in further solidifying TI's leadership as a DSP silicon vendor, and in fact triggered a flurry of similar acquisitions during the dot-com boom.
Once inside TI, the Spectron team broadened the application of its patented configuration technology \u2013 which had already enabled DSP\u2009/\u2009BIOS to fit comfortably within the 2\u2009K boot ROM of broadly-deployed, low-power DSPs. This effort culminated in the open-source RTSC project, hosted at the Eclipse Foundation beginning in 2007 and still used today within TI software products. But RTSC fell a bit short in fulfilling its vision \u2013 and provided some much needed impetus for the birth of EM a few years later.
EM's coming-of-age began in 2011 with the founding of Emmoco \u2013 an early player offering an embedded\u2009\u2194\u2009mobile connectivity stack targeting new TI wireless MCUs which supported the emerging BLE standard. Acquired by Shelfbucks in late 2015, the Emmoco stack ultimately evolved to support long-range, low-power subGHz radios in which just one cloud-connected HUB could interact with (say) 10,000 TAGs in a 500,000 sq ft venue.
After Shelfbucks ceased operations in 2019 \u2013 and thanks to some legacy licensing agreements \u2013 EM found its way onto other low-power wireless MCUs from vendors such as NXP, Analog Devices, and ON Semiconductor; EM's foray into the world of RISC-V (as detailed in an earlier note) also began in this time frame. Targeting very high-volume applications for over a decade now, EM's uncanny ability to reduce firmware footprint proved critical in keeping system size, power, and cost in check.
As of today, EM has supported more than twenty 8\u2009/\u200916\u2009/\u200932-bit MCUs from almost a dozen silicon vendors. The EM language translator \u2013 which ultimately outputs ANSI C/C++ code for portability \u2013 has also targeted the most popular toolchains for embedded development [GCC, IAR, Keil, LLVM].\u00a0 Thanks to a recent rewrite of the translator into TypeScript, EM now enjoys robust language support within the VS Code IDE.
More important, perhaps, just a handful of EM programmers have developed thousands of EM modules used (and often re-used\u2009) across a broad spectrum of IoT applications targeting these MCUs. But due to the proprietary nature of these applications, the EM language and its runtime has remained closed \u2013 until now\u2009!!!
"},{"location":"intro/to-1/","title":"The client/supplier dichotomy","text":"EM revolves around the concept of a concrete module
, a programmatic construct that plays a seminal role within the language comparable to the position held by a class within C++ or Java. Not unlike classes, each EM module defines a programmatic boundary between its clients \u2013 users of the module \u2013 and the supplier of the module itself.
To minimize direct coupling between clients and suppliers \u2013 and therefore to increase software re-use \u2013 EM also supports module abstraction through an interface
construct which clients in turn can leverage through a module proxy
.
Reflecting a basic dichotomy between module clients and module suppliers, consider the overall organization of a sample EM module named Mod1
, whose source code will reside in a file named Mod1.em
:
package bob.pkg\n\nmodule Mod1 # client-visible feature declarations\n const C: ...\n type T: ...\n function f( ... )\n # etc\n\nprivate: # supplier-proprietary feature declarations\n var x: ...\n function g( ... )\n # etc\nend\n\ndef f(...) # supplier-proprietary function definitions\n # body of 'f'\nend\n\ndef g(...)\n # body of 'g'\nend\n\n# etc...\n
Starting from the top, each EM module lives within the logical scope of a package
which physically corresponds to a file-system directory of the same name. As in Java or Python, an EM package will generally bear a globally-unique qualified name that identifies its supplier \u2013 bob.pkg
, bob.pkg.test
, dave.pkg
, and so forth. By extension, all EM modules have a (globally-unique) fully-qualified canonical name \u2013 bob.pkg/Mod1
in the current example \u2013 though in practice you'll invariably refer to modules using simple names like Mod1
.
But unlike Java or Python, where the contents of a package named bob.pkg
would actually reside in a nested directory structure with the path bob/pkg
, EM employs a flat organization in which the logical package-name and physical directory-name must match exactly.
Moving on, the declarations beginning at line 3 comprise the public specification of this module \u2013 a coherent collection of constants, types, and functions (what others might term an \"API\"\u2009) available for direct use by clients of Mod1
. Taken together, these externally-visible features of the module constitute a programmatic contract in which future changes on the part of the supplier should (hopefully!!!\u2009) not violate prior client assumptions.
By contrast, features of Mod1
declared beginning at line 9 along with all definitions of its public and private functions beginning at line 15 remain hidden from any clients of this module. The supplier of Mod1
can consequently change the internal implementation of this module \u2013 optimizing performance or improving robustness \u2013 while maintaining a measure of \"plug-compatibility\" from its clients' perspective.
By enforcing crisp boundaries between clients and suppliers, EM modules encourage a software \"best-practice\" known as separation of concerns \u2013 organizing application functionality into discrete programmatic elements that encapsulate specific implementation decisions. Besides helping us digest software-rich systems in \"bite-sized\" chunks (where each EM module becomes a small world unto itself), our ability to manage change throughout the software life-cycle emerges as the most enduring benefit of modularity in general.
"},{"location":"intro/to-1/#importing-modules","title":"Importing modules","text":"To gain access to public features of bob.pkg/Mod1
, clients must explicitly import
this module within their own\u2009.em
files prior to any direct usage. As an illustration, consider a sample module named dave.pkg/Mod2
:
package dave.pkg\n\nfrom bob.pkg import Mod1\n\nmodule Mod2 # client-visible feature declarations\n function f( ... )\n\nprivate: # supplier-proprietary feature declarations\n var t: Mod1.T\nend\n\ndef f(...) # supplier-proprietary function definitions\n Mod1.f( ... )\nend\n
The directive at line 3 effectively adds the identifier Mod1
to this file's top-level namespace, which already includes all public\u2009/\u2009private feature names of Mod2
; an optional trailing as
clause can resolve name conflicts, should they arise. Through their import
directives, EM modules organize themselves into a static hierarchy of clients and suppliers. As a rule, this client-supplier relation must remain acyclic; an EM module can neither directly nor indirectly import itself.
After importing Mod1
, the example accesses this module's public type T
at line 9 followed by its public function f
at line 13 using a qualified name of the form Mod1.feature
. This syntax enables client modules using Mod1
to (coincidentally) declare their own features with identical names, such as the function f
defined here within the scope of Mod2
; unqualified identifiers always refer to features defined within the current module.
By implementing a special (intrinsic) function named em$run
, any EM module can potentially serve as the entry-point for an executable application \u2013 a common pattern in many modern languages. Said another way, EM has no inherent notion of a \"main-program\"; instead, application developers will designate a particular module as the top of a client-supplier hierarchy defined via import
directives.
At the end of the day, EM application programs comprise a set of concrete modules \u2013 each contributing some measure of encapsulated code and data to the final executable image. In the example above, where we directly imported bob.pkg/Mod1
, this module will auto\u00admatically and unconditionally become an element of any application program that directly or indirectly uses dave.pkg/Mod2
.
As an alternative to this mode of direct coupling between modules, EM introduces a language construct known as a proxy
that adds a level of indirection between clients and suppliers \u2013 abstracting a particular supplier's identity from the client's perspective. Akin to polymorphism within object-oriented programming, EM proxies can enhance application flexibility and improve software re-use by further decoupling clients from suppliers; the same client module, as you'll soon see, can effectively (re-)use different supplier imple\u00admentations of otherwise common functionality.
And even more important than re-use, we can better manage change\u2009!!!
By not having to modify client modules that employ proxies, our software becomes more resilient and malleable when \u2013 and not if \u2013 application requirements evolve over time.
To capture commonality amongst a family of \"plug-compatible\" modules, EM enables us to separately publish a set of client-visible features as an interface
\u2013 a public specification independent of any particular supplier implementation. As an illustration, let's refactor the bob.pkg/Mod1
module presented earlier.
bob.pkg/ModI.em
package bob.pkg\n\ninterface ModI # public specification only \n const C: ...\n type T: ...\n function f( ... )\nend\n
bob.pkg/Mod1.empackage bob.pkg\n\nfrom bob.pkg import ModI\n\nmodule Mod1: ModI # public specification \n # additional features\n\nprivate: # internal implementation\n # same as before\nend\n\ndef f()\n # body of 'f'\nend\n\n# etc...\n
The declarations beginning at line 3 mimic the earlier public specification of bob.pkg/Mod1
; but unlike a concrete module, an abstract EM interface cannot have an internal implementation. Our new rendition of Mod1
now inherits its public specification from the interface ModI
at line 3, while declaring any additional (public) features unique to this module; the private portion of Mod1
beginning at line 8 remains unchanged from before.
In practice, abstract interfaces and implementing modules will often reside in different packages: for instance, the EM runtime contains a package named em.hal holding ConsoleUartI
, GpioI
, WakeupTimerI
, and other interfaces; packages such as ti.mcu.cc23xx then hold concrete implementations of these abstract interfaces targeting a particular MCU architecture.
Returning to the matter at hand \u2013 abstracting suppliers \u2013 we'll now refactor our original dave.pkg/Mod2
client module to remove its direct dependence on bob.pkg/Mod1
and instead leverage a local proxy
implementing the ModI
interface.
package dave.pkg\n\nfrom bob.pkg import ModI\n\nmodule Mod2 # client-visible feature declarations\n proxy ModX: ModI\n function f( ... )\n\nprivate: # supplier-proprietary feature declarations\n var t: ModX.T\nend\n\ndef f(...) # supplier-proprietary function definitions\n ModX.f( ... )\nend\n
The syntax at line 6 adds the name ModX
to the top-level scope of Mod2
, as well as declares that the proxy ModX
provides all client features specified within the interface ModI
; access to the public type T
and the public function f
at lines 10 and 14 respectively mirror earlier direct usage of bob.pkg/Mod1
.
Once again, we should emphasize that client Mod2
has no overt coupling to the concrete module Mod1
; instead, Mod2
only knows about the abstract interface ModI
\u2013 which would admit an unbounded number of alternate implementations beyond that provided by Mod1
.
The pattern exemplified here occurs extensively within the EM runtime, and largely holds the key to maintaining platform portability. As a case in point, the package em.utils contains only portable modules such as AlarmMgr
which declares a local proxy implementing the em.hal/WakeupTimerI interface. Client application modules likewise desiring hardware independence can simply follow suit, using the proxy
\u2009\u2013\u2009interface
pattern at each point of potential variability.
Moving on to Chapter 2, we'll now explore the process of binding the Mod2
.
ModX
proxy to the Mod1
module.
By leveraging the proxy
\u2009\u2013\u2009interface
design pattern, EM modules exhibit component-like qualities \u2013 enabling third-party integrators to effectively compose a set of modules that otherwise have no direct knowledge of one another.
To facilitate general aggregation and assembly of discrete modules into larger application entities, the EM language introduces a multi-faceted construct known as a composite
to service these needs.
Like modules and interfaces, each EM composite resides under a named package and will import other units \u2013 modules, interfaces, even composites \u2013 into its top-level namespace. Consistent with their role as higher-level collection points for discrete modules, EM composites will often import surprisingly large numbers of modules in practice.(1)
In their most elementary form, EM composites will selectively export
a group of concrete modules under logical names known to higher-level clients.
package bob.pkg\n\nimport Mod1 as ModX # omit redundant 'from' clause\n\nexport ModX # a logical module\n\ncomposite CompC \nend\n
After re-labeling bob.pkg/Mod1
at line 3 using an as
clause, the export
directive at line 5 publicizes the name ModX
; clients importing CompC
can then use the (logical) module named ModX
, otherwise oblivious to its true identity as bob.pkg/Mod1
. While not declared explicitly, ModX
in fact belongs to the ModI
family of modules \u2013 serving as a concrete delegate suitable for binding to an abstract proxy implementing a common programmatic interface.
Programming with components \u2013 which takes modularity, separation of concerns, and resilience in the face of change to entirely new levels \u2013 dictates that (abstract) interfaces define all functional interactions between independently-replaceable elements. As such, each (concrete) software component \"provides\" and \"requires\" services defined by some set of interfaces \u2013 not unlike the standardized \"plugs\" and \"sockets\" found in hardware components. Through disciplined use of export
directives within EM composites and proxy
declarations within EM modules, we can emulate the provides\u2009\u2013\u2009requires paradigm that forms the backbone of all component-oriented systems.
Besides aggregating modules, EM composites will often take on the task of \"wiring\" together a set of modules into a more integrated assembly \u2013 especially modules like our last version of dave.pkg/Mod2
, which utilize local proxies to further decouple themselves from potential suppliers. To illustrate:
package geof.pkg\n\nfrom bob.pkg import Mod1 # concrete delegate\nfrom dave.pkg import Mod2 # exposes abstract proxy\n\ncomposite CompC\n # no features\nend\n\ndef em$configure()\n Mod2.ModX ?= Mod1 # bind proxy to its delegate\nend\n
Reflecting its higher-level position within the application hierarchy, this particular composite reaches across multiple packages starting at line 3 when importing a set of modules. The actual binding of bob.pkg/Mod1
to dave.pkg/Mod2
via the latter's ModX
proxy then occurs at line 11, using a special single-assignment operator [\u2009?=
\u2009] which we'll explain later. We'll also have more to say about the role played by the intrinsic function em$configure
.
To use the EM language, you'll need an EM distro \u2013 a multi-tiered sub-system that melds portable runtime content in packages like em.utils
with hardware-specific content in packages like ti.mcu.cc23xx
. By design, each EM distro will publish a top-level composite with a fully-qualified name like ti.distro.cc23xx/BoardC. This BoardC
composite in turn builds upon other composites \u2013 whether hardware-specific ti.distro.cc23xx/McuC or else portable em.mcu/CommonC.
Just as individual functions may have parameters, EM modules as a whole can publicize a special kind of parameter known as a config
which clients in turn can selectively assign inside of em$configure
. To illustrate typical usage, let's expand our earlier versions of bob.pkg/Mod1
and dave.pkg2/Mod2
to now expose some new client-visible features in the form of configuration parameters.
package bob.pkg\n\nmodule Mod1\n\n config flag: bool\n # ^| Enable some functionality\n # other public features\n\nprivate:\n\n# etc...\n
dave.pkg/Mod2.empackage dave.pkg\n\nmodule Mod2\n\n config count: uint8\n # ^| Establish some threshold\n # other public features\n\nprivate:\n\n# etc...\n
As you might glean from the special documentation comments, the flag
parameter declared at Mod1
6 and the count
parameter declared at Mod2
6 should each render these modules more flexible than before, and hence increase opportunities for clients to (re-)use Mod1
and Mod2
in a wider range of applications. Syntactically similar to const
and var
declarations within the language, config
parameters have a very special semantic property:
An EM config
behaves like an assignable var
at build-time, but like a read-only const
at run-time.
Once we delve into the EM program life-cycle \u2013 from build-time through run-time \u2013 you'll understand the rationale behind this paradox, as well as more fully appreciate the implications of module configuration for resource-constrained embedded applications. For now, suffice it to say that EM composites serve as an ideal site for assigning module config
parameters when assembling application programs. Expanding our earlier example:
package geof.pkg\n\nfrom bob.pkg import Mod1\nfrom dave.pkg import Mod2\n\ncomposite CompC\nend\n\ndef em$configure()\n Mod2.ModX ?= Mod1 # bind proxy to its delegate\n\n Mod1.flag ?= true # assign config parameters\n Mod2.count ?= 100\nend\n
Just as we bound proxies to delegates, the single-assignment statements at lines 12 and 13 bind parameter values consistent with the types used in corresponding config
declarations found at Mod1
6 and Mod2
6. In this light, clients can treat proxies as a special kind of con\u00adfiguration parameter \u2013 assigned module
values implementing a declared interface
type.
The em$preconfigure
and em$configure
functions defined within McuC provide a realistic example of configuration \u2013 starting with a call to BoardInfo.readRecordH
, which returns a data-structure of board-specific parameter values read from a YAML source file. The (portable) implementation of readRecordH
found within the em.utils/BoardInfo module hints at the range of programmability we can bring to bear during configuration.
The EM language provides a general-purpose template
mechanism for synthesizing other source-level artifacts \u2013 from fragments of C code to complete\u2009.em
files \u2013 during the program build process. The latter scenario, which we'll focus on here, enables suppliers to deliver concrete modules in a more generic embodiment \u2013 one that clients will frequently instantiate within the context of an EM composite.
Starting from the client's perspective, the following composite effectively manufactures a pair of new modules \u2013 locally aliased as Clone1
and Clone2
\u2013 by instantiating a common imported template named thom.pkg/GenT
.
package geof.pkg\n\nfrom thom.pkg import GenT {flag: true, count: 100} as Clone1\nfrom thom.pkg import GenT {flag: false, count: 200} as Clone2\n\nexport Clone1\nexport Clone2\n\ncomposite CompC\nend\n\ndef em$configure()\n # assign Clone<n> config parameters\n # bind Clone<n> proxies to delegates\n # delegate other proxies to Clone<n>\nend\n
Using an expanded form of import
directive at lines 3 and 4, this composite works with (synthesized) modules Clone1
and Clone2
no differently than how we employed the bob.pkg/Mod1
or dave.pkg/Mod2
modules defined earlier. Since Clone1
and Clone2
have no prior outside identity, composites will often export
these newly-formed modules for use by higher-level clients; composites may further configure and assemble these synthesized modules within the body of their own em$configure
function.
The flag
and count
values bound at lines at lines 3 and 4 above \u2013 which shape the essential characteristics of the synthesized Clone1
and Clone2
modules \u2013 ultimately correspond to public configuration parameters declared within thom.pkg/GenT
:
package thom.pkg\n\ntemplate GenT\n\n config flag: bool\n # ^| Enable some functionality\n config count: uint8\n # ^| Establish some threshold\nend\n\ndef em$generateUnit(pn, un)\n |-> package `pn`\n |->\n |-> module `un`\n |->\n if flag\n |-> const MAX: Uint8 = `count`\n end\n # generate remainder of this module\nend\n
Like any other config
, the parameters declared after line 5 become assignable variables at program build-time \u2013 in this case, via import
directives beginning at line 3 of CompC
. The GenT
template will then consume these configuration parameters within the body of its em$generateUnit
function, which synthesizes lines of source code using special EM output statements prefixed by the |->
symbol.
The flag
config declared at 6 impacts the generated output by controlling execution flow within em$generateUnit
; by contrast, this function directly interpolates the count
config declared at 9 within the generated output. When invoked, em$generateUnit
receives pn
and un
arguments bound to strings like \"geof.pkg\"
and \"CompC__Clone1\"
\u2013 reflecting the original context in which the geof.pkg/CompC
composite imported and instantiated the GenT
template back on the earlier line 3.
Our McuC composite instantiates a module-per-pin using this GpioT template. Inspecting this template's em$generateUnit
function, note how the pin
config ultimately shapes the synthesized module through interpolation within the |->
output statements. Though not a rule, synthesized modules will often implement an interface that captures their commonality \u2013 em.hal/GpioI in the example at hand.
To further grasp the special role played by composite
and template
units, let's proceed onward to Chapter 3 and explore EM's rather unique build flow.
We turn now to the life-cycle of an EM program \u2013 covering its build-time transformation from a single \"main\" source module into a binary program image comprising multiple modules, as well as its run-time execution phases from hardware reset to system shutdown.
Along the way, you'll understand the role played by EM language intrinsics throughout the program life-cycle \u2013 not only to demarcate execution phases at program run-time (em$run
), but also to enable active participation by content suppliers at various stages during program build-time (em$configure
and em$generateUnit
).
The following figure depicts the four principal phases of the EM program life-cycle, as well as maps out the high-level flow of build-time artifacts \u2013 starting with a ModP.em
source file and ending with a main.out
binary image. The first three of these phases unfold on your host computer, and collectively constitute program build-time; the final phase, needless to say, represents run-time execution of the generated program on target hardware.
Each\u2009.em
source file \u2013 whether a module
, interface
, composite
, or template
\u2013 represents an independent unit of translation within EM. Starting from a designated top-level unit \u2013 in our case, a module named ModP
which would implement the em$run
intrinsic \u2013 EM will (recursively) process an N-element hierarchy of other translation units that ModP
directly or indirectly imports; since the relation defined by import
directives cannot have cycles, translating ModP
effectively yields a top-to-bottom (partial) ordering of its dependent units.
Translating concrete modules such as ModP
will generally produce three corresponding output files, consumed in subsequent phases of the program build process:
ModP.hpp
the public\u2009/\u2009private features of ModP
translated into a C++ header file ModP.cpp
internal function definitions within ModP
translated into equivalent C++ code ModP.js
a JavaScript rendition of ModP
which will contribute during program configuration Translating abstract interfaces such as our earlier ModI
example will only yield a ModI.js
and ModI.hpp
output file. Translating composites or templates such as our earlier CompC
or GenT
examples \u2013 which contribute at build-time but not run-time \u2013 will only yield a CompC.js
or GenT.js
output file.
Finally, all template instantiations encountered en route through import
directives (such as the references to GenT
in CompC
) will trigger immediate execution of the designated template's em$generateUnit
intrinsic \u2013 already translated to JavaScript within the GenT.js
output file. Unit translation of the new\u2009.em
file produced at this step then proceeds recursively.
A top-level module such as ModP
could easily have static dependencies on more than 100 other translation units \u2013 especially when imported composites aggressively instantiate templates managing discrete MCU resources like GPIO pins. To accelerate program build-time, the EM translator maintains an internal cache of all generated files and will only (re-)translate a particular\u2009.em
file when deemed necessary.
The configuration phase of the EM program life-cycle \u2013 still upstream from the final compilation of all generated C++ code into a binary image \u2013 actually entails executing a special hosted version of the program rendered in JavaScript. Labeled main.js
in the earlier figure, this fabricated program basically amalgamates the\u2009.js
files output for each module or composite found within the N-element import
hierarchy rooted at ModP
itself.
Seemingly, any hosted language (Java, Python, Ruby) could provide a suitable execution environment for this phase of the EM program life-cycle. Some might argue the case for Python, as this language already plays a similar role with respect to C/C++ code \u2013 especially in emerging platforms such as TinyML, which deploy machine-learning algorithms (developed in a hosted Python environment) onto embedded target hardware.
As it turns out, JavaScript had already claimed the host language role among EM's predecessors \u2013 notably the Eclipse\u2009/\u2009RTSC project which in turn drew upon earlier DSP/BIOS configuration technology. Given the Java-centricity of the Eclipse IDE, Mozilla's Rhino \u2013 a JavaScript engine written in Java and seemlessly integrated with the JVM runtime \u2013 served as an ideal environment at that point in time.
Indeed, an Eclipse plug-in (written in Java) provided almost a decade of IDE support for the EM language; and Rhino therefore remained our JavaScript platform of choice. But now that language support for EM has migrated to the VS Code IDE \u2013 written in TypeScript and running on the Chromium\u2009/\u2009V8 engine \u2013 Node.js provides an even richer JavaScript platform for hosting the configuration phase of the EM program life-cycle.
During its execution, the prog.js
program makes three top-to-bottom passes over the N-element import
hierarchy rooted in ModP
\u2013 invoking JavaScript translations of certain EM intrinsics on a per-unit basis (if defined).
The 1st pass invokes em$preconfigure
, which only composites may elect to define; public proxies and config parameters bound at this time using the single-assignment operator [\u2009?=
\u2009] become immune to further modification in the next pass.
The 2nd pass invokes em$configure
, which modules as well as composites may elect to define; proxies and configs bound here using the [\u2009?=
\u2009] operator become immune to further modification by lower-level units yet to execute in this pass.
The 3rd pass invokes two intrinsics on modules whose special em$used
config parameter tests true: em$construct
, for initializing private module state; and em$generateCode
, for synthesizing internal C/C++ code using the EM template mechanism illustrated in GenT
.
The [\u2009?=
\u2009] operator, as hinted earlier, implements single-assignment semantics \u2013 sealing the first value assigned to a configurable proxy or parameter, while silently ignoring all sub\u00adsequent assignments to the same feature. With a top-to-bottom ordering imposed on the ModP
import
hierarchy, [\u2009?=
\u2009] operations executed by higher-level modules and composites essentially \"override\" (default) binding decisions made by lower-level units. By implementing em$configure
, ModP
itself can now preempt proxy\u2009/\u2009parameter assignments otherwise made by any modules or composites it may import.
Higher-level modules such as ModP
cannot, however, effect the values of configurable features already bound in the first configuration pass via em$preconfigure
; the latter intrinsic enables suppliers of EM composites to selectively freeze proxy bindings and parameter values, tempering flexibility in the interest of robustly assembling elements for a fixed application setting. As an example, our McuC composite binds physical pin numbers read from a board-specific YAML file \u2013 reflecting the \"hard reality\" of the underlying hardware.
Referring to the earlier figure, one practical consequence of configuration becomes pruning the original (and often large) N\u2013element import
hierarchy into a more tractable M\u2013element subset comprising those modules actually used within the program. In support, each module has an intrinsic em$used
parameter \u2013 automatically bound in most cases, but explicitly con\u00adfigurable if necessary \u2013 that ultimately determines membership in the M\u2013element subset.
The top-level module ModP
has its em$used
parameter automatically set, and is always used within the program.
If module Mod1
is used and Mod1
imports module Mod2
(directly or via a composite), then Mod2
is used as well.
If module Mod1
is used and proxy Mod1.ModX
ultimately delegates to module Mod2
, then Mod2
is used as well.
Otherwise Mod1
is not used in the program, unless some higher-level module or composite explicitly sets Mod1.em$used
.
The final configuration pass gives each used module within the M\u2013element subset an opportunity to focus internally; configuration of all public features of these modules would have already occurred. By defining the em$construct
intrinsic, modules may programmatically initialize their private var
, config
, or even proxy
features at this point within the flow.
But since em$construct
actually executes on your host computer, module suppliers can now implement complex initialization algorithms at build-time that would otherwise prove far too costly to execute at run-time on resource-constrained MCUs.
With language constructs normally used to implement target-side functions like em$run
also available in hosted functions like em$construct
, module suppliers can now migrate (expensive) computations from run-time to build-time with little effort. Said another way, EM can serve as its own meta-language \u2013 synthesizing the final form of a concrete module by statically reflecting upon values assigned to its configurable parameters.
The em$construct
function of ti.mcu.cc23xx/ConsoleUart0 computes values for private configs ibrd
and fbrd
, eventually used to initialize hardware registers defining the UART's baud-rate; a less efficient implementation would perform this computation at run-time. Taking this approach to the next level, the em$construct
function of em.utils/FftC32 initializes a custom sine-wave table at build-time.
Besides executing (costly) math functions, EM meta-programming can also initialize complex, linked data-structures at build-time \u2013 such as the em$construct
function and createH
functions of em.utils/FiberMgr, which in turn call build-time functions of em.utils/ListMgr. As a general rule, any static initialization of data at build-time results in more compact programs at run-time.
Complementing em$construct
\u2013 oriented towards initializing private state \u2013 some modules will also implement the em$generateCode
intrinsic. Using the same form of templatized output statements illustrated earlier in GenT
, module suppliers can inject customized C/C++ code fragments into the final program image \u2013 with public config par\u00adameters typically shaping the synthesized output.
On the low end of the scale, MCU-specific modules like ti.mcu.cc23xx/Regs use the em$generateCode
intrinsic to #include
vendor-supplied header files; modules such as Rtc will then reference symbols and macros defined in these headers using a special ^^
escape token.
Moving up a notch, the ti.mcu.cc23xx/IntrVec module programmatically synthesizes the run-time vector table for this MCU using build-time bindings of interrupt handlers \u2013 complete with compiler-specific directives to control placement in memory. In the limit, em$generateCode
can leverage the full capabilities of JavaScript executing on your host computer.
Referring back to the earlier figure, the ultimate outcome of executing the main.js
(meta-) program within the overall EM build-flow becomes yet another program \u2013 this time, a single C++ program labeled main.cpp
. As suggested earlier, this program only incorporates generated code from the M used modules selected from the original set of N imported units traced back to ModP.em
.
Each module Mod
participating in this consolidated C++ program respectively contributes (in order) the following portions of code, which collectively represents the bulk of the generated main.cpp
file's content:
constant, type, variable, and function declarations from Mod.hpp
, generated during the initial translation of Mod.em
;
static data initializers reflecting the values assigned to public\u2009/\u2009private features of Mod
during the prior configuration phase;
any C/C++ code synthesized by the Mod.em$generateCode
intrinsic, executed during the prior configuration phase; and
definitions of declared and intrinsic functions from Mod.cpp
, generated during the initial translation of Mod.em
.
By merging all generated C/C++ code into a single input file, the underlying compiler for the target MCU can aggressively optimize the program as a whole \u2013 folding away constants, inlining small functions, and eliminating unused code or data. As a case in point, client function calls via abstract proxies to configured delegate modules \u2013 seemingly a double-indirection at run-time \u2013 will usually \"melt-away\" and leave the delegate function body inlined at the client call-site.
Example of whole-program optimizationReturning to FftC32, its exec
function uses three config
parameters at run-time which em$construct
previously initialized by at build-time \u2013 N_WAVE
, N_WAVE_LOG2
, SINE_WAVE
. Knowing the values of these parameters when digesting main.cpp
, the compiler has greater latitude in making time\u2009/\u2009space tradeoffs when generating object code for FftC32.exec
.
As another example, em.utils/FiberMgr makes many function calls via Common.GlobalInterrupts
\u2013 a proxy which conforms to the GlobalInterruptsI interface, and ultimately delegates to a hardware-specific implementation such as ti.cc23xx.mcu/GlobalInterrupts. Knowing this particular proxy\u2009-\u2009delegate binding, the compiler would inline the delegate's (small) functions directly at each Common.GlobalInterrupts
call site.
This final phase of the EM program life-cycle \u2013 which represents the transition from build-time to run-time \u2013 technically commences when you load the executable main.out
image into target memory and reset the MCU. But as you'll see, run-time contributions from the M concrete modules used within this program won't occur until execution reaches main
.
main
The path actually taken from loading the main.out
file to executing the C/C++ main
function can vary widely from one target environment [MCU\u2009+\u2009compiler\u2009+\u2009board] to the next; but fortunately, each distribution of the EM software platform will render this process transparent to the application developer. In practice, each EM distro will leverage much of the tooling infrastructure supporting the underlying MCU \u2013 from flash loaders that operate on standard\u2009.bin
or\u2009.hex
files, to compiler startup files like crt0.s
that manage the transition from MCU reset to C/C++ main
as efficiently as possible.
For the M concrete modules bound within the main.out
image, program run-time actually begins when target execution reaches the C/C++ main
function. Since the un\u00adderlying compiler's own startup file does little more than prepare data memory and initialize critical CPU registers, more comprehensive startup of the target board and the MCU peri\u00adpherals still needs to occur prior to calling the top-level ModP.em$run
intrinsic.
The main
function initially calls a C++ rendition of Modr.em$reset
, where Modr
represents the first module to implement this intrinsic found by a top-to-bottom scan of the M modules used in this program; needless to say, this scan occurs at program build-time, not run-time. In practice, some target-specific module included with your EM distribution will assume responsibility for defining the em$reset
intrinsic; higher-level application modules generally avoid (re-)defining this intrinsic.
The main
function will next call C++ renditions of Modi.em$startup
for each Modi
found to implement this intrinsic; here too, a top-to-bottom scan of all M program modules occurs at build-time. Unlike em$reset
, higher-level application modules down to target-specific driver modules will define this intrinsic in order to perform (run-time) initializations not possible during (build-time) execution of em$construct
.
The main
function then calls a C++ rendition of Mods.em$startupDone
, where Mods
represents the first module found to implement this intrinsic through a top-to-bottom scan of all M modules participating in the program. As with em$reset
, your EM distribution will usually take responsibility for defining em$startupDone
\u2013 which performs any final hardware setup before the application program assumes control.
The main
function finally calls a C++ rendition of ModP.em$run
, which effectively transfers control to the top-level module of this application. Since embedded applications often execute some form of \"run forever\" loop \u2013 whether explicitly coded within the program or else implicitly managed by some run-time task scheduler \u2013 in practice the em$run
intrinsic will not return control back to the calling main
function.
em$startup
Many modules that manage MCU hardware peripherals will define em$startup
\u2013 such as Idle and Rtc found in the ti.mcu.cc23xx
package; clearly, this sort of hardware setup must occur at run-time. By extension, portable modules like em.utils/SoftUart which leverage proxies to interact with underlying hardware may likewise rely upon em$startup
to perform some run-time initialization \u2013 in this case, initializing a GpioI proxy named TxPin
.
Should the top-level ModP.em$run
intrinsic actually return to main
, control then transfers to a distinguished __halt
function \u2013 also generated prior to program compilation \u2013 which supervises an orderly shutdown of the target application. If necessary, any program module can explicitly initiate the shutdown sequence at run-time through a special halt
statement that can appear inside EM function definitions.
The __halt
function will first call C++ renditions of Modi.em$shutdown
for each Modi
found to implement this intrinsic; higher-level applications down to target-specific drivers modules will define this intrinsic in order to perform run-time finalization prior to halting the processor.
The __halt
function then calls a C++ rendition of Modh.em$halt
, where Modh
represents the first module to implement this intrinsic found by a top-to-bottom scan; each EM distro offers a \"default\" version of em$halt
, though higher-level modules may (re-)define this intrinsic in some cases.
Should the implementation of em$halt
happen to return to __halt
, program control would fall-through to a special block of code that simply spins within an infinite loop.
In cases where something goes \"seriously wrong\" within the system and execution should terminate more abruptly, EM also supports a special fail
statement that can appear in any function definition. When executed at run-time, fail
immediately transfers control to an implementation of the em$fail
intrinsic \u2013 often found within the same module implementing em$halt
; should em$fail
return, the program likewise enters an infinite loop.
By design, each EM distro relies upon the portable em.utils/BoardController module which centralizes definitions of the singleton intrinsics em$reset
, em$startupDone
, em$halt
, and em$fail
; configuration of the BoardController
module and its dependents typically occurs within the distro's BoardC composite. In those (rare) circumstances where some higher-level module needs to \"override\" one of these special functions, the higher-level intrinsic definition would likely call the corresponding \"base\" function within BoardController
.
While we've already directed you to browse selected\u2009.em
source files drawn from EM platform runtime, Chapter 4 concludes our technical overview with more comprehensive picture of this environment.
While software design and development focuses on individual\u2009.em
files \u2013 modules, interfaces, composites, templates \u2013 a more coarse-grained construct known as a bundle serves as the unit of software delivery within the EM platform. Like the packages they ultimately contain, each EM bundle bears a globally-unique qualified name suggestive of its publisher and purpose \u2013 though individual\u2009.em
files will only reference packages by name, and never the containing bundle itself.
Representing \"components-in-the-large\", an EM bundle will not only publicize the elements it provides but will also identify other bundles it requires for successful deployment; de\u00adpendent bundles may in turn require other bundles \u2013 reminiscent of the hierarchic import
relation between individual\u2009.em
files. Bundles can also serve as a unit of software versioning within the platform, with dependencies optionally constrained to particular labeled releases.
In the sections that follow, we'll respectively explore these three EM bundles:
em.corehardware-independent packages fundamental to EM
ti.cc23xxhardware-dependent packages comprising a typical EM distro
em.docshardware-independent example programs which use the prior bundles
"},{"location":"intro/to-4/#portable-content","title":"Portable content","text":"As its name suggests, the em.core
bundle incorporates rudimentary and essential content present in all distributions of the EM software platform \u2013 some of which we've already visited earlier in this document. Through aggressive use of the proxy
\u2009\u2013\u2009interface
pattern, a critical subset of the em.core
bundle \u2013 specifically, its em.mcu
and em.utils
packages \u2013 in fact remain 100% portable across all target environments.
This package contains abstract interfaces reflecting the functionality of low-level hardware elements found within embedded MCUs \u2013 ConsoleUartI
, LedI
, WakeupTimerI
, and many others; this package contains also contains \"empty\" implementations of these interfaces (ConsoleUartN
, LedN
, etc), often used as default proxy
bindings. As such, this package serves as a critical hardware abstraction layer (HAL) \u2013 prescribing a fundamental architectural boundary that insulates portable (hardware-independent) content from target-specific (hardware-dependent) content.
This package comprises a somewhat eclectic (and ever-expanding) collection of run-time application services \u2013 ranging from elementary modules like ListMgr
and FiberMgr
up to more sophisticated templates like ButtonT
and LedT
. While mostly supporting program run-time, special host
modules like BoardInfo
and BoardMeta
offer (portable) services used by EM distros at program build-time.
Unlike em.utils
, this package generally limits its contents to what we'll term as global proxies \u2013 a set of well-known modules that implement certain em.hal
interfaces by effectively forwarding function calls to a conformant delegate; ConsoleUart
and Poller
illustrate this pattern. This package also contains the widely-used Common
module, which conveniently aggregates additional global proxies into a single unit.
Invisible to most clients, this package helps bootstrap the implementation of the EM language itself through several distinguished interfaces declaring intrinsic configs and functions:\u00a0 ModuleI
, that all modules or interfaces will inherit; CompositeI
, that all composites will inherit; and TemplateI
, that all templates will inherit. This package also contains the Console
module and its ConsoleProviderI
interface \u2013 supporting printf
statements innate to the language. Finally, this package houses common C/C++ and JavaScript code fragments interpolated during the EM program build-flow.
Using EM will take a more detailed look at the source code of specific\u2009.em
files found in the first three of these packages.
Complementing em.core
and its portable packages, the ti.cc23xx
bundle delivers support for the Texas Instruments CC2340R5 wireless MCU. While specifically targeting a particular MCU family, the organization of the packages found within the ti.cc23xx
bundle follows a pattern generally seen within any EM distro.
This package (and in fact the entire ti.cc23xx
distro bundle) revolves around a single composite conventionally named BoardC
\u2013 responsible for configuring parameters as well as binding proxies exposed by elements of em.core
. This package also contains two other conventionally named units found in any EM distro:\u00a0 the McuC
composite, which (in this case) focuses on the ti.mcu.cc23xx
package; and the special host
BoardMeta
module, which effectively defines the schema of the board-specific YAML file mentioned earlier in the context of configuration and pre-configuration.
Many of the modules in this package provide MCU-specific implementations of abstract interfaces found in em.hal
\u2013 ConsoleUart0
, Idle
, OneShotGpt3
, and others. Likewise, this package features templates such as GpioT
and InterruptT
whose em$generateUnit
intrinsics synthesize MCU-specific implementations of other HAL interfaces at program build-time. Finally, this package contains even lower-level auxiliary modules (eg, Regs
and Rtc
) whose clients typically remain within the distro itself.
This package contains host
modules that control the compiling\u2009/\u2009linking of target EM programs using a particular C/C++ toolchain. The modules typically pass target-specific information such as memory maps or register declarations to generic modules found in a special em.build
bundle, which we'll discuss in a later document.
Porting EM will dive into virtually all of the\u2009.em
source files found in these three packages, as well as explore the em.build
bundle mentioned above.
The em.examples.basic package within the em.docs
bundle contains a curated set of programs described at length in Using EM. A live sequence of \"guided tours\" that view\u2009/\u2009build\u2009/\u2009load these programs revolve around the following modules:
To ensure their portability, none of these programs explicitly reference the \"current\" EM distro package [\u2009ti.distro.cc23xx
\u2009] by name; rather, these programs use a special language intrinsic [\u2009em$distro
\u2009] as a logical name which you will bind to a particular distro package.
If you want to learn more about EM \u2013 but continue to fly at 10,000' for now \u2013 we strongly recommend reading through Using EM to get a sense for the platform's tooling and runtime from a ground-level perspective; a quick pass through the other documents at this site should prove informative as well.
When you want to hit the ground running and start coding, however, you'll definitely need to first work through Installing EM before proceeding onward to Using EM. Both of these documents also accomodate a special **\u2009FAST-TRACK\u2009** option, which enables you to rapidly experience the EM development environment as well as learn more about em.core.
The Porting EM document continues on the ground, exploring the ti.cc23xx bundle in greater detail as well as outlining the steps required to create new EM distros that support other MCUs. Over time, we expect Porting EM will draw upon a broader set of distro bundles to further reinforce the EM platform's architecture for encapsulating target-specific content.
Finally, Advancing EM contains an ever-growing collection of standalone articles addressing a wide-range of topics about EM. While many of these articles expect that you've already spent time on the ground coding, others presume just a 10,000' understanding of Using EM.
Case in point:\u00a0\u00a0 we strongly recommend the Energy Consumption article, which presents further evidence supporting a hypothesis about EM put forth earlier.
Happy coding\u2009!!! \u2002
"},{"location":"porting/","title":"Managing hardware diversity in EM","text":"Under Construction \u2014 estimated completion in 1Q24 \u2009
"},{"location":"using/","title":"Writing portable applications in EM","text":"If you've made it this far, let's hit the ground running and starting coding\u2009!!!\u00a0\u00a0 This document will first familiarize you with the mechanics of viewing, editing, building, and loading EM programs and their constituent units using the EM Builder extension for VS Code. If you haven't worked with VS Code, the web abounds with tutorials\u2009+\u2009references for this ever-popular IDE.
The main portion of this document comprises a series of tours that visit some basic programming examples \u2013 curated to introduce elements of the EM runtime environment, as well as to reinforce common coding idioms of the EM language(1). To support these documentation resources, EM Builder includes a \"tour guide\" utility that walks you through this material in a somewhat specialized fashion.
If you've chosen to bypass Installing EM for now, a quick read through this document would still have benefit \u2013 even if you can't execute real software on real hardware.
If you have elected to install EM Builder \u2013 but haven't received your hardware or else prefer the **\u2009FAST-TRACK\u2009** option \u2013 you can still build the programs featured in any of the tours. Until your board arrives and you can actually load these programs, studying the logic capture(s) associated with each example should help bridge the gap.
These captures in fact reveal subtle timing details otherwise invisible to the naked eye.
We've generated all of the captures presented later in this document using a Saleae Logic Analyzer \u2013 in our opinion, the single most valuable tool for \"debugging\" real-time embedded software running on target MCU hardware. At a minimum, you should download the (free) Saleae Logic 2 software and explore these logic capture files off-line.
If you can't see the problem, you can't fix it\u2009!!!Software debuggers have traditionally focused upon a rich presentation of program state (variables, call stacks, peripherals) \u2013 but only after program execution halts (say) upon reaching a breakpoint. Given the general nature of embedded software, however, a dynamic presentation depicting state-changes over time proves far more helpful for troubleshooting real-world problems.
While modern debuggers attempt to address this need, they often do so by introducing significant amounts of hardware\u2009/\u2009software overhead \u2013 limiting their utility to software development rather than system deployment. These approaches may also preclude the underlying MCU from entering its \"deep-sleep\" mode \u2013 a serious limitation that can obscure real-world, real-time wakeup issues.
But armed with no more than a basic logic analyzer, the EM language provides intrinsic \"real-time debug\" capabilities that enable us to dynamically capture program state without incurring excessive program overhead. In the spirit of \"test what you fly, fly what you test\", these capabilities prove equally valuable during development in the factory and well as during deployment in the field.
At the same time, we also want you to experience the joy of blinking LEDs\u2009!!!\u00a0\u00a0 With that, let's first skill-up on EM Builder and then tour the EM runtime.\u00a0
When finished, circle back to Learning more EM and plan your next adventure.
"},{"location":"using/using-01/","title":"Working with EM Builder","text":"To flatten your learning curve, we'll work exclusively within the VS Code environment \u2013 using the EM Builder extension which you've already installed. Besides introducing some core capabilities of the extension, the material that follows will also verify the integrity of your installation.
But what about the command line\u2009???Strictly speaking, we don't need VS Code to develop EM software:\u00a0 you can create folders\u2009/\u2009files as you always have; you can create\u2009/\u2009modify\u2009.em
sources in your favorite text editor; and you can build\u2009/\u2009load executable EM programs using the em-cli
command-line interface.\u00a0 The implementation of EM Builder and em-cli
, in fact, share quite a bit of common code \u2013 with the former often invoking the latter. The Command-line inteface reference material describes the em-cli
tool in greater detail.
The following animation illustrates the overall organization of your workspace folder when using the EM Builder extension, and picks up from where we left off when Installing EM.\u2009 We also encourage you to launch VS Code by entering code
workspace
from your PC's shell, and to simultaneously explore this environment at your own pace.
The following animation illustrates how to build HelloP.em
using the EM - Build command, selected from this file's (right-click) context menu. Since this program contains a main em$run
function, the EM translator produces a corresponding executable image; see the OUTPUT panel for details.
The following animation illustrates how to load the previously-built HelloP.em
program using the EM - Load command \u2013 also selected from a context menu. Should loading the executable image fail for some reason (eg, you don't even have a target board), an error message would appear in the OUTPUT panel.
The following animation illustrates how to monitor any printf
output from the currently loaded program image, by invoking EM - Open Console within the VS Code Command Palette\u2009. Once the special TERMINAL panel has launched, press the RESET button on your target board and enjoy the show\u2009!!!
The following animation illustrates how to create a workspace bundle using the EM - New Bundle command, likewise found in the Command Palette\u2009. In this case, we've named the new artifact as my.bundle
; the remainder of this document will often refer to this folder as your \"personal\" bundle.
The following animation illustrates how to populate your personal bundle folder with other EM artifacts \u2013 packages, modules, interfaces, etc \u2013 created using commands selected from the (right-click) context menu of the containing folder. In this case, we'll invoke EM - New Package followed by EM - New Test Program to create a unit named my.pkg/Test
.
The following animation illustrates the EM - Clone command, used to populate personal bundles with runtime content delivered by the EM Builder extension and housed under the special\u2009.em-cargo
folder. In this case, we'll first locate em.examples.basic/FiberP
and em.utils/FiberMgr
under\u2009.em-cargo
and then clone these units into my.bundle
.
The following animation illustrates a few of the VS Code language features supported by the EM Builder extension. In this case, we'll hover over EM identifiers and jump between\u2009.em
source files; we'll also use the OUTLINE panel to navigate amongst the declarations\u2009/\u2009definitions contained within individual EM units.
While these capabilities prove helpful when exploring EM source code, the EM Builder extension also provides support for \"smart completion\" (IntelliSense) \u2013 even more helpful when editing your code.\u00a0 At the same time, VS Code language support remains a never-ending journey of incremental improvement.
Help wanted \u2013 VS Code programmer
"},{"location":"using/using-02/","title":"Programming with EM","text":"Turning now to the EM runtime, a series of curated \"guided tours\" will incrementally introduce components of the em.core bundle \u2013 many of which you may have browsed in your reading of Introducing EM.\u2009 By convention, each tour revolves around a specific EM program found in the em.examples.basic package \u2013 with each of these small programming examples motivating us to visit key elements of the EM runtime.
In the material that follows, we'll reference a number of logical MCU \"pins\" configured by the current EM distro and generally accessible through headers on your target board:
\u2003\u2003\u2003\u2003\u2003appBut
application button pin \u2003\u2003\u2003\u2003\u2003appLed
application LED pin (usually green) \u2003\u2003\u2003\u2003\u2003appOut
application console TX pin \u2003\u2003\u2003\u2003\u2003sysDbgA
system debug pin A \u2003\u2003\u2003\u2003\u2003sysDbgB
system debug pin B \u2003\u2003\u2003\u2003\u2003sysDbgC
system debug pin C \u2003\u2003\u2003\u2003\u2003sysDbgD
system debug pin D \u2003\u2003\u2003\u2003\u2003sysLed
system LED pin (usually red) A special YAML file named em-boards
found the em$distro
package folder binds these logical pin names to physical pin numbers of your target board. Porting EM will have more to say about em-boards
, as well as explain the special setup-*.properties
files located in the root of your distro bundle.
The following animation illustrates the EM - Start Tour command, which you'll use to view each of the tours described in the remainder of this document. You'll find these tours in appropriately named\u2009.emtour
files, located inside the\u2009.tours/101_Using_EM
folder delivered with the em.docs
bundle.
With that, go ahead and actually take tour 00_HelloP.emtour
within your VS Code environment. Before proceeding to take Tour 01\u2009\u2013\u2009Tour 12\u2009, however, you should feel comfortable with the UI presented in Tour 00\u2009; and do retake any of these tours whenever necessary.
This tour centers around the BlinkerP program, which toggles the appLed
pin on your target board \u2013 typically connected to a green LED. This tour also visits the BusyWaitI and LedI interfaces, as well as presents the Common module and its constituent proxies.
\u00a0 The EM runtime quickly blinks your board's sysLed
at startup.
\u00a0 The BlinkerP
program toggles appLed
every ~0.5\u2009s.
\u00a0 The EM runtime turns on sysLed
upon normal program termination.
\u00a0 The next capture zooms into the appOut
signal.
\u00a0 The EM runtime outputs this sequence of (non-printable) bytes at startup, after blinking sysLed
.
\u00a0 The EM runtime output this single byte upon normal program termination, before turning on sysLed
.
This tour focuses upon the BlinkerDbgP program, which also toggles the appLed
pin on your target board. This tour highlights a number of capabilities within EM for visualizing and troubleshooting program execution in real-time.
\u00a0 The busy-wait bracketed by %%[d+]
and %%[d-]
measures ~498 ms.
\u00a0 Executing a fail
statement causes sysLed
to blink rapidly.
\u00a0 The %%[>cnt]
statement outputs the (non-printable) 0x82
control byte followed by 2 bytes of payload.
\u00a0 The %%[>bits11]
statement outputs the (non-printable) 0x81
control byte followed by 1 byte of payload.
\u00a0 The %%[c:bits11]
statement causes the dbgC
pin to rapidly toggle 2 times \u2013 since (bits11\u2009==\u20090x1)
\u00a0 The ASCII character output of printf
begins here \u2013 0x63('c')
, 0x6E('n')
, and so on.
This tour centers around the FiberP program, which introduces a very lightweight threading construct termed a fiber. This tour also visits the EM runtime's (portable) implementation of fibers found in the FiberMgr module.
Tour 03 \u2013 Logic Capture \u00a0 The %%[d]
statement in blinkFB
marks each point in time where the blinkF
fiber begins execution.
\u00a0 The ~200 ms between blinkF
cycles includes appLed
on\u2009/\u2009off time plus FiberMgr
dispatch overhead.
This tour centers around the Button1P program, which handles incoming events generated when pressing appBut
on your board. This tour also visits the GpioEdgeDetectMinI interface that in turn extends the GpioI abstraction.
\u00a0 The EM runtime uses dbgB
to mirror the MCU's execution mode [\u2009L \u2013 actively running, H \u2013 awaiting wakeup\u2009]
\u00a0 Pressing your board's button drives the appBut
pin low; appLed
then blinks shortly thereafter.
\u00a0 A 125\u2009ns \"glitch\" on the appBut
signal fired another event at this time \u2013 causing an extra appLed
blink.
\u00a0 You've pressed appBut
at this time, which then begins awakening the MCU from its low-power sleep.
\u00a0 Requiring 34.2\u2009\u03bcs to fully power the MCU, dbgB
's falling edge marks when Button1P
starts actively running.
\u00a0 Control enters handler
within 3.4\u2009\u03bcs, whose initial %%[c]
statement toggles dbgC
for 1.2\u2009\u03bcs
\u00a0 After some housekeeping, Button1P
finally turns-on appLed
\u2013 5.9\u2009\u03bcs since actively running.
This tour centers around the Button2P program, which uses a Fiber
object to better handle incoming events. This tour also analyzes the performance impact of this approach compared with the earlier Button1P program.
dbgB
shows that Button2P
awoke exactly twice in response to pressing appBut
\u2013 no glitches this time\u2009!!!
Button2P
resumes execution in 35.2\u2009\u03bcs; keep in mind that low-power MCU wakeup times can vary slightly.
\u00a0 Control then enters handler
3.4\u2009\u03bcs later \u2013 consistent with the Button1P
capture seen above.
\u00a0 Unlike Button1P
, the Button2P
handler
readies the blinkF
fiber which then gains control 5.3\u2009\u03bcs later.
\u00a0 Finally, the blinkFB
function turns-on appLed
\u2013 with only ~5\u2009\u03bcs of FiberMgr
scheduling overhead.
This tour centers around the Button3P program, which debounces button input signals in a portable fashion. This tour also visits the ButtonI interface along with a module implementing this interface \u2013 the latter generated by the ButtonT template at build-time.
Tour 06 \u2013 Logic Capture \u00a0 Once triggered by pressing your board's button, the EM runtime polls the state of appBut
every 100\u2009ms.
\u00a0 If pressed for \u2265\u2009100\u2009ms but \u2264\u20094\u2009s, Button3P
will blink appLed
within 100\u2009ms of releasing the button.
\u00a0 Hitting the 4\u2009s mark, Button3P
immediately blinks sysLed
\u2013 regardless of how long appBut
remains low.
\u00a0 After polling appBut
for 4\u2009s, the EM runtime invokes Button3P.onPressedCB
which leaves a %%[c]
mark.
\u00a0 Discovering that appBut
remains pressed [\u2009=\u2009L\u2009], onPressedCB
then blinks sysLed
for 40\u2009ms.
\u00a0 Only 7.5\u2009\u03bcs elapses from dbgB
falling to sysLed
rising, which includes scheduling fibers plus 1.2\u2009\u03bcs for %%[c]
.
This tour centers around the OneShot1P program, which handles timer events that awaken the MCU. This tour also visits the OneShotMilliI interface, which abstracts the functionality of a short-term timer with millisecond resolution.
Tour 07 \u2013 Logic Capture \u00a0 The dbgB
signal shows that OneShot1P
awakens from low-power sleep every 500\u2009ms.
\u00a0 Once awake, OneShot1P
toggles dbgC
and dbgD
before blinking appLed
\u2013 like our earlier Button1P
capture
\u00a0 The 7.5\u2009\u03bcs \"wakeup-to-blink\" latency seen here includes the overhead of servicing an on-chip MCU timer.
"},{"location":"using/using-02/#tour-08-timer-fibers","title":"Tour 08 \u2013 timer fibers","text":"This tour centers around the OneShot2P program, which now uses Fiber
objects to enhance robustness when handing timer events. This tour also invites performance comparision with the earlier OneShot1P program.
\u00a0 Fiber scheduling adds the extra 1.1\u2009\u03bcs of latency seen here, compared with our previous OneShot1P
capture.
This tour centers around the PollerP program, which introduces a portable function for pausing execution at run-time. This tour also visits the top-level Poller module as well as the lower-level PollerAux module \u2013 both implementing the PollerI interface.
Tour 09 \u2013 Logic Capture \u00a0 Without %%[c]
and %%[d]
marks, PollerP
further reduces latency compared to OneShot1P
and OneShot2P
.
\u00a0 The [L] dbgB
signal indicates that the PollerP
program has awoken.
PollerP
then calls AppLed.wink
within 2.5\u2009\u03bcs, which then asserts the appLed
pin
\u00a0 Calling AppLed.wink
2.5\u2009\u03bcs later asserts the appLed
pin and then internally invokes Poller.pause
.
Poller.pause
proceeds to suspend program execution for 5\u2009ms \u2013 the duration of the wink.
\u00a0 The EM runtime toggles dbgB
once before awaiting the next wakeup [H]\u2009; we'll explain more in a later tour.
\u00a0 An MCU timer event will eventually awaken the program 5\u2009ms later, with dbgB
now signalling [L]\u2009.
\u00a0 Control unwinds from Poller.pause
back to AppLed.wink
, which will now lower the appLed
pin.
em$run
finally regains control, and then suspends execution for 500\u2009ms by calling Poller.pause
directly.
This tour centers around the Alarm1P program, which uses Alarm
objects to schedule longer-term wakeups with (sub-)second resolution. This tour also visits the AlarmMgr module, which internally uses a proxy implementing the WakeupTimerI interface.
\u00a0 The Alarm1P
program alternately awakens from a low-power deep-sleep of 2\u2009s or else 750\u2009ms in duration.
\u00a0 The program's blinkFB
function winks appLed
for 100\u2009ms, with the MCU idling in the interim.
\u00a0 Once AppLed.wink
returns, Alarm1P
calls alarm.wakeup
to re-enter an extended period of deep-sleep.
\u00a0 The single dbgB
mark indicates the MCU has entered its \"lite-sleep\" mode, after Alarm1P
calls AppLed.wink
\u00a0 The MCU awakens from its lite-sleep within 100\u2009ms, returning to wink
which then turns-off appLed
.
\u00a0 The double dbgB
mark indicates the MCU has entered its \"deep-sleep\" mode, after calling alarm.wakeup
This tour centers around the Alarm2P program, which now aligns Alarm
wakeups with a given time-window. This tour also re-visits the AlarmMgr implementation, seeing how it schedules the next wakeup event as well as the role played by the EpochTime module.
\u00a0 Note how the wakeups from deep-sleep align with a series of 1.5\u2009s windows along the time-line.
\u00a0 Due to startup overhead, the first alarm.wakeup
call deep-sleeps the MCU for only 1.250\u2009s to stay aligned.
\u00a0 After a 5\u2009ms appLed
wink, this alarm.wakeup
call deep-sleeps the MCU for 1.495\u2009s to stay aligned.
\u00a0 But after a 100\u2009ms wink, this alarm.wakeup
call deep-sleeps the MCU for just 1.400\u2009s to stay aligned.
This tour centers around the TickerP program, which takes duty-cycled functions to a higher level. This tour also visits the TickerMgr module, whose implementation of Ticker
objects emulates as well as builds upon the AlarmMgr and FiberMgr studied earlier.
\u00a0 The appTicker.start
call by the TickerP
program initiates a train of 100\u2009ms appLed
winks, spaced 1\u2009s apart.
\u00a0 The sysTicker.start
call by TickerP
then initiates a train of 100\u2009ms sydLed
winks, but spaced 1.5\u2009s apart.
\u00a0 When appTicker
and sysTicker
wakeups coincide, their callbacks run on a first-come, first-served basis.