Real-Time Computing and Embedded Systems

Joshua Gregory

Intro        7

Syllabus        7

Introduction        8

What is Real-Time?        8

Correctness        8

Steps to (non-Real-Time) correctness        8

Embedded Development        8

Customer Lock-in        8

Tool Differentiation        9

Simple Development        9

Tempting Alternatives        9

mbed        9

Beware of Configuration Tools        9

ARM Cortex M        9

Documents        9

The Cortex M Family        10

ARM7 vs Cortex M3 (2006)        10

Cortex M0+ Implementation        12

Register Set        12

Memory Map        13

Magic Smoke        13

Keeping the magic smoke in        14

Valves (Vacuum Tubes)        14

CMOS        14

Latch-up is not so bad now        15

Wires are not nodes        16

Power/ground inductance is bad        16

Beware of earth loops        16

Absorb noise energy        16

Wires are transmission lines        16

Mismatched transmission lines        17

Skew        17

Ringing        18

Digital scopes lie        18

Ringing is bad        18

Clean up a clock        18

Keep your fingers out        19

A sense of smell is good        19

CISC and RISC        19

Von Neumann Architecture        19

Harvard Architecture        20

Von Neumann vs. Harvard        20

Modern View of Von Neumann Architecture        21

Structure of a Harvard architecture CPU        22

Modified Harvard Architecture        22

“Harvard Mark 1”        23

CISC and RISC        23

Example: CISC style        24

Example RISC style        24

Look at the x86 instruction set        24

RISC Philosophy        24

CISC Characteristics        25

One way of looking at it…        25

The RISC 1 (1981)        25

Other RISC families        25

MIPS        25

SPARC        26

Buses: on board        26

On-board buses        26

Classic On-board buses        26

Asynchronous serial interconnect        27

RS232        27

MAX232        27

Differential serial: RS422, RS485        27

Care is needed turning around RS485        28

I2C        28

I2C Chips        28

SPI        29

SPI does not need dedicated chips        30

JTAG        30

LVDS (TIA/EIA-644)        31

Buses: off board        31

Reliability / Safety        31

Off-board buses        32

RS232        32

CAN-BUS        32

CAN-BUS parts        32

USB        32

Firewire (IEEE1394)        32

Ethernet        33

W5500 SPI Ethernet        33

Don’t abuse RJ45 patch leads        33

USB        33

History        33

Evolution        34

PC interfaces were a mess        34

Basic characteristics of USB        34

Network and Connectors        34

Type C        35

Common cable colour assignments        36

Connector evolution        36

Speed        36

And the new Type C        36

A modern standard?        37

Software environment        37

A USB network is a tree        37

Power        37

Device States        38

On-the-go        38

Layered protocols: Low Speed Electrical        40

Bit layer        40

NRZI encoding of data bits        40

Bit-bash        40

Protocol layer        41

Four classes of packets        42

Transactions        42

Interrupt Transfers        43

Control Transfer        43

That’s a lot of layers        44

Communication Flow        45

Transfer Types        45

Endpoints        45

Device startup        46

Descriptors        46

Enumeration        48

Useful USB gizmo and books        48

Synchronisation        48

Synchronization        48

A sad story        48

Processes and Threads        49

Process C code        50

Thread C++ code        51

Table        51

Critical Regions        51

Solutions….        51

Mutual Exclusion with Busy Waiting        52

Race Conditions        52

Another solution?        53

Peterson’s Solution        53

TSL        53

Test and Set on x86        54

Legacy        54

Mutual exclusion on the Cortex M0+        54

Disabling interrupts on the M0+        55

Non-busy synchronization: Sleep and Wakeup        55

Producer-consumer implementation with fatal race condition        56

How do we fix this?        57

Java’s synchronized, wait() and notify()        57

Producer/Consumer in Java        58

Bad style        58

Just plain wrong        60

Semaphores        60

Producer-consumer problem using semaphores        61

Memory management        61

Semaphores in Java, before Java 1.5        62

Message Passing        62

Memory management etc.        63

Message queue sizes        63

