Conception du processeur Verilog


Partie I
Partie II
Partie III
Partie IV
Partie V

Nous concevons Little Man Computer dans Verilog.

L'article sur LMC était sur Habré.

Le simulateur en ligne de cet ordinateur est ici .

Nous écrivons un module RAM / RAM composé de quatre (N = 2) mots de quatre bits (M = 4). Les données sont chargées dans la RAM à partir de data_in at adr lorsque vous cliquez sur le bouton:
module R0 #(parameter N = 2, M = 4) ( input RAM_button, // input [N-1:0] adr, // input [M-1:0] data_in, //   output [M-1:0] RAM_out //   ); reg [M-1:0] mem [2**N-1:0]; //  mem always @(posedge RAM_button) //    mem [adr] <= data_in; //     data_in assign RAM_out = mem[adr]; // RAM_out    endmodule 

En tant que générateur externe, connectez une minuterie CMOS 555 (fonctionnant à partir de 3,3 V).
Nous connectons la minuterie 555 au compteur, connectons le compteur à l'entrée d'adresse de la RAM :
 module R1 #(parameter N = 2, M = 4) ( input timer555, RAM_button, //input [N-1:0] adr, input [M-1:0] data_in, output [M-1:0] RAM_out ); reg [1:0]counter; //  always @(posedge timer555) //    counter <= counter + 1; //    1 wire [N-1:0] adr; assign adr = counter; //       reg [M-1:0] mem [2**N-1:0]; always @(posedge RAM_button) mem [adr] <= data_in; assign RAM_out = mem[adr]; endmodule 

Ici, lors de la description du compteur de compteur et de la mémoire mem , des affectations non bloquantes sont utilisées <= Les opérateurs d'affectation sont considérés sur le site marsohod.org ici
Une description du compteur est sur marsohod.org ici

Ajoutez la fonction de téléchargement au compteur.
Le téléchargement se fait avec la commande Counter_load :
 //input Counter_load; wire [3:0] branch_adr; //   assign branch_adr = data_in; always @(posedge timer555) begin if(Counter_load) //  "Counter_load"    "branch_adr" counter <= branch_adr; else counter <= counter + 1; end 


Dans un module séparé, créez un registre 4 bits (batterie):
 module register4 ( input [3:0] reg_data, input reg_button, output reg [3:0] q ); always @(posedge reg_button) q <= reg_data; endmodule 

Ajoutez l'accumulateur Acc , le multiplexeur MUX2 et l'additionneur de somme au circuit général.
L'additionneur ajoute au nombre dans les numéros d'accumulateur de la batterie de la mémoire.
Les entrées de signal du multiplexeur reçoivent les nombres data_in et sum .
Ensuite, le numéro du multiplexeur MUX2 est chargé dans la batterie Acc :
 module R2 #(parameter ADDR_WIDTH = 2, DATA_WIDTH = 4) ( input timer555, Counter_load, RAM_button, input MUX_switch, input Acc_button, input [3:0] data_in, output [3:0] Acc, output [DATA_WIDTH-1:0] RAM, output reg [1:0] counter ); wire [1:0] branch_adr; assign branch_adr = data_in[1:0]; //Counter always @(posedge timer555) begin if(Counter_load) counter <= branch_adr; else counter <= counter + 1; end wire [ADDR_WIDTH-1:0] adr; assign adr = counter; //RAM reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0]; always @(posedge RAM_button) mem [adr] <= Acc; assign RAM = mem[adr]; //sum wire [3:0] sum; assign sum = Acc + RAM; //MUX reg [3:0] MUX2; always @* // Always @* —  «» MUX2 = MUX_switch ? sum : data_in; //Accumulator register4 Acc_reg( .reg_data(MUX2), .reg_button(Acc_button), .q(Acc) ); endmodule 

