Biocores - Arquitectura de um acelerador em …...Biocores - Arquitectura de um acelerador em...
Transcript of Biocores - Arquitectura de um acelerador em …...Biocores - Arquitectura de um acelerador em...
Biocores - Arquitectura de um acelerador em hardware para
alinhamento de sequências biológicas
Gilberto André Santinha Rodrigues
Dissertação para obtenção do Grau de Mestre em
Engenharia Electrotécnica e de Computadores
Júri
Presidente: Prof. João Manuel Torres Caldinhas Simões Vaz
Orientador: Prof. Paulo Ferreira Godinho Flores
Vogal: Prof. José António Soares Augusto
Maio 2013
ii
iii
Para os meus pais, irmã e Daniela
iv
v
Agradecimentos Gostaria de começar por agradecer a todos aqueles que me apoiaram nesta dissertação e
também ao longo da minha vida académica.
Ao Professor Paulo Flores, orientador desta dissertação, pela sua ajuda e disponibilidade que
sempre demonstrou.
À Daniela por me ensinar o que é o amor e o que é ter alguém com quem se pode partilhar
tudo e que está sempre presente em qualquer momento, nos atura e dá força para seguirmos
em frente e ultrapassar os obstáculos.
Aos meus pais e irmã, o meu profundo agradecimento pela possibilidade de obter este Curso,
pelo esforço, dedicação, empenho e confiança que sempre demonstraram para que
conseguisse alcançar os meus objectivos profissionais e pessoais.
Gostaria também de recordar o meu avô Gilberto que tanto me apoiou, incentivou e encorajou
para que concluísse a minha Formação Superior.
A todos os meus amigos, em especial Diogo Primor, Francisco Pinto, Jorge Carola, Luís Pragosa
Pedro Morgado e ao Ninho Florido e seus moradores Pedro Ferrão, Pedro Simão e Vasco
Patrício pela amizade, companheirismo e acompanhamento durante o percurso académico.
Por último mas não menos importante, gostaria de agradecer ao José Cabrita, ao Venkatesh
Jakke e a toda a equipa da Coreworks por toda a ajuda e disponibilidade durante a realização
do projecto.
vi
vii
Resumo
O alinhamento de sequências de ADN é um tema bastante atual e que suscita bastante
interesse na comunidade científica. Com o crescimento das bases de dados de sequências
genéticas, torna-se imperativo acelerar todo o processo de alinhamento em hardware de
forma a diminuir o tempo de execução desta tarefa que é muito elevado nos computadores
genéricos atuais.
Para isso recorreu-se à tecnologia da Coreworks, de modo a fazer a implementação do
algoritmo de alinhamento mais comum, o Smith-Waterman. Este algoritmo faz o alinhamento
local de sequências e tem como tarefa computacionalmente mais intensa o preenchimento de
uma matriz N*M com o custo dos vários alinhamentos possíveis, onde N e M são o
comprimento das sequências que se pretendem alinhar.
Neste trabalho foi desenvolvida, na plataforma configurável da Coreworks, constituída por um
acelerador de hardware, Sideworks, e um processador, Fireworks, uma solução completa para
o alinhamento de sequências. Devido aos recursos finitos da plataforma de desenvolvimento
recorreu-se à implementação de um sistema de alinhamento com partições. Este sistema
permite o alinhamento de sequências cujo tamanho excede os recursos disponíveis no
Sideworks realizando o processo em várias fases.
O desempenho do sistema desenvolvido para o cálculo do máximo na plataforma configurável
da Coreworks, permitiu chegar aos 694M Cell Updates Per Second, para uma sequência de 30
elementos na sequência fixa e 120 na sequência variável, com um tempo de execução total do
programa de 15 μs. Nestas condições conseguiu-se estimar um incremento de desempenho de
13 vezes relativamente a uma aplicação otimizada em software referida em [2].
Ao longo do projeto surgiram algumas limitações na implementação do mesmo, relativas ao
número de elementos das sequências a alinhar e tamanho das partições usadas, as quais serão
abordadas na tese.
Palavras Chave
Bioinformatica, alinhamento de sequências, algoritmo Smith-Waterman, aceleração por
hardware, Field Programmable Gate Array, Cell Updates Per Second
viii
ix
Abstract
The DNA sequence alignment is an actual subject with great attention by the reseach
comunity. With the fast growth of the databases it is very important to speed up all the
alignment in hardware in order to reduce execution time of this task that is too large on
existing generic computers.
To execute this project we used the technology of Coreworks in order to implement the most
commun sequence aligment algorithm, the Smith-Waterman. This algorithm makes the local
alignment of sequences and has as the most computational intensive task the filling of an N*M
matrix with the cost of all the possible alignments, where N and M are the length of the
sequences that we are analising.
Along this work it was developed a complete sollution for sequence alignment, using the
Coreworks configurable platform that consists in hardware accelerator, the Sideworks, and
one processor, the Fireworks. Due to limited resources of the development platform it has
been required to implement an alignment system with partions. This system allows us to align
sequences with length superior to the resources of the Sideworks, doing the process in several
phases.
The performance of the system developed to calculate the maximum in the Coreworks
configurable platform, allow us to reach the 694M Cell Updates Per Second, for a fixed
sequence of 30 elements and a variable sequence of 120 elements, with an execution time of
the program of 15 μs. In these conditions it is possible to estimate an increment of
performance of 13 times when compared to a software optimized application referred in [2].
Throughout the project there were a few limitations in the implementation, regarding the
number of elements of the sequences to align and the size of partitions used, which will be
referred in the thesis.
Keywords
Bioinformatics, Sequence Alignment, S-W algorithm, Hardware Acceleration, Field
Programmable Gate Array (FPGA), Cell Updates Per Second (CUPS)
x
xi
Índice Agradecimentos ............................................................................................................................ v
Resumo ......................................................................................................................................... vii
Abstract ......................................................................................................................................... ix
Lista de figuras ............................................................................................................................ xiii
Lista de Tabelas ............................................................................................................................ xv
Lista de abreviações .................................................................................................................... xvi
1 Introdução ............................................................................................................................. 1
1.1 Motivação ...................................................................................................................... 2
1.2 Objectivos ...................................................................................................................... 2
1.3 Organização do Documento .......................................................................................... 3
2 Algoritmos de alinhamento e plataforma de desenvolvimento ........................................... 5
2.1 Introdução ..................................................................................................................... 5
2.1 A sequência de ADN ...................................................................................................... 6
2.1.1 Alinhamento global de sequências ....................................................................... 7
2.1.2 Alinhamento local de sequências ........................................................................ 13
2.1.3 Construção da matriz H ....................................................................................... 17
2.2 Unidade de Processamento da CoreWorks ................................................................ 20
2.2.1 Arquitetura do SideWorks ................................................................................... 22
2.2.2 Arquitetura do FireWorks ................................................................................... 23
2.2.3 Fluxo de projeto da Coreworks ........................................................................... 24
2.3 Conclusão .................................................................................................................... 25
3 Implementação do sistema de alinhamento ...................................................................... 27
3.1 Introdução ................................................................................................................... 27
3.2 A aplicação SSEARCH ................................................................................................... 27
3.2.1 Análise temporal do SSEARCH ............................................................................. 30
3.3 Arquitetura da Unidade Funcional de alinhamento ................................................... 31
3.3.1 Arquitetura da Unidade Funcional de alinhamento para Sideworks .................. 34
3.4 Implementação no Sideworks da arquitetura da Unidade Funcional ......................... 35
3.4.1 Nova Unidade Funcional para o Sideworks: Linear Gap Penalty ........................ 36
3.4.2 Nova Unidade Funcional para o Sideworks: Trigger ........................................... 43
3.4.3 Configuração do Sideworks ................................................................................. 44
3.5 Implementação da arquitetura com partição de sequências ..................................... 49
3.5.1 Partição do cálculo do processamento da matriz H ............................................ 49
xii
3.5.2 Alterações à arquitetura da Unidade Funcional já apresentada ......................... 50
3.5.3 Alterações à Unidade Funcional Trigger ............................................................. 57
3.5.4 Datapath com partição de sequências ................................................................ 57
3.6 Implementação do sistema de alinhamento .............................................................. 61
3.6.1 Simulação em software ....................................................................................... 63
3.6.2 Implementação em Hardware ............................................................................ 70
3.7 Conclusão .................................................................................................................... 71
4 Resultados ........................................................................................................................... 73
4.1 Introdução ................................................................................................................... 73
4.2 Apresentação dos resultados ...................................................................................... 73
4.3 Limitações ................................................................................................................... 74
4.4 Área ocupada .............................................................................................................. 76
4.5 Análise temporal e de desempenho ........................................................................... 76
4.6 Conclusão .................................................................................................................... 79
5 Conclusões........................................................................................................................... 81
5.1 Conclusões Gerais ....................................................................................................... 81
5.2 Trabalhos Futuros........................................................................................................ 82
6 Bibliografia .......................................................................................................................... 85
Anexo 1 ........................................................................................................................................ 87
Anexo 2 ........................................................................................................................................ 89
xiii
Lista de figuras
Figura 2.1 - Métodos de alinhamento de sequências. .................................................................. 5
Figura 2.2 - Matriz de substituição da BLOSUM50 ........................................................................ 6
Figura 2.3 - Bases nitrogenadas. ................................................................................................... 7
Figura 2.4 – Matriz por preencher. ............................................................................................... 8
Figura 2.5 – Matriz completa. ....................................................................................................... 8
Figura 2.6 - Alinhamento das sequências na matriz H. ................................................................. 9
Figura 2.7 - Matriz H inicializada. .................................................................................................. 9
Figura 2.8 - Exemplificação do ponteiro para origem dos elementos. ....................................... 10
Figura 2.9 - Matriz H completamente preenchida. ..................................................................... 12
Figura 2.10 - Matriz com os apontadores de cada elemento. .................................................... 12
Figura 2.11 - Aspeto da matriz H inicialmente. ........................................................................... 14
Figura 2.12 - Inicialização da matriz H. ........................................................................................ 15
Figura 2.13 - Matriz H completamente preenchida. ................................................................... 16
Figura 2.14 - Matriz com os apontadores de cada elemento. .................................................... 16
Figura 2.15 - Diagrama de blocos dos três passos básicos do algoritmo S-W. ........................... 19
Figura 2.16 - Índices da matriz H. ................................................................................................ 19
Figura 2.17 - Matriz H retangular. ............................................................................................... 20
Figura 2.18 - Interface do UPCW. [8] .......................................................................................... 21
Figura 2.19 Arquitetura do SideWorks. [8] ................................................................................. 22
Figura 2.20 Arquitetura do FireWorks. [8] .................................................................................. 24
Figura 3.1 - Excerto do anexo 1 com a primeira parte dos resultados do SSEARCH. .................. 28
Figura 3.2 - Excerto do anexo 1 com a segunda parte dos resultados do SSEARCH. .................. 29
Figura 3.3 - Excerto do anexo 1 com a terceira parte dos resultados do SSEARCH. ................... 29
Figura 3.4 - Excerto do anexo 1 com a quarta parte dos resultados do SSEARCH. ..................... 30
Figura 3.5 - Resultados do profiler. ............................................................................................. 31
Figura 3.6 Comparação de sequências numa cadeia de EP em série. ........................................ 32
Figura 3.7 Arquitetura de um elemento de processamento para uma penalização de gap linear.
..................................................................................................................................................... 33
Figura 3.8 Nova arquitetura de um elemento de processamento para uma penalização de gap
linear. .......................................................................................................................................... 34
Figura 3.9 Cabeçalho da UF sidefu_linear_gap_penalty_sim. .................................................... 36
Figura 3.10 Definição do número de bits das entradas da UF. ................................................... 37
Figura 3.11 Definição e inicialização das saídas do EP. ............................................................... 38
Figura 3.12 - Configuração da LUT. ............................................................................................. 39
Figura 3.13 - Descrição do cálculo do score. ............................................................................... 40
Figura 3.14 - Calculo do máximo e atribuições finais. ................................................................. 40
Figura 3.15 - Ficheiro XML. .......................................................................................................... 41
Figura 3.16 - LUT em VHDL. ......................................................................................................... 42
Figura 3.17 - Comparadores, Flip-Flops e subtrator de alpha. .................................................... 43
Figura 3.18 - UF Trigger: lado esquerdo código VHDL e lado direito código c. ........................... 44
Figura 3.19 - Time Unit e registos da datapath. .......................................................................... 45
Figura 3.20 - Declaração dos EPs e memórias. ............................................................................ 46
Figura 3.21 - Descrição da datapath............................................................................................ 46
xiv
Figura 3.22 - Definição da Linear_gap_penalty e da escrita dos máximos. ................................ 47
Figura 3.23 - Datapath, escrita e print das memorias. ................................................................ 48
Figura 3.24 - Arquitetura da datapath. ....................................................................................... 48
Figura 3.25 - Processamento particionado [11]. ......................................................................... 49
Figura 3.26 - EPs numa matriz com partição das sequências. .................................................... 51
Figura 3.27 - Diagrama temporal dos sinais de controlo. ........................................................... 52
Figura 3.28 - MUXs criados para seleção das entradas dos EPs. ................................................. 52
Figura 3.29 - Arquitetura com alterações para partição de sequências. .................................... 53
Figura 3.30 - Código alterado para partição de sequências. ....................................................... 54
Figura 3.31 - Ficheiro XML com novas entradas e saídas. ........................................................... 55
Figura 3.32 - Registos triggerpl, trigger2 e trigger4. ................................................................... 56
Figura 3.33 - MUXs implementados em VHDL. ........................................................................... 56
Figura 3.34 - UF Trigger com o novo contador implementado. .................................................. 57
Figura 3.35 - Registos para armazenamento e leitura dos dados da subiteração. ..................... 58
Figura 3.36 - Definições adicionais. ............................................................................................. 58
Figura 3.37 - Datapath: atribuições aos registos, triggers e BAUs. ............................................. 59
Figura 3.38 - Cabeçalhos das UFs linear_gap_penalty. ............................................................... 60
Figura 3.39 - Armazenamento dos máximos e dos dados das partições. ................................... 60
Figura 3.40 - Arquitetura da datapath com partição de sequências implementada. ................. 61
Figura 3.41 - Exemplo de macros para as várias configurações. ................................................. 62
Figura 3.42 - Fluxograma do funcionamento do programa ........................................................ 63
Figura 3.43 – Matriz de substituição para o ADN. ...................................................................... 64
Figura 3.44 - Exemplo de ficheiros com sequências fixas em cima e sequências varáveis em
baixo. ........................................................................................................................................... 64
Figura 3.45 – Excerto de código da simulação; partições das sequências fixa e variável. .......... 65
Figura 3.46 - Inicialização dos vectores da LUT. .......................................................................... 66
Figura 3.47 - Excerto de código da simulação; funções para carregamento de memórias e
registos. ....................................................................................................................................... 66
Figura 3.48 - Carregamento dos vectores da LUT para a datapath. ........................................... 67
Figura 3.49 - Função "SV_init" . ................................................................................................... 67
Figura 3.50 - Matriz com todas as linhas e colunas para se fazerem carregamentos. ............... 68
Figura 3.51 - Funções de carregamento e armazenamento de dados e controlo da datapath. . 68
Figura 3.52 - Armazenamento dos máximos e dados da subiteração. ....................................... 69
Figura 3.53 – Excerto de código da implementação em hardware. ........................................... 70
Figura 3.54 - Exemplo do envio de vectores do hardware para o pc. ......................................... 71
Figura 4.1 - Comunicação entre o computador e a FPGA. .......................................................... 73
Figura 4.2 - Resultados. ............................................................................................................... 74
xv
Lista de Tabelas
Tabela 1- Programas do FASTA Program Package divididos por grupos. [9] .............................. 27
Tabela 2 - Limites das sequências. .............................................................................................. 75
Tabela 3 - Ocupação para o projecto com várias configurações de EPs. .................................... 76
Tabela 4 - Tempos de carregamentos/armazenamento. ............................................................ 77
Tabela 5 - Performance da arquitetura para vários tipos de sequências. .................................. 78
Tabela 6 - Performance da arquitetura para vários tipos de sequências com 168 EPs. ............. 79
xvi
Lista de abreviaturas
ADN - ácido desoxirribonucleico
ARN - ácido ribonucleico
CUPS - Cell Updates Per Second
EP – Elemento de processamento
FIFO - First In First Ou
FPGA - Field Programmable Gate Array
FU – Functional unit
LUT - lookup table
MUX - Multiplexer
N-W - Needleman - Wunch
S-W – Smith-Waterman
TU – Time Unit
UF – Unidade funcional
UPCW - Unidade de processamento da CoreWorks
1
1 Introdução
Bioinformática, tal como o próprio nome indica, é um campo que combina a biologia
molecular e a informática. Pode ser definida como a área que faz as ferramentas
computacionais, bases de dados e métodos usados na pesquisa do genoma. O alinhamento de
sequências é uma das subáreas mais importantes e a mais usada da bioinformática. Esta
subárea consiste na organização de duas ou mais sequências de ácido desoxirribonucleico
(ADN), ácido ribonucleico (ARN) ou proteínas de forma a encontrar regiões de similaridade [1].
Estas regiões com estruturas similares em proteínas diferentes desempenham, também, uma
função semelhante, o que tem inúmeras implicações, pois se uma das sequências já é
conhecida, as informações sobre ela podem ser aplicadas a todas as outras similares. Isto tem
grandes aplicações, por exemplo no diagnóstico preventivo de doenças, na indústria da
medicação, no estudo do desenvolvimento evolucionário e na história das espécies [2].
Todos os anos assistimos a um crescimento exponencial de bases de dados de sequências
genéticas. As sequências de ADN, ARN ou proteínas são representadas como cadeias de
caracteres, cada um simbolizando um nucleótido (ADN/ARN) ou um aminoácido [3]. Tomando
como exemplo concreto as sequências de ADN, das quais se vai falar ao longo do trabalho,
tendo estas em média entre 500-800 bases (caracteres) de comprimento, o processo de
alinhamento torna-se práticamente impossível para um ser humano [4].
Para se fazer o alinhamento de sequências foram desenvolvidos vários algoritmos, que se
podem dividir em globais e locais. Destes algoritmos destacam-se um proposto por Needleman
e Wunsch em 1970, algoritmo N-W, e outro proposto por Smith e Waterman, em 1981, o
algoritmo S-W. Ambos os algoritmos são semelhantes no seu desenvolvimento, recorrendo a
programação dinâmica para a resolução do problema do alinhamento, preenchendo uma
matriz bidimensional avançando do canto superior esquerdo para o canto inferior direito. De
seguida, faz-se o caminho inverso, começando num ponto específico, no canto inferior direito,
e percorrendo inversamente a matriz, encontrando-se assim sempre o melhor alinhamento,
ou seja, o alinhamento ótimo.
2
1.1 Motivação
O aparecimento de novas tecnologias (mais baratas e eficientes) para sequenciar ácidos
nucleicos e proteínas conduziu a um colossal crescimento nas bases de dados de sequências,
pois grandes quantidades de informação são produzidas em todo o mundo [5].
Este crescimento e a necessidade de alinhar cada vez mais sequências (e eventualmente
maiores) em tempo útil, levou a uma procura de técnicas para aumentar a velocidade de todo
o processo de alinhamento. Por se tratar de uma tarefa computacionalmente muito intensiva,
os atuais computadores são ineficazes em obter alinhamentos ótimos em tempo útil. Assim,
apareceram algoritmos de alinhamento de sequências baseados em heurísticas, que podem
não obter o melhor alinhamento mas que são os mais adequados para implementação em
software, sendo desenvolvidos e usados para reduzir o tempo de execução dos alinhamentos
nos computadores tradicionais [2].
Outra alternativa para melhorar o desempenho dos algoritmos de alinhamento consiste na sua
aceleração por hardware. No alinhamento de sequências, tanto para o algoritmo S-W como
para o N-W, a tarefa computacionalmente mais intensa é o preenchimento de uma matriz
N*M com o custo dos vários alinhamentos possíveis, onde N e M são o comprimento das
sequências que pretendem alinhar. Como era de esperar, a complexidade temporal é O(NM),
ou seja, quanto maiores forem as sequências, maior será a matriz produzida o que aumentará
o tempo necessário para a sua construção e posterior análise, fase designada por traceback. É
assim conveniente acelerar a parte crítica do algoritmo em hardware de forma a que o tempo
de execução seja significativamente reduzido.
Para a aceleração por hardware foi escolhida a tecnologia da Coreworks que entre outras
vantagens, permite gerir de forma rápida várias configurações de hardware, consegue atingir a
performance de hardware dedicado mas mantendo a flexibilidade do software e já contém
memórias embebidas e unidades funcionais, desenhadas e verificadas.
1.2 Objectivos
O objectivo deste trabalho será construir um sistema de alinhamento completo recorrendo a
uma arquitetura de hardware que permita acelerar os algoritmos de alinhamento de
sequências, algoritmo N-W ou o S-W, pois estes algoritmos permitem obter um alinhamento
ótimo.
Para isso pretendemos acelerar a parte mais crítica do algoritmo na arquitetura da Coreworks,
esperando conseguir assim um bom desempenho e melhorar os tempos de execução globais.
3
Neste sentido, vai ser desenvolvido também todo o software necessário para que seja possível
alinhar sequências com a ajuda do Sideworks. Desde os ficheiros necessários para teste no
ambiente de desenvolvimento da Coreworks, até aos ficheiros para programação do
Sideworks. Vai ser também desenvolvido a fase de traceback, obtendo-se assim uma aplicação
funcional, que faz o alinhamento completo das sequências.
Pretende-se assim desenvolver na plataforma configurável da Coreworks uma solução
completa para o alinhamento de sequências, incluindo o traceback.
1.3 Organização do Documento
Este trabalho encontra-se dividido em cinco capítulos, sendo que no segundo é feito um
levantamento dos algoritmos de alinhamento de sequências, tanto globais como locais, bem
como a apresentação da arquitetura da Coreworks que irá ser utilizada na execução do
projecto.
O terceiro capítulo irá apresentar todo o desenvolvimento do projecto. Será apresentado um
estudo de um software que faz o alinhamento de sequências e seguidamente uma arquitetura
de referencia para o projeto. Depois vai ser apresentado como implementar e integrar a
arquitetura desejada no sistema de processamento da Coreworks.
Os resultados serão apresentados no capítulo 4, juntamente com as análises de área, tempo
de execução e de performance no global. São ainda apresentadas neste capítulo as limitações
encontradas da arquitetura criada.
Por último seram apresentadas as conclusões e os trabalhos futuros no capítulo 5.
4
5
2 Algoritmos de alinhamento e plataforma de
desenvolvimento
2.1 Introdução
Os algoritmos de alinhamento de sequências podem dividir-se em globais e locais, sendo que
na figura 2.1 se apresentam alguns deles. Os globais tentam alinhar tantos caracteres quanto
possível desde o início até ao fim de uma dada sequência. É o caso do algoritmo N-W
anteriormente mencionado e do Dotplot referido na secção 2.2.1.
Os métodos locais tentam identificar pequenas áreas similares entre as duas sequências, tal
como faz o algoritmo S-W, o FASTA ou o BLAST, entre outros.
Figura 2.1 - Métodos de alinhamento de sequências.
As sequências biológicas, entre as quais o ADN, evoluem através dos processos de mutação,
seleção e derivações aleatórias. O processo de mutação manifesta-se através de três
processos: a substituição, a inserção e a eliminação. Na substituição, um nucleótido numa
sequência é substituído por outro diferente, a inserção insere um novo elemento numa
sequência, enquanto na eliminação um nucleótido é eliminado da sequência. Estes dois
últimos são também referidos como gaps [2].
O alinhamento de sequências pode ser quantificado através de um valor, o qual é obtido pela
soma dos termos iguais de cada uma das sequências e também dos possíveis gaps, quando o
alinhamento está a ser feito. Os valores que vão ser somados, no caso de uma igualdade entre
as sequências que estão a ser analisadas, são obtidos por modelos probabilísticos e guardados
em matrizes designadas por matrizes de score ou de substituição [2]. Estas têm os valores para
6
uma igualdade de elementos das sequências, a que correspondem os máximos da matriz e
também os valores para o caso em que as sequências são diferentes. Um exemplo de uma
matriz de substituição pode ser encontrado na figura 2.2, onde é apresentada uma matriz de
20x20 aminoácidos.
Figura 2.2 - Matriz de substituição da BLOSUM50
Como foi mencionado anteriormente, existem vários algoritmos para fazer o alinhamento de
sequências, alguns dos quais serão apresentados nas secções seguintes. Será igualmente feita
uma breve introdução ao ADN.
2.2 A sequência de ADN
O ADN juntamente com o ARN e com ajuda de outras espécies bioquímicas, como enzimas, é
responsável pela hereditariedade, isto é, armazena na sua constituição as características físicas
e metabólicas (e segundo alguns autores, algumas características psicológicas). Nele são
guardadas todas a informações genéticas, como as instruções para a síntese de proteínas [7].
O ADN e o ARN são designados de ácidos nucleicos e são assim chamados pelo facto de terem
sido descobertos primeiramente no núcleo das células.
Ambos são constituídos por dezenas, centenas ou até milhões de moléculas denominadas
nucleotidos, unidas entre si como elos de uma corrente. Por tal motivo, os ácidos nucléicos são
classificados como polinucleotídeos. Quanto às bases nitrogenadas, tanto o ADN quanto o
ARN, apresentam adenina (A), guanina (purinas) (G) e citosina (pirimidina)(C). Somente o ADN,
todavia, contém timina (pirimidina) (T), e somente o ARN contém uracilo (U).
7
Figura 2.3 - Bases nitrogenadas.
Foi todo este conjunto de resultados que possibilitaram Watson e Crick demonstrassem a
representação em hélice dupla. Quanto às bases, o que podemos verificar é que quando numa
das cadeias da hélice dupla há uma timina, esta só emparelha com um nucleotidos de adenina
da cadeia adjacente. Isto é conseguido através de duas ligações designadas pontes de
hidrogénio. O mesmo se pode dizer em relação à guanina e à citosina, mas neste caso a ligação
estabelece-se à custa de três ligações de hidrogénio [7].
Quando há divisão há duplicação de ADN e cada nova célula recebe uma quantidade idêntica
de ADN. A partir de uma molécula molde vão originar-se moléculas com uma constituição
idêntica.
2.2.1 Alinhamento global de sequências
Tal como o nome sugere, o alinhamento global tem em consideração todos os elementos das
duas sequências que se estão a alinhar, ou seja, tenta alinhar o máximo de elementos possível
do princípio ao fim das sequências.
8
2.2.1.1 Dotplot
O método mais básico para comparar duas sequências é conhecido como dotplot. Este é um
método visual onde as sequências a serem comparadas são colocadas nas margens de uma
matriz. [1] Tomando como exemplo as sequências A (actgg) e B (actgg) podemos obter uma
matriz, tal como está representado na figura 2.4.
a c t g g
a
c
t
g
g
Figura 2.4 – Matriz por preencher.
Para preencher a matriz, depois de esta estar construída, basta colocar um ponto em cada
intersecção de linha e coluna que têm a mesma letra nas duas sequências, ou seja, onde elas
são iguais. Assim sendo, é fácil concluir que linhas diagonais indicam regiões onde as
sequências são similares. A figura 2.5 apresenta a matriz completamente preenchida onde se
pode ver a linha diagonal, indicando a similaridade entre as sequências.
a c t g g
a x
c x
t x
g x x
g x x
Figura 2.5 – Matriz completa.
A complexidade em termos de tempo e espaço é O(MN), onde M e N são o comprimento das
sequências a serem comparadas [1]. Este método serve apenas para referência e de estudo
prévio para se perceber o que vem de seguida.
9
2.2.1.2 Algoritmo N-W
O algoritmo proposto por Needleman e Wunch é um algoritmo de programação dinâmica que
faz o alinhamento global entre duas sequências, A = A1 A2 … Ai …AM de comprimento M e B = B1
B2 … Bi … BN de comprimento N. O algoritmo constrói então uma matriz H de scores, onde cada
elemento H(i, j) representa o melhor alinhamento no segmento das sequências alinhadas até
aí, A1 A2 … Ai e B1 B2 … Bi [2].
Tomando como exemplo concreto a sequência A = P A W H E A E e a sequência B = H E A G W A
G H E E, ficamos com matriz H disposta da seguinte forma:
H E A G W A G H E E
P
A
W
H
E
A
E Figura 2.6 - Alinhamento das sequências na matriz H.
Para ser possível construir a matriz, é necessário inicializá-la. Assim é necessário definir os
valores para H(0, 0), H(i, 0) para i= 1, 2, … M e H(0, j) para j = 1, 2, … N. A posição H(0, 0) é o
valor a partir do qual todos os outros vão ser calculados, não representando nenhum
alinhamento. Assim sendo, o valor desta célula deverá ser sempre zero. Quanto a H(i, 0) e
H(0,j) representam gaps de uma em relação à outra, ou seja, H(i, 0) representa um elemento
de A com todos os gaps em relação a B e ao contrário, H(0,j) representa um elemento de B
com todos os gaps em relação a A. Deste modo, H(i, 0) = -i * d e H(0, j) = -j * d. Considerando
d= 8, ficamos com uma matriz H inicializada como se mostra na figura 2.6 [2].
H E A G W A G H E E
0 -8 -16 -24 -32 -40 -48 -56 -64 -72 -80
P -8
A -16
W -24
H -32
E -40
A -48
E -56
Figura 2.7 - Matriz H inicializada.
10
Após a inicialização na primeira fase do algoritmo, a matriz H é construída recursivamente de
acordo com a seguinte regra:
{
(1)
onde d é a penalização de gap e é valor obtido na matriz de substituição para os
elementos Ai e Bj.
A equação (1) mostra-nos que uma vez que tenhamos o score calculado entre: , A1 A2 … Ai-1 e
B1 B2 … Bj-1 isto é H(i – 1, j – 1) , A1 A2 … Ai-1 e B1 B2 … Bj isto é H(i – 1, j) e , A1 A2 … Ai e B1
B2 … Bi-1 isto é H(i, j – 1) o melhor alinhamento possível entre A1 A2 … Ai e B1 B2 … Bi ou seja
H(i, j), é máximo de três alternativas representadas na equação (1) e que significam
respectivamente:
o Um alinhamento entre Ai e Bj, neste caso temos um score dado por .
o Um alinhamento entre Ai e um gap em B, dado pela equação , sendo d a
penalização de gap, tal como referido anteriormente.
o A ultima alternativa, , é um alinhamento entre Bj e um gap [2].
Sabendo como se inicializa a matriz e como se preenche a mesma, falta apenas saber como se
faz o último passo, o traceback. Este é simples no algoritmo N-W e é ele que nos vai permitir
encontrar o melhor alinhamento.
Para fazer o traceback necessitamos de guardar informação sobre a origem de cada um dos
elementos, para que seja possível percorrer do fim para o início a matriz de forma a
encontrarmos o melhor alinhamento. Este processo pode ser construído, guardando um
ponteiro, em cada célula, para o elemento que lhe deu origem, ou seja, o da esquerda, o de
cima ou o superior esquerdo.
Em geral, se a célula (i, j) tiver origem em (i – 1, j – 1) dever-se- á adicionar à sequência o par
de entradas Ai, Bj. Se, por outro lado, for proveniente de (i – 1, j), adiciona-se apenas Ai ao
alinhamento A e um gap ao alinhamento B. Por último, se a célula (i, j) tiver sido produzia por
(i, j – 1) adicionamos Bj ao alinhamento B e um gap ao alinhamento A [2].
H(i – 1, j – 1) H(i, j – 1)
H(i – 1, j) H(i, j)
Figura 2.8 - Exemplificação do ponteiro para origem dos elementos.
11
Assim, partindo do último elemento da matriz, vamos percorrendo inversamente a mesma,
indo sempre para o elemento que deu origem àquele onde estamos. Desta forma, obtém-se o
melhor alinhamento para as sequências que se estão a estudar.
Para que todo este processo seja facilmente perceptível, incluindo a visualização do algoritmo,
vai ser construído um exemplo desde o início, utilizando as sequências anteriormente
apresentadas.
A inicialização da matriz é então feita, tal como descrito anteriormente, e ficamos com o
mesmo que na figura 2.7. Seguidamente é necessário começar a preencher a matriz, para isso
basta utilizar a equação (1).
Relativamente à primeira linha da equação e sabendo que =0, necessitamos, ainda, de
saber . Para isso ter-se-á de consultar a matriz de substituição, figura 2.2. Como A = P e B
= H, constata-se que o valor pretendido é -2. O valor da penalização por gap é definido como
oito, ou seja, d = 8. Assim, consultando a figura 2.7, podemos verificar que = = -8.
Fazendo as devidas substituições na equação (1), obtemos os seguintes resultados:
{
Assim, concluiu-se que valor, score, máximo é -2 valor este que será que será atribuído ao
elemento. Vai, igualmente, ser guardado um ponteiro para o elemento que deu origem a este
valor, para que posteriormente possa ser usado no traceback. Deste modo, deverá ser
guardada a informação relativa a H(0, 0).
Depois de observado este exemplo, o modo de funcionamento será o mesmo para todas as
células. A matriz completa é apresentada na figura 2.9. Analisando o modo de preenchimento
da matriz constatamos que pela dependência de dados do algoritmo, a matriz deve ser
construída na anti-diagonal. Este aspeto será apresentado com mais detalhe, posteriormente,
no capítulo 2.1.3.
12
H E A G W A G H E E
0 -8 -16 -24 -32 -40 -48 -56 -64 -72 -80
P -8 -2 -9 -17 -25 -33 -42 -49 -57 -65 -73
A -16 -10 -3 -4 -12 -20 -28 -36 -44 -52 -60
W -24 -18 -11 -6 -7 -15 -5 -13 -21 -29 -37
H -32 -14 -18 -13 -8 -9 -13 -7 -3 -11 -19
E -40 -22 -8 -16 -16 -9 -12 -15 -7 3 -5
A -48 -30 -16 -3 -11 -11 -12 -12 -15 -5 2
E -56 -38 -24 -11 -6 -12 -14 -15 -12 -9 1
Figura 2.9 - Matriz H completamente preenchida.
Tal como referido anteriormente, para se fazer o traceback, necessitamos de ter informações
sobre a origem de cada célula, ou seja, para cada um dos elementos foi reservado um ponteiro
indicando a sua proveniência. Na figura 2.10 ilustra-se a matriz completa com os ponteiros de
cada elemento.
Figura 2.10 - Matriz com os apontadores de cada elemento.
Com o presente resultado, vamos iniciar o processo de traceback. Este último passo, começa
na última célula da matriz, H(M, N) = H(7, 10). Observando a figura 2.10, constatamos que este
elemento provem de H(6, 9). Uma vez que é derivado de (i – 1, j – 1), adiciona-se A7 e B10, ou
seja, E e E aos alinhamentos. Continuando com o traceback, podemos verificar que o elemento
H(6, 9) tem a sua origem em H(5, 9). Deste modo, derivando este elemento de (i–1,j) e
adicionando-se A6 e um gap obtém-se o seguinte:
13
Melhor alinhamento global: - E A E
Continuando com o traceback e a aplicar o mesmo método a todos os elementos da matriz,
aproximamo-nos da célula H(0, 0), ou seja, o início de H. Assim, o melhor alinhamento possível
cujo score é 1 ( valor da célula H(7, 10)) será:
Melhor alinhamento global: H E A G A W G H E - E - - P - A W - H E A E
Quanto a complexidades, para a inicialização, é fácil de perceber que é simplesmente O(M +
N), correspondendo, apenas, ao preenchimento de uma linha e de uma coluna. Para o
preenchimento da matriz, tal como foi referido anteriormente, necessitamos de conhecer três
vizinhos do elemento que estamos a computar, o da esquerda, o de cima e o da diagonal
superior esquerda, sendo calculados de seguida os três scores baseados nos vizinhos e
assignado o valor máximo ao elemento. Isto é, uma operação de tempo constante, pelo que o
preenchimento da matriz H tem uma complexidade temporal igual ao número de elementos
da mesma, ou seja, O(M N). O último passo, o traceback, como podemos mover um máximo
de N linhas e M colunas, a complexidade será O(M + N).
Deste modo a complexidade temporal total do algoritmo é dada por: O(M + N) + O(M N) + O(M
+ N), logo será O(M N). A complexidade espacial da matriz é simplesmente o número de
entradas da mesma, ou seja, O(M N) [1].
2.2.2 Alinhamento local de sequências
Contrariamente aos algoritmos globais, que têm em consideração todos os elementos das
sequências, os algoritmos locais encontram o melhor alinhamento possível entre duas
sequências, ou seja, encontram regiões similares entre as sequências. Estes algoritmos são
mais úteis, principalmente quando se estão a comparar sequências longas, onde os algoritmos
locais podem levar a uma correlação baixa e até mesmo a erros. É também útil utilizar
algoritmos locais quando se pretende comparar duas sequências muito diferentes, mas que
não tenham a mesma origem e onde apenas uma parte delas preserva a similaridade [2].
14
2.2.2.1 Algoritmo S-W
O algoritmo S-W foi desenvolvido por Smith e Waterman e tal como o N-W, é um algoritmo de
programação dinâmica. Considerando as sequências A = { A G G T A C } e B = { C A G C G T T G },
este algoritmo encontra o melhor alinhamento entre subsequências de A e B, isto é, o melhor
alinhamento local das sequências. Tal como no algoritmo anterior ( N-W ), as sequências são
colocadas nas linhas e colunas da matriz H, como se pode observar na figura 2.12.
C A G C G T T G
A
G
G
T
A
C Figura 2.11 - Aspeto da matriz H inicialmente.
O algoritmo é muito semelhante ao N-W apresentado anteriormente, diferindo deste apenas
em dois pontos, a equação recursiva e no traceback [2].
Para o algoritmo N-W tínhamos a equação (1) de onde derivava a matriz H, agora, para o
algoritmo S-W, temos a equação (2).
{
(2)
Confrontando as duas equações, chegamos à conclusão que o termo zero é adicionado à
mesma, indicando que é melhor iniciar um novo alinhamento se até ao momento se tiver um
resultado negativo. Ou seja, ao contrário do algoritmo N-W, onde poderíamos ter valores
negativos, pois estávamos a fazer o alinhamento de toda a matriz, aqui tal não acontece, pois
o que estamos a fazer é alinhar localmente, logo se um valor é negativo este passa a zero.
Visto que o resultado do mínimo valor possível é zero, a primeira linha e primeira coluna
devem ser iniciadas com zeros. Ou seja, H (i, 0) = 0 e H (0, j) = 0, em vez de –i*d e -J*d,
respectivamente, como acontecia no algoritmo N-W. Assim, podemos ver a inicialização da
matriz H com as sequências A e B na figura 2.12 [2].
15
C A G C G T T G
0 0 0 0 0 0 0 0 0
A 0
G 0
G 0
T 0
A 0
C 0 Figura 2.12 - Inicialização da matriz H.
Quanto ao traceback, uma vez que com este algoritmo pretendemos um alinhamento local, o
score máximo pode estar localizado em qualquer sítio na matriz. Assim sendo, é necessário
encontrar a posição do valor máximo da matriz H e então iniciar o processo de traceback a
partir dessa posição. Todo o processo de traceback é igual ao N-W, com a excepção do já
referido. Este procedimento finaliza caso se encontre um zero, o que pode acontecer, mais
uma vez, em qualquer sítio da matriz H.
Tal como para o algoritmo N-W, vamos construir um exemplo desde a raiz para se perceber
melhor o funcionamento do algoritmo. Para este exemplo vamos definir a penalização de gap
como dois, ou seja, d = 2 e como:
{
(3)
Fazendo, então, a inicialização da matriz H como descrito anteriormente, ficamos com esta
como mostra a figura 2.12. De seguida vamos aplicar a equação (2), começando pelo elemento
H (1, 1).
Para a equação , percebe-se, através da figura 2.12 que H (0, 0) = 0 e aplicando a
equação (3) a , como Ai = A é diferente de Bj = C, retiramos que o seu valor é -1. Olhando
de novo para a figura 2.12 constata-se que = = 0. Fazendo as substituições na equação
acima apresentada, verificamos o seguinte:
{
( )
16
Pode-se, então, concluir que o valor máximo será então zero e será esse o valor a ser atribuído
ao elemento H (1, 1). No caso do algoritmo S-W também vai ser necessário guardar os
ponteiros com a informação de onde provém o elemento em questão, assim como acontecia
no algoritmo N-W. Neste caso específico do elemento H (1, 1) não é guardado nenhum
ponteiro para nenhum dos seus vizinhos, pois o valor máximo não provém de qualquer um
deles.
Tal como no algoritmo N-W a matriz é preenchida na anti-diagonal sendo tal facto explicado
no capitulo 2.4.
Continuando a aplicar o algoritmo como no exemplo apresentado em cima, obtemos uma
matriz com todas as entradas preenchidas tal como se apresenta na figura 2.13.
C A G C G T T G
0 0 0 0 0 0 0 0 0
A 0 0 2 0 0 0 0 0 0
G 0 0 0 4 2 2 0 0 2
G 0 0 0 2 3 4 2 0 2
T 0 0 0 0 1 2 6 4 2
A 0 0 2 0 0 0 4 5 3
C 0 2 0 1 2 0 2 3 4 Figura 2.13 - Matriz H completamente preenchida.
Como já foi referido, para se fazer o traceback, é imprescindível termos a informação da
origem de cada uma das células, ou seja, para cada elemento, foi guardado um ponteiro
indicando a sua proveniência. Assim, na figura abaixo exposta, está patente a matriz com os
ponteiros de cada um dos elementos.
Figura 2.14 - Matriz com os apontadores de cada elemento.
17
Uma vez que já possuímos a matriz completamente preenchida e com os apontadores de cada
elemento é o momento de começar o processo de traceback. A partir do elemento mais
elevado que tem o valor 6 em H (4, 6), tal como se pode verificar na figura 2.14, dá-se então
início ao traceback. De seguida, basta ver qual o elemento que dá origem a H (4, 6) olhando
para o apontador guardado previamente. Assim, chega-se à conclusão que este provém de H
(3, 5). Continuando a retroceder na matriz H, chegamos a H (2, 4). Até agora o alinhamento
tem o seguinte aspecto:
Melhor alinhamento local: G T T T
O traceback continua a ser aplicado até se encontrar um zero. Na figura 2.14 podemos verificar
alguns elementos a negrito, são os que têm o melhor alinhamento local. No final, o melhor
alinhamento local para estas duas sequências aplicando o algoritmo S-W será:
Melhor alinhamento local: A G C G T A G - G T
De referir que o traceback nem sempre é realizado existindo um limite mínimo a baixo do qual
não é feito o alinhamento. Assim apenas se irá despender tempo a calcular o máximo da
matriz, o que será feito em hardware, poupando-se depois o tempo de traceback das
sequências que não interessa alinhar.
Visto que os algoritmos S-W e N-W são semelhantes, as suas complexidades são igualmente
equivalentes, com a excepção do traceback. Assim sendo, temos, tal como no N-W, uma
complexidade de O(M + N) para a inicialização e O(M N) para o preenchimento da matriz.
Quanto ao traceback, uma vez que o máximo pode estar em qualquer sítio, tem uma
complexidade de O(M N), pois no pior dos casos será necessário iniciar este processo na última
posição.
Deste modo, a complexidade do algoritmo será O(M + N) + O(M N) + O(M N) por conseguinte
a complexidade temporal do algoritmo é O(M N). Quanto à espacial permanece inalterada em
relação ao algoritmo N-W, ou seja, é O(M N) [1].
2.2.3 Construção da matriz H
Uma vez analisados os algoritmos anteriores, teremos de saber onde despendem mais tempo
durante a sua execução e formas de os acelerar. A aceleração por hardware é o uso de
hardware especializado para desempenhar uma tarefa de forma mais rápida que num pc
18
genérico. O hardware que realiza esta aceleração, quando está separado do CPU, é designado
por acelerador de hardware.
Quer no caso do algoritmo N-W, quer no caso do algoritmo S-W, quando são executados num
computador, a maioria do tempo despendido é para o preenchimento da matriz H. Isto é,
neste passo é gasto mais de 95% do tempo total de execução.
Particularizando o caso do algoritmo S-W, este ocupa cerca de 98,6% do tempo a calcular os
elementos da matriz H [1]. Um sistema acelerado em hardware podia acelerar todo o
processo, calculando vários elementos da matriz H em paralelo para, deste modo, reduzir o
tempo gasto na procura pelo máximo. Este algoritmo, quando acelerado, apresenta algumas
vantagens como o facto de utilizar uma grande quantidade de tempo para realizar a mesma
tarefa, logo é ótimo para o uso da paralelização. Outra vantagem centra-se no facto de
oferecer um grande nível de eficiência de instruções, isto é, as sequências de caracteres
podem ser representadas com representações de 2 a 5 bits. Um outro aspecto é que todas as
computações podem ser implementadas usando inteiros de 24 bits [1].
Na figura 2.15 é representado um diagrama de blocos com os vários passos do algoritmo S-W e
a respetiva complexidade.
19
Figura 2.15 - Diagrama de blocos dos três passos básicos do algoritmo S-W.
A paralelização do preenchimento da matriz não é um processo fácil devido à dependência de
dados. Cada célula Hi, j depende de três vizinhos Hi-1, j-1, Hi-1, j e Hi, j-1, por sua vez, cada uma
destas depende de outros três vizinhos. Tomando como exemplo uma matriz quadrada, NxN,
ou seja, com ambas as sequências com comprimento N, podemos observar que é possível
calcular H1, 3, H2, 2 e H3, 1 em simultâneo, uma vez que, cada uma destas células não está na
zona de dependência das outras [1]. A figura 2.16 apresenta a matriz com os índices de cada
uma das células para um N=4 . Como podemos verificar, depois de analisada a figura, os
elementos possíveis de calcular em simultâneo são os que se encontram nas anti-diagonais da
matriz.
Y=1 ... Y=4
x=1 1 1 1 2 1 3 1 4 ... 2 1 2 2 2 3 2 4 3 1 3 2 3 3 3 4 x=4 4 1 4 2 4 3 4 4
Figura 2.16 - Índices da matriz H.
20
Assim, considerando a metade superior esquerda da matriz, a anti-diagonal superior, as células
passíveis de serem calculadas em simultâneo são dadas por Hx-y+1, y para 1 ≤ y ≤ x e x ≤ N. Por
exemplo, se considerarmos x=3, y tomará os valores 1 ≤ y ≤ 3, sendo que as células possíveis
de calcular são as que se encontram a cinzento na figura 2.18.
Por conseguinte o número máximo de células a ser calculado em simultâneo é N, ou seja, o
número de linhas da matriz. Para a metade inferior, anti-diagonal inferior, da matriz espelha-se
o modelo [1].
No caso das matrizes rectangulares, NxM, este processo sofre ligeiras alterações. Nesta
situação a condição x ≤ N passa a ser x ≤ M, pois todas as colunas têm que ser percorridas e se
assim não fosse as colunas com o índice maior que N não seriam preenchidas.
Y=1 ... Y=6 x=1 1 1 1 2 1 3 1 4 1 5 1 6 ... 2 1 2 2 2 3 2 4 2 5 2 6 3 1 3 2 3 3 3 4 3 5 3 6 x=4 4 1 4 2 4 3 4 4 4 5 4 6
Figura 2.17 - Matriz H retangular.
Relativamente à outra condição, permanece a mesma, mas terá de ser feita uma verificação
para que o número de linhas não seja ultrapassado. A referida condição, Hx-y+1, y, x-y+1 terá de
ser sempre menor ou igual a N, o número de linhas da matriz. O número de elementos que se
podem calcular em simultâneo mantêm-se em N, tal como se pode ver na figura 2.17, com os
elementos a azul, verde e cinzento.
Voltando ao caso das matrizes quadrangulares, são necessários N ciclos para calcular a anti-
diagonal superior da matriz e N-1 para a inferior, uma vez que é redundante calcular as células
de x=N. Assim o algoritmo S-W paralelizado, requer 2N-1 operações. Generalizando agora para
o caso das matrizes NxM, o algoritmo requer M + N – 1 operações, isto é, O(N) [1].~
2.3 Unidade de Processamento da CoreWorks
Um dos objectivos para o desenvolvimento e implementação do projecto é utilizar a tecnologia
da CoreWorks.
A Unidade de Processamento da CoreWorks (UPCW) é um sistema desenhado para correr
aplicações computacionalmente intensivas, num ambiente de processadores múltiplos. O
grande poder computacional deste sistema provém essencialmente do SideWorks que permite
várias configurações do acelerador como veremos [8].
21
FireWorksTM
Instruction
Cache
Data
Cache
SideWorksTM
AMBA-AHB bus
Memory controller
Coreworks Engine
External RAM
Status/
Control
Ma
ste
r B
us in
terf
ace
User BusBus Arbiter
User data source/sinkUser cores
User cores
SideWorksTM
Boot I/F
DMA
Boot Device
Figura 2.18 - Interface do UPCW. [8]
Para além do já referido SideWorks, este sistema contém também um processador, o
FireWorks, uma DMA (Direct Memory Access), uma interface de I/O para dados, ficheiros de
registo acessíveis externamente para verificação do estado e controlo, interface para
inicialização e uma interface master bus [8].
O processador FireWorks é um RISC de 32bit que contém cache para instruções e dados e
controla a configuração do SideWorks que está a correr em paralelo para executar as tarefas
mais intensivas [8].
O mecanismo de DMA embebido, controlado pelo Fireworks, é responsável pela transferência
autónoma de dados de alta velocidade entre o SideWorks e a memória externa ou entre a
interface I/O para dados e a memória externa [8].
A interface I/O para dados, na figura2.18 representada por Boot I/F, é um periférico do
FireWorks, sendo baseada numa FIFO (First In First Out) e é usada para streaming de dados [8].
Os registos de estado e de controlo, Status/Control, são acedidos por circuitos externos para
que seja possível operar sobre o processador e a aplicação que está a ser executada. O
Fireworks lê as configurações e comanda a partir do registo de controlo. Depois escreve o seu
estado para o registo de estado [8].
A interface de inicialização, Boot Device, é usada para carregar o programa a partir de um
dispositivo de inicialização quando o sistema é ligado [8].
Por último o master bus, que serve a cache de dados e de instruções do FireWorks, assim
como o motor DMA, é usada principalmente para aceder à memória externa, a qual pode ser
partilhada por outros processos que estejam a ser executados [8].
22
2.3.1 Arquitetura do SideWorks
O Sideworks é uma arquitetura de uso geral para aceleradores reconfiguráveis. Usando esta
arquitetura podemos gerar automaticamente os componentes de hardware pretendidos,
através do ambiente de desenvolvimento integrado da Coreworks. Estes componentes irão
funcionar como aceleradores para o Fireworks, sendo que este controla o SideWorks pelo bus
local, acedendo aos registos de controlo e de estado internos do Sideworks [8].
A arquitetura está pré-definida podendo ser constituída por unidades funcionais pré-definidas
(FUs), unidades de memória (MUs), unidades de geração de endereços (AGUs), que podem ser
utilizadas livremente. De acordo com o pretendido pelo utilizador, a informação, contendo
uma configuração, é guardada num registo de configuração, sendo que existe um segundo
registo que permite ter a próxima configuração pretendida, permitindo, assim, uma rápida
reconfiguração. A transferência de dados e das configurações são, normalmente, feitas através
do DMA. O Sideworks utiliza unidades funcionais (FU) da sua própria biblioteca, que contém
unidades de uso geral, como por exemplo ALUs, multiplicadores, shifters, tornando assim esta
tecnologia flexível para criar qualquer tipo de aplicação de forma simplificada [8].
Timing
UnitAGU_1 AGU_2 AGU_... AGU_Na
Address Output Xbar
MU_1 MU_2 MU_3 MU_... MU_Nm
Address Input Xbar
Data Read Xbar
FU_1 FU_2 FU_3 FU_... FU_Nf
Data Write Xbar
CONFIGCONFIG_NXT
Da
ta in
terf
ace
Co
ntr
ol in
terf
ace
Figura 2.19 Arquitetura do SideWorks. [8]
Na figura 2.19 mostra-se um esquema simplificado da arquitectura Sideworks.
O endereço das memórias é gerado nas AGUs, sendo todas as memórias embebidas e de dual
port, conseguindo-se, assim, maximizar a largura de banda. No entanto, podem ser usadas
memórias single port, caso seja necessário [8].
23
Os dados processados pelas FUs podem ser provenientes quer das MUs quer das saídas de
outras FUs. As entradas de cada FU são selecionadas pela Data Read Xbar. Cada FU tem várias
saídas de dados, sendo que poderão, também, existir vários comprimentos de palavra,
incluindo 1bit para a flags. Estas flags são também controladas pela Read Xbar, para que
possam ser usadas como controlo para a entrada de outras FU [8].
Os dados provenientes das saídas das FUs podem ser encaminhados para Read Xbar de forma
a serem usados por outras FUs. Outra opção será enviá-los para a Write Xbar de forma a serem
escritos nas MUs. Por último, podem ser reencaminhados para os Address Output Xbar, para
serem usados como endereços de memória [8].
As unidades de memória podem ser acedidas por duas interfaces: a primeira é uma slave do
FireWorks, a segunda é uma slave do motor de DMA. Este último acesso é particularmente
relevante, pois o acesso às memórias por parte do DMA, enquanto a aplicação está a correr, é
importantíssimo para minimizar os tempos de transferência de dados [8].
O registo de configuração contém os dados que definem a configuração das FUs programáveis,
das Xbar e dos geradores de endereços. Além disso, são também aí guardadas as constantes
usadas nas computações. Este registo pode também ser acedido pelo DMA enquanto a
aplicação está a ser executada para minimizar o tempo da reconfiguração [8].
As instruções do SideWorks são descritas na linguagem C, usando uma biblioteca de funções
proprietária da CoreWorks, SideC. Os dados dos registos de configuração criam uma datapath
no hardware de forma a executar as tarefas pretendidas [8].
2.3.2 Arquitetura do FireWorks
O FireWorks é um processador RISC de uso generalizado com arquitetura Harvard de 32bit.
Este utiliza uma arquitetura pipeline de 5 etapas, que passam a ser descritas, muito
sucintamente: procura da próxima instrução (Fetch), decodificação (Decode), execução
(Execute), memória (Memory) e escrita (Write Back). O processador apresenta uma interface
de programação paralela, que pode ser usada para escrever em todo o espaço de memória,
através de instruções de escrita. O processador tem cache de instruções e de dados de
tamanho configurável, existindo, ainda, memórias de dados e de instruções locais para guardar
pequenos programas de inicialização. Na figura 2.20, apresenta-se o diagrama de blocos da
arquitetura do processador [8].
24
Data CacheInstruction CacheLocal Instruction
MemoryLocal Data Memory
Processor Bus
Interface
Pipeline Controller
FetchDecode
Write
BackExecute
Parallel
Programming
Interface
Memory
dmem matriximem matrix
B1 - AMBA-AHB
master
B2 - AMBA-AHB
masterB3 - AMBA-AHB
master
Figura 2.20 Arquitetura do FireWorks. [8]
2.3.3 Fluxo de projeto da Coreworks
O fluxo de projecto da Coreworks, pode ser dividido em 3 partes: as unidades funcionais (UF),
a configuração para o Sideworks, com a arquitetura desejada, e os ficheiros que constituem o
core.
Para a descrição de uma UF na arquitetura da Coreworks verificou-se, então, que seria
necessário construir 4 ficheiros: um .c com a descrição do funcionamento da UF, o qual é
usado essencialmente para a simulação; um .h relativo ao .c; um ficheiro XML, com a descrição
das entradas e saídas da unidade funcional e os respectivos tamanhos, sendo este ficheiro
utilizado na construção da datapath da arquitetura; e a descrição da UF em VHDL. A
compilação das unidades funcionais gera uma biblioteca de elementos para serem utilizados
na construção da configuração para o Sideworks.
Esta última é construída com as funções definidas nos ficheiros em linguagem C das unidades
funcionais efectuando as ligações necessárias entre elas. A compilação da arquitectura gera o
elemento Sideworks para o core e também ficheiros RTL para construção de todo o hardware
necessário para gerar o Sideworks para a FPGA. Utilizando o ISE da Xilinx, vão ser
acrescentados os ficheiros RTL a um projeto, específico para a FPGA pretendida, para que seja
gerado o bitfile de programação.
25
O core é constituído por 3 configurações distintas: SIM, TEST e EMB. A configuração SIM faz a
simulação do hardware, sem ser necessário recorrer a uma FPGA. Assim é possível testar o
funcionamento das configurações do Sideworks criadas. A compilação do SIM gera um
executável, que simulará hardware no pc. As configurações TEST e EMB são ambas usadas para
a implementação em hardware. O TEST consiste na parte da aplicação que vai correr no PC,
inclui os processamentos que não são efectuados no Fireworks e também as transferências de
dados entre o PC e as memórias da placa para serem utilizados pelo Fireworks. Quanto ao EMB
contém o código que vai ser executado no Fireworks, incluindo a configuração e controlo do
Sideworks e transferências de dados entre as memórias da placa e as memórias da FPGA. A
compilação deste código gera um ficheiro de extensão .elf que é utilizado para programar o
processador Fireworks.
Para se executar um projeto no hardware, é necessário seguir os seguintes passos: programar
a FPGA com o bitfile, depois utilizar o .elf para programar o Fireworks e por último correr a
configuração TEST no computador.
Toda esta descrição pode ser visualizada na figura 2.21.
SideWorks
RTL description
Compute
Bottleneck
(1)
SideConf™
SideWorks
Architecture(XML file)
Compute
Bottleneck
(2)
Ref.
code
Embedded
code
Accelerator
Call (1)
Accelerator
Call (2)
Runs on
PC
SideWorks
Configs(XML file)
GCC
(FireWorks)
Embedded
Executable
Code (.elf)
SideSim TM
SideGen™
Runs on
embedded
platform
SideWorks.a
SideWorksParameters(.c and .h files)
GCC
(PC)
GCC
(PC)
XMLdump
GCC
(PC)
Accelerator
descriptions
(.c files)
Figura 2.21 - Fluxo de projeto do Sideworks.
2.4 Conclusão
Neste capítulo, foi feita uma pequena introdução ao mundo do ADN, apresentando-se
essencialmente de onde provêm as bases nitrogenadas usadas para se fazer o alinhamento de
sequências.
26
Foram analisados e estudados alguns algoritmos de alinhamentos globais e um local, com
especial ênfase para a sua compreensão e aplicação e tendo já em vista a sua aplicabilidade
futura. Foi também apresentado o porquê de se fazer a diagonalização da matriz H e como se
faz. Por último, as características principais do hardware que irá ser utilizado foram
apresentadas e analisadas, ficando-se assim a conhecer as potencialidades do mesmo.
De referir que o algoritmo que irá ser utilizado daqui em diante será o algoritmo S-W pois é
aquele que apresenta um melhor desempenho, é o mais estudado e aplicado e é também o
que tem maior utilidade, pois alinhamentos globais são raros. Além destes aspetos, dado que
dispende muito tempo a repetir a mesma tarefa, o preenchimento da matriz H, é ótimo para
se fazer paralelização.
27
3 Implementação do sistema de alinhamento
3.1 Introdução
Neste capítulo vai ser apresentada toda a implementação do projeto e as fases pelas quais
este passou. Desde a arquitetura escolhida até à implementação em software, passando pela
primeira abordagem feita ao problema e posterior alteração para fazer a partição das
sequências.
Vai ser apresentada a simulação e também a implementação em hardware, com toda a parte
de software envolvente de comunicação e tratamento de dados que lhe está inerente.
3.2 A aplicação SSEARCH
O FASTA Program Package [9] foi o pacote de programas de base usado como ponto de partida
para o projeto. Este pacote foi desenvolvido na universidade da Vírginia, por William R.
Pearson, e apresenta um vasto leque de soluções para comparação de sequências com bases
de dados, tanto para ADN como para proteínas. Apresenta ainda um programa para analisar a
significância estatística de sequências aleatórias. Todos estes programas encontram-se
divididos em quatro grupos, tal como é apresentado na tabela 1.
Tabela 1- Programas do FASTA Program Package divididos por grupos. [9]
Pesquisa em Bibliotecas
Alinhamento Local Significância estatística
Alinhamento global
FASTA LFASTA PRDF ALIGN FASTX PLFASTA RELATE
TFASTA LALIGN PRSS TFASTX PLALIGN RANDSEQ
SSEARCH FLALIGN
28
A pesquisa e análise focou-se num único elemento, o SSEARCH, que faz o alinhamento local,
mas com pesquisa em bibliotecas de sequências. Este programa faz exatamente o que se
pretende, ou seja, compara uma sequência com uma base de dados de sequências utilizando o
algoritmo Smith-Waterman, tal como o conhecemos. Este programa é aproximadamente 10
vezes mais lento que o FASTA, mas é exato na comparação de sequências.
Utilizar o SSEARCH é muito simples. Em ambiente Linux, basta compilar e seguidamente
executar o programa na linha de comandos inserindo como parâmetros o nome dos ficheiros
das sequências que se pretende comparar e da biblioteca. Dependendo das opções utilizadas,
o programa corre e no final exibe os resultados que podem também ser guardados num
ficheiro de resultados.
Exemplificando, se utilizarmos o comando ../bin/fasta35 -q ../seq/mgstm1.aa
../seq/prot_test.lseg iremos obter como resultado o que se apresenta no anexo 1. Fazendo
uma análise aos resultados obtidos, estes podem ser divididos em quatro partes que iremos
analisar de seguida: o comando utilizado na linha de comandos e a descrição da sequência de
query e da biblioteca utilizada; descrição das estatísticas e dos parâmetros de procura; uma
lista dos resultados mais elevados encontrados na biblioteca, descrição, resultados de
similaridade e significância estatística e por último os alinhamentos que produziram os
resultados [9].
No que concerne à primeira parte, a qual é apresentada na figura 3.1 verificamos o comando
utilizado, assim como a sequência de query, que neste caso é a GT8.7, 218 aa e a biblioteca é
a prot-test.lseg, a qual contém 12 sequências com 2245 resíduos [9].
Figura 3.1 - Excerto do anexo 1 com a primeira parte dos resultados do SSEARCH.
Na segunda parte dos resultados, exposto na figura 3.2, é apresentada a descrição das
estatísticas e parâmetros de procura. Observamos que é utilizado o algoritmo Smith-
Waterman, com a matriz de substituição BL50 e uma penalização de gap de -10 para um gap
em aberto e de -2 para um gap de resíduo. O gap de resíduo é quando já existe um gap
anterior e vamos inserir outro, quanto ao gap em aberto, é quando é o primeiro gap a
aparecer. No projeto desenvolvido não existem estes dois tipos de gap, apenas um linear de
valor sempre igual a ‘-2’ [9].
29
Figura 3.2 - Excerto do anexo 1 com a segunda parte dos resultados do SSEARCH.
A terceira parte é a que contém mais informação. Esta é uma lista dos resultados com scores
mais elevados das comparações e acima do limite mínimo definido para se fazer o traceback,
com descrição da sequência da biblioteca e respectivos resultados.
Figura 3.3 - Excerto do anexo 1 com a terceira parte dos resultados do SSEARCH.
Tal como se pode visualizar na figura 3.3, depois da descrição da sequência aparece um
número entre parêntesis que indica o comprimento da sequência em questão. Tomando como
exemplo a sequência GT8.7 esse valor é 218. O valor seguinte, 1497, é o resultado do
algoritmo Smith-Waterman para o alinhamento em análise. O valor de 354.7 representa o bit
score. Este, em conjunto com o comprimento da sequência de query, e da biblioteca pode ser
usado para calcular a significância do alinhamento. Estes bit score podem ser importantes, pois
oferecem-nos um resultado independente para cada matriz, que pode ser comparado com
outras procuras, com outras matrizes de substituição e diferentes penalidades de gap. Por
último, o E() é a significância estatística do resultado do alinhamento. Este valor de E()
depende do tamanho da base de dados e no caso da sequência GT8.7 é 9.4e-102 [9].
Temos, então, os alinhamentos que produziram os resultados anteriores. Estes são
apresentados de forma intuitiva e, para além das sequências, são também apresentados
alguns resultados importantes como a percentagem de igualdade entre as duas sequências, a
percentagem de similaridade e as fronteiras do alinhamento.
30
Figura 3.4 - Excerto do anexo 1 com a quarta parte dos resultados do SSEARCH.
Depois da apresentação dos alinhamentos, temos a indicação do tempo utilizado pelo
programa enquanto esteve a decorrer, tanto em termos de tempo de execução, como de
apresentação dos resultados para visualização.
Este é um programa bastante completo e com muitas funcionalidades, mas também bastante
complexo em termos do seu funcionamento interno.
3.2.1 Análise temporal do SSEARCH
Depois de feita uma análise geral ao programa e do seu funcionamento, é tempo para uma
análise temporal. Esta foi elaborada através do profiler. O resultado de um dos testes
realizados está disponível na figura 3.5.
31
Figura 3.5 - Resultados do profiler.
Como se pode verificar na figura 3.5, a função smith_waterman_sse2_byte é a que gasta quase
a totalidade do tempo de execução do programa. Tal como o nome indica, a função em
questão é responsável por fazer o preenchimento da matriz H utilizando o algoritmo Smith-
Waterman. Assim se comprova, mais uma vez, a necessidade de acelerar este processo de
forma a minimizar ao máximo o tempo despendido nesta tarefa. Uma das formas de o fazer é
a aceleração por hardware.
Consequentemente, passou-se à fase seguinte, o estudo de uma solução para este problema.
3.3 Arquitetura da Unidade Funcional de alinhamento
Conhecidas as capacidades e compreendido o funcionamento da plataforma SideWorks,
iniciou-se o desenvolvimento do hardware no sentido de criar para este sistema uma unidade
funcional de alinhamento de sequências. Para tal pretende-se desenvolver uma unidade
funcional de maneira a ser replicada várias vezes em hardware. Cada unidade funcional será
designada por elemento de processamento (EP). O objectivo de se replicar os EPs é para que
seja possível fazer a diagonalização da matriz, ou seja, a paralelização do algoritmo.
Tal como apresentado anteriormente, o algoritmo S-W com penalização de gap linear, é
representado por:
32
{
Este algoritmo pode ser mapeado em EPs colocados em série, criando-se assim uma
cadeia de elementos e aumentando a eficiência do mesmo. Uma forma de mapear é
assignar a cada EP um caracter da sequência query e propagar a sequência de subject
pela cadeia de EPs. Este processo está representado na figura 3.6 [10].
Figura 3.6 Comparação de sequências numa cadeia de EP em série.
Nesta figura é possível verificar o que foi exposto anteriormente, ou seja, a sequência de query
é carregada para os EPs, um caracter por cada EP, e a sequência de subject passa pela cadeia
de EPs da esquerda para a direita de forma que cada elemento desta é comparado em cada EP
com um elemento da sequência de query.
Assim por cada ciclo é feita uma computação de um elemento da anti-diagonal da matriz H em
cada um dos EPs. Como se pode perceber, é um grande passo para se diminuir o tempo gasto
no preenchimento da matriz H, se a primeira sequência tiver M elementos e a segunda um
comprimento de N, todo este processo é feito em M+N-1 passos e utilizando M elementos de
processamento, em vez dos MxN passos que seriam necessários num processador sequencial
[10].
A figura 3.7 exibe a arquitetura de um elemento de processamento para uma penalização de
gap linear tal como apresentado em [10].
33
Figura 3.7 Arquitetura de um elemento de processamento para uma penalização de gap linear.
Esta arquitetura retirada de [10], foi a primeira aproximação ao EP pretendido e servirá de
base ao trabalho realizado. Analisando agora o esquema, a LUT contém a matriz de
substituição para os dados em questão, sendo o seu comprimento variável consoante o que se
pretender analisar. No nosso caso utilizaremos o ADN pelo que teremos um comprimento de 4
posições de memória na LUT. Consequentemente, lw terá tamanho 2 pois apenas é necessário
codificar 4 caracteres A, C, G e T. O tamanho de sw (substitution weight) depende do valor da
matriz de substituição, sendo que neste caso esse valor será no máximo ‘2’, sw é 3. Quanto a
dw o tamanho é definido por: ( ) , onde é o tamanho da
sequência mais pequena.
Como se pode constatar na figura 3.7 cada EP tem três registos que servem de memória local
para guardar os valores de H(i – 1, j – 1), H(i – 1, j) e H(i , j – 1). O elemento de processamento
mantém ainda atualizado o máximo encontrado até ao momento e passa sempre essa
informação para o EP seguinte, assim como o resultado de H(i, j) [10].
No que concerne à explicação do funcionamento do EP, em primeiro lugar ter-se-á que
carregar a LUT com a linha da matriz de substituição correspondente ao elemento que
pretendemos colocar no EP. Seguidamente, os vários elementos da sequência de subject irão
passar pelo elemento de processamento, tal como mostra a figura 3.6. Uma vez que um
elemento esteja na entrada da LUT, o valor correspondente à matriz de substituição será
colocado na saída e passará pelo sign extend que não faz mais do que uma extensão de sinal
de sw para dw, colocando-o assim em concordância com os outros. A entrada H(i – 1, j), que
provém do elemento anterior, é somada com o valor da matriz de substituição e colocada num
registo. Esse registo já contém um determinado valor armazenado, que não é nada mais nada
menos que H(i – 1, j – 1). O registo que contém H(i – 1, j) irá também fornecer esse valor para
um comparador onde este é comparado com H(i, j – 1), já presente no EP. Na saída do
comparador vamos encontrar o maior valor e a esse será adicionado α, que no capítulo 2.3.1
foi designado por d. Uma vez feita essa operação o resultado será comparado com o que
contém o registo H(i – 1, j – 1) ) + obtendo-se assim o valor final do elemento e sendo
o seu valor armazenado num registo que na próxima iteração irá ser H(i, j – 1). A partir desse
34
registo vai também surgir o valor para ser comparado com o máximo proveniente do elemento
anterior. Calcula-se então se o novo valor, calculado nesta iteração, é maior que o anterior, o
qual estava previamente armazenado no EP, ficando o máximo atualizado. Depois de todos os
registos estarem carregados e se tivermos mais elementos para serem calculados, iremos ter
um resultado por cada ciclo de relógio.
3.3.1 Arquitetura da Unidade Funcional de alinhamento para Sideworks
O elemento de processamento apresentado anteriormente, no capítulo 3.3, sofreu algumas
alterações para adaptação à arquitetura da Coreworks. Assim, a nova arquitetura do EP é
exposta na figura 3.8.
Figura 3.8 Nova arquitetura de um elemento de processamento para uma penalização de gap linear.
Este EP tem apenas algumas diferenças relativamente ao original: foi introduzido um
comparador, houve uma mudança no sinal de α, colocou-se um registo de enable e outro
registo para receber um sinal de trigger e a LUT modificada.
O registo de enable foi introduzido para se ter um sinal que permita ativar todos os registos da
EP. Na figura 3.8 representam-se as ligações do sinal enable através de en, não se
apresentando os fios para que o esquema não fique confuso. Quanto ao registo de trigger este
existe devido a uma necessidade de transferir para a LUT um conjunto de memórias, de 8 bits
35
cada, que são comuns a todos os EPs. O sinal só está positivo num EP em qualquer ciclo de
relógio, podendo assim guardar-se o que está nas memórias no EP correspondente.
A LUT sofreu uma grande modificação, pois passou a ser constituída por quatro entradas em
cada EP em vez de ser uma LUT igual para todos os elementos. Deste modo a LUT continua a
ser estática para cada EP, apenas é carregada uma única vez, mas diferente consoante o
elemento da sequência subject daqui em diante designada também por sequência fixa, pois
esta é a mesma para todas as sequências de query. Cada EP será carregado com uma linha da
matriz de substituição. Tal como se poderá verificar, mais adiante, o elemento da sequência
query, daqui em diante designada também por sequência variável, pois percorre todos EPs, irá
escolher o valor para a saída da LUT.
Foi, ainda, substituído o somador por um subtrator, para penalização de gap, ou seja, α. Este
era um erro do esquema anterior e que foi, desta forma, retificado.
Por último, foi introduzido um comparador com zero na saída do valor calculado como score.
Este comparador surge para garantir que nunca se terá um valor negativo na saída do
elemento de processamento. Poderíamos ter esta situação, caso a entrada H(i – 1, j) fosse zero
e no processamento anterior apenas estivessem armazenados zeros nos registos. No caso
desta situação acontecer, na saída do primeiro máximo, teremos zero e, subtraindo depois o
valor α, o valor resultante será negativo. Se estivermos perante um caso de uma desigualdade,
o valor saído da LUT será também negativo e uma vez adicionado ao zero da entrada, irá
resultar num valor inferior a zero. Assim, na entrada do segundo máximo, temos dois valores
negativos, sendo que a única forma de garantir um valor igual ou superior a zero será colocar
um comprador. Desta forma é assegurado um dos requisitos do algoritmo que é o de não ter
valores negativos na matriz H.
Estas foram as primeiras alterações e as que se detectaram que seriam necessárias logo à
partida. Outras foram aparecendo ao longo do projeto, nomeadamente com a introdução da
partição do cálculo das sequências e que serão indicadas no momento oportuno, juntamente
com a descrição da implementação do EP.
3.4 Implementação no Sideworks da arquitetura da Unidade
Funcional
Depois de estudado e explorado o SSEARCH e o Cwide, que é o ambiente de desenvolvimento
da Coreworks, e tendo-se encontrado a arquitetura adequada para o problema em questão,
encetou-se o desenvolvimento do projeto propriamente dito. Para tal, chegou-se à conclusão
que o melhor seria o desenvolvimento de duas novas unidades funcionais (UF) dedicadas à
resolução do problema proposto. Estas novas UFs são a linear_gap_penalty e a trigger. Estas
36
UF conjuntamente com as que já existem desenvolvidas pela Coreworks serão as suficientes
para o desenvolvimento do projeto.
3.4.1 Nova Unidade Funcional para o Sideworks: Linear Gap Penalty
Para a descrição de uma nova UF na arquitetura da Coreworks, seguiu-se o descrito na secção
2.2.3, criando-se os ficheiros .c, .h, XML e VHDL. Todos estes ficheiros foram desenvolvidos no
ambiente da Coreworks, o Cwide, e são descritas nas secções seguintes.
3.4.1.1 Ficheiros .c, .h
Os primeiros ficheiros a serem construídos foram o .c e o .h. Inicialmente, foram identificadas
dez entradas necessárias para o funcionamento da UF: o S2_in que mais não é que a entrada
de cada um dos elementos constituintes da sequência variável; o h_in que será o elemento
proveniente do PE anterior e será a entrada do registo H(i - 1, j); o max_in é o valor do máximo
proveniente também do PE anterior; as entradas sfcol1_in, sfcol2_in, sfcol3_in, sfcol4_in que
irão ser as entradas da nossa LUT, tal como explicado anteriormente, no capítulo 3.3.1; en_in é
um sinal de enable para se dar início a todo o processo; o sinal trigger_in foi introduzido para
se carregar em cada ciclo de relógio o valor correto da LUT; e, finalmente, o alpha_in é o valor
de α que assim pode ser alterado caso se pretenda. Tudo isto pode ser verificado na figura 3.9.
Figura 3.9 Cabeçalho da UF sidefu_linear_gap_penalty_sim.
37
Tal como se pode observar na figura 3.9, todas estas entradas são do tipo sideC_port_t, sendo
este o utilizado para todas as entradas deste tipo de funções em todas as UF da Coreworks.
Uma vez definidas as entradas pretendidas é tempo de decidir qual o número de bits que cada
uma terá. Para isso, utiliza-se a função validate_input_length() e, por uma questão de
implementação, pois já existem registos de 11 bits nas UFs da Coreworks, todas as entradas
serão de onze bits, à exceção de en_in e trigger_in, que apenas necessitam de um bit. Esta
função tem a responsabilidade de verificar se as entradas têm o tamanho correto,
comparando com os valores do ficheiro XML. Assim, não temos incompatibilidades ao nível do
tamanho e garantimos a ausência de erros devido ao tamanho das entradas incorretas.
Figura 3.10 Definição do número de bits das entradas da UF.
Uma vez criadas e definidas as entradas iremos definir as saídas. Em termos de saídas apenas
são necessárias quatro: o max_out, para servir depois de entrada para o próximo EP, para que
o máximo se mantenha sempre atualizado; h_out, que mais não é que o resultado do
processamento do EP, ou seja, é o H(i, j) e será a entrada h_in do EP seguinte; a saída S2_out
não é nem mais nem menos que a saída do registo que armazenou S2_in e que será passada
para o EP seguinte para que a sequência se possa propagar pelos EPs; os sinais en_out e
trigger_out apenas são a propagação dos sinais de entrada pelos vários EPs. Para se fazer a
inicialização das saídas, que é um requisito do sistema, é necessário fazer duas coisas. A
primeira é defini-las na estrutura sidefu_linear_gap_penalty, que é definida no ficheiro
linear_gap_penalty.h, a segunda é fazer a inicialização dos seus valores através da atribuição
direta de valores.
38
Figura 3.11 Definição e inicialização das saídas do EP.
Na figura 3.11 está o código que implementa o que foi explicado no parágrafo anterior. No
lado esquerdo, a definição das saídas na estrutura da UF, enquanto no lado direito se
apresenta, como exemplo, a inicialização do output max_out, requisito do sistema da
Coreworks. Na inicialização é possível ver o signal.current.value que corresponde à atual
chamada à função enquanto o signal.next.value irá conter o valor da próxima chamada.
Definidas as entradas e saídas vamos começar a descrever a arquitetura apresentada
anteriormente. O comportamento da UF, aqui descrita, tem de estar em conformidade com o
VHDL ao nível das saídas, ou seja, teremos que ter nas saídas deste ficheiro e do VHDL os
mesmos valores nas mesmas iterações. Na descrição da UF irão ser necessárias variáveis
internas por forma a realizar os vários cálculos requeridos, sendo estas declaradas
normalmente em C. Para além destas, são também definidas variáveis na estrutura
sidefu_linear_gap_penalty, a mesma onde foram também descritas as saídas, as quais
funcionam como registos internos.
Começando com a descrição do funcionamento da UF em si, em primeiro lugar tratou-se da
LUT. Na figura 3.12 pode ver-se o código relativo a essa parte.
39
Figura 3.12 - Configuração da LUT.
Tal como se pode observar na figura 3.12, cada um dos valores da entrada da LUT, é passado
para uma variável interna. Isto só acontece se tivermos o valor en_in a um e se o trigger_in for
também um. Tendo isto, faz-se então a atribuição do valor da LUT utilizando-se a variável
S2_in para definir o valor a atribuir em cada caso. Como estamos a trabalhar com ADN apenas
iremos ter quatro casos possíveis.
Continuando a descrever a arquitetura apresentada anteriormente, far-se-á uma atribuição de
alguns dos valores que estão guardados na estrutura sidefu_linear_gap_penalty para variáveis
locais, de forma a serem manipuladas de seguida.
40
Figura 3.13 - Descrição do cálculo do score.
A figura anterior mostra as comparações necessárias para obter o valor final de H(i, j). Note-se,
também, que neste passo já é feita a atribuição do valor de h_out, ou seja, o valor do cálculo
do EP. Para além disso são, também, armazenados os valores intermédios.
Para finalizar falta a atualização do valor do máximo da matriz H, o qual se faz de forma
semelhante ao cálculo do valor de h_out. Ao terminar fazem-se as atribuições dos valores a
serem passados para o EP seguinte que ainda não tinham sido feitas anteriormente.
Figura 3.14 - Calculo do máximo e atribuições finais.
41
3.4.1.2 Ficheiro XML
O ficheiro XML é bastante simples, pois apenas contém as saídas e entradas da UF com o
respectivo nome e comprimento. Juntamente com a configuração do CWide XML irá criar a
arquitetura da datapath pretendida em XML para ser depois lida pelo programa Sidegen o qual
irá gerar em verilog toda a arquitetura do Sideworks.
Figura 3.15 - Ficheiro XML.
Embora bastante simples, o ficheiro apresentado na figura 3.15 é muito importante, pois
servirá para fazer ligação entre o VHDL e o crossbar da Coreworks.
3.4.1.3 Ficheiro VHDL
O ficheiro com o código em VHDL é aquele que irá implementar a arquitetura da nossa UF na
FPGA. O comportamento deve ser semelhante ao descrito para o ficheiro .c.
Começando pelas entradas, estas serão as mesmas dos ficheiros da simulação e, tal como
descrito anteriormente, todas irão ter onze bits de comprimento, com exceção do en_in e do
42
trigger_in. Da mesma forma, também as saídas serão de onze bits com exceção do trigger_out
e en_out.
É de referir que um dos pontos mais importantes na implementação é o da LUT. Assim, os
quatro sinais de entrada com os valores da LUT são concatenados num único sinal, ‘d6’, de
forma a ser, posteriormente, utilizado um MUX, para se selecionar os 8 bits a serem utilizados.
Figura 3.16 - LUT em VHDL.
A figura 3.16 ilustra o que foi explicado anteriormente e mostra, ainda, o MUX utilizado para
fazer a seleção do valor da LUT. É também perceptível que o sinal mux_sel é a entrada S2_in,
ou seja, o elemento da sequência variável que vai ser analisado num determinado instante.
Assim, faz-se a seleção do valor de saída da LUT de uma forma bastante simples e utilizando
poucos bits para cada EP. Para além disso, é também visível a extensão de sinal que é
necessário aplicar ao sinal de saída da LUT, para que este possa ser utilizado no somador com
o valor de entrada h_in, que mais não é que o valor de H do EP anterior.
43
Figura 3.17 - Comparadores, Flip-Flops e subtrator de alpha.
Na figura 3.17, é visível o subtrator de α, logo no topo da imagem, e também um dos vários
Flip-Flops criados. Além destes dois componentes, pode-se ainda observar dois dos
comparadores anteriormente referidos, neste caso o comparador colocado antes da saída para
garantir que nunca se irá ter um valor negativo e o que irá comparar a saída do subtrator de α
com H(i - 1, j – 1).
Uma vez concluídos estas descrições em VHDL, temos tudo o que é necessário para a
construção e integração da nova UF “linear_gap_penalty” na arquitetura da Coreworks.
3.4.2 Nova Unidade Funcional para o Sideworks: Trigger
A UF trigger é muito mais simples que a anterior e pelo que a sua apresentação será diferente
e mais simplificada.
Esta UF tem apenas uma entrada, o enable, e duas saídas, o enable e o trigger. Tal como se
pode observar na figura 3.18, esta é uma UF bastante pequena e que pouco espaço ocupa em
termos de hardware. No lado esquerdo da figura 3.18 apresenta-se o código VHDL, que
consiste numa máquina de estados bastante simples. Do lado direito da imagem está o código
em C que apresenta o mesmo comportamento. Na realidade é uma máquina de estados com
apenas 3 estados, que permite gerar um sinal de enable e um trigger.
44
Figura 3.18 - UF Trigger: lado esquerdo código VHDL e lado direito código c.
Esta UF tem como função criar um novo sinal, o trigger, que fica ativo apenas durante um ciclo
de relógio.
Tal como no caso da UF linear_gap_penalty para esta UF também foram criados os ficheiros .c,
.h, VHDL e XML.
3.4.3 Configuração do Sideworks
Embora a configuração do Sideworks não seja um dos requisitos para se criar uma UF nova,
decidiu-se incluí-la neste subcapítulo, pois foi criada com esta UF inicial e ajuda a perceber o
funcionamento do projeto e das secções que se encontram a seguir.
Para se criar uma configuração para o Sideworks é necessário ter conhecimento das UFs, já
disponibilizadas pela Coreworks, e fazer algum trabalho prévio de análise a outras
configurações. Cada uma é constituída pela declaração dos objetos usados na configuração e
depois pela descrição da datapath que implementa a configuração.
Uma vez definida uma nova UF com o comportamento desejado para a arquitetura em
questão, a configuração criada acabou por não ser muito complexa e não conter muitas UFs,
previamente criadas pela Coreworks. Assim sendo, o primeiro passo foi a definição do nome
da configuração. Isso mesmo está ilustrado na figura 3.19, em que a definição do nome da
45
configuração está presente logo na primeira linha. Neste exemplo irá ser apresentada uma
configuração com 10 EPs.
Figura 3.19 - Time Unit e registos da datapath.
Nestas primeiras linhas do ficheiro, são também definidas as Time Units (TU) e o registo que
vai receber o número de ciclos de relógio de funcionamento, loop_end_k. Os registos são
criados recorrendo à função REG_11(), sendo estes registos de 11bits. Mais ainda, é criado um
registo para receber o hin e o maxin, que serão inicializadas a zero, pois são as entradas do
primeiro EP, como se poderá ver mais à frente. De referir que estas entradas serão muito
importantes na entrada do primeiro EP quando for feita a partição do cálculo da matriz, o que
acontece no capítulo 3.5.
É também criado o registo de alphain que irá receber o valor de α que será comum a todos os
EPs e uma entrada em todos eles. Depois destes, são introduzidos os registos que vão
armazenar os máximos de cada um dos EPs. Optou-se por esta via, pois no caso de sequências
pequenas, em que nem todos os EP são utilizados para fazer o cálculo, temos de saber qual o
máximo num determinado EP. Exemplificando, se tivermos 10 EPs implementados e a
sequência fixa for de apenas 5 elementos, necessitamos do valor máximo no EP número 5, pois
é aí que acaba a computação. De referir ainda que todos estes registos são de 11 bits um dos
tamanhos que está disponível na plataforma da Coreworks.
Na figura 3.19 é ainda perceptível a criação de uma Basic Address Unit, com a função
BAU_11(). Esta, é responsável por gerar os endereços de acesso a todas as memórias que vão
ser utilizadas.
O passo seguinte foi a introdução do trigger, dos EPs e das memórias. Em primeiro lugar
declarou-se o trigger, seguindo-se os 10 EP e, por fim, 10 memórias.
46
Figura 3.20 - Declaração dos EPs e memórias.
Observando a figura 3.20, constata-se que o bus de dados de cada uma das memórias tem
também 11 bits.
Uma vez definidos os elementos a utilizar, é tempo de construir a datapath. Para começar com
a descrição da datapath utiliza-se SIW_DATAPATH(), tal como se pode ver na figura 3.21.
Figura 3.21 - Descrição da datapath.
A função F_STATE_11() serve para colocar num determinado registo o valor pretendido,
enviado pelo software. Neste caso, vão ser necessários definir 4 valores, já previamente
apresentados: loop_end_k, hin, maxin e alphain.
O F_TIME() vai ser o controlador do tempo de funcionamento, enquanto que F_DELAY()
acrescenta um atraso ao sinal pretendido. Neste caso, vai ser introduzido um atraso no sinal
tu_wr para ser usado depois pelo trigger. A função F_BAU_11() define o tipo de BAU que se
47
quer e como se pretende que este opere. Neste é possível, por exemplo, configurar o valor do
incremento, definido como ONE, ou o valor inicial, definido como ZERO. Este BAU vai ser
utilizado em todas as memórias, tanto para a escrita como para a leitura.
A figura mostra em último lugar a configuração das memórias de leitura que serão usadas pelo
EP para leitura dos dados de entrada.
Para a datapath ficar completa falta apenas instanciar as unidades funcionais anteriormente
criadas e fazer a escrita dos valores dos máximos para os respectivos registos. A figura 3.22
mostra isso mesmo.
Figura 3.22 - Definição da Linear_gap_penalty e da escrita dos máximos.
Para se fazer uso UF criadas, basta usar F_TRIGGER() para o trigger e
F_LINEAR_GAP_PENALTY() com os respetivos argumentos apresentados anteriormente. Como
se pode verificar, o PE1 tem entradas diferentes nos argumentos em comparação com todos
os outros. Este facto deve-se a que ao primeiro EP têm de ser fornecidas entradas iniciais para
que se possa começar com todo o processo. A partir do PE2 vai sempre haver dependência do
EP anterior. Note-se que a sequência variável, os valores de H(i, j), máximo, o enable e o
trigger vão ser passados de PE em PE. A LUT é um caso diferente, pois os valores, em cada EP,
não vão ser alterados, são carregados apenas uma vez com a coluna correspondente da matriz
de substituição e permanecem iguais até ao fim do cálculo.
Na figura 3.22 pode ainda observar-se a escrita dos valores de máximo para cada um dos
registos de saída correspondentes, através da função F_REG_11(), por exemplo para o PE1
corresponde PE1.max_out.
Falta apenas ver a escrita para as memórias. Para isso utiliza-se novamente a função
F_MEM_PORT_A(), mas com argumentos diferentes. Na figura 3.23, para além desta última
função, ver-se-á ainda a função sideC_print_port(), que irá servir para fazer print dos valores
que estão nas memórias no modo de simulação.
48
Figura 3.23 - Datapath, escrita e print das memorias.
Esta foi uma primeira versão funcional da datapath que permite calcular apenas sequências
com comprimento fixo e com tamanho igual ao número de EPs. Muitas alterações foram ainda
introduzidas, embora o núcleo duro se tenha mantido presente. Na figura 3.24, apresentar-se-
á uma imagem da arquitetura da datapath, para mais fácil percepção do que acabou de ser
explicado.
Figura 3.24 - Arquitetura da datapath.
49
3.5 Implementação da arquitetura com partição de sequências
Uma vez construída a base para as sequências simples, é altura de se fazer a partição das
sequências, para que se consiga alinhar sequências grandes, com milhares de elementos, caso
seja necessário. Com a arquitetura apresentada anteriormente não possível fazer comparações
de sequências com comprimentos muito grandes, até porque o tamanho da FPGA é limitado,
sendo essa a razão para se fazerem partições.
3.5.1 Partição do cálculo do processamento da matriz H
Para que a análise de sequências muito longas seja exequível, é necessário fazer a divisão das
mesmas. Tanto a sequência fixa como a sequência da biblioteca terão que ser particionadas
em sequências mais pequenas, calculando-se, depois, várias submatrizes H.
O processamento é feito em várias iterações, olhando para a figura 3.25 uma iteração é
alinhamento de uma coluna de l elementos, as quais podem conter, por sua vez, várias
subiterações, olhando novamente para a figura 3.25 corresponde t elementos nas linhas, onde
é feito o processamento de um certo número de linhas da matriz H, sendo que uma secção da
sequência fixa é alinhada com uma secção da sequência variável. No fim de uma iteração
completa surge toda a sequência variável alinhada com uma partição da sequência fixa [11].
Figura 3.25 - Processamento particionado [11].
De forma a chegarmos ao final do alinhamento com o resultado correto, a arquitetura para
implementar esta solução tem de ser capaz de guardar os resultados intermédios. Para cada
subiteração teremos de armazenar a linha inferior de cada submatriz para podermos fazer a
subiteração seguinte e ainda a coluna mais à direita, de forma a poder ser usada na próxima
50
iteração. Para além destes dados, vai, ainda, ser necessário guardar o máximo da matriz até ao
momento e passá-lo em cada subiteração [11].
O comprimento das sequências requer uma escolha cuidadosa, pois influencia diretamente o
desempenho de todo o alinhamento. Teremos de ser cuidadosos com o comprimento de
sequência fixa (l na imagem 3.25), visto que se esta for muito pequena, teremos muitas
iterações e, por conseguinte, muito tempo gasto no carregamento da sequência variável. Por
outro lado, se for muito grande vamos necessitar de uma grande quantidade de memória para
o buffer da subiteração. Quanto à sequência variável (t na imagem 3.25), se o tamanho desta
for muito pequena vamos ter problemas com o tempo de armazenamento dos dados da
subiteração e posterior leitura. No caso de ser muito grande necessitamos de mais tempo para
fazer o seu carregamento e para armazenar os resultados intermédios.
Assim sendo, o tamanho de cada divisão da sequência fixa, como é de esperar, será limitado
pelo número de EPs existentes, pois cada EP terá um elemento desta sequência. Este processo
minimiza o tempo de carregamento das LUTs para cada um dos EPs e garante-se igualmente,
que o tempo despendido para armazenamento e leitura dos dados, que são necessários
guardar no buffer de cada subiteração, a linha inferior, não é muito relevante.
Quanto à sequência variável, esta terá que ter no máximo 128 elementos, pois é o tamanho
máximo dos vectores que se podem enviar do computador para a placa. Uma vez na placa o
Fireworks terá que enviar estes vectores para o Sideworks e neste momento apenas
conseguimos enviar o vector que recebemos.
3.5.2 Alterações à arquitetura da Unidade Funcional já apresentada
Evidentemente que o processamento de uma matriz em partes aumenta significativamente a
sua complexidade e consequentemente foram realizadas algumas alterações à UF,
anteriormente apresentada. Embora a base permaneça a mesma e o funcionamento interno
também, foram introduzidos novos sinais e registos de forma a poder fazer-se a divisão das
sequências.
Uma vez que com a introdução da partição das sequências vai ser necessário armazenar
resultados intermédios e depois voltar a carregá-los nos EPs, o primeiro desafio foi
precisamente saber como carregar esses novos dados, como selecioná-los.
Recordando a figura 3.25, vamos começar por analisar os dados da subiteração. Observando
uma matriz com os EPs representados, podemos constatar que para a subiteração vamos
necessitar de armazenar os valores H(i, j) de cada um EP que estiverem a ser utilizados.
51
H j j+6
i EP1 EP2 EP3 EP4 EP5 EP6
Dad
os
da
iter
ação
EP1 EP2 EP3 EP4 EP5 EP6
EP1 EP2 EP3 EP4 EP5 EP6 EP1 EP2 EP3 EP4 EP5 EP6
EP1 EP2 EP3 EP4 EP5 EP6 EP1 EP2 EP3 EP4 EP5 EP6
EP1 EP2 EP3 EP4 EP5 EP6 EP1 EP2 EP3 EP4 EP5 EP6
EP1 EP2 EP3 EP4 EP5 EP6 EP1 EP2 EP3 EP4 EP5 EP6
i+6 EP1 EP2 EP3 EP4 EP5 EP6 EP1 EP2 EP3 EP4 EP5 EP6
Dados da Sub-iteração corner Dados da Sub-iteração
EP1 EP2 EP3 EP4 EP5 EP6
Dad
os
da
iter
ação
EP1 EP2 EP3 EP4 EP5 EP6
EP1 EP2 EP3 EP4 EP5 EP6 EP1 EP2 EP3 EP4 EP5 EP6
EP1 EP2 EP3 EP4 EP5 EP6 EP1 EP2 EP3 EP4 EP5 EP6
EP1 EP2 EP3 EP4 EP5 EP6 EP1 EP2 EP3 EP4 EP5 EP6
EP1 EP2 EP3 EP4 EP5 EP6 EP1 EP2 EP3 EP4 EP5 EP6
EP1 EP2 EP3 EP4 EP5 EP6 EP1 EP2 EP3 EP4 EP5 EP6
Figura 3.26 - EPs numa matriz com partição das sequências.
A figura 3.26 demonstra exatamente o que foi referido, ou seja, como o preenchimento da
matriz é feito na diagonal, o que é exemplificado pelas linhas a sombreado em cima da
primeira sub-iteração, cada um dos EPs necessita de armazenar o último valor válido que
calculou. Neste caso, na linha i+6 cada EP vai armazenar o valor de H(i, j) para que possa ser
utilizado na sub-iteração a seguir, onde cada valor vai também ter que entrar num dos
respectivos EP. Ou seja, no EP1 terá que entrar o valor correspondente ao EP1 anterior, no EP2
o valor correspondente ao EP2 anterior e assim sucessivamente. Estes valores vão entrar
diretamente para o registo correspondente a H(i , j – 1), pois é o registo que deve conter o
valor anteriormente armazenado. De salientar que neste caso é preciso ter cuidado com o
valor da diagonal.
Para dar início ao processamento de uma nova sub-iteração temos que carregar o valor
correspondente a H(i , j – 1), sendo que na entrada h_in vamos ter o valor do EP anterior.
Tomando como exemplo o EP2 da partição 2, ou seja, a sub-iteração inferior esquerda na
figura 3.26, para além do valor do EP de cima, ou seja, EP2 da subiteração anterior, é preciso
ter em conta o valor do EP1 da subiteração anterior. Ora este valor da diagonal, ou seja, o H(i,
j) do EP1 da subiteração anterior devia ter entrado no h_in no EP2 da subiteração anterior.
Para contornar este enorme problema foi introduzido um novo sinal a que vamos chamar
triggerpl, que é o sinal de saída do registo do trigger_in, o qual irá ser o sinal de seleção de um
MUX representado na figura 3.28. O trigger já existente vai, assim, carregar o valor da diagonal
para a entrada h_in, a qual irá ser depois somada com a saída da LUT e armazenada no local
correto H(i – 1, j – 1).
52
Figura 3.27 - Diagrama temporal dos sinais de controlo.
É possível verificar na figura 3.27 o que foi exposto anteriormente. Posteriormente será
também exibido o código em C onde será perceptível todo este encadeamento de operações.
Outro dos aspectos a ter em conta é que o primeiro resultado de cada EP é sempre para
descartar, pois o primeiro ciclo de relógio para cada EP serve apenas para carregar o registo
correspondente a H(i – 1, j – 1) com o valor correto. De mencionar que, no ciclo seguinte ao
triggerpl ativado, o funcionamento retoma a normalidade, como já foi apresentado.
Figura 3.28 - MUXs criados para seleção das entradas dos EPs.
A figura 3.28 mostra os MUXs usados para resolver este problema ao mesmo tempo que ajuda
a compreender o funcionamento do carregamento de dados.
Existe ainda um valor especial, designado na figura 3.26 por corner, que é preciso ter em
atenção quando se tem várias divisões. Este valor é uma diagonal que vai ser carregado como
tal, no entanto é preciso carregá-lo a partir dos dados da iteração e não da subiteração como
seria normal, pois este corner é o H(i, j) do EP6 da sub-iteração superior esquerda. Para
resolver este dilema criou-se um registo específico para este valor, o corner. Este irá ser
utilizado em duas sub-iterações como é possível verificar na figura 3.26.
53
Quanto aos dados da iteração, ou seja, a coluna de valores intermédios, é um pouco mais
simples. Como se pode verificar na figura 3.26, os dados que são necessários neste caso são
sempre os do último EP. Assim sendo, basta armazenar numa memoria esses valores no tempo
correto. Quanto ao carregamento, estes também vão ser sempre carregados no EP1, podendo
assim atribuir-se à entrada h_in do EP1 o valor dos dados da iteração.
Para além destas últimas alterações, foi ainda necessário introduzir mais 2 triggers, para
controlo da escrita dos dados de saída dos EPs. O trigger2 irá servir para dar indicação de
quando se deve guardar os dados da subiteração e o trigger4 para guardar os valores dos
máximos. Posteriormente será explicado como irão funcionar estes dois triggers e onde vão
ser utilizados. Note-se que embora estes dois triggers se encontrem dentro da UF o seu efeito
vai-se refletir fora da mesma.
Figura 3.29 - Arquitetura com alterações para partição de sequências.
Na figura 3.29 está representada a arquitetura para o UF já com as alterações necessárias para
a partição das sequências.
Quanto ao armazenamento dos dados para a partição de sequências, esses foram guardados
de duas formas diferentes. Tomando como exemplo a figura 3.26 em que temos 6 EPs, os
dados da iteração, a última coluna de uma partição, a qual é dada sempre pelo último EP,
neste caso EP6 é armazenada numa memória. Isto porque o acesso de leitura é sempre feito
pelo EP1 e a escrita é feita pelo EP6. Desta forma, temos apenas um EP a aceder à memória
em cada um dos casos, simplificando muito a tarefa.
Para a sub-iteração, uma vez que cada um dos EPs tem que escrever um valor, optou-se por
registos e não por memórias, pois assim cada EP tem um registo próprio em que pode
escrever. Caso se optasse por memória seria necessário construir um MUX com o número de
54
entradas igual ao número de EPs existentes e seguidamente arranjar um sinal de seleção para
decidir qual o EP a escrever para a memória em cada instante. A solução encontrada é muito
mais simples e muito mais intuitiva.
3.5.2.1 Alterações aos ficheiros .c e .h da UF Linear Gap Penalty
Tal como foi dito anteriormente, a base destes ficheiros vai permanecer a mesma. Foram
então inseridas as entradas adicionais, o trigger2_in, o trigger4_in, o top_in e o diag_in e
também mais duas saídas o trigger2_out e o trigger4_out.
Tal como aconteceu anteriormente, também estes novos elementos vão ter que ser
inicializados, exatamente da mesma forma que os outros foram. Tanto a LUT como as
atribuições de variáveis iniciais também irão permanecer na mesma. A grande mudança vem a
seguir, com a inicialização do funcionamento do EP e a atribuição das entradas às respectivas
variáveis.
Figura 3.30 - Código alterado para partição de sequências.
Como se pode ver na figura 3.30, assim que o sinal de enable é ‘1’ e o trigger_in, que
anteriormente já existia, é ‘1’ também, vão ser carregados para as variáveis os valores, sendo
que no caso de Hi1_j, que corresponde à entrada H(i – 1, j) esta vai receber o valor de diag_in.
Isto é nem mais nem menos que o MUX apresentado anteriormente com a entrada de seleção
trigger_in.
Se atentarmos agora às primeiras duas linhas de código da figura, podemos ver que o triggerpl
irá ter sempre um ciclo de relógio de atraso em relação ao trigger_in, uma vez que este último
está a ‘1’, o seu valor é armazenado na variável triggeraux e apenas no próximo ciclo de
55
relógio é que o triggerpl vai receber esse valor. E assim que o triggerpl é ‘1’, vai dar-se início a
uma nova fase, com o carregamento de top_in para a variável Hi_j1, que corresponde ao
registo H(i, j – 1) da figura 3.29. Este top_in é o valor da subiteração anteriormente dito.
Depois dos 2 triggers estarem a zero e olhando para a figura 3.27, podemos verificar que o
funcionamento retoma o rumo normal e tudo o que foi feito até aqui permanece válido, não
havendo mudanças no funcionamento interno dos EPs.
3.5.2.2 Alterações aos ficheiros XML e VHDL da UF Linear Gap Penalty
As alterações ao XML são mínimas, uma vez que este apenas contém os sinais de entrada e
saída. Assim, apenas foi necessário acrescentar as 4 novas entradas e as 2 saídas com os
respetivos tamanhos, tal como se poderá comprovar na figura 3.31.
Figura 3.31 - Ficheiro XML com novas entradas e saídas.
Quanto ao VHDL houve algumas mudanças, uma vez que foi necessário introduzir os 2 MUXs
criados e os registos para o trigger2, trigger4 e triggerpl.
Começando pelos registos dos triggers, estes são iguais aos já existentes, tal como se pode
examinar na figura 3.32.
56
Figura 3.32 - Registos triggerpl, trigger2 e trigger4.
Quanto aos MUXs, tal como foi referido, estes vão ter como sinal de controlo o trigger_in e o
q7 que é o sinal de saída do registo do trigger_in tal como foi apresentado anteriormente, para
selecionar entre o modo de operação normal ou a seleção dos dados armazenados para as
partições.
Figura 3.33 - MUXs implementados em VHDL.
Como se pode constatar, as alterações não foram muito significativas em termos código da UF.
Na realidade, a estruturação e preparação prévia à implementação permitiram chegar a este
ponto, assim como a realização de vários testes para confirmar o bom funcionamento de todo
o modelo. Estes foram em grande parte feitos no ambiente CWide, usando o projeto já
existente para a UF anterior e analisando apenas os resultados para garantir os resultados
correctos.
57
3.5.3 Alterações à Unidade Funcional Trigger
A UF trigger foi significativamente alterada. Agora, esta passa a conter um contador e um sinal
de entrada para receber o valor de fim do contador, para que se possam utilizar registos com
enable para guardar os dados das iterações e sub-iterações e o valor dos máximos.
Em termos de entradas é adicionada apenas uma nova, o countin_in, e quanto a saídas não
existem mudanças. No interior desta, no seu núcleo, é que há mudanças, como se poderá
atestar na figura 3.34.
Figura 3.34 - UF Trigger com o novo contador implementado.
Na figura 3.34 é possível ver a implementação do contador, tanto em VHDL, do lado esquerdo,
como em C, do lado direito. Esta nova funcionalidade permite ter um maior controlo, pois
agora é possível escolher o ciclo de relógio exato em que se pretende que o trigger esteja a
um, bastando para isso definir o valor do contador. Assim, o trigger vai ter de ser
implementado na datapath várias vezes, para servir como sinal de controlo para os registos
com enable.
3.5.4 Datapath com partição de sequências
A datapath foi outra das partes que teve mudanças significativas. Tomando mais uma vez
como exemplo uma datapath com 10 EPs, adicionaram-se primeiro 21 registos: 10 para
58
servirem como entrada para os EPs, ou seja, servem como leitura dos dados da sub-iteração,
esses ficarão apelidados de HighH; outros 10 vão ser utilizados para escrever os dados da sub-
iteração, ou seja, os valores que se pretendem guardar para a subiteração seguinte, esses
ficaram designados por LowH; por fim, um para servir de entrada para o corner.
Figura 3.35 - Registos para armazenamento e leitura dos dados da subiteração.
Além destes registos, foram ainda definidos mais 4 triggers e ainda os registos LineSavein,
ColSavein e MaxSavein como se pode observar na figura 3.36.
Figura 3.36 - Definições adicionais.
O registo LineSavein vai guardar o número de ciclos de relógio que é necessário aguardar para
se armazenar os dados de uma subiteração. Este valor vai ser a entrada do contador do trigger.
Da mesma forma, o ColSavein serve para guardar os dados da iteração enquanto o MaxSavein
contém o número de ciclos de relógio para guardar os máximos.
Na figura 3.36 pode ainda observar-se a implementação de um novo BAU, que irá servir para
gerar os endereços da memória que irá guardar os dados das iterações, ou seja, os valores de
H(i, j) do último EP. Além disso são ainda definidos mais 4 triggers que vão ser necessários
para armazenar os dados das partições e os máximos.
59
Passando à implementação da datapath, como se poderá observar na figura 3.37, em termos
do seu funcionamento, começa-se por fazer as atribuições dos registos recentemente
definidos.
Figura 3.37 - Datapath: atribuições aos registos, triggers e BAUs.
Depois disso, passamos para os triggers e aqui, para além do enable necessário anteriormente,
fornece-se também o valor do contador. Nesta datapath são também definidos 2 BAUs, sendo
o colsave utilizado para guardar os valores dos dados das iterações, as colunas de resultados
que necessitam de ser guardadas entre cada iteração.
Com todas as alterações sofridas pelos EPs, até em termos de novas entradas, os cabeçalhos
da UF linear_gap_penalty tiveram que ser alterados.
60
Figura 3.38 - Cabeçalhos das UFs linear_gap_penalty.
Como se pode constatar na figura 3.38, embora algumas partes fiquem iguais, outras foram
adicionadas, para permitir a integração tanto dos novos triggers como da entrada dos dados
de subiteração, ou seja, as entradas com as variáveis HighH.
Figura 3.39 - Armazenamento dos máximos e dos dados das partições.
A última linha da figura 3.39 mostra exatamente o que se explicou com os valores H(i, j) do
EP10 a serem guardados na memória 7 assim que o sinal colsave.out começa a gerar
endereços.
Na figura 3.39 são ainda visíveis novos registos, F_REG_WEN_11(). Estes registos com enable,
permitem armazenar, no caso do máximo, apenas quando a saída trigger4_out de cada um dos
EPs é ativa. Esta saída é o TR4 apresentado na figura 3.36, o qual recebe o valor do registo
61
MaxSavein. Este TR4 é uma entrada no EP1 e o seu valor é depois propagado ao longo da
cadeia de EPs. Quanto aos registos LowH, estes vão armazenar os dados necessários para a
subiteração seguinte, H(i, j) de cada um dos EPs.
O resto da datapath mantém as características da apresentada no capitulo 3.4.3. De seguida,
na figura 3.40, é apresentada a arquitetura da datapath anteriormente explicada.
Figura 3.40 - Arquitetura da datapath com partição de sequências implementada.
3.6 Implementação do sistema de alinhamento
Depois de construídas as UFs e a datapath, chegou a altura de elaborar o software. Em
primeiro lugar, será feita a apresentação da implementação necessária para realizar a
62
simulação e só depois o funcionamento em hardware, uma vez que o desenvolvimento do
projeto também seguiu esta ordem, pois com a simulação a funcionar, é mais fácil fazer a
migração para a parte embebida.
Para se fazer o desenvolvimento da simulação e da implementação em hardware, tal como
referido na secção 2.2.3, são necessárias três configurações distintas no CWide: SIM, TEST e
EMB. O software irá distinguir as várias configurações através das macros #ifdef como é
possível observar na figura 3.41.
Figura 3.41 - Exemplo de macros para as várias configurações.
O processo de alinhamento segue um fluxo predefinido, de modo a que todas as sequências
fixas sejam comparadas com todas as sequências variáveis presentes nos respectivos ficheiros.
Assim dá-se inicio ao processo com a leitura de uma sequência fixa e de seguida uma
sequência variável e só posteriormente se procederá à partição de cada uma delas. Primeiro,
faz-se a partição da sequência fixa e vai alinhar-se essa partição com a sequência variável. Esta
é então particionada e no fim da última partição volta-se à sequência fixa para ver se ainda
falta alguma partição desta. No caso de termos chegado ao fim, vamos então comparar a
próxima sequência variável com esta sequência fixa. Só depois de todas as sequências
variáveis terem sido comparadas com uma sequência fixa é que se volta a ler o ficheiro das
sequências fixas para passarmos à próxima. Este processo encontra-se ilustrado no fluxograma
da figura 3.42.
63
Figura 3.42 - Fluxograma do funcionamento do programa
3.6.1 Simulação em software
Começando, então, com o desenvolvimento da simulação, o primeiro passo foi a leitura dos
ficheiros com a matriz de substituição para o ADN e as sequências variável e fixa. A matriz
normalmente usada para alinhamento de ADN é bastante simples, pois caso exista uma
igualdade o valor retornado é 2, caso contrário é -1 [1].
64
Figura 3.43 – Matriz de substituição para o ADN.
A figura 3.43 mostra uma matriz de substituição igual à usada no ficheiro de carregamento
desta matriz, sendo o carregamento do mesmo feito para uma matriz de 20x20. Embora não
sejam necessárias tantas posições para o ADN, optou-se por este processo para que
posteriormente se possam fazer implementações para o alinhamento de até 20 aminoácidos.
Carregada a matriz de substituição, passou-se ao tratamento das sequências fixa e variável.
Neste caso, não precisamos de uma matriz, mas sim de um único vector, cujo tamanho
utilizado foi VEC_SIZE = 128. Este vector vai ser posteriormente enviado para o hardware
sendo que o seu tamanho fica limitado a este valor. As duas sequências vão ser lidas e
armazenadas exatamente da mesma forma, sendo que os ficheiros de entrada terão o mesmo
formato. As funções utilizadas para a leitura das sequências são: SF_read_init() e
BIB_read_init() para as sequências fixa e variável, respetivamente.
Figura 3.44 - Exemplo de ficheiros com sequências fixas em cima e sequências varáveis em baixo.
Cada uma das letras das sequências vais ser transformada num dígito, e uma vez que no caso
do ADN apenas existem 4 variações possíveis, apenas serão necessários 4 dígitos o que
corresponde a 2 bits. Assim atribuímos a seguinte codificação: A=0, C=1, G=2 e T=3.
Para os ficheiros com várias sequências, como é o caso dos ficheiros da figura 3.44, vai ser lida
e armazenada uma sequência de cada vez. Depois da primeira ter sido analisada passa-se à
segunda e assim sucessivamente. É importante referir que as funções de leitura das sequências
retornam o comprimento das mesmas, sabendo-se assim qual comprimento de cada uma
delas. Caso já não exista nenhuma sequência para analisar no ficheiro retornam ‘-1’.
Tendo estas funções construídas, passou-se ao desenvolvimento da função main. Um dos
primeiros procedimentos a fazer é o carregamento da configuração que estamos a utilizar, ou
seja, da datapath. Para isso usa-se o comando siw_ld_conf(cwt01_siw.sw_3pe) em que o
argumento é o nome da datapath que se está a usar.
De seguida, passou-se à parte nuclear do software. Deste modo, procurou-se um vector para
armazenar todos os resultados auxiliares, como o comprimento total das sequências, o
comprimento da parte em análise, o valor de α, etc. Esse vector apelidou-se de BIBSF. Assim, o
primeiro vector a ser lido foi o da sequência fixa, isto porque cada sequência fixa tem de ser
comparada com todas as sequências variáveis e, assim, poder-se-á carregar o vector com a
sequência pretendida para, de seguida, se proceder à leitura das sequências variáveis. Da
65
mesma forma irá ser lida uma sequência variável, mas de cada vez que uma acaba irá ser lida a
seguinte até se chegar ao fim do ficheiro.
Uma vez que por cada nova sequência variável se faz um novo alinhamento, terá que se fazer
reset a todos os vectores com resultados intermédios, como os valores dos máximos, os
valores guardados para as iterações e sub-iterações e a coluna que guarda os valores das
iterações e sub-iterações.
Figura 3.45 – Excerto de código da simulação; partições das sequências fixa e variável.
Depois de lida uma sequência fixa e atualizados os valores do vector auxiliar, proceder-se-á à
partição da sequência, utilizando a função part_sf(), como se poderá ver na figura 3.45. Esta
função recebe como argumentos a sequência fixa que irá ser analisada, o tamanho desta e o
número de partições que já foram feitas até ao momento. De seguida faz uma partição
completa da sequência fixa e devolve o número de partições que já foram feitas até ao
momento. Exemplificando, considerando 10 EP, cada parte da sequência fixa terá que ter 10
elementos. Se existir uma sequência com 45 elementos para serem analisados, estes têm que
ser divididos em 5 subsequências. Cada uma é armazenada num vector e este será ainda
analisado, para sabermos se este contém todos os seus elementos, recorrendo-se à função
lastpartSF(). É importante conhecer o tamanho do último vector, para se saber onde está o
máximo e para que no hardware se executem o número de ciclos adequados ao tamanho das
partições. Cada sequência vai ser particionada até ao fim, não se prosseguindo para a
sequência seguinte enquanto a primeira não acabar.
Quanto à sequência variável, a operação é muito semelhante à anterior. Vai ser lida, do
mesmo modo, uma sequência completa para um vector e depois vai ser feita a separação em
partes, tal como para a sequência fixa. Vai ser calculado o tamanho de cada parte, da mesma
forma que para a sequência fixa, para se poder controlar o número de ciclos de relógio que o
hardware vai estar em funcionamento e os dados que irão ser carregados para as iterações e
66
sub-iterações. Neste caso, vai-se percorrer o ficheiro das sequências variáveis até ao fim, pois,
como já foi dito anteriormente, cada sequência fixa é comparada com todas as sequências
variáveis, ou seja, por cada sequência fixa que aparecer, o ficheiro com as sequências variáveis
é lido até ao fim.
Neste momento já possuímos todas as informações necessárias para o tratamento da
sequência fixa, passando-se, então, à inicialização dos vectores da LUT, através da função
init_vecs(), representada também na figura 3.45. Esta última função recebe como argumentos
a partição da sequência fixa que vai ser analisada, o tamanho da mesma e a matriz de
substituição. Vão ser criados 4 vectores para os valores da LUT, tal como já foi referido. A sua
inicialização faz-se recorrendo ao ciclo que se apresenta na figura 3.44.
Figura 3.46 - Inicialização dos vectores da LUT.
Este ciclo lê a variável que está em causa na sequência fixa, percorre cada uma das colunas da
LUT e armazena cada valor num vector. Por outras palavras, vai ser armazenada a coluna
correspondente ao elemento da sequência fixa, mas um valor em cada vector. Desta forma,
quando se for fazer a leitura no hardware, para cada EP, vamos ter 4 memórias ligadas e cada
uma delas irá dar um valor apenas num determinado ciclo de relógio e recorrendo apenas a
um endereço de memória. No ciclo de relógio e endereço seguintes ler-se-ão os dados do EP
seguinte. Assim, minimizam-se os tempos de carregamento das LUTs. De referir igualmente
que apenas se vai começar a escrever nas segundas posições dos vectores, para introduzir um
atraso na LUT, pois o primeiro resultado de cada EP é descartado. Este atraso é necessário
para se poderem inicializar os registos do EP.
Figura 3.47 - Excerto de código da simulação; funções para carregamento de memórias e registos.
Depois de se ter os vectores com as sequências, LUT e vectores das iterações e sub-iterações
prontos, é altura de os passar para as memórias da datapath. A função SF_init(), que se pode
observar na figura 3.47, encarrega-se da sequência fixa, que depois de todos os processos por
que passou são os vectores da LUT. Assim, esta função apenas recebe os vectores da LUT e faz
67
o seu carregamento para as respectivas memórias na datapath, recorrendo-se, para isso, à
função siw_ld_vec() como se pode verificar na figura 3.48.
Figura 3.48 - Carregamento dos vectores da LUT para a datapath.
Observando novamente a figura 3.47, a função SV_init() para além da sequência variável vai
também carregar o vector com os dados das iterações, ou seja, a coluna que necessita de ser
carregada para servir de entrada para o H(i – 1, j); o vector que contém os dados das sub-
iterações que corresponde à entrada top_in na figura 3.28, sendo estes os valores que cada
um dos EPs vai ter que receber no inicio do seu funcionamento para continuar a partir da
partição anterior; o máximo de H até ao momento e o valor de α.
Para se fazer o carregamento destes valores usam-se as funções representadas na figura 3.49,
sendo que para o carregamento dos vectores utiliza-se a mesma função já usada
anteriormente, siw_ld_vec(), enquanto para o carregamento dos registos se usa
siw_set_state(). Esta última vai carregar para um registo da datapath, anteriormente
apresentada, o valor que lhe for passado como argumento.
O valor do corner vai ser carregado a partir da posição zero do vector que contém os dados da
iteração. Isto acontece porque previamente foi armazenado nessa posição o valor
correspondente, ou seja, a última posição do vector que contém a coluna previamente
armazenada. Assim para uma partição este valor vai ser a última posição de uma memória
enquanto para outra vai ser o registo corner.
Figura 3.49 - Função "SV_init" .
Vamos ter dois casos completamente diferentes para os vectores que contêm os dados das
iterações e das sub-iterações, pois em cada sub-iteração o vector correspondente é carregado
com novos valores e não com o que é recebido do cálculo.
68
L1 L4 L7
C1 Part 1
C4 Part 4
C7 Part 7
L2 L5 L8
C2 Part 2
C5 Part 5
C8 Part 8
L3 L6 L9
C3 Part 3
C6 Part 6
C9 Part 9
Figura 3.50 - Matriz com todas as linhas e colunas para se fazerem carregamentos.
Recorrendo à figura 3.50, quando se está a fazer a Part 1, os valores que são necessários
armazenar relativamente às colunas, são os correspondentes a C4. Para a Part 2 será C5 e à
Part 3 corresponderá C6. Estes valores são todos recebidos através do vector das iterações
quando se faz o cálculo e armazenados numa matriz recorrendo à função colstore(), o que se
pode observar na figura 3.47. No momento em que se vai fazer a Part 4 é carregado para o
vector da iteração os valores correspondentes a C4. No final do cálculo, o vector da iteração,
que corresponde a C7, vai ser guardado na mesma posição de memória que C4, sendo
carregado para o vector da iteração o C5. Assim, consegue-se poupar em termos de espaço
utilizado no armazenamento dos vectores. Outra particularidade deste vector é que as duas
primeiras posições de memórias são iguais ao corner, isto porque é necessário fazer o
carregamento do registo correspondente a H(i – 1, j – 1) no primeiro ciclo de relógio e no ciclo
seguinte carregar o registo H(i – 1, j), tal como foi explicado previamente.
Observando novamente a figura 3.47, depois do carregamento dos dados com a função
SV_init(), aparece a função SIW_Calc(). Vai ser nesta última que irá estar a função responsável
por dar início ao funcionamento da datapath. No entanto, vão ainda ser carregados antes os
valores para os registos de controlo, como se poderá constatar na figura 3.51.
Figura 3.51 - Funções de carregamento e armazenamento de dados e controlo da datapath.
Os registos que contêm os valores de controlo dos triggers, que vão depois controlar o tempo
do armazenamento de dados do máximo, dos valores das sub-iterações e dos valores das
iterações, vão ser carregados agora, bem como o número de ciclos total que a configuração
deve estar a ser executada, através do registo loop_end_k_state.
69
Neste momento estão todos os dados carregados e está tudo pronto para se proceder ao
cálculo. É então chamada a função siw_run(), para que a configuração do Sideworks
selecionada inicie o processamento. A configuração vai então ser executada durante o número
de ciclos previamente indicados.
Uma vez terminado o cálculo, os registos que contêm os máximos e os dados das subiterações
são de novo armazenados nos vectores, tal como se poderá comprovar através da figura 3.52.
Figura 3.52 - Armazenamento dos máximos e dados da subiteração.
Finalizado o cálculo de todas as partições, analisa-se o valor do máximo para saber se este
atinge o valor mínimo necessário para que se realizar o traceback. A implementação do
processo de traceback não foi a mais eficaz, pois este não era o objectivo inicial do projeto. A
intenção inicial passava por utilizar as próprias rotinas do SSEARCH, integrando nele as
chamadas ao hardware do Sideworks. No entanto essa tarefa não foi possível de realizar em
tempo útil, recorrendo-se então a esta solução.
Esta implementação do traceback introduz uma limitação, que consiste no número de
elementos de cada sequência que é possível analisar, que como veremos na secção 4.3 está
limitado a 600 elementos . De cada vez que é chamada, a função constrói uma matriz até
encontrar o máximo previamente calculado. Atingido esse valor e sabendo qual a posição em
que se encontra, far-se-á o traceback utilizando a fórmula matemática (eq. (2)) até se atingir a
posição inicial. De seguida, preenchem-se 2 vectores com o alinhamento local das sequências,
sendo esses vectores escritos, posteriormente, num ficheiro.
Recorde-se que a simulação utiliza apenas o computador, não recorrendo em momento algum
ao hardware.
70
3.6.2 Implementação em Hardware
Uma vez concluída a simulação com sucesso, passa-se à implementação em hardware. Nesta,
as funções utilizadas serão as mesmas que para o caso anterior, com exceção da adição das
funções de envio de dados do computador para o hardware e vice-versa. Tal como referido na
secção 3.6, a implementação recorre a duas configurações do CWide, a EMB e a TEST.
Figura 3.53 – Excerto de código da implementação em hardware.
Tal como se poderá comprovar através da figura 3.53, as funções utilizadas são as mesmas que
para a simulação. Aqui, a diferença é que estas são chamadas cada uma na configuração
devida. As funções de leitura dos ficheiros são apenas chamadas no TEST, ou seja, no
computador visto que os ficheiros se encontram aí, enquanto a SIW_conf() apenas no EMB.
Na figura 3.53 são observáveis as funções de transferência e recepção de dados. Assim, a
função TEST_data_send(BIBSF) chamada no TEST vai enviar o vector BIBSF para o hardware,
para que esteja disponível com os valores atualizados, sendo que a função de recepção de
dados no hardware é a EMB_data_send(BIBSF). De cada vez que se faz uma atualização de
valores no TEST, os vectores correspondentes vão ser enviados para o EMB, para que este
esteja atualizado e possa utilizar esses valores. Por exemplo em relação ao vector BIBSF há
necessidade de ter os dados mais recentes, pois os ciclos while correm em ambas as
configurações e os dados de BIBSF são utilizados como argumentos sendo que é necessário
que estejam sempre atualizados. O contrário também é válido, caso tenham sido atualizados
dados no EMB, o que acontece, por exemplo, na função SIW_Calc(), quando é calculada a
matriz H, os dados vão ter que ser enviados para o TEST. Nesse caso, no EMB utiliza-se
71
EMB_data_receive() para enviar os dados e TEST_data_receive() para receber no TEST, como
se poderá ver na figura 3.54.
Figura 3.54 - Exemplo do envio de vectores do hardware para o pc.
Com isto se finaliza a implementação em hardware. Embora a explicação e todo o processo
tenha sido, aparentemente, bastante simples na realidade a implementação em hardware foi
muito mais complicada e demorada que a simulação. Verificámos que só conseguimos saber
os valores dos registos e memórias de saída, configurados na datapath, não sendo possível
aceder aos valores internos da implementação VHDL. Assim sendo, a única forma de fazer
debug é imprimir os valores das memórias e registos de saída para o terminal. De salientar
também que cada vez que se pretende alterar a datapath, um novo bit file tem que ser gerado,
demorando aproximadamente 1 hora a gerar pois para além da nossa arquitetura tem
também a da Coreworks.
3.7 Conclusão
Neste capitulo foi explanada a implementação do projeto, desde o estudo e tentativas iniciais
até à solução final. Numa primeira análise mostrou-se o que se havia dito na introdução
teórica: a parte crítica do algoritmo S-W é a construção da matriz H.
Com uma arquitetura adequada desenvolveu-se então o projeto para a plataforma da
Coreworks, numa primeira aproximação com um desenvolvimento mais simples, e depois
implementando uma solução final com partição de sequências.
De referir que de cada vez que se carrega uma sequência fixa não é necessário gerar um .bit
novo, apenas carregar os vectores da LUT com os respectivos valores.
A solução encontrada e desenvolvida permite uma grande flexibilidade e vai acelerar muito o
algoritmo, indo de encontro ao pretendido no inicio do projeto.
72
73
4 Resultados
4.1 Introdução
Neste capítulo serão apresentados os resultados não só do funcionamento do sistema em si,
mas também os resultados da ocupação espacial da implementação, os resultados temporais
do cálculo da matriz para retorno do máximo e ainda do tempo de execução total.
Vão ainda ser apresentadas as limitações presentes no projeto e os resultados que se
poderiam obter caso algumas dessas limitações pudessem ser mitigadas.
Figura 4.1 - Comunicação entre o computador e a FPGA.
Na figura 4.1 é possível ver a comunicação entre o computador e a FPGA, sendo a
comunicação de dados feita através da porta ethernet e a comunicação da interface de
controlo através de RS232, sendo depois o conteúdo exibido no hyperterminal.
4.2 Apresentação dos resultados
Tal como foi referido no capitulo 3, os resultados são apresentados num ficheiro. Este ficheiro
é escrito pela função responsável pelo traceback. Esta escreve no ficheiro as sequências que
analisou, o valor do máximo e o melhor alinhamento local encontrado.
74
Figura 4.2 - Resultados.
A figura 4.2 mostra o ficheiro de resultados de um conjunto de sequências geradas
aleatoriamente e com as sequências completas a aparecem logo a seguir a “Testing
Sequences”, sendo depois apresentado o valor máximo da matriz H aqui designado por
“score”. Por último, é apresentado o alinhamento local. Cada ficheiro de resultados irá expor
todas as sequências que apresentem um máximo superior ao limite definido para se fazer o
traceback. As que não atinjam esse valor não será feito o traceback e passar-se-á à próxima
sequência a alinhar.
Optou-se por não se apresentar os resultados no hyperterminal que comunica com a FPGA,
através da comunicação da interface de controlo mostrada na figura 4.1, para não se obterem
atrasos relacionados com os prints, uma vez que estes necessitam de bastante tempo para
escrever, introduzindo atraso. Assim apenas se escrevem os resultados para o respectivo
ficheiro, sendo que o anexo 2 apresenta um ficheiro de resultados completo.
4.3 Limitações
As limitações, embora nunca sejam desejáveis, existem. Neste projeto, a maior e mais
prejudicial das limitações para o desempenho, foi o número máximo de EPs que é possível ter,
75
pois quanto menos EPs mais divisões se vão fazer e mais tempo se perde com o envio e a
recepção e dados.
Com a plataforma atual da Coreworks apenas nos foi possível instanciar 30 EPs. Foram feitas
várias tentativas no sentido de aumentar esse valor, nomeadamente com 40, 50 e 60 EPs, mas
sem resultado. Pensou-se inclusivamente que fosse um problema de acesso à memória por
parte de muitas UFs e, por isso, fizeram-se testes com 2 registos intermédios e ligando apenas
esses à memória e metade das UFs a cada um deles, mas sem resultado. Esta é uma limitação
da plataforma da Coreworks e não da arquitetura desenvolvida, para a qual não descobrimos
solução em tempo útil.
Assim, o número máximo de elementos da sequência fixa que é possível analisar em cada
iteração é de 30, obrigando a várias divisões para cada sequência analisada, pois, geralmente,
cada sequência tem algumas centenas de elementos.
Tal como referido na secção 3.5, o número de elementos da sequência variável também está
limitado. Decidiu-se que esse valor seria de 120 elementos, apesar de teoricamente se
conseguir 128. No entanto, na prática isso não é possível, pois existem 3 ciclos de atraso,
desde a disponibilização dos elementos nos EPs até ao início do cálculo, sendo que no limite
seria possível fazer cálculos com 125 elementos. Para se ter alguma margem de manobra,
decidiu-se baixar esse valor para 120 elementos. De referir que esta limitação é imposta pela
forma como foi desenvolvido o projeto e pela sua implementação. Assim sendo, cada iteração
poderá ser no máximo de 30x120 elementos.
Relativamente ao número máximo de elementos de cada uma das sequências, pensou-se
inicialmente que seria de 600 e estaria limitado pelo traceback. Isto porque, no pior dos casos,
o traceback teria que construir uma matriz de 600X600. Foram testadas matrizes com 700X700
e 650X650 elementos, mas não se conseguiu executar a aplicação com estes tamanhos
ficando-se assim pelos 600 elementos por sequência.
No entanto, o limite de elementos por sequência foi revisto em baixa para 511, pois os valores
dos máximos em hardware estão implementados em registos de 11 bits “signed”. Assim,
temos 10 bits disponíveis para o máximo o que dá um valor limite para o máximo de 1023.
Como se está a usar uma matriz de substituição com o máximo de , o número de
elementos máximo, no pior caso, será 1023 ÷ 2 = 511,5 elementos, ou seja, o número máximo
é de 511 elementos.
Tabela 2 - Limites das sequências.
Tamanho da partição
(n. elementos) Tamanho máximo
(n. elementos)
Sequência fixa
30 511
Sequência variável
120 511
Na tabela 2 apresentam-se as limitações para a arquitetura em termos do número de
elementos máximo por sequência. Na coluna tamanho da partição é apresentado o limite
76
máximo de cada uma da sequências para uma partição apenas. Quanto ao tamanho máximo,
aqui é apresentado o tamanho máximo que uma sequência pode ter sem qualquer partição,
ou seja o tamanho que pode ter no ficheiro de entrada.
4.4 Área ocupada
A área ocupada será, em grande parte, preenchida por toda a plataforma da Coreworks. A
FPGA utilizada foi uma Spartan3 xc3s5000, a qual tem 33,280 slices disponíveis.
Um EP apenas, sem estar integrado no projeto, ocupa 99 Slices. Este valor contrasta em muito
com os 10788 slices que são ocupados quando se faz a integração do EP com a arquitetura da
Coreworks. No entanto comparando com o valor total de slices disponíveis, constata-se que
apenas se ocupa um terço do espaço total disponível.
Tabela 3 - Ocupação para o projecto com várias configurações de EPs.
Número de
Slices ocupados Número de LUTs de
4 entradas
1 EP 10 788 17 193
10 EPs 12 025 19 037
20 EPs 13 546 21 198
30 EPs 14 805 23 300
A tabela 3 mostra o número de slices ocupados por cada uma das configurações. Fazendo uma
média dos valores, chega-se a um valor de 134 slices ocupados por cada EP. Com este
resultado, concluímos que seria possível implementar mais 138 EPs, ou seja, um total de 168
EPs, caso não houvesse a limitação apresentada anteriormente. Com este número os ganhos
seriam enormes para sequências grandes, pois o número de partições diminuiria
consideravelmente.
4.5 Análise temporal e de desempenho
O primeiro resultado que se obteve foi o período mínimo de relógio possível no hardware.
Esse valor é de 27.7 ns, a que corresponde uma frequência máxima de 36 Mhz, tendo sido
estes os valores usados para as análises apresentadas nesta secção. O caminho crítico que irá
determinar este tempo não é dado pela arquitetura do EP.
77
Na tabela 4 são apresentados os tempos de algumas operações importantes, nomeadamente
o tempo de carregamento e leitura das memórias e registos, em número de ciclos de relógio.
Tabela 4 - Tempos de carregamentos/armazenamento.
Média (n. ciclos)
1. Tempo de load de uma configuração (siw_ld_conf())
172
2. Carregamento das memórias da datapath
367
3. Leitura das memórias da datapath
244
4. Carregamento de registos da datapath
20
5. Leitura de registos da datapath
26
6. Vectores recebidos no hardware
13279
7. Envio de vectores para o computador
1004
É apresentado o tempo médio, pois o tempo pode variar ligeiramente dependendo do número
de dados que se pretendem carregar ou ler. A primeira linha apresenta o tempo de
carregamento de uma configuração do Sideworks. Na linha 2 apresenta o tempo de
carregamento médio de uma memória do Fireworks para o Sideworks, enquanto a linha 3 nos
mostra o contrário, a leitura de uma memória, vinda do Sideworks, para o Fireworks. As linhas
4 e 5 apresentam o tempo de carregamento de um registo do Fireworks para Sideworks e o
contrário, respectivamente. Tanto as memórias como os registos serão usados na configuração
que contém a datapath. Quanto à linha 6, esta apresenta o envio de um vector do “TEST” para
o “EMB”, ou seja, do computador para a placa, sendo que a linha 7 contém o contrário.
Como se pode verificar, quer os valores de carregamento, quer os de leitura são bastante
elevados, em particular quando comparados com o tempo de cálculo dos EPs, como se verá de
seguida.
Na tabela 5 apresentam-se os tempos de execução, em termos do número de ciclos de relógio,
e o desempenho em cell updates per second (CUPS), uma medida normalmente usada em
bioinformática e que indica o número de elementos da matriz H que são atualizados por
segundo.
Na primeira coluna é apresentado o tamanho total das sequências, sendo que na segunda se
apresenta o número de partições que foi necessário fazer para analisar as sequências. A coluna
“N. ciclos por partição” mostra o número de ciclos de relógio que cada partição demorou para
fazer o cálculo, depois de ter as memórias e os registos carregados, ou seja, depois de estar
pronto a iniciar as suas funções, até à apresentação do valor máximo do alinhamento. A coluna
Cálculo do máximo, apresenta o desempenho calculado em relação à coluna anterior em CUPS.
A coluna Tempo total de cálculo mostra o tempo desde que o programa é iniciado até terminar
78
a sua operação. Na última coluna apresenta-se o desempenho relativamente ao Tempo total
de cálculo em termos de CUPS.
Tabela 5 - Desempenho da arquitetura para vários tipos de sequências.
Sequência Variável X Sequência Fixa
(n. células)
N. de partições
N. ciclos por
partição
Cálculo do máximo (CUPS)
Tempo total de cálculo
(μs)
Execução total
(CUPS)
1X1 (1) 1 72 0,5 M 15 67 K
10X1 (10) 1 72 5 M 15 0,7 M
1X20 (20) 1 84 9 M 15 1,3 M
1X30 (30) 1 84 13 M 15 2 M
2X30 (60) 1 84 26 M 15 4 M
5X30 (150) 1 84 66 M 15 10 M
10X30 (300) 1 84 132 M 15 20 M
20X30 (600) 1 92 242 M 15 40 M
30X30 (900) 1 104 320 M 15 60 M
120X30 (3 600) 1 192 694 M 15 240 M
60X60 (3 600) 2 134 498 M 30 120 M
60X120 (7 200) 4 192 347 M 38 189 M
360X120 (43 200) 12 196 680 M 93 464 M
510X510 (260 100) 85 196 655 M 470 553 M
Como era de esperar, a desempenho aumenta com o tamanho das sequências até ao ponto
em que é preciso fazer-se partições para resolver o problema das sequências muito grandes. O
melhor resultado obtém-se apenas com uma passagem e com o tamanho máximo das
sequências para esse efeito. É o caso de uma sequência fixa de 30 elementos e uma sequência
variável de 120 para a qual se obtém um desempenho máximo de 694 MCUPS. A partir do
momento em que é necessário fazer-se algumas partições, existe uma perda de performance,
como acontece para as sequências de 360X120 e 510X510, onde é necessário fazer 12 e 75
partições, respectivamente. O desempenho de um programa em C optimizado referido em [2]
é de 50 MCUPS, comparando com o melhor resultado aqui conseguido, consegue-se uma
aceleração de 13 vezes.
Comparando os resultados da tabela 4 com os da tabela 5, é possível constatar que o tempo
de execução é muito inferior ao tempo de carregamento de dados. Tal facto leva, mais uma
vez, à conclusão de que quanto menos partições, maior será o desempenho global do sistema
para realizar o alinhamento.
Comparando o desempenho obtido e exposto na tabela 5 com a de [2], onde os autores
conseguem um desempenho máximo de 12 GCUPS, verificamos que a nossa é inferior. No
79
entanto existem duas diferenças significativas relativamente à nossa implementação: os
autores do referido artigo, que conseguem usar 252 EPs, enquanto na nossa implementação
apenas conseguimos usar 30 EPs. A outra diferença prende-se com a frequência, sendo que os
autores do artigo conseguem uma frequência de 48 MHz, enquanto no nosso projeto apenas
conseguimos uma frequência máxima de 36 MHz.
Para este sistema, seria possível conseguir um desempenho muito superior caso não existisse
uma limitação no número de EPs e se utilizassemos todos os slices disponíveis, no máximo
teríamos 168 EPs. Para este número de EPs podemos verificar na tabela 6 o desempenho da
arquitetura para vários tamanhos de sequências.
Tabela 6 - Desempenho da arquitetura para vários tipos de sequências com 168 EPs.
Sequência Variável X Sequência Fixa
(n. células)
N. Ciclos médio por partição
CUPS – Cálculo do máximo
60X120 (7 200) 192 1.4 G
120X30 (3 600) 192 694 M
360X120 (43 200) 196 2.8 G
510X510 (260 100) 196 2.5 G
Como é possível observar, com 168 EPs já se conseguiria chegar aos GCUPS, o que seria uma
ordem de grandeza bastante interessante e em linha com o já conseguido por outras
arquiteturas, como por exemplo em [10]. Outro aumento de desempenho descomunal seria o
aumento do número de elementos da sequência que se conseguem fazer numa única partição.
Caso se conseguisse 512 EPs utilizando uma FPGA com um maior número de slices, para o caso
de 510X510 seria possível chegar aos 9.8 GCUPS.
4.6 Conclusão
Neste capítulo foram apresentados os resultados de desempenho. Foram também feitas as
análises espaciais e temporais. O projeto desenvolvido encontra-se totalmente funcional,
embora, como foi anteriormente referido, contenha algumas limitações. Estas são, em grande
parte, devido às atuais limitações do sistema da Coreworks.
O objectivo principal, que era um incremento do desempenho relativamente ao software, foi
conseguido pois obteve-se um aumento de performance de 13 vezes relativamente ao
software referido em [2].
80
Foi nossa preocupação, também, apresentar resultados para o caso de não existirem
limitações, os quais apresentam um aumento de desempenho mais significativo.
81
5 Conclusões
5.1 Conclusões Gerais
Esta dissertação teve como objetivos a implementação de uma arquitetura em hardware, para
realizar o alinhamento local de sequências, na plataforma Sideworks da Coreworks e a
realização do cálculo da matriz H do algoritmo S-W e retorno do respectivo máximo. No caso
de sequências muito grandes fazer as partições necessárias para que o cálculo seja possível.
No decorrer deste trabalho fez-se um estudo do algoritmo S-W e de uma implementação em
software, o SSEARCH, o que nos permitiu confirmar que a parte crítica do mesmo era,
efetivamente, o preenchimento da matriz H. Foi, então, estudada uma arquitetura que
permitia resolver esta questão e a qual se selecionou para implementar na plataforma
Sideworks.
Seguidamente abordaram-se as arquiteturas que permitem fazer o alinhamento de sequências
grandes numa plataforma de desenvolvimento com recursos limitados, usando a técnica de
partição do cálculo. Assim foi feita a adaptação da arquitetura já existente para uma
arquitetura final que permite o alinhamento de sequências usando partições.
O grande desafio do trabalho consistiu na implementação do projeto na plataforma Sideworks,
da Coreworks. No ambiente de desenvolvimento Cwide foram desenvolvidas as Unidades
Funcionais linear_gap_penalty onde está implementada a arquitetura que permite fazer o
alinhamento de sequências, juntamente com a Unidade Funcional trigger. Foi também feita
uma configuração para Sideworks com a datapath pretendida para a implementação do
projeto. De acordo com o fluxo de projeto da Coreworks foi, igualmente, desenvolvida uma
simulação em software de forma a permitir testar a implementação em hardware sendo assim
possível corrigir alguns erros. Desenvolveu-se ainda todo o software necessário para correr no
computador, fazer o tratamento das sequências antes de serem enviados para o hardware, a
comunicação com a placa utilizada e a apresentação de resultados.
Surgiram algumas limitações inerentes a este tipo de projetos, as quais foram impossíveis de
mitigar, em tempo útil, quer na parte do desenvolvimento e implementação do projeto, quer
na parte das limitações do próprio Sideworks. Estas restrições introduziram um limite no
número de elementos da sequência fixa e sequência variável a 511 elementos. Quanto às
partições estão limitadas a 30 elementos relativamente à sequência fixa, restrição esta devido
82
ao atual sistema de desenvolvimento da Coreworks e 120 para a sequência variável, pois é o
tamanho máximo dos vectores que se podem enviar do computador para a placa.
O projeto foi implementado numa Spartan3 xc3s5000 e a área ocupada pela arquitetura, está
limitada a um máximo de 14805 slices da FPGA que se está a utilizar, restando ainda 18475
slices por ocupar. Estes poderiam ser completamente preenchidos caso não houvesse
limitação no número de EPs por parte da atual plataforma de desenvolvimento.
Quanto ao desempenho da arquitetura para o cálculo do máximo conseguimos chegar aos
694MCUPS, para uma sequência de 30 elementos na sequência fixa e 120 na sequência
variável, com um tempo de execução total do programa de 15 μs. Nesta situação conseguiu-se
estimar um incremento de desempenho de 13 vezes relativamente a uma aplicação otimizada
em software referida em [10]. No entanto, estes resultados poderiam ser superiores caso não
existissem limitações em termos do número de EPs implementados.
5.2 Trabalhos Futuros
No desenvolvimento deste projeto ficaram bem patentes as limitações do mesmo, com grande
ênfase para os limites no número de elementos de cada uma das sequências que é possível
analisar numa partição. Assim temos 3 limitações, o número máximo de elementos da
sequência fixa e variável para uma partição, o valor do máximo e o número máximo de
elementos total das sequências. No futuro, um dos objetivos será tentar suprimir estas
limitações, embora não se vislumbre tarefa fácil.
Quanto à limitação do número de elementos da sequência variável por partição, ter-se-á de
fazer-se um ajuste quer da Coreworks quer do desenvolvimento do projeto, de modo a
incrementar o número de elementos dos vectores a enviar para o hardware acima de 128.
Para a sequência fixa o caso é um pouco mais complexo, pois apenas com uma análise
pormenorizada de toda a arquitetura e com a ajuda da equipa da Coreworks é que será
possível eliminar esta limitação.
A limitação do valor do máximo que é possível calcular, constitui também um dos pontos
fulcrais num trabalho futuro, pois basta utilizar registos de 24 bits, já implementados pela
Corewoks, para solucionar este problema, não sendo, de todo, um problema difícil de resolver.
O número máximo de elementos por sequência é limitado pelo traceback, tal como referido
anteriormente. Para suprimir esta limitação temos várias alternativas. A primeira passa pelo
melhoramento do traceback, no software, para permitir a utilização de um maior número de
elementos por sequência e fazer um melhor aproveitamento da memória.
Outra possibilidade é a substituição do cálculo do máximo no FASTA Program Package,
ficando-se assim com todas as funcionalidades do pacote, mas acelerando a parte crítica dos
83
algoritmos. Por último, poder-se-á optar por uma implementação do traceback em hardware,
sendo que esta não será uma tarefa fácil, mas apresentará, certamente, um melhor
desempenho e um grande incremento na performance.
Caso se conseguissem eliminar as limitações teríamos um impacto gigantesco no desempenho
do sistema.
84
85
6 Bibliografia
[1] L. Hasan, Z. Al-Ars e S. Vassiliadis, “Hardware Acceleration of Sequence Alignment
Algorithms—An Overview,” 2007.
[2] Y. L. A. B. Khaled Benkrid, “A Highly Parameterized and Efficient FPGA-Based Skeleton for
Pairwise Biological Sequence Alignment,” IEEE Transactions on Very Large Scale Integration
(VLSI) Systems, vol. 17, n.º 4, 2009.
[3] S. Lloyd e Q. O. Snell, “Sequence Alignment with Traceback on Reconfigurable Hardware,”
International Conference on Reconfigurable Computing and FPGAs, 2008.
[4] T. H. Genome. [Online]. Available: http://genome.wellcome.ac.uk/doc_WTD021036.html.
[Acedido em Dezembro 2011].
[5] S. S. Bandyopadhyay, S. Paul e A. Konar, “Improved Algorithms for DND Sequence
Alignment and Revision of Scoring Matrix,” pp. 485-490, 2005.
[6] Caglar Yilmaz e Mustafa Gok, “An Optimized System for Multiple Sequence Alignment”
International Conference on Reconfigurable Computing and FPGAs, 2009.
[7] E. Chemello. [Online]. Available:
http://www.quimica.net/emiliano/artigos/2007mar_forense4.pdf. [Acedido em Outubro
2012].
[8] CoreWorks, SideWorks Reference Book, Lisboa, 2010.
[9] W. R. Pearson, “The FASTA program package - guide,” 2011. [Online]. Available:
http://fasta.bioch.virginia.edu/fasta_www2/fasta_list2.shtml. [Acedido em Outubro 2012].
[10] T. Oliver, B. Schmidt e D. Maskell, “Hyper Customized Processors for Bio-Sequence
Database Scanning on FPGAs,” 2005.
[11] N. Sebastião, N. Roma e F. Paulo, “Integrated Hardware Architecture for Efficient
Computation of the n-Best Bio-Sequence Local Alignments in Embedded Platforms,” IEEE
Transactions on Very Large Scale Integration (VLSI) Systems, 2011.
[12] Coreworks, Coreworks, [Online]. Available: http://www.coreworks-
sa.com/index.php?view=computing_text&PHPSESSID=5b4f2fc6bd9c1f4deaeba9fab9796fc5.
[Acedido em Outubro 2012].
86
87
Anexo 1
Extracto do output do programa SSEARCH:
../bin/ssearch35 ../seq/mgstm1.aa ../seq/prot_test.lseg SSEARCH searches a sequence data bank version 35.04 Feb. 20, 2010 Please cite: T. F. Smith and M. S. Waterman, (1981) J. Mol. Biol. 147:195-197; W.R. Pearson (1991) Genomics 11:635-650 Query: GT8.7, 218 aa 1>>>GT8.7 | 266 40001 90043 | transl. of pa875.con, - 218 aa - 218 aaresults.txt Library: ../seq/prot_test.lseg 2245 residues in 12 sequences Statistics: (shuffled [500]) Expectation_n fit: rho(ln(x))= 11.6325+/-0.00641; mu= -17.6526+/- 0.311 mean_var=63.0515+/-26.495, 0's: 0 Z-trim: 0 B-trim: 0 in 0/8 Lambda= 0.161520 Algorithm: Smith-Waterman (SSE2, Michael Farrar 2006) (6.0 Mar 2007) Parameters: BL50 matrix (15:-5), open/ext: -10/-2 The best scores are: s-w bits E(12) GT8.7 | 266 | transl. of pa875.con, 19 to 675 @P:2 ( 218) 1497 354.7 9.4e-102 XURTG | 266 | glutathione transferase (EC 2.5.1.18 ( 222) 235 60.6 3.3e-13 HAHU | 1114 | Hemoglobin alpha chain - Human, chim ( 141) 51 18.3 1.1 OKBO2C | 296 | Protein kinase (EC 2.7.1.37), cAMP- ( 350) 53 17.6 3.7 TPHUCS | 1322 | Troponin C, skeletal muscle - Huma ( 159) 41 15.8 5.4 CCHU | 1 | Cytochrome c - Human @P:25-85 ( 105) 36 15.2 5.5 K3HU | 1099 | Ig kappa chain C region - Human ( 106) 35 15.0 6.2 FEPE | 25 | Ferredoxin - Peptostreptococcus asacch ( 54) 25 13.5 7.7 RKMDS | 677 | Ribulose-bisphosphate carboxylase (E ( 140) 36 14.8 7.8 K1HUAG | 1091 | Ig kappa chain V-I region (Ag) - ( 109) 33 14.5 7.8 HMIVV | 2581 | Hemagglutinin precursor - Influenza ( 567) 50 16.3 9.3 N2KF1U | 1021 | Long neurotoxin 1 - Many-banded kr ( 74) 26 13.3 9.5 >>GT8.7 | 266 | transl. of pa875.con, 19 to 675 @P:21-18 (218 aa) s-w opt: 1497 Z-score: 1878.6 bits: 354.7 E(): 9.4e-102 Smith-Waterman score: 1497; 100.0% identity (100.0% similar) in 218 aa overlap (1-218:1-218) 10 20 30 40 50 60 GT8.7 MPMILGYWNVRGLTHPIRMLLEYTDSSYDEKRYTMGDAPDFDRSQWLNEKFKLGLDFPNL :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: GT8.7 MPMILGYWNVRGLTHPIRMLLEYTDSSYDEKRYTMGDAPDFDRSQWLNEKFKLGLDFPNL 10 20 30 40 50 60 70 80 90 100 110 120 GT8.7 PYLIDGSHKITQSNAILRYLARKHHLDGETEEERIRADIVENQVMDTRMQLIMLCYNPDF :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: GT8.7 PYLIDGSHKITQSNAILRYLARKHHLDGETEEERIRADIVENQVMDTRMQLIMLCYNPDF
88
70 80 90 100 110 120 130 140 150 160 170 180 GT8.7 EKQKPEFLKTIPEKMKLYSEFLGKRPWFAGDKVTYVDFLAYDILDQYRMFEPKCLDAFPN :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: GT8.7 EKQKPEFLKTIPEKMKLYSEFLGKRPWFAGDKVTYVDFLAYDILDQYRMFEPKCLDAFPN 130 140 150 160 170 180 190 200 210 GT8.7 LRDFLARFEGLKKISAYMKSSRYIATPIFSKMAHWSNK :::::::::::::::::::::::::::::::::::::: GT8.7 LRDFLARFEGLKKISAYMKSSRYIATPIFSKMAHWSNK 190 200 210 >>XURTG | 266 | glutathione transferase (EC 2.5.1.18) Ya (222 aa) s-w opt: 235 Z-score: 289.0 bits: 60.6 E(): 3.3e-13 Smith-Waterman score: 235; 27.4% identity (57.0% similar) in 223 aa overlap (4-218:6-218) 10 20 30 40 50 GT8.7 MPMILGYWNVRGLTHPIRMLLEYTDSSYDEKRYTMGDAPDFDRSQWLNEKFKL--GLD .: :.:.:: . :: :: . .::: : .: ::.: .: XURTG MSGKPVLHYFNARGRMECIRWLLAAAGVEFDEK---------FIQSPEDLEKLKKDGNLM 10 20 30 40 50 60 70 80 90 100 110 GT8.7 FPNLPYL-IDGSHKITQSNAILRYLARKHHLDGETEEERIRADIVENQVMD-TRMQLIML : ..:.. ::: :..:. ::: :.: :. : :. .:: :. . ..: :.: . .. XURTG FDQVPMVEIDG-MKLAQTRAILNYIATKYDLYGKDMKERALIDMYTEGILDLTEMIMQLV 60 70 80 90 100 110 120 130 140 150 160 170 GT8.7 CYNPDFEKQKPEFLK--TIPEKMKLYSEFLGK--RPWFAGDKVTYVDFLAYDILDQYRMF :: .. : . : : . . . . : . . ...:...: ::. ..: . : XURTG ICPPDQKEAKTALAKDRTKNRYLPAFEKVLKSHGQDYLVGNRLTRVDIHLLELLLYVEEF 120 130 140 150 160 170 180 190 200 210 GT8.7 EPKCLDAFPNLRDFLARFEGLKKISAYMKSSRYIATPIFSKMAHWSNK . . : .:: :. : .:. .: ... ... . :. .:. . . : XURTG DASLLTSFPLLKAFKSRISSLPNVKKFLQPGSQRKLPMDAKQIEEARKIFKF 180 190 200 210 220 ... (alignments deleted) ... 218 residues in 1 query sequences 2245 residues in 12 library sequences Scomplib [35.04] start: Tue Sep 25 20:53:24 2012 done: Tue Sep 25 20:53:33 2012 Total Scan time: 0.020 Total Display time: 0.020 Function used was SSEARCH [version 35.04 Feb. 20, 2010]
89
Anexo 2 Ficheiro de Resultados:
Testing Sequences
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
score = 980
Best local alignment
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
*****************************************
Testing Sequences
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
90
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TACGATCGACGATGGGGGGGGGGGGGGGGTTTTTTTTTTTTTTTTAAAAAAAAAAAAACCCCCCCCC
score = 32
Best local alignment
TTTTTTTTTTTTTTTT
TTTTTTTTTTTTTTTT
*****************************************
Testing Sequences
AGCTAGCATAGGGGATGAGCTGAATACGGGATCGTTTTTTTTTTGAGAGAAGTAGCTAGCATAGGGGATG
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
score = 20
Best local alignment
TTTTTTTTTT
TTTTTTTTTT
*****************************************
Testing Sequences
AGCTAGCATAGGGGATGAGCTGAATACGGGATCGTTTTTTTTTTGAGAGAAGTAGCTAGCATAGGGGATG
TCGATCGGATCGACAGGCATCGATCGATCGGATCGACAGGCATCGATCGATCGG
91
score = 40
Best local alignment
TAGCATAGGGGAT-GAGC-TGAATACGGGATCGTTTTTTTTTTGAGA--GA-AGTAGCTAGC-ATAGGGGAT-G
TCG-AT--CGGATCGA-CAGGCAT-C--GATCG-------ATCG-GATCGACAG--GC-ATCGAT---CGATCG
*****************************************
Testing Sequences
AGCTAGCATAGGGGATGAGCTGAATACGGGATCGTTTTTTTTTTGAGAGAAGTAGCTAGCATAGGGGATG
TACGATCGACGATGGGGGGGGGGGGGGGGTTTTTTTTTTTTTTTTAAAAAAAAAAAAACCCCCCCCC
score = 41
Best local alignment
A-GCTAG-C-ATAGGGGATGAGCTGAATACGGG-ATCGTTTTTTTTTTGAGAGAA
ACGATCGACGAT-GGGG--G-G-GGGGGGGGGGTTTTTTTTTTTTTTTTA-A-AA
*****************************************
Testing Sequences
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
score = 84
92
Best local alignment
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
*****************************************
Testing Sequences
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TACGATCGACGATGGGGGGGGGGGGGGGGTTTTTTTTTTTTTTTTAAAAAAAAAAAAACCCCCCCCC
score = 32
Best local alignment
TTTTTTTTTTTTTTTT
TTTTTTTTTTTTTTTT
*****************************************
Testing Sequences
TATCATCGAGCTAGCGGGGGGGGGGGGATCAGCTAGCATCAGCTACGGACT
TCGATCGGATCGACAGGCATCGATCGATCGGATCGACAGGCATCGATCGATCGG
score = 44
Best local alignment
ATC--ATCGAGCTA-GC-GGGGGGGGGGGGATCAG-CTA-GCATC-AGCTA-CGG
ATCGGATCGA-C-AGGCATCGATCGATCGGATC-GAC-AGGCATCGATCGATCGG
*****************************************
Testing Sequences
TATCATCGAGCTAGCGGGGGGGGGGGGATCAGCTAGCATCAGCTACGGACT
TACGATCGACGATGGGGGGGGGGGGGGGGTTTTTTTTTTTTTTTTAAAAAAAAAAAAACCCCCCCCC
93
score = 41
Best local alignment
TATC-ATCGA-GCTAGCGGGGGGGGGGGGAT
TA-CGATCGACGATGGGGGGGGGGGGGGGGT
*****************************************