Same C code, different assembly

Arduino on the STM8 (8 bit processor)
Post Reply
Jimmus
Posts: 16
Joined: Tue Nov 28, 2017 4:08 am

Same C code, different assembly

Post by Jimmus » Mon Jan 15, 2018 6:27 am

I really like the AutoWakeUp feature of the STM8. So I want to use it as a delay() substitute, and it's working quite well. Basically I set the timeout in the AWU registers and enable the interrupt, and every time I do a halt() instruction it does a sleeping delay(). Which is pretty slick. But I'm seeing an inconsistency in the assembly code that it generates. I found that if I do direct register manipulation instead of pinMode() and digitalWrite() it saves me quite a bit of program space, and is remarkably efficient. But I'm seeing weirdness. The exact same line of C code is being converted differently. Specifically bitSet(GPIOB->ODR, 5); which is basically digitalWrite(PB5, HIGH);

I'm using the sdcc compiler on a STM8S003F3P6 minimum system development board. It's the sduino core, STM8S103F3 breakout board option in the IDE.

Code: Select all

// For the STM8 board.
// ledPin is same as pin 3, PB5.  Notice that the LED is inverted.  LOW = on.
// Once I enable the AutoWakeUp interrupt and set the timeout, each halt() is basically a delay()

#define ledPin PB5

const byte messageString[] = "  Hello, World!  ";

const byte MorseCodeArray[] =
{
  0, 0, 0x52, 0, 0, 0, 0, 0x5E, 0x6D, 0x6D, 0, 0, 0x73, 0x61, 0x55, 0x32,
  0x3F, 0x2F, 0x27, 0x23, 0x21, 0x20, 0x30, 0x38, 0x3C, 0x3E, 0x78, 0, 0, 0, 0, 0x4C,
  0, 5, 0x18, 0x1A, 0xC, 2, 0x12, 0xE, 0x10, 4, 0x17, 0xD, 0x14, 7, 6, 0xF,
  0x16, 0x1D, 0xA, 8, 3, 9, 0x11, 0xB, 0x19, 0x1B, 0x1C, 0, 0, 0, 0, 0,
  0, 5, 0x18, 0x1A, 0xC, 2, 0x12, 0xE, 0x10, 4, 0x17, 0xD, 0x14, 7, 6, 0xF,
  0x16, 0x1D, 0xA, 8, 3, 9, 0x11, 0xB, 0x19, 0x1B, 0x1C, 0, 0, 0, 0, 0
};

void setup()
{
  __critical
  {
    bitSet(GPIOB->CR1, 5);        // pinMode(ledPin, OUTPUT_FAST);
    bitSet(GPIOB->DDR, 5);
    bitSet(GPIOB->CR2, 5);
  }
  bitSet(GPIOB->ODR, 5);          // Turn off LED  ***<<<===    This line   ***
  CLK->ICKR |= 0x20;              // REGAH.  Shut down power regulator when asleep
  FLASH->CR1 |= 8;                // Power down flash when in Active Halt mode
  AWU->APR = 62;                  // Set up to wake up in like 1 second
  AWU->CSR = 0x10;                // Enable AWU interrupt
}

void BlinkDot()
{
  bitClear(GPIOB->ODR, 5);        // Turn on LED
  halt();
  bitSet(GPIOB->ODR, 5);          // Turn off LED   ***<<<===   and This line   ***
  halt();
}

void BlinkDash()
{
  bitClear(GPIOB->ODR, 5);        // Turn on LED
  halt();
  halt();
  halt();
  bitSet(GPIOB->ODR, 5);          // Turn off LED
  halt();
}

void EndOfLetter()
{
  halt();
  halt();
}

void EndOfWord()
{
  halt();
  halt();
  halt();
  halt();
}

void BlinkLetterCode(byte LetterCode)
{
  if (LetterCode > 1)
  {
    BlinkLetterCode(LetterCode >> 1);
    if (LetterCode & 1)
      BlinkDash();
    else
      BlinkDot();
  }
  else
    EndOfLetter();
}

void loop()
{
  int i;
  char ch;

  AWU->TBR = 9;                   // like 125 milliSeconds
  for (i = 0;; ++i)
  {
    ch = messageString[i];
    if (ch == 0)
      break;
    if (ch == ' ')
      EndOfWord();
    else if (ch > ' ' && ch <= 0x7F)
    {
      ch = MorseCodeArray[ch - 0x20];
      BlinkLetterCode(ch);
    }
  }
  AWU->TBR = 14;                  // like 4 Seconds
  halt();
}

void AWU_IRQHandler(void) __interrupt(ITC_IRQ_AWU)
{
  AWU->CSR;     // Reading AWU_CSR1 register clears the interrupt flag.
}
And the assembly:

Code: Select all

	.area CODE
;	/home/...HelloWorldBlink.ino: 19: void setup()
;	-----------------------------------------
;	 function setup
;	-----------------------------------------
_setup:
;	/home//...HelloWorldBlink.ino: 26: }
	sim
;	/home/.../HelloWorldBlink.ino: 23: bitSet(GPIOB->CR1, 5);        // pinMode(ledPin, OUTPUT_FAST);
	bset	20488, #5
;	/home/.../HelloWorldBlink.ino: 24: bitSet(GPIOB->DDR, 5);
	bset	20487, #5
;	/home/.../HelloWorldBlink.ino: 25: bitSet(GPIOB->CR2, 5);
	bset	20489, #5
	rim
;	/home/.../HelloWorldBlink.ino: 27: bitSet(GPIOB->ODR, 5);          // Turn off LED   ***<<<===   and This line   ***
	bset	20485, #5
;	/home/.../HelloWorldBlink.ino: 28: CLK->ICKR |= 0x20;              // REGAH.  Shut down power regulator when asleep
	bset	20672, #5
;	/home/.../HelloWorldBlink.ino: 29: FLASH->CR1 |= 8;                // Power down flash when in Active Halt mode
	bset	20570, #3
;	/home/.../HelloWorldBlink.ino: 30: AWU->APR = 62;                  // Set up to wake up in like 1 second
	mov	0x50f1+0, #0x3e
;	/home/.../HelloWorldBlink.ino: 31: AWU->CSR = 0x10;                // Enable AWU interrupt
	mov	0x50f0+0, #0x10
;	/home/.../HelloWorldBlink.ino: 32: }
	ret
;	/home/.../HelloWorldBlink.ino: 34: void BlinkDot()
;	-----------------------------------------
;	 function BlinkDot
;	-----------------------------------------
_BlinkDot:
;	/home/.../HelloWorldBlink.ino: 36: bitClear(GPIOB->ODR, 5);        // Turn on LED
	ld	a, 0x5005
	and	a, #0xdf
	ld	0x5005, a
;	/home/.../HelloWorldBlink.ino: 37: halt();
	halt
;	/home/.../HelloWorldBlink.ino: 38: bitSet(GPIOB->ODR, 5);          // Turn off LED   ***<<<===   and This line   ***
	ld	a, 0x5005
	or	a, #0x20
	ld	0x5005, a
;	/home/.../HelloWorldBlink.ino: 39: halt();
	halt
;	/home/.../HelloWorldBlink.ino: 40: }
	ret
I rather prefer the first way. Why is it different, and more importantly, how can I get it to do the shorter version all of the time?

tenbaht
Posts: 12
Joined: Wed Oct 11, 2017 3:10 pm

Re: Same C code, different assembly

Post by tenbaht » Mon Jan 15, 2018 10:04 am

The halt() is actually #defined as __asm__("halt"). The problem is, that inline assembly disables the internal optimizer of the compiler for the command before, as it can't know for sure if the values of this command are needed for the inline assember commands.

This is not a bug, but an intentional feature of SDCC: https://sourceforge.net/p/sdcc/bugs/2623/
https://github.com/tenbaht/sduino - Programming the STM8 the Arduino way

dannyf
Posts: 230
Joined: Wed May 11, 2016 4:29 pm

Re: Same C code, different assembly

Post by dannyf » Mon Jan 15, 2018 12:31 pm

The exact same line of C code is being converted differently.
get a different compiler.

IAR:

Code: Select all

     23          		IO_SET(LED_PORT, LED);				//set led
   \                     ??main_0:
   \   00001F 7210 500F    BSET      L:0x500f, #0x0
     24          		halt();
   \   000023 8E           HALT
     25          		IO_CLR(LED_PORT, LED);				//clear led
   \   000024 7211 500F    BRES      L:0x500f, #0x0
     26          		halt();						//halt the mcu
   \   000028 8E           HALT
   
I'm sure Cosmic does the same.

edit: it does.

Code: Select all

2822  0000               L1002:
2823                     ; 11 		IO_SET(LED_PORT, LED);
2825  0000 7210500f      	bset	_PD_ODR,#0
2826                     ; 12 		halt();
2829  0004 8e            halt
2831                     ; 13 		IO_CLR(LED_PORT, LED);
2833  0005 7211500f      	bres	_PD_ODR,#0
2834                     ; 14 		halt();
2837  0009 8e            halt

Jimmus
Posts: 16
Joined: Tue Nov 28, 2017 4:08 am

Re: Same C code, different assembly

Post by Jimmus » Mon Jan 15, 2018 10:50 pm

Thanks, you two, for your help. You answered both my questions, and that's exactly what I needed.

I tried the iar compiler, but as far as I could find it's a Windows compiler, and although it works with Wine -- kinda, it's more than I'm willing to deal with today.

So I'm left with either ignoring the inefficiency (which isn't all THAT bad. It works OK, just a little bit bigger and slower) or building my own macro that converts it to an __asm__ statement. Which also isn't that bad.

Or I guess I could make my own version of sdcc. It is open source, after all, and that can't be too difficult a change.

Options...

dannyf
Posts: 230
Joined: Wed May 11, 2016 4:29 pm

Re: Same C code, different assembly

Post by dannyf » Tue Jan 16, 2018 1:35 am

Options...
Options are expensive, for a reason, :)

One of the biggest issues with STM8 is the lack of available compilers. When I tried SDCC out back then, it wasn't ready for prime time. Looks like they have made some progress since but not enough.

IAR and Cosmic both are excellent but unfortunately they aren't available on non-Windows platforms.

One solution, as you pointed out, is to run windows VMs on those machines - that is how most people develop MCU applications on non-windows platforms, by my causal observations.

Post Reply