Wednesday, January 29, 2014

Printing the Linux kernel version in your kernel module

The kernel version that a kernel module is built with can differ from the kernel version it is running on. The version of the kernel that the module is running on is shown in the first log and the version of the kernel is compiled with is shown in the second log in the version_init function:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/utsname.h>
#include <generated/utsrelease.h>

static int __init version_init(void)
{
        int i = 0;

        pr_alert("Kernel module running of version %s\n", utsname()->release);
        pr_alert("Kernel module compiled with version %s\n", UTS_RELEASE);
        return 0;
}

static void __exit version_exit(void)
{
        pr_alert("Over and out!\n");
}

module_init(version_init);
module_exit(version_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("zeuzoix");
MODULE_DESCRIPTION("Kernel Module Version Example Module");

Wednesday, January 15, 2014

Caches

Let's talk about the cache and its requirement in a computing system. The word cache is derived from the french word cacher which means to hide.

In a nutshell it's a memory subsystem which improves the speed of execution of a thread by providing faster access to instructions and data based on their temporal and spacial characteristics in the program.
 
With memory there's typically a trade off between space and time. If speed is required then the storage capacity of the memory reduces particularly because the cost of the memory goes up with speed.

So system designers often incorporate a cache in between the processor and main memory which can significantly improve the speed at which memory access occurs.

The cache can be of different levels typically most processors these days have an inbuilt cache termed as L1 which is the smallest memory device in the hierarchy of memory in the system. Some processors like ARM Cortex's A series have an in built L2 cache also.

There are several popular designs for caches. The following videos give a very nice description of the different types of caches:

  1. Direct Mapped Cache
  2. Associative Cache
  3. Set Associative Cache

Friday, December 6, 2013

Linux Linked Lists

The linux kernel has a very peculiar implementation of the linked lists data structure. The linked list is actually a circular linked list. The following article is useful in understanding more about the design:
http://kernelnewbies.org/FAQ/LinkedLists

What's interesting is that the entire implementation is conveniently located in 
http://lxr.free-electrons.com/source/include/linux/list.h

 

Saturday, September 7, 2013

I2C/TWI with AVR Data Transfer


We now take a look at the TWI/I2C API responsible for data transfer. The header file twi.h has the prototype. We'll take a look at the arguments and their description now:
TWI_RET_E twi_send_recv_buffer(TWI_SEND_RECV_BUFFER_PARAMS_X *px_send_buffer_params,
                               TWI_MODE_E e_mode);
The first argument is a pointer to TWI_SEND_RECV_BUFFER_PARAMS_X defined as follows:


typedef struct _TWI_SEND_RECV_BUFFER_PARAMS_X
{
   uint8_t uc_dev_addr;
   uint8_t uc_dev_id;
   uint8_t *puc_reg_addr;
   uint32_t ui_reg_addr_sz;
   uint8_t *puc_data;
   uint32_t ui_data_sz;
    
}TWI_SEND_RECV_BUFFER_PARAMS_X;

Where
  1. uc_dev_addr is the higher nibble of the I2C slave device address
  2. uc_dev_id is the lower nibble of the I2C slave device address. It is used where the slave address can be configured
  3. puc_reg_addr is the pointer to the buffer holding the register address of the slave device.
  4. ui_reg_addr_sz is the size of the buffer with the register address.
  5. puc_data is the pointer to the data buffer
  6. ui_data_sz is the size of the data buffer.
The second argument is an enumeration which indicates if a send or receive operation is to be done
typedef enum _TWI_STATE_MODE_E
{
   eTWI_MODE_READ = 0,
   eTWI_MODE_WRITE,
   eTWI_MODE_INVALID

}TWI_MODE_E;

Saturday, August 31, 2013

I2C/TWI with AVR Register Intialization

We learned about the different registers available in the Atmel AVR microcontroller to program the TWI interface. In this section we'll go through the twi_init API and the initialization of the TWI interface.

The initialization code in the testbench.c initializes the TWI/I2C library as follows:
x_twi_init_params.e_mode = eTWI_INIT_MODE_16_0000M_100K_1;
x_twi_init_params.pfn_debug = testbench_debug;
e_twi_ret = twi_init(&x_twi_init_params);
if(eTWI_SUCCESS != e_twi_ret)
{
   i_ret = -1;
   goto LBL_INIT_IO_RET;
}

The API twi_init takes a structure variable x_twi_init_params of the type shown below.  The definition is in twi.h
typedef struct _TWI_INIT_PARAMS_X
{
   TWI_INIT_MODE_E e_mode;
   TWI_DEBUG_PFN pfn_debug;
 
}TWI_INIT_PARAMS_X;

The TWI interface can be initialized based on several modes each of which is given an identifier which signifies the CPU clock frequency, SCL clock speed and the pre-scaler value. For instance in the enumeration definition below eTWI_INIT_MODE_16_0000M_100K_1 is the mode for a 16.0000MHz micro-controller, with the desired SCL clock frequency set at 100KHz and the pre-scaler set to 1.
typedef enum _TWI_INIT_MODE_E
{
   eTWI_INIT_MODE_16_0000M_100K_1 = 0,
   eTWI_INIT_MODE_16_0000M_400K_1,
   eTWI_INIT_MODE_16_0000M_100K_4,
   eTWI_INIT_MODE_16_0000M_400K_4,
   eTWI_INIT_MODE_INVALID
 
}TWI_INIT_MODE_E;

Internally if we take a look at the twi_init function defined in twi.c we see a look up table is employed to initialize the5 registers TWBR, TWCR, TWSR, TWDR, TWAR. The look up table has the following structure for each row:

typedef struct _TWI_INIT_REG_X
{
   TWI_INIT_MODE_E e_mode;
   uint8_t uc_twbr;
   uint8_t uc_twcr;
   uint8_t uc_twsr;
   uint8_t uc_twdr;
   uint8_t uc_twar;
    
}TWI_INIT_REG_X;
The lookup table is a global array gxa_init_reg and is defined as follows:
static TWI_INIT_REG_X gxa_init_reg[] =
{
   {
      eTWI_INIT_MODE_16_0000M_100K_1,
      0x48,                               //0b01001000
      0x80,                               //0b10000000
      0x00,                               //0b00000000
      0x00,                               //0b00000000
      0x00                                //0b00000000
   }
};
The code in twi_init cycles through the array gxa_init_reg until it finds the correct mode and then initializes the 5 registers with the values associated with the mode.
for(uc_i = 0 ; uc_i < uc_size_arr ; uc_i++)
{
   if(px_init_params->e_mode == gxa_init_reg[uc_i].e_mode)
   {
      px_init_reg = &gxa_init_reg[uc_i];
      break;
   }
}
 
if(NULL == px_init_reg)
{
   if(NULL != px_init_params->pfn_debug)
   {
      px_init_params->pfn_debug("Can't find correct mode");
   }
    
   e_ret = eTWI_FAILURE;
   goto LBL_TWI_INIT_RET;
}
 
TWBR = px_init_reg->uc_twbr;
TWCR = px_init_reg->uc_twcr;
TWSR = px_init_reg->uc_twsr;
TWDR = px_init_reg->uc_twdr;
TWAR = px_init_reg->uc_twar;
In the next section we'll check out transmit and receive functionality on the TWI/I2C bus with I2C/TWI with AVR Data Transfer.

Thursday, August 29, 2013

I2C/TWI with AVR Register Descriptions

So you want to program the TWI/I2C interface of the AVR. Well you'll need to read up about the registers used to control this interface. Though the microcontroller uses two pins SDA and SDC to drive the bus there is a bunch of registers which are used to control the pins themselves. Let's take a look at them.

TWI Bit Rate Register (TWBR)


This register controls the SCL clock frequency which can either be 100Hz or 400Hz. 



The equation to set the correct SCL clock frequency is:



  1. CPUclockfreq is the CPU clock frequency(16MHz in my case).
  2. SCLfreq is the desired SCL clock frequency(100Hz in my case).
  3. TWBR is the value we store in the register.
  4. TWPS is the prescaler bits in the TWI status register TWSR which can take values from 0 to 3(0 in my case).

TWI Control Register (TWCR)

This register is used to control the operation of the TWI/I2C interface. Start or stop a transaction and acknowledge the receipt of a data packet.

Bit 7 - TWINT: Set when the TWI has finished it's task. Also used to initiate a transaction. This flag is normally cleared by writing a 1 to it. All other operations to the TWDR and TWAR must be done before this is cleared.

Bit 6 - TWEA: Controls generation of the acknowledge bit.
Bit 5 - TWSTA: Controls generation of a start bit.
Bit 4 - TWSTO: Controls generation of a stop bit.
Bit 3 - TWWC: This is a read only bit. It is set if you write TWDR when TWINT is low. It is reset if you write TWDR when TWINT is high.
Bit 2 - TWEN: This bit enables the TWI/I2C interface
Bit 0 - TWIE: This bit in conjunction with the I-bit in SREG enables the interrupt mode 

TWI Status Register (TWSR)

After every operation of the TWI interface this register is read to understand the status of the bus.

Bits 7:3 - TWS7: TWS3: Status of the last TWI/I2C transaction
Bits 1:0 : These are the pre-scaler bits used to modify the SCL frequency.

TWI Data Register (TWDR)  

This register is used to stores the bytes to be transmitted or the bytes received on the TWI bus.

Bits 7:0 - TWD7:TWD0: Data byte to be written to the TWI bus or read from the TWI bus.

TWI Address Register (TWAR)   

This register is loaded with the 7 bit Slave address to which it must respond to when programmed as a slave or when working in a multiple master mode. In the multiple master mode the device will compete to be a master and if it senses that it has lost the arbitration of the line it will have to turn into a slave and listen for a possible message from another master device.







Bits 7:1 - TWA6: TWA0: 7 bit slave address of the TWI interface.
Bit 0 - TWGCE: Indicates if the device will acknowledge a general call when sent. A general call is an address to all the slaves connected on the bus. The Slave address for a general call is usually all 0s.

In the next section I2C/TWI with AVR Register Intialization we'll go through the initialization code for the TWI/I2C interface and the values assigned to these registers.

Sunday, August 25, 2013

I2C/TWI with AVR Introduction

In this post we'll take a look at implementing an I2C/TWI library which can be used with Atmel's AVR micro-controller. The code has been uploaded to GitHub at https://github.com/zeuzoix/sardine and will be referenced here. The TWI library consists of the following files:
  1. twi.h
  2. twi.c
  3. twi_states.h
  4. twi_states.c
  5. twi_debug.h

There are two API which the application can use to interface with the I2C/TWI interface. The prototypes are in twi.h
   uint32_t ui_data_sz;
    
}TWI_SEND_RECV_BUFFER_PARAMS_X;
 
 
TWI_RET_E twi_init(TWI_INIT_PARAMS_X *px_init_params);
TWI_RET_E twi_send_recv_buffer(TWI_SEND_RECV_BUFFER_PARAMS_X *px_send_buffer_params,
                               TWI_MODE_E e_mode);
 
#endif

The first API twi_init is used to initialize the TWI interface in master mode by assigning a set of register values to the TWI registers in the AVR Atmel micro-controller.

The second API  twi_send_recv_buffer is used in both transmit and receive mode to send and receive data over the TWI SDA data line.


We'll take a look at the registers used in the programming of the TWI interface in the next section i.e.
I2C/TWI with AVR Register Descriptions.