Abordagens para projeto low power (2/3)

17zhswf3uasrj.jpg

(Um PDA da Nokia com acesso à internet, 1995. Fonte: gizmodo.com)

5 Abordagens low power no nível arquitetural

Penso que desenhar a arquitetura de um projeto é, para todos os efeitos, definir como cada módulo do sistema se comunica com os outros módulos para trabalharem em conjunto, e através de análises, seja pela experiência do projetista ou por resultados de modelos, elaborar a melhor forma de construí-los. Abaixo seguem algumas abordagens utilizadas na indústria para elaborar arquiteturas eficientes em consumo.

5.1 Redução dinâmica da tensão de alimentação e frequência (DVFS)

Apesar dos avanços, o problema entre o poder computacional disponível e o tempo de bateria ainda são os principais desafios da indústria. O seu smartphone aguenta 1 único de dia de uso pesado com navegação, músicas, redes sociais e sinal intermitente? O meu não.

Mas como disse, uso pesado. Quer dizer, o seu smartphone sabe quando mais e quando menos energia serão necessárias para aplicação, reduzindo ou aumentado a tensão e/ou a frequência fornecidas dinamicamente para os periféricos do microprocessador.

Os próprios microprocessadores em geral são especificados de forma que a frequência de operação máxima depende da tensão fornecida. Assim quando a performance não estiver crítica a tensão (e a frequência) podem ser reduzidas. Infelizmente isto não vale para sistemas de tempo real cujo principal requisito é atender aos deadline. Existem soluções que incorporam DVFS entre o escalonador e o disparador do RTOS.

5.2 Múltiplas tensões

Projetar um chip utilizando uma única tensão de alimentação é comum e também tem a penalidade de que os piores caminhos de tempo a serem cumpridos terão a mesma força de drive dos demais. Ou seja, haverá slack times bastante altos para poder compensar os caminhos críticos. Claro que um bom projeto e uma boa ferramenta tentam encontrar um compromisso.

O ponto chave é que se todos os caminhos forem críticos, o consumo global vai ser o menor possível. A forma mais simples é: operar todos os caminhos que não são críticos em tensão mais baixa, e os críticos em tensão mais alta. A penalidade se dará principalmente em área pela adição de layers para comunicar blocos em diferentes tensões.

Formas mais eficientes adicionam inteligência nas ferramentas de projeto para escolher as tensões de cada caminhos. Falando de forma muito simplificada, um algoritmo calcula o slack de uma célula, e decide se ela pode ou não ser substituída por uma célula low voltage. Caso os demais caminhos continuem positivos esta célula é então substituída. Se não, será preciso sacrificar o consumo em detrimento da performance naquela célula.

Ainda, alguns autores sugerem que o caminho crítico de um circuito não é o caminho mais longo, mas sim o caminho mais longo e mais demandado na operação – uma variável a mais. Esta técnica demonstrou alguns ganhos em relação a anterior, e é chamada de PVCS (path-oriented clustered voltage scaling).

É preciso dizer que os layers adicionados para comunicar módulos em diferentes tensões também consumirão energia tanto estática quanto dinâmica. Existem pesquisas com as mais diversas soluções para a utilização de múltiplas tensões, inclusive com técnicas livres de conversores de nível de tensão.

5.3 Clock-gating

Em primeiro lugar é importante dizer que a árvore de clock consome aproximadamente 50% da energia em um circuito integrado digital. Depois, cada vez que um gate é chaveado, energia dinâmica será consumida. Se o dado a ser disponibilizado após o chaveamento é o mesmo que lá está, por que então consumir esta energia? Condições para habilitar ou não o clock são as técnicas chamadas de clock gating.

Está apresentada aqui em nível arquitetural pois, assim como as outras técnicas neste nível, é implementada no projeto através das diretivas de síntese que o projetista lança e das células disponíveis no PDK (Process Design Kit). Entretanto o código em que o hardware é descrito também tem forte influência no que a ferramenta vai conseguir fazer, pois é a partir desta descrição que as melhores ‘condições’ para habilitar ou não o clock serão inferidas.

