Diseño del procesador Verilog


Parte 1
Parte II
Parte III
Parte iv
Parte v

Diseñamos Little Man Computer en Verilog.

El artículo sobre LMC fue sobre Habré.

El simulador en línea de esta computadora está aquí .

Escribimos un módulo RAM / RAM que consta de cuatro (N = 2) palabras de cuatro bits (M = 4). Los datos se cargan en la RAM desde data_in en adr cuando hace clic en el botón:
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 

Como generador externo, conecte un temporizador 555 CMOS (que funcione desde 3.3V).
Conectamos el temporizador 555 al contador, conectamos el contador a la entrada de dirección de 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 

Aquí, al describir el contador del contador y la memoria de memoria, se utilizan asignaciones sin bloqueo <= Los operadores de asignación se consideran en el sitio web marsohod.org aquí
Una descripción del contador está en marsohod.org aquí

Agregue la función de descarga al contador.
La descarga se realiza con el comando 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 


En un módulo separado, cree un registro de 4 bits (batería):
 module register4 ( input [3:0] reg_data, input reg_button, output reg [3:0] q ); always @(posedge reg_button) q <= reg_data; endmodule 

Agregue el acumulador Acc , el multiplexor MUX2 y el sumador al circuito general.
El sumador agrega al número en la batería los números Acc de la memoria.
Las entradas de señal del multiplexor reciben los datos data_in y sum .
A continuación, el número del multiplexor MUX2 se carga en la batería 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 

Siempre @ * significa "siempre". Algunos sintetizadores no entienden este diseño. También se puede escribir un multiplexor sin Always @ * (aquí se usa solo como ejemplo).


Resta


Para realizar una resta, es necesario proporcionar un número restado en un código adicional . Puede leer sobre la suma y resta de números binarios en el libro de texto "Circuitos digitales y arquitectura de computadoras" (David M. Harris y Sarah L. Harris) en el capítulo 1.4.6 El signo de los números binarios

Agregue al módulo principal un elemento que resta del número en la batería los números almacenados en la memoria:
 wire [3:0] subtract; assign subract = Acc - RAM ; 

Reemplace el multiplexor de 2 entradas con 4 entradas:
 always @* MUX4 = MUX_switch[1] ? (MUX_switch[0] ? RAM : subtract) : (MUX_switch[0] ? sum : data_in); 

Conectamos el dispositivo de salida a la batería (4bit'ny registro), también conectamos 2 banderas a la batería:

1. La bandera "Cero" es un registro. Elemento 4 O NO. La bandera se levanta si el contenido de Ass es cero.

2. La bandera "Número cero o positivo" es un registro. el elemento NO está en el nivel alto de la batería de 4 bits. La bandera se levanta si el contenido de Ass es mayor o igual que cero.

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


4 o no
Aquí describimos una válvula de entrada múltiple O NO como ~ (| Acc)
Verilog también es compatible con un conjunto de tipos de puerta.

Las siguientes palabras clave se definen para puertas lógicas: y (AND), nand (AND-NOT) u (OR), ni (OR-NOT), xor (Exclusive OR), xnor (Exclusive OR-NOT), buf (elemento Buffer) , no (Negación, NO).

En Verilog, cuando use puertas, debe especificar las entradas y salidas del elemento, así como (opcionalmente) el nombre de la puerta. Por ejemplo, las válvulas y y o deben tener una salida y dos o más entradas. Entonces, para la válvula nor, tenemos
ni nombrar list_of_ argumentos
ni mynor (fuera, in0, in1, in2, in3);




Agrega tres equipos

1. cargar el contenido de la batería en el dispositivo de salida data_out
2. cargar la dirección en el contador si se levanta la bandera "cero" ( JMP si Acc = 0)
3. cargar la dirección en el contador si se levanta la bandera "cero o un número positivo" ( 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 



Ponemos los comandos y las direcciones en una RAM / RAM, y los datos en otra.



El esquema se puede descargar desde aquí .

En los primeros ocho dígitos, los comandos se almacenan, en los últimos cuatro dígitos la dirección se carga en el contador.

En general, la carga de un número en la batería Ass debe realizarse después de cambiar el multiplexor MUX (para los comandos ADD , SUB , LDA ), de acuerdo con la caída del reloj.

T.O. en nuestra computadora el siguiente sistema de comando

48x - AGREGAR agrega un número de RAM a Ass
50x - SUB resta el número almacenado en la RAM del culo
80x: STA guarda el número del culo de la batería en la RAM en la dirección x
58x - LDA carga un número de la dirección x en Ass
04x: transición incondicional de BRA a la celda con dirección x
02x: transición de BRZ a la celda con la dirección x, si Ass = 0 (transición condicional)
01x: transición de BRP a la celda con dirección x, si Ass> = 0 (transición condicional)
40x - INP carga un número de data_input en Ass
20x - OUT carga el número de Ass a data_out

No tendremos un equipo HLT .

Tomemos, por ejemplo, el algoritmo para encontrar el máximo de dos números del sitio http://peterhigginson.co.uk/LMC/

El algoritmo funciona así: almacenamos dos números de data_in en la memoria de datos. Resta el primero del segundo número:

  • si el resultado es negativo, escriba el primer número en Ass, escriba el número de Ass en data_out;
  • si el resultado es positivo, escriba el segundo número en Ass, escriba el número de Ass en 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


En nuestro sistema de comando, este algoritmo se verá así

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



El elemento que NO está en la entrada de control del contador necesario para cargar datos en el contador es una característica del programa Logisim; en esquemas reales, el elemento que NO está en la entrada de control no es necesario (al menos no conozco tales contadores).

Quartus II se puede descargar desde el sitio web oficial.

Cuando se registre en Mi función de trabajo principal es *, seleccione Estudiante.
A continuación, debe descargar el controlador para el programador (el controlador para usb-blaster se puede instalar desde C: \ altera \ ... \ quartus \ drivers \ usb-blaster).

Logisim se puede descargar aquí .

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


All Articles