FlexC and 64 bit integers
ManAtWork
Posts: 2,175
in Propeller 2
For my CNC/motion controller project I need to do some calculations with fixed point math for speed reasons. Most of the values fit in 32 bit but some do not. For example I need an averaging filter where input, output and buffer uses 32 bits but the sum needs 64 bit.
I've noticed that long long aka int64_t has been implemented in FlexC which is very useful. However, in some functions I'd like to use hand-optimized code with inline assembler. So is something like this possible?
uint32_t sqrt64 (uint64_t a) { uint32_t sr; __asm{ qsqrt a.lo,a.hi getqx sr } return sr; }
Or probably there are already predefined functions for most of what I need, just like __builtin_mulh()? Are the builtin functions documented in "c.pdf" all that exists or are there more of them hidden somewhere?
Comments
No, at the moment there's no way to access the components of a C 64 bit variable in inline assembly. However, you could write it in Spin as something like:
Hmm, I don't know how your spin example is different from C. If the 64 bit variable is already split into two 32 bit ones I could also do the same in C, right? So I'll try the old union-trick...
When trying to manipulate pointers or unions to split the 64 bit value into two 32 bit variables I get error messages like "Variable must be placed in memory (probably due to an @ expression) and hence cannot be accessed in inline assembly".
But this seems to work:
... though it compiles to rather funny ASM code. But optimization is not so important at the moment.We can take care of that later.
In the most recent source code (checked into github) you can do:
Thanks a lot! Now the code is really highly optimized. The function gets inlined automatically and the compiler even places other instructions between the qsqrt and the getqx to make best use of the pipelined execution.
Yes, that was a nice optimization that @Wuerfel_21 added -- she's done a lot to improve the compiler.
I thought
should print out the whole long long but it seems only to print out the least significant 32 bits.
The format should be "%llX", not "%Xll". The format specifier (
X
in this case) always has to come last, after any length modifiers or width/precision specification. See https://cplusplus.com/reference/cstdio/printf/ for details.OK, printf doesn't know any 64 bit types, yet. The function call seems to put it on the stack so that...
prints out all 64 bits but in the wrong MSW/LSW order (little endian instead of big endian)
Aaarrgg!! Most of the time, the problem is on the other side of the screen.
I've been debugging my code for hours and it turns out it worked well from the beginning. Only my output was shifted because I pushed more onto the stack than printf popped out. Of course, the compiler can't check this with "..." parameters.
What is wrong with
? The compiler says "error: Operand too complex for inline assembly". But the example in post #5 works.
qmul takes 32-bit operands.
maybe it's uint64_t versus unsigned long long?
The error is displayd for the line with getqx, not qmul. "unsigned long long" makes no difference.
@ManAtWork : there's a missing case in the inline assembly for r+N, it can deal with parameters but has trouble with local variables. There also seems to be a bug in getqy, so even after I fixed the missing case incorrect code was generated. For now, I suggest doing:
Ok, the basic calculations seem to work, now. But now I encountered another problem. My rather complex program showed unexplainable results. Because of my stupid problems with printf I started to doubt everything and even ported parts of the code to the PC to be able to use a single step source level debugger. It was kind of a Heisenbug and when observed to close the problem magically vanished...
It took me more than a day but I finally managed to make a small program that does almost nothing but can still reproduce the bug. It must be some combination of me getting cought in some of the numerous C trapdoors and maybe the compiler not giving correct warnings. I can't tell but the behaviour is really strange. This is what it prints
The code should run without modifications on a KISS board. For boards with the standard 20MHz crystal you have to modify/delete the _clkfreq value in the main program.
As I understand it this should display vn=0 if all other variables are initialized correctly and I haven't made a stupid mistake, again. But what's suspicious is that if I turn off optimisation (-O0) the program doesn't compile at all. The compiler complains with "error: Changing hub value for symbol __struct__s_vfs_file_t_putchar".
Wow, calling C functions from Spin2. Didn't know one could do that...
The "changing hub value" error message is one I've seen before in -O0, and related to multiple copies of the struct functions being compiled (even though only one copy is ever used). I've checked in a work-around for now, which is to enable unused function removal even at -O0.
Serial corruption seems to be related to switching back and forth between debug() output and printf() output. Chip's debug code seems to make different assumptions from mine, and although I've tried various changes I can't seem to make them work well together. A work-around is to use printf debug (plain -g instead of -gbrk).
Thank you very much! It now compiles without errors with both -O0 and -O1 and it doesn't output any scrambled characters anymore.
Yes, -g makes sense. I haven't thougt that this would cause any (serious) conflicts and just used the defaults. OK, dropped or corrupted characters are logical because the two debug outputs use different buffers but the same smart pins. But why does it overwrite my variables? Never mind.... it's all fine, now.
Yes, that's a really cool and useful feature. Spin(2) fits the Propeller very well and is good for smaller programs and hardware related drivers. The graphical debugging (unfortunatelly not yet available in FlexProp) is especially useful for realtime debugging. On the other hand, for bigger projects with complex data structures I like C more. It supports real data types and not only bytes, words and longs. And to try out complicated algorithms I could use an IDE on the PC which offers a source level single step debugger where I can watch variables while the code is being executed with just a click instead of having to insert debug() or printf().
All 4 languages in Flexprop can be mixed in one project, which is very convenient. Now I use Basic for near all things on a P2. Much simpler and much more readable than Spin or C ( C is known as "write only language" ) , structural, and I can use C and Spin code imported as a class.
Grrr, bad news, celebrated too early. Debug output now works flawlessly. But my program still outputs false results. I've added a memory hexdump function, see attached code. This is the output:
Opposed to yesterday, vn now equals zero at the end of the ClearBuffer() function which is correct. But after return from ClearBuffer() and after calling Test() vn first equals 00000006 in the DumpBuffer() function and then even gets overwritten with 0635C when DumpMem() is called. And if I change the "24" of
memset (buf1, 0xAA, 24);
in ClearBuffer() to "32" which means pre-filling the first 8 instead of 6 longwords of AxisBuffer[0] with $AA then execution stops after returning from ClearBuffer() or Init_Axes(). The program crashes although sizeof(AxisBuffEntry)==64.This looks like the stack and the memory of AxisBuffer are overlapping. I don't know if there's a classic stack in FlexC. If I look at the assembler code I think it uses the normal call stack for call/return and "objptr" for variables.
I'm not sure if it has anything to do with the 64 bit types. But it's possible. I've written much bigger programs with much more structures and arrays and the compiler always calculated the adresses correctly. However, things often get screwed up when new features are added... Or am I still doing something wrong?
The B2M_memdump.zip file is missing B2M_Axes.h. I tried using an older B2M_Axes.h, but it didn't produce the same output as in your example.
My suspicion right now is that the problem is in the Spin <-> C interface, that the Spin object declaration is not reserving the necessary space for C variables. A possible work-around for this (for now) would be to declare the variables as "static" in C.
@ManAtWork : Using a header file (.h) for an object probably won't work as you'd like. Try using B2M_Axes.c as the file for the object, instead of B2M_Axes.h.
Hmm, the file B2M_Axes.h hasn't changed since Nov 15th. So if you use the one from post #17 it should work. As there are probably dangling pointers and corrupted memory there's ahigh chance that the program doesn't output exacly the same for you as for me. It may depend on uninitialized memory. But it should output something different from zero in the AxisBuffer[0].vNom variable after returning from ClearBuffer().
The first dump is correct as ClearBuffer() sets it to 0 and the second is different. Do you see at least some differences? The bufffer should theoretically stay the same as no variable assignments are made after calling Clear Buffer(). What results do you get? Did you also test if the program stops or crashes if you change the memset length to "32"?
Ok, I'll try that.
BASIC ain't going away If it wasn't for the unfortunate acronym....
Was just looking at "Structured Text" in the PLC world ...... It's BASIC! OK, they use := (the colon makes it a real programming language, I guess ).
It looks more like Pascal than Basic .... or maybe I should say modern Basics have changed to look more like Pascal. The old interactive simplicity has long gone.
Ok, replacing the
with
helped a lot. The output looks clean and consistent now:
So I guess I was the first person who tried out calling C functions from a Spin main program? Well... kind of my fault (although I don't feel really guilty). As there are no clear instructions of how this is supposed to be done I just tried it out the same way as I did as if I #include external C libraries into other C code, and that means with header files. And C is well known to be susceptible for alle those cases where changing a single character can completely mess up everything...
I'll have to check if everything else works, now, if I put the 64 bit calculations back in. Thanks for the support, @ersmith !
No, but you may be the first one to use a .h file instead of a .c file, or at least a function-only .h file. There's a compiler bug where the Spin object created for a .h file ends up with the wrong size if the variables are not declared in the .h (basically the object size is calculated too early, before all functions are resolved; since in B2M_Axes.h there are no variables, the Spin object is created with 0 bytes reserved for member variables).
This is actually going to be a tricky bug to fix, but in the current github source code there is at least a check for this and an error is printed.
Thanks for finding this.
Regards,
Eric
Yeah, I know what you mean! I have hated C my whole life and things like the above problems make me hate it more and more every day. I thought I have to use it for portability reasons. But portability turns out to be nothing but whishful thinking. If we develop embedded systems and need real time performance that means it has to be closely coupled to the hardware and nothing is really portable. I've lost two full days now and finally found out that changing a "h" to a "c" fixes it. I could have re-written my whole program in a different language in those two days. And I think it would have been a lot better for my blood pressure and hair color.
So Basic supports structured data types (classes)? I've seen it also supports 64 bit integers. But no inline assembler. And I think I would miss a source level debugger. I want my Oberon IDE back.