# ATTiny414 inline assembly operand constraints for IO pin parameter

I'm trying to use the light_ws2812 library to drive WS2812 LEDs from an ATTiny414. The core of that library is an inline assembly snippet that bitbangs the serial line. here it is with the timing-nops stripped out:

void inline ws2812_sendarray_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi)
{
uint8_t sreg_prev;

ws2812_DDRREG |= maskhi; // Enable output

sreg_prev=SREG;
cli();

while (datlen--) {
curbyte=*data++;

asm volatile(
"       ldi   %0,8  \n\t"
"loop%=:            \n\t"
"       out   %2,%3 \n\t"    //  '1' [01] '0' [01] - re
"       sbrs  %1,7  \n\t"    //  '1' [03] '0' [02]
"       out   %2,%4 \n\t"    //  '1' [--] '0' [03] - fe-low
"       lsl   %1    \n\t"    //  '1' [04] '0' [04]
"       out   %2,%4 \n\t"    //  '1' [+1] '0' [+1] - fe-high

"       dec   %0    \n\t"    //  '1' [+2] '0' [+2]
"       brne  loop%=\n\t"    //  '1' [+3] '0' [+4]
:  "=&d" (ctr)
);
}

SREG=sreg_prev;
}


An obvious difference between the 'regular AVR' and the ATTiny*14 series C definitions is that the IO registers aren't called DDRx and PORTx but PORTx.DIR and PORTx. I already fixed that in the light_ws2812 header and it seems to work fine.

After this I am however getting an error with the assembly operand 2 ("I" (_SFR_IO_ADDR(ws2812_PORTREG))):

lib/light_ws2812.c: In function 'ws2812_sendarray_mask':
lib/light_ws2812.c:119:5: warning: asm operand 2 probably doesn't match constraints
asm volatile(
^~~
lib/light_ws2812.c:119:5: error: impossible constraint in 'asm'
make: *** [Makefile:28: lib/light_ws2812.o] Error 1


I also tried using a lowercase i as the constraint instead of the I, which changes the error to

/tmp/ccBR4JKE.s:48: Error: operand out of range: 1028


Looking at the datasheet this value makes sense, PORTA starts at 0x400 (1024) and the PORTx.OUT register has an additional offset of 4 bytes, placing PORTA.OUT at 0x404 = 1028.

with a 16-bit register constraint like x or w it compiles but then I get a linker error:

avr-ld: lib/light_ws2812.o: in function loop32':
light_ws2812.c:(.text+0x38): undefined reference to r30'


I am compiling this using avr-gcc and avr-ld, with a current avr-libc that I manually added the relevant files from the Atmel ATtiny Series Device Support package to.

• perhaps I should be using the VPORTA.OUT register instead? It starts at 0x0 so unlike PORTA.OUT it should fit into i... – sol Jul 26 '18 at 13:40

The ATTiny 1-series has the GPIO registers mapped outside of the io port register space, so they cannot be accessed using out or in. You will either need to use the VPORT registers, or load the address of the io port into a register and use an st instruction to write the data. If you decide to use a regular store instruction, it may change the timing of the loop so you may have to adjust the number of nops.
• I'm not sure about the tiny414, but the overall AVR arch defines st as taking 2 cycles, whereas out takes 1 cycle. – C_Elegans Jul 26 '18 at 13:59
• I think it would SBI and CBI. Since they are all single-cycle instructions I suppose there is no difference (except in that if there was a peripheral active we wouldn't/couldn't overwrite their values in the port register, but I'm not sure if that is actually a possible problem). I will check if this works as expected when I get home and accept the answer :) – sol Jul 26 '18 at 14:01
• okay, I couldn't get my code to work yet but thats likely unrelated to the question since the original code with the "I" constraint now works with VPORT*. – sol Jul 27 '18 at 12:56