-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GPIO PUT32 vs MMIO #1
Comments
I have a long list of reasons. First off have had compiler issues not generating the desired instruction. But more important is that is a good habit to go through an abstraction, the code is massively more re-usable. You can use the language to turn it back into pointers easily (with the put32/get32 as written) but you cannot trivially turn all of the pointers into an abstraction. With the abstraction (in general, I do not work for ST nor any MCU company) I can write code against a sim, so run the code on the dev machine connect the abstraction to a simulation (verilog, etc) on the same machine, or over a network, etc. Can link the code with a software simulation that fakes the hardware. Can use the abstraction to punch through operating system layers. Can use the abstraction to talk to a kernel level driver and have that talk to the target. Can put a debug layer in the abstraction. Can use the layer to trace accesses to certain registers for debug or other reasons. Writing the code one time and re-using it pre-silicon and post silicon, using it bare metal or on top of an operating system. If nothing else the bare metal code used early on in the development process and use it through the development of the hardware/system. And certainly down the road as a reference design to write a library that might resemble something closer to what you would see for whatever the final target is. If a kernel driver for example, you are going to want to use an abstraction for the kernel driver naturally, but might use a call more native to the operating system than PUT32/GET32. With respect to my online mcu examples, a major goal is to provide a minimal easy to read example, that has a higher chance of success for the reader, than the large sdk libraries that rely on a particular toolchain, paritcular C library and other libraries, etc. Grossly complicated linker scripts and other. The new FAD of relying on implementation defined areas of the language (including using structs across compile domains and misuse/misunderstanding of unions). Or even a simple mistake as you have used: #define GPCLR1 ((unsigned int *) 0x2020002C) That wont work in general it will fail. #define GPCLR1 ((volatile unsigned int *) 0x2020002C) Is what you wanted instead. Most important I control specifically the instruction I want used for the transaction, using an incredibly trivial and easy to read and understand way. Whose risks are minor and manageable (asm is not portable, calling conventions can technically change within a compiler family or from one compiler to another), compared to ghee whiz language tricks or worse areas of the language that are implementation defined. I am also in control of the code generated by the compiler where the pointer solution the compiler can see what you are doing and can order the instructions depending on the compiler, version, optimization, and change the timing of the code. For example when you enable the clock on a gpio there is a delay you must provide before WRITING the moder register, for reasons I dont quite understand you can read the register (even if the clocks are off and maybe that is just some logic hack). while the compiler can still optimize the code around the calls the are still serialized and forced to happen. Clang vs gcc for example have a different interpretation of the term volatile. Now if you go research that you find that it does not mean what you think it means, you should not rely on volatile for example to share a variable between an isr and application code in an mcu application. In theory compilers are supposed to make work for accessing control registers. But even in that case clang and gcc dont agree. In general avoid using volatile as much as possible in your code, wherever you use it you need to really evaluate why you need that workaround. You will see me use volatile in the risky situation, between an isr and application. And I accept that risk, perhaps I should go back and add that explanation to the readmes. I used it in msp430 code simply for efficiency that instruction set provides features that at the time I felt the volatile pointer risk was tolerable to get cleaner faster code for those applications. for arm in general Ill burn the extra clocks for more reliable/readable code. Avoiding the use of volatile, structs and unions to point across compile domains are very much intentional, to the point that I ask about them (what is wrong with this code) if you ever were to interview with me. Short answer I was taught this way but fought it and used volatile pointers for a while, improperly used structs and unions, until, as one should predict, bit me. Learned the lesson, as taught, used the specific instruction I desired for the transaction in this trivial and reliable way. Then once adopted found how greatly beneficial it was for day to day problems that come up. Didnt hurt that my first programming experience was on a vic-20 with poke and peek, so it was also natural in that way. |
I appreciate your repo, as it is very easy to understand and is helping me get started on bare metal for this plaform.
Forgive the question if i'm missing some fundamental information on this, but is there any reason why you're going through the hassle of setting the GPIO data through register access instead of using pointers to directly assign memory?
i.e.
#define GPCLR1 0x2020002C
PUT32(GPCLR1, 1<<(47-32));
instead of
#define GPCLR1 ((unsigned int *) 0x2020002C)
*GPCLR1 = 1<<(47-32);
The text was updated successfully, but these errors were encountered: