Stellaris Luminary Lm3s6965
N'hésitez pas à enrichir cette page
Ce kit est construit autour d'un microcontrolleur ARM Cortex-M3 Lm3s6965 de marque Luminary Micro appartenant au groupe Texas Instrument.
Les principales caratéristiques et intérêts de ce kit sont:
- Processeur ARM 32bit Cortex-M3
- Port Ethernet
- Ecran OLED monochrome 128 x 64 pixels (16 niveaux)
- Un bon support d'OpenOCD
- Support de eLua (port du langage de script Lua)
Ressources
- Site officiel du kit Stellaris Luminary Lm3s6965
- LM3S6965: Tips & Tricks / OpenOCD : [1]
- Building toolchain and OpenOCD for Stellaris on Linux : [2]
- Test qemu + arm + gdb + ld ... :[3]
- ARM Projets divers : [4]
- Tutorial général: Media:Tuto_lm3s6965.pdf
- Le document technique (datasheet) du microcontrolleur Lm3s6965: Media:Datasheet-LM3S6965.pdf
Exemples d'utilisation
Les exemples ci-dessus rassemblent un ensemble d'outils et de codes complétant ceux proposé par le constructeur dans une vision opensource. Une image virtuelle est aussi fournie, voir ci-dessous.
Debut avec la Carte Stellaris
Pour programmer la carte, il faut tout d'abord installer la chaîne de compilation.
Compilation du toolchain
Suivre Binaire Linux pour récupérer la chaîne de compilation Sourcery G++ Lite.
D'autres paquets disponibles sur le site du logiciel
Compilation des exemples
Une fois le toolchain installé, il est intéressant de regarder les fichiers de tests disponible sur le CD d'Installation. L'éxecutable SW-EK-LM3S6965.exe propose un grand nombre de test recompilable à l'aide des fichiers adjacents. Ces codes étant fournis par le constructeur, ce sont eux qui vont valider la bonne émulation de la carte.
Compilation d'exemples plus simples
Les fichiers test-cortex-m3.c, startup_gcc.c et lm3s.ld utilisés ci-aprés sont disponibles dans l'archive: Media:Test-cortex-m3.tar
Construction de l'exécutable
arm-none-eabi-gcc -c -mcpu=cortex-m3 -mthumb test-cortex-m3.c -o test-cortex-m3.o arm-none-eabi-gcc -c -mcpu=cortex-m3 -mthumb startup_gcc.c -o startup_gcc.o arm-none-eabi-ld -T lm3s.ld startup_gcc.o test-cortex-m3.o -o test-cortex-m3.elf arm-none-eabi-nm test-cortex-m3.elf
Juste pour voir le code déassemblé
arm-none-eabi-objdump -d test-cortex-m3.elf
Il faut une version binaire simple à charger et non la version trop riche
arm-none-eabi-objcopy -O binary test-cortex-m3.elf test-cortex-m3.bin
Le binaire peut ensuite être placé(flashé) sur la carte avec OpenOCD ou utilisé avec l'émulateur QEMU, voir ci-dessous.
Chargement du bin sur la carte
Utilisation avec OpenOCD sous linux
Récupérer le script setup_lm3s6965.sh sur [5]. Ce script va récupérer et compiler openocd. Une copie est disponible Media:Setup_lm3s6965.txt
sh setup_lm3s6965.txt
Construire et installer le bon pilote FTDI
wget http://www.intra2net.com/en/developer/libftdi/download/libftdi-0.18.tar.gz #install open FTDI drivers tar -xzf libftdi-0.18.tar.gz cd libftdi-0.18/ sudo apt-get install libusb-dev ./configure make sudo make install sudo ldconfig
On lance openocd en root
sudo openocd -f /usr/local/share/openocd/scripts/board/ek-lm3s6965.cfg
Et on peut flash le microcontrolleur
#flash mcu with some code at address 0x00000 and execute telnet localhost 4444 halt flash write_image erase /tmp/main.bin 0 reset
Exemple avec le déboggeur gdb
Debut avec QEMU
Compilation et première utilisation
Récupération et compilation du qemu version qemu-0.13.0
wget http://wiki.qemu.org/download/qemu-0.13.0.tar.gz tar zxvf qemu-0.13.0.tar.gz cd qemu-0.13.0 ./configure --target-list=arm-softmmu # On configure pour ne pas tout compiler make
Note : La version qemu-0.14.0 a été réalisée le 18/02/11
Lancement de l'émulation avec qemu (faire crtl a + x pour sortir ou crtl a + c pour accéder au moniteur)
./qemu-0.13.0/arm-softmmu/qemu-system-arm -M lm3s6965evb -nographic -kernel test-cortex-m3.bin Hello world! QEMU 0.13.0 monitor - type 'help' for more information (qemu) quit
Moniteur (console de commande et d'interrogation de Qemu)
Qemu dispose d'un moniteur assez puissant. Utilisez l'option -S pour lancer le moniteur dès le démarrage l'émulation sera alors suspendu, puis faire crtl+a c pour accèder à la console d moniteur
./qemu-0.13.0/arm-softmmu/qemu-system-arm -S -M lm3s6965evb -nographic -kernel test-cortex-m3.bin crtl+a c
(qemu) info roms addr=00000000 size=0x0001e0 mem=rom name="test-cortex-m3.bin" (qemu) info registers R00=000001d0 R01=20000000 R02=0000000a R03=00000000 R04=00000000 R05=00000000 R06=00000000 R07=2000fff0 R08=00000000 R09=00000000 R10=00000000 R11=00000000 R12=00000000 R13=2000fff0 R14=000001c9 R15=00000160 PSR=60000173 -ZC- T svc32
Malheureusement pour une émulation des cpus cortex-M3, l'ensemble des fonctions du moniteur ne sont pas toutes supportées. Par exemple le déassembleur intégré ne décode pas correctement les instructions. Pour aller plus loin, il faut utiliser le déboggeur cf suite.
Qemu et GDB
On peut attaché GDB à Qemu
./qemu/qemu-0.13.0/arm-softmmu/qemu-system-arm -M lm3s6965evb -nographic -monitor null -serial null -semihosting -kernel /tmp/hello.bin -S -gdb tcp::51234
arm-none-eabi-gdb /tmp/hello.axf
(gdb) target remote localhost:51234 ... (gdb) load (gdb) break main (gdb) continue Continuing. Breakpoint 1, 0x000000f4 in main () (gdb) stepi 0x00000640 in SysCtlClockSet () (gdb) disassemble 0x640 Dump of assembler code for function SysCtlClockSet: 0x00000640 <SysCtlClockSet+0>: ldr r3, [pc, #308] ; (0x778 <SysCtlClockSet+312>) 0x00000642 <SysCtlClockSet+2>: push {r4, r5, r6, lr} 0x00000644 <SysCtlClockSet+4>: ldr r2, [r3, #0] ....
On peut attaché GDB à Qemu
http://cgi.cs.indiana.edu/~geobrown/stm32/Main/Simulation http://garden.seeedstudio.com/index.php?title=DSO_Nano/Qemu_gdb&redirect=no
Modifications réalisées sur QEMU
Suivi et accès des GPIO en sortie
Le principe va être d'utiliser le code élémentaire de clignotement de LED blinky.bin disponible dans le répertoire StellarisWare puis de modifier le fichier stellaris.c dans repertoire hw de qemu pour repérer et intercepter les changement d'état
Comprendre l'émulation du stellaris proposé par QEMU suppose de se plonger dans le code. On peut notamment explorer les fichiers suivant, liste non exhaustive:
hw/irq.c hw/qdev.c hw/stellaris.c hw/stellaris_input.c hw/sysbus.h hw/irq.h hw/qdev.h hw/stellaris_enet.c hw/sysbus.c qemu-common.h
Il est important d'observer la struct qemu_irq. Dans le stellaris.c nous notons la déclaration suivante
static void stellaris_init(const char *kernel_filename, const char *cpu_model, stellaris_board_info *board) { static const int uart_irq[] = {5, 6, 33, 34}; static const int timer_irq[] = {19, 21, 23, 35}; static const uint32_t gpio_addr[7] = { 0x40004000, 0x40005000, 0x40006000, 0x40007000, 0x40024000, 0x40025000, 0x40026000}; static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31};
qemu_irq *pic; DeviceState *gpio_dev[7]; qemu_irq gpio_in[7][8]; qemu_irq gpio_out[7][8];
La déclaration qemu_irq gpio_out[7][8]; va permettre l'émulation des 8 fils de sortie des 7 port d'E/S (A,B,C,D,E,F,G).
Si on suit 'qemu_irq à travers les fichiers, nous avons:
Dans qemu-common.h qemu_irq est un pointer sur IRQState
typedef struct IRQState *qemu_irq;
IRQState dans hw/irq.c
struct IRQState { qemu_irq_handler handler; void *opaque; int n; };
Dans hw/irq.h:
typedef void (*qemu_irq_handler)(void *opaque, int n, int level);
qemu_irq_handler est un pointeur sur une fonction qui sera appelée lorsque le fils changera de valeur, voir la fonction qemu_set_irq dans irq.c:
void qemu_set_irq(qemu_irq irq, int level) { if (!irq) return;
irq->handler(irq->opaque, irq->n, level); }
On va maintenant maintenant de modifier le fichier stellaris.c pour suivre l'état du bit 0 du port F en sortie lequel est cablé un LED sur la carte physique (utilisé par le code blinky.bin).
static void print_yop(void *opaque, int line, int level) { /* opaque is null, can by use to access to a global state ??? */ printf("poy line %d level %d\n",line,level); }
qemu_irq yop; /*test*/ yop = qemu_allocate_irqs(print_yop, NULL, 1)[0]; /* on alloue de ma mémoire et on positionne le handler, ici print_yop*/
gpio_out[GPIO_F][0] = yop; /* /!\ à faire après l'intialisation de tous les gpio_out, si il y a écrassement */
Archive contenant le fichier modifié stellaris.c et le code de clignotement de LED blinky.bin Media:stellaris_mod_blinky.tar
./qemu-0.13.0/arm-softmmu/qemu-system-arm -M lm3s6965evb -nographic -kernel blinky.bin
poy line 0 level 1 poy line 0 level 0 poy line 0 level 1 poy line 0 level 0 poy line 0 level 1 poy line 0 level 0
Note Ctrl A puis x quitter QEMU, voir les commandes du moniteur pour plus de détail.
Affichage de Caractères sur l'OLED
Ajout d'un registre d'horloge
Les programmes de test de l'OLED fournis ne fonctionnent pas en tant que tels. Qemu affiche une erreur dès le début de l'exécution. Après des recherches dans le code des fonctions utilisées dans les exemples. Il vient qu'il est nécessaire de modifier les fonctions ssys_read et ssys_write de stellaris.c en ajoutant un cas supplémentaire.
case 0x070: /* RCC2 */ return s->rcc2;
et
case 0x070 : /* RCC2 */ //p95 datasheet Register 10: Run-Mode Clock Configuration 2 if(value >> 31 != 0 ){ // if the USERCC2 bit is to 1 we s->rcc=value; // override RCC; } if ((s->rcc2 & (1 << 13)) != 0 && (value & (1 << 13)) == 0) { /* PLL enable. */ s->int_status |= (1 << 6); } s->rcc2 = value; break;
Le champs rcc2 de la structure s a été ajouté également. Ce champ correspond au registre Run-Mode Clock Configuration 2 (RCC2) décrit dans le datasheet.
Ajout d'un registre déprécié
Rédaction en Cours
Documentation
Une documentation sommaire a été réalisée afin que les personnes continuant le projet puisse comprendre ce qui a déjà été fait. Elle contient également quelques explications de la structure de Qemu et les fonctions. Le code n'étant pas très commenté, il est difficile à appréhender.
Présentation Générale
Ce texte présente brievement comment marche globalement l'émulation de la carte Stellaris et quelques points importants pour comprendre le fonctionnement.
De la documentation spécifique à certains fichier est disponible. Elle est plutot sommaire la plupart du temps mais permet d'appréhender mieux le système.
La majeur partie de l'émulation de la carte Stellaris est réalisée dans le fichier stellaris.c du dossier hw de Qemu. On peut trouver dans ce fichier la déclaration de plusieurs structures correpondant au convertisseur analogique numérique (adc), ssi, timer (gptm) et i2c. Ensuite, on trouve les fonctions de lecture et d'ecriture dans les registres systemes. Les 29 Registres systemes sont décrit dans le DataSheet de Stellaris. (Attention, certains commentaires de code redirige vers un Datasheet plus ancien que celui qu'on peut trouver sur le site du constructeur)
Stellaris utilise également des éléments extérieurs pour fonctionner. L'OLED fait parti de ces éléments, étant donné qu'il peut être utilisé dans d'autres carte.
Pour comprendre comment est émulé la carte, il est important de partir des tests
de base proposés sur le CD de Stellaris. Les bin donnés en paramètres de qemu
"simulent" déjà une partie du code donné.
Par exemple : le fichier hello.c appelle la fonction RIT128x96x4StringDraw avec
le paramètre "Hello World!". La compilation du programme va "déjà" modifier le paramètre
entré pour le mettre sous la forme de donnée en hexadécimal transmises à l'OLED.
C'est logique, étant donné que les bin seront éxécutés aussi sur la carte.
Cependant la compréhension de ce code est indispensable pour pouvoir ajouter des
fonctionnalités à qemu. Il faut savoir quelles sont les infos contenues dans le bin
afin de pouvoir les traiter et simuler le comportement réél.
Exemple pour une fonction largement répandue dans les fichiers de test :
Decomposition de l'exécution de l'appel à SysCtlClockSet (fichier sysctl.c) Sortie console : //Debut Qemu Trying to read at offset : 0x0 //Printf ajoutés dans ssys_read Trying to read at offset : 0x0 Début fonction : l 1367 Ne rentre pas dans le if du début : CLASS_IS_SANDSTORM && (ulConfig & SYSCTL_RCC2_USERCC2) Lecture de RCC et RCC2 : l 1389 Trying to read at offset : 0x60 ulRCC = HWREG(SYSCTL_RCC); Trying to read at offset : 0x70 ulRCC2 = HWREG(SYSCTL_RCC2); Calculs... Ecriture de RCC et RCC2 : l 1402 Trying to write 0x78e3ac0 at offset : 0x60 HWREG(SYSCTL_RCC) = ulRCC; Trying to write 0x800 at offset : 0x70 HWREG(SYSCTL_RCC2) = ulRCC2; N'entre pas dans le if : (((ulRCC & SYSCTL_RCC_IOSCDIS) && !(ulConfig & SYSCTL_RCC_IOSCDIS)) || ((ulRCC & SYSCTL_RCC_MOSCDIS) && !(ulConfig & SYSCTL_RCC_MOSCDIS))) L'oscillateur n'a pas besoin d'être activé Calculs... Ecriture MISC : l 1468 // Clear the PLL lock interrupt HWREG(SYSCTL_MISC) = SYSCTL_INT_PLL_LOCK; Trying to write 0x40 at offset : 0x58 Passe dans le else : !ulRCC2 & SYSCTL_RCC2_USERCC2 Ecriture de RCC et RCC2 : l 1480 Trying to write 0x78e3b80 at offset : 0x60 HWREG(SYSCTL_RCC) = ulRCC; Trying to write 0x2800 at offset : 0x70 HWREG(SYSCTL_RCC2) = ulRCC2; Ecriture de RCC et RCC2 : l 1537 Trying to write 0x78e3b80 at offset : 0x60 HWREG(SYSCTL_RCC) = ulRCC; Trying to write 0x7802800 at offset : 0x70 HWREG(SYSCTL_RCC2) = ulRCC2; QEMU: Terminated
Le fait de dérouler le code de l'appel de la fonction nous a permis de cibler les erreurs et de comprendre comment les traiter. De cette facon nous avons pu émuler l'horloge. Cette façon d'avancer dans le developpement est assez fastidieuse mais assure de ne pas faire d'erreur et d'avoir bien compris ce qu'il se passe. L'utilisation d'un outil de debuggage est recommandé.
SSD0323
Le fichier ssd0323 correspond à l'interface entre l'OLED et le bus SSI sur la carte. Les les commandes et données du bus SSI sont passées en paramètre de la fonction ssd0323_transfer. A partir de cette fonction, on pilote l'OLED selon les spécification fournie dans la documentation. (voir Treiber_IC-SSD0323_OLED_128x64_GELB et SSD1329_1.1) De base, l'interface émulée etait la SSD0323 commme l'indique le fichier, mais il a fallu ajouter des fonctions comprise dans le SSD1329 pour qu'il n'y ait plus d'erreur apparante à l'éxécution.
Details de la fonction ssd0323_transfer : Cette fonction récupère commandes et données. Pour faire la différence entre les deux il faut regarder le champ mode de la structure. Pour les données, un travail effectué pour conserver la position du curseur de l'OLED Pour les commandes, une astuce est utilisée pour recuperer la commande dans un premier temps puis les paramètres. En fait, tant que le nombre de paramètre n'est pas bon, le paramètre courant est stocké. (Voir le traitement fait à l'entrée du case l93 et la macro DATA définie ) Le detail des commandes est un peu commenté, pour plus d'info voir les fichier sur le SSD0323 et SSD1329 à la section de la liste des commandes.
Details de la structure : Les champs de la structures sont utiles pour la simulation des commandes. Les noms des entiers utilisés sont assez explicite. De plus il est assez intuitif d'avoir un SSIDev dans la structure étant donné que c'est l'interface. De même pour la présence du DisplayState.
PL061
Le fichier pl061.c correpond aux registres GPIO. Il a la même structure que les autres fichiers (fonction pour lire/ecrire/initialiser, structure d'info ...) Le pl061 est connecté à l'APB (Advanced Peripheral Bus) sur la carte le Bus utilisé lors d'un accès à un péripherique. Les GPIO sont très souvent utilisés dans les programmes de test disponible (OLED par exemple)
Détails sur différentes structures
Details sur la création d'un device
La fonction qdev_create est utilisée pour creer des peripheriques. Elle est située dans le fichier qdev.c. Cette fonction n'est pas directement appellée dans stellaris.c. On peut la trouver par exemple dans ssi_create_slave. La fonction a besoin d'information sur le péripherique pour pouvoir le creer. De telles informations sont déclarées dans des structures que l'on peut trouver dans stellaris (par exemple). Ces structures ont un formatage particulier, il faut faire attention. Il faut également spécifier un bus, sinon c'est celui du système qui est pris par défaut.
Exemple :
static SSISlaveInfo stellaris_ssi_bus_info = { .qdev.name = "evb6965-ssi", .qdev.size = sizeof(stellaris_ssi_bus_state), .init = stellaris_ssi_bus_init, .transfer = stellaris_ssi_bus_transfer };
Details sur l'ajout d'un device
Dans qdev.c on peut trouver la fonction suivante : DeviceState *qdev_device_add(QemuOpts *opts) Cette fonction recupère les options pour creer un driver. Ensuite elle recupère les infos relatives à ce driver puis le bus correspondant A partir du bus et des infos elle créée un device et met les propriétés.
Détails sur la création d'un QemuOpts
Dans qemu-options.c on trouve la fonction suivante : QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, int fail_if_exists) Elle se contente de verifier la forme de l'id et recuperer les options dans la liste afin de les placer dans la structure à retourner.
Les QemuOptsList sont à initialiser à la main à priori le detail de cette structure se trouve dans qemu-options.h
Image de machine virtuelle
L'image de machine virtuelle ci-dessous contient les outils adaptés pour le LM3S6965EVB comme présenté précédemment: la chaîne de compilation Sourcery G++ Lite, l'outil de pilotage de sonde matérielle OpenOCD, et QEMU.
- Image de système au format vdi pour VirtualBox: [6]
FAQ
Quels sont les composants reconnus de la carte?
- Extrait de la Documentation Officielle :
The Luminary Micro Stellaris LM3S6965EVB emulation includes the following devices: Cortex-M3 CPU core. 256k Flash and 64k SRAM. Timers, UARTs, ADC, I^2C and SSI interfaces. OSRAM Pictiva 128x64 OLED with SSD0323 controller connected via SSI.