Verilog-Prozessordesign


Teil I.
Teil II
Teil III
Teil iv
Teil v

Wir entwerfen Little Man Computer in Verilog.

Der Artikel ĂŒber LMC war auf HabrĂ©.

Online-Simulator dieses Computers ist hier .

Wir schreiben ein RAM / RAM-Modul, das aus vier (N = 2) Vier-Bit-Wörtern (M = 4) besteht. Daten werden von data_in bei adr in den RAM geladen, wenn Sie auf die SchaltflÀche klicken:
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 

Schließen Sie als externen Generator einen 555 CMOS- Timer an (Betrieb mit 3,3 V).
Wir verbinden den 555-Timer mit dem ZĂ€hler, verbinden den ZĂ€hler mit dem Adresseneingang des 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 

Hier werden bei der Beschreibung des ZĂ€hlerzĂ€hlers und des Mem- Speichers nicht blockierende Zuweisungen verwendet. <= Zuweisungsoperatoren werden auf der Website marsohod.org hier berĂŒcksichtigt
Eine Beschreibung des ZĂ€hlers finden Sie hier auf marsohod.org

FĂŒgen Sie die Download-Funktion zum ZĂ€hler hinzu.
Das Herunterladen erfolgt mit dem Befehl 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 


Erstellen Sie in einem separaten Modul ein 4-Bit-Register (Batterie):
 module register4 ( input [3:0] reg_data, input reg_button, output reg [3:0] q ); always @(posedge reg_button) q <= reg_data; endmodule 

FĂŒgen Sie den Akkumulator, den MUX2- Multiplexer und den Summenaddierer zur allgemeinen Schaltung hinzu.
Der Addierer addiert die Nummer in den Batterie- Acc- Nummern aus dem Speicher.
Die SignaleingÀnge des Multiplexers erhalten die Zahlen data_in und sum .
Als nÀchstes wird die Nummer vom MUX2- Multiplexer in die Acc- Batterie geladen:
 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 

Immer @ * bedeutet "immer". Einige Synthesizer verstehen dieses Design nicht. Ein Multiplexer kann auch ohne Always @ * geschrieben werden (hier wird er nur als Beispiel verwendet).


Subtraktion


Um eine Subtraktion durchzufĂŒhren, muss eine subtrahierte Zahl in einem zusĂ€tzlichen Code angegeben werden . Informationen zum Addieren und Subtrahieren von BinĂ€rzahlen finden Sie im Lehrbuch „Digitale Schaltkreise und Computerarchitektur“ (David M. Harris und Sarah L. Harris) in Kapitel 1.4.6 Das Zeichen der BinĂ€rzahlen

FĂŒgen Sie dem Hauptmodul ein Element hinzu, das die im Speicher gespeicherten Zahlen von der Zahl in der Batterie abzieht:
 wire [3:0] subtract; assign subract = Acc - RAM ; 

Ersetzen Sie den Multiplexer mit 2 EingÀngen durch einen Multiplexer mit 4 EingÀngen:
 always @* MUX4 = MUX_switch[1] ? (MUX_switch[0] ? RAM : subtract) : (MUX_switch[0] ? sum : data_in); 

