Design do processador Verilog


Parte I
Parte II
Parte III
Parte iv
Parte v

Desenhamos Little Man Computer em Verilog.

O artigo sobre o LMC foi publicado em Habré.

O simulador online deste computador está aqui .

Escrevemos um módulo RAM / RAM composto por quatro (N = 2) palavras de quatro bits (M = 4). Os dados são carregados na RAM a partir de data_in em adr quando você clica no botão:
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 um gerador externo, conecte um timer de 555 CMOS (operando a partir de 3,3V).
Conectamos o cronômetro 555 ao contador, o contador à entrada de endereço da 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 

Aqui, ao descrever o contador e a memória mem , são utilizadas atribuições sem bloqueio <= Operadores de atribuição são considerados no site marsohod.org aqui
Uma descrição do contador está em marsohod.org aqui

Adicione a função de download ao contador.
O download é feito com o 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 


Em um módulo separado, crie um registro de 4 bits (bateria):
 module register4 ( input [3:0] reg_data, input reg_button, output reg [3:0] q ); always @(posedge reg_button) q <= reg_data; endmodule 

Adicione o acumulador Acc , o multiplexador MUX2 e o somador ao circuito geral.
O somador adiciona ao número nos números de Acc da bateria da memória.
As entradas de sinal do multiplexador recebem os números data_in e soma .
Em seguida, o número do multiplexador MUX2 é carregado na bateria 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 

Sempre @ * significa "sempre". Alguns sintetizadores não entendem esse design. Um multiplexador também pode ser escrito sem Always @ * (aqui é usado apenas como exemplo).


Subtração


Para executar uma subtração, é necessário fornecer um número subtraído em um código adicional . Você pode ler sobre a adição e subtração de números binários no livro “Circuitos digitais e arquitetura de computadores” (David M. Harris e Sarah L. Harris) no capítulo 1.4.6 O sinal de números binários

Adicione ao módulo principal um elemento que subtraia do número na bateria os números armazenados na memória:
 wire [3:0] subtract; assign subract = Acc - RAM ; 

Substitua o multiplexador de 2 entradas por 4 entradas:
 always @* MUX4 = MUX_switch[1] ? (MUX_switch[0] ? RAM : subtract) : (MUX_switch[0] ? sum : data_in); 

Conectamos o dispositivo de saída à bateria (registro 4bit'ny), também conectamos 2 sinalizadores à bateria:

1. A bandeira "Zero" é um log. Elemento 4 OU NÃO. A bandeira é levantada se o conteúdo de Ass for zero.

2. A bandeira “Número Zero ou Positivo” é um log. o elemento NÃO está no nível alto da bateria de 4 bits. A bandeira é levantada se o conteúdo de Ass for maior ou igual a zero.

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


4 OU NÃO
Aqui descrevemos uma válvula de entrada múltipla OU NÃO como ~ (| Acc)
O Verilog também suporta um conjunto de tipos de portas.

As seguintes palavras-chave são definidas para portas lógicas: e (AND), nand (AND-NOT) ou (OR), nem (OR-NOT), xor (OR exclusivo), xnor (OR-NOT exclusivo), buf (elemento do buffer) , não (Negação, NÃO).

No Verilog, ao usar portões, você deve especificar as entradas e saídas do elemento, bem como (opcionalmente) o nome do portão. Por exemplo, as válvulas e e ou devem ter uma saída e duas ou mais entradas. Então, para a válvula nem, nós temos
nem nome list_of_ argumentos
nem mynor (fora, in0, in1, in2, in3);




Adicione três equipes

1. carregando o conteúdo da bateria no dispositivo de saída data_out
2. carregando o endereço no contador se a bandeira “zero” for levantada ( JMP se Acc = 0)
3. carregando o endereço no contador se a bandeira “zero ou um número positivo” for levantada ( JMP se 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 



Colocamos os comandos e endereços em uma RAM / RAM e os dados em outra.



O esquema pode ser baixado aqui .

Nos oito primeiros dígitos, os comandos são armazenados, nos últimos quatro dígitos o endereço é carregado no contador.

Em geral, o carregamento de um número na bateria Ass deve ser feito após a troca do multiplexador MUX (para comandos ADD , SUB , LDA ), de acordo com a queda do relógio.

T.O. em nosso computador, o seguinte sistema de comando

48x - ADD adiciona um número da RAM ao Ass
50x - SUB subtrai o número armazenado na RAM do Ass
80x - STA salva o número do conjunto da bateria na RAM no endereço x
58x - LDA carrega um número do endereço x no Ass
04x - transição incondicional do BRA para a célula com endereço x
02x - transição BRZ para a célula com o endereço x, se Ass = 0 (transição condicional)
01x - transição BRP para a célula com endereço x, se Ass> = 0 (transição condicional)
40x - INP carrega um número de data_input no Ass
20x - OUT carrega o número de Ass para data_out

Não teremos uma equipe HLT .

Tomemos, por exemplo, o algoritmo para encontrar o máximo de dois números no site http://peterhigginson.co.uk/LMC/

O algoritmo funciona assim: armazenamos dois números de data_in na memória de dados. Subtraia o primeiro do segundo número:

  • se o resultado for negativo, escreva o primeiro número em Ass, escreva o número de Ass em data_out;
  • se o resultado for positivo, escreva o segundo número em Ass, escreva o número de Ass em 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


Em nosso sistema de comando, esse algoritmo será parecido com este

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



O elemento NÃO na entrada de controle do contador necessário para carregar dados no contador é um recurso do programa Logisim; em esquemas reais, o elemento NÃO na entrada de controle não é necessário (pelo menos eu não conheço esses contadores).

O Quartus II pode ser baixado do site oficial.

Quando o registro em Minha função principal de trabalho for *, selecione Aluno.
Em seguida, é necessário baixar o driver do programador (o driver do usb-blaster pode ser instalado em C: \ altera \ ... \ quartus \ drivers \ usb-blaster).

O Logisim pode ser baixado aqui .

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


All Articles