Thursday, July 27, 2017

Race condition in multithreading: Why to use lock mechanism for shared memory ?

Race condition is race between the users to access some resource.

Example: 
Two thread with no synchronisation or no locking mechanism for variable access, can end up in race condition.

thread1:

if (x == 5) {
    y = x * 2;
}

thread2:
x = 6;

Here problem occurs when check condition in thread1 is done, and then thread2 occurs which chnage x, then the result in y can be different(12) than expected(10).

To solve this kind of problem, Memory lock mechanism, Thread synchronisation, Interprocess ommunication etc. concepts are used.

Volatile and Constant usage

volatile uint32_t x;  // tells compiler not to optimize this. stored on SRAM in global section
const uint32_t x;  // stored on ROM. Part of code memory. Value cant be changed.
volatile const uint32_t x;  // Value cant be changed from the code inside the file where it is declared but can be changed from outside the file.

volatile uint32_t * TCNT;
// TCNT is a pointer to register whose value can be changed outside the scope of program flow.
Read write register. Pointer value (i.e. address of register) can be changed.

const uint32_t * TCNT;
// TCNT is pointer to register whose value cant be changed using pointer dereferencing.
Readonly register. Pointer value (i.e. address of register) can be changed.

volatile uint32_t * const TCNT;
// TCNT is a pointer whose value cant be changed. It point to register whose value can get changed out of flow of program.
Read or Write register.  Pointer value (i.e. address of register) cannot be changed.

const uint32_t * volatile TCNT;
// TCNT is a pointer whose value can be get changed out of flow of code execution. It points to register whose value cant be changed through the code. Its constant and cant be changed by pointer deferencing.
Read only memory.

volatile const uint32_t * const TCNT;
// TCNT is a pointer whose value is constant. It points to register whose value is constant for code, but can get changed out of flow of program execution.
Read only register accessing.

Using pointers, when accessing
- Read only register, whose value is not going to be changed at all use
const uint32_t * ptr;
- Read only register, whose value can change outside the flow of execution
volatile const uint32_t * ptr
- Write register
uint32_t *ptr


Wednesday, July 26, 2017

Clarification on various terms of Embedded microcontrollers

Memory access Architectures:


  • Von-neumann:

Code and data memory on same memory address map
Only one set of address and data bus shared for code, data memory

  • Harvard

Code and data memory on separate memory address map
Two set of address and data bus, one for each memory
eg. 8051, PIC

  • Modified Harvard

1. Almost harvard

Code and data memory on separate memory address map.
But, instruction can be stored on data memory (like SRAM), or data can be stored on Code memory (like FLASH). For accessing data from code memory special instructions are provided.
eg. AVR8

2. Almost von-neumann

Code and data memory on same memory address map
But internal to CPU architecture there are separate instruction and data cache.
Depending on the coherency between the data and instruction cache there are two types:
     a. With coherence (eg. x86, x64)
     b. Without coherence (eg. ARM)
NOTE: Here in ARM architecture, there are possibility of problems with coherency. In order to avoid this the variables should be defined volatile as far as possible. This makes sure, after every operation the output is stored back to SRAM rather than just keeping it on cache and using it for next operation.

Interesting article for the explanation:
http://ithare.com/modified-harvard-architecture-clarifying-confusion/

CPU architecture:


  • 8051

8051is a harvard architecture. Good for small applications, but for big applications where data processing size requried is more. Number of vectored interrupts are less. Lack of memory management unit on chip.

  • PIC

Good for applications where lot of peripheral support required on silicon. It has very small stack memory.

  • AVR

Modified Harvard architecture for 8bit data processing. Good for small applications

  • ARM

Most advanced architecture with memory management, nest vectorred interrupt controller, debug and trace units with support for SWD and JTAG. Capable of operating at high frequencies.

Instruction sets:

  1. CISC - complex instruction set machine

Code size less
Pipelining cant be implemented
Time per instruction not the same.

  1. RISC - reduced instruction set machine

Code size more
Pipelining can be implemented
Time per instruction the same.
Instruction execution statictics are measured in DIPS(Dhrystone instructions per second) or MIPS (milion instruction per second)

