Como funciona a UCP

Figura-7-2
Figura 2

Para suporte ao nosso trabalho vamos desenhar uma UCP muito básica, com tamanho de palavra de 8 bits e com um muito reduzido conjunto de instruções, concretamente 16. Com 8 bits podem-se endereçar 256 posições de memória ou Bytes, pelo que o tamanho da memória  será de 256 Bytes no máximo.

Tentámos mostrar como é que na realidade havendo só uma memória a UCP consegue, através da cache L1 dirigir-se a duas memórias separadas, dados e e instruções. Claro que a se a memória tiver 8 bits por posição as duas caches L1 terão 8 bits cada.

Mas isto é a realidade. Para a nossa UCP vamos ter duas memórias separadas com 256 posições cada uma. A Memória de Dados (MD) terá 8 bits por posição e a Memória de Instruções (MI) terá 12 bits por posição. Já vamos entender o porquê da diferença e também porque é que os Registadores (a palavra) mantêm os 8 bits de largura.

No nosso imaginário, as Memórias vão ter as suas posições organizadas em forma de pilha, como se vê na Figura 2, em que cada posição é definida por um número sequencial começando em 0.

Os Dados

Figura-7-4
Figura 4

A ULA é capaz de executar operações aritméticas e lógicas múltiplas, conforme os circuitos que nela estiverem integrados, dos quais já vimos alguns. Contadores, deslocadores de bits, circuitos aritméticos diversos, circuitos lógicos diversos, etc.

Para a nossa UCP, vamos admitir a possibilidade de 4 operações: soma, subtração, multiplicação e comparação, pelo que nela estarão incluídos os circuitos somador/subtrator, multiplicador e comparador.

Veja-se o esquema gráfico da constituição desta ULA na  Figura 4. O número 8 ao lado de uma linha significa que essa linha representa 8 linhas de bit. O número 1 significa que essa linha representa 1 linha de bit.

Mas como vamos dizer à ULA qual a operação que queremos que execute em cada situação?

Definir uma operação na ULA

Figura-7-5
Figura 5

Todos os circuitos de operações têm as suas saídas convergindo em simultâneo para a saída da ULA, mas só uma de cada vez lá pode estar presente. Para que tal seja possível utilizamos portas Tristate ativas a 1, uma em cada linha de bit de cada operação. É pela ativação destas portas Tristate que vamos conseguir escolher de qual dos circuitos é o resultado presente na saída da ULA. Para essa ativação vamos utilizar um DEMUX sem valor na entrada, que assim se limita a tornar ativa uma das suas  saídas de cada vez, sendo essa saída que vai por sua vez ativar as portas Tristate que se pretende.

Então o nosso DEMUX precisa de 4 saídas, cada uma ativando uma das operações soma, subtração, multiplicação ou comparação. Para um DEMUX de 1 para 4 precisamos de um sinal de controlo de 2 bits. Vamos chamar-lhe DEFULA (DEFinição da Operação na ULA) que com 2 bits definirá a soma (00), a subtração (01), a multiplicação (10) ou a comparação (11). Veja-se o esquema gráfico da constituição desta DEFULA na  Figura 5.

Começamos aqui a construção da Unidade de Controlo da nossa UCP. É nesta unidade que são definidos os sinais que vão controlar o funcionamento da UCP.

Já encontrámos forma de selecionar a operação que em cada caso pretendemos que a ULA execute. Mas  para executar uma operação ela precisa de operandos.

Obter os Operandos

Aqui vai ficar evidente a forte ligação existente entre a UCP e a Memória.

Na construção da nossa UCP, como forma de  facilitar o que se pretende agora, o entendimento da UCP,  vamos considerar as Memórias como sendo circuitos integrados na UCP, sendo portanto a comunicação direta e imediata entre UCP e Memórias. A forma real como a UCP comunica com a Memória não interessa neste ponto, mas será profusamente tratada em Capítulos próprios, pela complexidade e importância de que se reveste.

A UCP está em ligação permanente com a Memória de Dados (MD) e só trabalha com dados que lá se encontrem. Logo, os operandos são obtidos da MD. Para controlar o modo de operação sobre a MD teremos o sinal WR (WRite – escrever), que será ativo a 0. Este será o único sinal que vamos utilizar para controlar os acessos à MD.

  • Quando ativo (WR=0), escreve na MD, no endereço indicado, o valor que se encontrar na sua entrada.
  • Quando inativo (WR=1), está disponível na saída da MD o valor na posição indicada pelo endereço.

