Inline assembler with Due

Hi, looking for instruction how to use inline assembler within C++ code I found several examples for the Arduino 8 bit controllers. I want to use inline assembler on my Due for which there is a different compiler.

To the following example for an Atmega16u2 I have some questions:

#include <EEPROM.h>

int i;
byte countlow, counthigh, on, off;

  // inc duty cycle every 4096 cycles
  on = 10;
  off = 90;
  counthigh = 16;
  countlow = 0;
  EEAR = 0x01;				// EEPROM addres reg

  for (i = 0; i < 400000; i++) {
    digitalWrite(pwm, HIGH);
    delayMicroseconds(on);
    asm volatile (
      "dec %0 \n\t"    			// countlow -= 1
      "brne 1f \n\t"			// skip 1 if countlow != 0 
      "dec %1 \n\t"		    	// counthigh -= 1
      "1: \n\t"
      : "=r" (countlow), "=r" (counthigh)
      :
    );
    digitalWrite(pwm, LOW);
    delayMicroseconds(off);
    asm volatile (
      "lds r24, (counthigh) \n\t"    
      "brne 2f \n\t" 			// skip 7 if counthigh != 0
      "inc %1 \n\t"			    // on += 1
      "dec %0 \n\t"			    // off -= 1 (PWM duty cycle += 1)
      "ldi r24, 0x10 \n\t"
      "sts (counthigh), r24 \n\t"	// count = 4096 
      "out %[eedr], %1 \n\t"		// EEPROM data reg
//      "sbi EECR, "EEMPE"
//      "sbi EECR, EEPE"
      "2: \n\t"
      : "=r" (off), "=r" (on)
      : [eedr] "I" (_SFR_IO_ADDR(EEDR)
      : "r24"
    );
    }

There are reference to variables with %0 and %1, but according to the comments, e.g. %0 is referencing variable countlow and later variable off. I don't understand this.

Generally, can somebody recommend some literature about inline assembler on Due?

The operand references (%1 %0 etc) are defined after the assembly instructions. The documentation is a bit heavy reading, but after a while it is decipherable. Unfortunately most examples are for x86 code, but it is similar for other processors. For example
https://avrdudes.github.io/avr-libc/avr-libc-user-manual-2.2.0/inline_asm.html

Has some examples for AVR code.

I read through the mentioned documents and tried a simple example. It is derived from an example in "Inline Assembler Cookbook" with assembler statements for the SAM3X which is the controller on the Due board.

uint32_t getData(uint8_t* ptdata, uint8_t* ptend) {
  uint32_t st_value;  // vom EPM3064 eingelesenes Ergebnis
  do {
    // wait for Flag:
    REG_PIOC_SODR = 0x00800000;  // set TICPIN
    // do
    //   st_value = REG_PIOC_PDSR;
    // while ((st_value & 0x000000200) == 0);  // wait for Flag
    asm volatile (
      "PUSH R0, R1, R11 \n\t"
      "MOV  R0, #0 \n\t"
      "MOV  R11, %[PDSRPIOC] \n\t"    // address of PIOC_PDSR
      "1: \n\t"
      "LDR  R1, [R11, R0] \n\t"       // input PORTC to R1
      "LSLS R1, R1, #22"                // Flag is bit 9 of register R1
      "BPL 1f \n\t" 			// jump back to "1:" if Flag=0
      :
      : [PDSRPIOC] "I" (_SFR_IO_ADDR(PIOC_PDSR))
      :
    );
  ...

TICPIN is a special pin on the Due board which is set/reset by code to show operations in real time with an oscilloscope.

The three commented lines:

// do
    //   st_value = REG_PIOC_PDSR;
    // while ((st_value & 0x000000200) == 0);  // wait for Flag

shall be replaced by the following assembler statements.
Removing the commenting // and without assembler the code works fine.

The compiler is reporting errors:

  • error: 'PIOC_PDSR' was not declared in this scope
  • error: '_SFR_IO_ADDR' was not declared in this scope

My questions:

  1. what includes are necessary in order to use the macro _SFR_IO_ADDR() which is copied from an example in "Inline Assembler Cookbook"?
  2. where can I find the correct names for ports to be used in assembler, e.g. PIOC_PDSR?

It has been decades since I have done inline assembly and have never used a Due. I would start with the data sheet, particularly section 31. There are probably header files defined somewhere in the Atmel development system.

https://www.microchip.com/content/dam/mchp/documents/OTH/ProductDocuments/DataSheets/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf

I know that document. I took the register name PIOC_PDSR from there.

There are probably header files defined somewhere in the Atmel development system.

Probably, but where?

I can't get assembler running.
Now I tried a very simple test.

uint32_t getData(uint8_t* ptdata, uint8_t* ptend) {
  uint32_t st_value=0;

  do {
    asm volatile (
      "PUSH {R11} \n\t"
      "MOV  R11, %[PDSRPIOC] \n\t"    // address of PIOC_PDSR
      "STR R11, %0 \n\t"                 // [R0, #0] \n\t"
      "POP {R11}"
      : "=m" (st_value)
      : [PDSRPIOC] "r" (REG_PIOC_PDSR)       // (0x400e123c)
      : "cc", "memory", "r11"
    );

    Serial.print("st_value=0x");
    Serial.println(st_value, HEX); 
    delay(3000);
...
    

The assembler part shall load the address of register PIOC_PDSR to R11 and store it to the variable st_value.

Outside the assembler part st_value ist printed out as: st_value=0x0

The compiler accepts the assembler statement
"STR R11, %0 \n\t"
which is the syntax of 8 bit AVR assembler (example from my research). But code descriptions of Dues SAM3XA assembler tell me that store statements need to look like
STR Rd, [Rn, <offs>]
with e.g. #0.
This means register Rd will be stored to the address Rn+#0, i.e. I need the address of st_value.

In all the docs I read I couldn't find an example how to get the address of a variable. Has anybody an idea?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.