« SE4Binome2025-2 » : différence entre les versions

De projets-se.plil.fr
Aller à la navigation Aller à la recherche
Aucun résumé des modifications
Aucun résumé des modifications
Ligne 35 : Ligne 35 :




= Firmware: RTOS =
= Firmware RTOS (version restructurée et en français) =


== 1. Vue d’ensemble du système ==
Le firmware implémente un petit RTOS coopératif optimisé en mémoire pour ATmega (ex. ATmega328P). 
Principales caractéristiques :
* Ordonnanceur round-robin simple.
* Table des tâches statique (pas d’allocation dynamique).
* Gestion des sections critiques imbriquées.
* Timer système à 100 Hz pour la gestion des ticks et du sommeil.


====== System Architecture Overview ======
== 2. Arborescence (extrait) ==
 
<pre>
The firmware implements a cooperative real-time operating system (RTOS) for ATmega328p microcontrollers, featuring a memory-optimized kernel with round-robin scheduling capabilities.
 
===== Component Hierarchy =====
 
<code>
firmware/
firmware/
├── kernel/
├─ kernel/
  ├── kernel.[ch]      - Core kernel management
├─ kernel.h
  ├── scheduler.[ch]  - Task scheduler implementation
│  ├─ kernel.c
  ├── task.[ch]        - Task control block system
├─ scheduler.h
  └── config.h         - Resource configuration
│  ├─ scheduler.c
└── main.c             - Application layer
├─ task.h
</code>
└─ task.c
 
├─ config.h
====== Core Kernel Mechanisms ======
└─ main.c
</pre>


===== Task Management System =====
== 3. Paramètres de configuration ==
<syntaxhighlight lang="c">
#define MAX_TASKS      4      // nombre max de tâches
#define STACK_SIZE      96    // octets par pile de tâche
#define TICK_FREQUENCY  100    // Hz — fréquence du tick système
#define TASK_NAME_LEN  8
</syntaxhighlight>


The kernel implements a static task control block (TCB) architecture:
== 4. Structure de contrôle de tâche (TCB) ==
<syntaxhighlight lang="c">
typedef enum {
    TASK_INVALID = 0,
    TASK_READY,
    TASK_RUNNING,
    TASK_SLEEPING
} task_state_t;


<code objc>
typedef struct {
typedef struct task_control_block {
     void (*function)(void *);
     void (*function)(void*);   // Task entry point
     void *arg;
     void* arg;                 // Task parameters
     uint8_t *stack_base;
     uint8_t* stack_base;       // Stack memory (96 bytes)
     task_state_t state;
     task_state_t state;         // Current task state
     uint16_t sleep_ticks;
     uint16_t sleep_ticks;       // Sleep countdown
     uint8_t priority;
     uint8_t priority;         // Execution priority (0-3)
     char name[TASK_NAME_LEN];
     char name[TASK_NAME_LENGTH]; // Task identifier
} task_t;
} task_t;
</code>
</syntaxhighlight>


===== Task State Transitions =====
== 5. Gestion des sections critiques (imbriquées) ==
<syntaxhighlight lang="c">
#include <avr/interrupt.h>
static volatile uint8_t critical_nesting = 0;


The system implements a finite state machine for task management:
static inline void enter_critical(void) {
    cli();
    critical_nesting++;
}


$$\text{TASK\_READY} \rightleftharpoons \text{TASK\_RUNNING} \rightarrow \text{TASK\_SLEEPING} \rightarrow \text{TASK\_READY}$$
static inline void leave_critical(void) {
    if (critical_nesting > 0) {
        critical_nesting--;
        if (critical_nesting == 0) sei();
    }
}
</syntaxhighlight>


===== Task Creation Protocol =====
== 6. Création de tâche ==
<syntaxhighlight lang="c">
int8_t task_create(const char *name, void (*function)(void *), void *arg,
                  uint8_t priority, uint8_t *stack_buffer)
{
    for (uint8_t i = 0; i < MAX_TASKS; i++) {
        if (task_table[i].function == NULL) {
            task_table[i].function = function;
            task_table[i].arg = arg;
            task_table[i].stack_base = stack_buffer;
            task_table[i].state = TASK_READY;
            task_table[i].priority = priority;
            strncpy(task_table[i].name, name, TASK_NAME_LEN);
            return i;
        }
    }
    return -1;
}
</syntaxhighlight>