A forma mais comum de clock-gating, simplesmente compara se a entrada D de um banco de registradores é igual ou não à saída Q.

clockcombinacional

Figura 1: Clock gating combinacional (figura retirada de Mohit Arora)

Na figura acima, uma condição de enable permite ou não que aquele registrador seja ‘clockado’, e o dado segue adiante na pipeline. (perceba que o segundo registrador não tem uma lógica de clock gating representada, e muitos menos relacionada com a primeira)Estima-se que 5-10% de energia dinâmica é salva com essa técnica se implementada combinacionalmente.

Pensando num pipeline onde lógicas são encadeadas entre um banco de registro e outros podemos também reduzir o chaveamento redundante na porção do circuito que está conectada à saída banco de registradores que estão sob clock gating, se toda a cadeia subsequente da pipeline for chaveada levando em conta as condições de enable da anterior. Na literatura costumam chamar esta técnica de “clock-gating avançado”.

clocksequencial

Figura 2: Clock gating sequencial (de Morit Ahora)

5.4 Power gating

Desabilitar completamente um módulo quando ele não está em uso, é consideravelmente importante nas atuais tecnologias onde a componente de consumo estático é dominante. Os módulos são habilitados ou desabilitados conforme a necessidade da aplicação. As chaves utilizadas para habilitar ou desabilitar passagem de corrente para o módulo são comumente chamadas de sleep transistors. As chaves conectadas entre Vdd e o módulo são os chamados ‘headers’ e entre o módulo de Vss são os ‘footers’. A inserção destes sleep transistors insere agora dois grandes domínios de tensão no sistema: um permanente, conectado à fonte de alimentação, e um virtual que é o visto pelo módulo de fato. O maior desafio é projetar uma chave que permita que o domínio de tensão real e virtual sejam muito similares em todas as suas características.

header-footer-swithces

Figura 3: Diagrama de blocos de dois circuitos utilizando sleep transitors, (a) header e (b) footer. (de Mohit Arora)

Não é difícil imaginar que o tamanho do sleep transistor seja bastante considerável (no dedão: ~3x a capacitância a ser driveada). Devido ao seu tamanho, ligar ou desligar um módulo através do seu chaveamento leva um tempo. Assim, não parece uma boa política adicionar estas chaves a módulos que ficarão pouco tempo em idle na operação típica do sistema. Assim, ou fazemos um sistema de power gating de baixa granularidade ou alta granularidade.

No modo fine-grain (alta granularidade) cada módulo tem um sleep transistors que são construídos como parte do PDK e adicionados durante a síntese, o que traz imensas vantagens na facilidade de projeto. A economia de corrente de leakage pode chegar a 10X.

No sistema de baixa granularidade, menos sleep-transitors são disponibilizados na forma de ‘grid’ que ligam ou desligam os domínios de módulos conforme a aplicação. Isto implica em menor overhead de área (e por consequência, menor variação de processo). Nesta abordagem os sleep-transistors são de fato parte das linhas de alimentação do circuito, não células adicionadas na síntese lógica, e portanto mais próximos do projetista de back-end.

Por outro lado, nesta abordagem de grid, com menos sleep-transistors chaveando mais circuitos, teremos mais domínios de power, com maiores variações de IR entre eles, maiores correntes de pico de power-up, que podem, entre outros problemas, ocasionar transições indesejadas em módulos vizinhos, o que vai exigir contra-medidas no back-end. Além de é claro, fritar um circuito cujas linhas foram subdimensionadas.

coarsepower

Figura 4: Representação de um esquema de power-gating de baixa granularidade com sleep transistor do tipo footer (de Mohit Arora)

Na prática não existe uma linha bem definida entre fine-grain e power-grain, e um misto de ambos é utilizado em um projeto real.

5.5 Sub-threshold/near-threshold design

A diminuição da tensão de alimentação (mantendo uma tensão de threshold fixa) resulta na diminuição quadrática do consumo dinâmico, às custas de performance, o que a depender da área de aplicação (sensores biomédicos, para dar um único exemplo) não é um problema.