Barriers        64

The Dining Philosopher        64

Java        65

Modifying solution…        68

Working Solution        69

Java Pthread        69

Java wait() is “truly horrid”        70

Java wait/notify unfairness        70

Java.util.concurrency        70

Old Style        70

New(ish) Style        71

AtomicInteger (or Long) is fast        71

POSIX C Semaphores        72

The Readers and Writers Problem        73

POSIXC        73

C 2011 language features        74

C++ 2011        74

Scheduling        74

Model Checking        74

Verifying the correctness of our code        74

What can a program do?        74

Assertions        75

What can go wrong?        75

Assert and Assume        76

Corrected version of program        76

Unbounded Model Checking Loops        76

Bounded Model Checking Loops        77

Replacement        77

We now have ordinary algebra        78

Feed the expression to an SMT solver        78

Proof By Induction        78

Inductive Proof        79

The invariant        79

Not quite enough        80

Where do these invariants come from?        80

Now the proof falls into three parts:        81

Base case        81

Inductive step        81

Termination condition        82

It is all much harder in practice        82

DSP        82

Model Checking Synchronisation        82

ESBMC        82

Automata        82

FreeRTOS Example?        83

Lab Stuff?        83

TL;DR        83

What is a real time system?        83

Three types of correctness:        83

Make real time system correct        83

Embedded Development        84

ARM Cortex M        84

Magic Smoke        86

Von Neumann, Harvard, CISC and RISC        87

On-board Buses        87

Off-board Buses        88

USB        88

Synchronisation        90

Processes and Threads        90

Process C code        90

Thread C++ code        91

Table        92

Critical Regions (Critical Sections)        92

Race Conditions        92

How will we solve these problems?        93

Peterson’s Solution        93

Peterson’s Solution Code        93

Busy waiting:        94

Volatile Keyword Note        94

Test and Set Lock (TSL)        94

Non-busy synchronization        95

Sleep and Wakeup        95

Implementations in Java        95

Code (for Producer/Consumer)        95

Fairness        96

Semaphores        96

Producer/Consumer with Semaphores Code        97

POSIX        98

Message Passing        98

Code in C Producer/Consumer        98

Barriers        99

The Dining Philosophers Problem.        99

Java code; working solution        100

Model Checking        101

Other stuff we sadly didn’t get to do in a real time module        102

Intro

Basically this is a summary of the slides. Okay this module was a bit of a mess with the lectures, so following shows the updated syllabus Denis showed us on the last lecture before christmas.

Syllabus

Introduction

What is Real-Time?

A real-time system is any information processing system which has to respond to externally generated input stimuli within a finite and specified period.

In computer science, real-time computing (RTC), or reactive computing describes hardware and software systems subject to a "real-time constraint", for example from event to system response. Real-time programs must guarantee response within specified time constraints, often referred to as "deadlines". [-Wiki]

Correctness

Steps to (non-Real-Time) correctness

  1. Use idealised mathematical types to simplify the reasoning.
  2. Establish partial correctness.
  3. Establish that the function will terminate.
  4. Arrange to cope with any problems from C’s restricted int implementation.

Embedded Development

Customer Lock-in

Each vendor used to have their own architecture. But now, everybody makes ARM Cortex M. They try to differentiate on peripherals and tools.

Tool Differentiation

Simple Development

Tempting Alternatives

mbed

Beware of Configuration Tools

ARM Cortex M

Documents

There are basic documents/reference manuals available for ARM stuff and for our board (FRDM-KW41Z). Links can be found on slides.

The Cortex M Family

ARM7 vs Cortex M3 (2006)

Features

ARM7TDMI-S

Cortex-M3

Architecture

ARMv4T (von Neumann)

ARMv7-M (Harvard)

ISA Support

Thumb / ARM

Thumb / Thumb-2

Pipeline

3-Stage

3-Stage + branch speculation

Interrupts

FIQ / IRQ

NMI + 1 to 240 Physical Interrupts

Interrupt Latency

24-42 Cycles

12 Cycles

Sleep Modes

