Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.
La shoutbox n'est pas chargée par défaut pour des raisons de performances. Cliquez pour charger.

Forum Casio - Projets de programmation


Index du Forum » Projets de programmation » La conquête des coprocesseurs DSP
Lephenixnoir Hors ligne Administrateur Points: 24229 Défis: 170 Message

La conquête des coprocesseurs DSP

Posté le 22/09/2020 10:42

Contexte, le DSP intégré et les DSP du SPU

Récemment, j'ai redécouvert avec l'aide de Yatis que le SH7305, le microprocesseur qu'on trouve dans les calculatrices SH4 (y compris les Graph mono récentes et la Graph 90+E) possède un module de traitement de son (SPU) avec deux DSP.

Pour information, un DSP est un coprocesseur dédié au traitement du signal. C'est une sorte de processeur qui travaille en parallèle du processeur central et possède un jeu d'instructions permettant de traiter rapidement des signaux. C'est donc particulièrement adapté au traitement du son. Leur existence est très intéressante car on peut dans une certaine mesure les détourner pour faire d'autres tâches intensives. Mon plan par exemple c'est de faire des shaders 2D avec.

Il y a une poignée de ressources parlant de DSP ([1], [2], [3]), mais en fait la plupart concernent un DSP intégré au processeur central, documenté dans la section 6 du manuel du SH4-AL DSP (le processeur qu'on a et qu'on appelle communément « SH4 »). Ce DSP est intéressant mais il ne tourne pas en parallèle du processeur central : pour l'utiliser, on insère des instructions DSP dans le code et dans ce cas le processeur ne fait rien quand le DSP travaille. De plus, son jeu d'instructions est difficile à détourner dans des buts graphico-ludiques (il n'a par exemple qu'une seule multiplication 16x16→32, ce qui est boiteux au possible).

Par contre le DSP intégré a deux zones de RAM appelées XRAM et YRAM de 8k chacune, qui sont foudroyantes en vitesse parce que situées près du bus processeur. Pour vous donner une idée, les memcpy() et memset() optimisé de gint tournent à 10 Mo/s et 30 Mo/s environ pour la RAM normale. Avec le DSP, on peut monter aisément à 300 Mo/s voire 450 Mo/s si on s'y prend bien. Le potentiel est très élevé pour les calculs intensifs, avec la limite que 16k c'est très peu.

Les deux DSP de l'unité de traitement de son (SPU) sont différents. Ils ont une mémoire de travail beaucoup plus grande (vaguement connue [4]), avec chacun 170k de XRAM, au moins 30k de YRAM (pas clair encore), et 160k de RAM supplémentaire pour stocker du code. Puisque la mémoire pour le code est différence de la mémoire où on stocke le programme central, ces DSP calculent vraiment en parallèle à la façon d'un processeur multi-coeurs. Le hic, c'est qu'il n'y a pas de documentation. Il faut donc rétro-analyser tout ce dont on dispose pour récupérer le fonctionnement de 0. C'est là que ça se complique...

Le modèle 24-bits et la RAM à trous

Dans la doc du SH7305, il y a une seule page expliquant les fonctionnalités principales du SPU, c'est en quelque sorte le teaser de Renesas envers ses potentiels clients ; tout le détail n'est fourni qu'aux clients qui utilisent le processeur pour de vrai. Mais la doc explique déjà que le DSP manipule des mots de 24 bits, qu'on peut imaginer sont des valeurs en point fixe pour représenter des échantillons de signal. C'est différent du DSP intégré donc d'emblée on sait que le jeu d'instructions sera différent.

