Temps reel et lecture de port

Bonjour,

J'ai un petit problème de temps reel. (Arduino Mega 2600)

Je souhaite lire l’écriture dans une adresse mémoire externe d'un processeur Z80 cadancé à 3.5 MHz. Actuellement, une interruption est généré lorsque cette adresse est atteinte en écriture.

Des que l'adresse du z80 est 0xFFFF ET que le z80 ecrit (ligne /WR du z80 LOW), une interruption hardware INT 2 est généré.

Un circuit discret s'occupe de regarder l'adresse et la ligne /WR, et genere une impulsion LOW quand la condition cité est respecté. C'est cette impulsion qui est lié a INT2 de l'arduino.

Les 8 bits de données sont connecté a un port complet de l'arduino.

#define PINDATA     PIN[X]  //Definition du port 8 bit de donné du Z80

virtual uint8_t buf[256]; 
virtual uint8_t bufPos = 0; 
// Access to 0xFFFF
ISR(INT2_vect){
    // Lecture du port z80, au plus proche de l'interruption.
    uint8_t data = PINDATA; 

    // Enregistrement dans le buffer
    buf[bufPos++] = data;
}

void loop(){

    //...

    // Traitement du buffer
    while(bufPos){
        // C'est bien un write et non print, un logiciel java coté PC s'occupe de 
        // recevoit bit a bit le buffer et de l'afficher comme int a l'ecran.
        Serial.write(buf[--bufPos]);
    }

    //...
}

