Recent Changes - Search:

edit SideBar

Duktape Implementation in ARM-M4

Minimum Requirements

In order to use Accessor in with Duktape, the system shall have the following as a minimum:

  • 250Kbytes in RAM-Heap available
  • 2Kbytes in RAM
  • 200Kbytes in Flash

Note: Above configuration is assuming bare-metal application, OS is NOT considered.

Duktape Configuration

For smaller devices like ARM-Cortex M without File System the next options needs to be set:

  • DUK_OPT_NO_FILE_IO
  • DUK_OPT_LIGHTFUNC_BUILTINS
  • DUK_OPT_BUFLEN16
  • DUK_OPT_OBJSIZES16
  • DUK_OPT_REFCOUNT16
  • DUK_OPT_STRHASH16
  • DUK_OPT_STRLEN16
  • DUK_OPT_SELF_TESTS

For details on what each option does, refer to Duktape Feature Options

Duktape Auxiliary print option

Duktape implements the print function as a mechanism to send information to the screen. In case of small devices, there is no such availability, however it is possible to create a new print function which will redirect the information to a desired port or display.

Example: Using Semi-host trace function as Duktape print

When Duktape has the option DUK_OPT_NO_FILE_IO it automatically disable all print functions.

To create and register a new function using the Semihost option trace provided in Eclipse, use the next funciton

#include <stdio.h>
#include "duktape.h"

static duk_ret_t my_print(duk_context *ctx) {
    duk_idx_t i, n;
    trace_printf("PRINT: ");
    for (i = 0, n = duk_get_top(ctx); i < n; i++) {
        if (i > 0) {
                trace_printf(" ");
        }
        trace_printf("%s", duk_safe_to_string(ctx, i));
    }
    trace_printf("\n");

    return 0;
}

void my_print_register(duk_context *ctx) {
  duk_push_global_object(ctx);
  duk_push_c_function(ctx, my_print, DUK_VARARGS);
  duk_put_prop_string(ctx, -2, "print");
  duk_pop(ctx);
}

Call the function my_print_register and now we have a print function available. It can be changed to send data via serial port or any other way.

Accessor Host Integration to Duktape

Accessor duktape is divided in two groups:

  • One is the core of Accessor framework which is Javascript code and couple files are C-file implementing timing functions and file localization
  • The other part is the application.

As it appears in the terraswarm repository, we can divide the files as shown below:

As it can be seen, the file nofileio.c has a double function:

  • It registers the available JS files
  • It serves as a duktape modSearch implementation

If it is desired to modify the application, it will be necessary to re-create nofileio.c

Necessary Modifications

  • Current Accessor host assumes that fprintf function is supported. For small devices, it is not always the case, so it was necessary to replace ALL the fprintf functions with trace_printf.
  • In particular, the function print_pop_error needs to be modified, commenting out the call to fflush
// print_pop_error is from Duktape's duk_cmdline.c
/* Print error to stderr and pop error. */
void print_pop_error(duk_context *ctx) {
    /* Print error objects with a stack trace specially.
     * Note that getting the stack trace may throw an error
     * so this also needs to be safe call wrapped.
     */

    (void) duk_safe_call(ctx, get_stack_raw, 1 /*nargs*/, 1 /*nrets*/);
    trace_printf("%s\n", duk_safe_to_string(ctx, -1));
    // fflush(f);
    duk_pop(ctx);
}

main Function call

The main function to run Accessor consist of function registration and event thread execution as follows

int main (void) {
  const char *accessorFileName = "RampJSTestDisplay.js";    // Desired Accessor to run
  duk_context *ctx = NULL;
  int timeout = 5000;
  int returnValue = 0;
  // Normally at this stage most of the microcontroller subsystems, including
  // the clock, were initialised by the CMSIS SystemInit() function invoked
  // from the startup file, before calling main().
  // (see system/src/cortexm/_initialize_hardware.c)
  // If further initialisations are required, customise __initialize_hardware()
  // or add the additional initialisation here, for example:
  //
  trace_puts("Hello ARM-M$ World!");
   HAL_Init();

   // Create duktape environment
   ctx = duk_create_heap_default();

   // Register Modules
   eventloop_register(ctx);
   modSearch_register(ctx);
   nofileio_register(ctx);
   my_print_register(ctx);
   trace_printf("Registered all the modules.\n");

   trace_printf("eduk: About to run %s\n", accessorFileName);
   returnValue = runAccessorHost(ctx, accessorFileName, timeout);

   return returnValue;
}

runAccessorHost

/** Run the accessor.
 * @param accessorFileName The file name of the accessor, suitable getAccessorCode()
 * @param timeout The number of milliseconds to wait after the
 * accessor is instantiated and initialized.  If the timeout is less than zero,
 * then the timeout will be forever.
 * @return 0 if successfully, non-zero if there is an error.
 */