Indo um pouco além, podemos pensar em utilizar o que seria a corrente de leakage desperdiçada para de fato implementar a lógica do sistema, o que é atingido quando se reduz a tensão de alimentação para um valor menor ou muito próximo de Vth. A corrente de sub-threshold é exponencialmente dependente da tensão no gate. A literatura demonstra algumas reduções de até 20X quando comparados com circuitos operando com (super)-Vth.

O grande problema? A pequeníssima diferença na corrente de um transistor ligado e um desligado, faz com que as variações de processo sejam muito impactantes no circuito construído. As contra medidas partem da arquitetura do sistema e chegam ao nível de transistor.

* * *

A próxima e última publicação vai falar das técnicas em nivel RTL.

Se não concorda, não entendeu, achou muito bom ou muito ruim, comentários são muito bem vindos.

O texto desta publicação é original. As seguintes fontes foram consultadas: The Art of Hardware Architecture, Mohit Ahora Ultra-Low Power Integrated Circuit Design, Niaxiong Nick Tan et al.

Abordagens para projeto low-power (1/3)

iphone-battery

1 Eficiência

Na última publicação sugeri que a Compaq teria percebido a portabilidade como requisito mais que desejável para a computação pessoal. Apesar de os primeiros computadores da companhia terem ~12 kg (!) e serem considerados “portáteis” simplesmente por serem apresentados em uma maleta com alças, ainda precisavam de uma tomada de corrente para funcionar.

Um pouco mais tarde, com a popularização das tecnologias multimídias e dos PDAs (personal digital assistants, ou “palmtops”), a computação portátil começa a precisar de cada vez mais performance, e aí a otimização de consumo passou a ser critério determinante em um projeto. O objetivo é ser eficiente: realizando tanto ou mais trabalho com menos energia, as baterias duram mais. Não sei dizer exatamente quando e nem quem começou as pesquisas no assunto, mas um dos trabalhos acadêmicos mais relevantes na área é datado de 1992 (Low-power CMOS Digital Design, CHANDRAKASAN et. al).

Nesta publicação quero falar um pouco sobre técnicas de baixo consumo e a oportunidade de aplicá-las nas diferentes fases e camadas de abstração de um projeto. Aqui estou assumindo que os circuitos são essencialmente digitais. O projeto low-power para circuitos (integrados) analógicos tem técnicas distintas que podem ser assunto para outra postagem.

2 Consumo estático e dinâmico

Dois componentes gerais de consumo são o consumo estático e o consumo dinâmico. Este refere-se ao consumo ocasionado pelas transições de uma porta lógica e é proporcional à corrente circulante, à frequência de transições e às capacitâncias que são carregadas e descarregadas nessas transições. O consumo estático se refere às correntes que circulam entre Vdd e Gnd mesmo quando os módulos de um dispositivo estão desligados. Com os comprimentos de canais dos transistores cada vez menores, as correntes de leakage passaram a ser uma significativa parcela do consumo total, quando não a componente dominante. Quanto menor o canal de um transistor, menor será a sua tensão de threshold e menor será a diferença entre a corrente de operação e a corrente de leakage. Na verdade, a corrente de leakage aumenta exponencialmente com a diminuição da tensão de threshold.

De maneira geral, para um gate lógico:

Potência total = Potência dinâmica + Potência estática [W]

Potência dinamica = Vdd^2 * Freq * Cl* p [W]

onde p é a probabilidade de uma transição lógica ocorrer.

3 Níveis de abstração de um projeto digital

Fazendo uma analogia com carros, uma Lamborghini Diablo 1991 é um sistema igual a um Renault Clio 2010. Ambos são automóveis, do tipo “carro”, compostos por carroceria, 4 rodas, motor, diferencial, volante, transmissão e etc. Porém arquiteturalmente são projetos radicalmente distintos.