<code objc>
== 7. Ordonnanceur (round-robin) ==
int8_t task_create(const char* name, void (*function)(void*),
<syntaxhighlight lang="c">
                  void* arg, uint8_t priority, uint8_t* stack_buffer)
static uint8_t current_task_id = 0;
</code>


**Creation Constraints:**
static inline uint8_t is_task_valid(uint8_t id) {
 
    return (id < MAX_TASKS) &&
  * Maximum task count: $MAX\_TASKS = 4$
          (task_table[id].function != NULL) &&
  * Stack size: $STACK\_SIZE = 96$ bytes
          (task_table[id].state == TASK_READY);
  * Priority levels: $\{PRIORITY\_IDLE, PRIORITY\_LOW, PRIORITY\_MEDIUM, PRIORITY\_HIGH\}$
}
 
====== Scheduling Algorithm ======
 
===== Round-Robin Implementation =====


The scheduler employs a circular search algorithm to find the next executable task:
<code objc>
static uint8_t get_next_task(void) {
static uint8_t get_next_task(void) {
     uint8_t next_task = (current_task_id + 1) % MAX_TASKS;
     uint8_t next = (current_task_id + 1) % MAX_TASKS;
     for (uint8_t i = 0; i < MAX_TASKS; i++) {
     for (uint8_t i = 0; i < MAX_TASKS; i++) {
         if (is_task_valid(next_task) &&
         if (is_task_valid(next)) return next;
            task_table[next_task].state == TASK_READY) {
         next = (next + 1) % MAX_TASKS;
            return next_task;
         }
        next_task = (next_task + 1) % MAX_TASKS;
     }
     }
     return current_task_id;
     return current_task_id;
}
}
</code>


===== Execution Flow =====
void scheduler_run(void) {
 
    enter_critical();
The main scheduler loop follows this sequence:
    if (is_task_valid(current_task_id)) {
 
        task_table[current_task_id].state = TASK_RUNNING;
  - Enter critical section (disable interrupts)
        leave_critical();
  - Execute current task function
        task_table[current_task_id].function(task_table[current_task_id].arg);
  - Calculate next task ID using round-robin
        enter_critical();
  - Leave critical section (enable interrupts)
         if (task_table[current_task_id].state == TASK_RUNNING)
  - Apply 1ms CPU delay to prevent overload
            task_table[current_task_id].state = TASK_READY;
 
     }
====== Memory Management ======
     current_task_id = get_next_task();
 
     leave_critical();
===== Resource Allocation =====
 
The system employs static memory allocation for predictable resource usage:
 
$$\begin{align*}
\text{Total RAM for stacks} &= MAX\_TASKS \times STACK\_SIZE = 4 \times 96 = 384 \text{ bytes} \\
\text{TCB memory footprint} &= MAX\_TASKS \times sizeof(task\_t) \approx 112 \text{ bytes} \\
\text{Global variables} &\approx 20 \text{ bytes} \\
\text{Total estimated usage} &\approx 516 \text{ bytes}
\end{align*}$$
 
===== Configuration Parameters =====
 
<code objc>
#define MAX_TASKS 4          // Maximum concurrent tasks
#define STACK_SIZE 96         // Bytes per task stack
#define TICK_FREQUENCY 100    // Hz - scheduler frequency
#define TASK_NAME_LENGTH 8    // Maximum task name characters
</code>
 
====== Interrupt and Critical Section Management ======
 
===== Atomic Operation Protection =====
 
The kernel implements nested critical sections to protect shared resources:
 
<code objc>
void enter_critical_section(void) {
    cli();                   // Disable interrupts
     critical_nesting++;      // Track nesting depth
}
 
void leave_critical_section(void) {
     if (critical_nesting > 0) critical_nesting--;
     if (critical_nesting == 0) sei(); // Re-enable interrupts
}
}
</code>
</syntaxhighlight>
 