None

Intergrated

Memory Protection

None

8 region Memory Protection Unit

Dhrystone

0.95 DMIPS/MHz (ARM mode)

1.25 DMIPS/MHz

Power Consumption

0.28mW/MHz

0.19mW/MHz

Area

0.62mm² (Core Only)

0.86mm² (Core * Peripherals)

Cortex M0+ Implementation

Register Set

Memory Map

Magic Smoke

Keeping the magic smoke in

Good ways to keep it in are:

Valves (Vacuum Tubes)

They were really tough.

CMOS

(Complementary metal–oxide–semiconductor) is more fragile...

Latch-up used to be a serious problem. A parasitic four-layer device could turn the part into a permanent short circuit if you took a pin outside 0..Vcc.

Latch-up is not so bad now

Wires are not nodes

Power/ground inductance is bad

As a computer science student writing these notes, I do not understand >70% of the words in this subsection lol.

Beware of earth loops

Absorb noise energy

Wires are transmission lines

Mismatched transmission lines

Skew

Ringing

Digital scopes lie

Ringing is bad

Clean up a clock

Keep your fingers out

A sense of smell is good

Different magic smokes smell different:

CISC and RISC

Von Neumann, Harvard, CISC and RISC

(Not sure if we actually went through these slides in lectures, but probably useful to know)

There are two common ways to store programs and data.

Von Neumann Architecture

Harvard Architecture

Von Neumann vs. Harvard

Von Neumann

Harvard

Theoretical design based on the stored-program computer concept

Modern computer architecture based on the Harvard Mark I relay-based computer model

Uses same physical memory address for instructions (program) and data

Uses separate memory and addresses for instructions and data

Processor needs two clock cycles to execute an instruction

Processor needs one cycle to complete an instruction

Simpler control unit design and development of one is cheaper and faster

Control unit for two buses is more complicated which adds to the development cost

Data transfers and instruction fetches cannot be performed simultaneously

Data transfers and instruction fetches can be performed at the same time

Used in personal computer, laptops and workstations

Used in microcontrollers and signal processing

Modern View of Von Neumann Architecture

Structure of a Harvard architecture CPU

Modified Harvard Architecture

“Harvard Mark 1”

Or IBM Automatic Sequence Controlled Calculator

CISC and RISC

Example: CISC style

add R1, (R2)                 

R1 = R1 + (value from address stored in R2)

movl (%ebx, %esi, 4), %eax

Multiply contents of esi by 4, add result to contents of ebx, treat result as an address – copy what is there to register eax

Example RISC style

LDR R2, [R1]                read from address stored in R1, into R2

ADD R4,R2,R3        R4 = R2 + R3

STORE R4,[R1]        store result to memory address R1

Look at the x86 instruction set

https://en.wikipedia.org/wiki/X86_instruction_listings

RISC Philosophy

  1. Instructions of fixed length executing in a single clock cycle
  2. Pipelines to achieve one-instruction-per-one-clock-cycle throughput (need to predict branches in program flow in advance)
  3. Simple control logic to increase clock speed, no micro-code
  4. Operations performed on internal registers only; only LOAD and STORE instructions access external memory

CISC Characteristics

  1. Binary compatibility
  1. old binary code can run on newer versions
  1. Complex control logic to support many instructions
  2. Use of micro-code
  1. one program instruction can execute in many cycles
  1. Variable-length instructions to save program memory
  2. Small internal register sets compared with RISC
  3. Complex addressing modes, operands can reside in external memory or internal registers

One way of looking at it…

Runtime = instruction-time x cycles-per-instruction x number-of-instructions

The RISC 1 (1981)

Other RISC families

MIPS

SPARC

Buses: on board

On-board buses

Designed to operate only in a controlled environment

Classic On-board buses

Asynchronous serial interconnect

RS232

MAX232

Differential serial: RS422, RS485

Care is needed turning around RS485

I2C

I2C Chips

SPI

SPI does not need dedicated chips

Have fun with this diagram

JTAG

LVDS (TIA/EIA-644)

