Really high level (as in visual programming) is actually the most optimal way to do some of the embedded programming - in particular when it comes to the setup/configuring part.
Setting up the clock tree, configuring pins/port functions, enabling peripheral clocks, interrupts (which ones and their priority levels), fixing erratas, etc.
All of that should at the end of the day come down to a couple loads/stores to some memory mapped registers, and these registers are usually "wide" where they contain N bits to set N things - so if you design a C (or rust) api something like this:
enable_clock(int clock_id);
You wont get optimal code for:
enable_clock(FOO);
enable_clock(BAR);
Because if FOO and BAR are both enabled by setting bit 3 and 7 in register X you get two loads, two bitwise or's and two stores when a single store with a single constant would have sufficed.
So fire up a GUI program that lets you click on "enable FOO, enable BAR", "set main clock to crystal oscillator at N MHz, enable PLL x4, etc", have that generate optimal code and go from there.
Setting up the clock tree, configuring pins/port functions, enabling peripheral clocks, interrupts (which ones and their priority levels), fixing erratas, etc. All of that should at the end of the day come down to a couple loads/stores to some memory mapped registers, and these registers are usually "wide" where they contain N bits to set N things - so if you design a C (or rust) api something like this:
You wont get optimal code for: Because if FOO and BAR are both enabled by setting bit 3 and 7 in register X you get two loads, two bitwise or's and two stores when a single store with a single constant would have sufficed.So fire up a GUI program that lets you click on "enable FOO, enable BAR", "set main clock to crystal oscillator at N MHz, enable PLL x4, etc", have that generate optimal code and go from there.