Playing around with a software PWM implementation a bit, I noticed that this piece of code
#include "ez80f92.h"
#define PWM_PORT_DR IO(PC_DR)
#define MAX_PWM_CHANNELS 8
#define PWM_TIMER_FREQ_HZ 50
#define PWM_RESOLUTION 256
static uint8_t pwm_flat[PWM_RESOLUTION * MAX_PWM_CHANNELS];
static volatile uint8_t inv_pwm_mask = 0xff;
static uint8_t pwm_ctr;
__attribute__((interrupt))
void PRT1_Handler(void)
{
IO(TMR1_CTL); // clear interrupt flag
// OR 8 channels
uint8_t bits =
pwm_flat[pwm_ctr * MAX_PWM_CHANNELS + 0] |
pwm_flat[pwm_ctr * MAX_PWM_CHANNELS + 1] |
pwm_flat[pwm_ctr * MAX_PWM_CHANNELS + 2] |
pwm_flat[pwm_ctr * MAX_PWM_CHANNELS + 3] |
pwm_flat[pwm_ctr * MAX_PWM_CHANNELS + 4] |
pwm_flat[pwm_ctr * MAX_PWM_CHANNELS + 5] |
pwm_flat[pwm_ctr * MAX_PWM_CHANNELS + 6] |
pwm_flat[pwm_ctr * MAX_PWM_CHANNELS + 7];
// write port
PWM_PORT_DR = (PWM_PORT_DR & inv_pwm_mask) | (bits);
// advance pointer and counter
pwm_ctr++;
}
Was translated to a piece of code where the multiplication by 8 (to get the base pointer essentially) was done by a call to the __ishl (integer shift left) function, even though the multiplicant "8" (or shift amount: 3) is statically known
000004cc <__Z12PRT1_Handlerv>:
4cc: d9 exx
4cd: 08 ex af,af'
4ce: fd e5 push iy
4d0: fd 21 c4 00 04 ld iy,0x400c4
4d5: ed 38 83 in0 a,(0x83)
4d8: 3a c4 08 04 ld a,(0x408c4)
4dc: b7 or a,a
4dd: ed 62 sbc hl,hl
4df: 6f ld l,a
4e0: 0e 03 ld c,0x03
4e2: cd 19 08 00 call 0x0819
4e6: e5 push hl
4e7: d1 pop de
4e8: fd 19 add iy,de
4ea: fd 7e 01 ld a,(iy+1)
4ed: fd b6 00 or a,(iy+0)
4f0: fd b6 02 or a,(iy+2)
4f3: fd b6 03 or a,(iy+3)
..
00000819 <__ishl>:
819: cb 41 bit 0,c
81b: 28 01 jr z,0x081e
81d: 29 add hl,hl
0000081e <.bit0>:
81e: cb 49 bit 1,c
820: 28 02 jr z,0x0824
822: 29 add hl,hl
823: 29 add hl,hl
00000824 <.bit1>:
824: cb 51 bit 2,c
826: 28 04 jr z,0x082c
828: 29 add hl,hl
829: 29 add hl,hl
82a: 29 add hl,hl
82b: 29 add hl,hl
0000082c <.bit2>:
82c: cb 59 bit 3,c
82e: 20 06 jr nz,0x0836
830: cb 61 bit 4,c
832: c8 ret z
00000833 <__ishl_16>:
833: 65 ld h,l
834: 2e 00 ld l,0x00
00000836 <__ishl_8>:
836: 29 add hl,hl
837: 29 add hl,hl
838: 29 add hl,hl
839: 29 add hl,hl
83a: 29 add hl,hl
83b: 29 add hl,hl
83c: 29 add hl,hl
83d: 29 add hl,hl
83e: c9 ret
This was a bit sad to see as I would have loved it to either use the builtin SLA r or an inline of the 8 ADD instructions or just one MULT instruction.
I think the cause of this is that it cannot perform link time optimization of the generated code because LTO is disabled during compilation and linking staging (maybe also "whole program optimization"?).
While it is easy to generate LTO enabled objects with clang through -flto, the GNU linker (ez80-none-elf-ld) then of course doesn't know what to do with these objects and fails.
ez80-none-elf-ld -o .pio\build\agonlight2\firmware.elf -T C:\Users\Max\.platformio\packages\framework-arduinoez80\variants\agonlight2\ldscript.ld --no-warn-rwx-segments -nostdlib --gc-sections -defsym=EXTRAM_START=0x40000 -defsym=EXTRAM_SIZE=0x80000 -defsym=INTRAM_START=0xb7e000 -defsym=INTRAM_SIZE=0x2000 -defsym=FLASH_SIZE=0x20000 .pio\build\agonlight2\src\main.cpp.o -L.pio\build\agonlight2 .pio\build\agonlight2\libFrameworkArduinoVariant.a .pio\build\agonlight2\libFrameworkArduino.a
.pio\build\agonlight2\src\main.cpp.o: file not recognized: file format not recognized
I don't understand the linker well enough but there are plugin capabilities so maybe I'm just not building or passing the right plugin for it to do LTO. Or, I need to use a different linker all together to produce the ELF file, like the llvm linker tools (?).
Playing around with a software PWM implementation a bit, I noticed that this piece of code
Was translated to a piece of code where the multiplication by 8 (to get the base pointer essentially) was done by a call to the __ishl (integer shift left) function, even though the multiplicant "8" (or shift amount: 3) is statically known
This was a bit sad to see as I would have loved it to either use the builtin
SLA ror an inline of the 8ADDinstructions or just oneMULTinstruction.I think the cause of this is that it cannot perform link time optimization of the generated code because LTO is disabled during compilation and linking staging (maybe also "whole program optimization"?).
While it is easy to generate LTO enabled objects with clang through
-flto, the GNU linker (ez80-none-elf-ld) then of course doesn't know what to do with these objects and fails.I don't understand the linker well enough but there are plugin capabilities so maybe I'm just not building or passing the right plugin for it to do LTO. Or, I need to use a different linker all together to produce the ELF file, like the llvm linker tools (?).