Quinze ans !

Monday, 04 August 2025
|
Écrit par
Grégory Soutadé

Encore un pallier de franchi pour le blog ! Quinze années de présence continue en ligne, c'est une belle réussite. Pour fêter ça, j'ai sorti mon éditeur favoris et ajouté un mode sombre ! Qu'est ce qui me motive encore à tapoter sur le clavier après tout ce temps ? Je pourrais naturellement citer le goût d'écrire des articles ainsi que l'envie de mettre en lumière ce que l'on ne trouve que trop peu ailleurs (ou même pas du tout). D'ailleurs, si je devais définir mes sections préférées, je dirais que ce sont celles qui racontent des histoires, qui exposent des points de vues argumentées. Viens ensuite la section balade et, de manière générale, chaque article qui contient des photos jolies/artistiques. Côté statistiques, la réalité est plus mitigée : ce sont les articles techniques (surtout en Anglais) qui sont le plus consultés.

Comme pour beaucoup de créateurs, la fréquentation est source de motivation. Quand elle est importante, on se sent redevable moralement vis-à-vis d'un public d'inconnus, sans pour autant qu'il n'y ait eu ni contrat, ni transaction financière. Ce qui nous pousse à produire plus de contenu. Même si j'ai une toute petite base de lecteurs réguliers, je constate qu'il y a globalement moins de connexions sur le blog (le trafic est principalement dirigé vers la forge). De ce fait, j'avoue avoir écrit un peu moins d'articles cette saison, après 2 années plus intenses. Néanmoins, cette baisse ne démotive pas le marcheur/coureur qui est en moi : il faut continuer à avancer même si le chemin est difficile. Car je rédige avant tout pour moi mon plaisir. Et quel plaisir de pouvoir se re plonger dans mes anciennes publications lorsque je recherche une information. Je suis souvent étonné et ravi de re découvrir ces bouts de texte et d'images, un peu comme s'ils avaient été écrits par quelqu'un d'autre. C'est l'avantage du blog par rapport aux plateformes en flux continu : les articles sont référencés et facilement accessibles des années plus tard !

Concernant le bilan de l'année écoulée, et bien c'est malheureusement la guerre qui tient le haut de panier. Le petit poucet Ukrainien résiste encore et toujours à l'envahisseur, qui ne cesse de grignoter du territoire de toutes pars malgré le coût humain faramineux. Il n'est désormais plus rare d'entendre voler dans le ciel plusieurs centaines de drones chaque nuit (avec un pic de 700 le 8 juillet). De son côté, Kiev ne se démonte pas et réplique également avec des drones (des cibles sont régulièrement touchées sur tout le territoire russe) car c'est le meilleur moyen pour supporter son infériorité numérique. L'Oblast russe de Koursk a été totalement "libéré" (notamment grâce aux soldats Nord Coréens) et ne peut plus servir de monnaie d'échange. La russie s'est même payé le luxe d'ouvrir deux nouveaux fronts dans le Nord (Soumy) et l'Est du pays (Kharkiv). Heureusement, parmi toutes ces mauvaises nouvelles, le nœud stratégique que constitue Pokrovsk tient toujours. Longtemps chancelant, le soutien Américain semble avoir repris (pour combien de temps ?). Non loin de là, c'est Israël qui continu de frapper tous ses voisins, dans une guerre visant l'anéantissement du Hamas (et le maintien au pouvoir de l'actuel premier ministre), quelles que soient les pertes civiles. Il s'agit clairement de crimes de guerre, voire carrément de génocide si on ne regarde que la bande de Gaza. Après 1 an et demi de blocus total et une famine qui s'installe de plus en plus durement, les nations occidentales commencent à réagir, même si le soutien Américain reste entier.