Um sistema é concebido para prover uma solução. No caso dos automóveis o problema a ser resolvido é como deslocar algo de um lugar a outro. Se eu preciso levar uma pessoa de um ponto ao outro, eu posso escolher construir um carro, uma moto ou um patinete (que não é um automóvel!). Enfim, as escolhas em nível de sistema, são aquelas que irão definir o que é o meu projeto e quais são suas características. Estas escolhas terão como ponto de partida os requisitos do produto. Se o propósito é deslocar até 5 pessoas de um ponto ao outro e com economia de energia, o Clio faz muito mais sentido que a Lamborghini, se o requisito for um carro.

De maneira geral, podemos representar a estrutura de um projeto, nos seguintes níveis de abstração:

Nível de Sistema: refere-se a definição do conjunto de módulos de um projeto e suas conexões (microprocessador+RAM+NVM+I/O, etc.)

Nivel Arquitetural: refere-se a forma como são construído os módulos definidos no sistema e como eles interagem: a definição de interfaces, dos protocolos de controle, comunicação, etc. (ex.: microprocessador RISC-V 32-bit, 8KB de RAM, 64KB de memória NAND Flash, transreceptor compatível com NFC, camada MAC comunica-se com a PHY através de uma interface AMBA-PB, etc.)

Nível de Registradores (Register Transfer Level): representação do circuito digital como um conjunto de registros, ULAs, Muxes, contadores, etc. Pode ser chamado de “microarquitetura”.

Nível lógico: mapeamento do RTL como um conjunto de portas lógicas, latches e flip-flops.

Nível de Circuito: a representação elétrica do sistema, através de um esquemático de transistores e outros componentes elétricos.

4 Abordagens low-power no nível de sistema

Quando pensamos em sistemas digitais o consumo geralmente estará relacionado à área e performance. Uma maior performance exige uma operação em alta freqüência e com suficiente força de drive. A área relaciona-secom o tamanho dos dispositivos que por sua vez dita o tamanho das capacitâncias a serem carregadas/descarregadas.

4.1 Faça um SoC

O advento dos SoCs, sistemas inteiramente construídos em um único circuito integrado, possibilitou drástico aumento na eficiência energética. Se falamos de um sistema que está construído na forma de chips e componentes discretos conectados em uma placa, a transformação em SoC pode ser vista como uma técnica para redução de consumo. E das mais eficientes.

4.2 Capriche no co-projeto de hardware+software

O que implementar em hardware e o que implementar em software? Tipicamente as implementações em hardware consumirão menos energia que as suas contrapartes em software.

Podemos simplesmente identificar aquelas rotinas que consomem mais processamento e escolher implementá-las em hardware.

Entretanto em um fluxo de projeto que realmente aproveite a oportunidade de se poder implementar qualquer parte de um sistema em hardware ou software, uma abordagem baseada em modelos deve ser considerada. Existem linguagens como o SystemC ou SystemVerilog que permite a confecção de modelos em alto nível de abstração. Outras ferramentas como SystemVue, Matlab/Simulink são dedicadas a modelar sistemas. Depois de o sistema modelado, as simulações são utilizadas para fazer estimativas da performance e consumo das funcionalidades, e pode-se fazer escolhas direcionadas sobre o que será implementado em hardware ou software. As funcionalidades que consomem mais recursos beneficiarão todo o sistema se forem construídas com arquitetura visando baixo consumo. Ou seja, as análises no nível do sistema guiam nossas escolhas arquiteturais mais adiante.

A validação da arquitetura pode ser feita através de uma abordagem TLM (Transaction-Level Modeling).

lowpower_hwswcodesignflow

4.3 Software eficiente

Idealmente o seu compilador deve conseguir produzir um código objeto otimizado, mas ele não tem nenhuma outra informação a não ser o código que você entrega a ele, puro e duro. Compiladores “system aware” só amanhã.

Quantas instruções tem a task mais executada do sistema? Quantos ciclos de clock cada instrução consome? Se um sistema rodando a 5 MHz acorda o processador a cada segundo para executar 500 instruções, admitindo 1 instrução/ciclo, ao diminuírmos somente uma instrução desta task, estaremos dando uma sobrevida a bateria de ~ 6,5 segundos por ano, num sistema 24/7. Escalone isso para mais MHz de operação e mais instruções economizadas, e veja por si só.