O endereço que referimos será obtido do conteúdo  das instruções que ordenam movimentos com a memória, como já veremos de seguida. Para já interessa saber que estarão disponíveis nas instruções indicações sobre o endereço da posição da MD a ser operada em cada momento.

Começamos por ler da MD o valor do 1º operando. Mas a ULA precisa de dois operandos para executar uma operação e  só conseguimos aceder a um dado de cada vez na leitura à MD. Então, o 1º operando terá que ser armazenado provisoriamente em qualquer local dentro do núcleo da UCP.

É agora que os Registadores vão entrar na nossa história.

Para guardar temporariamente valores que a nossa UCP necessita para as suas operações vamos introduzir-lhe um Registador, o Registador A, que passaremos a chamar por RegA a partir daqui. O 1º operando, depois de obtido da memória de dados, é guardado no RegA.

Figura-7-6
Figura 6

Para que um dado seja memorizado no RegA, ele tem que estar ligado à saída da memória de dados e é necessário que receba um sinal de Clock controlado. Esse sinal será MEMA (MEMoriza em A).

MEMA será ativo a 1, pois será ele o Clock do RegA. Ligando-o em conjunto com o sinal de clock real a uma porta AND garantimos que só quando MEMA estiver em alta (1) o Clock atua no RegA, memorizando o valor que se encontra na sua entrada.

De seguida esse valor propaga-se à entrada do 1º operando da ULA.

Guardado o 1º operando, podemos agora ler da MD o valor do 2º operando, o que se consegue ligando a entrada deste 2º operando na ULA à saída da MD. Assim, quando a posição em que se encontra na MD for endereçada, o seu valor estará disponível na saída da MD, propagando-se para a ULA como 2º operando da operação.

Na Figura 6 está graficamente representado o que descrevemos até agora. Está introduzido um Registador e mais dois sinais a serem definidos e emitidos na Unidade de Controlo.

Executar a Operação

Estando o 1º operando e o 2º operando nas respetivas entradas da ULA, por propagação dos seus sinais fica o resultado da operação selecionada na saída da ULA. Ora, estando o 2º operando a ser lido da memória de dados neste clock e portanto WR inativo para escrita, não pode o resultado da operação ser escrito na memória. Vamos seguir a descrição com a Figura 7.

Assim sendo, temos que o escrever temporariamente no RegA, sobrepondo o valor do 1º operando. Ligando a saída da ULA à entrada do RegA, ainda por efeito da mesma propagação o resultado da operação chega à sua entrada. E aí pára, pois estamos num Registo e a porta da estalagem ainda está fechada.

Mas para além deste resultado os circuitos de operações da ULA produzem outro tipo de informação fundamental para o entendimento do resultado propriamente dito, como por exemplo os bits com a informação de Cout (Carry Out) e Exs (Excesso) e os bits Ig e R, entre outros, escrita no fim de cada operação num outro Registador a que vamos chamar RegULA  (Registador da ULA), e que podemos ver na Figura 8.

Incluímos na informação fornecida pela nossa UCP ao RegULA unicamente os bits Ig e R que resultam de uma operação de comparação, porque são os únicos que nos interessam para já.

O Registador A e o Sinal MEMA

Figura-7-7
Figura 7

Então o RegA estará ligado à saída da MD e também à saída da ULA, i.e. estão a convergir para a sua entrada dois conjuntos de linhas de 8 bits. Como só um conjunto lá pode estar presente de cada vez, vamos intercetar estas linhas com portas Tristate.

Como dizer então ao RegA, quando se ativa o sinal MEMA, qual dos dois conjuntos deve registar?

Estamos novamente perante uma decisão condicional, a resolver com o DEMUX A, que com o seu sinal de controlo de 1 bit vai ativar um dos conjuntos de portas Tristate.  Vamos chamar a este sinal de controlo DEFA  (DEFine a entrada de A).

Quando o valor memorizado no RegA não interessar para a operação seguinte, é chegada a altura de guardar na MD o valor nele contido.

Ligando a sua saída à entrada da MD, o seu valor será gravado no endereço indicado, sempre que WR estiver ativo (WR=0). Ver na Figura 7.

Sensível a que Flanco do Clock