===== Timer Interrupt Configuration =====
 
The system timer generates 100Hz interrupts for tick management:
 
$$OCR1A = \frac{F\_CPU}{Prescaler \times Frequency} - 1 = \frac{16,000,000}{64 \times 100} - 1 = 2499$$
 
<code objc>
// Timer1 configuration for 100Hz
TCCR1A = 0;
TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);
OCR1A = 2499;
TIMSK1 |= (1 << OCIE1A);
</code>
 
====== Sleep and Timing Mechanisms ======
 
===== Tick-Based Sleep System =====
 
Tasks can suspend execution for precise durations using system ticks:
 
$$\text{sleep\_ticks} = \left\lceil \frac{\text{milliseconds}}{10} \right\rceil$$


<code objc>
== 8. Mécanismes de sommeil et tick système ==
void task_sleep(uint16_t ticks) {
<syntaxhighlight lang="c">
     enter_critical_section();
void timer1_init_100hz(void) {
     task_table[current_task_id].sleep_ticks = ticks;
     TCCR1A = 0;
     task_table[current_task_id].state = TASK_SLEEPING;
     TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);
     leave_critical_section();
     OCR1A = 2499;
    schedule();
     TIMSK1 |= (1 << OCIE1A);
}
}
</code>
===== Tick Update Algorithm =====
The interrupt service routine manages sleeping tasks:


<code objc>
ISR(TIMER1_COMPA_vect) {
ISR(TIMER1_COMPA_vect) {
    extern volatile uint32_t system_ticks;
     system_ticks++;
     system_ticks++;
     for (uint8_t i = 0; i < MAX_TASKS; i++) {
     for (uint8_t i = 0; i < MAX_TASKS; i++) {
         if (task_table[i].state == TASK_SLEEPING &&  
         if (task_table[i].state == TASK_SLEEPING && task_table[i].sleep_ticks > 0) {
            task_table[i].sleep_ticks > 0) {
             task_table[i].sleep_ticks--;
             task_table[i].sleep_ticks--;
             if (task_table[i].sleep_ticks == 0) {
             if (task_table[i].sleep_ticks == 0)
                 task_table[i].state = TASK_READY;
                 task_table[i].state = TASK_READY;
            }
         }
         }
     }
     }
}
}
</code>


====== System Initialization Sequence ======
void task_sleep(uint16_t ticks) {
 
    if (ticks == 0) ticks = 1;
===== Boot Process =====
    enter_critical();
 
    task_table[current_task_id].sleep_ticks = ticks;
  - **Memory Zeroing**: Clear task table and global variables
    task_table[current_task_id].state = TASK_SLEEPING;
  - **Timer Configuration**: Setup 100Hz interrupt timer
    leave_critical();
  - **Idle Task Creation**: Initialize fallback task with lowest priority
     scheduler_run();
  - **Interrupt Enable**: Start scheduler tick generation
  - **Task Validation**: Mark all created tasks as READY state
 
====== Error Handling and Robustness ======
 
===== Boundary Condition Management =====
 
  * **Task Validation**: All task executions verify function pointer validity
  * **Sleep Sanitization**: Zero-tick sleep requests default to 1 tick
  * **Circular Search**: Scheduler handles empty task tables gracefully
  * **Nesting Safety**: Critical sections properly handle nested calls
 
===== Recovery Mechanisms =====
 
<code objc>
static uint8_t is_task_valid(uint8_t task_id) {
     return (task_id < MAX_TASKS && task_table[task_id].function != NULL);
}
}
</code>
</syntaxhighlight>


====== Performance Characteristics ======
== 9. Initialisation du système ==
# Mise à zéro des structures de données.
# Initialisation du timer à 100 Hz.
# Création de la tâche d’« idle ».
# Activation des interruptions globales.
# Lancement de la boucle principale qui exécute `scheduler_run()`.


