Search the Community
Showing results for tags 'xc8'.
-
I have a fairly general question for you all. I keep on running into situations where I need to mix C and ASM. Sometimes this works out easily by just using some asm("") instructions in the middle of my function, but sometimes I really feel like I could benefit from writing a function in ASM and calling it from C. I think my best example of this is the implementation of cryptographic functions such as AES or SHA. For these I see sometimes a 2x or even 3x speed improvement over what the compiler produces and I need to use these from more than one place so I really need a C function I can call to do these but I reallly need to implement it in ASM. Whenever I ask about mixing C and ASM I am told just not to do it, but it still seems to me that there are a lot of situations where this really is the best way to go? I recall a converstation with @holdmybeer where he needed very precise timing and the optimizer would always change the timing depending how the banks ended up being laid out (adding or removing bank switches), where implementing a function in ASM also seemed to be the solution. So I would like to get some opinions on this, do you guys agree? Any thoughts on this? PS. We recently had a related discussion about calculating parity as another example.
-
The secret weapon of the 8-bit PICmcu
N9WXU posted a blog entry in What every embedded programmer should know about ...
The PICmcu is not known for being fast or supporting large memories but it does have one feature that can significantly simplify developing your applications. That feature is the lack of a hardware call stack. Sacrilege you say! But wait... The lack of this important feature has caused the compiler team to develop an incredibly useful alternative that I would argue is better in nearly every way than an actual stack. For those of you who are now wondering what I am talking about, let's take a quick diversion into stacks. A stack is simply a data structure that arranges a number of data elements so that the last thing you inserted is the first thing you get back. Think of a stack of plates, you add plates to the top of the stack and remove them in reverse order from the top of the stack. This is important for a few reasons. First, imagine your code was interrupt by a hardware interrupt. The current address of your code is pushed onto the stack, the interrupt runs and your address is popped from the stack so the interrupt can return where it interrupt you. This is a handy feature and for "free" you can handle any number of interruptions. In fact, a function can call itself and so long as there is sufficient room on the stack, everything will be sorted out on the return. Now, the PICmcu's have hardware stacks for the the function calls and returns. They simply don't have any hardware support for anything else. If the hardware stack is so useful for keeping track of return addresses, it would also be useful for all the parameters your function will need and the values your functions will return. This parameter stack is an important feature of most languages and most especially the C language. The AVR, ARM, 68000, IA86, Z80, VAX11, all have instruction level support for implementing a parameter stack for each function. I have written millions of lines of C code for the PIC16 so how does it do its job without this important part of the language and why do I think this missing features is such a strong strength of the CPU. The secret to the ability of XC8 to produce reasonably efficient C code for the PIC16 and PIC18 without a stack lies in the "compiled stack" feature. This feature analyses the call tree of your program and determines what the stack would look like at any point in the program. Functions that could be in scope at the same time (consider a multiply function in "main" and the interrupt) are duplicated so there are no parameters that need to be in two places at the same time. Any recursive functions are detected and the user alerted. Finally, the complete stack is converted to absolute addresses and mapped into the physical memory of the CPU. Then all the instructions are fixed up with those absolute addresses. This big-picture view of the program also allows the compiler to move parameters around to minimize banking (banking is extra instructions required to reach addresses further than 128 bytes away) and finally, the finished program is ready to run in your application. The amazing thing is the final memory report is the complete memory requirements of the program INCLUDING THE LOCAL VARIABLES. This is a shocker. In fact, when I was looking at the Arduino forums,. I would frequently encounter users who added one more line of code and suddenly their application stopped working. They were told to go buy the bigger CPU. Imagine if your compiler could tell you if the program would fit and that would include all the stack operations. This is a game changer and I would love to see the industry apply this technology across all CPU's. There is no reason why any CPU would not be able to operate with a compiled stack. In fact, most CPU's are capable of operating with both kinds of stacks at the same time. The biggest reason against this sort of operation is really in large project management. Consider, you develop a library for some special feature, perhaps GPS token parsing, Now you want to include this library in all of your applications. You MUST NOT recompile this code because it has passed all of your certification testing and is known good "validated" code. (remember, the compiler is permitted to produce a different binary on each run). If you cannot recompile the code, on some architectures you cannot change the addresses as there could be side effects (banking on a PIC16). If your code only relies upon a stack, then there is never any recompiling required and the linker task is dramatically simplified. Anyway, I must go back into the FreeRTOS world and continue my quest to find the thread with the overflowing stack. Life would be simpler with static analysis tools for the stack. Until next time., -
We found that we have quite a lot to say about programming embedded systems and this lounge is not quite flexible enough for us to do this, so we decided to start a community blog area. We have posted the first blog post as a test here https://www.microforum.cc/blogs/entry/1-floating-point-numbers/ and the landing page for the blog collection is here https://www.microforum.cc/blogs/blog/1-what-every-embedded-programmer-should-know-about/. We are calling this one "What every embedded programmer should know about ..." and it will feature a series of posts along this topic. If there is any topic close to your heart that you would like us to cover please leave a comment and we will see if we can make it happen. As a start we will allow moderators to create and post blogs, if you want to make a contribution please contact one of the moderators and we should be able to hook you up. We have been toying with the idea to allow members to blog once they reach a certain level in the community, but since we are just starting out that will not be practical right now, let us know what you think, we may "upgrade" the system like that sometimg in the future!
- 1 reply
-
- blog
- programming
-
(and 1 more)
Tagged with:
-
This is my attempt to convert the blazingly fast assembler routine for calculating parity into a C function. The original comes from here: https://www.microchip.com/forums/m4762.aspx Unfortunately Microchip have killed off the board where the original discussion took place at http://asp.microchip.com/webboard/wbpx.dll/~DevTools/read?21443,5 #include <xc.h> //returns 0x00 (even parity) or 0x80 (odd parity) unsigned char parity(volatile unsigned char dat) //("volatile" is required because no C code reads the parameter) { asm("swapf parity@dat,w"); //assume correct bank is still selected asm("xorwf parity@dat,w"); //W has 8 bits reduced to 4 with same parity asm("addlw 41h"); // bit 1 becomes B0^B1 and bit 7 becomes B6^B7 asm("iorlw 7Ch"); // for carry propagation from bit 1 to bit 7 asm("addlw 2"); // Done! the parity bit is bit 7 of W asm("andlw 80h"); // set NZ if odd parity, and leave 00 or 80 in W asm("return"); return 1; //dummy instruction to defeat "no return value" error } void main(void) { unsigned char idx=0; while(1) { PORTA = parity(idx); idx++; } } I'm not sure if there's a cleaner way to suppress the "no return value" error, without generating extra code.
-
I am seeing weird behavior when I am using sprintf with my code, the code looks fine but it is printing junk! Here is an example program for a PIC16F18875 : #include <xc.h> #include <stdio.h> #include <stdint.h> char buffer[128]; void main(void) { uint32_t multiplier = 10; sprintf(buffer, "Value1 : %02d, Value2 : %02d, Value 3: %02d", 5*multiplier, 7*multiplier, 8*multiplier); NOP(); } The resulting string printed is this: Value1 : 50, Value2 : 00, Value 3: 70 Why is this happening?
-
I have a bunch of old projects using the @ method to hard locate variables at a specific location in data memory, but since I upgraded to XC8 2.0 none of these are working any more! How do I do this with the latest compiler? I am using XC8 v2.xx. I also saw this error at my interrupt service routine and I suspect that this is the same problem. My interrupt code looks like this void interrupt my_isr(void) { ... my code here } error: variable has incomplete type 'void'