A questão em análise é como garantir que, quando MEMA exerce o efeito de Clock sobre o RegA, o valor nele memorizado é sempre aquele que nos interessa.

Os sinais têm tempos de propagação dentro dos circuitos, tão maiores quão maior a distância percorrida e o número de transístores atravessados.

Ora, a subida do clock no RegA só se dá quando o valor 1 de MEMA chega à porta AND e deixa que o real Clock, em alta entretanto, se propague. E será nesse momento que o Clock sobe no RegA e o valor que estiver na sua entrada é memorizado.

Reparem bem no que se disse: a subida do clock no RegA não se dá em simultâneo com a subida do verdadeiro Clock, mas durante o seu período em alta.

E que valor estará nessa ocasião na sua entrada? Será que, por exemplo, quando queremos memorizar o resultado da operação ele já estará na sua entrada? Quem sabe?

Bom, mas a ação de MEMA como clock sobre o RegA vai-se repetir na subida do clock real do ciclo seguinte, pois nesse instante o valor 1 de MEMA ainda se mantém e o valor pretendido já estará na entrada de RegA.

E quem é que nos garante que aquando da chegada de MEMA e da sua 1ª ação sobre o RegA o valor na entrada deste não foi já alterado?

Pois é. E se assim for, o 1º operando da ULA é alterado e consequentemente o resultado da operação que vai ficar na entrada de RegA para ser memorizado no Clock seguinte.

Ficaria tudo baralhado. Continuamos na dúvida.

A solução passa por fazer coincidir o verdadeiro Clock com o Clock provocado por MEMA. Para que isto seja possível MEMA tem que já estar ativo (1) quando a ação do Clock que pretendemos sincronizar acontecer. E isso só vai acontecer na descida do Clock.

Portanto, o RegA vai ser um Registo de Básculas sensíveis ao flanco descendente do Clock.

As Instruções

Já sabemos o que é e como funciona a ULA, como se obtêm os seus operandos e resultado e como se trabalha com a MD. Vimos alguns sinais de comando relativos à operação com dados:

DEFULA, MEMA, WR e DEFA.

A questão agora é: Como é que indicamos o seu valor?

Esse é o papel das instruções que fornecemos à UCP, que tornarão evidente a tal capacidade que faz a UCP tornar o computador diferente da máquina de lavar. Cada instrução deve especificar os valores necessários à execução de cada operação. Esse deve ser o conteúdo da posição da MI correspondente a essa instrução.

Uma instrução isoladamente não produz grande coisa. Por isso elas são usadas em grupos, ordenadas de acordo com uma determinada sequência: o Programa. Um programa executa-se de acordo com a sequência natural das suas instruções, que são guardadas na MI precisamente com essa sequência.

Tal como na MD, a MI também precisa de ser informada do endereço onde se encontra a instrução a ser executada. Para isso a UCP dispõe de um apontador.

Ponteiro de instruções

O PI (Ponteiro de Instruções) contém sempre o endereço na MI da instrução que está a ser executada. Na nossa UCP vai ser representado pelo Registador PI.

As instruções são lidas da MI de acordo com a sua sequência natural. O Registador PI será incrementado por cada instrução executada, passando a apontar para a posição da MI onde se encontra a instrução seguinte. Por esta razão será um contador crescente.

Figura-7-9
Figura 9

Mas, em qualquer programa existem Saltos, condicionais ou incondicionais. Um Salto é um desvio da sequência normal de execução de um programa. O Salto pode ser condicional ou incondicional:

  • Um Salto incondicional acontece quando é executada uma instrução cuja função é desviar o programa da sua sequência normal para outro local indicado nessa instrução.
  • Um Salto condicional acontece quando é executada uma instrução cuja função é, mediante a análise de uma condição, desviar ou não o programa da sua sequência normal para outro local indicado nessa instrução.

Portanto, em caso de Salto, em que a sequência natural de execução do programa deve ser alterada, tal indicação fará parte da própria instrução lida e o Registador PI deverá ser carregado com o endereço de início da nova sequência, que está incluído na própria instrução. Por esta razão será um contador crescente com carregamento em paralelo.

Na Figura 9 mostra-se o conjunto Registador PIMI a incluir na nossa UCP. E é precisamente da análise desta Figura que surge a  próxima questão:

De onde vêm a indicação de salto e a indicação de carregamento em paralelo, se for o caso?