===== Computational Complexity =====
== 10. Gestion d’erreurs ==
* Vérification de la validité des pointeurs de fonctions.
* Conversion des durées nulles (`sleep(0)`) en `sleep(1)`.
* Gestion des appels imbriqués de sections critiques.
* Fallback automatique sur la tâche idle si aucune tâche prête.


Algorithm Complexity Analysis
== 11. Estimations mémoire ==
^**Operation**    ^**Time Complexity** ^**Space Complexity**  ^
* Piles : 4 × 96 = 384 octets
|Task Creation    |$O(n)$              |$O(1)$                |
* TCB : 4 × 28 = 112 octets (approx.)
|Task Scheduling  |$O(n)$              |$O(1)$                |
* Variables globales : ~20 octets
|Sleep Update    |$O(n)$              |$O(1)$                |
* **Total estimé :** ~516 octets
|Context Switch  |$O(1)$              |$O(1)$                |


===== Memory Efficiency =====
== 12. Intégration du build system ==
 
<syntaxhighlight lang="makefile">
The system achieves high memory efficiency through:
 
  * Static allocation eliminating heap fragmentation
  * Fixed-size arrays for predictable memory usage
  * Stack sharing between kernel and application
  * Minimal TCB overhead (28 bytes per task)
 
====== Build System Integration ======
 
===== Compilation Configuration =====
 
<code make>
MCU = atmega328p
MCU = atmega328p
F_CPU = 16000000UL
F_CPU = 16000000UL
CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Os -Wall -std=c99
CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Os -Wall -std=c99
</code>
</syntaxhighlight>
 
===== Memory Section Allocation =====
 
$$\begin{align*}
\text{Program Memory} &= \text{.text} + \text{.data} \\
\text{RAM Usage} &= \text{.data} + \text{.bss} + \text{Stack}
\end{align*}$$

Version du 11 novembre 2025 à 13:57

Objectif

L'objectif du projet est de concevoir un pico-ordinateur complet, intégrant :

  • Une carte mère basée sue le microcontrôleur AT90USB1286

Une partie logicielle permettant l'éxecution de de commandes telles que ls, cp ou mv

Shield Arduino

Une première étape du projet a consisté à développer un shield pour Aduino uno, servant de plateforme de test et de développement pour les cartes filles SPI.

Fonctionalités:

  • Connexion de 5 périphériques SPI via des cartes filles.
  • Gestion des signaux Reset et Interruption.
  • Ajout d'une mémoire externe carte micro-SD via un connecteur Molex 10431.
  • Adaptation des niveaux logiques (5V a 3,3V) grâce à la puce 74LV125.

Ce shield joue le rôle de plateforme de développement temporaire, en attendant la carte mère du pico-ordinateur.

Schématique et routage

Schema shield arduino.jpg

Objectif

Schema shield arduino


Routage shield arduino


Carte mère

Schématique

Schéma carte mère du pico ordinateur



Firmware — RTOS (version restructurée et en français)

1. Vue d’ensemble du système

Le firmware implémente un petit RTOS coopératif optimisé en mémoire pour ATmega (ex. ATmega328P). Principales caractéristiques :

  • Ordonnanceur round-robin simple.
  • Table des tâches statique (pas d’allocation dynamique).
  • Gestion des sections critiques imbriquées.
  • Timer système à 100 Hz pour la gestion des ticks et du sommeil.

2. Arborescence (extrait)

firmware/
 ├─ kernel/
 │  ├─ kernel.h
 │  ├─ kernel.c
 │  ├─ scheduler.h
 │  ├─ scheduler.c
 │  ├─ task.h
 │  └─ task.c
 ├─ config.h
 └─ main.c

3. Paramètres de configuration

#define MAX_TASKS       4      // nombre max de tâches
#define STACK_SIZE      96     // octets par pile de tâche
#define TICK_FREQUENCY  100    // Hz — fréquence du tick système
#define TASK_NAME_LEN   8

4. Structure de contrôle de tâche (TCB)

typedef enum {
    TASK_INVALID = 0,
    TASK_READY,
    TASK_RUNNING,
    TASK_SLEEPING
} task_state_t;

