-
Notifications
You must be signed in to change notification settings - Fork 0
ULA
[English]
A Unidade Aritmética e Lógica (ULA) é essencialmente a calculadora do processador. Ela é responsável por executar as diversas operações matemáticas definidas na ISA. Uma ULA pode operar de diferentes formas dependendo da arquitetura, mas no DRISC-V, podemos pensá-la como realizando uma operação matemática simples entre dois operandos:
Onde:
-
Fonte1pode ser o valor do Barramento A ou do Contador de Programa Atual -
Fonte2pode ser o valor do Barramento B ou do Barramento Imediato -
Operaçãorepresenta um conjunto de operações matemáticas como+,-,*,>>, etc., codificadas em 5 bits -
Resultadoé direcionado ao Barramento C para ser escrito de volta no Banco de Registradores -
CNZVsão 4 bits usados para calcular comparações em instruções de desvio e são enviados ao Controlador de Operações-
CNZVsignifica:- Carry (vai-um)
- Negativo
- Zero
- Overflow (estouro)
-
A figura a seguir mostra o componente ULA na simulação Logisim. Note que as entradas para os barramentos A e C são rotuladas como Fonte 1 e Fonte 2, e que há dois sinais de controle extras para selecionar o Contador de Programa e o Valor Imediato:
A tabela a seguir mostra as operações disponíveis na ULA:
| Codificação | Símbolo da Operação | Observações | Exemplo |
|---|---|---|---|
| 0 | + |
Soma com carry armazenado em cnzv[0]
|
5 + 3 = 8 |
| 1 | << |
Deslocamento lógico à esquerda, equivalente a multiplicar por 2 | 5 << 1 = 10 |
| 2 | < |
Comparação menor que (com sinal) | -2 < 3 → 1 |
| 3 | <U |
Comparação menor que (sem sinal) | 250 < 300 → 1 |
| 4 | ^ |
XOR bit a bit | 0101₂ ^ 0011₂ = 0110₂ |
| 5 | >> |
Deslocamento lógico à direita | 1000₂ >> 2 = 0010₂ |
| 6 | ` | ` | OR bit a bit |
| 7 | & |
AND bit a bit | 0101₂ & 0011₂ = 0001₂ |
| 8 | * |
Multiplicação de 32 bits (parte inferior do resultado) | 6 * 7 = 42 |
| 9 | *H |
Multiplicação com sinal, retorna os 32 bits superiores | 100000 * 100000 → bits altos |
| 10 | *HSU |
Multiplicação sinal × sem sinal, retorna os 32 bits superiores | -1 * 2^31 → bits altos |
| 11 | *HU |
Multiplicação sem sinal, retorna os 32 bits superiores | 2^31 * 2^31 → bits altos |
| 16 | - |
Subtração com carry armazenado em cnzv[0], usada em comparações |
10 - 3 = 7 |
| 21 | >>> |
Deslocamento aritmético à direita (com sinal), equivalente a dividir por 2 | -8 >>> 1 = -4 |
| default | 0 |
Caso padrão, resultado é zero | Operação inválida → 0 |
Nota: O DRISC-V, como a maioria das arquiteturas modernas, utiliza o sistema de números complemento de dois para representar números com sinal (ou seja, que podem ser negativos).
Quando uma operação é rotulada como "sem sinal", significa que o número é tratado como se o bit mais significativo não representasse o sinal.
Por exemplo, na operação
<U, testar se-1 < 0resulta em falso, pois-1(0xffffffff) é tratado como4.294.967.295, que é maior que0.
De acordo com a especificação RISC-V, parte do valor imediato é usada para codificar a operação >>> (deslocamento aritmético à direita).
Isso ocorre porque deslocar mais de 32 bits em um valor de 32 bits resulta em 0 ou 0xFFFFFFFF, dependendo do sinal. Portanto, apenas 5 bits são necessários do valor imediato para especificar o deslocamento.
O formato de instrução OP-IMM fornece apenas 3 bits para selecionar a operação, que já são usados por outras instruções. Para acomodar a instrução SRAI (Shift Right Arithmetic Immediate), bits extras do campo imediato são reaproveitados.
No entanto, o seletor de operação limitado a 3 bits impede que multiplicação e subtração sejam suportadas diretamente na forma imediata. Há uma solução alternativa para subtração: usar um imediato negativo com a instrução ADDI para efetivamente realizar uma subtração.