Salta ou Incrementa

Salta se zero, Salta se menor que zero, Salta se igual a, Salta se menor que, Salta se maior que, Salta se igual a, Salta se diferente de, etc., são algumas possíveis instruções de salto condicional. São a análise  de uma expressão (o tal predicado) de comparação de duas variáveis e/ou constantes que devolve o resultado verdadeiro/falso, i.e. Salta ou não Salta.

Por isso juntámos um circuito comparador na ULA. Lembramos que esse circuito devolvia dois sinais de 1 bit:

  • Ig que é 1 quando A e B são iguais.
  • R que é 1 quando A é maior ou igual a B ou 0 quando A é menor que B.

Para o nosso caso, A representa o 1º operando e B representa o 2º operando. Assim, com os valores de Ig e R que a ULA devolver, já podemos tomar decisões.

Na nossa UCP vamos introduzir três condições de Salto:

  • Se A for igual a zero, em que compara A com B, assumindo B o valor 0. Se Ig for 1 então A=0 e Salta.
  • Se A for menor que B, em que compara A com B. Se R for 0 então A e Salta.
  • Se A for negativo, em que compara A com B, assumindo B o valor 0. Se R for 0 então A e Salta.

Figura-7-8No Registador PI vamos fazer PL ativo a 1:

  • Quando PL for 1 o endereço da instrução a executar,  o do Salto, é carregado em paralelo.
  • Quando PL for 0  o endereço da instrução a executar incrementa em relação ao anterior.

No nosso caso vamos ter 4 hipóteses de evolução dos endereços das instruções: Salta se for menor, Salta se for igual, Salta sempre, Incrementa. E destas 4 hipóteses tem que resultar um valor para PL do Registador PI. Um MUX com as 4 hipóteses nas entradas é a solução. Vamos chamar-lhe MUX PI e ao seu sinal de seleção de 2 bits DEFPLI (DeFine o estado de PL do PI). Podemos vê-lo representado na Figura 8. Nas 4 entradas (X0,X1,X2,X3) do MUX PI estarão:

  • Em X0, o valor 0 selecionável com DEFPLI(00) resultando PL=0 e o incremento do Registador PI.
  • Em X1, o valor 1 selecionável com DEFPLI(01) resultando PL=1 e um salto no Registador PI.
  • Em X2, o valor Ig devolvido pela ULA, selecionável com DEFPLI(10). Se A=B então Ig=1 e portanto PL=1 resultando um salto no Registador PI. Se A≠B então Ig=0 e portanto PL=0 resultando o incremento do Registador PI.
  • Em X3, o valor ¬R devolvido pela ULA, selecionável com DEFPLI(11). Se A então R=0 logo ¬R=1 e portanto PL=1 resultando um salto no Registador PI. Se A≥B então R=1 logo ¬R=0 e portanto PL=0 resultando o incremento do Registador PI.

E quando salta, como se informa o MUX PI do endereço a carregar em paralelo?

A Composição da Instrução

A instrução lida da MI é composta por

  • um espaço para o código da instrução com 4 bits, os necessários para um conjunto de 16 instruções e
  • um espaço para o endereço ou algum outro valor necessário à instrução com 8 bits, pois é esta a largura dos Registadores da nossa UCP,
  • devendo portanto a MI ter 12 bits por cada posição.

Sempre que houver uma instrução com salto é preciso informar o MUX PI sobre o endereço da posição para onde salta, pelo que esse valor é incluído no espaço de 8 bits da instrução. Assim, o espaço destes 8 bits de menor ordem da memória de instruções deverá ser ligado à entrada em paralelo do Registador PI, para onde propagará o valor que será lido quando necessário, como se pode ver na Figura -8.

Se a instrução não for de salto, esse espaço de 8 bits fica vazio, pois não há nada para indicar ao Registador PI. Ele irá incrementar, como contador que é.

Mas o espaço não vai ficar vazio pois há outros usos para ele. Na escrita de um programa é por vezes necessário indicar na própria instrução o valor que vai intervir na operação da ULA, ou ainda o endereço da MD onde se pretende ler ou escrever.

Ora, como tanto uma instrução que define uma operação na ULA, como uma instrução que manda guardar ou ler um valor na MD não são  certamente instruções de salto, o tal espaço de 8 bits vai estar desocupado e nós vamos ocupá-lo com estes novos valores.

A Constante

