La réponse de @ vicatcu est assez complète. Une autre chose à noter est que le processeur peut se retrouver dans des états d'attente (cycles de processeur bloqués) lors de l'accès aux E / S, y compris la mémoire de programme et de données.
Par exemple, nous utilisons un TI F28335 DSP; certaines zones de la mémoire RAM sont à l'état d'attente 0 pour le programme et la mémoire de données, donc lorsque vous exécutez du code dans la mémoire RAM, il s'exécute à 1 cycle par instruction (à l'exception des instructions qui prennent plus d'un cycle). Lorsque vous exécutez du code à partir de la mémoire FLASH (EEPROM intégrée, plus ou moins), cependant, il ne peut pas fonctionner à 150 MHz et il est plusieurs fois plus lent.
En ce qui concerne le code d'interruption à grande vitesse, vous devez apprendre un certain nombre de choses.
Tout d'abord, familiarisez-vous avec votre compilateur. Si le compilateur fait du bon travail, il ne devrait pas être beaucoup plus lent que l'assemblage codé à la main pour la plupart des choses. (où "beaucoup plus lent": un facteur 2 me conviendrait; un facteur 10 serait inacceptable) Vous devez apprendre comment (et quand) utiliser les indicateurs d'optimisation du compilateur, et de temps en temps, vous devriez regarder à la sortie du compilateur pour voir comment il fonctionne.
D'autres choses que le compilateur peut faire pour accélérer le code:
utiliser des fonctions en ligne (je ne me souviens pas si C le supporte ou si ce n'est qu'un isme C ++), à la fois pour les petites fonctions et pour les fonctions qui ne seront exécutées qu'une ou deux fois. L'inconvénient est que les fonctions en ligne sont difficiles à déboguer, surtout si l'optimisation du compilateur est activée. Mais ils vous épargnent des séquences d'appel / retour inutiles, surtout si l'abstraction "fonction" est à des fins de conception plutôt que d'implémentation de code.
Regardez le manuel de votre compilateur pour voir s'il a des fonctions intrinsèques - ce sont des fonctions intégrées dépendantes du compilateur qui correspondent directement aux instructions d'assemblage du processeur; certains processeurs ont des instructions d'assemblage qui font des choses utiles comme inverser min / max / bit et vous pouvez gagner du temps en le faisant.
Si vous effectuez un calcul numérique, assurez-vous que vous n'appelez pas les fonctions de bibliothèque de mathématiques inutilement. Nous avons eu un cas où le code ressemblait y = (y+1) % 4
à un compteur qui avait une période de 4, s'attendant à ce que le compilateur implémente le modulo 4 en tant que AND au niveau du bit. Au lieu de cela, il a appelé la bibliothèque mathématique. Nous avons donc remplacé par y = (y+1) & 3
faire ce que nous voulions.
Familiarisez-vous avec la page de piratage de bits . Je vous garantis que vous en utiliserez au moins un souvent.
Vous devez également utiliser les périphériques de temporisation de votre CPU pour mesurer le temps d'exécution du code - la plupart d'entre eux ont un timer / compteur qui peut être réglé pour s'exécuter à la fréquence d'horloge du CPU. Capturez une copie du compteur au début et à la fin de votre code critique, et vous pouvez voir combien de temps cela prend. Si vous ne pouvez pas faire cela, une autre alternative consiste à abaisser une broche de sortie au début de votre code, à la relever à la fin et à regarder cette sortie sur un oscilloscope pour chronométrer l'exécution. Il y a des compromis à chaque approche: le temporisateur / compteur interne est plus flexible (vous pouvez chronométrer plusieurs choses) mais plus difficile à obtenir les informations, tandis que définir / effacer une broche de sortie est immédiatement visible sur une étendue et vous pouvez capturer des statistiques, mais il est difficile de distinguer plusieurs événements.
Enfin, il y a une compétence très importante qui vient avec l'expérience - à la fois générale et avec des combinaisons processeur / compilateur spécifiques: savoir quand et quand ne pas optimiser . En général, la réponse est ne pas optimiser. La citation de Donald Knuth est publiée fréquemment sur StackOverflow (généralement juste la dernière partie):
Il faut oublier les petites efficacités, disons environ 97% du temps: l'optimisation prématurée est à l'origine de tout mal
Mais vous êtes dans une situation où vous savez que vous devez faire une sorte d'optimisation, il est donc temps de mordre la balle et d'optimiser (ou d'obtenir un processeur plus rapide, ou les deux). N'écrivez PAS l'intégralité de votre ISR en assemblage. C'est presque un désastre garanti - si vous le faites, dans les mois ou même les semaines, vous oublierez des parties de ce que vous avez fait et pourquoi, et le code est susceptible d'être très fragile et difficile à changer. Il est probable que certaines parties de votre code soient de bons candidats pour l'assemblage.
Signifie que certaines parties de votre code sont bien adaptées au codage d'assemblage:
- des fonctions bien contenues et bien définies de petites routines peu susceptibles de changer
- fonctions pouvant utiliser des instructions de montage spécifiques (min / max / décalage à droite / etc)
- fonctions qui sont appelées plusieurs fois (vous obtenez un multiplicateur: si vous économisez 0,5usec sur chaque appel, et il est appelé 10 fois, cela vous fait économiser 5 usec, ce qui est important dans votre cas)
Apprenez les conventions d'appel de fonction de votre compilateur (par exemple, où il place les arguments dans les registres et quels registres il enregistre / restaure) afin de pouvoir écrire des routines d'assemblage appelables C.
Dans mon projet actuel, nous avons une base de code assez grande avec du code critique qui doit s'exécuter dans une interruption de 10 kHz (100usec - son familier?) Et il n'y a pas beaucoup de fonctions écrites en assembleur. Ceux qui le sont, sont des choses comme le calcul CRC, les files d'attente logicielles, la compensation de gain / décalage ADC.
Bonne chance!