Direct Port Manipulation, Arduino 101. Incomplete pins_arduino.h?

Good Evening,

I'm attempting to do some direct port manipulation on the Arduino 101, but the definitions for port registers (PORTB, PINB, etc) are missing from the pins_arduino.h found in /User/AppData/Local/Arduino15/packages/Intel/hardware/arc32/1.0.5/variants/arduino_101. It's also missing functions like digitalPinToInterrupt(), and as such, trying to compile for the 101 using the above gives "PINB not declared in this scope" errors.

The 101 looks a lot like an UNO board - I tried making a backup of the pins_arduino.h included with the 101 core, and copying over the pins_arduino.h from the UNO folder, then tried to compile again, but this was unsuccessful and I don't really know enough to know why. (edit2: this didn't work because Curie != Atmel) I'm not averse to figuring stuff out on my own, but it seems with the 101 being so new, there's a lot of stuff still under-documented.

I've attached the 101's pins_arduino.h as well as the one from the /avr/variants/standard folder. I have changed the filenames to distinguish them.

edit: In the interest of avoiding an xy problem; I'm doing some high speed pulse counting, which at first sounds like a good job for hardware interrupts, but I don't want all the interrupt overhead.

Essentially I need to look at the bits for two pins, and with a switch case for 01, 10, 11 increment a couple counters. This is something like 10 or 12 instructions total, so it seems a huge waste to blow 100+ cycles just handling interrupts. I'd rather loop around as fast as possible, just checking those bits. I'd expected to do this with PINB and bitmasking, but pins_arduino.h for core 1.0.5 doesn't have those macros yet.

pins_arduino_101.h (1.69 KB)

pins_arduino_standard.h (5.85 KB)

Is the version of the IDE you are using compatible with the board you are using?

PaulS:
Is the version of the IDE you are using compatible with the board you are using?

Thanks for your reply Paul. Yes, I am using IDE 1.6.8 with core 1.0.5 for Intel installed. I have successfully compiled and run several "example" sketches (like Blink) and a few of my own with no issues. In fact, the task I described in the edit to my post is successfully completed using hardware interrupts, I just want to do the job faster by reading the pin registers directly.

Direct port writes are CPU specific. The Intel CPU (or one of the Atmel ARM CPUs) does not have registers called PINB or PORTB or whatever... You can look at wiring_digital.c to see how it's done (but it's complicated on Curie. Involves SS_GPIO_SWPORTA_DR and such...)

For the newer chips, you need to look at variants//variant. as well as variants/*/pins_arduino.h (but, still complicated!)
(this is what digitalWrite() is such a good thing...)

westfw:
Direct port writes are CPU specific. The Intel CPU (or one of the Atmel ARM CPUs) does not have registers called PINB or PORTB or whatever... You can look at wiring_digital.c to see how it's done (but it's complicated on Curie. Involves SS_GPIO_SWPORTA_DR and such...)

For the newer chips, you need to look at variants//variant. as well as variants/*/pins_arduino.h (but, still complicated!)
(this is what digitalWrite() is such a good thing...)

Thank you for the reply, westfw. Indeed, I started digging around in wiring_digital.c a bit this morning, figuring (as you pointed out) that somewhere in there was the thing I'm looking for. wiring_digital.c includes Arduino.h and portable.h, so I've got those open as well.

In Arduino.h we have the following struct defined:

typedef struct _PinDescription
{
        uint32_t                ulGPIOId;               // GPIO port pin
        uint32_t                ulGPIOPort;             // GPIO port ID
        uint32_t                ulGPIOType;             // LMT or SS
        uint32_t                ulGPIOBase;             // GPIO register base address
        uint32_t                ulSocPin;               // SoC pin number
        uint32_t                ulPinMode;              // Current SoC pin mux mode
        uint32_t                ulPwmChan;              // PWM channel
        uint32_t                ulPwmScale;             // PWM frequency scaler
        uint32_t                ulAdcChan;              // ADC channel
        uint32_t                ulInputMode;            // Pin mode
} PinDescription;

Which we store in, what I think is, an array of structs:

/* Pins table to be instanciated into variant.cpp */
extern PinDescription g_APinDescription[] ;

Function calls inside wiring_digital.c create a pointer to the struct for whichever pin number was passed. Example: digitalRead()