Assim, o nosso código, como regra geral deve evitar primitivas complexas e ser otimizado em desempenho. Considero que conhecer o processador e o compilador, e a utilização de boas práticas de engenharia de software como a refatoração, são as melhores forma de escrever códigos eficientes. Abaixo alguns exemplos:

Código original

Código otimizado

if ((i % 10) == 0 )
{
   // faça algo
}
i++;
If (counter == 10)
{
   // faça algo
   counter=0;
}
else
{ 
   i ++
}
counter++;

A operação ‘módulo’ usualmente toma mais ciclos de instrução. É mais econômico reproduzir o mesmo efeito com operações mais baratas.

Código original

Código otimizado

for(i=0;i<10;i++)
{
   // faça algo 1
}
for(i=0;i<10;i++)
{
   // faça algo 2
}
for(i=0;i<10;i++)
{
   // faça algo 1
   // faça algo 2
}

Uma chamada de loop com inicialização, incremento e comparação é economizada.

Código original

Código otimizado

unsigned int x;
 for (x = 0; x < 100; x++)
 {
     A[x] = B[x];
 }
unsigned int x; 
 for (x = 0; x < 100; x += 5 )
 {
     A[x]   = B[x];
     A[x+1] = B[x+1];
     A[x+2] = B[x+2];
     A[x+3] = B[x+3];
     A[x+4] = B[x+4];
     
 }

No código acima, 100 elementos do vetor A serão copiados para as primeiras 100 posições do vetor B. A segunda implementação faz com que o loop precise ser rodado 20 vezes, ao invés de 100.

(Link externo: este AN da Atmel indica no capítulo 9 algumas formas de otimizar tamanho e tempo de execução para AVRs 8-bit)


 

Na parte 2 vou falar sobre técnicas aplicadas no nível arquitetural (power-gating, clock gating, multi Vdd, multi Vth, DVFS…).

Até mais!

O texto desta publicação é original. As seguintes fontes foram consultadas:
The Art of Hardware Architecture, Mohit Ahora
Ultra-Low Power Integrated Circuit Design, Niaxiong Nick Tan et al.

Operando números negativos em computadores

maxresdefault

Os conceitos de aritmética modular eram utilizados em computadores mecânicos, e já não eram novidade naquela época. (Imagem de: https://www.youtube.com/watch?v=nmwSmwNF9XY)

1. “Tudo é número” (Pitágoras)

Eu sempre me perguntei se Matemática é descoberta ou desenvolvimento. Os autores costumam dizer que ela é desenvolvida conforme o homem necessita. Assim, podemos imaginar que os humanos primitivos faziam risquinhos para representar uma coleção de objetos (cabras, peles, filhos…). Constatada sua ineficiência, criaram-se símbolos que representavam tamanhos de coleções diferentes, depois símbolos que representavam uma coleção de outros símbolos, e a coisa foi ficando cada vez mais sofisticada.

Costumamos representar os numerais em uma base B, da seguinte forma, em uma notação de ponto fixo onde “a” é inteiro:

eq

O sistema de base 10 que utilizamos é composto pelos símbolos {0,1,2,3,4,5,6,7,8,9} para representar as quantidades de zero a nove unidades de algo. Aliás, o zero é uma invenção muito interessante pois é um símbolo que representa ‘não há’. Sei que até há algum tempo não existia consenso sobre o zero ser Natural ou não.

Dizemos também que utilizamos um sistema posicional – desloque o número para esquerda ou direita ele assume centenas, dezenas, milhares, centésimos…

2. Preciso de um sinal

Certamente você já internalizou o conceito, mas se em engenharia os números são abstrações de grandezas físicas, o sinal sempre carrega outro significado. Se eu tenho R$100,00 na conta-corrente esse dinheiro sai do banco e vem para o meu bolso. Se eu tenho -R$100,00, além de estar infeliz, eu preciso fazer com que esse dinheiro chegue até o banco. Um móvel com aceleração de -12m/s^2 permanece em repouso sob uma referência que acelera a 12m/s^2. Não à toa, os números negativos também são chamados de relativos, e ter isto em mente ajuda a entender como representá-los em um computador digital.

Vamos analisar um somador hipotético, sem sinal, que utiliza 3 bits para representar cada uma das suas parcelas, e também 3 bits para representar o resultado. Em notação decimal, as entradas variam de 0 a 7. Ao somarmos 7+7, por exemplo, a saída ‘estoura’:

 111
+111
====
1110

O importante aqui é você perceber que estamos trabalhando num universo onde o maior número possível de ser representado é o 7. E isto é exatamente o que ocorre quando construímos computadores. Se o meu computador pode somente ter 3 bits, é como se tudo o que tivéssemos fosse um conjunto de 8 elementos distintos.

(d.i) Definição. Um Grupo G é um conjunto de elementos em que se pode definir uma operação binária *, que satisfaz os critérios de fechamento, elemento neutro, elemento simétrico e associatividade. Um exemplo de grupo é o conjunto dos inteiros (Z) para as operações de soma e multiplicação.

G = {x, y, z} é um grupo, se para uma operação binária *, {G, *} satisfaz os seguintes axiomas:

a) fechamento: Se x e y ∈ G, x *y ∈ G
b) elemento neutro: Existe um elemento I ∈ G, tal que a*I = I*a = a,
c) elemento simétrico: Para todo elemento a ∈ G, existe um elemento s tal que a * s = I
d) associatividade: Para x, y, z ∈ G é verdadeiro que: x*y*z = (x*y)*z = x*(y*z)

A ideia de infinito não é concebível para uma máquina, e utilizaremos grupos finitos.

(d.ii) Definição. Um grupo finito é um conjunto finito de elementos com uma operação binária que satisfaz os axiomas do fechamento, associatividade, e cancelamento (Se a*x = b*x, a = b).

Para o meu computador de 3 bits talvez seria conveniente utilizarmos os conjuntos:

A= { 0, 1, 2, 3, 4, 5, 6, 7}, ou

B = { 16, 17, 18, 19, 20, 21, 22, 23} quem sabe.

Eu empilhei os conjuntos acima para forçar a ideia de congruência entre os elementos. Além disso, para a operação de adição +, os axiomas de (d.ii) são satisfeitos se definirmos: 7+1 =0 em A e 23+1=16 e A e B podem ser considerados grupos.

(d.iii) Definição. Dizemos que dois grupos são equivalentes quando existe uma relação biunívoca, tal que para todo elemento a ∈ A e b ∈ B, existe k inteiro tal que a = b + kN, onde N é o número de elementos (módulo) de A e B.

Utilizando os exemplos A e B acima:

2 = 18 + 8k, se k = -2

20 = 4 + 8k, se k = 2,

3. Subtraindo com números não negativos

Talvez seja conveniente representarmos um grupo finito G onde o maior módulo possível é 8, na forma de um círculo de comprimento 8:

modulo

Desta forma, podemos representar qualquer número inteiro a = b + kN, onde k é o “número de voltas” dadas no círculo de comprimento N, e ‘b’ o restante de arco (em unidades) necessário para completar ‘a’, que chamamos de resto ou resíduo.

Assim, para o nosso grupo de módulo 8:
i) 21 = 5 + 2x8
ii) 64 = 0 + 8x8
iii) 10 = 2 + 1x8

Que podemos escrever da seguinte forma:
iv) 21 ≡ 5 (mod8) (21 é congruente a 5 módulo 8)
v) 64 ≡ 0 (mod8) (64 é congruente a 0 módulo 8)
vi) 10 ≡ 2 (mod8) (10 é conguente a 2 módulo 8)

Se eu quero resolver 8 – 6, posso evitar trabalhar com subtrações. Primeiro vamos pensar graficamente:

  • Partindo do zero e andando 8 unidades voltamos ao zero, ou: 8 ≡ 0 (mod8)
  • Partindo do zero e andando 6 unidades a esquerda, chegamos ao 2, ou: -6 ≡ 2 mod8

Logo: 8 (mod8) – 6 (mod8) ≡ 0 (mod8) + 2 (mod8) ≡ 2 (mod8).

Abaixo, com exemplos, descrevo um algoritmo para resolver subtrações utilizando números não-negativos em um grupo módulo N.

(p.1) Problema.
Resolva 15 – 8 somente com números não-negativos, em G = {0,1,2,3,4,5,6,7}
1o - Ache o minuendo congruente no grupo desejado. 
O ‘equivalente modulo 8’ de 15 pode ser encontrado em G com (1):
15 = b + 8k, se k = 1, b = 7.
2o - Ache o subtraendo equivalente ao grupo desejado e some ao minuendo:
O equivalente a 8 em G é 0.
8 = b + 8x1; b = 0
Assim:
7 + 0 = 7.
3o - Encontre o equivalente do resultado no grupo desejado:
7 já esta no grupo desejado, e é o resultado.

(p.2) Problema.
Resolva: 22 – 12, somente com números não negativos em 
G = {8,9,10,11,12,13,14,15}
O equivalente de 22 no grupo G é:
22 = a + 8k, k = 1, e a = 14 
Assim:
14 + 12 = 26
Cujo equivalente em {8, 9, 10, 11, 12, 13, 14, 15} é:
26 = b + k8, k = 2 e b = 10.

Uma pequena demonstração algébrica do método pode ser feita.
(dm.i) Demonstração.
Sejam M, S e D pontos do meu universo módulo N, de forma que:
(i) M-S=D para
(ii) M>=S e D <= N.
e
(iii) D = 0 para M=S.
Queremos demonstrar que:
(iv) D = N - [(N-M) + S]
O lado direito de (iv) satifaz (ii) para M>=S.
Ainda para M=S, (iii) é satisfeito: D=N-N-M+M=0
Naturalmente em (iv):
D= M-S = N-N+M-S
D = M-S
como queria demonstrar.

4. Complementos numéricos

(d.iv) Definição.
(d.iv’) O complemento de b de um número de n dígitos y, na base b, é dado por: b^n – y.
(d.iv’’) O complemento de b-1 de um número de n dígitos y, na base b, é dado por (b^n – 1) - y

Os livros em geral começam a tratando do assunto desta postagem com uma tabela:

 Elemento | Complemento de 9 
 ---------------------------
    0     |    9
    1     |    8
    2     |    7  
    3     |    6
    4     |    5
    5     |    4
    6     |    3
    7     |    2
    8     |    1
    9     |    0

Tabela 1. Complementos de 9 para dos inteiros de 0 a 9

O método dos complementos é uma aplicação direta da aritmética modular, e fundamentalmente utiliza dos mesmos conceitos explicados nas seções anteriores.

Podemos efetuar as subtrações somente com números não-negativos, usando o complemento do minuendo somado ao subtraendo, e tomando o complemento da soma como resposta.

8-3 → 1 + 3 = 4.

O complemento para 9 de 4 é 5, que é a nossa resposta.

Quando tratamos de números maiores que dezenas, fazemos o complemento para 99, 999, 9999… (nossa aritmética continua posicional!)

Exemplo:

Resolva 953 – 53 utilizando o método dos complementos, em módulo 9

046+ 53 = 99

Cujo complemento para 999 é 900, que é a nossa resposta.

5. Complemento de 2

A base para a representação numérica digital é a binária B = {0, 1}, e vamos utilizar os conceitos de aritmética modular para operarmos subtrações com números binários.

Decimal 0 a 7 | Binário | Congruentes
-----------------------------------------
      0       |   000   | ...,-8,0,8,...
      1       |   001   | ...,-7,1,9,...
      2       |   010   | ...,-6,2,10,...
      3       |   011   | ...,-5,3,11,...
      4       |   100   | ...,-4,4,12,...
      5       |   101   | ...,-3,5,13,...
      6       |   110   | ...,-2,6,14,...
      7       |   111   | ...,-1,7,15,...

