A curious case of missing interrupts: bit-fields vs bit-masks


Printed Circuit Board Assembly used for the project

While working with a PIC24F microcontroller, I came upon a situation where interrupts of UARTs 3 and 4 started disappearing. I reviewed my own code and even checked silicon errata for the MCU, but to no avail. Therefore, I turned my focus towards some third-party code I had incorporated in my project.

I found the problem in the USB host stack of Microchip Libraries for Applications (MLA) version 2013-06-15. The cause became apparent only with a disassembly listing, which I will use here for demonstration.

This is the original statement from the library, which clears the USB interrupt flag using a mask:

IFS5 &= 0xFFBF;

It was compiled by XC16 v1.24 at optimization level -O0 into this:

00365A 800475 MOV IFS5, W5
00365C 2FFBF4 MOV #0xFFBF, W4
00365E 628204 AND W5, W4, W4
003660 880474 MOV W4, IFS5

In this scenario, clearing the interrupt flag takes four instructions. This command sequence can unintentionally clear a flag if it is set by the hardware in-between the read and write of IFS5. The chances of this happening are not low because the USB interrupt occurs frequently, sometimes at 10,000 Hz. As it happens, the interrupt flags for UARTs 3 and 4 are present in IFS5, which solves the mystery behind this bug.

I fixed the problem by changing the original statement to the one below, which accesses the flag through a bit-field:

IFS5bits.USB1IF = 0;

This statement was compiled as:

00365A A9C08E BCLR IFS5, #6

In this case, the compiler has used an atomic bit clear instruction to clear the interrupt flag. IFS5, like other SFRs, is a bit-addressable register.

However, if I raise the compiler’s optimization level to -O1, even the original bit-mask statement is translated into a bit clear instruction.

The compiler appears to be acting inconsistently at -O0, as both the mask and bit-field C statements are logically equivalent. After searching the web as well as I could, I have found an explanation of this peculiar behavior. It is easier for the compiler to determine if a bit clear instruction can be used in case of a bit-field, as compared to a bit-mask. The use of BCLR is ultimately an optimization, albeit a very simple one.


Implementation of a DHCP client using AVR and W5100

In this article, we will take a look at an implementation of DHCP using Wiznet’s W5100 chip. The code is available for download. The TCP/IP stack is hardwired in the W5100 chip. We will only implement an incomplete version of DHCP. It will make the code relatively short so that it won’t be heavy on the microcontroller’s memory. It also keeps the code simpler and easier to understand. It is sufficient for most hobbyist and student projects.

Wiznet provides a library for implementing DHCP.  Some code has been taken from the library for our purpose. The code runs upon another library (called the driver) provided by Wiznet. It provides a BSD standard access to the W5100’s TCP/IP stack. The driver first needs to be configured for your particular microcontroller. It is assumed that you have already done that.

Detailed information about DHCP (Dynamic Host Configuration Protocol) is available in RFC 2131 and RFC 2132.

The code has been written for Atmel’s ATmega16 AVR microcontroller. The functionality has been implemented in a single function which is meant to be called only once during the program. It returns 1 on success and 0 on failure.

The working of the program is explained in the following flowchart:

Flowchart for incomplete DHCP client program

Flowchart for the DHCP client program

The code is available for download in two files. The file dhcp.h contains macro definitions and declarations. The file dhcp.c contains the definition of our function.

Click here to download the code

Communicating with W5100 via SPI

The purpose of this article is to explain the process of communication between a microcontroller and Wiznet’s W5100 networking chip. Of the three methods of interfacing with W5100, Serial Peripheral Interface (SPI) is the method of choice at least for all non-commercial work. Basic knowledge of SPI is assumed. The code is written for AVR ATmega16.

The first thing to do is to initialize the SPI.

#define DDR_SS_PORT     DDRD
#define SS_PORT         PORTD
#define SS_PIN          PD2
#define DDR_SPI_PORT    DDRB
#define SPI_PORT        PORTB
#define MISO_PIN        PB6
#define MOSI_PIN        PB5
#define SCK_PIN         PB7

void SpiInit(void)
    DDR_SS_PORT |= (1<<SS_PIN);                   // SS: Slave Select
    DDR_SPI_PORT |= (1<<MOSI_PIN) | (1<<SCK_PIN); // MOSI and SCK

    SPI_PORT |= (1<<MISO_PIN);       // Enabling pull-up register
    SS_PORT |= (1<<SS_PIN);          // Disabling slave

    // Enable, Master. Xtal/4. MSB first. Mode 0.
    SPCR |= (1<<SPE) | (1<<MSTR); } 

Now we come to the real part. Communication with W5100 occurs in 32-bit streams. Each stream takes 4 SPI cycles, one for each byte. The first byte is the opcode. There are only two operations: READ and WRITE. The second and third bytes are the address to the memory position in W5100 which is to be accessed. In case of a WRITE operation, the last byte indicates the value to be written. In case of a READ operation, the last byte has no purpose except to push the desired data out of W5100. The following function simply reads the value at the 16-bit address given in the parameter.

 uint8_t readW5100(uint16_t addr) { 	// Divide address into two bytes 	uint8_t addr0 = (uint8_t)(addr&0x0F); 	uint8_t addr1 = (uint8_t)((addr&0xF0)>>8);

	SS_PORT &= ~(1<<SS_PIN);      // Select slave

	SPDR = 0x0F;                  // 'Read' operation
	while(!(SPSR & (1<<SPIF)));

	SPDR = addr1;                 // Send address
	while(!(SPSR & (1<<SPIF)));
	SPDR = addr0;
	while(!(SPSR & (1<<SPIF)));

	SPDR = 0xFF;                  // Read data
	while(!(SPSR & (1<<SPIF)));

	SS_PORT |= (1<<SS_PIN);       // De-select slave 	return SPDR; } 

A similar function is used for writing the W5100 memory:

 void writeW5100(uint16_t addr, char data) { 	uint8_t addr0 = (uint8_t)(addr&0x0F); 	uint8_t addr1 = (uint8_t)((addr&0xF0)>>8);

	PORTD &= ~(1<<SS);          // Select slave

	SPDR = 0xF0;                // 'Write' operation
	while(!(SPSR & (1<<SPIF)));

	SPDR = addr1;               // Send address
	while(!(SPSR & (1<<SPIF)));
	SPDR = addr0;
	while(!(SPSR & (1<<SPIF)));

	SPDR = data;                // Send data
	while(!(SPSR & (1<<SPIF)));

	PORTD |= (1<<SS);

The motivation for this article comes from the experience that many of the libraries available for W5100 socket programming, including the one provided by Wiznet, do not contain definitions of the elementary functions that are discussed here. The reason given for this is the platform-dependent nature of the functions.

The code given in this post can be helpful for running W5100 from scratch without the use of any library. It can also be helpful for adapting a library to your system in order to make it functional.