ALU

L'ALU, che sta per Arithmetic Logic Unit, ha lo scopo di eseguire operazioni aritmetiche e logiche come and, or, add, beq, etc.

Somma

La somma di due numeri a 32 bit sulla ALU è formata da 1 bit adder (una serie da 32) con la tabella di verità:

00000
00110
01010
01101
10010
10101
11001
11111

dove è il riporto precedente, è la somma e è il riporto dell'operazione.

Minimizzando la tabella si arriva ad avere che: ma dato che non è semplificabile, si possono usare le porte XOR, infatti:

SommaXOR
0000
0111
1011
1100

e quindi:

Sottrazione

Utilizzando il complemento a due e l'adder è possibile ottenere una somma, dato che:

Di conseguenza basterà fare la somma tra e , mettendo il riporto entrante , in modo da considerare anche il .

Confronto

Un'altra funzione della ALU è quella di confronto, che servirà poi per implementare l'istruzione slt:

Lo stato del confronto si può ottenere dalla ALU sul MSB (Most Significant Bit) da cui viene letto il bit di segno della sottrazione tra e , con gli appropriati resti: dove il valore di verrà usato come input per (cioè less) sulla prima ALU in modo che , visto che il numero ha solo il LSB (Least Significant Bit) attivo.

Questo però non funziona se e , perchè è una somma tra due numeri positivi che può portare ad overflow, cosa che potrebbe dare un errato per cui il risultato sarebbe sbagliato.

ALU da 1 bit

Esempio di ALU da 1 bit

dove:

  • , sono gli input della ALU
  • , , sono dei flag con cui si decide se usare la versione negativa di o di come input
  • è il riporto precedente, mentre è il riporto di output
  • indica quale operazione vogliamo dalla ALU
  • è il risultato dell'operazione
  • , sono usati internamente per la condizione
  • indica l'overflow della somma

Quindi è possibile riassumere le operazioni in:

00000 and
00001 or
00010
01110
01111
11X00 nor

e visto che non ci interessa lo stato per il NOR, è possibile porre sulla prima 1 bit ALU nella catena delle ALU.

ALU da 32 bit

Per ricavare la ALU da 32 bit basta concatenare le varie ALU da 1 bit per ogni bit della word.

Concatenazione di più ALU

dove equivale al NOR tra tutti i bit di output e sarà quando .

Questa ALU però, avrà performance abbastanza lente, perchè ogni riporto di ogni 1 bit adder dovrà propagarsi per tutta la catena di 1 bit ALU, cosa che rallenterà il segnale. Per migliorare le prestazioni quindi, esiste un metodo chiamato Carry Lookahead.

Moltiplicazione

Questa parte non sarà contenuta all'interno dell'ALU ma la sfrutterà per creare l'algoritmo che permette la moltiplicazione tra due numeri.

L'algoritmo si basa sul metodo di moltiplicazione in colonna e si può riassumere in:

long mul(int a, int b) {
	unsigned long r0 = abs(a);	// Registro a 64bit perchè r0 << 1 per 32 volte
	unsigned int r1 = abs(b);

	long sum = 0;
	for (size_t i = 0; i < sizeof(a) * 8; i++) {
		if (r1 & 0x1) {
			sum += r0;	// Necessaria una ALU da 64bit
		}
		r0 <<= 1;
		r1 >>= 1;
	}

	if ((a > 0) ^ (b > 0)) {	// Controllo dei bit di segno
		sum = -sum;
	}

	return sum;	// a * b
}

che necessita, però, di una ALU e di un registro a 64bit, motivo per cui è stato ideato un algoritmo alternativo che è anche più ottimizzato:

long mul(int a, int b) {
	unsigned int r1 = abs(b);

	long sum = 0;
	for (size_t i = 0; i < sizeof(a) * 8; i++) {
		if (r1 & 0x1) {
			unsigned int upper = sum >> (sizeof(sum)*8/2);
			unsigned int lower = sum & ~0U;

			upper += abs(a);
			sum = (long) upper << (sizeof(sum)*8/2) | lower;
		}
		r1 >>= 1;
		sum >>= 1;
	}

	if ((a > 0) ^ (b > 0)) {
		sum = -sum;
	}

	return sum;
}