Toujours @ * signifie "toujours". Certains synthétiseurs ne comprennent pas cette conception. Un multiplexeur peut également être écrit sans Always @ * (ici, il est utilisé uniquement à titre d'exemple).


Soustraction


Pour effectuer une soustraction, il est nécessaire de fournir un nombre soustrait dans un code supplémentaire . Vous pouvez lire sur l'addition et la soustraction de nombres binaires dans le manuel «Circuit numérique et architecture informatique» (David M. Harris et Sarah L. Harris) au chapitre 1.4.6 Le signe des nombres binaires

Ajoutez au module principal un élément qui soustrait du nombre dans la batterie les nombres stockés en mémoire:
 wire [3:0] subtract; assign subract = Acc - RAM ; 

Remplacez le multiplexeur à 2 entrées par 4 entrées:
 always @* MUX4 = MUX_switch[1] ? (MUX_switch[0] ? RAM : subtract) : (MUX_switch[0] ? sum : data_in); 

Nous connectons le périphérique de sortie à la batterie (registre 4bit'ny), nous connectons également 2 drapeaux à la batterie:

1. Le drapeau "Zero" est un journal. Élément 4 OU NON. Le drapeau est levé si le contenu de Ass est nul.

2. Le drapeau «Numéro zéro ou positif» est un journal. l'élément n'est PAS au niveau haut de la batterie 4 bits. Le drapeau est levé si le contenu de Ass est supérieur ou égal à zéro.

 // "" output Z_flag; assign Z_flag = ~(|Acc); // 4-  - // "   " output PZ_flag; assign PZ_flag = ~Acc[3]; 


4 OU NON
Ici, nous avons décrit une vanne à entrées multiples OU NON comme ~ (| Acc)
Verilog prend également en charge un ensemble de types de portes.

Les mots clés suivants sont définis pour les portes logiques: et (AND), nand (AND-NOT), ou (OR), ni (OR-NOT), xor (Exclusive OR), xnor (Exclusive OR-NOT), buf (élément Buffer) , pas (Negation, NOT).

Dans Verilog, lorsque vous utilisez des portes, vous devez spécifier les entrées et sorties de l'élément, ainsi que (éventuellement) le nom de la porte. Par exemple, les vannes et et ou doivent avoir une sortie et deux ou plusieurs entrées. Donc, pour la valve nor, nous avons
ni nom list_of_ arguments
ni mynor (out, in0, in1, in2, in3);




Ajouter trois équipes

1. chargement du contenu de la batterie dans le périphérique de sortie data_out
2. chargement de l'adresse dans le compteur si le drapeau "zéro" est levé ( JMP si Acc = 0)
3. chargement de l'adresse dans le compteur si le drapeau "zéro ou un nombre positif" est levé ( JMP si Acc > = 0)

 module R3 #(parameter ADDR_WIDTH = 2, DATA_WIDTH = 4) ( input timer555, RAM_button, input JMP, Z_JMP, PZ_JMP, input [1:0] MUX_switch, input Acc_button, input Output_button, input [3:0] data_in, output [3:0] Acc, output [3:0] data_out, output [DATA_WIDTH-1:0] RAM, output Z_flag, PZ_flag, output reg [1:0] counter ); wire [1:0] branch_adr; assign branch_adr = data_in[1:0]; wire Z,PZ; assign Z = Z_flag & Z_JMP; assign PZ = PZ_flag & PZ_JMP; //Counter always @(posedge timer555) begin if(JMP|Z|PZ) counter <= branch_adr; else counter <= counter + 1; end wire [ADDR_WIDTH-1:0] adr; assign adr = counter; //RAM reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0]; always @(posedge RAM_button) mem [adr] <= Acc; assign RAM = mem[adr]; //sum wire [3:0] sum; assign sum = Acc + RAM; //subtract wire [3:0] subtract; assign subtract = Acc - RAM; //MUX reg [3:0] MUX4; always @* MUX4 = MUX_switch[1] ? (MUX_switch[0] ? RAM : subtract) : (MUX_switch[0] ? sum : data_in); register4 Acc_reg( .reg_data(MUX4), .reg_clk(Acc_button), .q(Acc) ); register4 Output_reg( .reg_data(Acc), .reg_clk(Output_button), .q(data_out) ); assign Z_flag = ~(|Acc); assign PZ_flag = ~Acc[3]; endmodule 



Nous mettons les commandes et les adresses dans une RAM / RAM, et les données dans une autre.



Le schéma peut être téléchargé à partir d'ici .

Dans les huit premiers chiffres, les commandes sont stockées, dans les quatre derniers chiffres, l'adresse est chargée dans le compteur.

En général, le chargement d'un nombre dans la batterie Ass doit être effectué après la commutation du multiplexeur MUX (pour les commandes ADD , SUB , LDA ), en fonction de la décroissance de l'horloge.

T.O. dans notre ordinateur, le système de commande suivant

48x - AJOUTER ajouter un numéro de RAM à Ass
50x - SUB soustrait le nombre stocké dans la RAM de Ass
80x - STA enregistre le numéro de la batterie dans la RAM à l'adresse x
58x - LDA charge un numéro à partir de l'adresse x dans Ass
04x - BRA transition inconditionnelle vers la cellule avec l'adresse x
02x - BRZ transition vers la cellule avec l'adresse x, si Ass = 0 (transition conditionnelle)
01x - BRP transition vers la cellule avec l'adresse x, si Ass> = 0 (transition conditionnelle)
40x - INP charge un nombre de data_input dans Ass
20x - OUT charge le nombre de Ass vers data_out

Nous n'aurons pas d'équipe HLT .

Prenez par exemple l'algorithme de recherche du maximum de deux nombres sur le site http://peterhigginson.co.uk/LMC/

L'algorithme fonctionne comme ceci: nous stockons deux nombres de data_in dans la mémoire de données. Soustrayez le premier du deuxième nombre:

  • si le résultat est négatif, écrivez le premier nombre dans Ass, écrivez le nombre de Ass dans data_out;
  • si le résultat est positif, écrivez le deuxième nombre dans Ass, écrivez le nombre de Ass dans data_out.

00 INP
01 STA 11
02 INP
03 STA 12
04 SUB 11
05 BRP 08
06 LDA 11
07 BRA 09
08 LDA 12
09 OUT


Dans notre système de commande, cet algorithme ressemblera à ceci

400
80b
400
80c
50b
018
58b
049
58c
200



L'élément NON à l'entrée de contrôle du compteur nécessaire pour charger des données dans le compteur est une telle caractéristique du programme Logisim; dans les schémas réels, l'élément NON à l'entrée de contrôle n'est pas requis (au moins je ne connais pas de tels compteurs).

Quartus II peut être téléchargé sur le site officiel .

Lorsque vous vous inscrivez sous la fonction Mon travail principal est *, sélectionnez Étudiant.
Ensuite, vous devez télécharger le pilote pour le programmeur (le pilote pour usb-blaster peut être installé à partir de C: \ altera \ ... \ quartus \ drivers \ usb-blaster).

Logisim peut être téléchargé ici .

Source: https://habr.com/ru/post/fr412057/


All Articles