int digitalRead( uint8_t pin )
{
    PinDescription *p = &g_APinDescription[pin];

    if (pin >= NUM_DIGITAL_PINS) return LOW;

    if (p->ulGPIOType == SS_GPIO)
    {
        uint32_t reg = p->ulGPIOBase + SS_GPIO_EXT_PORTA;
        if (READ_ARC_REG(reg) & (1 << p->ulGPIOId)) return HIGH;
    }
    else if (p->ulGPIOType == SOC_GPIO)
    {
        uint32_t reg = p->ulGPIOBase + SOC_GPIO_EXT_PORTA;
        if (MMIO_REG_VAL(reg) & (1 << p->ulGPIOId)) return HIGH;
    }

    return LOW;
}

The macro READ_ARC_REG() is defined in portable.h thusly:

#define READ_ARC_REG(reg) \
    __builtin_arc_lr((volatile uint32_t)(reg))

All very promising! The comments in Arduino.h make me think that array of structs (pin table) is being populated in variant.cpp. If I knew the address space for the struct array, and the offset SS_GPIO_SWPORTA_DDR, can't I just look at the bit myself, assuming I carefully step through the address space? Is it correct to assume that, since it is an array, the allocated memory must be contiguous?

I really appreciate your help, I hope this issue is interesting to you! (And sorry about the whitespace in the code blocks, I couldn't figure out how to get rid of it!)

promiscuous recursive grep to the rescue?

...system/libarc32_arduino101/common/scss_registers.h: #define SOC_GPIO_SWPORTA_DR 0x00
...system/libarc32_arduino101/common/scss_registers.h: #define SS_GPIO_SWPORTA_DR 0x00

/* ARC GPIOs */
#define SS_GPIO_8B0_BASE_ADDR           0x80017800
#define SS_GPIO_8B1_BASE_ADDR           0x80017900
#define SS_GPIO_SWPORTA_DR              0x00
#define SS_GPIO_SWPORTA_DDR             0x01
#define SS_GPIO_INTEN                   0x03
#define SS_GPIO_INTMASK                 0x04
#define SS_GPIO_INTTYPE_LEVEL           0x05
#define SS_GPIO_INT_POLARITY            0x06
#define SS_GPIO_INTSTATUS               0x07
#define SS_GPIO_DEBOUNCE                0x08
#define SS_GPIO_PORTA_EOI               0x09
#define SS_GPIO_EXT_PORTA               0x0a
#define SS_GPIO_LS_SYNC                 0x0b
typedef struct _PinDescription

The _PinDescription structure/array is an Arduino thing that the arduino core uses to translate from arduino "pin number" to everything else that the core might need. (Rather verbosely - all those uint32s designating two bits... somewhat smaller structures are used on chips with smaller memories.)

variant.cpp has the summary diagram:

/*
 * Arduino 101
 *  Board pin      |     GPIO     | Label
 * ----------------+--------------+-------
 *   0             |  GPIO_SS[9]  | "RX0"
 *   1             |  GPIO_SS[8]  | "TX0"
 *   2             |  GPIO[18]    | ""
 *   3             |  GPIO_SS[10] | "PWM0"
 *   4             |  GPIO[19]    | ""
 *   5             |  GPIO_SS[11] | "PWM1"
 *   6             |  GPIO_SS[12] | "PWM2"
 *   7             |  GPIO[20]    | ""
 *   8             |  GPIO[16]    | ""
 *   9             |  GPIO_SS[13] | "PWM3"
 *  10             |  GPIO[11]    | ""
 *  11             |  GPIO[10]    | "MOSI"
 *  12             |  GPIO[9]     | "MISO"
 *  13             |  GPIO[8]     | "SCK"
 *  14             |  GPIO_SS[2]  | "A0"
 *  15             |  GPIO_SS[3]  | "A1"
 *  16             |  GPIO_SS[4]  | "A2"
 *  17             |  GPIO_SS[5]  | "A3"
 *  18             |  GPIO_SS[6]  | "A4"
 *  19             |  GPIO_SS[1]  | "A5"
 *  20             |  GPIO_SS[0]  | "ATN"
*/

But I don't really understand what the difference between a GPIO and a GPIO_SS is...

It would presumably be easier if Intel published the datasheet without an NDA, or got around to open-sourcing the RTOS that the ARC talks to. Both of which are supposed to happen "any day now", according to previous statements from various places. (along with availability of bare Quark modules.) ("March 2016", "first quarter 2016.") Frankly, there's a rather limited amount of effort I'm willing to spend working on a product when the manufacturer won't show me the documentation. :frowning:

^^^ Great stuff!! I'm going to start playing with this today. I'll set a pin as input in the regular way and then just put a 50% duty cycle 3.3v on it and start poking bits until I find the ones I want.

This is not the first time I have heard someone mention that the datasheet requires an NDA - who/which department at Intel would I contact to submit a request for the datasheet? I'm happy to sign an NDA and it's for academic research so it's not like I'm going to go out and profit off this or something. :slight_smile:

I have also been looking at a port of the OneWire library for ARC. The functions are fairly concise as written, so it might be a nice compromise between digitalRead and bit manipulation. Here's a link if you're interested: Port OneWire to Atlas Edge board · arduino/ArduinoCore-arc32@4cf2499 · GitHub

edit: sent off an email to maker-info@intel.com this morning, let's see what happens!

This is not the first time I have heard someone mention that the datasheet requires an NDA - who/which department at Intel would I contact to submit a request for the datasheet? I'm happy to sign an NDA and it's for academic research so it's not like I'm going to go out and profit off this or something. :)