Podemos dividir a operação da ULA em três partes:
É aqui que os operandos são escolhidos. Há também um pequeno circuito para seleção da operação, responsável por ignorar os 2 bits mais significativos em operações imediatas que não sejam deslocamentos aritméticos, evitando que valores imediatos interfiram na operação atual.
Todas as operações recebem as mesmas entradas e são calculadas simultaneamente.
Note que o somador/subtrator e o multiplicador recebem bits de operação que permitem modificar seu comportamento. Isso possibilita a reutilização de circuitos, minimizando o número de componentes necessários.
A seleção é feita usando dois multiplexadores:
- O primeiro é de 1 bit e seleciona entre operações padrão e o deslocamento aritmético à direita (
>>>) - O segundo é de 4 bits e seleciona o resultado final entre todas as operações calculadas
O valor CNZV só é verdadeiramente válido para operações de subtração (e só é usado com elas), mas ainda pode retornar valores para outras operações, mesmo que não sejam significativos no contexto.
O código a seguir é a versão System Verilog da ULA:
`timescale 1s/1s
// Entradas e Saídas
module alu(
input use_pc,
input [31:0] a,
input [31:0] program_counter,
input use_immediate,
input [31:0] b,
input [31:0] immediate,
input [4:0] operation,
output logic [31:0] result,
output logic [3:0] cnzv
);
// Primeira parte: seleção das fontes e máscara de bits para operações imediatas
wire [4:0]op = (use_immediate & ~(operation == 1 | operation == 5 | operation == 21)) ?
{2'b0, operation[2:0]} :
operation;
wire [31:0] source1 = use_pc ? program_counter : a;
wire [31:0] source2 = use_immediate ? immediate : b;
always@ * begin
// Segunda e terceira partes (o case gera os multiplexadores)
case(op)
0: {cnzv[0], result} = source1 + source2;
1: result = source1 << source2[4:0];
2: result = ($signed(source1) < $signed(source2)) ? 1 : 0;
3: result = ($unsigned(source1) < $unsigned(source2)) ? 1 : 0;
4: result = source1 ^ source2;
5: result = source1 >> source2[4:0];
6: result = source1 | source2;
7: result = source1 & source2;
8: result = source1 * source2;
9: begin
logic [63:0] r64;
r64 = $signed(source1) * $signed(source2);
result = r64[63:32];
end
10: begin
logic [63:0] r64;
r64 = $signed({source1[31], source1}) * $signed({1'b0, source2});
result = r64[63:32];
end
11: begin
logic [63:0] r64;
r64 = source1 * source2;
result = r64[63:32];
end
16: {cnzv[0], result} = source1 - source2;
21: result = $signed(source1) >>> source2[4:0];
default: result = 32'b0;
endcase
// Cálculo final do CNZV
cnzv[3:1] = {
(operation == 16 ^ (source1[31] == source2[31]) && (result[31] != source1[31])), // Overflow
result == 0, // Zero
result[31] // Negativo
};
end
endmoduleA ULA é o componente principal utilizado tanto para instruções OP quanto OP-IMM. Mas além disso, no DRISC-V, ela também é utilizada para instruções de desvio e para a instrução AUIPC.
Nas instruções de desvio, a operação selecionada é sempre a subtração. Isso ocorre porque a subtração fornece todas as informações necessárias por meio dos sinais CNZV para avaliar comparações de forma eficaz.
Além das operações de desvio, a instrução AUIPC captura o valor do contador de programa e adiciona um valor imediato a ele.
Em versões mais antigas do DRISC, o somador da ULA era usado para calcular endereços de salto para o contador de programa. No entanto, para suportar o pipeline durante instruções de desvio, um circuito somador adicional foi introduzido dentro do próprio Contador de Programa.
-
- 1.1 Introduction
- 1.2 RISC-V Implementation
- 1.2.1 Available Instruction Set
- 1.2.2 Available Non-ISA Features
-
- 2.1 ALU
- 2.2 Register File
- 2.3 Program Counter
- 2.4 Input Buffer
- 2.5 RAM
- 2.6 Operation Controller
- 2.7 CSR Controller
-
- 3.1 Input Devices
- 3.1.1 Keyboard
- 3.1.2 Switches and Joystick
- 3.1.3 Random Number Generator
- 3.1.4 Real-Time Device
- 3.2 Output Devices
- 3.2.1 Screen
- 3.2.2 Terminal
- 3.2.3 Software Interrupt Register
- 3.1 Input Devices