Wir verbinden das AusgabegerÀt mit der Batterie (4bit'ny Register), wir verbinden auch 2 Flags mit der Batterie:

1. Das Flag "Null" ist ein Protokoll. Element 4 ODER NICHT. Das Flag wird gehisst, wenn der Inhalt von Ass Null ist.

2. Das Flag „Null oder positive Zahl“ ist ein Protokoll. Das Element befindet sich NICHT auf dem hohen Niveau der 4-Bit-Batterie. Das Flag wird gesetzt, wenn der Inhalt von Ass grĂ¶ĂŸer oder gleich Null ist.

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


4 ODER NICHT
Hier haben wir ein Ventil mit mehreren EingÀngen ODER NICHT als ~ (| Acc) beschrieben.
Verilog unterstĂŒtzt auch eine Reihe von Gate-Typen.

Die folgenden SchlĂŒsselwörter sind fĂŒr Logikgatter definiert: und (AND), nand (AND-NOT) oder (OR) oder (OR-NOT), xor (exklusives OR), xnor (exklusives OR-NOT), buf (Pufferelement) , nicht (Negation, NICHT).

In Verilog mĂŒssen Sie bei Verwendung von Gates die Ein- und AusgĂ€nge des Elements sowie (optional) den Namen des Gates angeben. Beispielsweise mĂŒssen die Ventile und und / oder einen Ausgang und zwei oder mehr EingĂ€nge haben. Also, fĂŒr das Nor-Ventil haben wir
noch Name Liste_der_ Argumente
noch mynor (out, in0, in1, in2, in3);




FĂŒgen Sie drei Teams hinzu

1. Laden des Batterieinhalts in das AusgabegerÀt data_out
2. Laden der Adresse in den ZĂ€hler, wenn das Flag „Null“ gesetzt ist ( JMP, wenn Acc = 0)
3. Laden der Adresse in den ZĂ€hler, wenn das Flag „Null oder eine positive Zahl“ gesetzt ist ( JMP, wenn 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 



Wir legen die Befehle und Adressen in einen RAM / RAM und die Daten in einen anderen.



Das Schema kann hier heruntergeladen werden .

In den ersten acht Ziffern werden Befehle gespeichert, in den letzten vier Ziffern wird die Adresse in den ZĂ€hler geladen.

Im Allgemeinen sollte das Laden einer Nummer in die Ass- Batterie nach dem Umschalten des MUX-Multiplexers (fĂŒr ADD- , SUB- , LDA- Befehle) entsprechend dem Taktabfall erfolgen.

T.O. in unserem Computer das folgende Befehlssystem

48x - ADD fĂŒge eine Nummer aus dem RAM zu Ass hinzu
50x - SUB subtrahiert die im RAM gespeicherte Zahl von Ass
80x - STA speichert die Nummer von der Batterie im RAM unter der Adresse x
58x - LDA lÀdt eine Nummer von Adresse x in Ass
04x - BRA bedingungsloser Übergang zur Zelle mit der Adresse x
02x - BRZ-Übergang zur Zelle mit der Adresse x, wenn Ass = 0 (bedingter Übergang)
01x - BRP-Übergang zur Zelle mit der Adresse x, wenn Ass> = 0 (bedingter Übergang)
40x - INP lÀdt eine Nummer aus data_input in Ass
20x - OUT lÀdt die Nummer von Ass nach data_out

Wir werden kein HLT-Team haben .

Nehmen Sie zum Beispiel den Algorithmus zum Ermitteln von maximal zwei Zahlen auf der Website http://peterhigginson.co.uk/LMC/.

Der Algorithmus funktioniert folgendermaßen: Wir speichern zwei Zahlen aus data_in im Datenspeicher. Subtrahieren Sie die erste von der zweiten Zahl:

  • Wenn das Ergebnis negativ ist, schreiben Sie die erste Zahl in Ass, schreiben Sie die Zahl von Ass in data_out;
  • Wenn das Ergebnis positiv ist, schreiben Sie die zweite Zahl in Ass, schreiben Sie die Zahl von Ass in 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


In unserem Befehlssystem sieht dieser Algorithmus folgendermaßen aus

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



Das Element NICHT am Steuereingang des ZÀhlers, der zum Laden von Daten in den ZÀhler benötigt wird, ist ein solches Merkmal des Logisim-Programms. In realen Schemata wird das Element NICHT am Steuereingang benötigt (zumindest kenne ich solche ZÀhler nicht).

Quartus II kann von der offiziellen Website heruntergeladen werden .

Wenn Sie sich unter Meine primÀre Jobfunktion * registrieren, wÀhlen Sie Student.
Als nĂ€chstes mĂŒssen Sie den Treiber fĂŒr den Programmierer herunterladen (der Treiber fĂŒr USB-Blaster kann unter C: \ altera \ ... \ quartus \ drivers \ USB-Blaster installiert werden).

Logisim kann hier heruntergeladen werden .

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


All Articles