Buses: off board

Reliability / Safety

Off-board buses

RS232

See previous lecture as its just copy pasted slides lol.

CAN-BUS

CAN-BUS parts

USB

Firewire (IEEE1394)

Ethernet

W5500 SPI Ethernet

¯\_(ツ)_/¯

Don’t abuse RJ45 patch leads

USB

History

Evolution

PC interfaces were a mess

Basic characteristics of USB

Network and Connectors

Type C

Common cable colour assignments

Connector evolution

Speed

The latest invocation is Super Speed USB 3.0 with a theoretical maximum transfer rate of 5Gbit/s, similar to PCIe Gen2, and five additional pins (including signal ground) in a backward compatible connector:

And the new Type C

A modern standard?

Software environment

A USB network is a tree

Power

Device States

Look at this coooooooooool finite state machine oooooooooooooo

On-the-go

Layered protocols: Low Speed Electrical

Bit layer

NRZI encoding of data bits

Bit-bash

You can “bit-bash” low speed USB

See: https://www.obdev.at/products/vusb/index.html

Protocol layer

All data is assembled into packets:

Four classes of packets

Transactions

A transactions is made up of a sequence of packets

Interrupt Transfers

Control Transfer

That’s a lot of layers

Communication Flow

Transfer Types

Endpoints

Device startup

Descriptors

Enumeration

Useful USB gizmo and books

Synchronisation

Synchronization

A sad story

Essentially concurrency and synchronisation are a pain in computer science.

Processes and Threads

What’s the difference?

Process C code

Thread C++ code

Table

Segment

Mode

Two Processes

Two Threads

Code

Execute, Read?

May be shared

Shared

R/O data

Read, Execute?

May be shared

Shared

R/W data

Read, Write

Copied

Shared

Stack (in BSS)

Read, Write, Execute?

Copied

New

Critical Regions

“A section of code that may only be executed by one process at any one time.” [-Oxford Reference]

Solutions….

Mutual Exclusion with Busy Waiting

You could try two process with this code:

But this will have a race condition!

Race Conditions

A race condition occurs when two or more threads can access shared data and they try to change it at the same time. Because the thread scheduling algorithm can swap between threads at any time, you don't know the order in which the threads will attempt to access the shared data. Therefore, the result of the change in data is dependent on the thread scheduling algorithm, i.e. both threads are "racing" to access/change the data.

Problems often occur when one thread does a "check-then-act" (e.g. "check" if the value is X, then "act" to do something that depends on the value being X) and another thread does something to the value in between the "check" and the "act".” - What is a race condition? 

Another solution?

This works but forces a strict alternation.

Peterson’s Solution

Lol copy pasting whole slides

TSL

This enters and leaves a critical region using the TSL instruction.

Unfortunately, the x86 does not have the TSL instruction.

Test and Set on x86

XCHG Exchange Registers

Logic: destination ←→ source XCHG switches the contents of its operands, which may be either bytes or words.

Example:

                LOCK XCHG SEMPHOR,   DX

Notes: Used in conjunction with the LOCK prefix, this instruction is particularly useful when implementing semaphores to control shared resources.

LOCK is a one-byte prefix that can precede any instruction. LOCK causes the processor to assert its bus lock signal wile the instruction that follows it executed. If the system is configured such that the LOCK signal is used, it prevents any external device or events from accessing the bus, including interrupts and DMA transfers.

Notes: This instruction was provided to support multiple processor systems with shared resources. In such a system, access to those resources is generally controlled via a software-hardware combination using semaphores. This instruction should only be used to prevent other bus masters from interrupting a data movement operation. This prefix should only be used with XCHG, MOV, and MOVS.

Legacy

Remember: all this legacy stuff makes assumptions that are often not true in modern systems:

Mutual exclusion on the Cortex M0+

Disabling interrupts on the M0+

Non-busy synchronization: Sleep and Wakeup

We can try to remove the polling load from the CPU by constructing new Operating System/runtime methods:

Producer-consumer implementation with fatal race condition