int runAccessorHost(duk_context *ctx, const char *accessorFileName, int timeout) {
    int  rc;

    //ftrace_printf(stderr, "%s:%d: About to load C version of c_eventloop.\n", __FILE__, __LINE__);

    // Use duk_peval_string_noresult() and avoid interning the string.  Good
    // for low memory, see
    // http://duktape.org/api.html#duk_peval_string_noresult
    if (duk_peval_string(ctx, c_eventloop_js) != 0) {
        trace_printf("%s:%d: Loading C version of c_eventloop failed.  Error was:\n", __FILE__, __LINE__);
        print_pop_error(ctx);
        return 1;
    } else {
        trace_printf("%s: Loading C version of c_eventloop worked\n", __FILE__);
        duk_pop(ctx);
    }

    // Use duk_peval_string_noresult() and avoid interning the string.  Good
    // for low memory, see
    // http://duktape.org/api.html#duk_peval_string_noresult
    if (duk_peval_string(ctx, ___duktapeHost_js) != 0) {
        trace_printf("%s:%d: Loading C version of duktapeHost failed.  Error was:\n", __FILE__, __LINE__);
        print_pop_error(ctx);
        return 2;
    } else {
        trace_printf("%s: Loading C version of duktapeHost worked\n", __FILE__);
        duk_pop(ctx);
    }

    // Call instantiateAndInitialize() on the accessorFileName, then timeout.

    // Build the command to be evaled.
    int length = my_strlen(accessorFileName) + 200;
    char buf[length];
    if (timeout >= 0) {
        // Timeout.  requestEventLoopExit() is defined in ecma_eventloop.js.
        snprintf(buf, length, "var args = ['%s'];\ninstantiateAndInitialize(args);\nsetTimeout(function () {requestEventLoopExit()}, %d);", accessorFileName, timeout);
    } else {
        // Prevent the script from exiting by repeating the empty function
        // every ~25 days.
        snprintf(buf, length, "var args = ['%s'];\ninstantiateAndInitialize(args);\nsetInterval(function () {}, 2147483647);", accessorFileName);
    }

    // Eval the command, avoid interning the string.
    if (duk_peval_string(ctx, buf) != 0) {
        trace_printf("%s:%d: Failed to invoke accessor %s.  Command was:\n%s\nError was:\n", __FILE__, __LINE__, accessorFileName, buf);
        print_pop_error(ctx);
        return 3;
    } else {
        duk_pop(ctx);
    }

    // Compile solution
    //duk_compile(ctx, 0);

    /* duk_push_global_object(ctx);  /\* 'this' binding *\/ */
    /* duk_insert(ctx, -2);  /\* [ ... global func ] *\/ */
    /* duk_put_prop_string(ctx, -2, "_USERCODE"); */
    /* duk_pop(ctx); */

    /* duk_eval_string(ctx, "setTimeout(function() { _USERCODE(); }, 0);"); */
    /* duk_pop(ctx); */


    rc = duk_safe_call(ctx, eventloop_run, 0 /*nargs*/, 1 /*nrets*/);
    if (rc != 0) {
        trace_printf("%s:%d: %s: Failed invoke eventloop_run()\n", __FILE__, __LINE__, accessorFileName);
        return 4;
    }
    trace_printf("runAccessorHost() done.\n");
    return 0;
}
 

Issues

Once that above changes were completed, we built the system and tried to run it on ARM-Cortex M4 (LPC 4088)

  • JS file failed parsing
eduk: About to run RampJSTestDisplay.js
../src/main.c:61 Loading C version of c_eventloop failed. Error was:
SyntaxError: utf8 decode failed (line 1)
          ../src/main.c:1
          duk_lexer.c:314

We tried using different J2H tool (the one that we developed internally) and the program started running.

PRINT: loading module: commonHost
SyntaxError: utf8 decode failed (line 1)
          ../src/main.c:1
          duk_lexer.c:314

As we progress, we identified that all JS programs needed to be re-created. We replaced all JS files and the system worked as expected.

Hello ARM-M$ World!
SDRMA Init and OK
Registered all the modules.
eduk: About to run RampJSTestDisplay.js
../src/main.c: Loading C version of c_eventloop worked
PRINT: loading module: commonHost
PRINT: loading module: util
PRINT: loading module: events
../src/main.c: Loading C version of duktapeHost worked
PRINT: duktapeHost.js: instantiateAndInitialize() start: RampJSTestDisplay.js 1
PRINT: duktapeHost.js: instantiateAndInitialize(): about to handle RampJSTestDisplay.js
PRINT: duktapeHost.js: instantiateAndInitialize(): about to call initialize name RampJSTestDisplay.js  Class  RampJSTestDisplay.js
PRINT: duktapeHost.js: before instantiate() : accessor RampJSTestDisplay.js with class RampJSTestDisplay.js
PRINT: Memory allocated successfully for accessor = RampJSTestDisplay.js.TestSpontaneous
PRINT: Memory allocated successfully for accessor = RampJSTestDisplay.js.TestDisplay
PRINT: Memory allocated successfully for accessor = RampJSTestDisplay.js.TrainableTest
PRINT: Memory allocated successfully for accessor = RampJSTestDisplay.js
PRINT: duktapeHost.js: instantiate() done: Instantiated accessor RampJSTestDisplay.js with class RampJSTestDisplay.js
PRINT: duktapeHost.js: instantiateAndInitialize(): about to call initialize on [object Object]
PRINT: duktapeHost.js: instantiateAndInitialize(): done with RampJSTestDisplay.js
PRINT: duktapeHost.js: instantiateAndInitialize() done
PRINT: 1
PRINT: 2
PRINT: 3
PRINT: 4

Comparing both one file using Terraswarm J2H tool and ours we notice:

  • In some cases J2H removes ASCII 10
  • In other the comment character (//) is chopped to /
  • Byte size reported is different in both cases (may be because J2H is removing comments?)

See Also

Edit - History - Print - Recent Changes - Search
Page last modified on December 30, 2016, at 04:08 PM