Tabela 2. Inteiros decimais de 0 a 7 representados na forma binária, e seus congruentes módulo 8.

No nosso computador de três bits podemos representar a reta numérica com os pontos decimais 0 a 7, em base 2, da seguinte forma:

    b'    a'    a     b    c    d    e    f    g    h
    ?     ?    000  001  010  011  100  101  110  111
   ------------------------------------------------->

Queremos representar números negativos. Ora, 
Se a + s = 0, s é o simétrico de a se 0 representar o elemento neutro 
da álgebra.

No caso dos números representados na reta numérica acima:
a + s = 000 ↔ a' = 000; (000 + 000 = 000) 
b + s = 000 ↔ b’ = 111; (001 + 111 = 1000)
c + s = 000 ↔ c’ = 110; 
d + s = 000 ↔ d’ = 101;
e + s = 000 ↔ e’ = 100;
f + s = 000 ↔ f’ = 011;
g + s = 000 ↔ g’ = 001.

Exceto para f e g, todos os simétricos tem 1 no bit mais significativo, de forma que para lançar mão de números negativos poderíamos definir nosso grupo com os seguintes elementos escritos em base 2:

Decimal | Binário
------------------
  -4    | 100
  -3    | 101
  -2    | 110
  -1    | 111
   0    | 000
   1    | 001
   2    | 010
   3    | 011

Escolhemos não representar o 4, e sim o -4 a partir dos resultados empíricos para encontrar o elemento simétrico.

Perceba que continuamos com o mesmos elementos de antes, entretanto, atribuímos a ele um grupo equivalente, com elementos côngruos modulo 2, mantendo todas as propriedades que já tornavam aquele um grupo finito válido (definições d.i, d.ii, d.iii). É possível provar que o conjunto G = {100, 101, 110, 111, 000, 001, 010, 011} é um grupo finito.

O complemento de 2, do número binário y de três dígitos por (d.iv’) é então 8 – y. A representação binária do complemento de 2 de um número y, é utilizada para a representar o elemento simétrico s, tal que y + s = 0.  Como regra prática dizemos que o complemento de 2 de um número na base 2, é sua negação adicionada a 1. (Pense em um círculo somente com os pontos 0 e 1 na base 2, e comprove graficamente por que funciona).
Interpretação do '1' como sinal: 

Peguemos como exemplo a igualdade 011 + 101 = 1000, que poderia significar 3 + 5 = 8, que é côngruo a 0 módulo 8. Ainda na base 2, oito é representado por 1000. O ‘1’ se pensado como sinal, indica o sentido pelo qual ultrapassamos a origem ao representarmos nossos arcos de maneira contínua. Andando da esquerda para direita os elementos assumem valores cada vez maiores. Quando os 3 bits estouram e o vai-um é necessário para representar o estado naquele instante , significa que os elementos agora estão assumindo valores cada vez mais negativos em relação aos anteriores.

Você pode experimentar efetuar as operações com o complemento de 2 e verificar como a notação é muito conveniente.

Note que:
O complemento de 2 é definido para representar números de base 2, de
N bits, no intervalo de -2^(N-1) a 2^(N-1) – 1.
Ao operarmos representações com números de dígitos diferentes, devemos estender o sinal. 
Exemplo:

-16 - 2 =
 10000
+11110
======
101110

No próximo artigo sobre o assunto, vou mostrar como operar aritméticas com sinal em circuitos digitais descritos em Verilog.

Até a próxima!

O texto deste artigo é original. As seguintes referências foram consultadas durante sua elaboração:
Conceitos Fundamentais da Matemática (Bento Jesus de Caraça)
Introduction to Logic Circuits and Logic Design With Verilog (Brock J. LaMeres)
Group Theory, em https://crypto.stanford.edu/pbc/notes/group/group.html
Introdução à Teoria dos Números, em http://vigo.ime.unicamp.br/Projeto/2012-1/ms777/ms777_Viviane.pdf