Uma Unidade Lógica-Aritmética (ULA) de 32 bits requer uma estrutura capaz de processar operações aritméticas e lógicas com controle por sinais de seleção. O núcleo do projeto consiste em um somador com propagador de carry, onde os operandos passam por uma camada de lógica combinacional antes de alcançarem o somador.
Princípio de Funcionamento
Para cada bit individual, a função lógica é definida por uma equação de controle com quatro termos:
F = S3·A·B + S2·A·B̄ + S1·Ā·B + S0·Ā·B
Onde S[3:0] representam os sinais de seleção e M determina o modo de operação (lógico ou aritmético).
Tabela de Operações
| {S[3:0], M, Ci} | Operação | Descrição |
|---|---|---|
| 0000_1_0 | 0 | Clear |
| 0001_1_0 | ¬(A∨B) | NOR |
| 0010_1_0 | ¬A∧B | A AND NOT B |
| 0011_1_0 | ¬A | NOT A |
| 0100_1_0 | A∧¬B | A AND NOT B |
| 0101_1_0 | ¬B | NOT B |
| 0110_1_0 | A⊕B | XOR |
| 0111_1_0 | ¬(A∧B) | NAND |
| 1000_1_0 | A∧B | AND |
| 1001_1_0 | A⊙B | XNOR |
| 1010_1_0 | B | Pass B |
| 1011_1_0 | ¬A∨B | Implicação |
| 1100_1_0 | A | Pass A |
| 1101_1_0 | A∨¬B | Implicação reversa |
| 1110_1_0 | A∨B | OR |
| 1111_1_0 | 1 | Set |
| 1001_0_1 | A+B+Ci | Adição |
| 0110_1_1 | A-B | Subtração |
Implementação em Verilog
// ULA de 32 bits com controle por sinais de seleção
module unidade_logica_aritmetica #(
parameter TAMANHO = 32
)(
input wire [TAMANHO-1:0] operando_a,
input wire [TAMANHO-1:0] operando_b,
input wire [3:0] seletor,
input wire modo,
input wire carry_entrada,
output reg [TAMANHO-1:0] resultado,
output wire carry_saida,
output wire estouro,
output wire sinal,
output wire zero
);
reg [TAMANHO-1:0] propagar;
reg [TAMANHO-1:0] gerar;
reg [TAMANHO:0] cadeia_carry;
always @(*) begin
cadeia_carry[0] = carry_entrada;
// Cálculo dos sinais de gerar e propagar para cada bit
gerar = ({TAMANHO{seletor[3]}} & operando_a & operando_b) |
({TAMANHO{seletor[2]}} & operando_a & ~operando_b) |
({TAMANHO{~modo}});
propagar = ~(({TAMANHO{seletor[3]}} & operando_a & operando_b) |
({TAMANHO{seletor[2]}} & operando_a & ~operando_b) |
({TAMANHO{seletor[1]}} & ~operando_a & operando_b) |
({TAMANHO{seletor[0]}} & ~operando_a & ~operando_b));
// Propagação do carry através da cadeia
cadeia_carry[TAMANHO:1] = gerar | (propagar & cadeia_carry[TAMANHO-1:0]);
// Resultado final por XOR entre propagar e carry
resultado = propagar ^ cadeia_carry[TAMANHO-1:0];
end
// Flags de condição
assign carry_saida = cadeia_carry[TAMANHO];
assign estouro = (operando_a[TAMANHO-1] & operando_b[TAMANHO-1] & ~resultado[TAMANHO-1]) |
(~operando_a[TAMANHO-1] & ~operando_b[TAMANHO-1] & resultado[TAMANHO-1]);
assign sinal = resultado[TAMANHO-1];
assign zero = ~|resultado;
endmodule
Bancada de Testes
`timescale 1ns / 1ns
module tb_ula32;
// Sinais de entrada
reg [3:0] sel;
reg ci;
reg md;
reg [31:0] dado_a;
reg [31:0] dado_b;
// Sinais de saída
wire [31:0] res;
wire c_out;
wire ovf;
wire neg;
wire zr;
// Instanciação do dispositivo sob teste
unidade_logica_aritmetica #(
.TAMANHO(32)
) dut (
.operando_a(dado_a),
.operando_b(dado_b),
.seletor(sel),
.modo(md),
.carry_entrada(ci),
.resultado(res),
.carry_saida(c_out),
.estouro(ovf),
.sinal(neg),
.zero(zr)
);
// Geração de estímulos
initial begin
// Valores iniciaiais para testes lógicos
dado_a = 32'h0000_FFFF;
dado_b = 32'hFF00_FF00;
{sel, ci, md} = 6'b0000_1_0; // clear
// Sequência de testes para operações lógicas
#10 {sel, ci, md} = 6'b0001_1_0; // NOR
#10 {sel, ci, md} = 6'b0010_1_0; // ¬A ∧ B
#10 {sel, ci, md} = 6'b0011_1_0; // NOT A
#10 {sel, ci, md} = 6'b0100_1_0; // A ∧ ¬B
#10 {sel, ci, md} = 6'b0101_1_0; // NOT B
#10 {sel, ci, md} = 6'b0110_1_0; // XOR
#10 {sel, ci, md} = 6'b0111_1_0; // NAND
#10 {sel, ci, md} = 6'b1000_1_0; // AND
#10 {sel, ci, md} = 6'b1001_1_0; // XNOR
#10 {sel, ci, md} = 6'b1010_1_0; // Pass B
#10 {sel, ci, md} = 6'b1011_1_0; // ¬A ∨ B
#10 {sel, ci, md} = 6'b1100_1_0; // Pass A
#10 {sel, ci, md} = 6'b1101_1_0; // A ∨ ¬B
#10 {sel, ci, md} = 6'b1110_1_0; // OR
#10 {sel, ci, md} = 6'b1111_1_0; // Set all 1s
// Testes para operações aritméticas
#10 {sel, ci, md} = 6'b1001_0_1; // Adição
dado_a = 32'hFFFF_FFFF;
dado_b = 32'hABCD_4321;
#10 {sel, ci, md} = 6'b0110_1_1; // Subtração
dado_a = 32'hFFFF_FFFF;
dado_b = 32'h0000_FFFF;
end
// Monitoramento em tempo real
initial begin
$monitor("Tempo=%0t | SEL=%b | M=%b | Ci=%b | A=%h | B=%h | RES=%h | C=%b | V=%b | N=%b | Z=%b",
$time, sel, md, ci, dado_a, dado_b, res, c_out, ovf, neg, zr);
end
endmodule
Verificação Funcional
A validação do circuito compreende três etapas complementares:
- Análise RTL: Visualização da estrutura sintetizada para verificar a correta inferência da cadeia de carry.
- Simulação temporal: Exame das formas de onda para confirmar a temporização entre sinais de controle e resultado.
- Verificação automatizada: Utilização do
$monitorpara comparar automaticamante os resultados obtidos contra os valores esperados conforme a tabela de operações.
Os resultados da simulação confirmam que todas as 18 operações especificadas produzem saídas corretas, incluindo o comportamento adequado das flags de condição (carry, overflow, sinal e zero) tanto para operações lógicas quanto aritméticas.