What happened to me was that I followed links for some datasheet, and when I finally got to the download link it said "You have to register to be able to download that. (register here)" So I registered, and then the link said "no, you need a premium registration with NDA (upgrade registration here)", at which point I gave up.

That's not the way it's behaving now; I can't even find any "data sheet" links for "Curie" or "Quark SE", and most of the other documents I tried are downloading without my being logged in.

Should I mention that I'm really NOT liking the Intel Website? Grr. Lots of gloss, hard to find real information :frowning:

So any news? Did you succeed? :slight_smile:

I got direct port reading working. here is example code:

uint32_t ioReg1=SS_GPIO_8B1_BASE_ADDR+SS_GPIO_EXT_PORTA;
uint32_t ioReg2=SOC_GPIO_BASE_ADDR+SOC_GPIO_EXT_PORTA;
uint32_t ioRegA=SS_GPIO_8B0_BASE_ADDR+SS_GPIO_EXT_PORTA;

#define readPin0 (__builtin_arc_lr(ioReg1)&0b00000010)
#define readPin1 (__builtin_arc_lr(ioReg1)&0b00000001)
#define readPin3 (__builtin_arc_lr(ioReg1)&0b00000100)
#define readPin5 (__builtin_arc_lr(ioReg1)&0b00001000)
#define readPin6 (__builtin_arc_lr(ioReg1)&0b00010000)
#define readPin8 (__builtin_arc_lr(ioReg1)&0b01000000)
#define readPin9 (__builtin_arc_lr(ioReg1)&0b00100000)

#define readPinA5 (__builtin_arc_lr(ioRegA)&0b00000010)
#define readPinA4 (__builtin_arc_lr(ioRegA)&0b01000000)
#define readPinA3 (__builtin_arc_lr(ioRegA)&0b00100000)
#define readPinA2 (__builtin_arc_lr(ioRegA)&0b00010000)
#define readPinA1 (__builtin_arc_lr(ioRegA)&0b00001000)
#define readPinA0 (__builtin_arc_lr(ioRegA)&0b00000100)

#define readPin2 (MMIO_REG_VAL(ioReg2)&0b1000000000000000000)
#define readPin4 (MMIO_REG_VAL(ioReg2)&0b10000000000000000000)
#define readPin7 (MMIO_REG_VAL(ioReg2)&0b100000000000000000000)
#define readPin10 (MMIO_REG_VAL(ioReg2)&0b100000000000)
#define readPin11 (MMIO_REG_VAL(ioReg2)&0b10000000000)
#define readPin12 (MMIO_REG_VAL(ioReg2)&0b1000000000)
#define readPin13 (MMIO_REG_VAL(ioReg2)&0b100000000)

void setup() {
Serial.begin(115200);

for(int i=0;i<14;i++)
pinMode(i,INPUT_PULLUP);

pinMode(A0,INPUT_PULLUP);
pinMode(A1,INPUT_PULLUP);
pinMode(A2,INPUT_PULLUP);
pinMode(A3,INPUT_PULLUP);
pinMode(A4,INPUT_PULLUP);
pinMode(A5,INPUT_PULLUP);
}

void loop() {
Serial.print(readPin0!=0);
Serial.print(readPin1!=0);
Serial.print(readPin2!=0);
Serial.print(readPin3!=0);
Serial.print(" ");
Serial.print(readPin4!=0);
Serial.print(readPin5!=0);
Serial.print(readPin6!=0);
Serial.print(readPin7!=0);
Serial.print(" ");
Serial.print(readPin8!=0);
Serial.print(readPin9!=0);
Serial.print(readPin10!=0);
Serial.print(readPin11!=0);
Serial.print(" ");
Serial.print(readPin12!=0);
Serial.print(readPin13!=0);
Serial.println();
delay(20);

}

I got direct port reading working

 delay(20);

Cute.

it was strictly for demonstration purposes so others can use it for reference ya smarty pants =p