MC68020
Généralités
Après le MC68000, il est temps de s'attaquer à un plus gros processeur, le MC68020.
Il s'agit là du digne successeur du 68000 !!
Dans le genre belle carte, voici celle que Marc m'a gentiment donnée, je la trouve vraiment très belle.
Le but premier était de récupérer des composants pour ma carte MC68020 ou pour la prochaine MC68000, mais je trouve presque dommage de la scrapper et je me demande si je ne vais pas plutôt l'encadrer pour l'accrocher dans mon bureau...
J'ai déjà dessoudé un MC68661 (celui qui est en bas à gauche du MC68000 avant que je ne le dessoude...) et c'est quelque part un peu dommage.
D'un autre côté, ça redonnerait vie à ce MC68000 et à toute sa petite famille, donc : réflexion en cours ...
Marc m'a également donné un MC68861 qui combine DUART, et quelques lignes de PIA pour la carte MC68020.
Revenons-en au MC68020 ...
Le 68020 est le successeur de 68000, en version full 32 bits.
Son bus de données sort du processeur 68020 en 32 bits, de D0 à D31. De ce fait, le câblage d'un prototype est beaucoup plus délicat.
Fort heureusement le MC68020 possède un mode 8 bits, ainsi qu'un mode 16 bits en plus de son mode natif 32 bits.
Pour signifier la largeur du bus de données, deux signaux sont utilisés sur cette maquette: DSACK0\ et DSACK1\, qui sont l'équivalent de DTACK\ et UDS\ LDS\ du MC68000.
Pour ces essais, j'ai pu acheter un processeur MC68020RC16 sur un site de matériel d'occasion, sans aucune garantie de son bon fonctionnement.
Monté sur une carte veroboard, les premiers essais ont pu commencer après une petite lecture du manuel MC68020 de Motorola ...
Le dessous de la carte:
Le dessous de la carte est beaucoup moins fun, avec pas mal de difficultés pour les bus...
Assembleur:
Tous les programmes Assembleur utilisés ici sont compilés avec Ed!, le cross assembleur 68000 que j'avait écrit en 1994 en Turbo Pascal, et qui est disponible en téléchargement ici.
Etant actuellement sous MacOS, j'utilise l'excellent émulateur Boxer qui tourne donc sur MacOS et qui permet très simplement de lancer et utiliser Ed, comme sur un ancien PC sous MSDOS.
Il est vraiment très cool, et il suffit de glisser le répertoire contenant Ed sur l'icône de Boxer, et une fenêtre DOS s'ouvre. Il suffit de taper Ed et return pour le lancer, comme sur un PC.
En plus de lancer Ed, Boxer crée un répertoire qui émule C: et qui permet de récupérer les fichiers compilés sur le Mac.
C'est vraiment excellent.
La seule restriction est qu'il ne fonctionne pas sous Catalina. Il faudra donc ressortir un vieux Mac du placard (j'ai un vieux MacBook blanc pour ça, qui fait très bien l'affaire...).
L'autre solution que j'utilise de plus en plus est Easy68K qui tourne sous Windows, ou bien sur MacOS en le faisant passer par PlayOnMac, qui permet de faire tourner des programmes Windows sur Mac. C'est une très bonne solution également, car il existe deux autres utilitaires avec Easy68K qui sont EasyBin (qui permet d'éditer et préparer les fichiers BIN), et Sim68K qui est un simulateur qui permet de faire trouner le programme en examinant tous les registres du 68000).
Cet ensemble est vraiment excellent.
MC68020 et Arduino ATMega:
Pour voir si mon MC68020 d'occasion pouvait encore fonctionner, j'ai monté une maquette où la ROM a été remplacée par un Arduino ATMega suivant le schéma ci-dessous.
Dans cette configuration, le MC68020 adresse l'Arduino comme s'il s'agissait d'une ROM, ce dernier répondant en mettant sur le bus de données le code correspondant à l'adresse sélectionnée par le 68020. Ensuite, il suffit ensuite pour lui d'acquitter DSACK0\.
Avec ce peu de code implanté dans l'Arduino, il a été facile de voir que le MC68020 que j'avais acheté répondait bien aux sollicitations, savait adresser l'Arduino comme une ROM, et savait répondre aux signaux de synchronisation.
L'Arduino ATMega n'est pas suffisamment rapide pour faire fonctionner le MC68020 correctement, mais ça a suffit pour voir les principaux ensembles du processeur fonctionner à l'oscilloscope.
Avec un peu de code, tout a pu fonctionner:
MC68020 en mode 8 bits:
Après cette phase de tests, j'ai pu reprendre une vielle carte sur laquelle était câblé un MC6809.
Après un petit moment de décâblage du 6809 et de recâblage en mode 68020 8 bits, voilà ce que ça donne:
Sur la carte, il n'y a pas de RAM, seulement une EEProm de type 28C64 afin de réaliser un fonctionnement minimal, ce qui a plus de chances d'aboutir du premier coup...
De fait, les appels aux sous-programmes sont interdits car le Stack Pointer n'est pas initialisé.
On retrouve donc le MC68020, avec son eeprom et un buffer sur lequel seulement 2 Leds sont câblées, ce qui est suffisant pour faire un clignotement gauche/droite et voir que tout fonctionne.
Un peu de logique câblée, et voilà...
Il n'y a pas de CPLD ou autre pour la logique externe, car bien qu'étant adepte des FPGA et autre CPLD, je préfère garder une logique hard autour du micro, comme on aura pû le faire il y a quelques dizaines d'années. Les composants en jaune ne sont pas montés pour l'instant, on verra plu stard.
Question de principe...
Le schéma de la carte à ce stade est le suivant:
Description des signaux:
Comme mentionné, le micro est passé en mode 8 bits pour ces premiers essais.
Pour cela, il suffit de forcer le signal DSACK1\ à Vcc, et d'acquitter DSACK0\ à Gnd à la fin du transfert sur les bus.
Par cette combinaison, le MC68020 sait qu'il est en mode 8 bits. Il va alors transmettre ses données, qui restent 32 bits pour lui, uniquement sur le poids fort de son DataBus (D31-D24).
Pour transmettre une données 32 bits, il fera donc cette opération 4 fois sur D31-D24.
Voici le détail de ce décodage DSACK0\ DSACK1\ d'après la doc Motorola:
Dans ce tableau, DSACK0\ et DSACK1\ étant actifs à l'état bas, Negated équivaut à mettre l'entrée à Vcc et Asserted équivaut à mettre l'entrée à Gnd.
Décodage d'adresse:
Le décodage d'adresse se fait à l'aide des signaux DataStrobe\ (DS\) et AdressStrobe\ (AS\).
Les vecteurs d'interruption (qui ne sont pas pris en compte pour l'instant), ainsi que le Stack Pointer et le Program Counter (PC) sont situés en bas du mapping mémoire, soit à partir de l'adresse $00000000.
Le MC68020 ayant 255 vecteurs sur 32 bits, ceux-ci occupent donc 255 x 4 = 1020 octets.
Le dernier vecteur se trouvant alors à l'adresse 1020 ($000003FC), et celui-ci occupant 4 octets, la première adresse disponible pour notre programme sera donc 1020 + 4 = 1024 ($00000400).
Notre vecteur PC sera donc initialisé à $00000400.
Le Stack Pointer n'est pas initialisé pour l'instant, ni les autres vecteurs car nous n'avons pas de RAM à cette étape du schéma.
On va donc utiliser directement A0-A12 sur l'eeprom, et par exemple A16-A18 sur le décodeur d'adresse 74LS138 (U5), en utilisant la sortie Q0 du décodeur afin de valider le boitier eeprom de $00000000 à $00001FFF.
On retrouve bien la totalité du boitier eeprom décodé: $00001FFF = 8192 octets = 8 Ko, ce qui correspond bien à notre boîtier eeprom 28C64 (8Ko sur 8 bits).
Comme le MC68020 est en mode 8 bits par le traitement de DSACK0\ DSACK1\, le bus de données de l'eeprom est relié à D24-D31 du processeur.
Le buffer de sortie de l'eeprom est quant à lui directement validé par le signal DS\ (DataStrobe\).
Elle sera détectée par le 74LS138 comme les autres boitiers, et sera adressée par A12-A0.
Génération de DSACK0\ DSACK1\:
Afin d'indiquer au MC68020 que le cycle en cours est en mode 8 bits, nous allons positionner DSACK1\ à Vcc, et DSACK0\ à Gnd quatre coups d'horloge après le décodage d'adresse.
La double bascule 74LS74 génère une horloge à CLOCK div 4, et la logique câblée va donc amener la sortie de cette bascule sur DSACK0\ lorsqu'un des boitiers est adressé par le 74LS138.
Lorsqu'aucun boitier n'est adressé par le 74LS138, la double bascule 74LS74 est bloquée au reset et DSACK0\ reste à Vcc.
On va ainsi acquitter DSACK0\ quatre coups de CLOCK après le décodage des boitiers par les 74LS138.
DSACK1\ étant figé à Vcc, le MC68020 comprends alors qu'il est en mode 8 bits et reconduit le reste de sa donnée 32 bits sur D24-D31, et ce en 4 fois.
J'essaierai de mettre des oscillogrammes en photo, ce qui sera plus clair qu'un long discours...
Buffer des Leds:
Le buffer des Leds est directement décodé par Q7 du décodeur 74LS138, et se trouve donc à l'adresse $00070000.
Code assembleur, compilé avec ED:
Le code assembleur pour faire tourner cette carte MC68020 est le suivant, afin d'allumer l'une des deux Leds, et de suivre les signaux à l'oscilloscope:
Premier programme:
; MC68020: premier programme de test
VECTEUR
SSP $00000000
RAM $00000000
ROM $00000400
MAIN
; Programme principal
@Init MOVE.L #$AAAAAAAA, D1 ; Chargement D1 pour OUT
MOVE.L D1, $00070000 ; Sortie sur OUT
@Bcle JMP @Bcle
Le code est très simple afin de minimiser les risques d'erreurs.
De même, les transferts s'effectuent en mots longs .L afin de s'affranchir dans un premier temps du transtypage en 8 bits effectué par le MC68020 suivant notre schéma.
En changeant la valeur de chargement AA à 55 sur deux eeproms différentes, on peut voir que la Led 1 ou la Led 2 s'allument.
La gestion des registres est faite en entiers Longs, même si le bus externe est 8 bits. Ceci permet de garder un code simple, sans extrapolation du MC68020 sur la longueur des mots à transmettre.
Cette méthode semble un peu simpliste de nos jours où l'on utilise des debuggers et compilateurs de plus en plus évolués, mais le meilleur principe reste ici KISS (Keep It Simple, Stupid...)
Si ça ne fonctionne pas, il n'y a que peu de choses à mettre en cause, et si ça fonctionne, la partie la plus importante du schéma est validée.
Ce code a été assemblé avec Ed qui se trouve en téléchargement à la page MC68000 de ce site...
Second programme:
Le second programme utilise des boucles et des sauts, afin de vérifier si l'adressage du MC68020 reste correct sur une plus grande quantité d'instructions enchainées.
Comme il n'y a toujours pas de RAM, il n'y a pas de possibilité d'utiliser de sous programmes par JSR, on va donc répéter le même code :
; MC68020: second programme de test
VECTEUR
SSP $00000000
RAM $00000000
ROM $00000400
MAIN
; Programme principal
@Init MOVE.L #$AAAAAAAA, D1 ; Chargement chenillard
MOVE.B D1, $00070000 ; Sortie sur Chenillard
MOVE.L #$0FFFFFFF, D0 ; Init Compteur pour tempo
@Bcl1 DBEQ D0, @Bcl1
MOVE.L #$55555555, D1 ; Chargement chenillard
MOVE.B D1, $00070000 ; Sortie sur Chenillard
MOVE.L #$0FFFFFFF, D0 ; Init Compteur pour tempo
@Bcl2 DBEQ D0, @Bcl2
JMP @Init
L'ensemble fonctionne également correctement, ce qui va permettre de passer à l'étape suivant au niveau du schéma et du code ...
NAND OR or AND NAND, that is the question...
Sur le schéma précédent, la génération de DSACK0\ était faite avec un deux NAND et un OR, qui allait sur le Reset des bascules D.
N'ayant pas de OR de type 74LS32 dans les tiroirs, il faut trouver une autre solution.
On va alors utiliser deux AND et une NAND, ce qui donnera au final le même résultat, les composants en jaune n'étant toujours pas montés à ce stade:
Démonstration:
On pose Y_ROM = sortie du 74LS138, noté ROM\ sur le schéma, idem pour les autres signaux.
Sachant que (a.b)\ = a\ + b\, et que a\.b\ = (a + b)\, on peut dire que:
RST_DSACK0\ = (Y_ROM . Y_OUT)\ + (Y_RAM . Y_LCD)\ ( Schéma 1 )
= (Y_ROM\ + Y_OUT\) + (Y_RAM\ + Y_LCD\)
= ( (Y_ROM\ + Y_OUT\) + (Y_RAM\ + Y_LCD\) ) \\ (On complémente deux fois pour pouvoir distribuer )
= ( (Y_ROM\ + Y_OUT\)\ . (Y_RAM\ + Y_LCD\)\ ) \
= ( ( Y_ROM . Y_OUT) . (Y_RAM . Y_LCD) ) \ ( Schéma 2 )
Et voilà ...
La prochaine étape va maintenant être de câbler l'afficheur LCD.
Afficheur LCD
L'afficheur LCD choisi est le même modèle que celui de la carte Lisa68K, à savoir un 2x8 caractères.
Ce n'est pas très grand, mais ça suffit amplement pour les premiers essais, et il ne tient pas de place sur la carte.
Sa datasheet peut être trouvée sur Internet en tapant simplement LCD 2x8, mais elle reste la même que pour les 2x16 ou 4x16 caractères.
J'ai pris celle d'un modèle CM0820S1LYC7-J2.
Décodage d'adresse:
Au niveau du décodage d'adresse, il faut apporter une modification au schéma précédent afin de l'adresser sur une adresse 32 bits: la sélection de registres RS va être adressée par A2 au lieu de A0 comme je l'avait fait précédemment.
De ce fait, l'afficheur aura les adresses $00060000 et $00060004.
Le schéma avec afficheur est disponible en téléchargement en bas de cette page, et la RAM en jaune n'est toujours pas montée.
Programme de tests:
Le programme de test de l'afficheur ne peut toujours pas utiliser les sous-programmes du fait de l'absence de RAM pour le Stack Pointer (ce devrait être la prochaine étape...).
Le code est donc long et répétitif, mais il permet de tester l'afficheur.
De même, les écritures sur OUT se font maintenant avec des MOVEQ et des .B au lieu des .L, ce qui est tout de même plus élégant...
; MC68020: Mise en route de l'ecran LCD
VECTEUR
SSP $00000000
RAM $00000000
ROM $00000400
CONST
OUT = $00070000 ; Out
LCD = $00060000 ; Ecran LCD - config -
LCDat= $00060004 ; Ecran LCD - caracteres -
MAIN
; Programme principal
@Init MOVEQ #$38, D0 ; 2 lignes, 5x7 dots, 8 bits
MOVE.B D0, LCD
MOVE.L #$0000FFFF, D0
@WLc0 DBEQ D0, @WLc0
MOVEQ #$01, D0 ; Clear screen
MOVE.B D0, LCD
MOVE.L #$0000FFFF, D0
@WLc1 DBEQ D0, @WLc1
MOVEQ #$02, D0 ; Return Home
MOVE.B D0, LCD
MOVE.L #$0000FFFF, D0
@WLc2 DBEQ D0, @WLc2
MOVEQ #$0E, D0 ; Turn display ON
MOVE.B D0, LCD
MOVE.L #$0000FFFF, D0
@WLc3 DBEQ D0, @WLc3
MOVEQ #$06, D0
MOVE.B D0, LCD ; Init and select DDRAM
MOVE.L #$0000FFFF, D0
@WLc4 DBEQ D0, @WLc4
@Car1 MOVE.B LCD, D1
ANDI.B #$80, D1
BNE @Car1 ; Attente Busy Flag= 0
MOVEQ #$46, D0
MOVE.B D0, LCDat
@Car2 MOVE.B LCD, D1
ANDI.B #$80, D1
BNE @Car2 ; Attente Busy Flag= 0
MOVEQ #$72, D0
MOVE.B D0, LCDat
@Car3 MOVE.B LCD, D1
ANDI.B #$80, D1
BNE @Car3 ; Attente Busy Flag= 0
MOVEQ #$65, D0
MOVE.B D0, LCDat
@Car4 MOVE.B LCD, D1
ANDI.B #$80, D1
BNE @Car4 ; Attente Busy Flag= 0
MOVEQ #$64, D0
MOVE.B D0, LCDat
@Wai MOVE.B LCD, D1
ANDI.B #$80, D1
BNE @Wai ; Attente Busy Flag= 0
MOVEQ #$C0, D0 ; Ligne 2
MOVE.B D0, LCD
@Car5 MOVE.B LCD, D1
ANDI.B #$80, D1
BNE @Car5 ; Attente Busy Flag= 0
MOVEQ #$36, D0
MOVE.B D0, LCDat
@Car6 MOVE.B LCD, D1
ANDI.B #$80, D1
BNE @Car6 ; Attente Busy Flag= 0
MOVEQ #$38, D0
MOVE.B D0, LCDat
@Car7 MOVE.B LCD, D1
ANDI.B #$80, D1
BNE @Car7 ; Attente Busy Flag= 0
MOVEQ #$30, D0
MOVE.B D0, LCDat
@Car8 MOVE.B LCD, D1
ANDI.B #$80, D1
BNE @Car8 ; Attente Busy Flag= 0
MOVEQ #$32, D0
MOVE.B D0, LCDat
@Car9 MOVE.B LCD, D1
ANDI.B #$80, D1
BNE @Car9 ; Attente Busy Flag= 0
MOVEQ #$30, D0
MOVE.B D0, LCDat
@Loop MOVEQ #$01, D1 ; Chargement chenillard
MOVE.B D1, $00070000 ; Sortie sur Chenillard
MOVE.L #$0FFFFFFF, D0 ;Init Compteur
@Bcl1 DBEQ D0, @Bcl1
MOVEQ #$02, D1 ; Chargement chenillard
MOVE.B D1, $00070000 ; Sortie sur Chenillard
MOVE.L #$0FFFFFFF, D0 ;Init Compteur
@Bcl2 DBEQ D0, @Bcl2
JMP @Loop
Câblage de la RAM:
La RAM D449C de 2Ko va également être montée en mode 8 bits, de la même façon que ce qui a été fait sur l'eeprom.
On pourra prendre un 6116, mais n'ayant que des D449C dans les tiroirs, mon choix s'est naturellement porté sur celle-ci...
Par contre, sa datasheet est difficile à trouver, et on va donc se baser sur une RAM statique 2Ko standard type CY6116 par exemple.
Elle sera détectée par le 74LS138 comme les autres boitiers, et sera adressée par A10-A0.
Le fichier pdf est également téléchargeable en bas de la page
Programme de test avec Sous-programmes:
Maintenant que la RAM est câblée sur la carte MC68020, on va pouvoir initialiser le Stack Pointer et utiliser les sous-programmes avec des instructions comme JSR (Jump to SubRoutine).
Le programme est tout de suite plus élégant ...
; MC68020: Mise en route de la RAM
; Code sur la base de la carte LIsa 68000
VECTEUR
SSP $00010FF0
RAM $00010000
ROM $00000400
CONST
OUT = $00070000 ; Out
LCD = $00060000 ; Ecran LCD - config -
LCDat= $00060004 ; Ecran LCD - caracteres -
Pile = $00010E00 ; Pile de sauvegarde generale - en plus du stack -
St1 = " 68020 ^"
St2 = " Ready ^"
MAIN
; Programme principal
LEA Pile, A0 ; Init Pointeur Pile A0
JSR @IniLcd ; Init LCD
LEA St1, A2 ; Affichage St1
JSR @StrLcd
JSR @Lg2Lcd
LEA St2, A2 ; Affichage St2
JSR @StrLcd
@Loop JSR @Chenil ; Appel au chenillard
JMP @Loop
; Chenillard sur OUT
@Chenil MOVE.L D0,(A0)+ ; Sauvegarde D0
MOVEQ #$AA, D0 ; Chargement chenillard
MOVE.B D0, $00070000 ; Sortie sur Chenillard
MOVE.L #$0FFFFFFF, D0 ;Init Tempo
@Bcl1 DBEQ D0, @Bcl1
MOVEQ #$55, D0 ; Chargement chenillard
MOVE.B D0, $00070000 ; Sortie sur Chenillard
MOVE.L #$0FFFFFFF, D0 ;Init Compteur
@Bcl2 DBEQ D0, @Bcl2
MOVE.L -(A0), D0
RTS
; Init LCD
@IniLCD MOVE.L D0,(A0)+
MOVEQ #$38, D0 ; 2 lignes, 5x7 dots, 8 bits
MOVE.B D0, LCD
JSR @WaiLcd ; Tempo reset LCD
JSR @CursOf ; Curseur Off
JSR @ClrLCD ; Efface ‚cran
MOVE.L -(A0), D0
RTS
; Display On, Curseur on, not Blink
@CursOn MOVE.L D0, (A0)+
MOVEQ #$0E, D0
MOVE.B D0, LCD
JSR @WaiLCD
MOVE.L -(A0), D0
RTS
; Display On, Curseur off, not Blink
@CursOf MOVE.L D0, (A0)+
MOVEQ #$0C, D0
MOVE.B D0, LCD
JSR @WaiLCD
MOVE.L -(A0), D0
RTS
; Efface l'cran LCD
@ClrLCD MOVE.L D0, (A0)+
MOVEQ #$01, D0 ; Clear screen
MOVE.B D0, LCD
JSR @WaiLCD
MOVEQ #$02, D0 ; Return Home
MOVE.B D0, LCD
JSR @WaiLCD
MOVE.L -(A0), D0
RTS
; Tempo d'init du LCD
@WaiLcd MOVE.L D0, (A0)+
MOVE.L #$00004FFF, D0
@WaiLc1 DBF D0, @WaiLc1
MOVE.L -(A0), D0
RTS
; Ecrit le caractere de (D0) sur le LCD
@CarLcd MOVE.L D1, (A0)+
@CarLc1 MOVE.B LCD, D1
ANDI.B #$80, D1
BNE @CarLc1 ; Attente Busy Flag= 0
MOVE.B D0, LCDat
MOVE.L -(A0), D1
RTS
; Ecrit la chaine pointe par A2 sur LCD
@StrLcd MOVE.L D0, (A0)+
@StrLc2 MOVE.B (A2)+, D0 ; Envoie le caractre en cours @CarLcd
CMP.B #$5E, D0 ; tant qu'on ne pointe pas sur ^: EoString.
BEQ @StrLc1
JSR @CarLcd
BRA @StrLc2
@StrLc1 MOVE.L -(A0), D0
RTS
; Lcd: Ligne 1
@Lg1Lcd MOVE.L D0, (A0)+
JSR @WaiLCD
MOVE.B #$80, D0
MOVE.B D0, LCD
JSR @WaiLCD
MOVE.L -(A0), D0
RTS
; Lcd: Ligne 2
@Lg2Lcd MOVE.L D0, (A0)+
JSR @WaiLCD
MOVE.B #$C0, D0
MOVE.B D0, LCD
JSR @WaiLCD
MOVE.L -(A0), D0
RTS
Connecteur Alimentation USB-B:
Afin de faciliter l'alimentation, l'ensemble est alimenté par une alimentation de type chargeur de téléphone USB.
Pour ce faire, un connecteur USB-B est monté sur la carte:
Le schéma est alors celui-ci:
DUART MC68681:
A présent, on va pouvoir monter un circuit d'interfaçage afin de communiquer avec un ordinateur, PC ou Mac.
L'interface la plus simple à mettre en oeuvre sera une liaison série de type RS232.
Si l'ordinateur connecté ne dispose pas de liaison série, ce qui est fort possible, il suffira d'utiliser un convertisseur Série/USB, ce qui fonctionne très bien.
Plus tard, on pourra embarquer un tel convertisseur sur notre carte.
Le connecteur standard de l'interface RS232 est un DB9 femelle en mode DCE, et un femelle DB9 mâle en mode DTE, on va donc implanter les deux.
Le connecteur DCE permettra de se connecter directement à un port série d'ordinateur (ou à un convertisseur RS232 / USB standard), tandis que le connecteur DTE permettra de brancher un équipement tel qu'une souris série par exemple.
Pour créer cette interface RS232, on va utiliser le DUART MC68681 que Marc m'a donné.
DUART MC68681
L'avantage de ce circuit, est qu'en plus de l'interface série, il dispose d'un Timer et de lignes de PIA intégrées (interface parallèle).
Horloge et générateur de Bauds:
La liaison série RS232 n'a pas de ligne d'horloge séparée, la synchronisation se fait avec le timing des bits transmis.
Pour générer ces signaux, le circuit DUART (ou UART ou ACIA pour certaines familles de processeurs) utilise un générateur de Bauds, qui peut être interne ou externe. Ce circuit génère une horloge aux temps normalisés, qui va permettre une transmission des bits sur la ligne série de façon connue des autre circuits périphériques que l'on connectera à notre interface.
Les vitesses de transmission les plus utilisées sont par exemple 4800, 9600, 19200 Bauds, ou leurs multiples et sous-multiples.
Comme le DUART MC68681 possède un générateur de Bauds intégrés, on n'aura pas besoin d'en câbler un sur la carte.
Pour travailler aux fréquences normalisées, le MC68681 utilise un quartz d'une valeur un peu exotique, à savoir 3,6864 MHz.
Pourquoi une telle valeur ??
Si on regarde la valeur de ce quartz par rapport aux vitesses de transmissions normalisée, on s'aperçoit de la chose suivante:
3,6864 MHz / 19200 = 192
3,6864 MHz / 9600 = 384
3,6864 MHz / 4800 = 768
Si on continue ainsi avec les autres valeurs de transmission normalisées, on va s'apercevoir que tous les résultats sont des nombres entiers, ce qui est plutôt une bonne chose pour créer une horloge.
Pour passer de 19200 Bauds à 4800 Bauds par exemple, il suffira de rediviser notre horloge par 19200/4800 = 4.
Si on montait notre générateur de Bauds avec un quartz de 1MHz par exemple, on aurait des valeurs de diviseurs plus exotiques:
1MHz / 19200 = 52,0833
1MHz / 9600 = 104,166
1MHz / 4800 = 208,333
On voit qu'ici le résultat est décimal, et est faux par rapport à nos valeurs normalisées.
Pour retomber juste, il faudrait diviser notre horloge primaire de 1MHz par une valeur décimale de 52,0833 au lieu de 52 pour tomber juste, ce qui est beaucoup plus compliqué à réaliser avec de simples bascules.
Si on divise par la valeur la plus proche de 52 par exemple, on aurait:
1MHz / 52 = 19230.76 Bauds.
Pour une valeur normalisée de 19200 Bauds, on arriverait donc à 19230 Bauds, ce que certains circuits récepteurs n'accepteront pas.
Si on prenait l'horloge système de notre carte qui est à 16MHz, on aurait le même problème:
16MHz / 19200 = 833,333
En fait, comme on peut difficilement diviser notre Clock avec une valeur décimale, on va contourner la difficulté en utilisant un quartz ou un oscillateur qui aura une valeur décimale.
Si on le choisit bien, il suffira alors de diviser sa sortie par la bonne valeur entière, et le tour est joué.
Les valeurs les plus courants sont donc 3,6864MHz ou 14,7456MHz ou par exemple 4,9152MHz.
On comprend mieux pourquoi la datasheet du MC68681 demande un quartz de 3,6864MHz.
Le problème est que je n'ai pas de quartz de 3,6864MHz comme demandé ....
Par contre, la carte de Marc a deux oscillateurs de valeurs décimales.... 3,93216 MHz et 4,9142MHz !!!
Peut-on utiliser le 3,93216MHz ?
Essayons avec 19200 Bauds:
3,93216MHz / 19200 = 204,8 -> Valeur décimale, pas bon...
On va donc essayer d'utiliser le second à 4,9152MHz.
4,9152MHz / 19200 = 256
4,9152MHz / 9600 = 512
4,9152MHz / 4800 = 1024
Impeccable, on va utiliser celui-ci.
Que dit la datasheet du MC68681 ? Elle dit que l'on peut utiliser un quartz de 3,6864MHz, ou un oscillateur d'une valeur max de 4MHz.
Il va donc falloir diviser l'horloge fournie par notre nouvel oscillateur par 2, ce qui donnera 2,4576MHz.
2,4576MHz / 19200 = 128
2,4576MHz / 9600 = 256
2,4576MHz / 4800 = 512
Plutôt Quartz ou plutôt Oscillateur ??
La solution avec oscillateur pour le MC68681 permet de se passer de quartz, et de récupérer l'oscillateur de la carte de Marc.
Par contre, je n'ai plus de 74LS74 ou équivalent pour ramener cette horloge à une vitesse acceptable par le DUART... Dilemme ...
Profitant donc d'une commande de composants, j'ai acheté un quartz de 3,6864 MHz et je réserve l'oscillateur pour plus tard.
Voici le schéma, avec les deux possibilités:
Le lien de téléchargement du schéma en format pdf se trouve en bas de page.
Et le DTACK\ ?
Le MC68681 est un périphérique 68000, donc avec une validation de bus de type DTACK\.
Le MC68020, de part sa structure 32 bits avec la possibilité de passer en 16 ou 8 bits, utilise DSACK0\ et DSACK1\ pour la même fonction.
Lorsque l'on aura plusieurs périphériques de la famille 68000 sur la carte, tous les DTACK\ devront aller au même endroit, sur DTACK_DUART\ du schéma.
Comme ces périphériques ont leur sortie DTACK\ de type Open drain, on peut donc tous les relier ensembles, avec une résistance de rappel au +5V.
En rebranchant la carte, elle refonctionne comme avant l'ajout du DUART, ce qui est plutôt encourageant.
On voit que la carte bien évolué depuis le début, avec en haut les deux connecteurs SubD9.
Par contre, le câblage devient un peu délicat...
Lorsque les tests seront terminés, on verra pour faire un circuit imprimé, avec le 68020 en mode 32 bits.
L'idée est de passer en mode 32 bits, avec 4 RAM, 4 EEProm, de la mémoire dynamique, un écran TFT en SPI mais on n'en est pas encore là...
Reste maintenant la partie logicielle à continuer....
MC68681 In et Out:
Afin de tester l'adressage correct du DUART MC68681 ainsi que la génération correcte du DTACK\, on peut commencer par les fonctions du port IN et du port OUT de ce circuit.
Une Led Rouge a été rajoutée sur OUT7 et un bouton poussoir a été ajouté sur IN5.
Ayant également ajouté un support ZIF (Zero Insertion Force) pour l'eeprom, la carte ressemble à présent à ceci (In5 et Out7 sont câblés en bas à gauche):
Le MC68681 ayant son adresse de base en $0000 0000 0005 0000, le registre de configuration des sorties OPCR va se trouver en écriture à l'adresse Base + $0D = $0000 0000 0005 000D.
OUT:
La doc du MC68681 nous indique que pour configurer OUT7 du DUART en pin de sortie utilisable comme bon nous semble, il faut mettre à 0 le bit b7 du registre OPCR, donc OPCR = $00 mettra toutes ses pins OUT en sortie.
En mettant ces bits à 1, on active d'autres fonctions spécifiques de ces pins (voir doc du MC68681).
La mise à 1 et à 0 des ces sorties se fera par l'écriture du bit correspondant dans le registre OPR. Cette opération s'effectue en utilisant les registres OPR.Set (Base + $0E) et OPR.Reset (Base + $0F), mais les sorties du port OP0 à OP7 ont des niveaux inversés par rapport au positionnement de ces bits.
Par exemple, pour mettre la sortie OP7 à 1, il faut mettre le bit OPR.7 à 0 et inversement. Pour mettre OPR.7 à 0, il suffira de mettre OPRReset.7 à 1.
Donc: OPRReset = 1 => OUT7 à 1 et OPRSet = 1 => OUT7 à 0.
Dans le source suivant, j'ai directement fait l'inversion dans ClrOut7 et dans SetOut7.
IN:
Pour la lecture de IN5, il suffira de faire une lecture du registre IP (Base + $0D).
Voici le source du programme de test qui change l'état de la Led OUT7 suivant l'état du bouton poussoir IN5, tout en continuant le chenillard d'origine sur le buffer:
; MC68020: Mise en route du DUART MC68681
; Sauvegarde SourceTree
; Code sur la base de la carte LIsa 68000
; Fred: microcontroller.robotics@gmail.com
; !!! Attention: Ed ne prend pas les TAB en debut de ligne
; Mettre des espaces
; !!! Attention: Suivant la longueur des constantes ROM,
; le PC peut etre a une adresse impaire (Bug de Ed)
; V5: Essai de OUT sur le DUART
; V6: Essai de IN sur le DUART
VECTEUR
SSP $00010FF0
RAM $00010000
ROM $00000400
CONST
OUT = $00070000 ; Out
LCD = $00060000 ; Ecran LCD - config -
LCDat= $00060004 ; Ecran LCD - caracteres -
Pile = $00010E00 ; Pile de sauvegarde generale - en plus du stack -
OPCR = $0005000D
SET_OPR = $0005000E
CLR_OPR = $0005000F
IP_DUART= $0005000D
St1 = " 68020 ^"
St2 = "IniDUART ^"
StRdy = "Ready V6 ^"
MAIN
; Programme principal
LEA Pile, A0 ; Init Pointeur Pile A0
JSR @IniLcd ; Init LCD
LEA St1, A2 ; Affichage St1: 68020
JSR @StrLcd
JSR @Lg2Lcd
LEA St2, A2 ; Affichage St2: IniDUART
JSR @StrLcd
JSR @IniOut7 ; Init Duart Out-7
JSR @Lg2Lcd
LEA StRdy, A2 ; Affichage StRdy: Ready
JSR @StrLcd
@Loop JSR @Chenil ; Appel au chenillard avec Leds et DUART
JSR @ReadIn5 ; Lecture In-5 du DUART
JMP @Loop
@IniOut7 MOVE.L D0,(A0)+
MOVEQ #$00, D0 ; Output Out-7 en Set/Rst (b7=0)
MOVE.B D0, OPCR ; Chargement OPCR
MOVE.L -(A0), D0
RTS
@ClrOut7 MOVE.L D0,(A0)+
MOVEQ #$80, D0 ; Output Out 7 inversee
MOVE.B D0, SET_OPR ; Chargement OPR Set
MOVE.L -(A0), D0
RTS
@SetOut7 MOVE.L D0,(A0)+
MOVEQ #$80, D0 ; Output Out 7 inversee
MOVE.B D0, CLR_OPR ; Chargement OPR Reset
MOVE.L -(A0), D0
RTS
@ReadIn5 MOVE.L D0,(A0)+
CLR.L D0
MOVE.B IP_DUART,D0 ; Lecture IP
AND.B #$20, D0 ; Masque IP5
BNE @Read0
JSR @SetOut7
BRA @Read1
@Read0 JSR @ClrOut7
@Read1 MOVE.L -(A0),D0
RTS
@Chenil MOVE.L D0,(A0)+ ; Sauvegarde D0
MOVEQ #$AA, D0 ; Chargement chenillard
MOVE.B D0, $00070000 ; Sortie sur Chenillard
MOVE.L #$0FFFFFFF, D0 ;Init Tempo
@Bcl1 DBEQ D0, @Bcl1
MOVEQ #$55, D0 ; Chargement chenillard
MOVE.B D0, $00070000 ; Sortie sur Chenillard
MOVE.L #$0FFFFFFF, D0 ;Init Compteur
@Bcl2 DBEQ D0, @Bcl2
MOVE.L -(A0), D0
RTS
; Init LCD
@IniLCD MOVE.L D0,(A0)+
MOVEQ #$38, D0 ; 2 lignes, 5x7 dots, 8 bits
MOVE.B D0, LCD
JSR @WaiLcd ; Tempo reset LCD
JSR @CursOf ; Curseur Off
JSR @ClrLCD ; Efface ecran
MOVE.L -(A0), D0
RTS
; Display On, Curseur on, not Blink
@CursOn MOVE.L D0, (A0)+
MOVEQ #$0E, D0
MOVE.B D0, LCD
JSR @WaiLCD
MOVE.L -(A0), D0
RTS
@CursOf MOVE.L D0, (A0)+ ; Display On, Curseur off, not Blink
MOVEQ #$0C, D0
MOVE.B D0, LCD
JSR @WaiLCD
MOVE.L -(A0), D0
RTS
@ClrLCD MOVE.L D0, (A0)+ ; Efface l'cran LCD
MOVEQ #$01, D0 ; Clear screen
MOVE.B D0, LCD
JSR @WaiLCD
MOVEQ #$02, D0 ; Return Home
MOVE.B D0, LCD
JSR @WaiLCD
MOVE.L -(A0), D0
RTS
@WaiLcd MOVE.L D0, (A0)+ ; Tempo d'init du LCD
MOVE.L #$00004FFF, D0
@WaiLc1 DBF D0, @WaiLc1
MOVE.L -(A0), D0
RTS
@CarLcd MOVE.L D1, (A0)+ ; Ecrit le caractre de (D0) sur le LCD
@CarLc1 MOVE.B LCD, D1
ANDI.B #$80, D1
BNE @CarLc1 ; Attente Busy Flag= 0
MOVE.B D0, LCDat
MOVE.L -(A0), D1
RTS
@StrLcd MOVE.L D0, (A0)+ ; Ecrit la chaine pointe par A2 sur LCD
@StrLc2 MOVE.B (A2)+, D0 ; Envoie le caractre en cours @CarLcd
CMP.B #$5E, D0 ; tant qu'on ne pointe pas sur ^: EoString.
BEQ @StrLc1
JSR @CarLcd
BRA @StrLc2
@StrLc1 MOVE.L -(A0), D0
RTS
@Lg1Lcd MOVE.L D0, (A0)+ ; Lcd: Ligne 1
JSR @WaiLCD
MOVE.B #$80, D0
MOVE.B D0, LCD
JSR @WaiLCD
MOVE.L -(A0), D0
RTS
@Lg2Lcd MOVE.L D0, (A0)+ ; Lcd: Ligne 2
JSR @WaiLCD
MOVE.B #$C0, D0
MOVE.B D0, LCD
JSR @WaiLCD
MOVE.L -(A0), D0
RTS
La suite au prochain épisode ...
Remarque:
Ed souffre de quelques imperfection, il nécessiterait un bon dépoussiérage mais je n'ai plus tous les sources.
Donc soit on continue de l'utiliser en étant prudents, soit il faut en réécrire un autre sous Windows ou MasOS mais je n'aurais pas le temps car d'autres projets sont en cours, soit trouver un assembleur (ou C ?) qui fonctionne correctement, de préférence sous MacOS ou DOS (ce qui permettrait de le faire tourner sur MACOS et Windows (avec Boxer).
A méditer ...
Easy68K
Pour l'instant, j'ai pu ressortir un vieux eeePC sous Windows XP, et y installer Easy68K (http://www.easy68k.com) .
Après quelques modifications du dernier source, j'ai pu créer un fichier .bin pour mon programmateur, en mode 8 bits.
Voilà ce que ça donne, et la partie déclarative a dû être adaptée, mais tout s'est fait facilement.
Il faut dire que Ed! ne respectait pas la syntaxe Motorola, car lorsque je l'ai développé, je n'avais ni assembleur Motorola de référence, ni guide de l'assembleur...
; MC68020: Mise en route du DUART MC68681
; Sauvegarde SourceTree
; Code sur la base de la carte LIsa 68000
; Fred: microcontroller.robotics@gmail.com
; !!! Attention: Ed ne prend pas les TAB en debut de ligne
; Mettre des espaces
; !!! Attention: Suivant la longueur des constantes ROM,
; le PC peut etre a une adresse impaire (Bug de Ed)
; V5: Essai de OUT sur le DUART
; V6: Essai de OUT sur le DUART
; V7: Adaptation du source pour Easy68K sous Windows
CODE EQU 0
DATA EQU 1
VECTEURS EQU 2
SECTION VECTEURS
org $0
SSP dc.l $00010FF0 ; Vecteur SSP: Stack Pointer
PC dc.l START ; vecteur PC
SECTION CODE
ORG $400
RAM EQU $00010000
OUT EQU $00070000 ; Out
LCD equ $00060000 ; Ecran LCD - config -
LCDat equ $00060004 ; Ecran LCD - caracteres -
Pile equ $00010E00 ; Pile de sauvegarde generale - en plus du stack -
OPCR equ $0005000D
SET_OPR equ $0005000E
CLR_OPR equ $0005000F
IP_DUART equ $0005000D
St1 dc.b ' 68020 ^'
St2 dc.b 'IniDUART ^'
StRdy dc.b 'Ready V7 ^'
START:
; Programme principal
LEA Pile, A0 ; Init Pointeur Pile A0
JSR .IniLcd ; Init LCD
LEA St1, A2 ; Affichage St1: 68020
JSR .StrLcd
JSR .Lg2Lcd
LEA St2, A2 ; Affichage St2: InitDUART
JSR .StrLcd
JSR .IniOut7 ; Init Duart Out-7
JSR .Lg2Lcd
LEA StRdy, A2 ; Affichage StRdy: Ready
JSR .StrLcd
.Loop JSR .Chenil ; Appel au chenillard avec Leds et DUART
JSR .ReadIn5 ; Lecture In-5 du DUART
JMP .Loop
.IniOut7 MOVE.L D0,(A0)+
MOVEQ #$00, D0 ; Output Out-7 en Set/Rst (b7=0)
MOVE.B D0, OPCR ; Chargement OPCR
MOVE.L -(A0), D0
RTS
.ClrOut7 MOVE.L D0,(A0)+
MOVEQ #$80, D0 ; Output Out 7 inversee
MOVE.B D0, SET_OPR ; Chargement OPR Set
MOVE.L -(A0), D0
RTS
.SetOut7 MOVE.L D0,(A0)+
MOVEQ #$80, D0 ; Output Out 7 inversee
MOVE.B D0, CLR_OPR ; Chargement OPR Reset
MOVE.L -(A0), D0
RTS
.ReadIn5 MOVE.L D0,(A0)+
CLR.L D0
MOVE.B IP_DUART,D0 ; Lecture IP
AND.B #$20, D0 ; Masque IP5
BNE .Read0
JSR .SetOut7
BRA .Read1
.Read0 JSR .ClrOut7
.Read1 MOVE.L -(A0),D0
RTS
.Tempo MOVE.L D0,(A0)+
MOVE.W #$9FFF, D0 ;Init Tempo
.Tmp1 DBEQ D0, .Tmp1 ; DBxx ne fonctionne que sur des Word
MOVE.L -(A0),D0
RTS
.Chenil MOVE.L D0,(A0)+ ; Sauvegarde D0
MOVEQ #$AA, D0 ; Chargement chenillard
MOVE.B D0, $00070000 ; Sortie sur Chenillard
JSR .Tempo
MOVEQ #$55, D0 ; Chargement chenillard
MOVE.B D0, $00070000 ; Sortie sur Chenillard
JSR .Tempo
MOVE.L -(A0), D0
RTS
; Init LCD
.IniLCD MOVE.L D0,(A0)+
MOVEQ #$38, D0 ; 2 lignes, 5x7 dots, 8 bits
MOVE.B D0, LCD
JSR .WaiLcd ; Tempo reset LCD
JSR .CursOf ; Curseur Off
JSR .ClrLCD ; Efface ecran
MOVE.L -(A0), D0
RTS
; Display On, Curseur on, not Blink
.CursOn MOVE.L D0, (A0)+
MOVEQ #$0E, D0
MOVE.B D0, LCD
JSR .WaiLCD
MOVE.L -(A0), D0
RTS
.CursOf MOVE.L D0, (A0)+ ; Display On, Curseur off, not Blink
MOVEQ #$0C, D0
MOVE.B D0, LCD
JSR .WaiLCD
MOVE.L -(A0), D0
RTS
.ClrLCD MOVE.L D0, (A0)+ ; Efface l'cran LCD
MOVEQ #$01, D0 ; Clear screen
MOVE.B D0, LCD
JSR .WaiLCD
MOVEQ #$02, D0 ; Return Home
MOVE.B D0, LCD
JSR .WaiLCD
MOVE.L -(A0), D0
RTS
.WaiLcd MOVE.L D0, (A0)+ ; Tempo d'init du LCD
MOVE.L #$00004FFF, D0
.WaiLc1 DBF D0, .WaiLc1
MOVE.L -(A0), D0
RTS
.CarLcd MOVE.L D1, (A0)+ ; Ecrit le caractre de (D0) sur le LCD
.CarLc1 MOVE.B LCD, D1
ANDI.B #$80, D1
BNE .CarLc1 ; Attente Busy Flag= 0
MOVE.B D0, LCDat
MOVE.L -(A0), D1
RTS
.StrLcd MOVE.L D0, (A0)+ ; Ecrit la chaine pointe par A2 sur LCD
.StrLc2 MOVE.B (A2)+, D0 ; Envoie le caractre en cours .CarLcd
CMP.B #$5E, D0 ; tant qu'on ne pointe pas sur ^: EoString.
BEQ .StrLc1
JSR .CarLcd
BRA .StrLc2
.StrLc1 MOVE.L -(A0), D0
RTS
.Lg1Lcd MOVE.L D0, (A0)+ ; Lcd: Ligne 1
JSR .WaiLCD
MOVE.B #$80, D0
MOVE.B D0, LCD
JSR .WaiLCD
MOVE.L -(A0), D0
RTS
.Lg2Lcd MOVE.L D0, (A0)+ ; Lcd: Ligne 2
JSR .WaiLCD
MOVE.B #$C0, D0
MOVE.B D0, LCD
JSR .WaiLCD
MOVE.L -(A0), D0
RTS
END START
Maintenant que tout ça fonctionne, il faudrait que j'arrive à le faire tourner sur Mac avec Wine par exemple, le temps d'écrire un autre compilateur pour MacOS.
En tout cas, je peux dire que Easy68K semble excellent, de même que les autres programmes qui l'accompagnent (Simulateur, utilitaire Bin). L'aide intégrée est également excellente.