2022-03-04

This was the class time activity for day18

1. Check if code gets optimized away

Specific example: a while() loop waits for some external condition (pin state or some other input) and the code just sits in a "busy loop" doing nothing else besides checking for the variable to change.

1.1. Version A

Consider this block of code:

shortsquawker busyloop c

Compile this for the ATtiny85 using the ATTinyCore board package and the following settings:

attinycore settings

Get a copy of the .lst, disassembled code back to assembly with Sketch -> Export compiled Binary.

Search in that file attiny_shortsquawker.ino.lst for the corresponding code (I searched for PLLCSR) and see the following section:

shortsquawker busyloop lst

Line 934 says that the following assembly originates from the .ino line 126.

PLLCSR |= (1 << PLLE); gets translated to three instructions. This is called a "read, modify, write" and simply sets the PLLE bit in the PLLCSR register without changing any other of the register’s bits.

Listing lines 946—​952 show what got compiled for the busy loop that waits for the PLOCK bit to go high in the PLLCSR register.

  • Line 947, code at flash address 0x3a6, loads the PLLCSR register (at address 0x27) into register r0.

  • sbrs r0, 0 — "skip next if bit in I/O register is set"

    • Check if bit 0 (the lsb) is set. If yes, the break out of the loop by skipping the rjmp.

    • If the bit is still 0, then sbrs does not skip and the rjmp jumps the Program Counter to flash address 0x3a6, which is the start of the while() loop.

Conclusion: this time, the loop did not get optimized away and things work as you expect.

1.2. Version B

This is a version ported to C and being built using the ..._valpo2.zip version of the WinAVR portable distribution.

shortsquawker busyloop winavr c

Use the WinAVR tools to compile, link, and export a .lst file from this code.

The while condition is different (and has several bugs in one line!). It is kept here for demonstration purposes.
shortsquawker busyloop winavr lst

Listing lines 135—​138 show that the code to set the PLLE bit in the PLLCSR register is unsurprising. The only difference is the compiler’s choice of temporary register (r24 versus r0).

Listing lines 141—​145 show what happened with the while() loop.

  • At flash address 0x78 is an instruciton to read I/O register at address 0x27 (PLLCSR) into register r24.

  • Then there are no more assembly instructions! The _NOP() is from the C version but has no corresponding nop CPU instruction in assembly.

Conclusion: this particular version in fact optimizes the busy loop away.

Not quite completely away because Listing line 142 has an instruction to read PLLCSR into a register, but nothing is done about that value.

1.3. Version C

Fix the bugs in the while() in the WinAVR C version:

shortsquawker busyloop winavr cfix

Re-build the project:

shortsquawker busyloop winavr remake

and look at the new listing:

shortsquawker busyloop winavr lstfix

Pay attention to listing lines 151—​154. This exactly the same instructions for the "good" result from Version A.

The generated assembly is slightly longer because of the _NOP() that was in this version of the C code. So, the busy loop related assembly starts at listing line 139 at Flash address 0x78.

  • Immediate jump to address 0x7c which reads and checks the PLLCSR bit.

  • Same "skip if set" instruction.

  • rjmp jumps back farther to address 0x7a

  • which is a nop instruction, which has no effect on the CPU state (including the all-important SREG CPU status register bits).

  • Next instruction is back to the while() loop’s reading of the PLLCSR I/O register into a CPU register.

Conclusion: Don’t have bugs in your conditions! The compiler assumes you wrote correct code. The Version B code’s condition is always false (figure out why!) and therefore the compiler did the nothing that you asked to happen.

2. Error generating .lst

using WinAVR and a Makefile

Do you see this? :

winavr make lst error

Instead of this? :

winavr make lst error fix

The difference is the first version used the _valpo1.zip or earlier version of the WinAVR distribution.

The second screenshot was using the _valpo2.zip version.

The difference is a single file:

WinAVR-20100110\utils\bin\msys-1.0.dll

(Don’t ask why, it took 2+ hours to figure this out, confirm the fix, and generate a the new _valpo2.zip. Prof. White will not be thwarted by some random DLL bug from the 90’s ;)

3. Compiling asm / C / Cpp

What incantations are needed to create an object file from a source file in assembly, C, or Cpp into a HEX file for programming?

That day13 page has been modified for better navigation to emphasize the five steps that happen in building an Arduino sketch.

day13 § 1.2 is the beginning of the _non-Arduino specific parts of the process that are common to any GCC-based flow. Any other compiler will have a very similar flow, but with potentially different tool names and options.

The example code comes from day12, which used an .ino (translated to Cpp by the Arduino platform’s preprocessing), an assembly file asmOne.S holding the assembly implementation of a function called from the .ino, and a trivial C file foo.c to show how C compilation differs from Cpp.

4. Calling assembly from C

This was from class times

  • day10 — Introduction about how GCC uses registers, and where to find that type of information.

  • day11 — Demo by comparing .ino with resulting assembly in the .lst for functions of 1—​4 (char or int) arguments.

  • day12 — Demo of using assembly .S with the Arduino IDE, to create a function in assembly that is called from C (technically the .ino, which is Cpp).

Assembling with the GNU toolchain is a little different that avr_sim !

Two references that can help translate:

day15 - §3. avr-gcc references collects links to the major documentation for avr-gcc and related tools.

The tools are many in number, but that is on purpose according to the "Unix philosophy". Read more about that at