Bugs de luminosité et de volume de speaker sur un ordi dual-boot

Note à moi-mème :

Windows considère toujours qu’il est le seul OS sur une machine. L’une des fâcheuses conséquences, c’est de penser qu’il y a un problème avec les speakers de son ordi pendant des mois, alors que tout fonctionne bien. Si on mute les speakers sur Windows et qu’on boote en linux ensuite, le setting reste dans le bios et linux ne peut plus le changer. Il faut donc toujours laisser les speakers avec du volume au moment de fermer Windows. Merci la vie.

Mesure des performances de vitesse de PySerial

J’ai recommencé à jouer avec la communication USB du TinyFPGA, à l’aide de ce programme : https://github.com/davidthings/tinyfpga_bx_usbserial

Je n’ai pas tout saisi le fonctionnement du pipeline, mais ce que j’ai pu observer, c’est qu’en maintenant le signal « uart_valid » de la transmission à 1, le caractère placé dans uart_data est transmis à l’infini, le plus rapidement possible. (J’envoie ici 97, ou « a » en ASCII).

Pour n’envoyer qu’une seule fois le caractère, je compte 2 transitions d’horloge où je maintiens uart_valid à 1. Voici la section du code Verilog (code complet à la fin de l’article) :

    reg[26:0] count1hz;

    always @(posedge clk_48mhz) begin
        if (count1hz == 0)
            uart_in_valid <= 1'b1;
        if (count1hz >= 10000) //valid doit être à 1 pendant n+1 cycles complets de clock pour transférer n byte
            uart_in_valid <= 1'b0;
    end

    always @(posedge clk_48mhz) begin
        if (count1hz >= 47999999) //tick 1Hz
            count1hz <= 0;
        else
            count1hz <= count1hz + 1;
    end

    assign uart_in_data = 97;

Et voici le programme python . J’utilise python 3.11.3 sur Fedora 38 et pyserial 3.5

import serial

ser = serial.Serial("/dev/ttyACM0")

idx = 0
while(True):
    msg_lenght = ser.in_waiting
    if msg_lenght > 0:
        idx = idx + 1
        print(idx)
        ser.read(msg_lenght)

Première constatation, le buffer est optimisé pour 64 caractères. Si je maintiens uart_valid pendant plus de 64 transitions d’horloge, seulement 64 caractères se rendent jusqu’à python. Pour arriver à 128 (donc 2 paquets USB), il faut attendre entre 5000 et 10000 transitions. En approximant vers le haut, cela signifie une fréquence de ~5kHz (L’horloge va à 48Mhz). Donc le taux de répétition des paquets est de 10kHz, ce qui fait 640ko/s, ou 6,4 Mbaud.

Honnêtement, je m’attendais à pire. Je lui ai quand même donné une chance en lisant d’un seul coup les 64 octets de son buffer, à l’aide de la commande in_waiting, au lieu d’une boucle for et d’un read sur chaque octet individuel (à proscrire).

Ma mesure concorde avec d’autres sur internet (voir par exemple https://stackoverflow.com/a/56240817, où cela donne 790ko/s).

Comparé au potentiel théorique de l’USB 2.0 de 48Mo/s, on est loin du compte. Toutefois, pour des applications où le débit d’information est bas, pyserial (et python en général) est très simple à utiliser.

Je suis à la recherche d’alternatives pour aller plus vite. Il y a la librairie pyserialtranfer (https://github.com/PowerBroker2/pySerialTransfer), mais je ne suis même pas sûr qu’il y aura un gain de vitesse. Sinon, essayer de voir ce qu’il se fait avec c++. Autre truc intéressant à comprendre, comment être sûr que le protocole à 480Mbit/s est utilisé. Parce que USB1 a la spécification d’aller à 12Mbit/s, ce qui est beaucoup plus dans l’ordre de grandeur que ce que je mesure (6Mbit/s). Donc peut-être que c’est aussi un problème de kernel et d’identification de la part du tinyFPGA. Peut-être (probablement) que l’implémentation de davidthings se base sur le standard USB1. Je ne connais pas les différences fondamentales au niveau du hardware.

EDIT : j’ai vérifié dans le kernel (avec la commande lsusb, puis lsusb -t) et le tinyFPGA est listé comme un device avec 12M de vitesse (USB1) :

Port 3: Dev 102, If 1, Class=CDC Data, Driver=cdc_acm, 12M

Ceci explique cela. La quête vers l’USB2 sera peut-être plus ardue que je le pensais. Cela change un peu ma conclusion : pyserial est une librairie parfaitement adaptée pour l’utilisation avec des devices suivant le protocole USB1.

Le code verilog complet :

/*
Voir le code pour le usb serial ici
https://github.com/davidthings/tinyfpga_bx_usbserial
*/

module uart_top (
        input  pin_clk,

        inout  pin_usb_p,
        inout  pin_usb_n,
        output pin_pu,

        output pin_led,
    );

    wire clk_48mhz;
    wire clk_locked;

    // Use an icepll generated pll
    pll pll48( .clock_in(pin_clk), .clock_out(clk_48mhz), .locked( clk_locked ) );

    // // LED
    // reg [22:0] ledCounter;
    // always @(posedge clk_48mhz) begin
    //     ledCounter <= ledCounter + 1;
    // end
    // assign pin_led = ledCounter[ 22 ];

    // Generate reset signal
    reg [5:0] reset_cnt = 0;
    wire reset = ~reset_cnt[5];
    always @(posedge clk_48mhz)
        if ( clk_locked )
            reset_cnt <= reset_cnt + reset;

    // uart pipeline in
    reg [7:0] uart_in_data;
    reg       uart_in_valid;
    reg       uart_in_ready;
    
    wire [7:0] uart_out_data;
    wire       uart_out_valid;
    wire       uart_out_ready;
    

    // usb uart - this instanciates the entire USB device.
    usb_uart uart (
        .clk_48mhz  (clk_48mhz),
        .reset      (reset),

        // pins
        .pin_usb_p( pin_usb_p ),
        .pin_usb_n( pin_usb_n ),

        // uart pipeline in
        .uart_in_data(uart_in_data),
        .uart_in_valid(uart_in_valid),
        .uart_in_ready(uart_in_ready),

        //.uart_out_data(uart_in_data),
        //.uart_out_valid(uart_in_valid),
        //.uart_out_ready(uart_in_ready)
    );

    // USB Host Detect Pull Up
    assign pin_pu = 1'b1;

    reg[26:0] count1khz;

    always @(posedge clk_48mhz) begin
        if (count1khz == 0)
            uart_in_valid <= 1'b1;
        if (count1khz >= 10000) //valid doit être à 1 pendant n+1 cycles complets de clock pour transférer n byte
            uart_in_valid <= 1'b0;
    end

    always @(posedge clk_48mhz) begin
        if (count1khz >= 47999999) //tick 1Hz
            count1khz <= 0;
        else
            count1khz <= count1khz + 1;
    end

    assign uart_in_data = 97;


endmodule