nmc-utils  0.1.1
Loading, starting and stopping DSP Applications

Functions

int easynmc_set_args (struct easynmc_handle *h, char *self, int argc, char **argv)
int easynmc_load_abs (struct easynmc_handle *h, const char *path, uint32_t *ep, int flags)
int easynmc_start_app (struct easynmc_handle *h, uint32_t entry)
int easynmc_exitcode (struct easynmc_handle *h)
int easynmc_stop_app (struct easynmc_handle *h)
void easynmc_register_section_filter (struct easynmc_handle *h, struct easynmc_section_filter *f)
 Register a section filter.
struct easynmc_handleeasynmc_open (int coreid)
int easynmc_for_each_core (int(*core_cb)(struct easynmc_handle *h, void *udata), int exclusive, void *udata)
void easynmc_close (struct easynmc_handle *hndl)

Detailed Description

This set of functions controls the execution of DSP programs. Neuromatrix core start in 'cold' (stopped) state. They first need a proper IPL code that occupies the first few KiB if DSP SRAM. This IPL code sits there, handles NMI interrupt and performs jumps to user code. IPL sources are located under the ipl/ directory of nmc-utils repository. The simplified state diagram of DSP states is shown below.

dot_nmc-states-simplified.png

Normally, you start working by opening a DSP core using easynmc_open(). The open function takes care off all the work to load the ipl code if the core is in 'cold' state, check ipl version compatibility and the rest.

easynmc_open() guarantees that the core it returns is in IDLE state. If EASYNMC_CORE_ANY is passed as the core number, easynmc() will search all cores in idle or cold state.

Once you have the core opened, you have a handle, which is pointer to a C struct. Members of this struct are file descriptors for Neuromatrix's stdio (iofd) and memory (memfd). Neuromatrix DSP memory is also mmaped to the application using and can be accessed as a byte array OR a uint32_t array via imem and imem32 pointers respectively.

Since all of the memory is exposed to userspace, care must be taken not to overwrite any part of the ipl code (which resides in first ~2KiB of internal ram).

After opening the device you can add any section filters using easynmc_register_section_filter().

N.B.: Each section filter can only be associated with one nmc handle.

After all the section filters have been registered you can load an abs file using easynmc_load_abs().

If the loading succeeds - you will get the valid entry point for your application.

At this point you can set up one or more arguments for you application with easynmc_set_args(), these will arrive in argc and argv on the nmc side.

Next, it's time to start your application using the obtained entry point passed to easynmc_start_app();

At this point you can interact with your application via stdio, listen for events, (See. Handling events: token API and/or Handling events: select/epoll) or just wait for the app to stop and fetch the exit code using easynmc_exitcode()

Closing the handle with easynmc_close() automatically terminates the application, unless application persistence has been explicitly enabled. (See DSP Application as a service)

Function Documentation

void easynmc_close ( struct easynmc_handle hndl)

Close and free easynmc handle. Unless app persistance has been enabled for this core, any running application will be killed by the kernel driver. This function releases the exclusive lock (if any) held by the process on this core.

Parameters
hndl

Definition at line 939 of file easynmc-core.c.

References dbg, EASYNMC_CORE_KILLABLE, EASYNMC_CORE_RUNNING, easynmc_handle::id, easynmc_handle::imem, easynmc_handle::imem32, easynmc_handle::imem_size, easynmc_handle::iofd, easynmc_handle::memfd, NMC_REG_CORE_STATUS, and easynmc_handle::persistent.

Referenced by do_irq(), do_start_app(), easynmc_for_each_core(), easynmc_open(), and run_interactive_console().

int easynmc_exitcode ( struct easynmc_handle h)

Fetch the exit code of the last executed app.

Parameters
h
Returns
exit code from nmc application

Definition at line 763 of file easynmc-core.c.

References easynmc_handle::imem32, and NMC_REG_PROG_RETURN.

Referenced by run_interactive_console().

int easynmc_for_each_core ( int(*)(struct easynmc_handle *h, void *udata)  core_cb,
int  exclusive,
void *  udata 
)

Helper function. Interate over all available cores and do the following:

  • Open the core (if exclusive is '1' - attempt to get exclusive core access
  • Run the specified callback with nmc core handle as the argument
  • Parse callback return code. The return code can be 0 or one of the following bit flags:
  • EASYNMC_ITERATE_STOP - break the loop
  • EASYNMC_INTERATE_NOCLOSE - Do not call easynmc_close after the callback has returned

Note: This function doesn't attempt to boot cores or alter the core state in any smart way, like easynmc_open() does. If you just need a free core - see easynmc_open()

Parameters
core_cbThe callback to run
exclusiveLock core for exclusive access
udataUserdata pointer to pass to the callback

Definition at line 903 of file easynmc-core.c.

References easynmc_close(), EASYNMC_ITERATE_NOCLOSE, EASYNMC_ITERATE_STOP, easynmc_open_noboot(), and err.

Referenced by easynmc_connect(), and easynmc_open_noboot().

int easynmc_load_abs ( struct easynmc_handle h,
const char *  path,
uint32_t *  ep,
int  flags 
)

Load an abs file into DSP memory and get a reference to the entry point. The entry point can only e considered valid if loading succeeds. You can later use the obtained entry point to start program execution.

Make sure you have set up any abs filters you need BEFORE calling this function.

For a reasonable set of default flags use ABSLOAD_FLAG_DEFAULT. This should be good for 99% of cases.

Normally, you can only load code when the core is not running (cold or idle). However, in some weird cases you may want to override this check. This can be done via ABSLOAD_FLAG_FORCE. Just don't shoot yourself in the knee.

Parameters
hdevice handle
pathfile path
epa pointer to uint32_t, will be used to store entry point if loading succeeds.
flagsone or more ABSLOAD_FLAG_*
Returns

Definition at line 582 of file easynmc-core.c.

References ABSLOAD_FLAG_FORCE, easynmc_handle::argoffset, dbg, EASYNMC_CORE_INVALID, EASYNMC_CORE_KILLABLE, EASYNMC_CORE_RUNNING, easynmc_state_name(), err, easynmc_section_filter::handle_section, easynmc_handle::imem, easynmc_section_filter::name, easynmc_section_filter::next, and easynmc_handle::sfilters.

Referenced by do_load_abs(), easynmc_boot_core(), and main().

struct easynmc_handle* easynmc_open ( int  coreid)
read

Open a Neuromatix core and return a handle. This function returns a handle, that should be used for all operations with this core. The handle itself is just a pointer to a structure that contains a few file descriptors and mmaped DSP memory. You can pass EASYNMC_CORE_ANY as coreid parameter to open the first available core. On multicore DSP systems easynmc_open() with EASYNMC_CORE_ANY searches for cores in the following order:

  • Cores that are in states EASYNMC_CORE_IDLE or EASYNMC_CORE_COLD are searched first
  • Cores that are in states EASYNMC_CORE_KILLABLE

Opening a NeuroMatrix core with easynmc_open() always acquires an exclusive lock for that core, so that no other app can use it. Killing the userspace application that has the lock automatically releases the core.

Parameters
coreid
Returns

Definition at line 849 of file easynmc-core.c.

References easynmc_boot_core(), easynmc_close(), EASYNMC_CORE_COLD, EASYNMC_CORE_KILLABLE, easynmc_open_noboot(), EASYNMC_PERSIST_DISABLE, easynmc_persist_set(), and easynmc_stop_app().

Referenced by add_core_to_logger(), and main().

void easynmc_register_section_filter ( struct easynmc_handle h,
struct easynmc_section_filter f 
)

Register a section filter.

Section filters are a convenient way to attach any custom code interfacing with DSP. If you need to implement your own way of talking to the DSP - this is how you can do it.

Parameters
h
f

Definition at line 816 of file easynmc-core.c.

References easynmc_section_filter::next, and easynmc_handle::sfilters.

Referenced by easynmc_init_default_filters().

int easynmc_set_args ( struct easynmc_handle h,
char *  self,
int  argc,
char **  argv 
)

Setup arguments for an NMC program. This function takes care of reformatting strings and putting them in the relevant memory places.

Note: The compiled binary must have enough space allocated for the arguments during compilation with EASYNMC_ARGS macro (Normally done in easyconf.asm).

Parameters
heasynmc handle
selfargv[0]
argcargc
argvArray of arguments. Starting at what would be argv[1] on nmc.
Returns
0 if everything is OK; -1 - current handle has no argument offset. -2 - arguments size exceed the space available in the relevant section of the DSP program.

Definition at line 521 of file easynmc-core.c.

References easynmc_handle::argdatalen, easynmc_handle::argoffset, err, and easynmc_handle::imem32.

Referenced by main().

int easynmc_start_app ( struct easynmc_handle h,
uint32_t  entry 
)

Start application execution at the specified entry point. Use the entry point obtained from easynmc_load_abs()

Parameters
h
entry
Returns

Definition at line 743 of file easynmc-core.c.

References EASYNMC_CORE_IDLE, easynmc_state_name(), err, easynmc_handle::imem32, NMC_REG_CORE_START, and NMC_REG_PROG_ENTRY.

Referenced by do_start_app(), and main().

int easynmc_stop_app ( struct easynmc_handle h)

Terminate a running application and return to IPL. This function may block for a little while.

Notes about termination: If the application overrides the NMI handler - this call will never succeed. This is normal. The application should never touch the NMI handler. IPL takes care of cleaning up any leftovers in vector FIFOs. Right now the only to fix if this function doesn't succeed - reboot the board.

Parameters
h
Returns

Definition at line 780 of file easynmc-core.c.

References dbg, EASYNMC_CORE_IDLE, EASYNMC_CORE_KILLABLE, EASYNMC_CORE_RUNNING, easynmc_send_irq(), and err.

Referenced by die(), do_kill(), and easynmc_open().