Friday, July 21, 2017

Practical significance of Bitfield, union and Structure

/**
  \brief  Union type to access the Special-Purpose Program Status Registers (xPSR).
 */
typedef union
{
  struct
  {
    uint32_t ISR:9;                      /*!< bit:  0.. 8  Exception number */
    uint32_t _reserved0:7;               /*!< bit:  9..15  Reserved */
    uint32_t GE:4;                       /*!< bit: 16..19  Greater than or Equal flags */
    uint32_t _reserved1:4;               /*!< bit: 20..23  Reserved */
    uint32_t T:1;                        /*!< bit:     24  Thumb bit        (read 0) */
    uint32_t IT:2;                       /*!< bit: 25..26  saved IT state   (read 0) */
    uint32_t Q:1;                        /*!< bit:     27  Saturation condition flag */
    uint32_t V:1;                        /*!< bit:     28  Overflow condition code flag */
    uint32_t C:1;                        /*!< bit:     29  Carry condition code flag */
    uint32_t Z:1;                        /*!< bit:     30  Zero condition code flag */
    uint32_t N:1;                        /*!< bit:     31  Negative condition code flag */
  } b;                                   /*!< Structure used for bit  access */
  uint32_t w;                            /*!< Type      used for word access */
} xPSR_Type;

xPSR_Ttpe APSR;

uint32_t var1 = APSR.w;  // If we want to access as a word. Underneath Load Store Instruction
uint8_t var2 = APSR.b.IT;  //If we want to read from bit 25 and 26 only. Under neath Bitfiled instruction.

Hence union brings the freedom of access to a particular register. 

Code provision for handling Little endian and Big endian schemes

Following text is copied from the following linkof Keil ARMCompiler:
http://www.keil.com/support/man/docs/armcc/armcc_chr1359124215078.htm
This is completely for my own reference

#ifndef __BIG_ENDIAN // bitfield layout of APSR is sensitive to endianness
typedef union
{
    struct
    {
        int mode:5;
        int T:1;
        int F:1;
        int I:1;
        int _dnm:19;
        int Q:1;
        int V:1;
        int C:1;
        int Z:1;
        int N:1;
    } b;
    unsigned int word;
} PSR;
#else /* __BIG_ENDIAN */
typedef union
{
    struct
    {
        int N:1;
        int Z:1;
        int C:1;
        int V:1;
        int Q:1;
        int _dnm:19;
        int I:1;
        int F:1;
        int T:1;
        int mode:5;
    } b;
    unsigned int word;
} PSR;
#endif /* __BIG_ENDIAN */

/* Declare PSR as a register variable for the "apsr" register */
register PSR apsr __asm("apsr");

void set_Q(void)
{
    apsr.b.Q = 1;
}

Thursday, July 20, 2017

Why the provision of executing code from SRAM if FLASH is already there on Chip for Microcontroller ?

This has always been a question for me.

FLASH is the area where it is supposed to store the Application instructions.
SRAM is supposed to be used for storing global, static and local variables.

So, why there is sometimes provision provided in micro-controllers to load the code on SRAM and then execute.

Well I found these reasons. Correct me if I am wrong.
- Code execution from SRAM is almost 3 times faster compared to executing from FLASH.
- There are some techniques like instruction pre-fetch buffering, to store the instructions in CPU before it executes. But still SRAM has added advantage over this.
- SRAMs are costly compared to FLASH hence they are usually less in size compared to FLASH on-chip

Tips:
Interrupt service routines(ISR) are the piece of codes that are usually required to be executed very fast. So it might be good idea to store the interrupt routines on SRAM along with the Interrupt vector table.

Developing code from scratch for Bare metal microcontroller

Step1:

Go through the datasheet and technical reference manual of the micro-controller (MCU). Search on the manufacturer website for 

  1. Header files of registers of MCU
  2. HAL layer to reduce the effort of creating from scartch.
  3. If above options are not available then prepare header file on your own by referring to memory map
Example
mcu123.h
...
...
#define ADC1_base 0x08000100
#deinfe ADC2_base 0x08000200
...
...

Step2:

Create user file where all structures will be there to address every register in MCU
user_mcu123.h

/**
 * Bit fields
 */
typedef struct {
  uint32_t ADRDY_MST : 1;  // BIT 0
  uint32_t EOSMP_MST : 1;  // BIT 1
.
.
.
} ADC_CFGR_t


typedef struct {
  uint32_t RESERVED : 3;  //  Bits 0,1,2 reserved
  uint32_t SMP1 : 3;  // BIT 3,4,5
  uint32_t SMP2 : 3;  // BIT 6,7,8
.
.
.
} ADC_SMPR_t

/**
* Structures
*/
typedef struct {
  ADC_CFGR_t CFGR;  // Offset = 0x00000000
  ADC_SMPR_t SMPR;  // Offset = 0x00000004 these are arranged as per the memory map
} ADC_t

Step 3:

user_main.c
volatile ADC_t * const ADC1, * ADC2  //declare two pointers which will of required data type
ADC1 = (ADC_t *) ADC1_base;
ADC2 = (ADC_t *) ADC2_base;

NOTE: There is reason for declaring volatile and const in such fashion for ADC1 and ADC2.
ADC1/2 is a pointer which is constant, but the memory map location to which is points can change. Memory location to which it points is volatile because we are telling compiler that the content of that memory location can change out of sync to the program flow. So restrains compiler from making and optimization to this object  and removing from compiler output.

Memorymap IO and Port mapped IO

Memory mapped IOs:

All memory and IOs that are addressed using same memory map are called Memory mapped IOs
eg. Motorola

Memory_mapped_io.png

Port Mapped IOs:

Method in which, memory is mapped on one memory map, while IOs are mapped on different memory map is called Port mapped IOs
So here with same address, either IOregister / memory location can be accessed depending upon teh IORQ line value

So if any architecture says it has Port mapped IOs, then it should have a separate instruction for performing fetch from memory and fetch from IO.

NOTE: IORQ line is completely handled by CPU hardlogic after decodiong instruction. User code can't access it through some instruction call.

eg. Intel

Port_Mapped_io.png

http://www.bogotobogo.com/Embedded/memory_mapped_io_vs_port_mapped_isolated_io.php

Callback and Interrupt service routines, one and the same thing.

Interrupts in baremetal:


void ISR_UART1(void) {
    task_a();
    task_b();
    task_c();
}

void ISR_ADC1(void) {
    task_1();
    task_2();
    task_3();
}

void ISR_ADC2(void) {
    task_10();
    task_20();
    task_30();
}

Interrupt vector table


SrNo Address Callback function
1 0x00000000 RESET
2 0x00000014 ISR_UART1
3 0x00000018 ISR_ADC1
4 0x0000001C ISR_ADC2

Here, ISR_UART1(), ISR_ADC1(), ISR_ADC2() are callback functions. They are getting called whenever and Interrupt occurred. When Interrupt occurs, the Program counter is filled with address from above vector table as per the interrupt type. Next step, it interprets the value at this location as function pointer and goes to particular location as mentioned by user.

After RESET, the program counter(PC) gets the next value as per the BOOT pin configuration. So after the Reset, the PC can get value which can be address of start location of FLASH/SRAM/Bootloader etc. as per the MCU specification.

Considering it enters the usercode, the startup code starts executing which is even before main(). Here the STACK, HEAP and instructions to fill vector table with appropriate callback function pointers is done. So the Stack pointer is initialized, Heap pointer is also initialized.

Once this process is done, the program counter is directed to main()

Conclusion: Interrupt service routines are actually callback functions whose address is passed to interrupt vector table. They get called back whenever interrupt occurs.

Wednesday, July 19, 2017

Quick way to represent information graphically using Python

When you have some information example histogram then here is a small example you can refer to:

import random
import numpy as np
import matplotlib.pyplot as plt

x = []
for i in range(1,101):
    x.append(random.randrange(1,101))

h, b = np.histogram(x, bins = 100, range = [0,100])
b = np.delete(b, 0)
plt.show(plt.plot(b,h))



PROFILE

My photo
India
Design Engineer ( IFM Engineering Private Limited )

Followers