The race condition can occur because access to count is unconstrained. The following situation could possibly occur.

While the wakeup waiting bit saves the day in this simple example, it is easy to construct examples with three or more processes in which one wakeup waiting bit is insufficient. We could make another patch and add a second wakeup waiting bit, or maybe 8 or 32 of them, but in principle the problem is still there.

How do we fix this?

Java’s synchronized, wait() and notify()

Producer/Consumer in Java

Bad style

Just plain wrong

Semaphores

        

Producer-consumer problem using semaphores

Memory management

Semaphores in Java, before Java 1.5

Warning: this also had bad fairness properties

Java 1.5 has `java.util.concurrent`

Message Passing

The producer-consumer problem with N messages

  1. Monitors and condition variables (wait, notify)
  2. Semaphores, or
  3. Message passing

Memory management etc.

Message queue sizes

Barriers

All processes have to arrive at the barrier before they can all proceed.

The Dining Philosopher

This is a common example used in concurrent algorithm design to illustrate synchronization issues and techniques. [-Wiki]

Java

Modifying solution…

Working Solution

The module as delivered in 2019–20 covered the slide decks in order up to and including Synchronisation slide 66.

This is where Denis says he stopped lecturing, so stuff after this shouldn’t be on the exam (but it probably might be lol)

Java Pthread

The Java authors adopted a version of Pthreads’ mutexes and condition variables. The standard (but dangerous. (You should use a private lock object)). usage is:

Java wait() is “truly horrid”

Java wait/notify unfairness

Java.util.concurrency

Some more books with pictures of trains on them….

Old Style

New(ish) Style

AtomicInteger (or Long) is fast

POSIX C Semaphores

POSIX also has mutex/condition variable support…

The Readers and Writers Problem

POSIXC

C 2011 language features

C++ 2011

Scheduling

Model Checking

Verifying the correctness of our code

What can a program do?

  1. Break something (e.g. burn out the hardware)
  2. Fail to complete
  3. Complete too late
  4. Complete and give the wrong result
  5. Complete and give the right result

But how do we specify the right result?

Assertions

The simplest specification is a (point) assertion.

An assertion is a predicate connected to a point in the program, that should always evaluate to true at that point in code execution. [-Wiki]

They are supported by the C standard as `assert()`.

But what can go wrong?

What can go wrong?

Assert and Assume

Corrected version of program

Unbounded Model Checking Loops

For unbounded model checking, we need to unroll the loop.

Becomes, if we bound the unrolling at nine iterations:

How I used to write code….

Bounded Model Checking Loops

In bounded model checking, we can only cope with loops up to a bounded depth.

Replacement

The unrolled program becomes:

We now have ordinary algebra

We look for satisfying assignments to the integer unknowns which would cause the assertion to fail. The program is broken if there is a set of assignments to `ain, b, a_0...a_9, results_0...result_9` which makes the following expression true:

Feed the expression to an SMT solver

Proof By Induction

A proof by induction is just like an ordinary proof in which every step must be justified. However it employs a neat trick which allows you to prove a statement about an arbitrary number n by first proving it is true when n is 1 and then assuming it is true for n=k and showing it is true for n=k+1. The idea is that if you want to show that someone can climb to the nth floor of a fire escape, you need only show that you can climb the ladder up to the fire escape (n=1) and then show that you know how to climb the stairs from any level of the fire escape (n=k) to the next level (n=k+1).