typedef struct {
    void (*function)(void *);
    void *arg;
    uint8_t *stack_base;
    task_state_t state;
    uint16_t sleep_ticks;
    uint8_t priority;
    char name[TASK_NAME_LEN];
} task_t;

5. Gestion des sections critiques (imbriquées)

#include <avr/interrupt.h>
static volatile uint8_t critical_nesting = 0;

static inline void enter_critical(void) {
    cli();
    critical_nesting++;
}

static inline void leave_critical(void) {
    if (critical_nesting > 0) {
        critical_nesting--;
        if (critical_nesting == 0) sei();
    }
}

6. Création de tâche

int8_t task_create(const char *name, void (*function)(void *), void *arg,
                   uint8_t priority, uint8_t *stack_buffer)
{
    for (uint8_t i = 0; i < MAX_TASKS; i++) {
        if (task_table[i].function == NULL) {
            task_table[i].function = function;
            task_table[i].arg = arg;
            task_table[i].stack_base = stack_buffer;
            task_table[i].state = TASK_READY;
            task_table[i].priority = priority;
            strncpy(task_table[i].name, name, TASK_NAME_LEN);
            return i;
        }
    }
    return -1;
}

7. Ordonnanceur (round-robin)

static uint8_t current_task_id = 0;

static inline uint8_t is_task_valid(uint8_t id) {
    return (id < MAX_TASKS) &&
           (task_table[id].function != NULL) &&
           (task_table[id].state == TASK_READY);
}

static uint8_t get_next_task(void) {
    uint8_t next = (current_task_id + 1) % MAX_TASKS;
    for (uint8_t i = 0; i < MAX_TASKS; i++) {
        if (is_task_valid(next)) return next;
        next = (next + 1) % MAX_TASKS;
    }
    return current_task_id;
}

void scheduler_run(void) {
    enter_critical();
    if (is_task_valid(current_task_id)) {
        task_table[current_task_id].state = TASK_RUNNING;
        leave_critical();
        task_table[current_task_id].function(task_table[current_task_id].arg);
        enter_critical();
        if (task_table[current_task_id].state == TASK_RUNNING)
            task_table[current_task_id].state = TASK_READY;
    }
    current_task_id = get_next_task();
    leave_critical();
}

8. Mécanismes de sommeil et tick système

void timer1_init_100hz(void) {
    TCCR1A = 0;
    TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);
    OCR1A = 2499;
    TIMSK1 |= (1 << OCIE1A);
}

ISR(TIMER1_COMPA_vect) {
    extern volatile uint32_t system_ticks;
    system_ticks++;
    for (uint8_t i = 0; i < MAX_TASKS; i++) {
        if (task_table[i].state == TASK_SLEEPING && task_table[i].sleep_ticks > 0) {
            task_table[i].sleep_ticks--;
            if (task_table[i].sleep_ticks == 0)
                task_table[i].state = TASK_READY;
        }
    }
}

void task_sleep(uint16_t ticks) {
    if (ticks == 0) ticks = 1;
    enter_critical();
    task_table[current_task_id].sleep_ticks = ticks;
    task_table[current_task_id].state = TASK_SLEEPING;
    leave_critical();
    scheduler_run();
}

9. Initialisation du système

  1. Mise à zéro des structures de données.
  2. Initialisation du timer à 100 Hz.
  3. Création de la tâche d’« idle ».
  4. Activation des interruptions globales.
  5. Lancement de la boucle principale qui exécute `scheduler_run()`.

10. Gestion d’erreurs

  • Vérification de la validité des pointeurs de fonctions.
  • Conversion des durées nulles (`sleep(0)`) en `sleep(1)`.
  • Gestion des appels imbriqués de sections critiques.
  • Fallback automatique sur la tâche idle si aucune tâche prête.

11. Estimations mémoire

  • Piles : 4 × 96 = 384 octets
  • TCB : 4 × 28 = 112 octets (approx.)
  • Variables globales : ~20 octets
  • **Total estimé :** ~516 octets

12. Intégration du build system

MCU = atmega328p
F_CPU = 16000000UL
CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Os -Wall -std=c99