[THOMSON] ARDDRIVE

Cette catégorie traite de développements récents pour nos vieilles machines, applications, jeux ou démos... Amis programmeurs, c'est ici que vous pourrez enfin devenir célèbres!

Modérateurs : Carl, Papy.G, fneck

hlide
Messages : 810
Enregistré le : 29 nov. 2017 10:23

Re: [THOMSON] ARDDRIVE

Message par hlide » 11 avr. 2019 18:21

Daniel a écrit :
11 avr. 2019 17:40
Quand le 6809 lit l'adresse $A7BF l'Arduino a le temps de détecter la demande de lecture (par polling ou interruption, j'essaierai les deux méthodes), mais pas le temps en 1 µs de lire l'octet de la carte et de le renvoyer via le port D. Il faut donc une première lecture pour demander l'octet, puis une deuxième pour le lire 4 µs plus tard. Enfin c'est ce que j'imagine, reste à tester...
Ok, entre le moment où il prend connaissance de la demande de lecture du 6809 et la récupération de l'adresse présenté, il se passerait trop de temps. J'ose espérer que tu n'utilises pas "digitalRead" pour ça. Tu fais un "byte addr = PIND;" juste après le polling ?

Après je vois un autre problème, l'adresse envoyé au SD va retourner un octet qui peut prendre du temps et manquer de répondre au 6809. Sur un Z80, on utilise le signal /WAIT le temps de pouvoir présenter l'octet sur le bus de donnée. Je n'arrive pas à trouver l'équivalence sur 6809.
Modifié en dernier par hlide le 11 avr. 2019 18:42, modifié 1 fois.

hlide
Messages : 810
Enregistré le : 29 nov. 2017 10:23

Re: [THOMSON] ARDDRIVE

Message par hlide » 11 avr. 2019 18:38

On parle bien d'un accès aux registres du contrôleur SD quand tu parles d'adresse fournie par le 6809 ? parce que si c'est le cas, je ne vois pas comme un port dual peut aider ici. Éventuellement, il doit y avoir un registre qui te retourne un octet de secteur à chaque accès qui pourrait être lu en avance par l'Arduino pour être en mesure de répondre au 6809 rapidement. J'imagine le cas de l'IDE, on a une commande et un statut - l'Arduino peut anticiper pour charger les octets d'un secteur dans sa SRAM après l'exécution de la commande et les restituer en flux à la demande du 6809. Ça c'est pour le cas où le temps de réponse du contrôleur serait trop variable pour espérer capturer la donnée avec une lecture double ou qu'il n'y ait pas un équivalent /WAIT.

En tout cas le projet m'intéresse pas mal. Est-ce que ça veut dire un matériel totalement différent de SDDRIVE ? ou ça se met entre le bus 6809 et celui de SDDRIVE ?

Daniel
Messages : 11510
Enregistré le : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: [THOMSON] ARDDRIVE

Message par Daniel » 11 avr. 2019 20:02

La communication avec la carte SD se fait uniquement par lecture/ecriture d'un octet, comme avec SDDRIVE.

Le contrôleur ARDDRIVE est une version simplifiée de SDDRIVE. L'accès à l'EPROM est identique. Il n'y a pas de buffer, c'est le port D de l'Arduino qui en tient lieu. Outre les 8 bits de données on envoie à l'Arduino un signal d'horloge pour déclencher la transmission et le signal /W pour indiquer le sens de transfert.
Daniel
L'obstacle augmente mon ardeur.

hlide
Messages : 810
Enregistré le : 29 nov. 2017 10:23

Re: [THOMSON] ARDDRIVE

Message par hlide » 11 avr. 2019 20:45

Le seul truc que je vois qui pourrait ressembler à /WAIT, c'est MRDY.

http://www.gbgmv.se/dl/doc/md09/MC6809_DataSheet.pdf

Page 12 et 13.

Le fait de le positionner à 0 fait allonger l'état de E et Q. Ça peut être une piste pour permettre à l'Arduino d'obtenir la donnée à coup sûr.

Donc en substance, on a un port 8-bit de donnée (port D) pour lecture et écriture des données, un clock (enclenché sur une adresse particulière), un R/W.

Donc un polling ressemblerait à ça

Code : Tout sélectionner

void loop()
{
    while (0 != ((ctrl = pinCtrl) & 1)); // clock is 0?
    if (ctrl & 2) // R/W == 1 -> reading
    {
        portCtrl &= ~4; // MRDY -> 0
        DDRD |= 255; // output D
        PORTD = readSDByte();
        portCtrl |= 4; // MRDY -> 1
        while (0 == (pinCtrl & 1)); // clock is 1? data acquired
        DDRD &= 255; // input D as PULL UP
        PORTD = 255; // as PULL UP
    }
    else // R/W == 0 -> writing
    {
        portCtrl &= ~4; // MRDY -> 0
        writeSDByte(PIND);
        portCtrl |= 4; // MRDY -> 1
        while (0 == (pinCtrl & 1)); // clock is 1? data acquired
    }
}
Lors de l'écriture, je crains une chose : quand une nouvelle écriture ou lecture est demandée avant que le SD ait fini et fasse revenir à la détection de l'horloge. Je suppose que MRDY devrait faire patienter le CPU même à l'écriture pour éviter de manquer une nouvelle tentative de lecture ou écriture à la suite de l'écriture.

Après écriture du port D, il me semble important de le remettre en haute impédance - est-ce que le clock peut aider à ça pour éviter de le mettre trop tôt ? vu que l'Arduino a sa propre horloge, il peut y avoir du jittering.

Daniel
Messages : 11510
Enregistré le : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: [THOMSON] ARDDRIVE

Message par Daniel » 11 avr. 2019 21:54

Oui, en gros c'est ce que je fais. Le passage de l'horloge à zéro met le port D en lecture ou en écriture, selon la valeur de /W. Le port D est remis en haute impédance quand l'horloge remonte à 1, ce qui laisse le temps au 6809 de le lire. La logique semble bonne, il faut maintenant évaluer les durées pour savoir si elles sont compatibles avec le délai entre le front descendant et le front montant de l'horloge, et si la lecture ou l'écriture des 8 bits de la carte est possible dans les quelques µs qui séparent deux accès successifs.

A l'instant je viens de faire un test sur MO5. Avec les lectures et écritures sur la carte SD, le MO5 plante. En mettant la lecture et l'écriture en commentaire ça ne plante plus. Les accès à la carte sont peut-être trop lents, comme tu l'as évoqué, au point de faire rater des changements d'état de l'horloge. C'est pourquoi je veux commencer par mesurer ces temps d'accès.
Daniel
L'obstacle augmente mon ardeur.

Daniel
Messages : 11510
Enregistré le : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: [THOMSON] ARDDRIVE

Message par Daniel » 15 avr. 2019 09:29

Le temps de lecture ou d'écriture du port D est très court : 1 cycle de l'Arduino.
Le temps de lecture et d'écriture d'un octet en mode SPI dépend de la vitesse de l'horloge. A la vitesse maximum, c'est entre 2 et 3 µs, mais reste à savoir si la carte SD peut l'accepter.

Voici l'état actuel du programme de l'Arduino. Le plus difficile reste à faire : la mise au point avec le matériel réel. Autant c'est facile avec un émulateur, autant c'est difficile avec la vraie machine, mais l'enjeu vaut bien un petit effort.

Code : Tout sélectionner

/**************************************************\
*                 A R D D R I V E                  * 
*           (c) 2019 - Daniel Coulom               *  
*           http://dcmoto.free.fr/                 *
*           http://forum.system-cfg.com/           *
*--------------------------------------------------*
* Ce code est distribue gratuitement dans l'espoir *
* qu'il sera utile, mais sans aucune  garantie  et *
* sans  engager  la  responsabilité  de  l'auteur. *
* Vous  pouvez  l' utiliser,  le  modifier  et  le *
* diffuser librement, en conservant cette  licence *
* et les références de l'auteur dans   toutes  les *
* copies. L'exploitation commerciale est interdite.*
\**************************************************/

/***************************************************
*                Version 2019.04.15                *
****************************************************
Historique
2019.04.15 version en cours de mise au point
2019.04.06 reprise du developpement
2018.05.09 premiere version issue de sdanim7

Communication parallele avec un ordinateur Thomson
Lecture/ecriture octet par octet par le port D
- D0-D7 sont les donnees 
- D8 est le signal d'horloge
- D9 est le signal R/W      

Connexion module Catalex pour carte micro SD
 GND --> GND
 SCK --> D13
MISO --> D12
MOSI --> D11
 VCC --> VCC (5V)
  CS --> D10
*/

// inslude the SPI library:
#include <SPI.h>

uint8_t octet; // zone de stockage temporaire d'un octet
uint8_t  CS;   // numero de la broche CS de la carte SD

void setup()
{
 
 //Initialisations interface Thomson
 pinMode(8, INPUT);           // configurer pin8 en entree (bit d'horloge)
 pinMode(9, INPUT);           // configurer pin9 en entree (signal R/W)
 DDRD = 0;                    // configurer port D en entree
 PORTD = 0;                   // haute impedance
 
 //Initialisations interface carte SD
 pinMode(MISO, INPUT);        //MISO en entree
 pinMode(SCK, OUTPUT);        //SCK en sortie
 pinMode(MOSI, OUTPUT);       //MOSI en sortie
 pinMode(SS, OUTPUT);         //CS en sortie       
 digitalWrite(SCK, HIGH);     //SCK=1
 digitalWrite(MOSI, HIGH);    //MOSI=1 
 digitalWrite(SS, LOW);       //CS=0

 //Initialisation communication SPI
 SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
}

void loop(void)
{
 // en entrant dans la boucle le port D est en entree (haute impedance) 
 // l'horloge (bit 0 du port B) est a l'etat haut
 uint8_t octet;  //octet transmis par le port D en lecture ou ecriture

 //////////////////////////////////////////
 // Attente front descendant de l'horloge
 //////////////////////////////////////////
 while((PINB & 1) != 0);    // attente horloge à 0 (PORTB bit0 est l'horloge) 
 
 //////////////////////////////////////////
 // Traitement en lecture de la carte
 //////////////////////////////////////////
 if((PINB & 2) != 0)        // en lecture (PORTB bit1 est le signal /W)
 {
  DDRD = 0xff;              // configurer port D en sortie
  PORTD = octet;            // envoyer l'octet au port D
  while((PINB & 1) == 0);   // attente horloge à 1 (PORTB bit0 est l'horloge) 
  DDRD = 0;                 //port D en entree
  PORTD = 0;                //port D en haute impedance
  //lecture octet suivant
  SPDR = 0xff; // send dummy data for receiving some
  while(!(SPSR & (1 << SPIF)));
  SPSR &= ~(1 << SPIF);
  octet=SPDR;
 }

 //////////////////////////////////////////
 // Traitement en ecriture de la carte
 //////////////////////////////////////////
 else 
 {
  //lecture octet du port D tant que l'horloge est a zero
  while((PINB & 1) == 0) octet = PIND;  // attente horloge à 1 (PORTB bit0 est l'horloge) 
  DDRD = 0;                             //port D en entree (inutile ? il est déjà en entree...)
  PORTD = 0;                            //haute impédance  (inutile ? il est déjà sans pullup...)
  //ecriture vers la carte de l'octet lu au port D
  SPDR = octet;
  while(!(SPSR & (1 << SPIF))); // wait for byte to be shifted out
  SPSR &= ~(1 << SPIF);
 }
}
Daniel
L'obstacle augmente mon ardeur.

hlide
Messages : 810
Enregistré le : 29 nov. 2017 10:23

Re: [THOMSON] ARDDRIVE

Message par hlide » 15 avr. 2019 11:59

Ca me semble bien à part une chose :

(1) Mettre les 8 pins du port D en sortie :

Code : Tout sélectionner

DDRD = 0xFF; // pins en sortie
PORTD = <l'octet voulu en sortie>;
(2) Mettre les 8 pins du port D en entrée **SANS** haute impédance :

Code : Tout sélectionner

DDRD = 0x00; // pins en entrée
PORTD = 0x00; // pas de haute impédance sur ces pins
(3) Mettre les 8 pins du port D en entrée **EN** haute impédance :

Code : Tout sélectionner

DDRD = 0x00; // pins en entrée
PORTD = 0xFF; // et en haute impédance
Je note un commentaire dans ton code pour la lecture :
PORTD = 0; //port D en haute impedance
que tu souhaites le (3) mais tu fais en fait le (2). Pareil pour l'écriture :
PORTD = 0; //haute impédance (inutile ? il est déjà sans pullup...)
que tu souhaite bien le (3) mais tu fais aussi le (2).

Il me semble que le (3) est souhaitable dans tous les cas.

Tu peux effectivement supprimer "PORTD = 0; PORTD = <(2) ou (3)>;" lors de l'écriture qui ne servent à rien à part à introduire 2 cycles dont je ne suis pas sûr qu'elles soient nécessaires.

hlide
Messages : 810
Enregistré le : 29 nov. 2017 10:23

Re: [THOMSON] ARDDRIVE

Message par hlide » 15 avr. 2019 12:10

Je vois un problème :

L'*octet* local dans *loop*, il n'est pas initialisé à une valeur par défaut, ce qui fait qu'à la première lecture par le 6809, ce dernier recevra n'importe quoi. Pire, comme *loop* est une fonction qui est appelée dans une vrai boucle du firmware, ça veut dire que l'on ne peut pas compter sur la persistance de la valeur de *octet* (du point de vue du langage, cette variable est "détruite" en sortie de *loop*). Je note qu'il existe deux variables du nom *octet*. Donc je préconise de retirer la locale au profit de l'autre.

hlide
Messages : 810
Enregistré le : 29 nov. 2017 10:23

Re: [THOMSON] ARDDRIVE

Message par hlide » 15 avr. 2019 12:45

J'en vois d'autres :

1) Le premier octet par le 6809 lu ne devra pas être pris en compte. Je suppose que tu prendras ça en compte dans ton code 6809.
2) Lire systématiquement deux octets pour ne considérer que la deuxième ne fonctionnera pas. Je suppose que dans une séquence de lecture, tu jetteras que le tout premier octet, c'est à dire juste après une écriture d'un octet (commande ?).

