Proj-2011-2012-qemu

=Émulation de la carte STM32L-Discovery avec Qemu= Enseignant:
 * Olivier Richard

Etudiants:
 * CLAVELIN Aurélien
 * EID Timothée
 * MERCIER Michael

Le but de ce projet est de d'ajouter le support de la carte STM32L-Discovery dans le logiciel de virtualisation Qemu. Il fait suite au projet de l'année dernière (fiche projet) basé lui aussi sur Qemu mais portant sur la carte Stellaris Luminary Lm3s6965.

Qemu
Voir Qemu

Téléchargement
Lien vers le projet à venir...

Description
Afin d'émuler la carte STM32L-Discovery les fichiers suivant ont été ajoutés au dossier src/hw de Qemu :
 * stm32.c pour décrire la carte dans son ensemble
 * stm32_gpio.c pour les GPIOs
 * stm32_button.c pour les boutons
 * stm32_led.c pour les LEDs
 * stm32_rcc.c pour le module RCC (ce module n'est que très partiellement émulé et donc non fonctionnel)

Processeur et Mémoire
Le processeur utilisé par la carte est un processeur Cortex-M3 r1p1 (doc) déjà supporté par Qemu.

Pour initialiser celui-ci, il est nécessaire d'utiliser la fonction MemoryRegion *address_space_mem = get_system_memory; pic = armv7m_init(address_space_mem, flash_size, sram_size, kernel_filename, cpu_model); où pic est un tableau de qemu_irq (voir Qemu) correspondant aux fils d'entrée dans le processeur. Dans notre cas les tailles des différentes mémoires sont :
 * RAM: 16 KBits
 * Flash: 128 Kbits

GPIO
L'émulation des GPIOs est gérée par le fichier stm32_gpio.c. Les GPIOs sont les connecteurs de base de la carte. Ils permettent de communiquer avec le bouton USER 0 et les LEDs.

Périphériques
L'émulation ne porte pour l'instant que sur les deux LEDs (stm32_led.c) et le bouton USER0 (stm32_button.c) qui communiquent avec l'extérieur de Qemu grâçe à chardev.

Exemple
Nous allons détailler la composition d'un composant à partir de l'exemple de stm32_gpio
 * Tout d'abord il faut inclure sysbus
 * 1) include "sysbus.h"

typedef struct { SysBusDevice busdev; /* Registres GPIO (Reference Manual p119 */   uint32_t mode; /* Mode */    uint16_t otype; /* Output type */    uint32_t ospeed; /* Output speed */    uint32_t pupd; /* Pull-up/Pull-down */    uint16_t ind; /* Input data */    uint16_t outd; /* Output data register */    uint16_t outd_old; /* Output data register */    uint32_t bsr; /* Bit set/reset */    uint32_t lck; /* Lock */    uint32_t afrl; /* Alternate function low */    uint32_t afrh; /* Alternate function high */    qemu_irq irq_out[NB_PIN];    unsigned char id; } stm32_gpio_state;
 * Les registres utilisés par le composant sont ensuite placés dans une structure :

static const VMStateDescription vmstate_stm32_gpio = { .name = "stm32_gpio", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) {       VMSTATE_UINT32(mode, stm32_gpio_state), VMSTATE_UINT16(otype, stm32_gpio_state), VMSTATE_UINT32(ospeed, stm32_gpio_state), VMSTATE_UINT32(pupd, stm32_gpio_state), VMSTATE_UINT16(ind, stm32_gpio_state), VMSTATE_UINT16(outd, stm32_gpio_state), VMSTATE_UINT16(outd_old, stm32_gpio_state), VMSTATE_UINT32(bsr, stm32_gpio_state), VMSTATE_UINT32(lck, stm32_gpio_state), VMSTATE_UINT32(afrl, stm32_gpio_state), VMSTATE_UINT32(afrh, stm32_gpio_state), VMSTATE_END_OF_LIST } };
 * Cette structure est décrite sous la forme d'une VMStateDescription permettant à Qemu de l'enregistrer correctement

static SysBusDeviceInfo stm32_gpioA_info = { .init = stm32_gpio_init_A, .qdev.name = "stm32_gpio_A", .qdev.size = sizeof (stm32_gpio_state), .qdev.vmsd = &vmstate_stm32_gpio, };
 * Une structure SysBusDeviceInfo renseigne la fonction d'initialisation, le nom, la taille de la structure des registres ainsi que la VMStateDescription.

static void stm32_gpio_register_devices(void) { sysbus_register_withprop(&stm32_gpioA_info); } device_init(stm32_gpio_register_devices)
 * Elle est passée en paramètre à la fonction sysbus_register_withprop elle même passée en paramètre de la macro device_init

static int stm32_gpio_init(SysBusDevice *dev, const unsigned char id) { int iomemtype; stm32_gpio_state *s = FROM_SYSBUS(stm32_gpio_state, dev); s->id = id; //Initialisation de la plage mémoire iomemtype = cpu_register_io_memory(stm32_gpio_readfn, stm32_gpio_writefn, s, DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x24, iomemtype); //Initialisation des pins qdev_init_gpio_in(&dev->qdev, stm32_gpio_in_recv, NB_PIN); qdev_init_gpio_out(&dev->qdev, s->irq_out, NB_PIN); //Initialisation stm32_gpio_reset(s); vmstate_register(&dev->qdev, -1, &vmstate_stm32_gpio, s); return 0; }
 * Voici la fonction d'initialisation du composant :

les structures stm32_gpio_readfn et stm32_gpio_writefn sont respectivement une CPUReadMemoryFunc et une CPUWriteMemoryFunc et contiennent les fonctions de lecture et d'écriture sur les registres déclarés dans le stm32_gpio_state. Ces fonctions sont de la forme : static uint32_t stm32_gpio_read(void *opaque, target_phys_addr_t offset) { stm32_gpio_state *s = (stm32_gpio_state *) opaque; switch (offset) { case 0x00: /* Mode */ return s->mode; case 0x04: /* oType */ return s->otype; case 0x08: /* oSpeed */ return s->ospeed; ...

Le fichier stm32_gpio.c complet est disponible

Interface graphique
Une interface graphique utilisateur (UI) a été développée afin de permettre à l'utilisateur une meilleure prise en main. Qemu se connecte à l'UI par des sockets fournis par les modules des deux LEDs et du bouton via chardev. Le schéma 1 résume ce fonctionnement.



L'UI a été développée en Python2.7 avec la bibliothèque tkinter.

Glossaire des acronymes
Vous trouverez le glossaire ici