I'm going through how FIQ works on ARM and came across the statement that FIQ should always be written in assembly not in C but couldn't understand why?
I have gone through the following link
http://comments.gmane.org/gmane.linux.ports.arm.kernel/14004
But still couldn't make out why is it required?
Can any one please point me out the need of writing FIQ in assembly through some example?
My guess is based on this:
Also, it's a little difficult to write the FIQ code in C, since you lack a stack :)
If there's no stack, that would mean that the compiler is restricted to only using registers for all variables, which I'm not sure how you'd even express.
You can put register on all the local variables, but that doesn't mean that the compiler has to comply.
Writing the code in assembly of course goes around this restriction, and makes it possible to combine registers and global state to do things.
See also this question's answers for more about the difference between an ordinary interrupt and a fast one.
Because what is the point, you are using an extra bank of registers to save a handful of clock cycles in saving the state, then to use C and completely blow that tiny cost savings? If you are not interested in optimizing to that level then dont bother with fiq just use irq.
Related
I apologize if this should sound trivial and unsubtle but I couldn't figure out an intuitive way to google it, Why are some kernel actions like saving the current state of the registers and the stack(just to mention a few) written in Assembly? Why can't they be written in C because after all, presumably, when compilation is done, all we get is object code? Besides when you use ollydbg, you notice that before a function call(in C), the current state of the register is pushed to the stack
When writing an OS the main goal is to maintain the highest abstraction to make the code reusable on different architectures, but at the end inevitably there is the architecture.
Each machine performs the very low level functions in such a specialized way that no general programming language can sustain.
Task switching, bus control, device interrupt handling, just to name few, cannot be coded efficiently using an high level language (consider instruction sequences, involved registers, and eventual critical CPU timings and priority levels).
On the other hand, it is not even convenient to use mixed programming, i.e. inline assembler, because the crafted module will be no more abstract, containing specific architecture code that can't be reused.
The common solution is to write all code following the highest abstraction level, reducing to a few modules the specialized code. These routines, fully written in assembly, are normally well defined in terms of supplied input and expected output, so the programmer can produce same results on different architectures.
Compiling for different CPU is then done by simply switching the set of assembly routines.
C does not assure you that it modifies the registers you need to modify.
C just implements a logic you write in your code and the interpretation given by the language will be as you expect, hiding completely the details behind the interpretation.
If you want a kind of logic like set the X register with a given value or move data from register X to register Y, as it's necessary to do in kernel sometimes, this kind of logic is not defined by the C language.
C is a generic high level language, not specific to one target. But at the kernel level there are things that you need to do that are target specific that the C language simply cannot do. Enabling an interrupt or configuring an MMU or configuring something to do with the protection mechanism. On some targets these items and others are configured using registers in the address space but on some targets specific assembly language instructions are required and so C cannot be used, it has to be assembly. There is usually at least one thing you have to use assembly for per target if not many.
Sometimes it is a simple case of wanting the correct instruction to be used for example a 32 bit store must be used for some operation to insure that and not hope the compiler gets it right then use asm.
There is no C equivalent for "return from exception". The C compiler can't translate everything that assembly can do. For instance, if you write an operating system you will need a special return function in the interrupt service routine that goes back to where the interrupt was initiated and the C compiler can't translate such a functionality, it can only be expressed in assembly.
See also Is assembly strictly required to make the "lowest" part of an operating system?
Context switching is critical and need to be really really fast which should not be written in high level language.
I have a code that works really fine with an -O1 optimization, but that crashes if I don't optimize the code. The last lines that are executing are the following :
OSCCTRL_CRITICAL_SECTION_ENTER();
((Oscctrl *)hw)->DFLLCTRL.reg = data;
If I put a breakpoint on this last line, and then go to the next instruction, then the debugger will lose track of the execution pointer.
This code is called as part of the chip initialization which is the following succession of functions :
void _init_chip(void)
{
hri_nvmctrl_set_CTRLB_RWS_bf(NVMCTRL, CONF_NVM_WAIT_STATE);
_set_performance_level(2);
OSC32KCTRL->RTCCTRL.bit.RTCSEL = 0x4;
_osc32kctrl_init_sources();
_oscctrl_init_sources();
_mclk_init();
_gclk_init_generators();
_oscctrl_init_referenced_generators();
}
The buggy line is called by the _oscctrl_init_referenced_generators(); line.
I would like to know the differences between optimized and non-optimized code, and if you guys any known issues with non-optimized embedded code.
I am developping on a SAML21J18B MCU, embedding a Cortex-M0+ CPU.
I'm going a different direction than the other answer and the comments. Looking at your code, it looks like you're playing with the oscillator control, and so I'm thinking that you are not using the correct process for configuring or adjusting the oscillator.
Depending on what you are trying to do, you may need to switch to a different clock before adjusting oscillator parameters, and by breaking and stepping, you may be losing your clock. When you don't optimize, there are probably some extra instructions that are causing the same result.
Consult the part's reference manual and make sure you're doing everything correctly. For this line of thinking, though, your question needs more of the code in that area and the model of microcontroller (not just the core type).
The most obvious effect of optimizations will be the debuggers ability to display execution state. The act of debugging can interfere with program execution. Specifically for this chip certain oscillator configurations can cause problems.
The debugger is probably not your problem however. If you step into _oscctrl_init_referenced_generators(); you will likely find that one of your configured oscillators is not starting and that the code is waiting for the DFLL or FDPLL to obtain a stable frequency lock. There can be a variety of reasons for this. Check that the upstream oscillators are configured and running.
In short, the difference is that the optimization depending on its type may simplify some code constructions, as well as change the location of the data in the memory. Thus, in most cases such a behavior signals about the code design is not made well. Most typical reasons are the use of non-initialized variables, hanging pointers, out of boundary access or the having similar issues. Thus, you should avoid code constructions which depend on assumptions which might become wrong due to optimization. Depending on the compiler and optimization level, the use of volatile might also help in some cases.
Also, if you perform at least a tight code review + static code analysis, and ensure there are no compiller warnings the behavior should remain the same independently from optimization.
I'm currently trying to implement interrupts on the STM32L152. I'm not using the standard peripheral libraries because I find them very confusing and difficult to get my head around. I'm not too competent with C for micro controllers yet.
I currently do everything through registers. Is there a way to implement interrupts in C through registers? There doesn't seem to be any information that actually makes sense out there. I did find learning C to be very inaccessible in the first place tbh.
Thanks
Of course you can implement interrupts by setting registers.
The registers-values tell the STM how to deal with interrupts, which interrupt is enabled, how the interrupt-controller has to work.
You'll need an Interrupt-vector-table. When an interrupt occurs a the program-counter will be set to an specific address of program-memory. There you should place a jump-command (assembler jmp) to your interrupt-service-routine.
You should read chapter 10 in this reference manual.
Hope this helps.
Following on from my other question, Help optimising this C (AVR) code? :), I just realised that I don't really know the mechanics of how to implement an ISR in assembly within my C program. I googled, but I didn't find anything too useful.
Basically I want to set up everything else in my C project (including the registers to get the interrupt to fire), but I want the interrupt to execute some assembly code that I've written.
I'm using AVR studio 6 on Windows if that helps. I've been told I need to use .s files but apart from that I'm not really sure what to do. Is the basic structure just the following?
#include <avr\io.h>
TIMER1_COMPA_vect:
; assembly instructions here
If I want the assembly to run when the TIMER1_COMPA_vect interrupt is fired (this is the name of the ISR in C). Or am I totally off track? What is a basic template of what I need to do? Or if it's a dumb question, is there a link where I can find more information?
The Art of Assembly Language (by Randall Hyde) has two chapters about ISRs (17th and 18th, specifically sections 18.5 and 18.6 might help you). Shortly:
Go to the IVT (interrupt vector table) and modify it with your ISR segment and offset, saving the old values
You should do a TSR (terminate and stay resident) program, so that your ISR stays resident into memory even when the user closes the window
Remember to call the old ISR after you're done with the work (this is called interrupt chaining)
your ISR should be re-entrant (so that if the interrupt is fired again when your ISR is still running the pc won't explode ;) )
By the way, you can obtain a pdf copy of that great book here
I am a firm believer in the idea that one of the most important things you get from learning a new language is not how to use a new language, but the knowledge of concepts that you get from it. I am not asking how important or useful you think Assembly is, nor do I care if I never use it in any of my real projects.
What I want to know is what concepts of Assembly do you think are most important for any general programmer to know? It doesn't have to be directly related to Assembly - it can also be something that you feel the typical programmer who spends all their time in higher-level languages would not understand or takes for granted, such as the CPU cache.
Register allocation and management
Assembly gives you a very good idea of how many variables (machine-word-sized integers) the CPU can juggle simultaneously. If you can break your loops down so that they involve only a few temporary variables, they'll all fit in registers. If not, your loop will run slowly as things get swapped out to memory.
This has really helped me with my C coding. I try to make all loops tight and simple, with as little spaghetti as possible.
x86 is dumb
Learning several assembly languages has made me realize how lame the x86 instruction set is. Variable-length instructions? Hard-to-predict timing? Non-orthogonal addressing modes? Ugh.
The world would be better if we all ran MIPS, I think, or even ARM or PowerPC :-) Or rather, if Intel/AMD took their semiconductor expertise and used it to make multi-core, ultra-fast, ultra-cheap MIPS processors instead of x86 processors with all of those redeeming qualities.
I think assembly language can teach you lots of little things, as well as a few big concepts.
I'll list a few things I can think of here, but there is no substitute for going and learning and using both x86 and a RISC instruction set.
You probably think that integer operations are fastest. If you want to find an integer square root of an integer (i.e. floor(sqrt(i))) it's best to use an integer-only approximation routine, right?
Nah. The math coprocessor (on x86 that is) has a fsqrt instruction. Converting to float, taking the square root, and converting to int again is faster than an all-integers algorithm.
Then there are things like accessing memory that you can follow, but not properly apprecatiate, until you've delved into assembly. Say you had a linked list, and the first element in the list contains a variable that you will need to access frequently. The list is reordered rarely. Well, each time you need to access that variable, you need to load the pointer to the first element in the list, then using that, load the variable (assuming you can't keep the address of the variable in a register between uses). If you instead stored the variable outside of the list, you only need a single load operation.
Of course saving a couple of cycles here and there is usually not important these days. But if you plan on writing code that needs to be fast, this kind of knowledge can be applied both with inline assembly and generally in other languages.
How about calling conventions? (Some assemblers take care of this for you - Real Programmers don't use those.) Does the caller or callee clean up the stack? Do you even use the stack? You can pass values in registers - but due to the funny x86 instruction set, it's better to pass certain things in certain registers. And which registers will be preserved? One thing C compilers can't really optimise by themselves is calls.
There are little tricks like PUSHing a return address and then JMPing into a procedure; when the procedure returns it will go to the PUSHed address. This departure from the usual way of thinking about function calls is another one of those "states of enlightenment". If you were ever to design a programming language with innovative features, you ought to know about funny things that the hardware is capable of.
A knowledge of assembly language teaches you architecture-specific things about computer security. How you might exploit buffer overflows, or break into kernel mode, and how to prevent such attacks.
Then there's the ubercoolness of self-modifying code, and as a related issue, mechanisms for things such as relocations and applying patches to code (this needs investigation of machine code as well).
But all these things need the right sort of mind. If you're the sort of person who can put
while(x--)
{
...
}
to good use once you learn what it does, but would find it difficult to work out what it does by yourself, then assembly language is probably a waste of your time.
It's good to know assembly language in order to gain a better appreciation for how the computer works "under the hood," and it helps when you are debugging something and all the debugger can give you is an assembly code listing, which at least gives you fighting chance of figuring out what the problem might be. However, trying to apply low-level knowledge to high-level programming languages, such as trying to take advantage of how the CPU caches instructions and then writing wonky high-level code to force the compiler to produce super-efficient machine code, is probably a sign that you are trying to micro-optimize. In most cases, it's usually better not to try to outsmart the compiler, unless you need the performance gain, in which case, you might as well write those bits in assembly anyway.
So, it's good to know assembly for the sake of better understanding of how things work, but the knowledge gained is not necessarily directly applicable to how you write code in high-level languages. On that note, however, I found that learning how function calls work at the assembly-code level (learning about the stack and related registers, learning about how parameters are passed on the stack, learning how automatic storage works, etc.) made it a lot easier to understand problems I had in higher-level code, such as "out of stack space" errors and "invalid calling convention" errors.
The most important concept is SIMD, and creative use of it. Proper use of SIMD can give enormous performance benefits in a massive variety of applications ranging from everything from string processing to video manipulation to matrix math. This is where you can get over 10x performance boosts over pure C code--this is why assembly is still useful beyond mere debugging.
Some examples from the project I work on (all numbers are clock cycle counts on a Core 2):
Inverse 8x8 H.264 DCT (frequency transform):
c: 1332
mmx: 187
sse2: 127
8x8 Chroma motion compensation (bilinear interpolation filter):
c: 639
mmx: 144
sse2: 110
ssse3: 79
4 16x16 Sum of Absolute Difference operations (motion search):
c: 3948
mmx: 278
sse2: 231
ssse3: 215
(yes, that's right--over 18x faster than C!)
Mean squared error of a 16x16 block:
c: 1013
mmx: 193
sse2: 131
Variance of a 16x16 block:
c: 783
mmx: 171
sse2: 106
Memory, registers, jumps, loops, shifts and the various operations one can perform in assembler. I don't miss the days of debugging my assembly language class programs - they were painful! - but it certainly gave me a good foundation.
We forget (or never knew, perhaps) that all this fancy-pants stuff that we use today (and that I love!) boils down to all this stuff in the end.
Now, we can certainly have a productive and lucrative career without knowing assembler, but I think these concepts are good to know.
I would say that learning recursion and loops in assembly has taught me alot. It made me understand the underlying concept of how the compiler/interpreter of the language i'm using pushes things onto a stack, and pops them off as it needs them. I also learned how to exploit the infamous stack overflow. (which is still surprisingly easy in C with some get- and put- commands).
Other than using asm in every-day situations, i don't think that i would use any of the concepts assembly taught me.
I would say that addressing modes are extremely important.
My alma mater took that to an extreme, and because x86 didn't have enough of them, we studied everything on a simulator of PDP11 that must have had at least 7 of them that I remember. In retrospect, that was a good choice.
timing
fast execution:
parallel processing
simple instructions
lookup tables
branch prediction, pipelining
fast to slow access to storage:
registers
cache, and various levels of cache
memory heap and stack
virtual memory
external I/O
Nowadays, x86 asm is not a direct line to the guts of the CPU, but more of an API. The assembler opcodes you write are themselves are compiled into a completely different instruction-set, rearranged, rewritten, fixed-up and generally mangled beyond recognition.
So it's not like learning assembler gives you a fundamental insight into what's going on inside the CPU. IMHO, more important than learning assembler is to get a good understanding of how the target CPU and the memory hierarchy works.
This series of articles covers the latter topic pretty thoroughly.