__sam__
Messages : 4552
Enregistré le : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: [THOMSON] ARDDRIVE

Message par __sam__ » 15 avr. 2019 14:22

Il manque un "static" dans la déclaration de "octet" ?
Samuel.
A500 Vampire V2+, A1200(030@50mhz/fpu/64mb/cf 8go),
GVP530 (MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.

hlide
Messages : 810
Enregistré le : 29 nov. 2017 10:23

Re: [THOMSON] ARDDRIVE

Message par hlide » 15 avr. 2019 15:11

__sam__ a écrit :
15 avr. 2019 14:22
Il manque un "static" dans la déclaration de "octet" ?
Ce n'est pas souhaitable dans une fonction. Pourquoi ?

On est en C++, qui plus est en C++11. Une variable statique avec initialisation va introduire une variable booléenne implicite qui vérifie si la variable statique a été initialisé à chaque entrée dans *loop* : c'est donc des instructions supplémentaires rajoutées par le compilateur qui faut absolument éviter ici. Une variable statique sans initialisation, je ne pense pas qu'il y aura des instructions supplémentaires mais je n'ai jamais vérifié ce cas parce que je ne vois pas l'intérêt de faire ça dans une fonction d'une manière générale (en C++ du moins). Utiliser *octet* défini à l'extérieur me paraît plus propre que de rajouter "static" sur le local - surtout si quelqu'un d'autre serait tenté de l'initialiser.

Et si jamais le type de la variable n'est pas un POD (Plain Old Data - genre char, int, etc.), alors le compilateur va vouloir appelé le constructeur pour le static et rebelote on a droit au booléen implicite pour vérifier que la variable statique a déjà été initialisée à chaque passage dans *loop*.

Donc mon conseil, bannir les static dans les fonctions si vous voulez éviter ce genre de surprise.
Modifié en dernier par hlide le 15 avr. 2019 15:15, modifié 1 fois.

__sam__
Messages : 4552
Enregistré le : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: [THOMSON] ARDDRIVE

Message par __sam__ » 15 avr. 2019 15:15

La vache, la gestion des "static" en c++ est étonnement complexe par rapport à ce que c'est sémantiquement au niveau asm (une simple zone mémoire globale mais avec un label privé connu seul de la fonction).
Samuel.
A500 Vampire V2+, A1200(030@50mhz/fpu/64mb/cf 8go),
GVP530 (MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.

hlide
Messages : 810
Enregistré le : 29 nov. 2017 10:23

Re: [THOMSON] ARDDRIVE

Message par hlide » 15 avr. 2019 15:18

En C++, l'initialisation est fait au premier appel de la fonction : ça permet de créer un singleton à la première *réelle* demande.

Et ce serait intéressant de voir ce que ça donne en C - je parierais que ce n'est pas différent en fait (sinon je ne vois pas comment ça peut fonctionner d'ailleurs...)

hlide
Messages : 810
Enregistré le : 29 nov. 2017 10:23

Re: [THOMSON] ARDDRIVE

Message par hlide » 15 avr. 2019 15:23

__sam__ a écrit :
15 avr. 2019 15:15
La vache, la gestion des "static" en c++ est étonnement complexe par rapport à ce que c'est sémantiquement au niveau asm (une simple zone mémoire globale mais avec un label privé connu seul de la fonction).
Après réflexion, si le static est POD (genre char, int, long) avec ou sans valeur initiale, ça ne devrait pas poser de problème pour le compilateur de mettre direct en mémoire cette valeur et de ne pas utiliser ce booléen implicite. Je vais tester ça sur Godbolt.org.

EDIT: ça passe pour les POD - pas de code implicite.

EDIT2:

Objet POD (sans constructeur) :

Code : Tout sélectionner

struct Object
{
    double x, y;

    //Object() : x(1.), y(1.) {}
};

void loop()
{
    static Object octet = { 1., 1. };

    octet = *((Object*)(0x8000));
}
donne bien le code souhaité :

Code : Tout sélectionner

loop():
        push    rbp
        mov     rbp, rsp
        mov     eax, 32768
        mov     rdx, QWORD PTR [rax+8]
        mov     rax, QWORD PTR [rax]
        mov     QWORD PTR loop()::octet[rip], rax
        mov     QWORD PTR loop()::octet[rip+8], rdx
        nop
        pop     rbp
        ret
Mais un objet non POD (avec un constructeur) :

Code : Tout sélectionner

struct Object
{
    double x, y;

    Object() : x(1.), y(1.) {}
};
donne un code qui gère un booléen *ET* un mutex pour être "multi-thread safe" :

Code : Tout sélectionner

Object::Object() [base object constructor]:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        movsd   xmm0, QWORD PTR .LC0[rip]
        movsd   QWORD PTR [rax], xmm0
        mov     rax, QWORD PTR [rbp-8]
        movsd   xmm0, QWORD PTR .LC0[rip]
        movsd   QWORD PTR [rax+8], xmm0
        nop
        pop     rbp
        ret
loop():
        push    rbp
        mov     rbp, rsp
        movzx   eax, BYTE PTR guard variable for loop()::octet[rip]
        test    al, al
        sete    al
        test    al, al
        je      .L5
        mov     edi, OFFSET FLAT:guard variable for loop()::octet
        call    __cxa_guard_acquire
        test    eax, eax
        setne   al
        test    al, al
        je      .L5
        mov     edi, OFFSET FLAT:loop()::octet
        call    Object::Object() [complete object constructor]
        mov     edi, OFFSET FLAT:guard variable for loop()::octet
        call    __cxa_guard_release
.L5:
        mov     eax, 32768
        mov     rdx, QWORD PTR [rax+8]
        mov     rax, QWORD PTR [rax]
        mov     QWORD PTR loop()::octet[rip], rax
        mov     QWORD PTR loop()::octet[rip+8], rdx
        nop
        pop     rbp
        ret
.LC0:
        .long   0
        .long   1072693248
Pas la même longueur hein !

Daniel
Messages : 11510
Enregistré le : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: [THOMSON] ARDDRIVE

Message par Daniel » 15 avr. 2019 21:30

Merci pour vos remarques, je vais en tenir compte.
La variable octet en local, c'est une erreur. J'ai mis octet en variable globale et j'ai oublié de le retirer en local.
Je sais qu'octet n'est pas initialisé, et que sa lecture systématique après une lecture est inutile si la prochaine commande est une écriture, mais normalement ça ne devrait pas poser de problème. Je le vérifierai plus précisément pendant la phase de mise au point.
Daniel
L'obstacle augmente mon ardeur.

Répondre