[Source: http://comet.lehman.cuny.edu/sormani/teaching/induction.html]

Another example:

How can it fail?

Inductive Proof

  1. Assert an equality claimed to be valid for all n.
  2. Show that it is true for n=0.
  3. Show that, if it is true for n=m, then it is also true for n=m+1.
  4. Then
  1. the n=0 case implies the n=1 case
  2. the n=1 case implies the n=2 case
  3. the n=2 case implies the n=3 case
  4. And so on for all n ≥ 0.

The invariant

For a proof by induction, we need an inductive hypothesis, inside the loop.

An inductive hypothesis is just something you assume is true to help with the proof. [Correct me if I’m wrong, just want a basic explanation]

Not quite enough

We also need to control the induction variable

Where do these invariants come from?

Now the proof falls into three parts:

  1. Base case

The base case can be extended to perform some bounded unrolling as well.

  1. Inductive step

Sometimes it helps to unroll the inductive step a few times (k-induction)

  1. Termination condition

The inductive step and the termination condition can be fused into a single test.

It is all much harder in practice

DSP

Model Checking Synchronisation

ESBMC

Automata

If you’re interested, check out the first half of Matt’s Notes on the Theory of Computing module.

Theory of Computing Notes 

I basically wrote 7 pages of notes on these slides and it was just the first half of that module.

FreeRTOS Example?

¯\_(ツ)_/¯

Lab Stuff?

Embedded system, microcontroller, turning on LEDs, UART communication, FreeRTOS, C coding, OpenThread, wireless communications…

Failover

When a node in a mesh network fails, communication between other nodes is unaffected as long as there are nodes to carry-over the signal

TL;DR

Main presentation title goes here.

Tbh read this cus idk how much you can really learn from just detailed electronics stuff unless you know it already, and the slides are pretty long winded and I try to explain stuff more usefully for the exam here as well idk lol.

What is a real time system?

Three types of correctness:

Make real time system correct

  1. Use maths to simplify reasoning
  2. Establish partial correctness
  3. Establish function will terminate
  4. Arrange to cope with problems from C’s int implementation

Embedded Development

Everyone makes stuff on ARM Cortex M Architecture nowadays. They have their own peripherals and proprietary software.

Tools:

Simple Dev:

Alternative

mbed

Config tools

ARM Cortex M

The ARM Cortex-M is a group of 32-bit RISC ARM processor cores. [-Wiki]

Cortex M3 is “better” than ARM7

Memory Map:

Registers:

Magic Smoke

CMOS (Complementary metal–oxide–semiconductor)

CMOS is basically a battery powered chip that gives information like date and time & hardware settings to computer’s BIOS (Basic Input Output System). [Source]

Latch-up….

Power/ground inductance is bad..

Beware of earth loops

Transmission lines, mismatched lines

Skew

Ringing

Von Neumann, Harvard, CISC and RISC

Are a thing but probably won’t be in exam. Basically RISC used in embedded real time stuff.

On-board Buses

Buses are data transmission lines.

Some will be on-board/inside an electrical system.

They need:

Low-voltage serial RS232

SPI (Serial Peripheral Interface)

I2C

JTAG (Joint Tested Action Group)

Off-board Buses

RS232 again lol

CAN-BUS

USB (Universal Serial Bus)

(Not even a bus lol)

Firewire

Ethernet

USB

Type-C

The latest invocation is Super Speed USB 3.0 with a theoretical maximum transfer rate of 5Gbit/s, similar to PCIe Gen2, and five additional pins (including signal ground) in a backward compatible connector:

...

Synchronisation

Processes and Threads

Process

Thread

https://www.geeksforgeeks.org/multithreading-c-2/

Process C code

Important methods: fork(); getpid(); getppid();

https://www.geeksforgeeks.org/fork-system-call/

https://www.geeksforgeeks.org/getppid-getpid-linux/

#include <stdlib.h>

#include <stdio.h>

#include <unistd.h>

int main()

{

        int id;

        printf(“Hello, World!\n”);

        id=fork();

        

        if(id>0)

{

                /* Parent Process */

                printf(“This is the parent, my PID: %d, child PID: %d\n”, getpid(), id);

        }

        else if(id==0)

        {

                /* Child Process */

                printf(“This is the child, my PID: %d, parent PID: %d\n”, getpid(), getppid());

        }

        else

{

                /* Fork Failed */

                fprintf(stderr, “fork creation failed!\n”); exit(EXIT_FILURE);

        }

        exit(EXIT_SUCCESS);

}

Thread C++ code

// Compile with: g++ -std=c++14 -o th th.cpp

// On old compilers, you needed an extra option “-pthread” or it

// would fail silently at run-time

#include <iostream>

#include <thread>

// This function will be called from a thread

void call_from_thread() {

        std::cout << “Hello from a C++ 11 thread.” << std::end1;

}

int main() {

        // Launch a thread

        std::thread t1(call_from_thread);

        // Join the thread with the main thread

        t1.join();

        return 0;

}

Table

Segment

Mode

Two Processes

Two Threads

Code

Execute, Read?

May be shared

Shared

R/O data

Read, Execute?

May be shared

Shared

R/W data

Read, Write

Copied

Shared

Stack (in BSS)

Read, Write, Execute?

Copied

New

Critical Regions (Critical Sections)

“A section of code that may only be executed by one process at any one time.” [-Oxford Reference]

Race Conditions

A race condition occurs when two or more threads can access shared data and they try to change it at the same time.

Because the thread scheduling algorithm can swap between threads at any time, you don't know the order in which the threads will attempt to access the shared data. Therefore, the result of the change in data is dependent on the thread scheduling algorithm, i.e. both threads are "racing" to access/change the data.”

-What is a race condition?

How will we solve these problems?

There are many ways to try and make your code protected against synchronisation problems, and many of them look like they might work, but there usually is a cheeky race condition in there somewhere.

Here’s some solutions that actually work:

Peterson’s Solution

Peterson’s Solution Code

// This version of the code is for two threads

#define FALSE 0

#define TRUE (!FALSE)

volatile int turn;

volatile int interested[2];

void enter_region(int thread)

{

        int other;

        other = 1-thread;

        interested[thread] = TRUE;

        turn = other;

        while (turn == other && interested[other]);

}

void leave_region(int thread)

{

        Interested[thread] = FALSE;

}

// The code would be used from the threads as follows (might be a bit bugged on smaller resolutions, change it I must!)

//

// Thread 0                                        Thread 1

// while(TRUE) {                                        while(TRUE) {

//         enter_region(0);                                   enter_region(1);

//        critical_region();                           critical_region();

//        leave_region(0);                                   leave_region(1);

//        noncritical_region(); }                   noncritical_region(); }

Explanation

Busy waiting:

When a process repeatedly checks to see if a condition is true, such as a lock becoming available.

Volatile Keyword Note

“The volatile keyword is intended to prevent the compiler from applying any optimizations on objects that can change in ways that cannot be determined by the compiler.” [Source]

Test and Set Lock (TSL)

However you can use XCHG Exchange Registers on x86.

Non-busy synchronization

To remove the polling load from CPU you can do these:

Sleep and Wakeup

Implementations in Java

Code (for Producer/Consumer)

(there will be no Java coding in the exam, only C, so get your cross compilers out ;) )

