🛠 Maîtriser les E/S sur Sapphire RV64 : GPIO et Protocoles
Objectifs
Ce tutoriel vise à implémenter une chaîne d'acquisition de données utilisant les interfaces natives du SoC Sapphire RV64. Nous explorerons la configuration des GPIO pour le contrôle de périphériques simples et l'utilisation des maîtres I2C/SPI pour communiquer avec des capteurs ou affichages. L'accent est mis sur l'optimisation des accès mémoire via le bus AXI4 et la gestion des interruptions.
Matériel nécessaire
- Carte FPGA Efinix (gamme Titanium, Topaz ou Trion T20+)
- Logiciel Efinity 2026.1 ou supérieur
- SoC Sapphire RV64 configuré avec au moins 1 cœur, extension A (atomics) activée
- Périphériques externes : capteur I2C (ex: BME680) ou écran SPI
- Câblage JTAG pour le débogage
Étapes
- Configuration du SoC dans Efinity : Ouvrez votre projet et accédez à la configuration du cœur VexiiRiscv. Activez les interfaces souhaitées. Notez que le Sapphire RV64 supporte jusqu'à 32 GPIO, 5 maîtres I2C et 3 maîtres SPI. Assurez-vous que les adresses de base des périphériques (ex: 0x01000000 pour GPIO) sont définies sans conflit dans la carte mémoire.
- Génération du bitstream : Compilez le projet pour générer le bitstream. Vérifiez que le synthétiseur ne rapporte pas de conflits de broches. Si vous utilisez la mémoire externe DDR3 ou LPDDR4x, assurez-vous que le contrôleur mémoire est correctement initialisé dans le bootloader.
- Développement du driver GPIO : Dans votre code C/C++, définissez les registres de contrôle. Par exemple, pour configurer une broche en sortie :
#define GPIO_BASE 0x01000000
#define GPIO_DIR (GPIO_BASE + 0x00)
#define GPIO_OUT (GPIO_BASE + 0x04)
void gpio_set_output(int pin) {
*(volatile uint32_t*)GPIO_DIR |= (1 << pin);
}
Utilisez les instructions atomiques (extension A) si plusieurs cœurs accèdent aux mêmes GPIO pour éviter les conditions de course.
- Implémentation du protocole I2C/SPI : Pour un capteur I2C, configurez le maître I2C via ses registres de statut et de données. Le bus AXI4/APB3 gère les transactions. Exemple d'écriture d'un octet :
#define I2C_BASE 0x02000000
void i2c_write(uint8_t addr, uint8_t data) {
// Attendre que le bus soit libre
while (*(volatile uint32_t*)(I2C_BASE + 0x04) & 0x01);
*(volatile uint32_t*)(I2C_BASE + 0x08) = addr;
*(volatile uint32_t*)(I2C_BASE + 0x0C) = data;
*(volatile uint32_t*)(I2C_BASE + 0x00) = 0x01; // Start
}
- Gestion des interruptions : Configurez le contrôleur d'interruptions pour réagir aux événements matériels (ex: fin de transfert SPI). Cela libère le CPU pour d'autres tâches, crucial avec le pipeline à 7 étages.
Vérification
Utilisez un analyseur logique pour vérifier les signaux sur les broches physiques. Sur le logiciel, imprimez les valeurs lues du capteur via UART. Vérifiez que les points d'arrêt matériels (jusqu'à 4 disponibles) fonctionnent correctement si vous déboguez le code du driver.
Pour aller plus loin
Explorez l'utilisation du cache L2 partagé pour accélérer l'accès aux tampons de données. Testez la MMU SV39 pour isoler les drivers dans l'espace utilisateur sous Linux si le système d'exploitation est déployé.
Autres tutoriels pour cette carte
Exemples de code pour cette carte
gpio_blink_riscv.inoC (RISC-V)uart_echo_riscv.inoC (RISC-V)spi_sensor_read.inoC (RISC-V)i2c_master_write.inoC (RISC-V)timer_interrupt_handler.inoC (RISC-V)i2c-bmp280-temperature-6.inoC