Ce qui est un peu casse-pieds, c'est que la XRAM et la YRAM sont à trous pour faciliter le travail du DSP. Vous voyez, un signal est souvent vu comme un tableau d'échantillons géant. Le problème c'est qu'avec des valeurs de 24 bits (trois octets), atteindre le n-ème échantillon demande de calculer n*3, ce qui prend du temps en électronique. Les processeurs ont depuis longtemps pris l'habitude d'utiliser des types de données à 1, 2, 4 ou 8 octets (des puissances de 2) parce que multiplier par des puissances de 2 est immédiat en électronique (il suffit de déplacer les fils et d'ajouter des 0). Ici, le DSP fait un truc encore plus fourbe : il place un mot de 24 bits (3 octets) tous les 4 octets, en ignorant un octet sur 4. Le premier mot couvre les adresses 0, 1, et 2 ; le second couvre les adresses 4, 5 et 6 ; le troisième, les 8, 9 et 10 ; et ainsi de suite.

Comme les adresses 3, 7, 11 etc sont inutilisées, le constructeur a trouvé malin de ne pas réellement y mettre de mémoire. Du coup la XRAM et la YRAM sont hyper fragmentées, avec un trou tous les quatre octets. Impossible donc d'y stocker un simple int puisqu'il n'y a pas 4 octets de mémoire d'affilée. Il faudra donc être malin pour faire marcher ça. Par contre, le RGB888 ça passera crème pourvu qu'on arrive à calculer avec efficacement.

Qu'y a-t-il à faire et où va-t-on ?

Actuellement je désassemble le programme de Renesas qui émule le SH7305 (d'ailleurs utilisé dans les émulateurs de Casio) pour déterminer quels sont les registres périphériques des DSP, comment les initialiser, les utiliser, et j'espère trouver le jeu d'instructions. Il y a pas mal de choses à comprendre :

• Liste des registres périphériques : fait
• Allumage et initialisation : en bonne voie
• Transfert de données via DMA intégré : en bonne voie
• Fonctionnement de la RAM et des bus : ?
• Jeu d'instructions : ???
• Horloge : ?

Ce sera un bon début pour cette fois.


Lephenixnoir Hors ligne Administrateur Points: 24229 Défis: 170 Message

Citer : Posté le 14/11/2021 09:21 | #


Great work, thanks! The ARTIC chip seems to match as far as memory layout and access sizes go, which is already pretty good. The instruction set of the DSP itself is not documented though, only these 8-bit commands that are defined at the chip level.

What kind of device did you use exactly? "SPUV" doesn't bring up anything suggestive in search results. Also what type of firmware did you dump? Was it the OS/kernel, libraries?

Your assumption is, fortunately, incorrect. You can absolutely write to PRAM (and XRAM/YRAM) with the CPU, you just need to use a 4-aligned address and you can only perform 4-byte accesses. Please see this document first.

I don't think the CPU will crash by initializing the DSP with an invalid program so your crash is probably a "trivial" bug. Note that we don't have full C++ support because it's kind of complicated. If possible I'd first externalize the lambdas as functions and only use C features, because all this code on the stack is an added "risk" (in terms of possible bugs lying around).

Oh before I forget the interrupt vector for DSP is 0xce0, not 0xcc1. The increment is 0x20. Any priority is fine. Also if you don't clear interrupt request flags in your handlers you'll likely freeze at the first interrupt.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Gladosse Hors ligne Membre Points: 229 Défis: 2 Message

Citer : Posté le 14/11/2021 11:44 | #


Pourquoi casio utilise ces processeurs 24 bits si c'est si inconvenient?
Il doit bien avoir une raison
Lephenixnoir Hors ligne Administrateur Points: 24229 Défis: 170 Message

Citer : Posté le 14/11/2021 11:55 | #


CASIO ne les utilise pas. Ils sont juste là au milieu de la puce, parce que le SH7305 que CASIO a commandé à Renesas est un produit dérivé d'un produit qui les avait. Et nous on se jette juste sur l'occasion pour gratter de la puissance de calcul partout.

Pourquoi ces 24 bits sinon ? C'est le format de point-fixe de ce DSP. Stocker des nombres à 24 bits au lieu de 32 bits permet de gagner de la mémoire si la précision supplémentaire n'est pas nécessaire, et pour le son effectivement on peut s'en passer.

Pourquoi mettre 24 bits tous les 32 bits et laisser un trou un octet sur 4 ? Simplement pour pouvoir accéder à chaque valeur par 4*index, ce qui est beaucoup plus efficace qu'une multiplication par 3. On y gagne pas mal mine de rien.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Bylaws Hors ligne Membre Points: 5 Défis: 0 Message

Citer : Posté le 14/11/2021 13:32 | #


Lephenixnoir a écrit :

What kind of device did you use exactly? "SPUV" doesn't bring up anything suggestive in search results. Also what type of firmware did you dump? Was it the OS/kernel, libraries?


I used spuv.bin from this device's firmware and wrote a little program using the kernel driver from here as a reference for parsing it. There are no strings aside from a copyright header in YRAM ('Copyright (c) 2013 Rensas Mobile Corporation & NXP Software') however running statistics on the PRAM binary gives quite a distinctive pattern so I'm trying to find other binaries with similar patterns in the hope of identifying the architecture.

Lephenixnoir a écrit :

Your assumption is, fortunately, incorrect. You can absolutely write to PRAM (and XRAM/YRAM) with the CPU, you just need to use a 4-aligned address and you can only perform 4-byte accesses. Please see this document first.

Oh thanks! I saw that page but missed the note on the alignment requirements, that explains my weird behaviour with the patterns too.

Lephenixnoir a écrit :

I don't think the CPU will crash by initializing the DSP with an invalid program so your crash is probably a "trivial" bug. Note that we don't have full C++ support because it's kind of complicated. If possible I'd first externalize the lambdas as functions and only use C features, because all this code on the stack is an added "risk" (in terms of possible bugs lying around).

I will try without the lambda then and verify if that fixes it, if I have the time I'll have a look at the generated code in ghidra and see how it could be causing a crash.

Lephenixnoir a écrit :

Oh before I forget the interrupt vector for DSP is 0xce0, not 0xcc1. The increment is 0x20. Any priority is fine. Also if you don't clear interrupt request flags in your handlers you'll likely freeze at the first interrupt.

Thanks, I'll fix that.
Lephenixnoir Hors ligne Administrateur Points: 24229 Défis: 170 Message

Citer : Posté le 14/11/2021 13:36 | #


Thanks for the details! I find this approach pretty audacious and quite impressive, I hope you'll find more stuff!
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Bylaws Hors ligne Membre Points: 5 Défis: 0 Message

Citer : Posté le 14/11/2021 21:50 | # | Fichier joint


I am having curious behaviour with sleep_us_spin, them issues I was having earlier were probably unrelated to the lambda. After extending my test program to sequentially try instructions one by one I kept having random freezes for no discernable reason, then after spending a good hour thinking it was a timing or irq issue I realised that even a simple loop printing text to the screen with a sleep will cause a lockup eventually. Is this an issue with what I am doing or have I discovered a gint bug? I have attached my test program if it helps.
Lephenixnoir Hors ligne Administrateur Points: 24229 Défis: 170 Message

Citer : Posté le 14/11/2021 22:39 | #


Aha that's a nice find. You're outside of tested use cases which is why I never saw that before, but it would be bad faith to not call it a bug.

Basically this function waits by watching the interrupt request flag, and is designed for use in drivers when interrupts are disabled. But you're using it with interrupts enabled, which enables a timing attack to occur where the timer driver's interrupt handler handles the interrupt and clears the flag before you can check it.

I should really use something else to detect the end of the waiting period, but there's no obvious way to do it. The timer API is really designed to be used with interrupts too.

Since you have interrupts enabled you should just use sleep_us() which uses interrupts normally. If this raises any other issue please let me know.

Also on the fx-CG series (which apparently you're not using) you have a different problem due to spamming the asynchronous dupdate() and doing diff-drawing on two separate VRAMs. There might be another race condition there, I'll check it out later...

Ajouté le 15/11/2021 à 06:45 :
I found a suitable fix. You can pull that if you want (but it's on dev for now) if sleep_us() really doesn't work for you.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)

LienAjouter une imageAjouter une vidéoAjouter un lien vers un profilAjouter du codeCiterAjouter un spoiler(texte affichable/masquable par un clic)Ajouter une barre de progressionItaliqueGrasSoulignéAfficher du texte barréCentréJustifiéPlus petitPlus grandPlus de smileys !
Cliquez pour épingler Cliquez pour détacher Cliquez pour fermer
Alignement de l'image: Redimensionnement de l'image (en pixel):
Afficher la liste des membres
:bow: :cool: :good: :love: ^^
:omg: :fusil: :aie: :argh: :mdr:
:boulet2: :thx: :champ: :whistle: :bounce:
valider
 :)  ;)  :D  :p
 :lol:  8)  :(  :@
 0_0  :oops:  :grr:  :E
 :O  :sry:  :mmm:  :waza:
 :'(  :here:  ^^  >:)

Σ π θ ± α β γ δ Δ σ λ
Veuillez donner la réponse en chiffre
Vous devez activer le Javascript dans votre navigateur pour pouvoir valider ce formulaire.

Si vous n'avez pas volontairement désactivé cette fonctionnalité de votre navigateur, il s'agit probablement d'un bug : contactez l'équipe de Planète Casio.

Planète Casio v4.3 © créé par Neuronix et Muelsaco 2004 - 2024 | Il y a 92 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd