Construire un CPU : gestion du registre programme

Nous poursuivons la conception de notre processeur simple. Dans cet article, nous abordons l'utilisation du regsitre R7 comme compteur de programme (PC).

L'idée fondamentale est que toute instruction modifiant la valeur de R7 effectue un saut (jump). Dans le cas contraire, le programme s'exécute de manière séquentielle et R7 doit être incrémenté à chaque cycle d'horloge.

Pour implémenter ce comportement, la section d'écriture des registres est modifiée :


always @(posedge clk) begin
    if (sel_pc) begin
        pc_reg <= alu_result;
    end else begin
        pc_reg <= pc_reg + 8'd1;
    end
    
    // Mise à jour des autres registres
    for (int i = 0; i < 7; i++) begin
        if (sel[i]) begin
            regs[i] <= alu_result;
        end
    end
end

La synthèse RTL génère alors un additionneur pour l'incrémentation et un multiplexeur contrôlé par le signal de sélection du PC. Cela permet de connecter le registre programme à une ROM contenant les instructions, permettant ainsi leur exécution séquentielle.

Pour améliorer la lisibilité du code, nous passerons du format Verilog (.v) à SystemVerilog (.sv). Cette extension offre des syntaxes plus compactes, notamment pour la déclaration des ensembles de registres :


module processeur_simple(
    input logic clk,
    input logic [15:0] instruction,
    output logic [7:0] registres [8]
);

logic [7:0] operande_a, operande_b, resultat_alu;
logic [2:0] index_destination;

assign index_destination = instruction[10:8];

always_comb begin
    operande_a = registres[instruction[10:8]];
    operande_b = instruction[11] ? registres[instruction[2:0]] : instruction[7:0];
end

always_comb begin
    casez(instruction[15:12])
        4'b0000: resultat_alu = operande_b;          // Chargement
        4'b0001: resultat_alu = operande_a + operande_b;    // Addition
        4'b0010: resultat_alu = operande_a - operande_b;    // Soustraction
        4'b0011: resultat_alu = operande_a & operande_b;    // ET logique
        4'b0100: resultat_alu = operande_a | operande_b;    // OU logique
        4'b0101: resultat_alu = (operande_a == operande_b); // Comparaison
        default: resultat_alu = '0;
    endcase
end

logic [7:0] selecteur_ecriture;
decodeur_3vers8 decodeur_inst(
    .entree(index_destination),
    .sortie(selecteur_ecriture)
);

always_ff @(posedge clk) begin
    // Traitement spécial pour le compteur de programme
    if (selecteur_ecriture[7]) begin
        registres[7] <= resultat_alu;
    end else begin
        registres[7] <= registres[7] + 1;
    end
    
    // Écriture dans les autres registres
    for (int i = 0; i < 7; i++) begin
        if (selecteur_ecriture[i]) begin
            registres[i] <= resultat_alu;
        end
    end
end

endmodule

Cette réécriture utilise des structures SystemVerilog comme logic, always_comb, always_ff et les opérateurs améliorés. L'architecture générée reste identique, mais le code source est plus concis et plus lisible.

Étiquettes: SystemVerilog RTL CPU design register file program counter

Publié le 5 juillet à 22h51