[CCC DEV] Foreign Functions

Matt Jadud matt at concurrency.cc
Mon May 10 15:09:37 BST 2010


Dennis asked this question off-list:

[q]
At the moment I'm trying to strip down the Arduino's C++ Servo  class
and then translate it into  occam-pi to see how that might work or it
may just stay as C.  The scheme that is used is dependent on interupts
and I can't find out anything about the  way interrupts are handled in
occam-pi. Any pointers would be much appreciated.
[/q]

The answer should be written up and added to the website. I'm not able
to do that now, but the list is archived, so that will do.

If we look at the Arduino wrapper directory:

http://projects.cs.kent.ac.uk/projects/kroc/trac/browser/kroc/trunk/tvm/arduino

This code did not spring into existence: it evolved.

1. We started by combining the complete Arduino library with the
Transterpreter (the bytecode interpreter, "virtual machine," or (from
here on) the "TVM"). This gave us a large binary, but it let us see if
we could upload a C program that included "everything."

2. We wrote a simple FFI that called out to the print function in the
Arduino library, and said "Hello."

3. We started migrating setup to C (where necessary) or occam-pi
(wherever possible), so that we could initialize the hardware without
using the Arduino code.

4. We got rid of the Arduino library, implemented interrupts directly,
and completed our migration of C code to occam-pi.

That's a rough sketch of how things happened. In other words, we took
our time, leveraged existing code, and made sure things worked at each
step of the way. For what you're trying to do, you might take a
similar approach.

In the wrapper, you'll see a number of C files. We've tried to keep
the amount of C code used in this wrapper to a minimum. However, for
servo pulses, you seem to have a performance issue. (Adam, Christian:
any thoughts on how we can better handle this from occam? We're going
to have blocking issues, I would guess, if servos are driven from C. I
suspect we need better PWM control...) So, you could do this all from
C.

1. Start by looking at the source for how servos are driven in the
Arduino codebase.

http://www.google.com/codesearch/p?hl=en#thaXon_DSGU/trunk/libraries/Servo/Servo.cpp&q=Servo%20package:http://arduino%5C.googlecode%5C.com&sa=N&cd=2&ct=rc

You've no doubt already been here. The Arduino implementation does a
fair bit of interrupt juggling. It would appear (and I just skimmed
the code for a minute) that the servo is turned on, a timer is set,
and when the timer fires, the servo is turned off. I would make my
first step to clearly map out what the existing code does, and make
sure that you have that completely clear. The transition from one
environment to another will need a clean design to start. Theirs is OO
in C++; we, ultimately, have to live in a more constrained linguistic
environment.

2. If you want to dig into interrupt handlers, we've implemented them
in interrupts.c:

http://projects.cs.kent.ac.uk/projects/kroc/trac/browser/kroc/trunk/tvm/arduino/interrupts.c

2a. Start by adding an enumeration for your new interrupt.

2b. If your interrupt can be handled entirely in occam, you can use
the "MAP_SIMPLE_INTERRUPT" macro. This essentially handles the
scheduling issues, and throws you back into occam-pi. If it needs work
in C-land, then you can use the RX/TX ISRs as examples. Note how they
call "handle_interrupt" when they are done? That is because the
interrupts must play nicely with the TVM's scheduler. If you're *ONLY*
writing an FFI, this isn't an issue. However, if you want to deal with
interrupts, you must deal with the scheduler.

2c. You'll want to define a constant for your interrupt from occam;
see avr.module for where we put these:

http://projects.cs.kent.ac.uk/projects/kroc/trac/browser/kroc/trunk/tvm/arduino/occam/avr.module

2d. You'll then want to be able to wait on that interrupt from occam.
This will probably involve enabling it (see hardware manual), and then
you can use the "wait.for.interrupt" FFI call. See "button.occ" for an
example.

http://projects.cs.kent.ac.uk/projects/kroc/trac/browser/kroc/trunk/tvm/arduino/occam/button.occ

This code sets up an external interrupt and waits on it. We haven't
decided yet how we want to handle all 40+ of the possible interrupts
that can be used on the Arduino, so we didn't add this to the library
during our last hack-sprint. However, it serves as an example of a
piece of code that:

  1. enables an interrupt from occam-pi.
  2. Waits on it from occam-pi.
  3. Works.

I think that's most of it. Working with interrupts necessarily throws
you into the scheduler, and you must be cognizant of both the C-side
and the occam-pi-side. You can't get away from it. You'll always need
to: 1. add an ISR in C, 2. add the enumerations to C and occam, 3. do
the hardware init work (somewhere) to enable the interrupt (preferably
from occam), 4. wait on the interrupt wherever you need to, and 5.
clean up after the interrupt (also preferably from occam).

JUST FFI
---

If you just want to write an FFI, look at "ffi.c"

http://projects.cs.kent.ac.uk/projects/kroc/trac/browser/kroc/trunk/tvm/arduino/ffi.c

1. Add your FFI to the table.
2. Add the compiler directive (for the occam compiler) to your occam
code. There is an example on line 33 of avr.module:

http://projects.cs.kent.ac.uk/projects/kroc/trac/browser/kroc/trunk/tvm/arduino/occam/avr.module

You can add the directive anywhere. (That is, you can add the
directive to your own file, as long as it comes before the definition
of the PROC).

Note the format:

34 #PRAGMA EXTERNAL "PROC C.tvmspecial.0.wait.for.interrupt (VAL INT
interrupt, INT time) = 0"
35	INLINE PROC wait.for.interrupt (VAL INT interrupt, INT time)
36	  C.tvmspecial.0.wait.for.interrupt (interrupt, time)
37	:

You need to make sure the number (in this case, "0") matches the index
of your function in the FFI table. We do have a tool to automate this
if you're writing a lot of FFI calls, but if you're writing a few,
it's not a big deal to number them "1", "2", and so on.

The C function itself should look like ffi_wait_for_interrupt in
"interrupts.c" (line 149):


149	int ffi_wait_for_interrupt (ECTX ectx, WORD args[]) {
150	        WORD interrupt = args[0];
151	        WORDPTR time_ptr = (WORDPTR) args[1];
152	
153	        if (interrupt < 0 || interrupt >= NUM_INTERRUPTS) {
154	                return ectx->set_error_flag (ectx, EFLAG_FFI);
155	        }
156	
157	        return wait_interrupt (&interrupts[interrupt], ectx, time_ptr);
158	}

All FFIs always get an execution context pointer and the arguments in
an array. The mapping for the arguments can be found in  David Wood's
paper:

http://www.cs.kent.ac.uk/projects/ofa/kroc/wood-native-call.pdf

We should move that into friendlier documentation.

Notice how we pull out the interrupt number (passed as a VAL INT) and
the time value (passed as an INT, so it comes in as a pointer).

The last thing ffi_wait_for_interrupt does is wait, leaving the time
in the time_ptr variable, which gets set and can then later be seen in
occam-pi.

CONCLUSION
---
That all must have sooo many errors in it, it isn't even funny. I need
to grade a whole lot of things before Wednesday, and have a proposal
to get out the door by the end of the day. However, that provides a
bit of a roadmap for interrupt handling and FFI -- more than there was
when I started the message, anyway. As I said at the start, this needs
to be documented nicely and added to the website/dev docs.

Cheers,
Matt




More information about the developers mailing list