Computing > Programming

Crazyness: C and C++ mixing

<< < (13/14) > >>

It's FFPI: flash, peripherals, I/Os.
RAM is proportional flash.

Yes, it's more or less a standard AVR core but something traps loads above 0x8000 and redirects them to flash.

I wasn't aware of 2-series, it looks like they lose some peripherals for a better ADC.


--- Quote from: Nominal Animal on June 19, 2021, 10:28:48 am ---address magic in the hardware

--- End quote ---

if you implement a RISC softcore in FPGA you basically have the same problems and you will probably end up with the same solutions:

* "software bridge" dedicated instructions
* "hardware bridge" dedicated circuits, usually achieved with a dual port mapping
I call them "bridge" because they somehow connect two different spaces: code-space <==> data-space

Personally I prefer the first approach for safety reasons. In my case, all the "software bridge" instructions that need to write something are privileged, hence only the kernel can use them to reprogram (e.g. the bootloader), while reading a constant is always allowed.

I could achieve the same goal with the other approach, but it would require extra hardware like a circuit that checks if an address belongs to the allowed range for a given operating mode.

E.g. don't allow a task in user-space to write things in the range 0xE000.0000..0xf000.000, because there it's mapped the code-space, and only the kernel can write there.

It's doable, just it costs more hardware and effort.

Nominal Animal:
Yep.  A simplified summary about address spaces is that in C, both gcc and clang handle them just fine; and in C++, clang++ handles them just fine, but g++ does not.

And by "handle correctly", I mean the compiler will generate correct access instructions and place the variables and objects in the correct sections without having to use helper functions or macros for the accesses, as long as either the variable or the type includes the address space attribute.

It's not just older AVRs that have multiple address spaces, like DiTBho mentioned (about same in FPGA implementations); OpenCL and even x86-64 have them, too.
On x86-64, named address spaces are used to indicate specific segment register must be used; clang uses address_space(256) for GS, address_space(257) for FS, and address_space(258) for SS, and has clear rules on how these work through casts etc.  (gcc end uses __seg_fs and __seg_gs; g++ does not support them.)
The special segments are used for example for thread-specific data, as well as some very nifty kernel-userspace transparent data stuff.

Version 9 clang/LLVM language extensions lists these.  It looks like OpenCL drove the need for named address space support in clang; hopefully, GNU folks will change their mind and come along.  This is definitely useful.

So yesterday i was reading gcc documentation after digging for named address space. And i was trying to focus on how to build a .bin.
Well i guess gnu doc repository is not the place for such information.

For exemple, when i was playing with my riscV gd32 i found this blog post very interesting but sadly im not familiar with 99% of the content:
- linker script
- vector table
- boot

My thinking is "ok let's learn C++ freestanding" but it's like when i read the K&R long time ago, beside playing with language concept, it was difficult to apply to real programming (back in the time i was trying to code a demo on msdos) K&R is unable to teach you how to play with cpu/vga card/sound blaster card/ and so on.

I feel pretty the same now if i want to go the hard way. I can setup an esp-idf framework ready to go, but lots of work and pitfall are hidden. It's like with Arduino, it's very fast to code a temp sensor pushing data over wifi in few line of code. But when you want to play with a peripherial at full extent, you feel rapidly limited with such env.

So where is it possible to find the fundamentals of MCU development from how to start with an empty host os (which tool to install) an empty directory (which file are mandatory and for what) to produce a working firmware ?

Nominal Animal:

--- Quote from: netmonk on June 19, 2021, 01:08:58 pm ---I can setup an esp-idf framework ready to go, but lots of work and pitfall are hidden. It's like with Arduino, it's very fast to code a temp sensor pushing data over wifi in few line of code. But when you want to play with a peripherial at full extent, you feel rapidly limited with such env.
--- End quote ---
Very true.

That's why I've been mentioning the ATmega32u4 so often.  On one hand, it is simple and one can basically grasp all of it.  On the other hand, because it does have native USB support (12 Mbit/s), it can appear as whatever USB device you want, including USB HID devices like keyboards and joysticks and gamepads, as an USB Serial device, or even some native USB device (like slower USB audio devices – note the 12 Mbit/s limitation).

Starting from those (well, actually AT90USB1280 aka Teensy 2.0++, then 2.0, then to Teensy 3.0, a diversion back to ATmega32u4 via cheap Pro Micro clones using Arduino Leonardo bootloader, and so on), I could grow my understanding and capabilities step by step, without encountering a sheer vertical wall at any point.

You can even look at the intermediate files the Arduino environment generates, and explore the C runtime (crtmcu.o object files linked with Arduino code for each MCU), to see exactly how stuff gets implemented.

Most of the technical details I've mentioned in this thread are advanced stuff, in the hopes that you and others reading this thread will someday reach the point where they can make informed choices, and remember this thread and my points; and make stuff that Is Just Better than what we have now.
It is perfectly acceptable – and personally recommended by me – to start with the "easy" environments like Arduino, as getting stuff working gives that delicious boost of success and keeps ones motivation up; and while the environments are less than perfect, they are still quite usable, and very good for learning (as one quickly gets visible feedback, instead of just a laconic "Ok, that worked", and can play with stuff).  Just keep in mind that it is just a step along the ladder – optional, though; not everybody needs to create stuff for others, it is perfectly okay to just play with stuff for fun – towards better control and mastery of the subject domain.