Les ecritures a 0xFFFF ne sont pas trop fréquentes (le buffer n'est jamais plein, d’ailleurs je peux baissé la taille de 256 a 64) et de manière générale elle sont de 1 écriture par milliseconde. Les interruptions sont généré quand il faut (Avec la latence du circuit discret, plus ou moins 80 nanosecondes.).

Mon problème est que mes données sont inconsistantes. Elle ne sont pas celle que j'attendais. J'ai l'impression que quand je lit PORTDATA (uint8_t data = PORTDATA;), le Z80 a déjà changé d'adresse.

Y a t'il un possibilité d'accélérer la lecture pour qu'elle se fasse au plus proche de l'interruption ? Via assembleur par exemple ? en enregistrant la valeur dans un registre de l'arduino ?

Merci d'avance !

C’est quoi un MEGA 2600?

C’est quel registre PINDATA?

Combien de temps vos données restent stables faudrait peut être un latch hardware si c’est trop court pour votre arduino

Pardon, c'est un MEGA 2560

PINDATA c'est PINL

Les données sont stables pendant 1.5 cycle d'horloge du Z80 soit 427.5 nanosecondes. Une instruction dure 62.5 nanosecondes sur arduino, d'ou mon idée de limiter au max les instructions. (ça laisse 6 instruction, je sait c'est pas énorme)

J'aurais aimé me passé hardware latch, j'ai déjà ma breadboard pleine ! cela me rajouterais une surcouche de fils + uc dont j'aimerais me passer, même si c'est techniquement simple a mettre en place avec un parallèle in to serial out.

De plus ce "sampling" a un but de debug, rajouté un hardware assez lourd pour du debug ne me plais pas trop.

Donc si il existe une méthode software pour capturer un port de 8 bit de l'Arduino a l'instant d'une interruption je suis preneur.

Bonjour,

Ca me parait plus que difficile. L'entrée en interruption prend 4 cycles minimum et il faut sauvegarder le status, et éventuellement les registres que tu vas utiliser.

Apparemment c'est plus 5 cycles, mais SANS COMPTER la sauvegarde des registre dans la pile. On est plus a 20 cycles avec une interruption classique.

Je viens de voir qu'il existait des interruption NAKED (ISR_NAKED) qui dispense de la sauvegarde de registre. La on se retrouve 5 cycles après generation de l'INT

l'idée serait : - Je déclare l'interruption ISR_NAKED, //Temps critique - Je push un registre (r3 par exemple) //Temps critique - Je sauvegarde le registre PINL dans r3 //Temps critique - La je souffle, je sauvegarde r3 dans une variable partagé, un buffer - Je pop r3 - Et fin de l'interruption (reti())

Mais même comme ça je crois que je serais juste en temps...

Vous pouvez effectivement essayer de creuser l'option ISR NAKED en utilisant une astuce qui va lier une variable à un registre register unsigned char counter asm("r3"); (r2 à r7 généralement OK, éventuellement r0 ou r1 en suivant la note ci dessous) en la déclarant globale ça pourrait vous permettre d'éviter de sauver le contexte en rentrant dans l'ISR(plus de détails sur les registres utilisés par le compilateur)

Vous n'éviterez jamais la sauvegarde du PC et le jump à l'adresse de l'interruption, je crois que c'est 5 cycles de mémoire - ça vous laisse 1 cycle pour l'écriture de registre à registre - qui est justement le temps d'exécution d'un MOV

donc c'est limite mais ça vaut le coup de tester...

Bonjour,

Merci pour toutes ces informations. Dites donc, du condensé de connaissance !

J'ai l'impression que ce n'est pas du tout possible de fonctionner avec de interruptions dans mon cas.

Mais en tous cas j'ai appris énormément grâce a vous merci. Je garde sous le coude ces codes qui me servirons bientot pour un tout autre projet a temps critique.

Sinon je sais que l'architecture sur laquel je travaille enregistre en mémoire la valeur que je souhaite récupéré. J'ai donc la solution de "volé" le bus au z80 (effectuer un bus reguest, normalement utiliser pour les access DMA) pour aller lire la valeur a l'adresse souhaité lorsque l'interruption est généré. Cette méthode ne me plais pas trop car elle est avasive, elle va modifier le temps d’exécution du z80 (qui va "freezer" le temps de l'acces DMA).

J'ai un peu chercher, et apparemment les PCINTs ne permettes pas une capture du port aillant généré l'interruption, le confirmez vous ?

Je m'explique : j'ai un IO expander, capable de généré une interruption sur le port. Lorsque une interruption est généré, une pin interruption est mise a l'etat bas, et un registre de capture interne a l'io expander capture le port au moment de l'interruption.

Ce mécanisme existe il sur l'atmega 2560 ?

Oui je sais qu'effectivement le MCU est loin d'etre la meilleur approche... Maintenant j'en ai un exemple parlant !

Quand tu dit qu'un MCU cadencé plus rapide (par exemple le duo, 84MHz) ne sert a rien, tu veux dire par la que les E/S sont elle synchronisé a une horloge plus basse (par exemple 16MHz ou 20 MHz) ?

Donc par exemple, si on fait un toggle d'une pin :

loop:
    sbi PORTX, _BV(0)
    cbi PORTX, _BV(0)
    jp loop

(Pardon pour les erreurs de syntaxe) On sera plus proche de 16-20MHz que de 42 MHz ?

L'acces au registre, se fait avec synchro (ça bloque tant que ça latche pas) ou la boucle s’exécute en parallèle du latche ? (Je sais pas si je suis clair dans ma question)

Je possede un duo pour justement profité de ça rapidité de calcul. Les acces E/S etant assez critique, cet info m'est tres utile, merci !

Avec la carte Arduino DUE, il est très facile d'obtenir une fréquence de sortie sur un bit de 16 Mhz, 42Mhz et même 84Mhz. Voir par exemple ici : http://ko7m.blogspot.fr/2015/02/high-frequency-due-waveform-output.html

Pour capturer cette fois une transition reçue d'un Z80 sur une pin du DUE, il est aussi très facile de le faire en générant une interruption. Voir par exemple la manip indiquée sur le Sam3x datasheet, chapitre 31.5.10 page 625.

Si par ailleurs on veut capturer l'état d'un port à partir d'un évènement, cette capture prend de 3 à 4 cycles d'horloge, soit 4* 11.9 nano secondes. Donc on pourra détecter des fréquences allant jusqu'à au moins 21 Mhz.

Merci encore.

Je déguste cette conversation !

@ard_newbie : Lien tres inintéressant ! Sauvegardé :) . Bien que @pepe ai raison, on parle ici d'une gestion purement software et non hardware des IOs.

Ceci dit, la gestion hardware de fréquence permet d'utiliser le PWM avec une définition bien plus importante, utile lors de contrôle de galvo par exemple.

@pepe : Ok c'est bien ce que j'avais compris. Il y a une limite matériel. La ou je ne suis pas d'accord avec toi, c'est qu'en cas de controle PID par exemple, la boucle sur un UNO ou un Mega 2560sera bien plus lente a exécuter que sur un Due.

Dans ce genre de cas de figure, on profite des 84MHz, et on doit etre a largement 5fois plus rapide (sans compter que le Due bosse sur 32 bits), non ?

Apres dans la lecture pur et brut de port, je me doute que le gains est minime.

Oui désolé je me suis un peu éloigné du sujet, car la question du PID m'interresse. Dans le PID, il y a aussi une question de gestion de signaux. J'ai crus que tu généralisais. Quoi qu'il en soit je ne voulais pas t’offenser !

Pour en revenir au sujet, je vais comme je l'ai dit passé par la fonction DMA du Z80 pour spoilé le bus mémoire et récupérer la valeur dont j'ai besoin.

Apres tout pour du debug tant pis pour la perte de temps engendré pour le z80, il n'y verra que du feu.

Niveau temps machine, je serai beaucoup plus relax du coup. Il faut quand meme que j'assure le fait qu'une autre écriture a l’adresse qui m'interresse ne se produise pas entre l'interruption et l'envoi du bus request (demande d'acces DMA). J’aurais environ une vingtaine de cycle d'horloge du z80 pour le faire, une eternité comparé au 1.5 cycle de mon problème.