Laboratório de Arquitetura de Sistemas Digitais
Implementação do datapath de um processador,
parte II
OBJETIVOS
Conhecer as funções realizadas pela Unidade Lógica/Aritmética (ULA) Implementar uma ULA completamente combinacional
Compreender os mecanismos de geração de circuitos combinacionais a partir de uma descrição com bloco always procedural
Refletir sobre o atraso ou tempo de propagação de um circuito combinacional complexo
Identificar como a temporização do circuito combinacional afeta a freqüência máxima de operação de um sistema digital
Compreender as questões de projeto de um datapath – compartilhamento de recursos e desempenho
Complementar o datapath iniciado no experimento anterior
Verificar o funcionamento de um datapath, realizando diversas operações manualmente
INTRODUÇÃO
Toda operação em um datapath é realizada como uma transferência de dados. O caso mais comum é a transferência entre registradores. Esta transferência pode ser realizada sem que haja nenhuma modificação dos dados, quando dizemos que estamos realizando uma operação de movimentação (move, em inglês). Também podemos efetuar as transferências modificando os dados e ou utilizando mais de uma fonte de dados. Todas estas transferências, alterando ou não os dados, chamamos de microoperações. As alterações nos dados podem ser de diversos tipos. Normalmente nos processadores as operações mais comuns são aritméticas e lógicas. Estas operações são realizadas por um circuito dedicado denominado Unidade Lógica/Aritmética, ou ULA. As operações realizadas pela ULA, juntamente com o restante da arquitetura, estão intimamente ligadas com as instruções realizadas pelo processador. Como exemplos de operações aritméticas encontramos soma e subtração de valores inteiros com e sem sinal. As operações lógicas comuns são AND, OR e XOR realizadas entre os bits individuais dos operandos.
O conceito de tamanha da palavra (word) de um processador está associado ao tamanho em bits dos registradores, dos barramentos e das operações realizadas pela ULA. Alguns processadores possuem mecanismos para utilizar palavras menores do que o default. Por exemplo, muitos processadores com palavras de 32 bits podem operar com dados de 16 bits (half word) e 8 bits (char).
PREPARAÇÃO
HDL_2
Neste experimento será implementado uma ULA completamente combinacional com palavras de 16 bits. A especificação com as operações que devem ser realizadas, juntamente com a codificação de bits de cada operação será apresentada adiante. De posse desta implementação da ULA e da implementação do banco de registradores do experimento anterior podemos concluir o datapath do processador em estudo. O funcionamento deste
datapath pode ser verificado manualmente, realizando-se diversas operações, através da
aplicação dos sinais de controle e valores dos dados nas chaves da placa DE2 e observação dos resultados através dos LEDs ou displays de 7 segmentos.
UNIDADE LÓGICA/ARITMÉTICA
A ULA é um circuito combinacional que efetua um conjunto de microoperações aritméticas e lógicas básicas. Ela possui algumas linhas de seleção usadas para determinar a operação a ser realizada. As linhas de seleção são decodificadas dentro da ULA, de tal forma que k linhas de seleção podem especificar até 2k operações distintas.
A Figura 1 mostra o símbolo para uma ULA típica de n bits. As n entradas de A são combinadas com as n entradas de B para gerar o resultado de uma operação nas saídas G. A entrada de seleção de modo S2 distingue entre operações aritméticas e lógicas. As duas
entradas de seleção de operação S1 e S0 e a entrada de Carry Cin especificam as oito operações
aritméticas (S2 em 0). A entrada de seleção S0 e Cin especificam as quatro operações lógicas (s2
em 1).
O projeto desta ULA será conduzido em três estágios. Primeiramente projetamos a parte aritmética. Em seguida projetamos a seção lógica e finalmente combinamos as duas seções. Como estamos desenvolvendo este projeto usando HDL, esta separação não precisa ser explícita e podemos deixar a cargo do sintetizador o trabalho de realizar o circuito combinacional com todas estas opções. Devemos deixar claro que esta simplificação é mais didática do que real, uma vez que a ULA é um circuito combinacional complexo e que precisa possuir o menor tempo de atraso possível, uma vez que está no caminho de todas as microoperações do datapath.
CIRCUITO ARITMÉTICO
O componente básico de um circuito arimético é um somador paralelo, o qual é construído com alguns circuitos somadores completos conectados em cascata, como mostrado na Figura 2. Ao controlar as entradas de dados do somador paralelo, é possível obter diferentes tipos de operações aritméticas. O diagrama de blocos na Figura 3 demonstra a configuração em que um conjunto de entradas do somador paralelo é controlado pelas linha de seleção S1 e S0.
Há n bits no circuito aritmético com duas entradas A e B e saída G. As n entradas de B passam pela lógica de entrada B indo para as entradas Y do somador paralelo. A entrada de carry Cin vai
para a entrada do somador completo na posição do bit menos significativo. A saída de carry Cout vem do somador completo na posição do bit mais significativo. A saída do somador
paralelo é calculado como
G = X + Y + Cin
onde X é o número binário de n bits das entradas e Y é o número binário da lógica de entrada
B. Cin é a entrada de carry, que pode ser 0 ou 1. Note que o símbolo + na equação denota
soma aritmética.
Figura 2. Somador em onda paralelo completo
A Tabela 1 mostra as operações aritméticas que podem ser obtidas ao controlar o valor de Y com as duas entradas de seleção S1 e S0. Se as entradas de B são ignoradas e inserimos
tudo com 0s nas entradas Y a saída da soma torna-se G = A + 0 + Cin. Isto resulta G = A quando
Cin = 0 e G = A + 1 quando Cin = 1. No primeiro caso, temos uma transferência direta da entrada
A para a saída G. No segundo caso, o valor A é incrementado de 1. Para uma soma aritmética
normal é necessário aplicar B nas entradas Y do somador paralelo. Obtemos G = A + B quando
Cin = 0. A subtração aritmética é obtida ao aplicarmos o complemento das entradas B nas
entradas Y do somador paralelo, obtendo-se quando Cin = 1. Isto resulta em
A mais o complemento de 2 de B, o que é equivalente a subtração com complemento de 2.
Tudo em 1 é a representação em complemento de 2 para -1. Logo, aplicando-se tudo em 1 nas entradas Y com Cin = 0 produz a operação de decremento G = A – 1.
Tabela 1. Funções do Circuito Aritmético
A lógica de entrada B na Figura 3 pode ser implementada com n multiplexadores. Os dados de entrada para cada multiplexador no estágio i para i = 0, 1, ... , n – 1 são 0, Bi, e 1, correspondendo aos valores de seleção S1S0: 00, 01, 10 e 11, respectivamente. Assim, o circuito
aritmético pode ser construído com n somadores completos e n multiplexadores 4-para-1.
O número de portas na lógica de entrada B pode ser reduzido se, no lugar de utilizarmos multiplexadores 4-para-1, fizermos o projeto lógico de um estágio (um bit) da lógica de entrada B.
Figura 3. Diagrama de blocos de um Circuito Aritmético
IMPLEMENTAÇÃO EM VERILOG
A implementação em HDL pode ser feita de forma completamente estrutural, utilizando o circuito acima como base. Pode também ser feita utilizando a capacidade de geração de circuitos aritméticos do sintetizador. No primeiro caso, temos um controle maior do circuito que será obtido. No segundo caso, a descrição é muito mais simples.
Como em um projeto de processador a ULA é um elemento que precisa ser o mais rápido possível, na indústria o projeto de ULAs e similares é feita de forma estrutural. Pelo mesmo motivo, o somador paralelo realizado como a concatenação de somadores completos não é normalmente utilizado por ser muito lento para um número de bits maior. Outras estruturas de somadores, um pouco mais complexas, são utilizadas. A lentidão do somador
formado pela concatenação direta de somadores completos, que recebe o nome de somador em onda, é devida a propagação do resultado do bit menos significativo até o bit mais significativo. Esta propagação ocorre na cadeia de bits de carry. Os somadores mas rápidos trabalham com a obtenção do carry para vários bits por vez. Tipos de somadores rápidos comuns são o CLA (Carry Look Ahead) e o CSeA (Carry Select Adder).
Devido ao avanço na tecnologia dos sintetizadores lógicos, atualmente é possível a síntese automática de somadores rápidos.
A utilização dos operadores + e – em Verilog requer alguns cuidados. Em primeiro lugar, o resultado destes operadores em vetores de n bits produz resultados de n + 1 bits. Este bit adicional produzido é o carry do valor mais significativo. Ele pode ser capturado se realizarmos a operação como {C, F} = A + B, onde C é o bit de carry, F é o resultado da soma e A e B são operandos. Deve-se lembrar que as chaves realizam a concatenação de vetores de bits. Outra observação que deve ser considerad é que os vetores de bits representam inteiros sem sinal em Verilog. Este fato é mais relevante quando realizamos simulações.
CIRCUITO LÓGICO
As microoperações lógicas manipulam os bits dos operandos ao tratar cada bit de um registrador como uma variável binária, realizando operações bit-a-bit. Há quatro operações lógicas usadas normalmente – AND, OR, XOR e NOT – das quais as outras podem ser convenientemente derivadas.
Na Figura 4 é mostrado um estágio do circuito lógico. Ele consiste de quatro portas lógicas e um multiplexador 4-para-1, embora a simplificação lógica pudesse levar a uma lógica menos complexa. A tabela da Figura 4b lista as operações lógicas obtidas para cada combinação dos valores de seleção.
UNIDADE LÓGICA/ARITMÉTICA
O circuito lógico pode ser combinado com o circuito aritmético para produzir uma ULA. Um estágio (um bit) desta ULA pode ser visto na Figura 5. O circuito combinado efetua 8 operações aritméticas e 4 lógicas, selecionados através das variáveis S2, S1, S0 e Cin. A Tabela 2 lista as 12 operações da ULA.
Figura 5. Um estágio (um bit) da ULA
O DESLOCADOR
O deslocador opera no valor do barramento B, colocando o resultado na entrada do MUX F. O deslocador básico realiza um dos dois tipos de transformação dos dados: desloca para a direita ou desloca para a esquerda.
O deslocador utilizado é combinacional para permitir a transferência de um registrador fonte e um registrador destino em um só pulso de clock. No pulso de clock o valor do barramento H é carregado em um registrador de destino.
Como vimos no experimento anterior, o deslocador pode ser descrito em Verilog utilizando-se os operadores de deslocamento ou a concatenação de vetores de bits.
REPRESENTAÇÃO DO DATAPATH
Na Figura 6 vemos uma representação do datapath em uma estrutura hierárquica, que reduz a complexidade aparente do datapath. Os dois módulos principais são o banco de registradores e a unidade funcional. O banco de registradores foi implementado no experimento anterior e deverá ser reaproveitado. A unidade funcional deve ser implementada neste experimento.
O banco de registradores é um tipo de memória rápida que permite que uma ou mais palavras sejam lidas e uma ou mais palavras sejam escritas, tudo simultaneamente. As entradas A select, B select e Destination select tornam-se endereços. Endereço A (A address) acessa uma palavra para ser lida e coloca no barramento A data, o endereço B (B address) acessa uma segunda palavra para ser lida e coloca no barramento B data e o endereço D (D
address) acessa uma palavra que deve ser escrita a partir de D data. Todos estes acessos
ocorrem no mesmo ciclo de clock. A entrada Write corresponde ao sinal Load Enable. Quando em 1, o sinal de Write permite que os registradores sejam carregados (alterem seus valores) e quando em zero, evita qualquer alteração. O tamanho do banco de registradores é
2
m
n
, onde m é número de bits de endereços e n é o número de bits por registradores. Utilizaremos neste experimento m = 3 e n = 16, em um total de 8 registradores de 16 bits.A Unidade Funcional (Function Unit) agrupa a ULA e o deslocador. Em conjunto eles realizam as operações apresentadas na Tabela 3. A seleção é feita pela entrada FS, que corresponde as três entradas de seleção G select, H select e MF select. As entradas da unidade funcional são o barramento A e Barramento B e as saídas vão para o MUX D. A unidade também possui os quatro bits de estado (status) V, C, N e Z como saídas adicionais.
Tabela 3. Todas as operações da Unidade Funcional
As variáveis de seleção para o datapath controlam as microoperações executadas no datapath para um dado pulso de clock. As variáveis de seleção controlam os endereços dos dados lidos do banco de registradores, a função realizada pela unidade funcional e os dados que serão carregados no banco de registradores, como também a seleção dos dados externos. Na Figura 9 temos uma versão do datapath com os bits da palavra de controle ligados aos sinais. Há 16 entradas binárias de controle. Os valores combinados especificam a palavra de controle. A palavra de 16 bits é mostrada na Figura 10. Ela consiste de 7 partes, chamadas de campos, cada um designado por um par de letras. Os três campos relativos aos registradores possuem 3 bits cada. Os três bits de DA selecionam um dos oito registros de destino para o resultado das microoperações. Os três bits de AA selecionam um dos oito registradores para o barramento A e a entrada da ULA. Os três bits de BA selecionam um registro de fonte para a entrada 0 do MUX B. O bit MB determina se no barramento B trafega o conteúdo do registrador fonte selecionado ou um valor constante. O campo de 4 bits FS controla a operação da unidade funcional. O campo de um bit MD seleciona a entrada do barramento D como sendo a saída da unidade funcional ou a entrada Data in. O último campo, RW, determina se um registrador vai ser escrito ou não. As funções de todos os códigos de controle são especificados na tabela 4.
Figura 7. Datapath e a Palavra de Controle
Tabela 4. Códigos de controle
Questões de Preparação
1. Descreva em Verilog um somador completo (com carry in e carry out) paralelo de 8 bits de forma estrutural.
2. Descreva em Verilog o somador da questão anterior de forma procedural.
3. Implemente em verilog, usando atribuição contínua, os bits de estado N, Z e V a partir dos valores F e C, que são respectivamente a saída de um somador e o carry out deste. 4. Como podemos garantir que um bloco always produz um circuito puramente
combinacional?
5. Implemente em Verilog a lógica que coloca o valor de todos os registradores de um banco de registradores em zero utilizando um laço for.
6. Implemente em Verilog um decodificador de HEXA para 7 Segmentos. Este decodificador será usado neste e nos próximos laboratórios;
7. Explique como podemos utilizar a diretiva parameter de verilog para definirmos a quantidade de registradores e o tamanho em bits de um datapath. Que vantagens você vê em codificar um módulo parametrizável?
8. Pesquise e responda onde e como são utilizados os blocos always_comb, always_ff e always_latch de SystemVerilog.
UNIVERSIDADE FEDERAL DE CAMPINA GRANDE DEPARAMENTO DE ENGENHARIA ELETRICA
LABORATÓRIO DE ARQUITETURA DE SISTEMAS DIGITAIS ALUNO:
Tarefa de laboratório
Faça a descrição de hardware de um módulo, denominado ULA, que implemente o hardware indicado na figura 1, usando Verilog.
Figura 1 – Módulo ULA.
A “Function Unit” implementa as funções descritas na tabela 4 do guia. O módulo apresenta as seguintes entradas e saídas
Entradas: A_Bus (16 bits) – Entrada de dados do operando A; B_Bus (16 bits) – Entrada de dados do operando B;
Data_in (16 bits) – Entrada de dados de 16 bits que pode ser aplicado diretamente a saída da ULA, sem passar pela unidade de funções;
Constant_in (16 bits) – Entrada de dados de um operando constante; FS (4 bits) – Bits para seleção da operação a ser implementada pela ULA; MB (1 bit) – Bit para selecionar a fonte para o operando B. Se MB=0, seleciona o operando aplicado a entrada “B_Bus”. Se MB=1, seleciona o operando aplicado a entrada “Constant_in”;
MD (1 bit) – Bit Pat selecionar que dado será direcionado para a saída “Bus_D” da ULA. Se MD=0, direciona para a saída o dado gerado pela função implementada pela “Function unit” entre os dois operandos A e B. Se MD=1, direciona para a saída o dado aplicado a entrada “Data_in”.
Saídas: Address_out (16 bits) – Saída correspondente ao dado aplicado a entrada A_Bus da ULA;
Data_out (16 bits) – Saída correspondente ao dado que representa o operando B, a ser aplicado a entrada B da “Function Unit”.
Bus_D (16 bits) – Saída do bloco ULA, onde, a depender do estado do bit MD, representará um dado gerado a partir da operação realizada com os operandos A e B, pela “Function Unit” (MD=0) ou o dado aplicado a entrada “Data_in” (MD=1).
V (1 bit) – Bit de Overflow; C (1 bit) – Bit Carry;
N (1 bit) – Bit de Sinal (0 – positivo) e (1 – negative); Z (1 bit) – Bit zero (0 – resultado ≠ 0) e (1 – resultado = 0);
OBS: A operação de todos os blocos do módulo ULA são assíncronos, ou seja, o comportamento é o de um circuito combinacional, de modo que, o barramento, Bus_D sempre estará repercutindo as alterações dos operandos A e B, caso MD=0, ou o dado da entrada “Data_in”, se MD=1.
Ao instanciar os módulos “ULA” e “Register File” no módulo de teste, “Mod_Teste” faça as conexões entre os mesmos, como indicado a seguir:
ULA Register File
Bus_D → D_Data
A_Bus → A_Data
B_Bus → B_Data
Para visualização de dados usando os displays de 7 segmentos use a descrição de hardware de um decodificador HEXA – 7 Segmentos, como o indicado na figura 2. O mesmo apresenta uma entrada de 4 bits e uma saída de 7 bits. Lembre-se que para ativar o segmento o respectivo bit deverá ser igual a 0 (zero).
Figura 2 – Decodificador HEXA – 7 Segmentos.
Para visualizar os dados das saídas “address_out” e “Data_out” da ULA, utilize 8 instâncias do decodificador HEXA – 7 Segmentos. Realize as conexões indicadas abaixo:
ULA BCD – 7 Segmentos
Address_out[3:0] → IN (conv_0) Address_out[7:4] → IN (conv_1) Address_out[11:8] → IN (conv_2) Address_out[15:12] → IN (conv_3)
Data_out[3:0] → IN (conv_4) Data_out[7:4] → IN (conv_5) Data_out[11:8] → IN (conv_6) Data_out[15:12] → IN (conv_7)
OBS: conv_x, corresponde ao decodificador HEXA – 7 segmentos x, com x=0, 1,2,...,7; Ao implementar as conexões indicadas acima, será obtido o módulo apresentado na figura 7 do guia. Para testar o módulo ULA + Register File, realize as seguintes conexões aos recursos de chaves, led’s e displays disponibilizados no módulo “Mod_Teste”:
Entradas KEY[0] – clock;
SW[0] – RW (equivalente ao bit Load_enable do experimento anterior); SW[3:1] – AA (equivalente aos bits A_Select do experimento anterior); SW[6:4] – BA (equivalente aos bits B_Select do experimento anterior);
SW[9:7] – DA (equivalente aos bits Destination_select do experimento anterior);
SW[13:10] – FS; SW[14] – MB; SW[15] – MD; {4{SW[16]}, 4{SW[6]}, 4{SW[5]}, 4{SW[4]}} – Constant_in; (*) e (**) {4{SW[17]}, 4{SW[3]}, 4{SW[2]}, 4{SW[1]}} – Data_in; (*) e (**) Saídas LEDR[15:0] – Bus_D[15:0];
HEX0 – Out (conv_0); HEX1 – Out (conv_1); HEX2 – Out (conv_2); HEX3 – Out (conv_3);
HEX4 – Out (conv_4); HEX5 – Out (conv_5); HEX6 – Out (conv_6); HEX7 – Out (conv_7);
LEDG[0] – C; LEDG[1] – V; LEDG[2] – N; LEDG[3] – Z;
Para avaliar a funcionalidade da implementação, realize a gravação de dados nos registradores, utilizando os sinais de controle DA e RW. Para definição do dado a ser gravado, estabeleça
MD=1 e use a entrada “Data_in” para estabelecer o valor dos dados. Após a gravação, realize algumas operações com os operandos A e B (selecionados através de AA e BA. No caso do operando B, estabeleça MB=0 para que o operando B venha do B_Bus). A seguir realize algumas operações usando uma constante para o operando B (estabeleça MB=1 e defina um valor na entrada “Constant_in”, usando as chaves SW[16]+SW[6:4]). Os resultados e valores dos operandos podem ser visualizados através dos led’s vermelhos e verdes e display’s de 7 segmentos HEX0 a HEX7. Os valores dos bits de estado C, N, V, e Z poderão ser visualizados através dos LED’s verdes de 0 a 3.
* Operadores de concatenação Considere A = 1’b1, B = 2’b00, C = 2’b10, D =3’b110 Y = {B, C} // Concatenação de B e C. Então Y = 4’b0010 Y = {A, B, C, D, 3’b001} // Resulta em Y = 11’b10010110001 Y = {A, B[0], C[1]} // Resultam em Y = 3’b101 ** Operador de replicação Considere A = 1’b1, B = 2’b00 Y = {4{A}} // Resulta em Y = 4’b1111 Y = {4{A}, 2{B}} // Resultam em Y = 8’b11110000