Avec ce genre d'actualité, on ne parle plus beaucoup d'écologie. Pourtant, la terre souffre toujours autant et de manière très évidente : inondations en région Parisienne, vague de chaleur hors normes pour un moins de juin (36°C dans le nord de l'Europe), nouvelle vague prévue début août. Pour compléter un tableau déjà bien noir : Les cadeaux empoisonnés du président Macron se retournent désormais contre les Français. Il était fier de déclarer le quoi qu'il en coûte pendant la pandémie. Tout autant que de supprimer la taxe d'habitation (20 milliards d'euros par an sur les 44 qui manquent au budget actuel) afin d'augmenter le pouvoir d'achat. Résultat, même dans un contexte de taux d'intérêts historiquement bas, la dette a explosée depuis 2020 : quasiment 500 milliards d'euros supplémentaires, dépassant ainsi le seuil symbolique des 100% du PIB ! Mais comme il est tabou de prononcer le mot "impôt", histoire de ne pas faire fuir les électeurs, il envoie ses premiers ministres successifs se casser les dents lorsqu'ils doivent élaborer un budget censé redresser les finances du pays. Quelles sont les solutions proposées ? Détruire les services publics déjà à bout de souffle, supprimer des "niches" (économies de bout de chandelle). Pire encore, supprimer des jours fériés : autant dire annihiler la culture et la mémoire pour récolter quelques maigres cotisations extraordinaires ! Ils détruisent le pays sur le long terme, mais aucun d'entre eux n'a les courage de prendre les vrais décisions et d'augmenter les impôts pour réparer les mauvaises décisions qui ont été les leurs. Finalement, ces deux thèmes sont assez proches : on ne peut pas faire indéfiniment de la merde, pour notre plaisir immédiat, sans avoir à le payer un jour. Mais en tant qu'individu totalement irresponsable, on espère que ce sont les autres qui vont payer à notre place...

Dans un registre plus positif, j'ai pu profiter de quelques jours de congés pour développer un projet que j'avais en tête depuis un certains temps : Drycat. l'objectif est de rendre secret un petit texte ou un fichier. Le logiciel génère alors un certain nombre de clés (parties) qui seront distribuées à différentes personnes. Pour reconstituer le secret originel, il faudra assembler les clés distribuées (nombres défini à la génération). Je trouve le principe génial ! Il s'appuie sur une théorie mathématique ancienne définie par Adi Shamir qui consiste à reconstruire une courbe par interpolation. D'ailleurs, il existe déjà une bibliothèque Javascript qui gère le cœur du projet, mais je lui ai ajouté une interface graphique moderne, avec QRCode, envoie de mail, chiffrement des fichiers, intégration d'OpenPGP. Le tout s'exécutant (quasiment) entièrement dans le navigateur ! Cerise sur le gâteau : mon frère a mis sa patte en modifiant le CSS. Ce projet n'est pas encore très populaire, mais il me permet de partager avec mes proches mes mots de passe (et autres informations personnelles) dans le cas où je ne serai plus en mesure d'administrer ma vie en ligne. Quant à libgourou, il continue d'être utilisé plus largement, avec 2 contributions et quelques tickets clôturés. Il draine une grand partie du trafic.

Statistiques 2024/2025

Nombre d'articles publiés Visites Données envoyées Pages affichées Trafic par domaine Systèmes d'exploitation Type d'IP

Top 10 :

Un top 10 qui représente 60% du trafic total du blog. La moyenne quotidienne (tous sites confondus) s'établit à 50 visites/jour. Le nombre de connexions IPv6 est en progression (de l'ordre de 40% ces derniers mois).

CRC16-CCITT optimization for ARM Cortex M4

Sunday, 13 July 2025
|
Écrit par
Grégory Soutadé

(Ooops) I did it again ! After doing the CRC32 optimization, I tried the same for CRC16-CCITT. This one is harder (but not so hard) to optimize for a C compiler because in modern CPU we mainly have 32 bits/64 bits registers, but for CRC16, we have to play with 16 bits values, split into upper and lower parts which need shift + mask operations.

Whatever, context is the same : target is ARM Cortex M4, no data/instruction cache, SRAM memory and GCC 9. One interesting point is that this time, I didn't target armv6, but armv7 (we'll see why later). Figures are still impressive, with a gain between 50% and 60% !

  • -Os compilation : 21.7 milliseconds
  • -O2 compilation : 17.2 milliseconds
  • -Os + optimizations : 8.8 milliseconds

I used the same optimization tricks than CRC32 (see article) plus this ones :

Use specific instruction if you can

ARMv7 instruction set provides thumb2 instructions which contains bitfield extraction. This is really really (yes 2 times) nice ! instruction ubfx (and variants) allows to extract a specified range of bits from a register and thus avoid to do shift + mask (2 instructions).

Be careful on instruction size

Thumb2 is really nice because you can mix 16 bits and 32 bits instructions. But, in order to save space (thus speed), you have to carefully choose your instructions. The case here is :

lsrs r5, r5, #24 C equivalent r5 = r5 >> 24

and

ubfx r5, r5, #24, #8 C equivalent r5 = (r5 & 0xff000000) >> 24

They both do have the same result but the first one is an "old" instruction and can be encoded on 16 bits while the second is new and is encoded into 32 bits.

Don't take care on unused register part

At some point, I do a 32 bits xor operation which generate random values on bits 31..15. But we don't care because we have to focus on 16 bits lower part.

Here is the optimized function. Whole C file can be found here. Optimization is effective for 16 bytes blocks (aligned).

uint16_t crc16_ccitt_opt16(
        const unsigned char*     block,
        unsigned int            blockLength,
        uint16_t          crc)
{
    /* unsigned int i; */

    /* for(i=0U; i<blockLength; i++){ */
    /*     uint16_t tmp = (crc >> 8) ^ (uint16_t) block[i]; */
    /*     crc = ((uint16_t)(crc << 8U)) ^ crc16_ccitt_table[tmp]; */
    /* } */

/*
      r0 -> s
      r1 -> len
      r2 -> crc16val
      r3 -> crc16tab
      r4 -> curval[0]
      r5 -> (crc >> 8) ^ (uint16_t) block[i]
      r6 -> crc16_ccitt_table[(crc >> 8) ^ (uint16_t) block[i])
      r7 -> curval[1]
      r8 -> curval[2]
      r9 -> curval[3]
     */
    __asm__ volatile (
        "mov r0, %1\n"
        "mov r1, %2\n"
        "mov r2, %3\n"
        "mov r3, %4\n"

        "push {r7, r8, r9}\n"

        "crc16_opt16_loop:\n"
        "ldm r0!, {r4, r7, r8, r9}\n"

        // curval[0]
        "eor r5, r4, r2, lsr #8\n"
        "uxtb r5, r5\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        "eor r5, r4, r2\n"
        "ubfx r5, r5, #8, #8\n\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        "eor r5, r4, r2, lsl #8\n"
        "ubfx r5, r5, #16, #8\n\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        "eor r5, r4, r2, lsl #16\n"
        "lsrs r5, r5, #24\n\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        // curval[1]        
        "eor r5, r7, r2, lsr #8\n"
        "uxtb r5, r5\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        "eor r5, r7, r2\n"
        "ubfx r5, r5, #8, #8\n\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        "eor r5, r7, r2, lsl #8\n"
        "ubfx r5, r5, #16, #8\n\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        "eor r5, r7, r2, lsl #16\n" 
        "lsrs r5, r5, #24\n\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        // curval[2]        
        "eor r5, r8, r2, lsr #8\n"
        "uxtb r5, r5\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        "eor r5, r8, r2\n"
        "ubfx r5, r5, #8, #8\n\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        "eor r5, r8, r2, lsl #8\n"
        "ubfx r5, r5, #16, #8\n\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        "eor r5, r8, r2, lsl #16\n"
        "lsrs r5, r5, #24\n\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        // curval[3]        
        "eor r5, r9, r2, lsr #8\n"
        "uxtb r5, r5\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        "eor r5, r9, r2\n"
        "ubfx r5, r5, #8, #8\n\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        "eor r5, r9, r2, lsl #8\n"
        "ubfx r5, r5, #16, #8\n\n"
        "ldrh r6, [r3, r5, lsl #1]\n"
        "eor r2, r6, r2, lsl #8\n"

        "eor r5, r9, r2, lsl #16\n"
        "lsrs r5, r5, #24\n\n"
        "ldrh r6, [r3, r5, lsl #1]\n"

        // Last two lines inverted
        "subs r1, r1, #16\n"
        "eor r2, r6, r2, lsl #8\n"

        "bne crc16_opt16_loop\n"

        "pop {r7, r8, r9}\n"
        "strh r2, %0\n"
        : "=m" (crc)
        : "r" (block), "r" (blockLength), "r" (crc), "r" (crc16_ccitt_table)
          // Missing r7-r9, manually save it
        : "r0", "r1", "r2", "r3", "r4", "r5", "r6"
        );

    return crc;
}

We can see that computation is not the same for all parts of the 32 bits register while it was really symmetric in CRC32.

Code has to be compiled with minimum -O1 or -Os option

For comparison, the (quite good) code generated by GCC 12 with -Os, working on a single byte :

 594:   428b            cmp     r3, r1
 596:   d100            bne.n   59a <crc16_ccitt+0x12>
 598:   bd30            pop     {r4, r5, pc}
 59a:   f813 2b01       ldrb.w  r2, [r3], #1
 59e:   0204            lsls    r4, r0, #8
 5a0:   b2a4            uxth    r4, r4
 5a2:   ea82 2210       eor.w   r2, r2, r0, lsr #8
 5a6:   f835 2012       ldrh.w  r2, [r5, r2, lsl #1]
 5aa:   ea84 0002       eor.w   r0, r4, r2
 5ae:   e7f1            b.n     594 <crc16_ccitt+0xc>

It's clearly focus on Armv6 compatibility as it use masking operation + shift at lines 59e and 5a0.

Dorothy : the way

Saturday, 21 June 2025
|
Écrit par
Grégory Soutadé

 The way (2025) 

Trois ans après Gifts from the Holy Ghost, Dorothy sort son nouvel album The way. Signe que le groupe prend de l'ampleur, on pourra y trouver une collaboration avec Slash sur le titre Tombstone Town. Le style y est plus rock que sur les précédents opus, avec plus de solos, même s'il y a énormément d'arrangements qui viennent polluer l'ensemble (il faudrait carrément supprimer le synthé). Pourtant, on sent bien que le fond est vraiment intéressant. Il faut dire que le groupe a quasiment changé puisque peu de temps après la sortie de Gifts from the Holy Ghost, les deux guitaristes et le batteur ont été remplacés, de quoi largement influencer la composition musicale. Le seul survivant reste le bassiste, présent depuis le clash avec la formation originelle (2017).

Au hasard de l'écoute, il y a un titre qui nous éclate à la figure : MUD ! Poussé le volume à fond, on ressent toute la puissance de cette chanteuse exceptionnelle, qui se permet un growl de toute beauté !

Pour profiter pleinement de cet album, il faudrait assister à un de leurs concerts afin d'avoir une version plus dépouillée. Malheureusement, malgré de nombreuses dates prévues, il n'y a rien en dehors des États-Unis.

CRC32 optimization for ARM Cortex M4

Sunday, 15 June 2025
|
Écrit par
Grégory Soutadé

At work I played with an ultra low power SoC powered by a single core ARM Cortex M4. To check some data integrity, we have to use CRC32 but there is no hardware peripheral to speed up computation and ARMv6 doesn't have special instruction for this (it starts from Armv8.1). After some researches, I was surprised not to find any optimized implementation on Internet. So, I wrote it by myself and the result is quite impressive : my version is ~50% faster ! Some figures (on ~30KB of data) :

  • -Os compilation : 19.4 milliseconds
  • -02 compilation : 15.2 milliseconds
  • -0s + optimizations : 8.2 milliseconds

Here, we have to consider that Cortex M4 doesn't have Data nor Instruction cache and memory accesses are done on a SRAM. Compilation is done in thumb mode with GCC 9.

Original version is the one from Wang Yaofu licensed under Apache2. It's quite simple and very academic. C primitives doesn't allows to optimize this algorithm so much because CRC has to be processed byte by byte, so we have to do some assembly !

I used multiple optimization tricks :

Use all registers available

The idea is to play with registers from r0 to 10 and not be limited to r0-r5 as commonly used.

Unroll loop

Avoid to break CPU pipeline by doing some checks + jump. Code is bigger and repetitive, but faster. We may write macro to reduce source code, not my choice here.

Do memory burst instead of unitary access

Especially when there is no cache, memory burst accesses are really faster. Here we load 4*32 bits at a time and keep all data into registers. Bytes outside burst window are computed using non optimized version.

Use shifts and rotates within load and eor instructions

ARM instructions allows to shift registers values within load and eor (and some other instructions) without having to do it in a separate line.

Avoid pipeline register lock

When it's possible, we can invert assembly lines to avoid working on the same registers on consecutive instructions (and thus avoid to lock them).

Update condition flags in sub instruction

Use subs variant to update EQ flag and avoid to check it for 0 in a separate instruction.

Do aligned accesses

In the calling function there is some code to inject "s" as a 32 bits aligned pointer (extra bytes processed by standard code).

Here is the optimized function. Whole C file can be found here

/**
 * Optimized version of _update_crc32 for 16 bytes blocks
 */
static void _update_crc32_opt16(const unsigned char *s, unsigned int len)
{
    /* unsigned int i; */

    /* for (i = 0;  i < len;  i++) { */
    /*     crc32val = crc32_tab[(crc32val ^ s[i]) & 0xFF] ^ ((crc32val >> 8) & 0x00FFFFFF); */
    /* } */

    /*
      r0 -> s
      r1 -> len
      r2 -> crc32val
      r3 -> crc32tab
      r4 -> curval[0]
      r5 -> (crc32val ^ s[i]) & 0xFF
      r6 -> crc32_tab[(crc32val ^ s[i]) & 0xFF]
      r7 -> curval[1]
      r8 -> curval[2]
      r9 -> curval[3]
     */
    __asm__ volatile (
        "mov r0, %1\n"
        "mov r1, %2\n"
        "mov r2, %3\n"
        "mov r3, %4\n"

        "push {r7, r8, r9}\n"

        "crc32_opt16_loop:\n"
        "ldm r0!, {r4, r7, r8, r9}\n"

        // curval[0]
        "eor r5, r2, r4\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        "eor r5, r2, r4, ror #8\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        "eor r5, r2, r4, ror #16\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        "eor r5, r2, r4, ror #24\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        // curval[1]        
        "eor r5, r2, r7\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        "eor r5, r2, r7, ror #8\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        "eor r5, r2, r7, ror #16\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        "eor r5, r2, r7, ror #24\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        // curval[2]        
        "eor r5, r2, r8\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        "eor r5, r2, r8, ror #8\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        "eor r5, r2, r8, ror #16\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        "eor r5, r2, r8, ror #24\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        // curval[3]        
        "eor r5, r2, r9\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        "eor r5, r2, r9, ror #8\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        "eor r5, r2, r9, ror #16\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"
        "eor r2, r6, r2, lsr #8\n"

        "eor r5, r2, r9, ror #24\n"
        "uxtb r5, r5\n"
        "ldr r6, [r3, r5, lsl #2]\n"

        // Last two lines inverted
        "subs r1, r1, #16\n"
        "eor r2, r6, r2, lsr #8\n"

        "bne crc32_opt16_loop\n"

        "pop {r7, r8, r9}\n"
        "str r2, %0\n"
        : "=m" (crc32val)
        : "r" (s), "r" (len), "r" (crc32val), "r" (crc32_tab)
          // Missing r7-r9, manually save it
        : "r0", "r1", "r2", "r3", "r4", "r5", "r6"
        );
}

Code has to be compiled with minimum -O1 or -Os option

For comparison, the (quite good) code generated by GCC 12 with -Os, working on a single byte :

 570:   4288            cmp     r0, r1
 572:   d100            bne.n   576 <_update_crc32+0x12>
 574:   bd30            pop     {r4, r5, pc}
 576:   6814            ldr     r4, [r2, #0]
 578:   f810 3b01       ldrb.w  r3, [r0], #1
 57c:   4063            eors    r3, r4
 57e:   b2db            uxtb    r3, r3
 580:   f855 3023       ldr.w   r3, [r5, r3, lsl #2]
 584:   ea83 2314       eor.w   r3, r3, r4, lsr #8
 588:   6013            str     r3, [r2, #0]
 58a:   e7f1            b.n     570 <_update_crc32+0xc>

Not so far from mine, but main weakness is result write at each loop, which is really time consuming.

Cime du Cheiron

Sunday, 01 June 2025
|
Écrit par
Grégory Soutadé

Aujourd'hui, direction la cime du Cheiron. Je l'avais dans un coin de ma tête depuis quelques années sans jamais oser me lancer. Surtout qu'après ma mésaventure d'Escragnolles, je voulais emprunter un chemin plus simple à suivre. Ici, la distance ne semble pas trop longue (~11km), mais le dénivelé y est plus important qu'à l'habitude avec 950m d'ascension verticale, qui va nous mener à 1778m d'altitude. La randonnée est notée comme difficile, principalement à cause du dénivelé je pense. Elle est pourtant assez populaire et l'on croise facilement tout le long du trajet des personnes, des groupes, voire même des trailers, qui font la montée et la descente en courant. Pour plus de simplicité, j'ai choisi l'option de randoxygène avec un départ depuis Gréolières, plus court, mais beaucoup moins alambiqué que les instructions visorando qui empruntent des chemins avec comme seul repère des cairns...

Village de Gréolières

Il ne fallait pas traîner pour en profiter pendant les beaux jours qui restent, car la chaleur sera très vite là (dès juin). En effet, bien qu'il ne fasse que 13°C au point de départ, la température corporelle monte rapidement, d'autant plus que les 2 premiers kilomètres sont vraiment raides. Il faudra donc bien penser à prendre de l'eau, un chapeau, des lunettes de soleil et de la crème solaire, car 80% du chemin se fait à découvert. À ce sujet, il est possible de se recharger en eau dans les fontaines du village !

Village de Gréolières

Le chemin passe à côté du second château (le hameau ayant été séparé et réunifié au fil du temps), offrant un beau panorama dès que l'on prend un peu d'altitude.

La progression verticale est plutôt rapide, alternant chemins plutôt agréables et d'autres avec plus de cailloux.

Au loin, le plateau de Calern avec l'observatoire de la Côte d'Azur.

Passé ces 2 kilomètres plutôt ardus, la pente se fait plus douce. On a même l'impression de toucher rapidement au but une fois arrivé au collet du Barri. Ce n'est en réalité que la première partie ! La suite se déroule dans un bois ombragé avec un sol parfois glissant (surtout après la pluie). Cette partie n'est pas forcément aisée, avec parfois un peu de dénivelé à passer.

Au sortir du bois, la perspective s'ouvre sur un paysage lunaire avec beaucoup de pierres. L'antenne relais au loin est notre véritable objectif ! Attention, à partir de ce point, le chemin est moins visible.

Arrivé au sommet, nous attend un panorama magnifique avec le haut des remontées mécaniques ainsi que les montagnes encore enneigées en arrière plan.

Les nuages chargés d'eau remontent sur la paroi Est, poussés par le vent.

J'ai raté le point de vue sur la mer, préférant rester non loin du point culminant pour me restaurer. Ce n'est que partie remise...

Le retour se fait en sens inverse. Il faudra être vigilant, le sol n'étant pas toujours très stable à cause des pierres.

La faune et la flore locale :