// Best not to allow inheritance as subclasses violate the

//synchronisation variants.

public final class ProducerConsumer {

        final N = 100;

        private int count = 0;

// Use a dedicated private object to ensure no non-class

//method tries to use the lock

        private Object lock = new Object();

// Run this as the producer thread

// The class should probably be generic, tagged with the type

//of item

        void producer() {

                Object item;

try {

        while(true) {

        item = Producer.produce_item();

        synchronized(lock) {

                while (count == N)

                        lock.wait();

                Buffer.insert_item(item);

                count = count + 1;

                lock.notifyAll();

        }

}

                }

                // This should never happen

                catch (InterruptedExcecption e) {

                        throw new AssertionError(e);

                }

        }

        // Run this as the consumer thread

void consumer() {

        Object item;

        try {

                while(true) {

                        synchronized(lock) {

                                while(count == 0)

                                        lock.wait();

                                

                                item = Buffer.remove_item();

                                count = count - 1;

                                lock.notifyAll();

                        }

                        Consumer.consume_item(item);

                }

        }

// This should never happen either

        catch (InterruptedException e) {

                throw new AssertionError(e);

        }

}

}

This whole mechanism has very bad fairness properties.

Fairness

Fairness basically resembles the likelihood that different threads are able to advance whatever they are doing. 100% fairness means: all threads should be advancing their work in almost equal portions; 0% fairness means that one single thread might be advancing all the time, and all other threads never (or almost never) make any progress.

It very much depends on your requirements exactly how fairness is required or achieved.” [Source]

Semaphores

P (prolaag) - wait, sleep, down operation.

V (verhoog) - signal, wakeup, up operation.

//wait when s is 0, else decrement it

down() {

        if (s>0)

s--;

        else

WAIT;

}

up() {

        if (THREADS_WAITING)

                WAKE_ONE;

        else

                s++;

}

  1. Binary Semaphore – This is also known as mutex lock. It can have only two values – 0 and 1. Its value is initialized to 1. It is used to implement the solution of critical section problem with multiple processes.
  2. Counting Semaphore – Its value can range over an unrestricted domain. It is used to control access to a resource that has multiple instances.

Producer/Consumer with Semaphores Code

#include <semaphore.h> // uses POSIX semaphores

sem_t mutex, empty, full;

void init(void) {

        sem_init(&mutex, 0, 1);

        sem_init(&empty, 0, N); //N is initial value

sem_init(&full, 0, 0);

}

// run in producer thread

void producer(void) {

        Item *pitem;

        while(true) {

                pitem = prduce_pitem();

                sem_wait(&empty);

                sem_wait(&mutex);

                insert_item(pitem);

                sem_post(&mutex);

                sem_post(&full);

        }

}

// run in consumer thread

void consumer(void) {

        Item *pitem;

        while(true) {

                sem_wait(&full);

                sem_wait(&mutex);

                pitem = remove_item();

                sem_post(&mutex);

                sem_post(&empty);

                consume_pitem(pitem);

        }

}

POSIX

“The Portable Operating System Interface (POSIX) is a family of standards specified by the IEEE Computer Society for maintaining compatibility between operating systems.” [-Wiki]

Message Passing

Code in C Producer/Consumer

#include <FreeRTOS.h>

#include <queue.h>

xQueuehandle queue;

void init(void) {

        Queue = xQueueCreat(N, sizeof(Item));

}

// run in producer thread

void producer(void)

        Item item;

while(true) {

        item = produce_item();

        (void)xQueueSendToBack(queue, &item, portMAX_DELAY);

}

}

// wait forever if necessary

void consumer(void) {

        Item item;

        while(true) {

                (void)xQueueReceive(queue, &item, portMAX_DELAY);

                consume_item(item);

        }

}

// WARNING: infinite wait only works if INDLUDE_vTaskSuspend is set

//to 1 in FreeRTOSConfig.h

// Note that message passing is normally pass-by-value, not by

//reference.

Barriers

All processes have to arrive at the barrier before they can all proceed.

The Dining Philosophers Problem.

Java code; working solution

class Fork {

        Semaphore s;

        int number;

        

        Fork(int number) {

                this.number = number;

                s = new Semaphore(1);

        }

        void take() {

                DiningPhilosophers p = (DiningPhilosophers)Thread.currentThread();

        s.acquireUninterruptibly();

        }

void drop() {

                DiningPhilosophers p = (DiningPhilosophers)Thread.currentThread();

        s.release();

        }

}

class DiningPhilosophers extends Thread {

        static final int N=2;  // N >= 2

        private static Fork[] forks = new Fork[N];

public void run()

                while(true) {

                        if(number == 0) {

                                forks[(number+1)].take();

                                forks[number].take();

                        }

                        else {

                                forks[number].take();

                                forks[(number+1)%N].take();

                        }

                        forks[number].drop();

                        forks[(number+1)%N].drop();

                }

}

}

public static final void main(String[] s) {

for (int i=0; i<N; i++)

forks[i] = new Fork(i);

for (int i=0; i<N; i++)

(new DiningPhilosophers(i)).start();

}

The module as delivered in 2019–20 covered the slide decks in order up to and including Synchronisation slide 66.

This TLDR goes up to slide 66. I’ll do a really short summary for other stuff, (cus we did actually have these lectures but Denis uhhhhh?)

Model Checking

Other stuff we sadly didn’t get to do in a real time module