@AndyProp said:
I see, so the shared is allowing other cogs to read from the array, I noticed you wrote in the "' initialize it" 1 to 2048, should that need to have also "Option base 1" and ubyte my values being 0 to 255
Good catch, definitely you'll want ubyte if the values are 0 to 255. As for the option base, that's of course a matter of taste, but it does make it more like "traditional" basic.
Having a brain dead moment, would anyone care to throw a pan at me and remind me how to MSB first in flexbasic, all I can remember right now is shl but totally lost my mind how to do it.
LSB first, 8 bits banged out of a pin.
sub txbyte(packet as ubyte)
dim bit as integer
for bit = 0 to 7
output(DO) = packet and 1
packet = packet >> 1
next bit
end sub
Actually shifting off the bottom or top of a full 32 bit word is a little more efficient, so the best bet is to either reverse the byte and shift out LSB, or else shift it up to the top and then test the top bit to determine what to send. Also, there's a special optimization for bit-banging on P2 with pinl() and pinh(), so I'd write it as:
sub txbyte_msb(packet as uinteger)
dim bit as integer
packet = packet << 24 ' put in upper byte
for bit = 0 to 7
if (packet and $8000_0000) then
pinh(D0)
else
pinl(D0)
end if
packet = packet << 1
next bit
end sub
With all optimizations turned on (-O2) this will generate code like:
@ersmith said:
Actually shifting off the bottom or top of a full 32 bit word is a little more efficient, there's a special optimization for bit-banging on P2 with pinl() and pinh(), so I'd write it as:
sub txbyte_msb(packet as uinteger)
dim bit as integer
packet = packet << 24 ' put in upper byte
for bit = 0 to 7
if (packet and $8000_0000) then
pinh(D0)
else
pinl(D0)
end if
packet = packet << 1
next bit
end sub
That brings me to a thought of what is best: to have 2 almost duplicate easier to read subs TX_Byte and TX_Word that are dedicated to word or byte transmission or just one that shifts left according to length in bits of the packet.
First of all it must be determined what the length of the packet in bits and call that something like bit_length.
if the bit_length = 8 then shift left 24
if the bit_length = 16 then shift left 16
The FOR being from 0 to bit_length
Just how to determine the bit_length I don't know right now.
With the addition of a clock pulse sub that contains the right amount of delays, very soon this becomes a nice little function to serial out some data
Finally ending instead of four dedicated subs:
TX_Byte_Msb1st
TX_Word_Msb1st
TX_Byte_Lsb1st
TX_Word_Lsb1st
Could result in
TX_Packet_Msb1st
TX_Packet_Lsb1st
Another thought Eric, what is best in FlexBasic: "Select Case" or "IF Then Else" statements. I always liked using Select Case
@AndyProp said:
That brings me to a thought of what is best: to have 2 almost duplicate easier to read subs TX_Byte and TX_Word that are dedicated to word or byte transmission or just one that shifts left according to length in bits of the packet.
Strictly from an efficiency perspective having seperate TX_Byte and TX_Word would be better (the compiler has more opportunities for optimization if it sees constants for the length of bits).
However, you'll have to trade that off against the cost of maintaining two different routines versus one routine.
Just how to determine the bit_length I don't know right now.
Probably the best bet would be to pass it in as a parameter to the subroutine.
Another thought Eric, what is best in FlexBasic: "Select Case" or "IF Then Else" statements. I always liked using Select Case
"Select Case" is better if there are many options. For smaller cases it probably doesn't matter, the two will end up being about the same.
That approach will never work, because BCgetDAToffset (with absolute == false) gets you the offset between a module's PBASE and it's DAT data, which in this case is nonsensical for any module that isn't current. An arguably more correct way would be to get the absolute address into baseExpr and use MEMOP_BASE_POP(see handling of clkfreq/clkmode). That'd work for modules that are already compiled, at least (which should(?) cover the case of a member function accessing a global varaible in C). For full cross-module support, some sort of fixup system would be required...
Thank you Ada -- I hadn't seen your comment. Your suggestion of using MEMOP_BAS_POP seems to work, at least for the common case (member functions accessing data in a parent object) and I've checked that in.
@AndyProp You could make that function completely size agnostic if you want. Just pass it the number of bits you want to shift and the source byte/word/long cast as a ulong
sub txbyte_msb(packet as ulong, bitcnt as ulong)
dim preshift as ulong
preshift = 32 - bitcnt
packet = packet << preshift
for bit = 0 to bitcnt-1
if (packet and $8000_0000) then
pinh(D0)
else
pinl(D0)
end if
packet = packet << 1
next bit
end sub
Now you can specify odd bit lengths (5, 12, 27, whatever) and the function just works. Write one for the opposite shift direction if you need the LSB-first.
Optimization problem. A spin program works with -o1, makes strange things with -o2
The symptoms: the robot has a display with an user interface. The user interface displays garbage when compiled with the full optimization and works OK with the standard optimization.
This is the robot controlling program, about 4000 lines of spin2 code, so it is complex to debug. I tried to catch the problem on a simpler code - the video driver+ strange behaving function alone. This worked well with -o2.
This means the bug can be somewhere else in the code and manifests itself in this simple drawing function. What kind of bug can cause this behavior?
This means the bug can be somewhere else in the code and manifests itself in this simple drawing function. What kind of bug can cause this behavior?
One issue that comes to mind is that full optimization can cause sections of timed code to run too fast for peripheral devices. For example setting a data line and then toggling the clock pin in sending data to an lcd. Slow function calls could get changed to inline drvh and drvl instructions. Since most are running the P2 at 200+ MHz this would be a very short pulse most likely violating one of the many hold time specs of the device.
Defensive coding would enforce the minimum delays between such pin transitions, using waits and delay durations that adjust to CPU clock speed.
Of course If there is an optimizer issue, it's not going to be able to be corrected from only listing vague symptoms specific to your application.
Try narrowing it down to the exact optimization flag. I.e. try -O1,loop-reduce, -O1,cse... there's a list of which flags each preset level enables somewhere. There's also an attribute to control optimizer flags per-function, so you can also trx narrowimg it down that way.
@pik33 said:
This is the robot controlling program, about 4000 lines of spin2 code, so it is complex to debug. I tried to catch the problem on a simpler code - the video driver+ strange behaving function alone. This worked well with -o2.
This means the bug can be somewhere else in the code and manifests itself in this simple drawing function. What kind of bug can cause this behavior?
Which version of FlexProp are you using? In 5.9.5 I fixed a fairly serious bug which could cause memory corruption, which was hard to track down. There are also a few significant bug fixes in 5.9.6, so it's worth updating to that one if you haven't already.
As @Rayman suggested, using "-O2,!cse" instead of "-O2" is worth a try -- the CSE (common sub-expression elimination) optimization added in -O2 is one of the ones where bugs often occur. There's a list of all of the individual optimizations in the general.md general compiler documentation.
I call a function which draws a battery on the screen and then another function which draws the rest of the user interface. Both functions use the graphic primitives from a HDMI driver. In the battery function lines are drawn, but not where I wanted them, garbage, instead of characters, are drawn not where I want them, as if the driver function got wrong parameters - bad x, y and character code and color. Then, all the rest of the user interface drawm in another function is drawn correctly. The same methods from HDMI driver are called from both of them. The battery function works better, but still not 100% good when I removed global variables from it (I searched for differences and the battery function uses global variables in it while the main menu doesn't)
Edit: O2,!cse works, while -O1,cse doesn't. A bug found in HDMI driver (unnecessary variable left from the previous version, it seems -O1 initialized it to 0 while -O2 did not). The variable is now removed and characters are displayed OK, even if full -O2 enabled. The problem "lines not in the proper place" remains. Maybe another bug left to find.
Edit 2. Something is wrong with parameters..
pub outtextxycf(x,y,text,f) | iii
debug(udec(f))
repeat iii from 0 to strsize(text)-1
putcharxycf(x+8*iii,y,byte[text+iii],f)
k:=15
if voltage<24800
k:=40
v.outtextxycf(x+19,y+4,v.inttostr(voltage+/1000),k)
there is
Cog0 f = 4_294_967_096
which is %1111 1111 1111 1111 1111 1111 0011 1000. Its 8bit value is 56 dec and it gives a purple color on the screen (expected with c==56 but f should be 15 or 40 in this case)
-O1... and the debug is good (15 and 40) and colors are good too
@pik33 : Sorry, but I cannot reproduce any problem given the fragments you posted. Are you able to share more of your program? I think we'd need at least the function that makes the call to v.outtextxycf. Having the whole program would be even better, if you can share it.
@ersmith said:
@pik33 : Sorry, but I cannot reproduce any problem given the fragments you posted. Are you able to share more of your program? I think we'd need at least the function that makes the call to v.outtextxycf. Having the whole program would be even better, if you can share it.
As it is work in progress and too big piece of code, I created simpler file - bugcatcher.spin2 - to try individual functions and catch the problem.
As it is now, I suspect this is rather a bug in my code, which appears with full optimization enabled, and doesn't appear with cse switched off - than a bug in the compiler itself. I only try to understand what kind of bug can make the function argument pass incorrectly. I will return to this project in Monday, as it needs a hardware I haven't at home.
There is a function "battery" near line 660, which calls outtextxycf and other graphic functions and passes incorrect values. It is called from processmainmenu(), starting near line 770
Edit 2: That is not parameter passing error. The 'k' local variable became 4_294_967_096 before calling outtextxycf. To be debugged further
This is the parent function:
pub battery() | i,j,k,x,y
x:=131
y:=8
voltage:=(31*voltage+adc.measure())>>5 ' accumulator voltage
if voltage<>oldv
if (oldv>24000) && (voltage<24000)
'' todo - block motors here!!!!
audio.sawbeep(2*audio.c4,8192,200)
audio.sawbeep(2*audio.f4,8192,200)
audio.sawbeep(2*audio.c4,8192,200)
audio.sawbeep(2*audio.f4,8192,200)
audio.say(4,16384)
waitms(3000)
repeat 100
voltage:=(31*voltage+adc.measure())>>5
j:=(voltage-24000)
if j<0
j:=0
if j>4800
j:=4800
j:=j/48
i:=116
if voltage<27200
i:=180
if voltage<24800
i:=244
if voltage<24200
i:=36
k:=15
if voltage<24800
k:=40
v.waitvbl(1)
v.frame(x,y,x+102,y+22,15)
v.frame(x+102,y+4,x+106,y+18,15)
v.box(x+1,y+1,x+101,y+21,0)
v.box(x+1,y+1,x+1+j,y+21,i)
v.outtextxycf(x+19,y+4,v.inttostr(voltage+/1000),15)
debug(udec(k))
v.outtextxycf(x+35,y+4,string("."),k)
v.outtextxycf(x+43,y+4,v.inttostr2(voltage+//1000,3),k)
v.outtextxycf(x+75,y+4,string("V"),k)
oldv:=voltage
Now
Cog0 k = 4_294_967_096
Edit 3.
k is good after it is set, it goes bad after first call to v.frame. I tried to call v.line which also destroys k.
Another lack of my understanding of how the Spin compiler works... I tried to make an array of 17 strings... so
long mapnames[17]
then
repeat i from 0 to 16
mapnames[i]:=string(" ")
What I wanted: initialize the array to 17 strings of 16 spaces each
What I got: all 17 pointers are the same. The compiler simply thought - "I already have the string defined so I will not waste the memory for 16 more identical strings."
A workaround: initialize every string with different text in it
But that’s how string() is defined. It will just create a static string that can be pointed to. Imagine you printing a string a 1000 times in a loop. Do you want 1000 allocations for that, gobbling up memory?
I’m not aware of dynamic memory allocation in spin. It is available in C. And comes with its 🥫 of 🪱!
However if I wanted to do such a thing, I’d declare a byte array of 17*16, and then just take the address of that in the first pointer, and then do the pointer arithmetic for the following 16.
repeat i from 0 to 16
mapnames[i]:=@buffer + i * 16
The best way to initialize an array in Spin is probably to use the DAT section, and then to use something like @deets 's solution to fill the array with pointers, something like:
DAT
rawdata
$20[16], 0 ' string 1, 16 spaces then a 0
$20[16], 0 ' string 2, 16 spaces then a 0
$20[16]. 0 ' string 3, 16 spaces then a 0
' repeat as many times as necessary
...
repeat i from 0 to 16
mapnames[i] := @rawdata + i * 17
Spin is simply not Pascal, not C, and not even Basic. This causes this type of problems for someone who programmed in these languages and unconsciously expects something else, than it really does. I use a lot of strings that are PITA in Spin, I need arrays of strings and multidimensional arrays of integers, and maybe my optimization problems are also result of something like this string problem, which I can't see yet.
No more big program in Spin. The robot control program was the last one. I (probably) will not rewrite this, it is too big and in the last pre-beta stage, but for my next P2 projects there is Basic and C available.
Unexpected Send
I have been working in flexprop a lot recently. I have some files that don't compile as the invisable last line seems to contain this error. add a dat section at the end of the file and the error moves down to the new end. I can block copy code sections out of the file into a new one and it will compile fine. how do I locate this mystery bug and squash it for good?
jim
@iseries : Unfortunately FlexC's parsing of data types is incomplete, and you've run into one of the unsupported cases. I'll try to fix that in the next release. I don't think there's any easy work-around.
@RS_Jim : Are you able to share any of the files that don't compile? If we could look at it then we might be able to figure out what's wrong. If you can't share, then look carefully at all the lines containing the word "send" and see if any of them are suspicious. It could be that there's some kind of error earlier in the file that doesn't get reported until the end. Note that in Spin2 the words "send" and "recv" are reserved, which is not the case in Spin1.
@RS_Jim said:
I have been working in flexprop a lot recently. I have some files that don't compile as the invisable last line seems to contain this error. add a dat section at the end of the file and the error moves down to the new end. I can block copy code sections out of the file into a new one and it will compile fine. how do I locate this mystery bug and squash it for good?
Looks like it could be unexpected non-breaking spaces for example, from copy & pasting from a browser for example. Maybe this helps: https://pages.cs.wisc.edu/~markm/ascii.html
@ersmith,
Eric, here is a zip of one of the files that fails to compile with the mystery "send" command. My environment is flexprop 5.9.2 on a HP G6 laptop running Lenux Mint 20. Thanks for taking a look at this.
Jim
I can't compile your file, because it depends on a whole bunch of libraries that I don't have. However, there is a suspicious { on line 154 which does not seem to ever be closed. flexspin 5.9.6 complains about this (WARNING: EOF seen inside comment). This then produces an error which says "error: syntax error, unexpected $end" which means unexpected end of file, not unexpected send.
Comments
Good catch, definitely you'll want ubyte if the values are 0 to 255. As for the option base, that's of course a matter of taste, but it does make it more like "traditional" basic.
Having a brain dead moment, would anyone care to throw a pan at me and remind me how to MSB first in flexbasic, all I can remember right now is shl but totally lost my mind how to do it.
LSB first, 8 bits banged out of a pin.
AND with binary 1000_0000. If result > 0, output a 1, else output a 0. Shift left one space. Lather, rinse, repeat 7 more times. 👍
Cheers
Actually shifting off the bottom or top of a full 32 bit word is a little more efficient, so the best bet is to either reverse the byte and shift out LSB, or else shift it up to the top and then test the top bit to determine what to send. Also, there's a special optimization for bit-banging on P2 with pinl() and pinh(), so I'd write it as:
With all optimizations turned on (-O2) this will generate code like:
which will output at 4 cycles/bit, which is just about optimal I think.
That brings me to a thought of what is best: to have 2 almost duplicate easier to read subs TX_Byte and TX_Word that are dedicated to word or byte transmission or just one that shifts left according to length in bits of the packet.
First of all it must be determined what the length of the packet in bits and call that something like bit_length.
if the bit_length = 8 then shift left 24
if the bit_length = 16 then shift left 16
The FOR being from 0 to bit_length
Just how to determine the bit_length I don't know right now.
With the addition of a clock pulse sub that contains the right amount of delays, very soon this becomes a nice little function to serial out some data
Finally ending instead of four dedicated subs:
TX_Byte_Msb1st
TX_Word_Msb1st
TX_Byte_Lsb1st
TX_Word_Lsb1st
Could result in
TX_Packet_Msb1st
TX_Packet_Lsb1st
Another thought Eric, what is best in FlexBasic: "Select Case" or "IF Then Else" statements. I always liked using Select Case
Strictly from an efficiency perspective having seperate TX_Byte and TX_Word would be better (the compiler has more opportunities for optimization if it sees constants for the length of bits).
However, you'll have to trade that off against the cost of maintaining two different routines versus one routine.
Probably the best bet would be to pass it in as a parameter to the subroutine.
"Select Case" is better if there are many options. For smaller cases it probably doesn't matter, the two will end up being about the same.
Comment on a recent git commit (not sure if you see it when I comment on the commit?): added warning for labels in other modules
That approach will never work, because
BCgetDAToffset
(withabsolute == false
) gets you the offset between a module's PBASE and it's DAT data, which in this case is nonsensical for any module that isn'tcurrent
. An arguably more correct way would be to get the absolute address intobaseExpr
and useMEMOP_BASE_POP
(see handling of clkfreq/clkmode). That'd work for modules that are already compiled, at least (which should(?) cover the case of a member function accessing a global varaible in C). For full cross-module support, some sort of fixup system would be required...Thank you Ada -- I hadn't seen your comment. Your suggestion of using MEMOP_BAS_POP seems to work, at least for the common case (member functions accessing data in a parent object) and I've checked that in.
I didn't try this time though.
@AndyProp You could make that function completely size agnostic if you want. Just pass it the number of bits you want to shift and the source byte/word/long cast as a ulong
Now you can specify odd bit lengths (5, 12, 27, whatever) and the function just works. Write one for the opposite shift direction if you need the LSB-first.
Optimization problem. A spin program works with -o1, makes strange things with -o2
The symptoms: the robot has a display with an user interface. The user interface displays garbage when compiled with the full optimization and works OK with the standard optimization.
This is the robot controlling program, about 4000 lines of spin2 code, so it is complex to debug. I tried to catch the problem on a simpler code - the video driver+ strange behaving function alone. This worked well with -o2.
This means the bug can be somewhere else in the code and manifests itself in this simple drawing function. What kind of bug can cause this behavior?
One issue that comes to mind is that full optimization can cause sections of timed code to run too fast for peripheral devices. For example setting a data line and then toggling the clock pin in sending data to an lcd. Slow function calls could get changed to inline drvh and drvl instructions. Since most are running the P2 at 200+ MHz this would be a very short pulse most likely violating one of the many hold time specs of the device.
Defensive coding would enforce the minimum delays between such pin transitions, using waits and delay durations that adjust to CPU clock speed.
Of course If there is an optimizer issue, it's not going to be able to be corrected from only listing vague symptoms specific to your application.
Try narrowing it down to the exact optimization flag. I.e. try
-O1,loop-reduce
,-O1,cse
... there's a list of which flags each preset level enables somewhere. There's also an attribute to control optimizer flags per-function, so you can also trx narrowimg it down that way.@Wuerfel_21 told me this one once:
REM -O2,!cse <-- Use this if -O2 doesn't work
Which version of FlexProp are you using? In 5.9.5 I fixed a fairly serious bug which could cause memory corruption, which was hard to track down. There are also a few significant bug fixes in 5.9.6, so it's worth updating to that one if you haven't already.
As @Rayman suggested, using "-O2,!cse" instead of "-O2" is worth a try -- the CSE (common sub-expression elimination) optimization added in -O2 is one of the ones where bugs often occur. There's a list of all of the individual optimizations in the general.md general compiler documentation.
5.9.6.
Today I will try to find more about it.
I call a function which draws a battery on the screen and then another function which draws the rest of the user interface. Both functions use the graphic primitives from a HDMI driver. In the battery function lines are drawn, but not where I wanted them, garbage, instead of characters, are drawn not where I want them, as if the driver function got wrong parameters - bad x, y and character code and color. Then, all the rest of the user interface drawm in another function is drawn correctly. The same methods from HDMI driver are called from both of them. The battery function works better, but still not 100% good when I removed global variables from it (I searched for differences and the battery function uses global variables in it while the main menu doesn't)
Edit: O2,!cse works, while -O1,cse doesn't. A bug found in HDMI driver (unnecessary variable left from the previous version, it seems -O1 initialized it to 0 while -O2 did not). The variable is now removed and characters are displayed OK, even if full -O2 enabled. The problem "lines not in the proper place" remains. Maybe another bug left to find.
Edit 2. Something is wrong with parameters..
Now if
v.outtextxycf(x+19,y+4,v.inttostr(voltage+/1000),15)
there is
Cog0 f = 15
but if
there is
Cog0 f = 4_294_967_096
which is %1111 1111 1111 1111 1111 1111 0011 1000. Its 8bit value is 56 dec and it gives a purple color on the screen (expected with c==56 but f should be 15 or 40 in this case)
-O1... and the debug is good (15 and 40) and colors are good too
@pik33 : Sorry, but I cannot reproduce any problem given the fragments you posted. Are you able to share more of your program? I think we'd need at least the function that makes the call to v.outtextxycf. Having the whole program would be even better, if you can share it.
All the big, alpha state mess is here: https://github.com/pik33/ultibo-propeller/tree/main/robuv001 - the working file is robuv015a.spin2, EDIT: robuv016a.spin2
As it is work in progress and too big piece of code, I created simpler file - bugcatcher.spin2 - to try individual functions and catch the problem.
As it is now, I suspect this is rather a bug in my code, which appears with full optimization enabled, and doesn't appear with cse switched off - than a bug in the compiler itself. I only try to understand what kind of bug can make the function argument pass incorrectly. I will return to this project in Monday, as it needs a hardware I haven't at home.
There is a function "battery" near line 660, which calls outtextxycf and other graphic functions and passes incorrect values. It is called from processmainmenu(), starting near line 770
Edit 2: That is not parameter passing error. The 'k' local variable became 4_294_967_096 before calling outtextxycf. To be debugged further
This is the parent function:
Now
Edit 3.
k is good after it is set, it goes bad after first call to v.frame. I tried to call v.line which also destroys k.
Edit 4. .lst attached.
Another lack of my understanding of how the Spin compiler works... I tried to make an array of 17 strings... so
long mapnames[17]
then
What I wanted: initialize the array to 17 strings of 16 spaces each
What I got: all 17 pointers are the same. The compiler simply thought - "I already have the string defined so I will not waste the memory for 16 more identical strings."
A workaround: initialize every string with different text in it
A "legal" solution: I don't know.
But that’s how string() is defined. It will just create a static string that can be pointed to. Imagine you printing a string a 1000 times in a loop. Do you want 1000 allocations for that, gobbling up memory?
I’m not aware of dynamic memory allocation in spin. It is available in C. And comes with its 🥫 of 🪱!
However if I wanted to do such a thing, I’d declare a byte array of 17*16, and then just take the address of that in the first pointer, and then do the pointer arithmetic for the following 16.
The best way to initialize an array in Spin is probably to use the DAT section, and then to use something like @deets 's solution to fill the array with pointers, something like:
Spin is simply not Pascal, not C, and not even Basic. This causes this type of problems for someone who programmed in these languages and unconsciously expects something else, than it really does. I use a lot of strings that are PITA in Spin, I need arrays of strings and multidimensional arrays of integers, and maybe my optimization problems are also result of something like this string problem, which I can't see yet.
No more big program in Spin. The robot control program was the last one. I (probably) will not rewrite this, it is too big and in the last pre-beta stage, but for my next P2 projects there is Basic and C available.
Trying to compile this code from littlefs but running into a compiler issue.
Here is a recreation of the offending code:
These are the errors:
Mike
Unexpected Send
I have been working in flexprop a lot recently. I have some files that don't compile as the invisable last line seems to contain this error. add a dat section at the end of the file and the error moves down to the new end. I can block copy code sections out of the file into a new one and it will compile fine. how do I locate this mystery bug and squash it for good?
jim
Sounds like the files should be looked at in a hex editor. I'd be curious about the last byte or two of the file.
@iseries : Unfortunately FlexC's parsing of data types is incomplete, and you've run into one of the unsupported cases. I'll try to fix that in the next release. I don't think there's any easy work-around.
@RS_Jim : Are you able to share any of the files that don't compile? If we could look at it then we might be able to figure out what's wrong. If you can't share, then look carefully at all the lines containing the word "send" and see if any of them are suspicious. It could be that there's some kind of error earlier in the file that doesn't get reported until the end. Note that in Spin2 the words "send" and "recv" are reserved, which is not the case in Spin1.
Looks like it could be unexpected non-breaking spaces for example, from copy & pasting from a browser for example. Maybe this helps: https://pages.cs.wisc.edu/~markm/ascii.html
@ersmith,
Eric, here is a zip of one of the files that fails to compile with the mystery "send" command. My environment is flexprop 5.9.2 on a HP G6 laptop running Lenux Mint 20. Thanks for taking a look at this.
Jim
I can't compile your file, because it depends on a whole bunch of libraries that I don't have. However, there is a suspicious { on line 154 which does not seem to ever be closed. flexspin 5.9.6 complains about this (WARNING: EOF seen inside comment). This then produces an error which says "error: syntax error, unexpected $end" which means unexpected end of file, not unexpected send.