--- Quote from: netmonk on June 19, 2021, 01:08:58 pm ---So where is it possible to find the fundamentals of MCU development from how to start with an empty host os (which tool to install) an empty directory (which file are mandatory and for what) to produce a working firmware ?
--- End quote ---
I wish I had a real answer to that!

I think –– but I seriously hope others will pipe in, because I'm not at all sure about this –– that the best approach is to start in the Arduino or PlatformIO environment with preferably a simple microcontroller, and learn to write code in this funky environment (while being aware of what are quirks of a particular environment, what is hardware-specific, and what is C or C++), and when you have something working (even a blinky one), in parallel start to examine the build process and intermediate files part by part.  If the microcontroller is suitable for bare metal development, then the "advanced" step would be to recreate something you did in the Arduino/PlatformIO environment in "bare metal".

I do definitely recommend getting development experience in Arduino/PlatformIO first with different microcontrollers and hardware types.  Not only is it different, but you'll find you yourself will shift paradigms based on the task at hand.  Something like an USB arcade controller with joystick and buttons is nice to do in freestanding C, but a complex configurable robot firmware with different compile-time selectable modules can be much easier to write using the C++ subset.  It is not just the language, but also how you go about looking for a way to implement something.

The best example I can think of is how the different display module libraries work.  Some of them have a full frame buffer, others only have a partial buffer, and you need to repeat the drawing commands (they only capture/rasterize to the current buffer slice) until the entire buffer has been generated and sent to the device.  So, for small memory devices you prefer to deal with the drawing primitives, and for larger memory devices you can operate on the entire frame buffer and can track changes (so that unnecessary parts are not updated, and the overall updates are faster – especially useful with eInk/e-paper displays).  It does not matter what language you use, but for efficient implementation, your approach will differ based on whether you expect to have SRAM for a full framebuffer or only a part of one.

Even though I think Arduino could be better, I still think it is a very good environment to learn, and to do interesting stuff and gadgets in.
For example, I designed a Pro Micro Gamepad so that you could solder a Pro Micro clone on top of the (bottom board) upside-down, add an 128×32 (0.91") I2C OLED display, and program the entire thing in the Arduino environment (treating it as an Arduino Leonardo there, but using the Pro Micro pinout to see how the hardware pins are actually connected to the gamepads).
There is absolutely no need to do this in bare metal; the Arduino version will work just fine.
(The idea is that the gamepad is actually a gamepad + keyboard combination, and generates keypresses when the buttons are pressed, so that one can play Online games with it using standard keyboard controls.  The two smaller tactile buttons are intended to switch between mappings, with the OLED display showing the currently selected mapping for a few seconds after the most recent small tactile button press.  It's a laughably simple design, so I designed another based on CH551g, but haven't built that yet either.)

--- Quote from: netmonk on June 19, 2021, 01:08:58 pm ---linker script
--- End quote ---
Linker script is a linker configuration file.  You can find these in various projects in a ldscripts directory.  The suffix determines the set of linker parameters used;

For both gcc and clang, you can specify a custom linker script using -Wl,-T -Wl,filename .

The default ones for avr-binutils are created by a shell script named in the binutils sources.

You can find a quick synopsis at OSDev Wiki, and full details at the binutils ld scripts documentation.

This is what determines where code starts, where the fuse bits are, where data will be put, and so on.

--- Quote from: netmonk on June 19, 2021, 01:08:58 pm ---vector table
--- End quote ---
Vector table is either an array of code addresses, or an array of jump instructions to code addresses.  When the microcontroller starts up, or a hardware interrupt occurs, it will use a specific entry in the vector table.

For ATmega32u4 (avr5), the vector table is in Flash, four bytes per entry (0x0c 0x94 0xLL 0xHH for a jump to address 0xHHLL), with the first (zeroth) one being the init/reset vector, that the MCU starts with when it powers on or resets.

--- Quote from: netmonk on June 19, 2021, 01:08:58 pm ---boot
--- End quote ---
When a microcontroller starts up or resets, it is in a very hardware specific state.  To make firmware upgrading easy, the very first code it executes is a bootloader.  Essentially, its purpose is to decide whether the microcontroller should expect a new firmware to be uploaded, or just jump to currently existing firmware.

Arduino uses its own bootloaders, so do Teensies.  There is an USB standard, Device Firmware Upgrade or DFU, that microcontrollers with native USB interfaces can support for updating the device firmware via USB.  In many cases, the bootloader will not replace itself, only the rest of the Flash contents, and to replace the bootloader, one needs to use the in-system programming interface (JTAG, for example) appropriate for the hardware.  While the bootloader is running, it can use the entire SRAM as it wishes, since once it hands control over to the existing firmware, the firmware will be in full control.  So, the only limited resource bootloaders consume, really is just the Flash they take up.

Therefore, "booting" in this context means the phase in microcontroller startup and reset sequences, where the "bootloader" code checks whether a new firmware should be uploaded to that microcontroller, or whether it should just hand over the reigns to the currently installed firmware.

Interestingly, the proprietary bootloader for Teensies, Halfkay, uses the HID protocol (and therefore needs no special OS drivers!), and the 8-bit AVR implementation (for Teensy 2.0 and 2.0++) takes less than half a kilobyte of Flash.  This made early Teensies so much nicer to develop on than e.g. Arduinos.


[0] Message Index

[#] Next page

[*] Previous page

There was an error while thanking
Go to full version