Post date: Feb 22, 2016 10:24:24 AM
In order to allow circuits to simulated on the device itself, it is necessary to have a SPICE engine on the device itself. As we stated in the proposal presentation, the intention is to use NGSPICE[1] for this. Specifically, NgSpice release 26 is being used. NgSpice is a C codebase, and thus it becomes necessary to cross the C-Java barrier between the UI and the simulator.
Two approaches for this are possible, one is to compile NgSpice as a separate executable, and to invoke it with Runtime.exec() [2] . The other is to compile it as a library, and interface with this library via the JNI. We decided to go with the former, both for simplicity and also since this interface can potentially be used over a network (via SSH) while the latter is inherently local.
Thus we arrive at the actual task being considered in this update: NgSpice must be cross-compiled.
(This must occur either way, only the desired target file changes). The NgSpice source tree is designed to be build with GNU autotools (i.e. a ./configure; make; make install). Since re-organising the source to use, say, Gradle, would be very time-consuming, we decided to cross-compile under Autotools.
Firstly, we installed the NDK to get the Android C compiler and libraries. The compilers shipped with this are configured to be invoked by Android Studio with library paths given explicitly each run, so the make-standalone-toolchain.sh[3] script from the NDK was used to make a copy of the compilers with the library paths baked-in so the compiler can be run directly from the command line, and the resulting binaries added to PATH so the compiler can just be invoked as arm-linux-androideabi-gcc.
Next the actual build can begin. First ./configure --host arm-linux-androideabi was used to configure for cross-compile, followed by make. This proceeded fine through the compile stages, but fails when linking, complaining that rpl_malloc is not defined. It turns out [4] that ./configure tests for corner-cases of malloc() behaving as per the standard, and on failure it replaces malloc() calls with rpl_malloc(), intended as a wrapper with workaround code. However in this case Android's malloc() isn't broken, it's just that configure can't run the test right due to the cross-compile. We could of course just write a dummy rpl_malloc, but the solution we chose is to override the test result by exporting the shell variable ac_cv_func_malloc_0_nonnull=yes before configure. The same issue arose with realloc() and was similarly solved.
The next unresolved reference was getpwent. This is a Unix function that is used to fetch user information. Although Android is Unix-like internally in many respects, it doesn't use the standard Unix notion of users (or more specifically it abuses it so each "user" is an app), it doesn't define the function. Since NgSpice only uses this call once for command completion, it was just removed. If more deeply embedded, it would also have been possible to define a dummy version, like [5].
The final issue encountered was with ngmakeidx. This is a helper program that the NgSpice make uses to build a help index (ngspice.idx). However here it gets cross compiled and then running it fails. The solution chosen[6] was to do an amd64 build of NgSpice and then copy the ngspice.idx file over from that. Doing this finally resulted in a compete build, and consequentially a ngspice ARM binary.
The next difficulty was testing this. Although in practice the binary will be included inside the app, for now we wanted to just run it as in a Unix shell. This involves finding a suitable directory on the device. Usually several locations are possible, such as /data, /sdcard and /tmp. On the Moto E intended as the test phone for this class, /data is locked down, /sdcard is a FAT partition (and thus we can't enable the execute permission on ngspice) and /tmp doesn't exist. Thus an Android virtual device was used. In the avd system image, /data is easily writable. It should be noted that this is a temporary issue, once enclosed in an app these problems will vanish.
Attempting to run ngspice yielded one more issue, specifically:
error: only position independent executables (PIE) are supported.
It turns out that from Lollipop onwards all C code must be position-independent to facilitate address-space layout randomisation. Although various sources list different sets of compiler options were listed for this, it was found that simply -pie sufficed, applying it to the whole build by doing export CFLAGS=-pie before configure.
Following this the ngspice executable ran correctly, and was tested with a simple circuit to determine the propagation delay of an inverter. This succeeded (copied from an ADB shell terminal):
Initial Transient Solution
--------------------------
Node Voltage
---- -------
vdd 1.8
_in 1.8
in 1.12013e-06
out 1.8
_out 1.12016e-06
vin#branch 0
vdd#branch -1.53158e-09
Reference value : 9.99850e-09
No. of Data Rows : 10065
Measurements for Transient Analysis
tp_hl = 2.893862e-11 targ= 1.056175e-09 trig= 1.027237e-09
tp_lh = 2.732908e-11 targ= 2.044054e-09 trig= 2.016725e-09
Performance was quite slow due to the processor emulation. The same circuit was also simulated on NgSpice on Amd64 Linux and the result agreed precisely (not that this was expected to be an issue).
The next back-end objective will be to call this executable from an simple Android GUI with textboxes (while the real front-end development continues in parallel).
P.S.
* In writing this blog an alternative solution to the rpl_malloc issue was found [8], this will probably be used in future builds.
* In the actual device tests it was found that the index file isn't used (wasn't uploaded to the device). Since the main binary is build before this, one can just accept the build terminating at this point and ignore the issue.
* The use of PIE, necessary for Android 5.0, is only supported from Android 4.1 onwards. This will likely set the minimum API level for this project, though it would also be possible to ship two binaries.
P.P.S.
It was later discovered that ngspice makes a tmpfile() call that is privileged on some Android phones, depending on their file structure. See later blog posts for details on this.
[1] http://ngspice.sourceforge.net/
[2] http://developer.android.com/reference/java/lang/Runtime.html
[3] http://developer.android.com/ndk/guides/standalone_toolchain.html
[4] https://groups.google.com/forum/#!topic/ikarus-users/_R0QHqwyYz8
[5] https://kernel.googlesource.com/pub/scm/fs/ext2/xfstests-bld/+/refs/heads/master/android-compat/getpwent.c
[6] https://sourceforge.net/p/ngspice/discussion/127605/thread/b02c86d0/
[7] https://source.android.com/security/enhancements/enhancements50.html
[8] http://rickfoosusa.blogspot.com/2011/11/howto-fix-undefined-reference-to.html