Figura-7-12
Figura 12

A partir daqui vamos chamar constante ao valor contido no espaço referente aos 8 bits de menor ordem de cada linha da MI.

Pelo que vimos até agora, essa constante pode ter vários  significados e vai ter que estar ligada a diversos locais. Pelo menos para já, verificamos que tanto pode ser um endereço da MI, como um operando da ULA, como ainda um endereço da MD.

Então, a constante vai estar ligada às entradas:

  • de endereços da MD,
  • de carregamento em paralelo do Registador PI,
  • da ULA se for o seu 2º operando e
  • do RegA se for o 1º operando da ULA.

Como nestas duas últimas entradas já estão ligados valores que vêm da MD vamos ter que interpor o MUX B, cuja função será ativar em alternativa portas tristate inseridas nas linhas de bit das entradas que vêm da MD e da constante. A seleção do valor que nos interessa será feita por um sinal de controlo a que chamaremos DEFB (DEFinição da entrada de B), de 1 bit, que se pode ver representado na Figura 12.

O Opcode

Figura-7-11
Figura 11

Na tabela da  Figura 11 resumimos os sinais de controlo que fazem parte da nossa UCP. Aí podemos verificar que são necessários 8 bits para representar em conjunto esses sinais de controlo, sendo que com 8 bits podemos ter 256 possíveis combinações. Mas, na composição da Instrução só temos 4 bits para selecionar as diferentes composições de sinais que nos interessam.

E agora? Como é que com um conjunto de 4 bits se consegue informar a UCP de todas as seleções?

Nós só queremos 16 dessas possíveis 256 combinações, que é o nosso conjunto de instruções. Com um descodificador conseguimos com 4 bits de seleção representar as 16 combinações que nos interessam das 256 combinações possíveis de 8 bits. A este conjunto de 4 bits de seleção do descodificador chama-se opcode (operation code) ou código da operação.

Figura-7-13
Figura 13

Então temos que introduzir um Descodificador entre a MI e a unidade de controlo da nossa CPU para que o código da operação (Opcode) que a instrução vai fornecer à UCP seja traduzido em sinais de controlo que por sua vez vão fazer cumprir essa instrução.

Na Figura 13 representamos a lógica do circuito descodificador para a nossa UCP onde se pode verificar como com os 4 bits do opcode se obtêm todas as combinações dos valores dos sinais de controlo que constituem o nosso conjunto de instruções.

Neste momento já sabemos falar com a UCP na sua língua. Basta criar o algoritmo passo a passo do que lhe queremos dizer, decompor em palavras que traduzimos em Opcodes a que juntamos a constante, que em conjunto formarão a frase que pretendemos transmitir.

Fácil, não é? Basta escrevermos uma série de palavras (instruções) com 12 carateres  de 0 e 1 cada uma, que em conjunto formarão a nossa frase (programa). Bom, o único problema é que T o seu autor provavelmente ficará mentalmente afetado.

Por essa razão arranjámos uma forma de substituir os Opcodes por mnemónicas que os representam e que são mais facilmente entendíveis por nós humanos, criando assim uma linguagem de programação de muito baixo nível (muito próxima do código máquina) a que se chama Assembly.

Mas abordagem a esta e outras formas de programação será feita no artigo seguinte.

O que temos até agora

Pelo que vimos até agora, parece que para a UCP uma instrução de um programa é um conjunto de  sinais de controlo que encaminham os seus estafetas (bits) para ligar e desligar interruptores (transístores) que os permitam percorrer os caminhos (circuitos) que os levarão até à solução para a sua tarefa.

Agora vamos ter que juntar tudo o que temos vindo a definir num único circuito onde tudo faça sentido. Tentámos deixar separados e evidenciados as memórias, a Unidade de Controlo, a ULA e os Registadores.

Na Figura 10 representa-se o aspeto final da nossa UCP já com o descodificador incluído. As linhas mais grossas a Laranja (1), a Verde (2) e a Ciano (3) representam o ciclo de funcionamento de uma UCP:

Busca, Descodificação e Execução.

Figura 1-10 Gráfico final dos circuitos da UCP evidenciando os seus ciclos de funcionamento 1 - Busca 2 - Descodificação 3 - Execução
Figura 10 – Gráfico final  da UCP com os seus ciclos de funcionamento – 1 – Busca – 2 – Descodificação – 3 – Execução