Compilation takes slightly longer, but editing and locating specific routines goes
much quicker :-)
I am now looking at throwing exceptions. It seems there are 4 standard exceptions:
nulllpointer, outofmemory, illegalargument and indexoutofbounds.
These math programs do not require native functions and are therefore most suitable
for testing the jvm, as they also use the String and StringBuffer classes.
When these math programs run properly we can be sure the jvm is operating correctly.
I rather postpone working on the native functions until the jvm runs properly, although
the native functions can be tested independantly using spin test programs I suppose.
Update.
Cause of s1.indexOf(s2) not working in my previous post, was that all values
are truncated to 16 bits, which means the comparisons did not function
properly. So I fixed the jvmCompareJump and jvmMath to sign extend 16bit values
before using the values. (Rather than sign extending the pop return values which
will make other functions disbehave because they rely on unsigned values).
Major update.
My math program f3 (16bit floating point) didn't work because I had swapped the arguments
in jvmLogic. That is fixed.
Also made readRegister and writeRegister native functions working, which means the
EEPROM and Memory class can be used (EEPROM read/write needs to be checked).
Also updated the instanceof bytecode to search for superclasses.
Now my f3 class yields the same results on the propeller as javelin,
so I am pretty convinced the jvm is running properly at this moment.
I'm getting a compile error in jvmNative.spin
> address %= jd.getMemSize 'to compensate for size 0x8000 in true javelin
changed to this and compiles
> address &= jd.getMemSize-1 'to compensate for size 0x8000 in true javelin
similar issue in two places.
Can you change jvmEngine.spin PRI doByte ... to PUB doByte ... ?
Thanks.
I have tested all my math classes and they all work. :-)
Downloading a java program to propeller goes faster than to javelin, but output to message window
is slower on the propeller than on a true javelin. So porting the jvm to assembler is wanted.
The only thing still missing regarding the java language,·is the try/catch/finally
construct. And of course the implementation of remaining native functions
and Virtual Peripheral code (ADC,DAC,PWM,Timer,UartRX,UartTX).
Yes, I've also done more testing and found the following work: A recursive
Fibonacci sequencer up to 20 items, a sieve of Erathosenes primes less than
500, and an inheritance test. This is pretty simple stuff, but don't have time
to get too involved for much more today.
How to do asm functions with the current code structure is beyond my propeller
knowlege cache. I suppose one could put the alternatives in jvmEngine, but that
would put us back a bit [noparse]:)[/noparse] I am very interested in your approach to it.
Let me know which outstanding element you're addressing so we don't duplicate.
I will work on the exception handling.
That leaves the native functions. These need to be tested
and optimized (eeRead and eeWrite for example could use the jvmEeprom object).
Since most native functions rely on a 8.68 usec tick, that should be the first
VP implemented. Having that timer base we can compare results with a
true javelin. VP parameters are passed via registers (using readRegister and writeRegister
and these already work). I think the VP code must be in asm.
Application note AN001 seems to provide the code we need. http://www.parallax.com/tabid/442/Default.aspx
I think the propeller is fast enough to combine ADC/DAC/PWM/Timer in one COG.
On the javelin all vp code for 6 VP's is executed every 8.68 usec.
Nice to see how the code is growing in this short time. I'm very interested in this project because I'm a java programmer.
I had downloaded the code the first time this week and was looking about the content of files. It looks very good for me.
I had also downloaded the javelin ide, but I doesn't the jvm on the prop while I had trouble with the ide.
Peter has mentioned that it could be time to port to assembly. I thought it would be helpfully if someone would do that. So
I will present you the work of the first night and some hours of today.
It was not to difficult to implement the byte code handler as they are mostly small. I have it not tested yet because it is
also not complete. But it should demonstrate how I think it could be realized. At the end it should be allocate 3 cogs
perhaps only 2 cogs with assembly.
Currently the mainloop is still in Spin but this should be later also in assembly.
I have only 3 files changed and currently no methods removed in Spin code. You can copy the attached files to the others
and open the jvmMainAsm at first.
Great work Thomas.
I can't say anything about the asm code as I have no·knowledge of all
the PASM conditional statements. But it looks good.
Is it possible to have more than 64 entries in the JemCodeJmpTable ?
(spin case only allows 64)
Hi Thomas,
Welcome to this effort.
Do you have or know of any benchmark comparisons between spin and asm for the same algorithms?
Javelin beats Propeller running spin JVM by roughly 15x today with a 100 prime Sieve of Erathostenes.
Thanks.
I am sure a lot of people have NOT followed this extremely comprehensive and for most parts quite technical thread. Although I have the suspicion that the interest in Java is as high as in FORTH (whatever I am saying with this) I think it is one of the most interesting developments here before (and maybe even after?) the advent of the C-Compiler.
So my request is: Can you prepare a description how to use the complete system for the simple Java User, in the near future?
My main point of interest is this: I have to accept the fact that the required use of SPIN is too high a threshold for many users to be, independent of its peformance, merrits or the opposite of it. The C compiler will bring up different issues...
But something like a free (!) Java implemention, based on something which already has a generic maturity will find acceptance. At least German universities use Java throughout in their CS education.
Also I see no reason why a piece of Java should run considerably slower than SPIN.
@Peter,
Yes, I have a Javelin. Had to dust it off. I don't normally pull data out of the air [noparse]:)[/noparse]
Here's the source I used for comparisons (not mine). Times were relative, not absolute.
@deSilva
Today spin is interpreting a subset of java bytecode generated from mostly
compliant java source (i am quite annoyed that i must remove args from main).
As far as a user guide goes, I think the Javelin IDE / manual are good starting points.
//
// Erathostenes.java
// class Erathostenes {
public static void main() {
final int end = 542;
int count = 1;
System.out.println("Sieve of erathostenes");
// primes initieras till false
boolean[noparse][[/noparse]] primes = new boolean[noparse][[/noparse]end];
for(int i = 0; i < end; i++) {
primes[noparse][[/noparse]i] = true;
}
System.out.println("Array initialized, end = " + end);
for(int i = 2; i < end; i++) {
if(primes[noparse][[/noparse]i]) {
System.out.println(count + ". prime: " + i);
count++;
for(int j = 2*i; j < end; j += i) {
primes[noparse][[/noparse]j] = false;
}
}
}
System.out.println(" ... OK");
}
}
Update.
Attached are updated jvmObject and jvmData.
The new bytecode used to check if size of the new object > 0.
However that need not be the case for a class without variables,
as in the following code.
import stamp.core.*;
public class ReadRegisterTest { · · static public class JVM { ··· public JVM() { ··· } ··· public void test() { ····· System.out.println("T"); ··· } · }
Note that (local) class JVM has no·variables, only a constructor and a method.
The size of the object created on the heap for j, is 0 bytes (meaning only the class reference
and the size are stored).
@DeSilva,
One only needs to download the jvmMain to the propeller (select right clock settings) using PropTool,
then startup the javelin ide and program java into the prop from the ide.
Jazzed is right about the javelin manual being the best·source, as it emphasizes the
difference between pc java and javelin java.
@Jezzed:
This could be a miusunderstanding. I shall not do anything in the direction of finding clues or doing real work for getting JAVA running I will use it if it does!
I just wanted to point out that it will be useful, if you bring it to a state of good documention and easy use! And I know peole you will definitely like it!
What I can do is provide you with some benchmark value for Eratosthenes for SPIN and PASM, if you want it...
For this you should put the printing ( System.out.println(count + ". prime: " + i); ) into a later, untimed loop.
Also the outer loop should stop at SQRT(end); but I also can remove my optimizations...
However Eratosthenes is not a good benchmark candidate as local manipulations (size of vector items, ByteFill,...) will influence its performance tremendiously...
deSilva said...
@Jezzed:
This could be a miusunderstanding. I shall not do anything in the direction of finding clues or doing real work for getting JAVA running I will use it if it does!
...
However, if you provide an apples to apples comparison between performance of spin -vs- asm·for some reasonable benchmark, however you please to write it, that would be outstanding.·Others will certainly appreciate your effort.
A very simple user guide for getting started with JavaProp or whatever in the Javelin environment would be useful. Peter has outlined pieces here for a tinkerer to get started, but a "product" requires a good package.
As far as optimizations go, one should optimize for the given goal. Here the goal was not to do a Sieve performance measurment, but to use a somewhat non-trivial code sample for a CPU-JVM side by side comparison. Having the ...println... is fine the way it is especially since part of the performance measurement is checking how well the JVM can deal with virtual calls. May not be the best example, but it fit the need. As needs change, so will implementation. One would expect at some point to need a performance measure based on an industry standard, but we're not there yet.
Jazzed: I understand all your points.. But I have seen two very promising projects fail now (FORTH as one) where people asked for...
The people I am thinking of will:
(a) .. be new to the Propeller
(b) .. know nothing of Parallaxes great other products
(c) .. want to learn things to do with the Microcontroller
(d) .. have a more or less good understanding of (some even recent programming practice with ) JAVA
(e) .. expect something "out of the box"
The benchmark topic came to mind, as we need SOME reference wrt SPIN (faster will be good - and not so difficult!) And using two COGS will be no problem either...
said...
@DeSilva,
One only needs to download the jvmMain to the propeller (select right clock settings) using PropTool,
then startup the javelin ide and program java into the prop from the ide.
Jazzed is right about the javelin manual being the best source, as it emphasizes the
difference between pc java and javelin java.
Tell me when it's safe to work according to that directives I shall do!!
But not today .. my schedule is more around March 15...
I don't know how fast it will run exactly if the engine including the main loop is located in assembly.
The first step I will do is only to implement the engine in assembly. I expect for this a execution time
of 40 µs per Java bytecode. This is related to the execution of mainloop which is in Spin. But this should
be at minimum 5 times faster than running all in Spin.
If we could move the mainloop also in assembly this should boost the speed again by factor 5 to 10.
@Thomas,
Well, at least there is some progress that can be made in performance.
I like your coding style. I've had to do assembly mainly for boot code,
ISR, and gdb (yuck) to support C mostly with gnu asm, but don't care
for it mostly. You're style offers a refreshing perspective in the dark art.
@Peter,
I've been looking at the Virtual Peripheral stuff, and came up with this
list of requirements. Please review and comment when you have time.
/**
* Implementing Virtual Peripherals (VP) requires a process to run in the background.
* This is a good opportunity for Propeller, although it takes another COG out of
* the user's hands. (We don't have a strategy for the user to control a cog anyway.)
*
* The elements that need a background process are: ADC, DAC, PWM, Timer, and UART.
* Being able to do all of these in one COG would be nice, but we are limited to
* counters per COG. If more VP are needed, more COGs will be used.
*
* Common VP design requirements:
*
* - Up to 6 VP should run at once.
* - All VP elements should run every 8.68us.
* - VP pin usage is mutually exclusive.
*
* - Can we have have 6 PWM or 6 ADC or 6 DAC or 6 Uart run at a time ?
* The java code clearly uses only 1 timer at any given moment, but
* there are 4 timer variables ... this is very confusing.
*
* - It is unclear how any VP would be stopped ... this means a fixed configuration.
*
* Design requirements for ADC:
*
* - ADC will be initialized by native call.
* - ADC should be readable by user ... I see no provision for this in jvmNative.
* - ADC pins should be selectable one for input one for reference output.
* - External RC network is required on the input pin for ADC to function.
* - Input measurements are limited from 0 to 3.3VDC.
* - Up to 2 ADC will be usable at once (unless more cogs are added).
*
* Design requirements for DAC:
*
* - DAC will be initialized by native call.
* - DAC should be writeable by user ... I see no provision for this in jvmNative.
* - DAC output pins should be selectable.
* - External RC network is required on the output pin for DDC to function.
* - Output measurements are limited from 0 to 3.3VDC.
* - Up to 2 DAC will be usable at once (unless more cogs are added).
*
* Design requirements for PWM:
*
* - PWM pins should be selectable (done in native code for now).
* - PWM will be initialized by native call.
* - Each PWM pin's pulse cycle characteristics can be updated any time.
*
* - Up to 2 PWM's should run at once now (allow for 4) ? To use the built-in
* counters for PWM, one needs 2 COG to get 4 PWM, or 3 COG for 6 PWM.
*
* Design requirements for Timer:
*
* - Timer should tick every 8.68us.
* - Timer should start when initialzied by native call.
* - Timer value should latch on demand from native call.
* - One timer will run at a time.
*
* - It is not clear how a timer would be stopped; need input.
*
* Design requirements for Uart:
*
* - Uart will operate in one direction per instance.
* - Uart will buffer input or output at 256 bytes each.
* - Uart receiver using HW flow control will stall sender when 16 RX buffer bytes are free.
* - Uart baud rate will be selectable from 600 to 57600 BPS.
* - Uart stop bits will be selectable from 1 to 8.
* - Uart level sense can be inverted.
*
* - Up to 6 Uart will be allowed ? Looks like 2 for now.
*
*/
Reading and writing parameters to Virtual Peripherals is done using the
readRegister and writeRegister native calls. Starting and stopping VP's
is done by native calls. In addition there are a CPU.installVP() and CPU.removeVP()
java methods. These maintain a list of installed VP's in java variables.
Before I explain the several VP's, where does this 8.68 usec come from?
The javelin uses a sx48 chip running on 25MHz with an interrupt period of 217.
That means the interrupt starts EXACTLY every 217 clockcycles.
217/25MHz = 8.68 usec
The number of clockcycles in the interrupt routine·must not exceed 214 (due to 3 cycles overhead).
The longest piece·of code is for the receive uart, something like 32 clockcycles.
With 6 running receive uarts that is 6x32 = 192.
VP's are installed in banks. Each bank is 16 bytes and the banks occupy addresses 0x80-0xDF.
In the jvmData these are simulated by·vpRambank byte 0[noparse][[/noparse]MAX_NUM_VPS*16].
The 6 banks are 0x80,0x90,0xA0,0xB0,0xC0,0xD0. The register addresses are offsets 0-15 ORed
with the allocated bank.
See CPU.installVP() and CPU.removeVP() for how the list of VP's is maintained.
Each VP has a java variable vpBank which either holds an allocated bank or -1 (NOT_INSTALLED).
/**
* Implementing Virtual Peripherals (VP) requires a process to run in the background.
* This is a good opportunity for Propeller, although it takes another COG out of
* the user's hands. ·· ***** (We don't have a strategy for the user to control a cog anyway.) ·· We can add this when extending the jvm with propeller specific enhancements.
*
* The elements that need a background process are: ADC, DAC, PWM, Timer, and UART.
* Being able to do all of these in one COG would be nice, but we are limited to
* counters per COG. If more VP are needed, more COGs will be used. · ***** We do not rely on the counters, except for generating the 8.68 usec tick.
*
* Common VP design requirements:
*
* - Up to 6 VP should run at once.
* - All VP elements should run every 8.68us.
* - VP pin usage is mutually exclusive. ·· ***** The java code does not impose the mutually exclusive usage of pins. ·· Obviously, when using the same pin for uart receive and pulseOut both functions ·· won't work. So it is up to the programmer to select the proper pins for the application.
*
* - Can we have have 6 PWM or 6 ADC or 6 DAC or 6 Uart run at a time ? ·· ***** Yes, any VP type can be used/mixed for 6 total.
* The java code clearly uses only 1 timer at any given moment, but
* there are 4 timer variables ... this is very confusing. ·· ***** There is only 1 free running 32bit timer and it is only started when the ·· static globalTimerVP is null. Once started that is set to the first Timer object value. ·· (see Timer class). Each Timer instance has 2 16bits variables, startHi and startLo ·· that make up a 32bits start value. The start value is the latched free running timer ·· value when calling t.mark(). The several timeout methods use this startvalue ·· together with a latched free running timer value to find out if a timer has expired. ·· So there are an unlimited number of timers (while memory serves) using only 1 VP bank.
*
* - It is unclear how any VP would be stopped ... this means a fixed configuration. ·· ***** VP's are started and stopped by native calls. Each VP type has these native calls ·· defined in their class. They are called from the VP's start() and stop() methods.
*
* Design requirements for ADC:
*
* - ADC will be initialized by native call. ·· ***** ADC.start() and ADC.stop() start and stop an ADC. start() calls install(bank). ·· stop() calls CPU.removeVP()
* - ADC should be readable by user ... I see no provision for this in jvmNative. ·· ***** ADC.value() returns the current value. It calls the readRegister native function.
* - ADC pins should be selectable one for input one for reference output. ·· ***** These are passed to the ADC constructor, eg. when using ADC myADC = new ADC(inpin,outpin)
* - External RC network is required on the input pin for ADC to function.
* - Input measurements are limited from 0 to 3.3VDC.
* - Up to 2 ADC will be usable at once (unless more cogs are added). ·· ***** If we mimic the javelin approach we can run 6 ADC if necessary. The ADC VP code ·· accumulates the number of times the outpin is set high during 256 interrupts. All the ADC ·· VP code needs to do is to set the outpin to the inverted value of the inpin and incrementing ·· a register when the outpin is set high.
*
* Design requirements for DAC:
*
* - DAC will be initialized by native call. ·· **** DAC.start() and DAC.stop()· start() calls install(bank). Note that this is a different install ·· then the one used for ADC. The linker distinguishes both based on class name.
* - DAC should be writeable by user ... I see no provision for this in jvmNative. ·· ***** Done using method DAC.update(value) update() calls writeRegister native function.
* - DAC output pins should be selectable. ·· ***** Pin to use is passed via constructor.
* - External RC network is required on the output pin for DDC to function.
* - Output measurements are limited from 0 to 3.3VDC.
* - Up to 2 DAC will be usable at once (unless more cogs are added). ·· ***** Same comment as under ADC. The pin is set high for the number ·· of interrupts specified in value passed to update(). The remaining time ·· to complete 256 interrupts the pin will be low. Basically, DAC is just a simple PWM.
*
* Design requirements for PWM:
*
* - PWM pins should be selectable (done in native code for now).
* - PWM will be initialized by native call.
* - Each PWM pin's pulse cycle characteristics can be updated any time.
*
* - Up to 2 PWM's should run at once now (allow for 4) ? To use the built-in
* counters for PWM, one needs 2 COG to get 4 PWM, or 3 COG for 6 PWM.
*·· ***** Again, same comment as under ADC (if using software PWM)
*
* Design requirements for Timer:
*
* - Timer should tick every 8.68us.
* - Timer should start when initialzied by native call. ·· ***** The global 32bits timer is started when using Timer.start(), stopped when using Timer.stop()
* - Timer value should latch on demand from native call. ·· **** Native function latch does that. It sets the latchregisters which are readout ··· using the native readRegister calls.
* - One timer will run at a time. ·· ***** There is only 1 timer. See comments on timer above.
*
* - It is not clear how a timer would be stopped; need input. ·· ***** Timer.stop() calls CPU.removeVP() which deletes the Timer VP.
*
* Design requirements for Uart:
*
* - Uart will operate in one direction per instance. ·· ***** Method setDirection can change the direction at runtime for the same instance.
* - Uart will buffer input or output at 256 bytes each. ·· ***** Data is read/written directly from the buffer in java. Which address in the ·· buffer to access is determined by head and tail pointers. These are read/written via ··readRegister and writeRegister native calls.
* - Uart receiver using HW flow control will stall sender when 16 RX buffer bytes are free. ·· ***** and will allow transmission again when there are 16 bytes left in the buffer.
* - Uart baud rate will be selectable from 600 to 57600 BPS.
* - Uart stop bits will be selectable from 1 to 8.
* - Uart level sense can be inverted. ·· ***** for both datapin and flowcontrol pin
*
* - Up to 6 Uart will be allowed ? Looks like 2 for now. ·· ***** If we can do the VP code in as little code as on the SX (30 instructions) ·· we should be able to manage 6.
*
*/
I have named all the registers used for the VP routines in jvmData.
Major update.
I implemented the try/catch/finally construct.
For this I moved jb_athrow together with a functions throw and throwException
to the file jvmException.
The function throw allows to throw a runtime error using the defined exceptions · kIllegalArgumentException· = 1 · kNullPointerException····· = 2 · kIndexOutOfBoundsException = 3 · kOutOfMemoryError········· = 4
I have implemented these throws in new,newarray,anewarray and other
functions where a parameter must not be 0. So these functions now throw
an exception when some parameter is invalid. This does not apply to math functions
as there is no·standard math exception. It is up to the programmer to
use try/catch when dividing and the denominator may be 0.
I also implemented the bytecodes jsr and ret as these are used when using finally.
There are two issues yet to solve:
1. The line ·'if offset => jd.readJavaIntBE(exceptionTable + 1 + index) AND offset < jd.readJavaIntBE(exceptionTable + 3 + index)
in function throwException in jvmException.spin is commented out because else the catch never works.
I based that function on the tinyVM function throw_exception(Object *exception)
located in the timyVM source file .\vmsrc\exceptions.c (attached)
2. When using finally and an exception is not catched, the finally clause is repeated over
and over again.
I attached my test java file.
If anyone could take a look at the throwException function to see where I obviously must be
do something wrong (possibly removing one frame too many ??) that would help me, because
I tried to get the finally working for over half a day now.
BTW, finally works when an exception is catched.
On the throw issue, one thing comes to mind that the exception address for Object is -1 $ffff.
It seems that throwException vars e and exceptionTable should be checking for > 0 rather than <> 0.
I can't spend too much time on this though. I've been in such a "trench" many times, and I find
some things help: 1) have a conversation with someone about the problem fully describing the
approach and expectations of process/data on which you depend ... this forces you to explain
in sufficient detail so that you may realize something overlooked and it allows a devil's advocate
to challenge your assumptions. 2) take a long break and get a good night's rest ... answers can
come there ... try to wake up and write it down! 3) take a shower and/or do something mundane
that requires no thought for 15 minutes. This puts you in "alpha" state where answers can also come.
On the VP stuff ... Yes, it had occured to me that the only way to make all this work in a CPU
with no parallel processor, etc... would be to use a "task" fired from an interrupt periodically
similar to what an o/s does with scheduled timer procs. Thanks for the confirmation [noparse]:)[/noparse]
On the UART, do we require 6 256 byte buffers or just 1 (managed by the java code) ?
I missed the part about inverting the control line too. Thanks.
We need to establish an IM connection. Please reply to my private message.
I am in the process of coming up with some basic guides to help people get "up and running", however, the JVM is still far enough from finished that there is little point in writing too much. In addition, I plan to get a prop stamp and make sure I can make that work, as I don't want to write something that is "incorrect" because I haven't actually tested my own instructions.
I'll hopefully get the prop stamp on monday or tuesday and then have some documentation in a day or two.
A spin stamp is not required, you can use a propeller protoboard.
Communication between the Javelin IDE and propeller can be
done using the TX/RX pins used to program the propeller.
(same serial port). It just means you cannot use the proptool
to program new firmware (the jvm program) when the IDE connection is in use.
@Jazzed,
thank you for your appreciation about my code style. I will do my best that everyone can understand my dark thoughts of coding.
@Jazzed, Peter
It seems that we would need 3 cogs to support 6 VP's at all. Each cog of the Propeller provides 2 timers which can be used for ADC, DAC, PWM or only as timer.
I would suggest that we use also the cogs which are running the bytecode decoder. Otherwise we would have 2 cogs for bytecode decoding and up to 3 separate cogs for VP's if necessary.
We could locate the smallest subroutines to the decoding cogs and the more comprehensive code to a separate cog. So the cogs would provide a static configuration about VP's. E.g. decoding cog1 supports 2 timer, decoding cog2 supports 2 PWM and another cog could be started to support 2 ADC, DAC or UART or a mix of 2 of this.
I'm hoping we can have a VP system equivalent to that on the Javelin SX48B i.e all timer process
functions (ADC, DAC, PWM, Timer, UART) can be performed in one "tick" of a COG.
Speculating that SX48B may be faster than one COG because of it's architecture ... I'm not sure.
I am attracted to the notion of having the counters perform the PWM tasks (timer can be done either
way it seems). It seems that having one COG doing it all for now is fine.
Perhaps copying this "VP set" to other COGS is a way to enrich the Javelin concept ? By providing
"N" times the number of Virtual Periperals, certain things become more possible like controlling
a 17 servo robot for example (current draw there would be a nightmare to coordinate though).
Comments
much quicker :-)
I am now looking at throwing exceptions. It seems there are 4 standard exceptions:
nulllpointer, outofmemory, illegalargument and indexoutofbounds.
For test suites you can use many of my javelin math test programs, for example
the UnsignedIntMath_test.java program (I already tested this and it fails
so there still are issues we need to fix).
http://tech.groups.yahoo.com/group/JavelinCode/files/Javelin%20Stamp%20IDE/lib/stamp/math/
Also usable are the Int32 package·and FloatLite6 package.
http://www.parallax.com/tabid/430/Default.aspx
These math programs do not require native functions and are therefore most suitable
for testing the jvm, as they also use the String and StringBuffer classes.
When these math programs run properly we can be sure the jvm is operating correctly.
I rather postpone working on the native functions until the jvm runs properly, although
the native functions can be tested independantly using spin test programs I suppose.
regards peter
Cause of s1.indexOf(s2) not working in my previous post, was that all values
are truncated to 16 bits, which means the comparisons did not function
properly. So I fixed the jvmCompareJump and jvmMath to sign extend 16bit values
before using the values. (Rather than sign extending the pop return values which
will make other functions disbehave because they rely on unsigned values).
regards peter
if. if-else, for, while,·do-while also work.
Attached is the updated jvmCompareJump file.
regards peter
My math program f3 (16bit floating point) didn't work because I had swapped the arguments
in jvmLogic. That is fixed.
Also made readRegister and writeRegister native functions working, which means the
EEPROM and Memory class can be used (EEPROM read/write needs to be checked).
Also updated the instanceof bytecode to search for superclasses.
Now my f3 class yields the same results on the propeller as javelin,
so I am pretty convinced the jvm is running properly at this moment.
regards peter
> address %= jd.getMemSize 'to compensate for size 0x8000 in true javelin
changed to this and compiles
> address &= jd.getMemSize-1 'to compensate for size 0x8000 in true javelin
similar issue in two places.
Can you change jvmEngine.spin PRI doByte ... to PUB doByte ... ?
Thanks.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
Attached are updated files, made doStep PUB.
regards peter
Using addr &= val-1 is probably faster, but only correct for val as a power of 2 [noparse]:)[/noparse]
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
Downloading a java program to propeller goes faster than to javelin, but output to message window
is slower on the propeller than on a true javelin. So porting the jvm to assembler is wanted.
The only thing still missing regarding the java language,·is the try/catch/finally
construct. And of course the implementation of remaining native functions
and Virtual Peripheral code (ADC,DAC,PWM,Timer,UartRX,UartTX).
regards·peter
Fibonacci sequencer up to 20 items, a sieve of Erathosenes primes less than
500, and an inheritance test. This is pretty simple stuff, but don't have time
to get too involved for much more today.
How to do asm functions with the current code structure is beyond my propeller
knowlege cache. I suppose one could put the alternatives in jvmEngine, but that
would put us back a bit [noparse]:)[/noparse] I am very interested in your approach to it.
Let me know which outstanding element you're addressing so we don't duplicate.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
That leaves the native functions. These need to be tested
and optimized (eeRead and eeWrite for example could use the jvmEeprom object).
Since most native functions rely on a 8.68 usec tick, that should be the first
VP implemented. Having that timer base we can compare results with a
true javelin. VP parameters are passed via registers (using readRegister and writeRegister
and these already work). I think the VP code must be in asm.
Application note AN001 seems to provide the code we need.
http://www.parallax.com/tabid/442/Default.aspx
I think the propeller is fast enough to combine ADC/DAC/PWM/Timer in one COG.
On the javelin all vp code for 6 VP's is executed every 8.68 usec.
regards peter
I had downloaded the code the first time this week and was looking about the content of files. It looks very good for me.
I had also downloaded the javelin ide, but I doesn't the jvm on the prop while I had trouble with the ide.
Peter has mentioned that it could be time to port to assembly. I thought it would be helpfully if someone would do that. So
I will present you the work of the first night and some hours of today.
It was not to difficult to implement the byte code handler as they are mostly small. I have it not tested yet because it is
also not complete. But it should demonstrate how I think it could be realized. At the end it should be allocate 3 cogs
perhaps only 2 cogs with assembly.
Currently the mainloop is still in Spin but this should be later also in assembly.
I have only 3 files changed and currently no methods removed in Spin code. You can copy the attached files to the others
and open the jvmMainAsm at first.
Any comments appreciated.
Thomas
I can't say anything about the asm code as I have no·knowledge of all
the PASM conditional statements. But it looks good.
Is it possible to have more than 64 entries in the JemCodeJmpTable ?
(spin case only allows 64)
regards peter
Thomas
Welcome to this effort.
Do you have or know of any benchmark comparisons between spin and asm for the same algorithms?
Javelin beats Propeller running spin JVM by roughly 15x today with a 100 prime Sieve of Erathostenes.
Thanks.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
Do you have a javelin? If not, how did you come up with that number?
regards peter
I am sure a lot of people have NOT followed this extremely comprehensive and for most parts quite technical thread. Although I have the suspicion that the interest in Java is as high as in FORTH (whatever I am saying with this) I think it is one of the most interesting developments here before (and maybe even after?) the advent of the C-Compiler.
So my request is: Can you prepare a description how to use the complete system for the simple Java User, in the near future?
My main point of interest is this: I have to accept the fact that the required use of SPIN is too high a threshold for many users to be, independent of its peformance, merrits or the opposite of it. The C compiler will bring up different issues...
But something like a free (!) Java implemention, based on something which already has a generic maturity will find acceptance. At least German universities use Java throughout in their CS education.
Also I see no reason why a piece of Java should run considerably slower than SPIN.
Yes, I have a Javelin. Had to dust it off. I don't normally pull data out of the air [noparse]:)[/noparse]
Here's the source I used for comparisons (not mine). Times were relative, not absolute.
@deSilva
Today spin is interpreting a subset of java bytecode generated from mostly
compliant java source (i am quite annoyed that i must remove args from main).
As far as a user guide goes, I think the Javelin IDE / manual are good starting points.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
Post Edited (jazzed) : 2/24/2008 2:23:32 AM GMT
Attached are updated jvmObject and jvmData.
The new bytecode used to check if size of the new object > 0.
However that need not be the case for a class without variables,
as in the following code.
import stamp.core.*;
public class ReadRegisterTest {
·
· static public class JVM {
··· public JVM() {
··· }
··· public void test() {
····· System.out.println("T");
··· }
· }
· static void test2() {
··· System.out.println("T2");
··· j.test();
· }
·
· static JVM j = new JVM();
·
· static void main() {
··· test2();
··· System.out.println("M");
··· while (true) {
··· }
· }
}
Note that (local) class JVM has no·variables, only a constructor and a method.
The size of the object created on the heap for j, is 0 bytes (meaning only the class reference
and the size are stored).
@DeSilva,
One only needs to download the jvmMain to the propeller (select right clock settings) using PropTool,
then startup the javelin ide and program java into the prop from the ide.
Jazzed is right about the javelin manual being the best·source, as it emphasizes the
difference between pc java and javelin java.
regards peter
This could be a miusunderstanding. I shall not do anything in the direction of finding clues or doing real work for getting JAVA running I will use it if it does!
I just wanted to point out that it will be useful, if you bring it to a state of good documention and easy use! And I know peole you will definitely like it!
What I can do is provide you with some benchmark value for Eratosthenes for SPIN and PASM, if you want it...
For this you should put the printing ( System.out.println(count + ". prime: " + i); ) into a later, untimed loop.
Also the outer loop should stop at SQRT(end); but I also can remove my optimizations...
However Eratosthenes is not a good benchmark candidate as local manipulations (size of vector items, ByteFill,...) will influence its performance tremendiously...
However, if you provide an apples to apples comparison between performance of spin -vs- asm·for some reasonable benchmark, however you please to write it, that would be outstanding.·Others will certainly appreciate your effort.
A very simple user guide for getting started with JavaProp or whatever in the Javelin environment would be useful. Peter has outlined pieces here for a tinkerer to get started, but a "product" requires a good package.
As far as optimizations go, one should optimize for the given goal. Here the goal was not to do a Sieve performance measurment, but to use a somewhat non-trivial code sample for a CPU-JVM side by side comparison. Having the ...println... is fine the way it is especially since part of the performance measurement is checking how well the JVM can deal with virtual calls. May not be the best example, but it fit the need. As needs change, so will implementation. One would expect at some point to need a performance measure based on an industry standard, but we're not there yet.
Cheers [noparse]:)[/noparse]
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
The people I am thinking of will:
(a) .. be new to the Propeller
(b) .. know nothing of Parallaxes great other products
(c) .. want to learn things to do with the Microcontroller
(d) .. have a more or less good understanding of (some even recent programming practice with ) JAVA
(e) .. expect something "out of the box"
The benchmark topic came to mind, as we need SOME reference wrt SPIN (faster will be good - and not so difficult!) And using two COGS will be no problem either...
Tell me when it's safe to work according to that directives I shall do!!
But not today .. my schedule is more around March 15...
Post Edited (deSilva) : 2/23/2008 6:19:29 PM GMT
I don't know how fast it will run exactly if the engine including the main loop is located in assembly.
The first step I will do is only to implement the engine in assembly. I expect for this a execution time
of 40 µs per Java bytecode. This is related to the execution of mainloop which is in Spin. But this should
be at minimum 5 times faster than running all in Spin.
If we could move the mainloop also in assembly this should boost the speed again by factor 5 to 10.
Thomas
Well, at least there is some progress that can be made in performance.
I like your coding style. I've had to do assembly mainly for boot code,
ISR, and gdb (yuck) to support C mostly with gnu asm, but don't care
for it mostly. You're style offers a refreshing perspective in the dark art.
@Peter,
I've been looking at the Virtual Peripheral stuff, and came up with this
list of requirements. Please review and comment when you have time.
/**
* Implementing Virtual Peripherals (VP) requires a process to run in the background.
* This is a good opportunity for Propeller, although it takes another COG out of
* the user's hands. (We don't have a strategy for the user to control a cog anyway.)
*
* The elements that need a background process are: ADC, DAC, PWM, Timer, and UART.
* Being able to do all of these in one COG would be nice, but we are limited to
* counters per COG. If more VP are needed, more COGs will be used.
*
* Common VP design requirements:
*
* - Up to 6 VP should run at once.
* - All VP elements should run every 8.68us.
* - VP pin usage is mutually exclusive.
*
* - Can we have have 6 PWM or 6 ADC or 6 DAC or 6 Uart run at a time ?
* The java code clearly uses only 1 timer at any given moment, but
* there are 4 timer variables ... this is very confusing.
*
* - It is unclear how any VP would be stopped ... this means a fixed configuration.
*
* Design requirements for ADC:
*
* - ADC will be initialized by native call.
* - ADC should be readable by user ... I see no provision for this in jvmNative.
* - ADC pins should be selectable one for input one for reference output.
* - External RC network is required on the input pin for ADC to function.
* - Input measurements are limited from 0 to 3.3VDC.
* - Up to 2 ADC will be usable at once (unless more cogs are added).
*
* Design requirements for DAC:
*
* - DAC will be initialized by native call.
* - DAC should be writeable by user ... I see no provision for this in jvmNative.
* - DAC output pins should be selectable.
* - External RC network is required on the output pin for DDC to function.
* - Output measurements are limited from 0 to 3.3VDC.
* - Up to 2 DAC will be usable at once (unless more cogs are added).
*
* Design requirements for PWM:
*
* - PWM pins should be selectable (done in native code for now).
* - PWM will be initialized by native call.
* - Each PWM pin's pulse cycle characteristics can be updated any time.
*
* - Up to 2 PWM's should run at once now (allow for 4) ? To use the built-in
* counters for PWM, one needs 2 COG to get 4 PWM, or 3 COG for 6 PWM.
*
* Design requirements for Timer:
*
* - Timer should tick every 8.68us.
* - Timer should start when initialzied by native call.
* - Timer value should latch on demand from native call.
* - One timer will run at a time.
*
* - It is not clear how a timer would be stopped; need input.
*
* Design requirements for Uart:
*
* - Uart will operate in one direction per instance.
* - Uart will buffer input or output at 256 bytes each.
* - Uart receiver using HW flow control will stall sender when 16 RX buffer bytes are free.
* - Uart baud rate will be selectable from 600 to 57600 BPS.
* - Uart stop bits will be selectable from 1 to 8.
* - Uart level sense can be inverted.
*
* - Up to 6 Uart will be allowed ? Looks like 2 for now.
*
*/
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
readRegister and writeRegister native calls. Starting and stopping VP's
is done by native calls. In addition there are a CPU.installVP() and CPU.removeVP()
java methods. These maintain a list of installed VP's in java variables.
Before I explain the several VP's, where does this 8.68 usec come from?
The javelin uses a sx48 chip running on 25MHz with an interrupt period of 217.
That means the interrupt starts EXACTLY every 217 clockcycles.
217/25MHz = 8.68 usec
The number of clockcycles in the interrupt routine·must not exceed 214 (due to 3 cycles overhead).
The longest piece·of code is for the receive uart, something like 32 clockcycles.
With 6 running receive uarts that is 6x32 = 192.
VP's are installed in banks. Each bank is 16 bytes and the banks occupy addresses 0x80-0xDF.
In the jvmData these are simulated by·vpRambank byte 0[noparse][[/noparse]MAX_NUM_VPS*16].
The 6 banks are 0x80,0x90,0xA0,0xB0,0xC0,0xD0. The register addresses are offsets 0-15 ORed
with the allocated bank.
See CPU.installVP() and CPU.removeVP() for how the list of VP's is maintained.
Each VP has a java variable vpBank which either holds an allocated bank or -1 (NOT_INSTALLED).
/**
* Implementing Virtual Peripherals (VP) requires a process to run in the background.
* This is a good opportunity for Propeller, although it takes another COG out of
* the user's hands.
·· ***** (We don't have a strategy for the user to control a cog anyway.)
·· We can add this when extending the jvm with propeller specific enhancements.
*
* The elements that need a background process are: ADC, DAC, PWM, Timer, and UART.
* Being able to do all of these in one COG would be nice, but we are limited to
* counters per COG. If more VP are needed, more COGs will be used.
· ***** We do not rely on the counters, except for generating the 8.68 usec tick.
*
* Common VP design requirements:
*
* - Up to 6 VP should run at once.
* - All VP elements should run every 8.68us.
* - VP pin usage is mutually exclusive.
·· ***** The java code does not impose the mutually exclusive usage of pins.
·· Obviously, when using the same pin for uart receive and pulseOut both functions
·· won't work. So it is up to the programmer to select the proper pins for the application.
*
* - Can we have have 6 PWM or 6 ADC or 6 DAC or 6 Uart run at a time ?
·· ***** Yes, any VP type can be used/mixed for 6 total.
* The java code clearly uses only 1 timer at any given moment, but
* there are 4 timer variables ... this is very confusing.
·· ***** There is only 1 free running 32bit timer and it is only started when the
·· static globalTimerVP is null. Once started that is set to the first Timer object value.
·· (see Timer class). Each Timer instance has 2 16bits variables, startHi and startLo
·· that make up a 32bits start value. The start value is the latched free running timer
·· value when calling t.mark(). The several timeout methods use this startvalue
·· together with a latched free running timer value to find out if a timer has expired.
·· So there are an unlimited number of timers (while memory serves) using only 1 VP bank.
*
* - It is unclear how any VP would be stopped ... this means a fixed configuration.
·· ***** VP's are started and stopped by native calls. Each VP type has these native calls
·· defined in their class. They are called from the VP's start() and stop() methods.
*
* Design requirements for ADC:
*
* - ADC will be initialized by native call.
·· ***** ADC.start() and ADC.stop() start and stop an ADC. start() calls install(bank).
·· stop() calls CPU.removeVP()
* - ADC should be readable by user ... I see no provision for this in jvmNative.
·· ***** ADC.value() returns the current value. It calls the readRegister native function.
* - ADC pins should be selectable one for input one for reference output.
·· ***** These are passed to the ADC constructor, eg. when using ADC myADC = new ADC(inpin,outpin)
* - External RC network is required on the input pin for ADC to function.
* - Input measurements are limited from 0 to 3.3VDC.
* - Up to 2 ADC will be usable at once (unless more cogs are added).
·· ***** If we mimic the javelin approach we can run 6 ADC if necessary. The ADC VP code
·· accumulates the number of times the outpin is set high during 256 interrupts. All the ADC
·· VP code needs to do is to set the outpin to the inverted value of the inpin and incrementing
·· a register when the outpin is set high.
*
* Design requirements for DAC:
*
* - DAC will be initialized by native call.
·· **** DAC.start() and DAC.stop()· start() calls install(bank). Note that this is a different install
·· then the one used for ADC. The linker distinguishes both based on class name.
* - DAC should be writeable by user ... I see no provision for this in jvmNative.
·· ***** Done using method DAC.update(value) update() calls writeRegister native function.
* - DAC output pins should be selectable.
·· ***** Pin to use is passed via constructor.
* - External RC network is required on the output pin for DDC to function.
* - Output measurements are limited from 0 to 3.3VDC.
* - Up to 2 DAC will be usable at once (unless more cogs are added).
·· ***** Same comment as under ADC. The pin is set high for the number
·· of interrupts specified in value passed to update(). The remaining time
·· to complete 256 interrupts the pin will be low. Basically, DAC is just a simple PWM.
*
* Design requirements for PWM:
*
* - PWM pins should be selectable (done in native code for now).
* - PWM will be initialized by native call.
* - Each PWM pin's pulse cycle characteristics can be updated any time.
*
* - Up to 2 PWM's should run at once now (allow for 4) ? To use the built-in
* counters for PWM, one needs 2 COG to get 4 PWM, or 3 COG for 6 PWM.
*·· ***** Again, same comment as under ADC (if using software PWM)
*
* Design requirements for Timer:
*
* - Timer should tick every 8.68us.
* - Timer should start when initialzied by native call.
·· ***** The global 32bits timer is started when using Timer.start(), stopped when using Timer.stop()
* - Timer value should latch on demand from native call.
·· **** Native function latch does that. It sets the latchregisters which are readout
··· using the native readRegister calls.
* - One timer will run at a time.
·· ***** There is only 1 timer. See comments on timer above.
*
* - It is not clear how a timer would be stopped; need input.
·· ***** Timer.stop() calls CPU.removeVP() which deletes the Timer VP.
*
* Design requirements for Uart:
*
* - Uart will operate in one direction per instance.
·· ***** Method setDirection can change the direction at runtime for the same instance.
* - Uart will buffer input or output at 256 bytes each.
·· ***** Data is read/written directly from the buffer in java. Which address in the
·· buffer to access is determined by head and tail pointers. These are read/written via
· ·readRegister and writeRegister native calls.
* - Uart receiver using HW flow control will stall sender when 16 RX buffer bytes are free.
·· ***** and will allow transmission again when there are 16 bytes left in the buffer.
* - Uart baud rate will be selectable from 600 to 57600 BPS.
* - Uart stop bits will be selectable from 1 to 8.
* - Uart level sense can be inverted.
·· ***** for both datapin and flowcontrol pin
*
* - Up to 6 Uart will be allowed ? Looks like 2 for now.
·· ***** If we can do the VP code in as little code as on the SX (30 instructions)
·· we should be able to manage 6.
*
*/
I have named all the registers used for the VP routines in jvmData.
regards peter
I implemented the try/catch/finally construct.
For this I moved jb_athrow together with a functions throw and throwException
to the file jvmException.
The function throw allows to throw a runtime error using the defined exceptions
· kIllegalArgumentException· = 1
· kNullPointerException····· = 2
· kIndexOutOfBoundsException = 3
· kOutOfMemoryError········· = 4
I have implemented these throws in new,newarray,anewarray and other
functions where a parameter must not be 0. So these functions now throw
an exception when some parameter is invalid. This does not apply to math functions
as there is no·standard math exception. It is up to the programmer to
use try/catch when dividing and the denominator may be 0.
I also implemented the bytecodes jsr and ret as these are used when using finally.
There are two issues yet to solve:
1. The line
·'if offset => jd.readJavaIntBE(exceptionTable + 1 + index) AND offset < jd.readJavaIntBE(exceptionTable + 3 + index)
in function throwException in jvmException.spin is commented out because else the catch never works.
I based that function on the tinyVM function throw_exception(Object *exception)
located in the timyVM source file .\vmsrc\exceptions.c (attached)
2. When using finally and an exception is not catched, the finally clause is repeated over
and over again.
I attached my test java file.
If anyone could take a look at the throwException function to see where I obviously must be
do something wrong (possibly removing one frame too many ??) that would help me, because
I tried to get the finally working for over half a day now.
BTW, finally works when an exception is catched.
regards peter
On the throw issue, one thing comes to mind that the exception address for Object is -1 $ffff.
It seems that throwException vars e and exceptionTable should be checking for > 0 rather than <> 0.
I can't spend too much time on this though. I've been in such a "trench" many times, and I find
some things help: 1) have a conversation with someone about the problem fully describing the
approach and expectations of process/data on which you depend ... this forces you to explain
in sufficient detail so that you may realize something overlooked and it allows a devil's advocate
to challenge your assumptions. 2) take a long break and get a good night's rest ... answers can
come there ... try to wake up and write it down! 3) take a shower and/or do something mundane
that requires no thought for 15 minutes. This puts you in "alpha" state where answers can also come.
On the VP stuff ... Yes, it had occured to me that the only way to make all this work in a CPU
with no parallel processor, etc... would be to use a "task" fired from an interrupt periodically
similar to what an o/s does with scheduled timer procs. Thanks for the confirmation [noparse]:)[/noparse]
On the UART, do we require 6 256 byte buffers or just 1 (managed by the java code) ?
I missed the part about inverting the control line too. Thanks.
We need to establish an IM connection. Please reply to my private message.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
I am in the process of coming up with some basic guides to help people get "up and running", however, the JVM is still far enough from finished that there is little point in writing too much. In addition, I plan to get a prop stamp and make sure I can make that work, as I don't want to write something that is "incorrect" because I haven't actually tested my own instructions.
I'll hopefully get the prop stamp on monday or tuesday and then have some documentation in a day or two.
~ Christopher
Communication between the Javelin IDE and propeller can be
done using the TX/RX pins used to program the propeller.
(same serial port). It just means you cannot use the proptool
to program new firmware (the jvm program) when the IDE connection is in use.
regards peter
thank you for your appreciation about my code style. I will do my best that everyone can understand my dark thoughts of coding.
@Jazzed, Peter
It seems that we would need 3 cogs to support 6 VP's at all. Each cog of the Propeller provides 2 timers which can be used for ADC, DAC, PWM or only as timer.
I would suggest that we use also the cogs which are running the bytecode decoder. Otherwise we would have 2 cogs for bytecode decoding and up to 3 separate cogs for VP's if necessary.
We could locate the smallest subroutines to the decoding cogs and the more comprehensive code to a separate cog. So the cogs would provide a static configuration about VP's. E.g. decoding cog1 supports 2 timer, decoding cog2 supports 2 PWM and another cog could be started to support 2 ADC, DAC or UART or a mix of 2 of this.
Thomas
Post Edited (Kaio) : 2/24/2008 9:19:08 PM GMT
I'm hoping we can have a VP system equivalent to that on the Javelin SX48B i.e all timer process
functions (ADC, DAC, PWM, Timer, UART) can be performed in one "tick" of a COG.
Speculating that SX48B may be faster than one COG because of it's architecture ... I'm not sure.
I am attracted to the notion of having the counters perform the PWM tasks (timer can be done either
way it seems). It seems that having one COG doing it all for now is fine.
Perhaps copying this "VP set" to other COGS is a way to enrich the Javelin concept ? By providing
"N" times the number of Virtual Periperals, certain things become more possible like controlling
a 17 servo robot for example (current draw there would be a nightmare to coordinate though).
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)