@ersmith (or anyone): Is there any way, at runtime, to determine the type of a variable that is passed to a SUB or FUNCTION in FlexBASIC?
No. The "any" type is just an undifferentiated 32 bit value (it's what everything is in the Spin language, if you're familiar with that). Once something is cast to "any" then all information about it is lost.
It might be possible to distinguish types in template functions, like:
ANY(T) FUNCTION WhatAmI(a as T) as string
if T = string then return "It's a string"
... (more of the same) ...
if T = long then return "It's a long"
END FUNCTION
In templated functions the type does exist (and may be referred to with a variable name, like "T" above), so the compiler could figure out what type is associated. That ability to do this test does *not* exist now, but since the type information is available then in theory we could implement something like that. It'd be some work though.
@ersmith I got to noodling on the SWAP function found in other BASICs and realized my library needed this to let new FlexBASIC coders have a leg up with a BASIC syntax they already know. (The construct x,y = y,x isnt something they will get until they’ve cracked the manual). So I wrapped the swap into a SWAP() subroutine:
SUB Swap(byref x as any, byref y as any)
x,y = y,x
END SUB
The problem of course is that ANY just tells the compiler not to enforce any type. But what if the user tries to feed it a ulong and a string to swap? SWAP() faithfully does it with that ANY, and garbage is the result with no error thrown.
So my thought was to make sure the two incoming types were at least of the same ilk (ie. Both numeric types, or both strings). If they are different but could be made compatible, then perhaps try to coerce them into something useful with a CAST, and if not then throw an error.
OK, the fix wasn't too hard. The updated fastspin.zip is attached.
Templates are still probably a bit fragile, but at least swap works right now:
' template function demo
any(T1) sub swap(byref x as T1, byref y as T1)
dim tmp as T1
tmp = x
x = y
y = tmp
end sub
var a = "hello"
var b = "goodbye"
swap(a, b)
print "a, b = ", a, b
var c = 10
var d = 20
swap(c, d)
print "c, d =", c, d
swap(c, d)
print "now c, d =", c, d
'swap(a, c) ' causes an error
@ersmith Any interest in making a modification to the PRINT statement that would arbitrate between competing print requests from multiple cogs?
As it currently exists, all cogs share the USB channel. If two cogs attempt to print at the same time, a bit of a trainwreck occurs. It seems to me that a PRINT-like variant that first checked, and then sets a global semaphore before it pushed any characters out, and then reset the semaphore when the last character was out on the wire, would allow competing PRINT requests to happen in an orderly manner. The downside is printing from one cog would stall printing from all other cogs, but this isnt necessarily a show-stopper.
I started coding this myself in a function wrapper called “PrintM()” (Print, managed), but realized this might be something that could be included as part of the FlexBASIC language.
@ersmith Any interest in making a modification to the PRINT statement that would arbitrate between competing print requests from multiple cogs?
The overall idea of allowing prints from multiple COGs is a good one, and I'd like to support it some day. But it actually opens quite a can of worms. For example, what should the granularity be: per-item, per PRINT statement, or per line of text? How should this work for other languages that don't have PRINT but do have similar functionality (like C's printf)? What if the COGs are printing to different devices (using PRINT #); one would think they should be able to do so in parallel, but how do we detect this? Etc.
We definitely need to think about this, but I don't have good answers for all of these questions yet.
My interim function just does it by each Print statement. Just really basic (pardon the pun) stuff. I hadnt even thought about how other languages would work, or the possibility of a PRINT # implementation, or the granularity aspects.
Sounds like some contemplation over an adult beverage is in order!
I've uploaded fastspin 4.3.0 to github, and the corresponding FlexGUI to my patreon page (a binary release to github will follow soon). There are some significant changes:
- P2 API Change: use call/ret instead of calla/reta
- Added finer-grained control over optimizations
- Added per-function control over optimizations
- Added preliminary implementation of DEBUG() for Spin2
- Fixed some bugs in cse and loop optimizations
- Fixed byref parameters in BASIC template functions
- Improved dead code removal
The optimizer bug fixes and new call/ret calling convention have improved the performance on CoreMark from 30 iterations/sec to 37 iterations/sec. so these are definitely wins. There are now options for individual optimizations, so for example you can compile with all optimizations except common sub expression removal by giving a command line option like "-Oall,!cse". And there is some primitive support for Chip's DEBUG() facility, which is enabled with the "-g" command line flag (if there's no -g then all the DEBUG() lines in Spin2 are silently ignored).
Your improvements sound good Eric. What will happen once the internal stack capacity is reached? Does Fastspin somehow know the call depth will then exceed 8 and then move to a hub based stack? Are there any restrictions here to deal with, like when using recursive functions etc?
Your improvements sound good Eric. What will happen once the internal stack capacity is reached? Does Fastspin somehow know the call depth will then exceed 8 and then move to a hub based stack? Are there any restrictions here to deal with, like when using recursive functions etc?
Non-leaf functions pop the address off the internal stack and save it to the HUB stack along with the other registers. Since the HUB stack write uses SETQ+WRLONG, this is (almost) free, and it means there are no limits to recursion, etc.
Is "reg" a reserved word now? An older code is giving an error on it...
"reg" is a reserved word in Spin2, so it will produce an error in files with a ".spin2" extension (but not in files with a ".spin" extension).
Also, is there a list of reserved words somewhere? In source or docs?
For BASIC it's all documented. For Spin it's spread across Chip's various documents (for Spin1 and Spin2) and I haven't had a chance to consolidate these for the fastspin documents. In the source it's in frontends/lexer.c, arrays "init_spin_words" (common keywords), "init_spin1_words" (Spin1 additional reserved words) and "init_spin2_words" (Spin2 additional reserved words).
I've released fastspin 4.3.1 to fix the SEND problem @rogloh discovered. It also fixes some DEBUG() problems and adds a few more missing Spin2 functions.
however when porting over to PNut I found I needed to add the extra parentheses for it to do what I want. e.g. I always want bit 24 OR'd into the mode variable, independent of the crystalHz check being less than 16M or not (which only OR's the bottom 4 bits):
So it looks like the precedence of the ternary operator a?b:c differs in the two toolchains and it has the lowest priority in the PNut toolchain but something higher in Fastspin.
This was with an older Fastspin v4.2.5 so you might have it working differently now. I'm thinking it's probably good to have the precedence working the same way if possible to avoid issues.
@ersmith You had mentioned that you needed to renew/replace your existing code signing certificate and were getting hammered on the price. I found these guys a while back and figured I'd pass it on. They are $80/yr, no matter if its new/renew/transfer, etc.
Comments
No. The "any" type is just an undifferentiated 32 bit value (it's what everything is in the Spin language, if you're familiar with that). Once something is cast to "any" then all information about it is lost.
It might be possible to distinguish types in template functions, like: In templated functions the type does exist (and may be referred to with a variable name, like "T" above), so the compiler could figure out what type is associated. That ability to do this test does *not* exist now, but since the type information is available then in theory we could implement something like that. It'd be some work though.
What are you trying to accomplish?
So my thought was to make sure the two incoming types were at least of the same ilk (ie. Both numeric types, or both strings). If they are different but could be made compatible, then perhaps try to coerce them into something useful with a CAST, and if not then throw an error.
Unfortunately "byref" confuses the template parser and leads to a whole rats nest of incomprehensible errors. I'll try to get that fixed.
Templates are still probably a bit fragile, but at least swap works right now:
As it currently exists, all cogs share the USB channel. If two cogs attempt to print at the same time, a bit of a trainwreck occurs. It seems to me that a PRINT-like variant that first checked, and then sets a global semaphore before it pushed any characters out, and then reset the semaphore when the last character was out on the wire, would allow competing PRINT requests to happen in an orderly manner. The downside is printing from one cog would stall printing from all other cogs, but this isnt necessarily a show-stopper.
I started coding this myself in a function wrapper called “PrintM()” (Print, managed), but realized this might be something that could be included as part of the FlexBASIC language.
Thoughts?
The overall idea of allowing prints from multiple COGs is a good one, and I'd like to support it some day. But it actually opens quite a can of worms. For example, what should the granularity be: per-item, per PRINT statement, or per line of text? How should this work for other languages that don't have PRINT but do have similar functionality (like C's printf)? What if the COGs are printing to different devices (using PRINT #); one would think they should be able to do so in parallel, but how do we detect this? Etc.
We definitely need to think about this, but I don't have good answers for all of these questions yet.
My interim function just does it by each Print statement. Just really basic (pardon the pun) stuff. I hadnt even thought about how other languages would work, or the possibility of a PRINT # implementation, or the granularity aspects.
Sounds like some contemplation over an adult beverage is in order!
- P2 API Change: use call/ret instead of calla/reta
- Added finer-grained control over optimizations
- Added per-function control over optimizations
- Added preliminary implementation of DEBUG() for Spin2
- Fixed some bugs in cse and loop optimizations
- Fixed byref parameters in BASIC template functions
- Improved dead code removal
The optimizer bug fixes and new call/ret calling convention have improved the performance on CoreMark from 30 iterations/sec to 37 iterations/sec. so these are definitely wins. There are now options for individual optimizations, so for example you can compile with all optimizations except common sub expression removal by giving a command line option like "-Oall,!cse". And there is some primitive support for Chip's DEBUG() facility, which is enabled with the "-g" command line flag (if there's no -g then all the DEBUG() lines in Spin2 are silently ignored).
Non-leaf functions pop the address off the internal stack and save it to the HUB stack along with the other registers. Since the HUB stack write uses SETQ+WRLONG, this is (almost) free, and it means there are no limits to recursion, etc.
Also, is there a list of reserved words somewhere? In source or docs?
"reg" is a reserved word in Spin2, so it will produce an error in files with a ".spin2" extension (but not in files with a ".spin" extension).
For BASIC it's all documented. For Spin it's spread across Chip's various documents (for Spin1 and Spin2) and I haven't had a chance to consolidate these for the fastspin documents. In the source it's in frontends/lexer.c, arrays "init_spin_words" (common keywords), "init_spin1_words" (Spin1 additional reserved words) and "init_spin2_words" (Spin2 additional reserved words).
In FastSpin this code behaved differently to PNut and actually worked as I wanted (maybe coded incorrectly for SPIN2, but I'm used to this from C):
however when porting over to PNut I found I needed to add the extra parentheses for it to do what I want. e.g. I always want bit 24 OR'd into the mode variable, independent of the crystalHz check being less than 16M or not (which only OR's the bottom 4 bits):
So it looks like the precedence of the ternary operator a?b:c differs in the two toolchains and it has the lowest priority in the PNut toolchain but something higher in Fastspin.
This was with an older Fastspin v4.2.5 so you might have it working differently now. I'm thinking it's probably good to have the precedence working the same way if possible to avoid issues.
https://www.ksoftware.net/code-signing-certificates
I have no relationship with them, financial or otherwise.
Jeff: thanks for the pointer, that looks promising!
https://www.st.com/content/st_com/en/products/embedded-software/mems-and-sensors-software/drivers-for-mems/c-driver-mems.html
I seems to be choking on the "float_t" data type declarations...
Can it be that not defined?
Seems OK with "int32_t"...
Had to add a fake main() to the .c file. Not sure why it doesn't include one...
I'm impressed that this code actually compiles... Got a lot going on in it... Bitfields and such...
Wonder if it actually works...
Does this mean it's just set to whatever loadp2 is set to?
Looks like the default is 80 MHz, right?
BTW, thanks for noticing that float_t and double_t were missing. I'll add those, but you're right, for now just do #define float_t float.
I know you were working on one, but I don't see anything about it in docs or examples...
I was just testing it with a C program that calls a Spin2 object.
This in the Spin2 code works: It shows up in the output terminal window of loadp2, along with the C printf output.
But, these don't work:
Should it? Or, am I doing it wrong? I get no output other than "Cog0"...
Can you add "-g" to the docs?
I seem to be seeing that it only works when baud is 230400.
Is this correct?
Also, my very first debug command seems to be producing gibberish. After that, it seems to work...