ChipCflow: ferramenta para conversão de código C em uma ...

134
ChipCflow: ferramenta para conversão de código C em uma arquitetura a fluxo de dados estática em hardware reconfigurável Antonio Carlos Fernandes da Silva

Transcript of ChipCflow: ferramenta para conversão de código C em uma ...

Page 1: ChipCflow: ferramenta para conversão de código C em uma ...

ChipCflow: ferramenta para conversão de código C em uma arquitetura a fluxo de dados estática em hardware

reconfigurável

Antonio Carlos Fernandes da Silva

Page 2: ChipCflow: ferramenta para conversão de código C em uma ...
Page 3: ChipCflow: ferramenta para conversão de código C em uma ...

ChipCflow: ferramenta para conversão de código C em uma arquitetura a fluxo

de dados estática em hardware reconfigurável

Antonio Carlos Fernandes da Silva

Orientador: Prof. Dr. Jorge Luiz e Silva

Tese apresentada ao Instituto de Ciências

Matemáticas e de Computação - ICMC-USP,

como parte dos requisitos para obtenção do título

de Doutor em Ciências - Ciências de Computação

e Matemática Computacional. VERSÃO

REVISADA

USP – São Carlos

Março de 2015

SERVIÇO DE PÓS-GRADUAÇÃO DO ICMC-USP

Data de Depósito:

Assinatura:________________________

______

Page 4: ChipCflow: ferramenta para conversão de código C em uma ...

Ficha catalográfica elaborada pela Biblioteca Prof. Achille Bassi e Seção Técnica de Informática, ICMC/USP,

com os dados fornecidos pelo(a) autor(a)

FF354cc

Fernandes da Silva, Antonio Carlos ChipCFlow: ferramenta para conversão de código Cem uma arquitetura a fluxo de dados em hardwarereconfigurável / Antonio Carlos Fernandes da Silva;orientador Jorge Luiz e Silva. -- São Carlos, 2015. 100 p.

Tese (Doutorado - Programa de Pós-Graduação emCiências de Computação e Matemática Computacional) -- Instituto de Ciências Matemáticas e de Computação,Universidade de São Paulo, 2015.

1. Arquitetura a Fluxo de Dados. 2. Conversão decódigo. 3. FPGA. I. Silva, Jorge Luiz e, orient. II.Título.

Page 5: ChipCflow: ferramenta para conversão de código C em uma ...

Dedico este trabalho a meus pais Leonilce e Severino (in memoriam), e a

minha esposa Elizabeth, pelo apoio durante esta jornada e a todos os amigos

que caminharam junto comigo.

v

Page 6: ChipCflow: ferramenta para conversão de código C em uma ...
Page 7: ChipCflow: ferramenta para conversão de código C em uma ...

Agradecimentos

Agradeço aos meus pais e minha esposa, pelo apoio, incentivo e compreen-

são pelos períodos de ausência durante o desenvolvimento deste trabalho.

Ao meu orientador Prof. Dr. Jorge Luiz e Silva, pelo apoio, orientação

acadêmica e amizade.

Aos amigos do projeto ChipCflow, em especial ao Bruno e ao Joelmir, pelas

discussões proveitosa e pelos momentos de descontração.

Aos amigos do LCR, Erinaldo, Jean, Helson, Marcilyanne, Arnaldo, André,

Eva, Cristiano, Lobo, Daniel, pelo momentos de diversão e pelo ótimo ambiente

do Laboratório.

Aos amigos da UTFPR, pela cooperação no período que estive ausente.

Page 8: ChipCflow: ferramenta para conversão de código C em uma ...

ii

Page 9: ChipCflow: ferramenta para conversão de código C em uma ...

Nem tudo que se enfrenta

pode ser modificado mas

nada pode ser modificado

até que seja enfrentado.

Albert Einstein

iii

Page 10: ChipCflow: ferramenta para conversão de código C em uma ...

iv

Page 11: ChipCflow: ferramenta para conversão de código C em uma ...

Resumo

Existe uma crescente busca por softwares e arquiteturas alternativas. Essabusca acontece pois houveram avanços na tecnologia do hardware, e estesavanços devem ser complementados por inovações nas metodologias de proje-tos, testes e verificação para que haja um uso eficaz da tecnologia. Os softwaree arquiteturas alternativas, geralmente são modelos que exploram o parale-lismo das aplicações, ao contrário do modelo de Von Neumann. Dentre asarquiteturas alternativas de alto desempenho, tem-se a arquitetura a fluxode dados. Nesse tipo de arquitetura, o processo de execução de programas édeterminado pela disponibilidade dos dados, logo o paralelismo está embutidona própria natureza do sistema. O modelo a fluxo de dados possui a vantagemde expressar o paralelismo de maneira intrínseca, eliminando a necessidadedo programador explicitar em seu código os trechos onde deve haver parale-lismo. As arquiteturas a fluxo de dados voltaram a ser uma área de pesquisadevido aos avanços do hardware, em particular, os avanços da ComputaçãoReconfigurável e dos Field Programmable Gate Arrays (FPGAs).Nesta tese édescrita uma ferramenta de conversão de código que visa a geração de aplica-ções utilizando uma arquitetura a fluxo de dados estática. Também é descritoo projeto ChipCflow, cuja ferramenta de conversão de código, descrita nestatese, é parte integrante. A especificação do algoritmo a ser convertido é feitaem linguagem C e convertida para uma linguagem de descrição de hardware,respeitando o modelo proposto pelo ChipCflow. Os resultados alcançados vi-sam a prova de conceito da conversão de código de uma linguagem de altonível para uma arquitetura a fluxo de dados a ser configurada em FPGA.

Palavras-chave: Fluxo de Dados, Código C, Compilador, VHDL, FPGA, ChipC-flow.

Page 12: ChipCflow: ferramenta para conversão de código C em uma ...

vi

Page 13: ChipCflow: ferramenta para conversão de código C em uma ...

Abstract

A growing search for alternative architectures and softwares have been no-ted in the last years. This search happens due to the advance of hardwaretechnology and such advances must be complemented by innovations on de-sign methodologies, test and verification techniques in order to use technologyeffectively. Alternative architectures and softwares, in general, explores theparallelism of applications, differently to Von Neumann model. Among highperformance alternative architectures, there is the Dataflow Architecture. Inthis kind of architecture, the process of program execution is determined bydata availability, thus the parallelism is intrinsic in these systems. The da-taflow architectures become again a highlighted search area due to hardwareadvances, in particular, the advances of Reconfigurable Computing and FieldProgrammable Gate Arrays (FPGAs). ChipCflow projet is a tool for executionof algorithms using dynamic dataflow graph in FPGA. In this thesis, the deve-lopment of a code conversion tool to generate aplications in a static dataflowarchitecture, is described. Also the ChipCflow project where the code conver-sion tool is part, is presented. The specification of algorithm to be converted ismade in C language and converted to a hadware description language, respec-ting the proposed by ChipCflow project. The results are the proof of conceptof converting a high-level language code for dataflow architecture to be usedinto a FPGA.

Palavras-chave: DataFlow, C code, Compiler, VHDL, FPGA, ChipCflow.

Page 14: ChipCflow: ferramenta para conversão de código C em uma ...

viii

Page 15: ChipCflow: ferramenta para conversão de código C em uma ...

Sumário

Sumário . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi

Lista de Figuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv

Lista de Tabelas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii

Lista de Códigos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix

Lista de Abreviaturas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxiii

1 Introdução 11.1 Motivação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1.2 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.3 Contribuições . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.4 Organização do Texto . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2 Computação Reconfigurável 52.1 Modelos de Computação . . . . . . . . . . . . . . . . . . . . . . . . 5

2.2 Field Programmable Gate Array (FPGA) . . . . . . . . . . . . . . . . 7

2.3 Arquiteturas Híbridas FPGA/CPU . . . . . . . . . . . . . . . . . . . 9

2.3.1 GARP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.3.2 Dynamic Instruction Set Computer (DISC) . . . . . . . . . . 11

2.4 Linguagens de Descrição de Hardware . . . . . . . . . . . . . . . . 13

2.4.1 VHDL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

2.4.2 Verilog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

2.5 Considerações Finais . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3 Compilação 153.1 O Que é Um Compilador . . . . . . . . . . . . . . . . . . . . . . . . 15

3.2 O Processo de Compilação . . . . . . . . . . . . . . . . . . . . . . . 16

3.2.1 Fase de Análise . . . . . . . . . . . . . . . . . . . . . . . . . . 17

3.2.2 Fase de Síntese . . . . . . . . . . . . . . . . . . . . . . . . . . 20

3.3 Compilação para Arquiteturas Reconfiguráveis . . . . . . . . . . . 21

ix

Page 16: ChipCflow: ferramenta para conversão de código C em uma ...

3.3.1 Grafos para Representação Intermediária . . . . . . . . . . 21

3.4 Considerações Finais . . . . . . . . . . . . . . . . . . . . . . . . . . 24

4 Arquitetura a Fluxo de Dados 254.1 Linguagem Básica para o Modelo a Fluxo de Dados . . . . . . . . 26

4.1.1 Estruturas Condicionais . . . . . . . . . . . . . . . . . . . . 27

4.1.2 Estruturas Iterativas e reentrância . . . . . . . . . . . . . . 27

4.2 Modelos de Arquiteturas a Fluxo de Dados . . . . . . . . . . . . . . 29

4.2.1 Arquitetura Estática . . . . . . . . . . . . . . . . . . . . . . . 30

4.2.2 Arquitetura Dinâmica . . . . . . . . . . . . . . . . . . . . . . 30

4.3 Descrição de Arquiteturas Propostas . . . . . . . . . . . . . . . . . 32

4.3.1 Máquina a Fluxo de Dados Estática . . . . . . . . . . . . . . 32

4.3.2 Máquina de Manchester . . . . . . . . . . . . . . . . . . . . . 35

4.3.3 MIT - Tagged-Token Dataflow (TTD) . . . . . . . . . . . . . . 36

4.3.4 Wavescalar . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

4.3.5 TRIPS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

4.4 Considerações Finais . . . . . . . . . . . . . . . . . . . . . . . . . . 43

5 Compiladores para Hardware 455.1 Trident . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

5.2 Molen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

5.3 HThreads: Modelo de programação multithreads . . . . . . . . . . 48

5.4 ARISE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

5.5 Nenya / Galadriel . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

5.6 ROCCC (Riverside Optimizing Configurable Computing Compiler) . 53

5.7 Spark . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

5.8 Considerações Finais e Analise Comparativa. . . . . . . . . . . . . 54

6 O Projeto ChipCflow 576.1 Operadores propostos para a máquina a fluxo de dados estática

do projeto ChipCflow . . . . . . . . . . . . . . . . . . . . . . . . . . 58

6.1.1 Operadores de processamento . . . . . . . . . . . . . . . . . 59

6.1.2 Operador para controle de Loop . . . . . . . . . . . . . . . . 60

6.1.3 Operadores de Memória . . . . . . . . . . . . . . . . . . . . . 63

6.1.4 Protocolo de comunicação entre operadores . . . . . . . . . 63

6.2 Considerações Finais . . . . . . . . . . . . . . . . . . . . . . . . . . 64

7 Conversor de Código C em Aplicações ChipCflow 657.1 Conversão de Código C . . . . . . . . . . . . . . . . . . . . . . . . . 66

7.1.1 Front-End . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

x

Page 17: ChipCflow: ferramenta para conversão de código C em uma ...

7.1.2 Back-End . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

7.2 Considerações Finais . . . . . . . . . . . . . . . . . . . . . . . . . . 76

8 Resultados 778.1 Testes Realizados . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

8.1.1 Algoritmo de Fibonacci . . . . . . . . . . . . . . . . . . . . . 77

8.1.2 Algoritmo de Fatorial . . . . . . . . . . . . . . . . . . . . . . 80

8.1.3 Algoritmo Determinante . . . . . . . . . . . . . . . . . . . . . 82

8.2 Análise Comparativa . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

9 Conclusões e Trabalhos Futuros 899.1 Trabalhos Futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

Referências Bibliográficas 99

A Apendice1 101

xi

Page 18: ChipCflow: ferramenta para conversão de código C em uma ...

xii

Page 19: ChipCflow: ferramenta para conversão de código C em uma ...

Lista de Figuras

2.1 Arquitetura reconfigurável típica [Cardoso & Dehon, 2010]. . . . . 7

2.2 Estrutura de um FPGA [Hauck & Dehon, 2007]. . . . . . . . . . . 8

2.3 Exemplo de LUT de duas entradas [Bobda, 2007]. . . . . . . . . . 8

2.4 Arquitetura básica do projeto GARP [Hauser & Wawrzynek, 1997]. 10

2.5 Fluxo de desenvolvimento de aplicações [Hauser & Wawrzynek,

1997]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2.6 Arquitetura do sistema DISC [Wirthlin & Hutchings, 1995]. . . . . 12

2.7 Arquitetura do processador DISC [Wirthlin & Hutchings, 1995]. . 13

3.1 Fluxo básico do processo de compilação [Aho et al., 2007]. . . . . 16

3.2 Estrutura básica do processo de compilação [Muchnick, 1997]. . 16

3.3 Locais para melhorias potenciais por parte de usuário e do com-

pilador [Aho et al., 2007]. . . . . . . . . . . . . . . . . . . . . . . . . 20

3.4 Exemplo de CFG, CDG, DFG e DDG [Duarte, 2006] . . . . . . . . 22

3.5 Exemplo de HTG [Silva, 2009]. . . . . . . . . . . . . . . . . . . . . . 23

3.6 Exemplo de CDFG [Silva et al., 2009]. . . . . . . . . . . . . . . . . 24

4.1 Processo de disparo de um operador em uma arquitetura a fluxo

de dados. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

4.2 Operadores branch, merge não determinístico e decisão, adap-

tado de [Veen, 1986]. . . . . . . . . . . . . . . . . . . . . . . . . . . 27

4.3 Grafo correspondente a estrutura condicional if teste then f(x, y)

else g(x, y) [Veen, 1986]. . . . . . . . . . . . . . . . . . . . . . . . . . 28

4.4 Problemas com ciclos em arquiteturas a fluxo de dados [Veen,

1986]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

4.5 Construção de loop com controle de reentrância [Veen, 1986]. . . 29

4.6 Métodos para controle de tags em arquiteturas estáticas [Silva,

2011]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

xiii

Page 20: ChipCflow: ferramenta para conversão de código C em uma ...

4.7 Métodos para controle de tags em arquiteturas dinâmicas [Silva,

2011]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

4.8 Arquitetura básica da máquina a fluxo de dados estática [Dennis,

1980]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

4.9 Célula de Instrução [Dennis & Misunas, 1975]. . . . . . . . . . . . 33

4.10Operadores básicos [Dennis & Misunas, 1975]. . . . . . . . . . . . 34

4.11Tipos de enlaces [Dennis & Misunas, 1975]. . . . . . . . . . . . . . 34

4.12Arquitetura da máquina de Manchester [Gurd et al., 1985]. . . . . 36

4.13Operadores definidos para a arquitetura TTDA [Arvind, 2005]. . . 37

4.14Formato da instrução da linguagem ID utilizada pela TTDA [Ar-

vind & Nikhil, 1990]. . . . . . . . . . . . . . . . . . . . . . . . . . . 38

4.15Elemento de processamento definido para a TTDA [Arvind & Nikhil,

1990]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

4.16Exemplo de código gerado pelo WaveScalar [Swanson et al., 2007]. 40

4.17Estrutura de um cluster do WaveCache [Swanson et al., 2007]. . 41

4.18Estrutura do pipeline de execução do WaveScalar [Swanson et al.,

2007]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

4.19Exemplo do mecanismo de anotação utilizado pelo WaveScalar[Swanson et al., 2007]. . . . . . . . . . . . . . . . . . . . . . . . . . 43

4.20Arquitetura do TRIPS [Sankaralingam et al., 2003]. . . . . . . . . 43

5.1 Fluxo de Compilação Trident [Tripp et al., 2007]. . . . . . . . . . . 47

5.2 Fluxo de Compilação HThreads [Andrews et al., 2008a]. . . . . . . 48

5.3 Organização Geral de uma Máquina ARISE. [Vassiliadis et al.,

2009]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

5.4 Fluxo de desenvolvimento de aplicações no ARISE. [Vassiliadis

et al., 2009]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

5.5 Fluxo dos compiladores Galadriel e Nenya [Cardoso, 2000]. . . . . 52

5.6 Fluxo de funcionamento do ROCCC [Buyukkurt et al., 2006]. . . 53

5.7 Fluxo de funcionamento do Spark [Gupta et al., 2004]. . . . . . . 55

6.1 Fluxo de execução do ChipCflow, adaptado de [Silva, 2006]. . . . 58

6.2 Tipos de links utilizados no projeto da máquina a fluxo de dados

estática do projeto ChipCflow [Arvind, 2005]. . . . . . . . . . . . . 59

6.3 Operadores do projeto da máquina a fluxo de dados estática do

projeto ChipCflow [Silva, 2006]. . . . . . . . . . . . . . . . . . . . . 60

6.4 Operadores usados em uma máquina a fluxo de dados com da-

dos chegando em seus arcos de entrada, e dados saindo após a

computação [Teifel & Manohar, 2004]. . . . . . . . . . . . . . . . . 60

xiv

Page 21: ChipCflow: ferramenta para conversão de código C em uma ...

6.5 (A) Controle de Loops utilizando os operadores convencionais de

uma máquina a fluxo de dados. (B) Controle de Loop usando o

operador For. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

6.6 Operador For. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

6.7 Exemplo de operadores For aninhados. . . . . . . . . . . . . . . . . 62

6.8 Formato dos operadores Load e Store. . . . . . . . . . . . . . . . . 63

6.9 Protocolo de Handshake entre operadores. . . . . . . . . . . . . . . 64

7.1 Fluxo de funcionamento da ferramenta de conversão de código. . 66

7.2 Fluxo de funcionamento do Flex. . . . . . . . . . . . . . . . . . . . 68

7.3 Exemplo de arquivo .COE. . . . . . . . . . . . . . . . . . . . . . . . 69

7.4 Exemplo de uso dos operadores branch e merge. . . . . . . . . . . 73

8.1 Grafo gerado para o algoritmo de Fibonacci. . . . . . . . . . . . . . 79

8.2 Trecho do código VHDL gerado para o algoritmo de Fibonacci. . . 80

8.3 Grafo gerado para o algoritmo de Fatorial. . . . . . . . . . . . . . . 81

8.4 Grafo gerado para o algoritmo de determinante. . . . . . . . . . . 84

8.5 Resultados das execuções do algoritmo de Fibonacci [Silva &

Silva, 2014] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

8.6 Resultados das execuções do algoritmo de para calculo Fatorial

[Silva & Silva, 2014] . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

8.7 Resultados das execuções do algoritmo de para calculo determi-

nante. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

xv

Page 22: ChipCflow: ferramenta para conversão de código C em uma ...

xvi

Page 23: ChipCflow: ferramenta para conversão de código C em uma ...

Lista de Tabelas

3.1 Exemplos de tokens [Aho et al., 2007]. . . . . . . . . . . . . . . . . 17

5.1 Comparação entre os compiladores estudados. . . . . . . . . . . . 55

7.1 Sub-conjunto da linguagem C . . . . . . . . . . . . . . . . . . . . . 67

7.2 Representação dos operadores no código intermediário . . . . . . 71

7.3 Representação das pseudo-instruções no código intermediário . . 72

8.1 Resultados obtidos após a síntese do algoritmo de Fibonacci no

modelo da máquina a fluxo de dados do projeto ChipCflow. . . . . 79

8.2 Resultados obtidos após a síntese do algoritmo de fatorial no

ChipCflow. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

8.3 Resultados obtidos após a síntese do algoritmo de determinante

no ChipCflow. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

8.4 Resultados das execuções do algoritmo de Fibonacci. . . . . . . . 85

8.5 Resultados das execuções do algoritmo de para calculo Fatorial. . 86

8.6 Resultados das execuções do algoritmo de para calculo de Deter-

minante. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

xvii

Page 24: ChipCflow: ferramenta para conversão de código C em uma ...

xviii

Page 25: ChipCflow: ferramenta para conversão de código C em uma ...

Lista de Códigos

1 Exemplo de instruções de três endereços [Aho et al., 2007]. . . . . 19

2 Exemplo de For aninhado. . . . . . . . . . . . . . . . . . . . . . . . . 62

3 Trecho da tabela de símbolos. . . . . . . . . . . . . . . . . . . . . . . 68

4 Instrução quebrada em instruções de três endereços. . . . . . . . . 70

5 Trecho de instruções de três endereços geradas. . . . . . . . . . . . 70

6 Trecho de instruções de três endereços com operadores específicos

da arquitetura. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

7 Trecho de instruções de três endereços com operadores específicos

da arquitetura. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

8 Trecho de código intermediário com a substituição de nomes das

variáveis. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

9 Exemplo de Fibonacci descrito em Linguagem C. . . . . . . . . . . . 78

10 Código intermediário gerado para o algoritmo de Fibonacci. . . . . 78

11 Exemplo de Fatorial descrito em Linguagem C. . . . . . . . . . . . . 81

12 Código intermediário gerado para o algoritmo de Fatorial. . . . . . 81

13 Trecho do código do algoritmo de determinante implementado. . . 83

xix

Page 26: ChipCflow: ferramenta para conversão de código C em uma ...

xx

Page 27: ChipCflow: ferramenta para conversão de código C em uma ...

Lista de Abreviaturas

ADL Add to Interaction Level

ADR Add Floating-point Values

ARISE Aristotle Reconfigurable Instruction Set Extension

ASICs Aplication Specific Integrated Circuit

BRR Branch

CDFG Control Dataflow Graph

CDG Control Dependence Graph

CFG Control Flow Graph

CGR Compare Floating-point Values

CLAy Configurable Logic Array

CPU Central Processing Unit

DDG Data Dependence Graph

DFG Data Flow Graph

DISC Dynamic Instruction Set Computer

DUP Explicit Duplicate

EABI Embedded Application Binary Interface

EDA Eletronic Design Automation

FIFO First-in-first-out

xxi

Page 28: ChipCflow: ferramenta para conversão de código C em uma ...

FPGA Field Programmable Gate Array

FPGAs Field Programmable Gate Arrays

GCC Gnu C Compiler

GFCD Grafo de fluxo de controle-dados

GPU Graphics Processing Unit

HDL Hardware Definition Language

HTG Hierarchical Task Graph

ICMC Instituto de Ciências Matemáticas e de Computação

ITG Initial Tag Generator

IEEE Institute of Electrical and Electronic Engineers

ISA Industry Standard Architecture

LCR Laboratório de Computação Reconfigurável

LLVM Low Level Virtual Machine

LUT Look-up table

MLR Multiply Floating-point Values

NIG New Iteration Generator

NTD New Tag Destructor

NTM New Tag Manager

OPT Output to Host Processor

OTT Old Tag Table

OVI Open Verilog International

PC Program Counter

RPU Unidade de Processamento Reconfigurável

UCP Unidade Central de Processamento

SIL Set Iteration Level

xxii

Page 29: ChipCflow: ferramenta para conversão de código C em uma ...

SRAM Static Random Access Memory

SSD Solid-State Drive

TR Tag Remover

TTD Tagged-Token Dataflow

TTDA Tagged-Token Dataflow Architecture

ULAs Unidades Lógica e Aritmética

VHDL Very High Speed Integrated Circuit - Hardware Description Language

VLIW Very Long Instruction Word

xxiii

Page 30: ChipCflow: ferramenta para conversão de código C em uma ...

xxiv

Page 31: ChipCflow: ferramenta para conversão de código C em uma ...

CAPÍTULO

1Introdução

1.1 Motivação

Segundo Cardoso [2000] existem áreas de aplicação nas quais o uso da

computação reconfigurável permite alcançar desempenho inalcançáveis por

processadores de uso geral, visto que estas arquiteturas permitem explorar de

forma eficiente o paralelismo. Dentro deste contexto e dos objetivos do projeto

ChipCflow [Silva et al., 2009], busca-se o desenvolvimento de um ferramenta

para gerar uma arquitetura a fluxo de dados a ser executada em um FPGA a

partir de código em linguagem de alto nível.

Além do projeto ChipCflow outras pesquisas estão sendo feitas para o de-

senvolvimento de máquinas baseadas no modelo a fluxo de dados, como as

encontradas em [Swanson et al., 2007] e [Sankaralingam et al., 2003].

A programação para este tipo de arquitetura depende do conhecimento em

projeto de hardware, por isso um grande desafio é criar ferramentas de apoio

que permitam a programadores de linguagem de alto nível gerar circuitos de

hardware com qualidade. Segundo Cardoso [2003] os grandes atrativos destas

ferramentas estão no fato de facilitarem a especificação de sistemas comple-

xos, pois aumentam o nível de abstração do projeto de hardware, e por per-

mitirem a migração de diversos algoritmos já existentes e implementados em

linguagem de alto nível como C/C++.

Além da migração de aplicações existentes, outra área que pode ser ex-

plorada é a criação de aplicações para sistemas embarcados. Os resultados

1

Page 32: ChipCflow: ferramenta para conversão de código C em uma ...

demonstram que o consumo de energia da arquitetura gerado é baixo, isso

aliado ao bom desempenho alcançado pode ser um motivador para maiores

estudos nesta área de desenvolvimento.

1.2 Objetivos

O principal objetivo deste trabalho foi o desenvolvimento de uma ferra-

menta de geração de código que permitisse a conversão de aplicativos descrito

em Linguagem C, para aplicações executáveis utilizando o modelo de arquite-

tura a fluxo de dados estática em FPGA, como parte fundamental do projeto

ChipCflow.

A partir da ferramenta desenvolvida teve-se como objetivo realizar a prova

de conceito, para a geração de arquiteturas a fluxo de dados estáticas, tendo

como código de entrada uma linguagem de alto nível. Esta ferramenta utiliza

técnicas de desenvolvimento de compiladores para software, além de mapea-

mento de grafos voltados a exploração do paralelismo a nível de instruções,

ponto este essencial para a obtenção de um bom desempenho de uma arqui-

tetura a fluxo de dados.

1.3 Contribuições

A principal contribuição encontra-se na prova de conceito proposta para

a conversão de código. Além desta contribuição, pode-se destacar o estudo

para o desenvolvimento da arquitetura a fluxo de dados estática desenvolvida

e descrita neste trabalho. A execução de algoritmos e a avaliação de seus

resultados serviram para demonstrar a viabilidade da conversão de códigos

descritos em linguagem de alto nível para uma arquitetura a fluxo de dados

estática.

Além da prova de conceitos, a comparação do desempenho alcançado na

execução de algoritmos, demonstra que arquiteturas a fluxo de dados estática

são uma boa opção para o desenvolvimento de aceleradores de hardware com

baixo consumo de energia e alto poder de processamento.

A demonstração da viabilidade de conversão de código e o bom desempenho

alcançado pela arquitetura, proporcionam um ambiente favorável ao avanço

das pesquisas nesta área de estudo.

2

Page 33: ChipCflow: ferramenta para conversão de código C em uma ...

1.4 Organização do Texto

Para contextualizar o trabalho, descrever seu desenvolvimento e apresen-

tar os resultados e conclusões, o presente documento está organizado como

segue: No Capítulo 2 é apresentada uma visão geral sobre os modelos de

computação, bem como os FPGAs, arquiteturas híbridas e linguagens de des-

crição de hardware. Em seguida, no Capítulo 3 é apresentada uma descrição

do processo de compilação tradicional e as particularidades da compilação

para arquiteturas reconfiguráveis. No Capítulo 4 é apresentada a arquitetura

a fluxo de dados, seu fluxo de execução, modelos e algumas das máquinas

a fluxo de dados propostas. No Capítulo 5 é apresentada uma revisão de al-

guns trabalhos relacionados a geração de código visando o desenvolvimento

de hardware. No Capítulo 6 é apresentado o projeto ChipCflow, base para o

desenvolvimento deste trabalho. No Capítulo 7 é apresentada a ferramenta

de geração de código do projeto ChipCflow, sua representação intermediária,

operadores disponíveis, grafos gerados, e demais passos necessários para a

geração do código em linguagem de descrição de hardware equivalente a ar-

quitetura proposta. No Capítulo 8 são apresentados os testes da ferramenta

e os resultados obtidos. Os dados obtidos são comparados com outras arqui-

teturas. No Capítulo 9 são apresentadas as conclusões deste trabalho, bem

como sugestões para sua continuidade.

3

Page 34: ChipCflow: ferramenta para conversão de código C em uma ...

4

Page 35: ChipCflow: ferramenta para conversão de código C em uma ...

CAPÍTULO

2Computação Reconfigurável

Neste capítulo será descrito o conceito de computação reconfigurável bem

como tecnologias ligadas a este paradigma de computação. O conceito de

computação reconfigurável foi apresentando por Estrin [Estrin et al., 1963],

e apresenta a possibilidade de unir a flexibilidade de um processador de uso

geral com a velocidade de um processador de uso específico. As características

destes tipos de processadores são apresentadas na próxima seção.

2.1 Modelos de Computação

Tradicionalmente os algoritmos podem ser executados com o uso de Apli-cation Specific Integrated Circuit (ASICs) ou microprocessadores de uso geral

[Hauck & Dehon, 2007].

Os ASICs são desenvolvidos especificamente para uma aplicação, e por este

motivo apresentam grande desempenho na execução, por outro lado não pos-

suem flexibilidade, ou seja, caso a aplicação sofra alterações o ASIC deve ser

totalmente reprojetado, o que gera alto custo. Por outro lado, os processa-

dores de uso geral são dispositivos que permitem sua programação para a

execução de qualquer tipo de operação digital, alterando-se o seu conjunto de

instruções, sem a necessidade de alteração de hardware. Estes processadores

têm um desempenho menor quando comparado com ASICs, mas apresentam

flexibilidade maior [Hauck & Dehon, 2007].

Para tentar unir as melhores características de cada um destes modelos de

5

Page 36: ChipCflow: ferramenta para conversão de código C em uma ...

computação surgiu a computação reconfigurável que, segundo Panainte et al.

[2007], vem demonstrando um grande crescimento devido à sua promessa de

aliar desempenho do hardware e a flexibilidade do software. O conceito básico

deste modelo surgiu nos anos 60, quando Estrin [Estrin et al., 1963] desen-

volveu uma máquina que poderia ser reconfigurada por meio da alteração da

interconexão de seus componentes, que era feita por meio de um painel de

fios e conectores. Este é o mesmo conceito utilizado atualmente, com a uti-

lização de componentes de hardware que podem ter sua lógica alterada por

meio de código gerado em alguma linguagem de descrição de hardware, em

substituição aos fios e conectores da máquina de Estrin.

Embora os sistemas reconfiguráveis apresentem uma alternativa para a

crescente demanda por desempenho e redução de consumo elétrico, a geração

de arquiteturas reconfiguráveis é complexa e exige conhecimentos em hard-

ware e software para otimizar seu funcionamento [Panainte et al., 2007]. Neste

cenário não atrativo para programadores em geral, devido a complexidade do

projeto de hardware, várias ferramentas vêm sendo desenvolvidas com o ob-

jetivo de permitir que um programador, usando linguagem de alto nível, gere

um sistema eficiente e que possa ser comparado ao sistema gerado de forma

manual por um especialista da área.

Segundo Hauck & Dehon [2007] a geração de sistemas reconfiguráveis pode

ser realizada de forma:

• Automática: meio simples e rápido para a descrição que pode ser feita em

linguagem de alto nível ou em linguagens de descrição de hardware, como

por exemplo Very High Speed Integrated Circuit - Hardware DescriptionLanguage (VHDL) e Verilog. Esta descrição é substituída por elementos

de hardware, que são depois mapeados para os componentes específicos

da arquitetura do dispositivo reconfigurável (mapeamento tecnológico). O

compilador pode ser responsável por detectar automaticamente as partes

da descrição que deverão ser mapeadas para hardware e software;

• Automática/Manual: neste tipo de processo o projetista decide quais as

partes do sistema vão ser mapeadas para hardware e software. Depois

de feita a descrição estrutural do sistema, a mesma pode ser mapeada

para o dispositivo reconfigurável por ferramentas de fabricantes;

• Manual: neste tipo de processo o projetista é responsável por toda a

definição do sistema, tornando o processo lento e mais suscetível a erros.

O desenvolvimento deste tipo de arquitetura normalmente baseia-se em

uma Unidade de Processamento Reconfigurável (RPU), usado como um co-

6

Page 37: ChipCflow: ferramenta para conversão de código C em uma ...

processador acoplado a um sistema que utiliza um processador de uso geral

[Cardoso & Dehon, 2010]. Um exemplo deste tipo de arquitetura pode ser

visto na Figura 2.1.

Figura 2.1: Arquitetura reconfigurável típica [Cardoso & Dehon, 2010].

Como RPU, normalmente, são utilizados Field Programmable Gate Arrays(FPGAs) que são dispositivos lógicos que podem ser reprogramados de acordo

com a necessidade de cada aplicação ou arquitetura. Em particular na Figura

2.1,temos um FPGA configurado com uma RPU contendo várias Unidades Ló-

gica e Aritmética (ULAs). Os FPGAs são apresentados a seguir.

2.2 Field Programmable Gate Array (FPGA)

Os FPGAs são constituídos por um circuito integrado com um arranjo de

células lógicas que oferecem suporte para a implementação de inúmeros cir-

cuitos lógicos. A estrutura básica de um FPGA é descrita na Figura 2.2 e é

formada basicamente pelos blocos lógicos, que implementam os circuitos ló-

gicos funcionais, os blocos de interconexão e as chaves de interconexão, res-

ponsáveis por conectar e fazer o roteamento, para ligar de forma adequada os

blocos lógicos, atendendo assim a necessidade de cada projeto [Bobda, 2007].

As funções lógicas dentro de um FPGA podem ser implementadas com o

uso de blocos de memória Look-up table (LUT). Estes blocos de memória,

quando configurados, armazenam todos os resultados de uma dada função

dado um conjunto de dados de entrada. Em FPGAs as LUTs são compostas por

blocos de memória Static Random Access Memory (SRAM) e um decodificador

7

Page 38: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 2.2: Estrutura de um FPGA [Hauck & Dehon, 2007].

responsável por acessar corretamente os endereços de memória de acordo com

o conjunto de entradas. Na Figura 2.3 é descrita uma LUT de duas entradas.

Figura 2.3: Exemplo de LUT de duas entradas [Bobda, 2007].

Em uma única LUT são implementadas funções lógicas simples. Para im-

plementação de funções complexas, a função deve ser dividida em partes e

cada uma das partes implementada em uma LUT. Estas células são voláteis,

ou seja, caso sejam desenergizadas perdem seu conteúdo, necessitando de

reprogramação [Moreno et al., 2005].

As células de um FPGA tem tamanhos variáveis ou seja granulações dis-

tintas e indica sua complexidade. Segundo Hauck & Dehon [2007] arquitetu-

ras internas de um FPGA podem ser classificadas em três sub-conjuntos de

acordo com sua granulação: Granulação fina, as células implementam por-

tas lógicas AND, OR, XOR e NOR que podem ser combinadas para formarem

circuitos mais complexos; Granulação média, as células implementam fun-

8

Page 39: ChipCflow: ferramenta para conversão de código C em uma ...

ções lógico-aritméticas e multiplicadores de poucos bits; Granulação grossa,

as células implementam somadores e multiplicadores de maiores quantidades

de bits e em nível de bytes . Pode-se encontrar arquiteturas como pequenos

processadores e unidades lógica e aritmética.

2.3 Arquiteturas Híbridas FPGA/CPU

Os FPGAs são ineficientes para a realização de algumas operações, como

por exemplo laços de tamanho variável e controle de saltos, além de necessi-

tar de uma fonte de controle para a sua reconfiguração. Por estes motivos e

também para obter maior desempenho e suporte a um número maior de apli-

cações, os FPGAs são normalmente conectados a processadores de uso geral,

criando assim uma arquitetura híbrida FPGA/CPU [Mittal et al., 2007].

Segundo Mittal et al. [2007], este novo modelo de arquitetura tende a criar

uma grande necessidade de migração de softwares projetados para uso em

processador de uso geral, para o modelo híbrido FPGA/CPU, pelo fato deste

modelo alcançar maior desempenho a um baixo custo. Este tipo de migração

é um desafio, pois nem sempre os usuários possuem o código fonte de suas

aplicações e nem sempre estão disponíveis ferramentas para auxilia-los neste

processo.

Na arquitetura híbrida o FPGA pode ser utilizado de várias formas, como

por exemplo:

• Unidade funcional dentro de um processador hospedeiro [Hauck et al.,

2004] [Razdan & Smith, 1994]: esta unidade funcional executa interna-

mente ao processador, o que permite a adição de instruções especiais,

sem alterar o modo de programação;

• Co-processador [Wittig & Chow, 1996] [HPCwire, 2009]: é iniciado pelo

processador do qual recebe os dados para a realização de processamento

de forma independente, podendo acessar memórias internas ou externas

ao sistema;

• Sistema multi-processador [Vuillemin et al., 1996] [Laufer et al., 1999]: o

FPGA é interligado a um processador de uso geral. A comunicação entre

eles ocorre utilizando-se de primitivas especializadas, assim como nos

sistemas multiprocessadores tradicionais;

• Unidade de processamento externa [Quickturn, 1999]: Semelhante ao

uso do FPGA como co-processador, mas neste caso existe pouca comuni-

cação entre o processador e o dispositivo reconfigurável.

9

Page 40: ChipCflow: ferramenta para conversão de código C em uma ...

A seguir são apresentadas duas das primeiras arquiteturas híbridas pro-

postas.

2.3.1 GARP

O projeto GARP [Hauser & Wawrzynek, 1997] foi uma proposta de arqui-

tetura híbrida baseada em um processador MIPS-II e uma unidade reconfi-

gurável (FPGA Xilinx Série 4000). A relação entre o processador e a unidade

reconfigurável era mestre-escravo, ou seja o processador MIPS era o respon-

sável por ativar e configurar o FPGA além de lhe fornecer as informações ne-

cessárias para execução de trechos de código. A arquitetura do GARP pode

ser vista na Figura 2.4.

Figura 2.4: Arquitetura básica do projeto GARP [Hauser & Wawrzynek, 1997].

A proposta de arquitetura do GARP era formada por uma memória com-

partilhada entre o processador e o FPGA e visava melhorar o desempenho de

execução de trechos críticos em aplicações. O processador era responsável

por informar os endereços dos dados a serem acessados ou gravados na me-

mória pelo FPGA tanto na memória principal quanto na cache, uma vez que o

FPGA tinha acesso a toda a hierarquia de memória do processador. Além da

memória principal faziam parte da arquitetura uma memória secundária para

armazenar as instruções e uma memória secundária para armazenamento dos

dados.

O conjunto de instruções disponível para o desenvolvimento de aplicações

era uma extensão do conjunto de instruções do processador MIPS. O pro-

cessador MIPS poderia iniciar ou parar a execução de um trecho de código no

10

Page 41: ChipCflow: ferramenta para conversão de código C em uma ...

FPGA sempre que fosse necessário, porém só era possível reconfigurar o FPGA

caso ele estivesse ocioso.

Para o desenvolvimento de aplicações no GARP foi desenvolvido um con-figurador para gerar código binário a partir de uma descrição textual para a

configuração da unidade reconfigurável, e um simulador para executar o có-

digo gerado como se estivesse executando em uma máquina GARP. O código

gerado era um conjunto de instruções do MIPS e as instruções desenvolvidas

para serem executadas no FPGA. O fluxo de desenvolvimento de uma aplica-

ção GARP pode ser visto na Figura 2.5

Figura 2.5: Fluxo de desenvolvimento de aplicações [Hauser & Wawrzynek,1997].

O código gerado pelo configurador é um arquivo de texto simples que pode

ser utilizado como um array por um código C compilado para a geração do có-

digo executável do MIPS. Todo o código é executado pelo simulador do GARP,

pois não foi realizada a implementação do hardware proposto [Hauser & Wa-

wrzynek, 1997].

2.3.2 Dynamic Instruction Set Computer (DISC)

O DISC [Wirthlin & Hutchings, 1995] foi desenvolvido como uma arquite-

tura híbrida que teve como objetivo utilizar o conceito de reconfiguração par-

cial, visando maior desempenho e também a capacidade de implementação de

11

Page 42: ChipCflow: ferramenta para conversão de código C em uma ...

um grande conjunto de instruções.

Para esta arquitetura foi desenvolvido um hardware específico com um bar-

ramento Industry Standard Architecture (ISA), dois FPGAs Configurable LogicArray (CLAy) e uma memória. Um controlador de configuração foi implemen-

tado no FPGA para realizar o monitoramento da execução de instruções e

manter a comunicação com um computador executando um sistema operaci-

onal baseado em UNIX. Este computador é responsável por validar a presença

de cada uma das instruções no hardware, caso a instrução necessária não

esteja presente, o computador solicita a configuração da instrução no proces-

sador DISC. A localização física da instrução a ser configurada é feita com

base nas áreas livres do FPGA. Caso não exista área livre para a implementa-

ção da instrução a instrução é colocada em uma fila de espera até que exista

espaço suficiente.

Instruções já utilizadas são desalocadas do FPGA pelo computador, ligado

ao barramento ISA, por meio da alteração do bitstream a ser enviado para o

FPGA. A arquitetura do sistema DISC pode ser visto na Figura 2.6.

Figura 2.6: Arquitetura do sistema DISC [Wirthlin & Hutchings, 1995].

Para a execução de uma aplicação em uma arquitetura DISC o passo ini-

cial é a alocação do programa na memória e após isso configurar o FPGA DISC

com o uso do controlador global. Este controlador é implementado na forma

de um processador de 8 bits com um conjunto reduzido de instruções e é o

responsável por monitorar o uso de recursos como memória externa, redes de

comunicação e também o estado global do FPGA. Este controlador provê inter-

face de comunicação com o sistema e um protocolo padrão para as instruções

específicas implementadas. Este controlador é interligado também a um a

um banco de instruções para formar um processador completo. A arquitetura

deste processador pode ser visto na Figura 2.7.

12

Page 43: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 2.7: Arquitetura do processador DISC [Wirthlin & Hutchings, 1995].

2.4 Linguagens de Descrição de Hardware

As linguagens de descrição de hardware são uma alternativa ao desenvol-

vimento de modelos esquemáticos para circuitos digitais. Estas linguagens

são orientadas a descrição das estruturas e do comportamento de um hard-

ware, para isso faz-se necessário duas descrições durante a programação, a

descrição estrutural que informa a interconexão dos componentes que fazem

parte de um circuito e uma descrição comportamental que descreve o funcio-

namento dos componentes deste circuito.

A descrição em linguagem de descrição de hardware, em conjunto com

uma biblioteca de componentes, é utilizada por uma ferramenta de síntese

para a geração automática de um circuito digital, este processo é conhecido

por síntese lógica.

As linguagem de descrição de hardware mais utilizadas são VHDL e Veri-

log. Estas duas linguagens são padronizadas pelo Institute of Electrical andElectronic Engineers (IEEE), e são utilizadas por vários fabricantes, sendo pos-

sível encontrar várias ferramentas. A seguir serão apresentadas as principais

características destas linguagens.

13

Page 44: ChipCflow: ferramenta para conversão de código C em uma ...

2.4.1 VHDL

O VHDL é uma linguagem que foi desenvolvida por empresas contratadas

pelo governo americano, para ser um padrão utilizado no desenvolvimento

dos ASICs projetados para fins militares. Após o inicio do seu uso ele foi

padronizado pelo IEEE.

O desenvolvimento em VHDL requer duas estruturas: a entity que é uma

declaração de entidade e a architecture que defini a arquitetura do sistema a

ser desenvolvido. A entidade é utilizada para definir os detalhes externos das

funções, como por exemplo, nomes de entradas e saídas e nome da função.

A arquitetura define os aspectos internos, como por exemplo, como as entra-

das e saídas influem no comportamento e como se relacionam com os sinais

internos.

2.4.2 Verilog

O Verilog foi criado como uma linguagem proprietária, pertencente a Ga-teway Design Automation. Alguns anos após a venda desta empresas ela

se tornou de domínio público, com a formação da Open Verilog Internatio-nal (OVI), sendo hoje padronizada pelo IEEE.

A linguagem Verilog tem semelhanças com a linguagem C, o que pode fa-

cilitar seu aprendizado por programadores não especialistas em hardware. A

linguagem também é case sensitive, assim como a linguagem C, ou seja dife-

rencia caracteres maiúsculos de minúsculos. O Verilog permite várias formas

para a descrição de um sistema digital como: descrições comportamentais,

que especificam como o sistema deve funcionar; descrições estruturais, que

define a estrutura interna do sistema ou simulação e permite a verificação de

alternativas para a implementação.

2.5 Considerações Finais

Neste capítulo foram apresentados os conceitos de computação reconfi-

gurável e outros modelos para execução de algoritmos, buscando situar este

modelo. Além disso foram apresentadas as tecnologias envolvidas com este pa-

radigma e também formas de utilização de dispositivos reconfiguráveis para

o desenvolvimento de arquiteturas que visam ganho de desempenho. Duas

das arquiteturas que representam o inicio do desenvolvimento de sistemas

reconfiguráveis foram apresentados visando exemplificar formas do uso da

computação reconfigurável.

14

Page 45: ChipCflow: ferramenta para conversão de código C em uma ...

CAPÍTULO

3Compilação

Têm-se como objetivo neste capítulo descrever o que é um compilador bem

como o processo de compilação tradicional, e o processo de compilação para

arquiteturas reconfiguráveis. Embora estes processos sejam muito semelhan-

tes, na compilação para arquiteturas reconfiguráveis algumas novas carac-

terísticas, que podem ser acrescentadas ao código gerado, apresenta novos

desafios para o processo. No final do capítulo são descritos alguns compilado-

res para arquiteturas de hardware.

3.1 O Que é Um Compilador

Segundo Aho et al. [2007] visto de forma bem simples, um compilador

pode ser descrito como uma programa que lê um texto em uma linguagem

de programação específica, conhecida como linguagem fonte, e o traduz em

uma outra linguagem, conhecida como linguagem de máquina ou linguagem

alvo. Uma parte muito importante deste processo de tradução é a informação,

repassada ao usuário, sobre possíveis erros. Na Figura 3.1 pode ser visto o

fluxograma básico de um compilador.

Embora possa parecer simples, este processo de tradução requer um con-

junto de fases que são descritas na próxima seção.

15

Page 46: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 3.1: Fluxo básico do processo de compilação [Aho et al., 2007].

3.2 O Processo de Compilação

O processo de compilação divide-se em duas fases: a análise e a síntese.

A análise faz a divisão do programa fonte e gera uma representação interme-

diária deste programa. A fase de síntese é a responsável por gerar o programa

alvo a partir da representação intermediária. Durante estas fases o compilador

também faz uso de outros dois componentes básicos: uma tabela de símbolos

utilizada para armazenar informações necessárias durante praticamente todo

o processo de compilação, e um interface com o sistema operacional, utili-

zada para realizar a gravação de arquivos e enviar mensagens ao usuário, por

exemplo. A estrutura do processo de compilação é mostrado na Figura 3.2.

Figura 3.2: Estrutura básica do processo de compilação [Muchnick, 1997].

Da mesma forma que o processo de compilação é dividido em duas fases,

16

Page 47: ChipCflow: ferramenta para conversão de código C em uma ...

um compilador pode ser desenvolvido em duas partes, uma chamada de front-end que é responsável pela fase de análise e uma outra chamada de back-end responsável pela fase de síntese. A seguir será explicado de forma mais

detalhada o processo para conclusão de cada uma destas fases.

3.2.1 Fase de Análise

A fase de análise realiza uma série de passos até a geração da representa-

ção intermediária, entre estes passos estão a análise léxica, análise sintática

e análise semântica. Ao término da execução destes passos um código inter-

mediário é gerado. Além disso, pode existir um passo para efetuar otimizações

de código independente de maquina. A seguir cada um destes passos serão

descritos.

Análise Léxica

Esta é o primeiro passo no processo de compilação e tem como objetivo

ler os caracteres de entrada (código fonte) e criar sequências de tokens, que

são unidades ou parte do texto significativas, que serão utilizados durante a

análise sintática, na Tabela 3.1 são apresentados alguns exemplos de tokens.

Tabela 3.1: Exemplos de tokens [Aho et al., 2007].Token Lexemas Exemplo Descrição Informal do Padrãoconst const constif if ifrelação <, <=, =, <>, >, >= < ou <= ou = ou <> ou > ou >=id pi, contador, D2 letra seguida por letras e/ou dígitosnum 3.1416, 0 , 6.02E23 qualquer constante numéricaliteral "conteúdo da memória" quaisquer caracteres entre aspas, exceto

aspa

Como pode ser visto no Tabela 3.1, quando trata-se de análise léxica alguns

termos como token, lexema e padrão são adotados com significados específi-

cos. O termo token como foi explicado anteriormente são partes significativas

do texto e é gerado com o uso de uma regra chamada de padrão associado ao

token, que é utilizada para reconhecer cada cadeia de caracteres. Os lexemas

são reconhecidos pelo padrão do token e podem receber tratamento em con-

junto como instâncias de uma unidade léxica (identificadores, números, etc).

Por fim o padrão é a regra que descreve o conjunto de lexemas válidos para

17

Page 48: ChipCflow: ferramenta para conversão de código C em uma ...

representar um token, por exemplo, o token relação é o conjunto de todos os

operadores relacionais.

O analisador léxico é a parte do compilador que lê todo o texto que será

usado como entrada do compilador e com isso pode realizar algumas tarefas

secundárias, como remover do código os espaços em branco e comentários,

além de relacionar as mensagens de erro do compilador com o texto de entrada

por meio da numeração de linhas [Aho et al., 2007].

Análise Sintática

O passo de análise sintática ocorre após a análise léxica, e para ser efe-

tuado recebe os tokens gerados no passo anterior e verifica se a cadeia de

tokens pode ser gerada pela gramática da linguagem1. Além disso a análise

sintática deve relatar erros encontrados e continuar a verificação mesmo de-

pois de encontrar um erro [Aho et al., 2007].

Os objetivos do tratamento de erros no analisador sintático são: Relatar

de forma clara a presença de erros; Recuperar-se de um erro, para poder

continuar a busca por novos erros; Ser rápido para não atrasar, de forma

significativa, a compilação de programas corretos.

Ao final deste passo é gerada uma estrutura hierárquica que permite iden-

tificar todos os operadores, operandos das expressões e enunciados.

Análise Semântica

Na análise semântica tem-se como objetivo verificar os tipos de dados para

a geração de código e verificar a existência de erros semânticos2. Além disso

tem-se como objetivo verificar o fluxo de controle do programa e a unicidade

da declaração de variáveis.

Para verificar as variáveis o compilador cria uma estrutura conhecida como

tabela de símbolos, com o objetivo de armazenar os nomes declarados no

código fonte. Esta tabela é consultada sempre que é encontrado a declaração

de uma nova variável. Esta consulta e a inserção de novos nomes tem que

ser feita de forma eficiente para não atrasar o processo de compilação. Além

do nome declarado esta tabela armazena informações como, tipo, tamanho e

escopo.

Alguns exemplos de erros semânticos são: Utilização de variáveis não de-

claradas; Problemas de atribuição entre tipos diferentes; Cálculos entre dados

1Gramática da linguagem pode ser definido como as regras que definem todas as combina-ções possíveis desta linguagem.

2Erro semântico pode ser definido como o uso incorreto de um comando ou um identifica-dor de uma variável.

18

Page 49: ChipCflow: ferramenta para conversão de código C em uma ...

de tipos incompatíveis.

Este passo busca identificar os erros e não resolvê-los, por este motivo

faz-se necessário uma forma eficiente de apontar os erros ao usuário.

Geração de Código Intermediário

Uma prática comum em vários compiladores é a geração de um código in-

termediário durante o processo de tradução do programa fonte, realizada após

o programa passar pela fase de análise. Embora seja possível gerar o código-

alvo diretamente, com o uso do código-fonte, a representação intermediária

apresenta benefícios, como por exemplo a facilidade de criação de um com-

pilador para uma máquina alvo diferente, substituindo-se apenas o módulo

de back-end ou também para a utilização de um otimizador dependente de

máquina [Aho et al., 2007].

Esta representação deve possuir duas características básicas: ser fácil de

produzir e ser fácil de ser traduzida para o programa alvo. Uma das formas

de representação deste código é por meio de instruções de três endereços,

que são facilmente geradas e traduzidas, por ser semelhante a linguagens de

montagem. Segundo Aho et al. [2007] esta representação pode ser vista como

um programa para uma máquina abstrata. Um exemplo deste tipo de código

é apresentado na Listagem 1.

Código 1: Exemplo de instruções de três endereços [Aho et al., 2007].temp1 := inttoreal (60)

temp2 := id3 * temp1

temp3 := id2 + temp2

id1 := temp3

A representação em forma de código de três endereços é formado por uma

sequencia de instruções com no máximo três operandos e um operador de

atribuição. Durante a geração deste tipo de código deve-se definir a ordem

de execução das instruções de modo a respeitar a regra de precedência de

operadores. Para cada operação que for dissociada em várias instruções faz-

se necessário gerar nomes temporários para receber valores computados em

algumas instruções.

Além de representar as instruções do código fonte, este código deve ser ca-

paz de representar o fluxo de controle existente. Para este propósito pode ser

utilizado marcações no código, de forma a permitir saltos dentro do conjunto

de instruções, ou uma representação gráfica, normalmente um ou mais gra-

fos, que agrupam instruções de acordo com o fluxo de dados e de controle.

A forma de representação intermediária com o uso de grafos permite uma

19

Page 50: ChipCflow: ferramenta para conversão de código C em uma ...

fácil identificação de pontos de paralelismo no programa, o que torna esta

representação bastante utilizada na geração de código para arquiteturas re-

configuráveis, como pode ser visto nos trabalhos [Tripp et al., 2007] [Cardoso,

2003], que buscam explorar este paralelismo.

3.2.2 Fase de Síntese

A fase de síntese não possui um conjunto de passos definidos, ela pode

ser composta pela otimização de código e da geração do código alvo. Sendo o

ultimo passo o único obrigatório.

Otimização

Segundo Aho et al. [2007] o processo de compilação deveria produzir có-

digo compatível com aquele que poderia ser escrito a mão, mas esta meta só

pode ser alcançada em casos limitados. O processo de compilação busca este

objetivo com o uso de alterações no código, chamadas de otimizações, embora

não se possa garantir que o código gerado seja o melhor possível.

Estas alterações no código buscam fazer com que o programa execute de

forma mais eficiente, tendo uma maior velocidade e ocupando menos memó-

ria. Estas otimizações são obtidas de forma mais eficiente se aplicadas a todos

os níveis, ou seja, desde o código-fonte até o código-alvo, como mostrado na

Figura 3.3.

Figura 3.3: Locais para melhorias potenciais por parte de usuário e do compi-lador [Aho et al., 2007].

Como pode ser visto na Figura 3.3 a otimização de código deve começar

na produção do código-fonte pelo programador, pois o processo de otimização

realizado pelo compilador é altamente dependente do código de entrada.

Geração de Código

A geração de código é o ultimo passo do compilador, e consiste em traduzir

o código intermediário gerado em código da linguagem alvo. Durante este pro-

20

Page 51: ChipCflow: ferramenta para conversão de código C em uma ...

cesso de tradução faz-se necessário definir algumas características do código

a ser gerado, como por exemplo: Como será feita o gerenciamento de memória

e alocação de registradores?

O código intermediário que é entregue ao gerador de código, já passou por

toda a fase de análise, por este motivo parte-se do principio que este código

esta isento de erros e não realiza nenhum tipo de verificação antes de traduzi-

lo para o código de maquina. Para esta tradução faz-se necessário conhecer o

conjunto de instruções da arquitetura alvo e suas particularidades.

Segundo Aho et al. [2007] as exigências de um gerador de código são bas-

tante rígidas, determinando que o código gerado seja correto e de alta quali-

dade, ou seja, utilize efetivamente os recursos da maquina alvo e que o gerador

deve executar de forma eficiente.

3.3 Compilação para Arquiteturas Reconfiguráveis

O processo de compilação para arquiteturas reconfiguráveis segue os mes-

mos passos de um processo de compilação para arquiteturas tradicionais.

Embora os passos sejam os mesmo, este processo de compilação apresenta

preocupações um pouco diferentes, como por exemplo, o particionamento da

aplicação em hardware e software e também a identificação de pontos de pa-

ralelismo em nível de instrução [Panainte et al., 2007].

3.3.1 Grafos para Representação Intermediária

Devido as preocupações da compilação para arquiteturas reconfiguráveis, a

representação intermediária gerada deve ser mais detalhada, visando permitir

explorar, por exemplo, todo o paralelismo presente em uma aplicação. Esta

representação pode ser feita utilizando-se de uma série de grafos que visam

armazenar o fluxo e dependência de dados, fluxo e dependência de controle,

informações de execução de blocos básicos e as estruturas de controle. Alguns

dos grafos que podem ser utilizados são descritos a seguir.

Control Flow Graph (CFG)

É um grafo dirigido, no qual cada nó representa um segmento de código e

cada arco representa um caminho possível a ser seguido durante a execução.

Cada segmento de código representa um conjunto de instruções que devem ser

executadas em ordem, a ultima instrução de um segmento é uma instrução

de salto e a primeira é o destino de um determinado salto. Este grafo é usado

21

Page 52: ChipCflow: ferramenta para conversão de código C em uma ...

como base para a geração de dependência para a exploração do paralelismo

máximo de uma aplicação [Cardoso, 2000].

Control Dependence Graph (CDG)

É um grafo dirigido formado pelo conjunto de nós do CFG, onde as ligações

entre os blocos representam a dependência de controle e restringem a ordem

de execução dos blocos, visando preservar a funcionalidade do programa.

Data Flow Graph (DFG)

Este grafo demonstra o fluxo de dados entre as operações de uma aplica-

ção, seus nós representam as operações e a ligação entre nós a existência de

dependência de fluxo entre eles e as condições segundo as quais determinada

operação é realizada, ou seja, as construções condicionais.

Data Dependence Graph (DDG)

Cada nó do CFG será considerado um nó do DDG, e as ligações entre estes

nós representam a dependência de ordem de execução em termos de depen-

dência de dados. Com a utilização deste grafo é possível explorar o paralelismo

a nível de blocos básicos3. Sempre que dois blocos forem independentes em

termos de fluxo de dados, eles poderão ser executados em paralelo.

Na Figura 3.4 é possível verificar os grafos CFG, CDG, DFG e DDG gerados

para um pequeno trecho de código.

Figura 3.4: Exemplo de CFG, CDG, DFG e DDG [Duarte, 2006]

3Um bloco básico é um trecho de código que se inicia no destino de um desvio e terminaao encontrar um desvio.

22

Page 53: ChipCflow: ferramenta para conversão de código C em uma ...

Hierarchical Task Graph (HTG)

Este grafo proposto por Girkar & Polychronopoulos [1992] é construído

com o uso do DDG e DFG e lida de forma eficiente com o paralelismo funci-

onal, pois permite representar de forma explicita todos os fluxos de controle

e dependência de dados, fluxos de controles múltiplos e os ciclos existentes.

Este grafo é formado por três tipos de nós: Nós simples, que representam

instruções ou blocos básicos do programa; Nós compostos, que representam

blocos de decisão if-then-else; Nós ciclos que representam blocos que podem

ser executados várias vezes consecutivas dentro do HTG.

Figura 3.5: Exemplo de HTG [Silva, 2009].

Control Dataflow Graph (CDFG)

É um grafo em que cada nó pode ser classificado como nó de operação

ou nó de controle. As ligações entre nós deste grafo podem representar a

transferência de valores ou de sinais de controle [Anellal & Kaminska, 1993].

Segundo Namballa et al. [2004] nós deste tipo de grafo podem ser classificados

como: Nó operacional, são nós responsáveis por cálculos e operações lógicas

ou relacionais; Nó de controle, nós responsáveis por construções como loops;

Nós de armazenamento, nós responsáveis por atribuir valores a uma variável;

Nós de chamadas, nós responsáveis por realizar chamadas a sub-programas

ou funções.

Na Figura 3.6 pode ser visto um exemplo de CDFG gerado para um pequeno

trecho de código.

23

Page 54: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 3.6: Exemplo de CDFG [Silva et al., 2009].

3.4 Considerações Finais

Neste capítulo foi apresentado o processo de compilação, suas fases e suas

particularidades. Nesta descrição buscou-se demonstrar como funciona um

compilador e todo o processo necessário para a tradução de uma linguagem

para um programa executável. Durante o capítulo foi dada uma atenção es-

pecial a representação intermediária formada por grafos, para auxiliar na tra-

dução de código, principalmente visando paralelismo a nível de instruções.

24

Page 55: ChipCflow: ferramenta para conversão de código C em uma ...

CAPÍTULO

4Arquitetura a Fluxo de Dados

Segundo Veen [1986] o conceito de arquitetura a fluxo de dados é tão antigo

quanto a computação eletrônica, porém, as pesquisas explorando este con-

ceito ganharam destaque acadêmico na década de 70, principalmente para o

seu uso em computação paralela [Dennis & Misunas, 1975]. Este conceito

surgiu como uma proposta alternativa para a computação de alto desempe-

nho, motivada pelo trabalho publicado por Dennis [1974], que apresentava

o conceito de implementação de um sistema altamente paralelo, cujo proces-

samento paralelo não exigisse grande esforço para ser obtido. Durante parte

dos anos 80 e 90 acreditou-se que este tipo de arquitetura seria o substituto

para os modelos tradicionais, mas várias dificuldades foram encontradas para

o seu desenvolvimento.

Após as expectativas e esforços iniciais, a pesquisa com este tipo de ar-

quitetura foi descontinuada, devido principalmente a restrição de hardware

[Johnston et al., 2004]. Com a evolução dos elementos de hardware, princi-

palmente os FPGAs, estas pesquisas foram retomadas [Cappelli et al., 2004]

[Swanson et al., 2007].

Este modelo é muito atrativo para a computação paralela devido a duas de

suas características principais, que são, ser assíncrono e explicitar o parale-

lismo inerente das aplicações [Lee & Hurson, 1994]. A escolha das instruções

a serem executadas é não determinística, ou seja é feita em tempo de execu-

ção, e são limitadas apenas pela dependência de dados do algoritmo. Além

de permitir a exploração do paralelismo implícito, este modelo falicita a pro-

gramação da máquina, sem a necessidade de conhecimentos em programação

25

Page 56: ChipCflow: ferramenta para conversão de código C em uma ...

concorrente ou paralela [Marques, 1993].

O modelo a fluxo de dados permite explorar o paralelismo à nível de instru-

ções em várias aplicações adaptando o hardware para a execução baseada na

presença de dados. Esta característica acrescenta um problema referente a

ordenação de memória, para disponibilizar os dados necessários no momento

necessário. Para tentar solucionar este problema, cada projeto de maquina a

fluxo de dados apresenta uma proposta para solucionar este problema.

4.1 Linguagem Básica para o Modelo a Fluxo de Da-

dos

Em uma arquitetura a fluxo de dado, cada instrução é vista como um ope-

rador. Os operadores deste modelo possuem somente uma referência para

a instrução que irá consumir o resultado gerado por sua computação, desta

forma um programa fluxo de dados pode ser visto como um grafo [Veen, 1986].

Devido a esta característica a descrição de programas, para este tipo de ar-

quitetura, pode ser representada na forma de um grafo direcional G = (V,A),

onde V é o conjunto de operadores e A o conjunto de arcos que interligam os

operadores. Estes arcos representam a dependência entre os dados. A repre-

sentação dos programas nesta forma de grafo é muito atrativa para máquinas

paralelas, desde que os nós que não possuam dependência de dados possam

disparar concorrentemente [Veen, 1986].

A computação na arquitetura a fluxo de dados ocorre quando um operador

(nó) é ativado, ou seja, quando os dados (tokens) necessários estão presentes

em seus arcos de entrada, após ativado o operador dispara, ou seja, realiza

a computação dos dados presentes em seus arcos e envia o resultado para o

seu arco de saída [Dennis & Misunas, 1975]. Na Figura 4.1 é ilustrado este

processo.

Figura 4.1: Processo de disparo de um operador em uma arquitetura a fluxode dados.

26

Page 57: ChipCflow: ferramenta para conversão de código C em uma ...

4.1.1 Estruturas Condicionais

Para a execução de instruções condicionais em arquiteturas a fluxo de da-

dos, faze-se necessário o uso de operadores que decidam de acordo com um

sinal true ou false qual o destino que o dado recebido em seu arco de entrada

deve seguir (ex.: Operador Branch, Figura 4.2). Para definir em qual arco o

operador deve entregar o dado disponível em sua entrada, é necessário que ele

receba o valor de controle, normalmente gerado por um operador de decisão

(ex.: Operador decider, Figura 4.2). Um operador de decisão recebe dois dados

em suas entradas, testa uma condição e gera em sua saída um valor true ou

false. Este operadores são apresentados na Figura 4.2.

Figura 4.2: Operadores branch, merge não determinístico e decisão, adaptadode [Veen, 1986].

O operador Non Deterministic Merge, descrito na Figura 4.2, é utilizado no

contexto condicional, entretanto não depende de um sinal de controle para

selecionar entre dois dados que recebe em suas entradas, ele simplesmente

transfere o primeiro dado a chegar para sua saída.

A Figura 4.3 mostra uma estrutura condicional que implementa a seguinte

expressão: if teste then f(x, y) else g(x, y). O grafo gerado, recebe como entrada

os dados x, y, e de acordo com o valor recebido como teste, decide se enviará

o fluxo ao sub-grafo f ou sub-grafo g, fazendo com que somente um dos sub-

grafos seja executado. Ao final da execução de um dos sub-grafos, o resultado

de sua execução é enviado para um operador merge não determinístico, que

repassa para a sua saída o primeiro sinal recebido em uma de suas entradas,

garantindo assim que a execução siga corretamente após a computação da

estrutura condicional [Veen, 1986].

4.1.2 Estruturas Iterativas e reentrância

A existência de ciclos em uma arquitetura a fluxo de dados, pode gerar

alguns problemas [Veen, 1986]. A Figura 4.4 apresenta exemplos deste pro-

blemas. No grafo a esquerda (A), ocorrerá um estado chamado deadlock, ou

27

Page 58: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 4.3: Grafo correspondente a estrutura condicional if teste then f(x, y)else g(x, y) [Veen, 1986].

seja, o processamento ficará impedido de continuar, pois o loop não atenderá

a condição de disparo. No grafo a direita (b) o operador nunca deixará de

disparar, pois sua entrada receberá dados infinitamente.

Figura 4.4: Problemas com ciclos em arquiteturas a fluxo de dados [Veen,1986].

Embora os exemplos da Figura 4.4 não representem a realidade, eles ilus-

tram problemas que podem surgir em qualquer grafo cíclico, caso ações não

sejam tomadas. A forma correta de implementação de um ciclo em um grafo é

apresentada na Figura 4.5 [Veen, 1986]. Neste exemplo, os operadores lidam

com um conjunto de dados, x e y. Este grafo implementa um comando whilef(x) do begin (x,y):=g(x,y), e usa um método conhecido como lock method para

proteger os sub-grafos da reentrância (f e g). São utilizados operadores mergee branch, para o controle da execução, além de uma verificação da condição

de parada do loop.

Devido a forma como o grafo da Figura 4.5, foi implementada, com uma

regra de habilitação bem definida, garante-se que cada um dos nós só seja

disparado após o sub-grafo g ter liberado os seus dois tokens. Esta resolução

28

Page 59: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 4.5: Construção de loop com controle de reentrância [Veen, 1986].

da reentrância gera uma arquitetura a fluxo de dados chamada de estática[Veen, 1986].

Para obtenção de um maior nível de paralelismo cada iteração de um grafo

deveria ser executada em uma instância de grafo separada, esta forma de

execução, com cópia de código, requer uma maquina capaz de criar novas

instâncias do sub-grafo e direcionar seus tokens para a instância apropriada.

Para a identificação destes tokens, pertencentes a instâncias diferentes, eles

devem receber tags de identificação, indicando a qual sub-grafo pertencem.

Este tipo de arquitetura a fluxo de dados é chamada de tagged-token e sua

regra de habilitação visa garantir que o nó só será habilitado, se em cada arco

de entrada existam tokens com tags idênticas. Esta resolução da reentrância

gera uma arquitetura a fluxo de dados chamada de dinâmica Veen [1986].

4.2 Modelos de Arquiteturas a Fluxo de Dados

Um ponto relevante a ser destacado é a impossibilidade de implementação

pura do modelo teórico proposto, pois não é possível aceitar infinitos tokens ao

mesmo tempo e nem processa-los em paralelo, pois a memória e o elementos

de processamento são finitos [Johnston et al., 2004].

Como visto anteriormente, existem duas implementações possíveis para

arquiteturas as fluxo de dados: Estática e Dinâmica. Estas implementações

são apresentadas a seguir.

29

Page 60: ChipCflow: ferramenta para conversão de código C em uma ...

4.2.1 Arquitetura Estática

A arquitetura a fluxo de dados estática [Dennis & Misunas, 1975] [Davis,

1978], é um modelo mais simples de ser implementado, mas apresenta uma

capacidade menor para explorar o paralelismo presente em aplicações, pois

limita a sequencia de dados que podem ser colocados nos arcos de vários

operadores, podendo bloquear um operador devido ao atraso na chegada de

seu dado parceiro. Isso se deve ao fato deste modelo de arquitetura permitir

que apenas um dado esteja presente em cada arco de um operador.

Sempre que um operador recebe um dado em algum de seus arcos, este

arco não poderá receber outro dado, até que o seu parceiro de processamento

chegue no outro arco de entrada. Este controle pode ser implementado com o

uso de um protocolo de comunicação, como por exemplo, lock e acknowledge.

A Figura 4.6 ilustra o funcionamento dos métodos lock e acknowledge.

Na Figura 4.6(a), nós para controle de chegada de dados e nós para controle

condicional são utilizados para controlar quantos dados estarão presentes nas

entradas de cada operador. No inicio da iteração um operador é incluído para

selecionar os dados de entrada que serão utilizados em uma iteração e devido

as regras de ativação do operador de controle condicional, ele só irá disparar

depois que subgrafo anterior tenha sido executado e tenha enviado o resultado

de sua computação para suas saídas.

No grafo ilustrado na Figura 4.6(b) os ciclos de execução podem ter tem-

pos diferentes, o que pode causar um comportamento inadequado, ou seja,

fazer com que os valores de x cheguem mais rápido que os valores de y, o que

causaria a computação de dados não parceiros, tornando o grafo não confiá-

vel. Para tornar este grafo confiável, é utilizado o método de acknowledge que

consiste em adicionar arcos extras que indicariam a presença de um dado no

arco e não permitiriam o recebimento de novos dados.

4.2.2 Arquitetura Dinâmica

A arquitetura a fluxo de dados dinâmica [Gurd et al., 1985] [Grafe et al.,

1989] [Papadopoulos & Culler, 1998], é uma evolução da arquitetura estática

que permite que vários dados estejam em cada arco de um operador, sendo

necessário então a identificação destes dados para permitir que os parceiros

corretos sejam computados. Para esta identificação podem ser utilizadas téc-

nicas como o tagged-token ou o code-copying o que torna sua implementação

mais complexa.

Na Figura 4.7 é ilustrada uma estrutura de repetição que utiliza-se do mé-

todo tagged-token. Este método consiste em inserir nós no grafo que tem como

30

Page 61: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 4.6: Métodos para controle de tags em arquiteturas estáticas [Silva,2011].

função manipular identificadores para cada dado que está dentro do grafo, ou

seja, identificá-los e com isso conseguir realizar o processamento dos parcei-

ros corretos.

Figura 4.7: Métodos para controle de tags em arquiteturas dinâmicas [Silva,2011].

O método de code-copying consiste na realização de várias cópias de um

mesmo grafo, permitindo assim o processamento de várias iterações em pa-

ralelo, gerando um alto nível de paralelismo, quando não há dependência de

dados. Este método é semelhante a técnica de loop-unrolling e necessita que

arquitetura permita a reconfiguração parcial dinâmica.

Estes métodos podem ser utilizados para resolver problemas decorrentes

de chamada de funções, visto que os problemas gerados são os mesmo [Veen,

31

Page 62: ChipCflow: ferramenta para conversão de código C em uma ...

1986].

4.3 Descrição de Arquiteturas Propostas

A seguir serão apresentadas as três arquiteturas a fluxo de dados mais in-

fluentes no inicio das pesquisas destes tipo de arquitetura. Em seguida serão

apresentados os dois principais projetos atuais que visam o desenvolvimento

destas arquiteturas.

4.3.1 Máquina a Fluxo de Dados Estática

A maquina proposta por Dennis & Misunas [1975] tinha como objetivo ser

um computador altamente paralelo e este paralelismo deveria ser obtido com

o menor sacrifico possível. Para alcançar este objetivo buscou-se uma arqui-

tetura que representasse o paralelismo das aplicações de forma simples. Para

isso projetou-se um processador para utilizar linguagem a fluxo de dados,

como linguagem base.

A arquitetura básica da máquina proposta pode ser vista na Figura 4.8.

Nesta arquitetura as instruções, que estão disponíveis na Memória do Pro-

grama, são armazenadas em forma de uma fila na Pilha de Instruções. Quando

estão prontas para serem executadas, o módulo Busca tem como função pe-

gar esta instrução, empacota-la e envia-la para a Unidade de Operação, onde

a instrução é processada e seu resultado é empacotado e enviado para o mó-

dulo Atualização. Após receber o pacote de resultados o módulo Atualização

armazena os operando e o resultado na Memória do Programa e verifica se

existe alguma nova instrução a ser executada, caso exista informa ao módulo

Busca para reiniciar o processo. Para a verificação de novas operações a se-

rem executadas é feita uma verificação de presença dos operandos em uma

operação, obedecendo-se assim a regra de disparo de uma arquitetura a fluxo

de dados.

A Memória do Programa é organizada em células de instruções, e cada uma

destas células corresponde a um nó do grafo a fluxo de dados a ser executado.

Uma célula de instruções pode ser vista na Figura 4.9. Cada uma das células é

formada por três registradores, o primeiro deles especifica o valor da operação

a ser executada e os endereços de registradores onde serão armazenados os

resultados. Os outros registradores armazenam os operandos necessários e

também possuem bits para indicar se o operando esta ou não disponível.

Os operadores básicos definidos, para a representação a fluxo de dados, fo-

ram: (a) Operador, responsável por realizar operações como adição, subtração,

32

Page 63: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 4.8: Arquitetura básica da máquina a fluxo de dados estática [Dennis,1980].

Figura 4.9: Célula de Instrução [Dennis & Misunas, 1975].

multiplicação e divisão; (b) Decisão, responsável por receber dois dados, apli-

car um predicado e produzir um token de controle verdadeiro ou falso no seu

arco de saída; (c) T-gate, responsável por repassar o dado do arco de entrada

para a saída quando recebe em seu arco de controle um sinal verdadeiro; (d) F-gate, responsável por repassar o dado do arco de entrada para a saída quando

recebe em seu arco de controle um sinal falso; (e) Merge recebe duas entra-

33

Page 64: ChipCflow: ferramenta para conversão de código C em uma ...

das de dados, uma associada a um valor verdadeiro e outra relacionada a um

valor falso, além de uma entrada de controle, que definirá quais das entradas

será direcionada para o arco de saída, (f) Operador Booleano, responsável por

receber dois dados em seus arcos, realizar uma operação booleana e enviar o

resultado da operação para seu arco de saída. Estes operadores podem ser

vistos na Figura 4.10.

Figura 4.10: Operadores básicos [Dennis & Misunas, 1975].

Além dos operadores foram definidos dois tipos de enlace: (a) Enlace de

dados, responsável por transmitir os dados entre os operadores e (b) Enlace

de controle, responsável por transmitir os valores, verdadeiro ou falso, res-

ponsáveis por definir o funcionamento de alguns operadores ou receber o valor

gerado por um operador booleano. Estes enlaces podem ser vistos na Figura

4.11.

Figura 4.11: Tipos de enlaces [Dennis & Misunas, 1975].

34

Page 65: ChipCflow: ferramenta para conversão de código C em uma ...

4.3.2 Máquina de Manchester

A máquina de Manchester [Gurd et al., 1985] foi desenvolvida como uma

arquitetura a fluxo de dados dinâmica, e para permitir a presença de vários

dados em um mesmo arco, utilizava-se do tag de dados ou tagged token. O

tag consiste em marcar cada um dos pares de dados com um conjunto de

informações de forma a permitir a identificação dos parceiros antes do pro-

cessamento.

As informações contidas em cada um dos dados são: (a) iteration level, para

controlar os operandos de diferentes iterações, que estejam em um mesmo

subgrafo; (b) activation name, para procedimentos, funções recursivas, vi-

sando controlar a qual iteração pertence o dado, caso várias iterações este-

jam ocorrendo simultaneamente; (c) index, serve para indicar de qual parte de

uma estrutura de dados o dado faz parte, como por exemplo, qual elemento

do vetor ele representa. Cada dado que transita pelos arcos dos operadores é

empacotado com o tag contendo estas informações.

Para o gerenciamento dos tags, devem ser criados operadores, que tem

como função criar e alterar estes tags. Os operadores definidos por Gurd

et al. [1985], para este gerenciamento foram: Add to Interaction Level (ADL),

responsável por informar qual o nível da iteração dentro de um grafo e SetIteration Level (SIL), responsável por alterar o nível de uma iteração.

Além destes operadores, foram definidos os operadores de processamento,

como parte da definição da linguagem e fluxo de dados. Este operadores são

[Gurd et al., 1985]: (a) Add Floating-point Values (ADR), responsável por reali-

zar adição entre valores de ponto-flutuante; (b) Branch (BRR), responsável por

controlar os saltos dentro do grafo; (c) Explicit Duplicate (DUP), responsável por

duplicar os dados, ou seja enviar os dados recebidos em seu arco de entrada

e enviar para dois arcos de saída; (d) Compare Floating-point Values (CGR),

responsável pela comparação booleana entre valores de ponto-flutuante; (e)

Multiply Floating-point Values (MLR), responsável pela multiplicação de valores

de ponto-flutuante e Output to Host Processor (OPT), responsável por enviar a

saída do grafo para o processador hospedeiro.

A arquitetura da máquina de Manchester é apresentada na Figura 4.12.

Segundo Gurd et al. [1985] arquitetura é baseada em um anel com quatro

módulos independentes que funcionam em forma de pipeline, sendo o anel

conectado a um processador hospedeiro por meio do módulo de chaveamento

que permite que programas e dados sejam carregados ou enviados ao proces-

sador hospedeiro.

Os dados circulam pelo anel encapsulados e na unidade de matching são

35

Page 66: ChipCflow: ferramenta para conversão de código C em uma ...

formados os pares necessários para o processamento. Como esta unidade

tem capacidade limitada, existe uma unidade de overflow para armazenar os

dados que não tenham espaço na unidade de matching. Após formar os pares,

ou em caso de instruções com apenas uma entrada, é realizada a busca da

instrução a ser executada na memória de instruções. A memória de instruções

armazena o código de máquina do operador a fluxo de dados. Os dados a

serem executados seguem o protocolo First-in-first-out (FIFO) e os dados são

consumidos de acordo com o pipeline do anel.

Figura 4.12: Arquitetura da máquina de Manchester [Gurd et al., 1985].

4.3.3 MIT - Tagged-Token Dataflow (TTD)

O projeto MIT - TTD teve como seu resultado o desenvolvimento de uma

arquitetura a fluxo de dados chamada Tagged-Token Dataflow Architecture(TTDA) [Arvind & Nikhil, 1990] visando o desenvolvimento de pesquisas na

área de computação paralela e de propósito geral com alto desempenho. A

TDDA utiliza o conceito de linguagem a fluxo de dado proposto por Dennis &

Misunas [1975] adaptando-o para construir uma arquitetura a fluxo de dados

dinâmica, ou seja vários dados podem estar presentes em um arco em um

dado momento.

Para a implementação da arquitetura dinâmica fez-se necessário a utiliza-

ção do mesmo princípio da máquina de Manchester, o tagged-token. Os tags

36

Page 67: ChipCflow: ferramenta para conversão de código C em uma ...

possuem cinco partes [Arvind & Nikhil, 1990]: (a)Invocation ID, responsável

por controlar os dados em chamadas a funções e procedimentos; (b)IterationID, responsável por controlar a qual iteração de um loop o dado pertence;

(c)Code block, identifica as funções definidas pelo programador, cada função

possui um identificador; (d)Instruction Address, responsável por identificar

qual a instrução de destino e (e)Destination port, indica qual a porta de destino

o dado deve se entregue, já que um operador de destino pode possuir mais de

uma porta de entrada.

Para gerenciar as tags foi proposto um conjunto de operadores. Para a

manipulação do campo iteration ID foram criados os operadores D para incre-

mentar o campo e D−1 para decrementa-lo. Foi definido também um operador

para atribuir valores para o invocation ID, sempre que for necessário o uso de

loops aninhados e funções. O operador chamado de apply serve para receber

os dados de um code block e depois criar uma nova invocation ID e alterar o

valor da iteration ID, indicando que o token entrou em um novo sub-grafo e

foi definido um operador para recuperar as tags originais para que os dados

possam ser devolvidos a níveis mais externos de um sub-grafo.

Além deste operadores de controle, foram definidos também operadores de

processamento [Arvind, 2005]. Este operadores são apresentados na Figura

4.13.

Figura 4.13: Operadores definidos para a arquitetura TTDA [Arvind, 2005].

O operador Fork recebe um dado em sua entrada e o duplica para suas

saídas, o operador Primitive Ops é o responsável por realizar as operações

aritméticas básicas como soma, subtração, multiplicação e divisão, o operador

Switch recebe um dado em sua entrada e de acordo com um sinal de controle

define para qual saída envia-lo e o operador Merge recebe dois valores de

entrada e de acordo com um valor de controle define qual valor será enviado

para a sua saída.

37

Page 68: ChipCflow: ferramenta para conversão de código C em uma ...

O TTDA utiliza uma linguagem chamada de ID. Na definição desta lingua-

gem as instruções foram definidas com duas entradas, um campo opcode para

descrever o tipo de operação a ser realizada, um campo chamado literal/cons-

tante que pode ser um valor literal ou um deslocamento de memória offsete campos para designar o destino da saída de cada uma das instruções. O

formato de uma instrução da linguagem ID pode ser visto na Figura 4.14.

Figura 4.14: Formato da instrução da linguagem ID utilizada pela TTDA [Ar-vind & Nikhil, 1990].

A arquitetura da máquina TTDA é formada por uma quantidade pré-definida

de elementos de processamento e por unidades de armazenamento conecta-

das em rede. As unidades de armazenamento contem os dados e códigos

que serão utilizados pelos elementos de processamento. A arquitetura de um

elemento de processamento pode ser vista na Figura 4.15. Os elementos de

processamento tem uma estrutura em formato de pipeline, podendo assim,

conter diversos dados ao mesmo tempo, em estágios diferentes.

O elemento de processamento é formado por um conjunto de módulos, cada

um deles sendo um estágio do pipeline. O módulo Wait-Match é uma memória

onde os dados ficam armazenados até a chegada de seus parceiros, caso uma

instrução necessite de mais de um dado para ser executada. Caso a instrução

necessite de apenas um dado, este é repassada para a Instruction-Fetch que

é responsável por fazer a busca da instrução a ser executada, além de todas

as outras informações necessárias para a execução da instrução. Os módu-

los ALU e Compute Tag funcionam em conjunto, sendo a ULA uma unidade

de lógica e aritmética simples e o Compute Tag atribui as tags corretas a um

dado que esta saindo do elemento de processamento. O módulo Form-Tokensconstrói a mensagem a ser enviada para a saída do elemento de processa-

mento, com a junção do resultado gerado pela ULA e as tags informadas pelo

Compute Tag. O módulo Control é responsável pela manipulação dos estados

do elemento de processamento e também permite a conexão do elemento de

processamento com o mundo externo.

38

Page 69: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 4.15: Elemento de processamento definido para a TTDA [Arvind &Nikhil, 1990].

4.3.4 Wavescalar

WaveScalar [Swanson et al., 2007] é um conjunto de instruções a fluxo de

dados e um modelo de execução projetado para utilizar processadores com

baixa complexidade e alto desempenho. No WaveScalar, é possível executar

aplicações convencionais de única thread, e também multi-thread, no estilo

pthread.

No WaveScalar, o programa é representado para o processador como grafos

a fluxo de dados [Swanson et al., 2007]. Cada nó no grafo é uma instrução,

os arcos entre os nós codificam estaticamente e são dirigidos, representando

para onde irá a saída da operação. O WaveScalar utiliza um código assemblemuito próximo do tradicional apresentando diferença em dois aspectos: Pri-

meiro, apesar das arestas do grafo serem sintaticamente iguais a nomes de

registradores, elas não correspondem a uma entidade arquitetural específica;

Segundo, a ordem das instruções no código não afeta a sua execução, uma

vez que a definição dessa ordem é controlada pelo fluxo de dados. Cada ins-

trução possui um endereço único, que é usado em chamadas de funções. Na

Figura 4.16 pode ser visto um trecho de programa, o grafo correspondente e o

assemble gerado.

O WaveScalar é uma arquitetura a fluxo de dados dinâmica, que utiliza

Tags para identificar os dados parceiros em um operador, estes Tags são cha-

mado de wave numbers [Swanson et al., 2007]. A utilização deste tipo de

39

Page 70: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 4.16: Exemplo de código gerado pelo WaveScalar [Swanson et al.,2007].

recurso é conhecido como tagged-token.

A arquitetura do WaveScalar é conhecida como WaveCache. O WaveCache é

formado por todos os elementos necessários para a execução de um programa

WaveScalar exceto a memória principal. O WaveCache é formado por um

conjunto de elementos de processamento idênticos organizados de forma hie-

rárquica, buscando reduzir os custo de comunicação [Swanson et al., 2007].

O WaveCache é formado por um conjunto de clusters, cada um deles con-

tendo quatro domínios formados por oito elementos de processamento idên-

ticos. Cada um dos clusters contém quatro bancos de memória cache, uma

interface de comunicação com os clusters vizinhos além de uma interface de

comunicação com a memória. Na Figura 4.17 é apresentada a estrutura de

um clusters.

No momento da execução cada instrução do programa WaveCache é alo-

cada em um elemento de processamento, buscando deixar instruções que

apresentem dependências no mesmo cluster, para reduzir o custo de comuni-

cação. A execução das instruções é realizado utilizando um modelo de pipelinede cinco estágios (Figura 4.18). Segundo Swanson et al. [2007], estes estágios

são:

1. Input: Estágio de recebimento de uma instrução pelo elemento de pro-

cessamento, esta instrução pode ser enviada por outro elemento de pro-

cessamento ou ser uma reentrada do próprio elemento;

2. Match: Verifica se a instrução que chegou esta pronta para ser executada,

ou seja, se seus operandos já estão disponíveis;

3. Dispatch: Responsável por selecionar uma instrução que esteja disponí-

vel para ser executada, buscar seus operandos na tabela de matching e

40

Page 71: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 4.17: Estrutura de um cluster do WaveCache [Swanson et al., 2007].

enviar os dados para a execução;

4. Execute: Estágio de execução da instrução pelo elemento de processa-

mento. O resultado da execução é enviado para uma fila de saída, ou

para a rede de interconexão dos clusters;

5. Output: A instrução é enviada para algum consumidor pela rede que

interliga os domínios de um WaveCache.

Um dos maiores problemas em arquiteturas á fluxo de dados é a organi-

zação da memória para permitir que os dados estejam disponíveis de forma

não sequencial no momento que um dado operador necessite [Swanson et al.,

2003]. Para resolver este problema o WaveScalar utiliza um mecanismo de

anotação que busca garantir que operações de memória aconteçam na ordem

correta, utilizando-se da representação de dependência de dados. Um exemplo

do mecanismo de anotação pode ser visto na Figura 4.19.

Na Figura 4.19 pode-se perceber que a anotação utilizada é formada por

três partes, a primeira parte serve para identificar qual instrução de load ou

store deve ser executada anteriormente, a segunda parte representa a sequen-

cia da própria instrução e a terceira parte qual a próxima instrução a ser exe-

cutada. O "."encontrado na primeira linha serve para indicar que esta é a

primeira instrução de memória a ser executada. O "."encontrado na ultima

linha serve para indicar que ela é a ultima instrução de memória a ser execu-

tada. Em instruções que necessitam executar um salto, não é possível prever

41

Page 72: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 4.18: Estrutura do pipeline de execução do WaveScalar [Swanson et al.,2007].

qual a próxima instrução a ser executada, assim a terceira parte da anotação

é representada pelo símbolo "?".

A geração do programa WaveScalar seria feita com o uso de um compi-

lador específico para a arquitetura, sendo o compilador o responsável pela

organização de memória principal e escalonamento do conjunto de instruções

buscando a execução de forma mais eficiente, mas com a descontinuidade do

projeto, este compilador não foi desenvolvido.

4.3.5 TRIPS

TRIPS é uma arquitetura polimórfica, hibrida von-Neumann/fluxo de da-

dos, a qual pode ser configurada para diferentes granularidades e tipos de

paralelismo [Sankaralingam et al., 2003].

Segundo Swanson et al. [2003] o TRIPS utiliza uma arquitetura híbrida

Very Long Instruction Word (VLIW)/fluxo de dados, e esta investe no mesmo

desafio tecnológico que o projeto Wavescalar.

TRIPS contém mecanismos que habilitam os núcleos de processamento e

sistema em memória "on-chip"que pode ser configurado e combinado em dife-

rentes nós para paralelismo ao nível de instruções, dados ou threads. Para

42

Page 73: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 4.19: Exemplo do mecanismo de anotação utilizado pelo WaveScalar[Swanson et al., 2007].

explorar o paralelismo da aplicação e prover um grande uso dos recursos dis-

poníveis, TRIPS usa três nós de execuções diferentes: D-morph, que procura o

paralelismo em nível de instruções; T-morph que trabalha ao nível de thread,

mapeando múltiplos threads em um único núcleo do TRIPS e S-morph que

tem como objetivo aplicações como fluxo de mídia (streaming media), com alto

nível de paralelismo ao nível de dados [Rutzig et al., 2007].

Na Figura 4.20 é apresentada a arquitetura do TRIPS. Na Figura 4.20(a) é

demonstrado o diagrama da arquitetura TRIPS que irá ser implementada em

um protótipo em chip. O protótipo consiste de quatro núcleos polimórficos,

com dezesseis nós de execução em um arranjo de 32Kb de memória conec-

tados por uma rede de roteamento e um grupo de controladores de memória

distribuídos com canais para memória externa. Na Figura 4.20(b) é descrito

uma visão expandida de um núcleo do sistema TRIPS e o sistema primário de

memória. Na Figura 4.20(c) é descrito um nó de execução do sistema.

Figura 4.20: Arquitetura do TRIPS [Sankaralingam et al., 2003].

4.4 Considerações Finais

Neste capítulo foi apresentado o conceito de arquitetura a fluxo de dados

estática e dinâmica. Este modelo de arquitetura foi proposto por Dennis &

43

Page 74: ChipCflow: ferramenta para conversão de código C em uma ...

Misunas [1975]. No inicio de seu desenvolvimento vários trabalhos foram

propostos, como os descritos no capítulo, após estas pesquisas este tipo de

arquitetura passou por um período de estagnação, não tendo trabalhos rele-

vantes desenvolvidos. Por volta dos anos 2000 algumas pesquisas voltaram a

ser desenvolvidas nesta área e foram descritas neste capítulo.

44

Page 75: ChipCflow: ferramenta para conversão de código C em uma ...

CAPÍTULO

5Compiladores para Hardware

Estes compiladores representam uma grande evolução para a síntese de

alto nível de circuitos digitais, por aproximar programadores de linguagens de

alto nível desta área tão restrita, a projetistas de hardware.

Segundo Hall et al. [2009] as linguagens de alto nível e o desenvolvimento

de compiladores eficientes podem ser vistos como as bases centrais para o

desenvolvimento destas arquiteturas.

5.1 Trident

O Trident [Tripp et al., 2007] é um framework aberto para compilação, que

visa a geração de VHDL tendo como entrada algoritmos descritos em lingua-

gem C. O Trident utiliza técnicas de otimização de código tradicional e técnicas

voltadas à computação de alto desempenho, como por exemplo verificação de

operações que podem ser executadas concorrentemente, geração de circuitos

para controle de fluxo de dados entre a memória, registradores e unidades de

operação.

Para uso do Trident, o programador deve particionar manualmente o pro-

grama e escrever o código C responsável pela comunicação entre o software e o

hardware. O trecho de código a ser mapeado para hardware contém restrições

quanto ao código de entrada, não sendo permitidos comandos de impressão,

código recursivo, uso de ponteiros, funções com argumentos ou passagem de

parâmetros ou vetores sem tamanho declarado. Durante sua execução, os ve-

45

Page 76: ChipCflow: ferramenta para conversão de código C em uma ...

tores e variáveis são alocadas de forma estática e todas as operações de ponto

flutuante são mapeadas para unidades de hardware.

Os passos utilizados na compilação são apresentados na Figura 5.1 e des-

critos a seguir:

• Criação da representação intermediária: Esta representação é gerada

pelo Low Level Virtual Machine (LLVM) [Lattner & Adve, 2004], que é um

framework que utiliza o gcc como front-end, para a geração de um código-

objeto independente de plataforma, conhecido como LLVM Bytecode;

• Transformações na representação intermediária: Para cada instrução if

é criado um hyperbloco1, para explorar o paralelismo a nível de instru-

ções. Nesta fase também é gerado o Control Flow Graph (CFG), que é

utilizado para as otimizações de código e para mapeamento de todas as

operações de hardware em módulos selecionados pelo usuário;

• Sincronização: Neste passo é feita a alocação de vetores. Esta alocação é

feita em tempo de compilação, de forma a se obter um tempo de acesso

determinístico à memória e baixa latência no acesso aos dados. Após

isso é realizada a sincronização, ou determinação de ordem de execução

dos hyperblocos. Para isso, o Trident seleciona a melhor opção entre

os seguintes algoritmos: as soon as possible,as late as possible, forcedirected ou iterative modulo.

• Síntese: Neste passo é feita a tradução para VHDL-RTL utilizando-se o

GFC.

O Trident compartilha seu código e serve de extensão para o SeaCucumber[Tripp et al., 2002], que é um compilador desenvolvido pela Brigham YoungUniversity, com objetivo de gerar VHDL com uma entrada em linguagem Java.

O Trident fornece a este compilador a capacidade de aceitar código de entrada

em C, suportar operações de ponto-flutuante, além de ser o responsável pela

geração do código VHDL.

5.2 Molen

O Molen [Panainte et al., 2007] é um compilador que tem como objetivo ge-

rar código para uma arquitetura específica conhecida como máquina de Molen

[Vassiliadis et al., 2004], constituída por um processador de uso geral e um

FPGA. Este compilador foi desenvolvido para gerar código para uma máquina

1Denominação dada aos blocos de código.

46

Page 77: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 5.1: Fluxo de Compilação Trident [Tripp et al., 2007].

de Molen formado por um processador IBM PowerPC 405 e um FPGA Virtex II

Pro.

O Molen foi construído a partir de dois frameworks, o SUIF [Hall et al.,

1998], utilizado como front-end, e o Machine SUIF [Smith & Holloway, 2002],

utilizado como back-end e ferramenta para aplicar as otimizações para a ge-

ração do código para o FPGA Virtex II. Este código é gerado por meio de uma

representação intermediária própria do Machine SUIF.

Para a geração de código para o processador PowerPC, foi desenvolvido um

back-end específico de acordo com o PowerPC Embedded Application BinaryInterface (EABI) [Sobek & Burke, 1995], no qual estão definidos os padrões

de instruções, alocação de registradores, emulação de operações de ponto-

flutuante e outras informações relevantes para o desenvolvimento do código

alvo.

O Molen propõe uma nova abordagem para sincronização da configurações

de hardware necessárias para executar trechos de uma aplicação. Devido à

complexidade de algumas aplicações, os circuitos em hardware necessários

podem ocupar uma área maior que a disponível no FPGA, o que pode gerar

conflito ou áreas de hardware sobrescritas.

Para evitar este problema foi proposto, com base na eliminação parcial de

expressões redundantes [Cai & Xue, 2003], um algoritmo que busca redu-

zir a área final necessária no FPGA. No caso de não haver espaço suficiente

47

Page 78: ChipCflow: ferramenta para conversão de código C em uma ...

para a implementação dos circuitos de hardware, parte da aplicação, que ini-

cialmente seria implementada em hardware, pode ser executada em software,

paralelamente com a execução de outros trechos em hardware. Desta forma

pode-se assim ganhar desempenho, devido aos altos tempos para a reconfigu-

ração de um FPGA [Sima et al., 2002].

5.3 HThreads: Modelo de programação multithreads

O HTreads [Andrews et al., 2008a] é um compilador para a linguagem C,

que utiliza como front-end o Gnu C Compiler (GCC), que é responsável pela

produção de uma forma intermediária de hardware.

Utilizando-se desta representação intermediária é gerado o código VHDL

para as threads que serão executadas em hardware, o código para o compi-

lador alvo e as bibliotecas necessárias para a sincronização e comunicação

entre as threads.

Para representação intermediária o HTreads utiliza o GIMPLE [Merrill, 2003],

que é uma forma de árvore para representação intermediária que visa facilitar

a otimização de código.

O fluxo de compilação do HTreads é apresentado na Figura 5.2.

Figura 5.2: Fluxo de Compilação HThreads [Andrews et al., 2008a].

O HThreads utiliza a mesma sintaxe e é totalmente compatível com a bi-

blioteca pthreads, permitindo inclusive o teste do código em linguagem de alto

nível em um computador executando qualquer distribuição do sistema opera-

cional Linux.

48

Page 79: ChipCflow: ferramenta para conversão de código C em uma ...

Para o funcionamento correto das threads, as funções que seriam desem-

penhadas pelo sistema operacional foram implementadas via hardware, como

por exemplo o gerenciamento de threads, gerenciamento de semáforos e inter-

rupções da Unidade Central de Processamento (UCP).

5.4 ARISE

O Aristotle Reconfigurable Instruction Set Extension (ARISE) [Vassiliadis et al.,

2009] é um framework que visa permitir a um processador de uso geral sua

utilização com um co-processador para execução de partes de código que ne-

cessite de computação intensa ou com um unidade funcional para execução

de instruções customizadas de granulação fina. O ARISE permite um desen-

volvimento modular de aplicações permitindo a separação entre o hardware e

o software.

O ARISE utiliza a linguagem C/C++ para geração de aplicações e utiliza

como front-end de compilação o MachineSUIF [Smith & Holloway, 2002]. O

código gerado tem como alvo uma arquitetura especifica, formada por um

processador MIPS-I e FPGAs. Nesta arquitetura o número de FPGAs é variável,

pois ela apresenta uma interface para controle dos FPGAs. O código objeto

gerado é constituído por instruções específicas para a arquitetura e instruções

para o processador MIPS-I.

Figura 5.3: Organização Geral de uma Máquina ARISE. [Vassiliadis et al.,2009].

A organização de uma maquina ARISE é apresentada na Figura 5.3 e con-

49

Page 80: ChipCflow: ferramenta para conversão de código C em uma ...

siste em: Um processador principal, um decodificador de instruções, que tem

como objetivo verificar se a instrução deve ser executada pelo FPGA ou pelo

processador de uso geral, a interface de comunicação com os FPGAs e os con-

troladores de execução das instruções (CCU wrappers).

A geração de código no ARISE não é um processo muito simples, sendo

composto por uma série de etapas. A etapa inicia consiste no profiling do

código, que é sub-dividido em duas partes, primeiro é feito o profiling que irá

servir para guiar o restante do processo de desenvolvimento e a segunda parte

serve para estimar o custo de comunicação entre procedimentos e a carga do

sistema. Após esta etapa é feita a otimização do código visando diminuir o

tempo de execução da aplicação, após isso é realizada a separação do código,

dividindo-se o que será executado em hardware e o que será executado em

software , como saída destas etapas temos o profiling results e o pruned code.

Estes arquivos de saída são utilizados para definir qual o tipo de instrução será

gerada, instruções para serem executadas em uma unidade funcional ou uma

instrução de granulação fina. O terceiro passo consiste em fazer as anotações

no código original, com diretivas pragma, de forma a marcar as partes de

instruções ARISE e as instruções a serem executadas pelo processador de uso

geral. Após as execuções destes passos pelo front-end o código é repassado

para o back-end que deve executar três tarefas: desenvolvimento do software,

desenvolvimento do hardware e integração e avaliação da aplicação. Caso a

avaliação da aplicação não seja satisfatória o processo pode ser reiniciado com

a intervenção do programador na definição do que ser executa em software ou

em hardware. O fluxo de desenvolvimento é apresentado na Figura 5.4.

5.5 Nenya / Galadriel

Este projeto foi desenvolvido como uma proposta inovadora para a com-

pilação de algoritmos descritos em Java, por meio de seus bytecodes, para

hardware reconfigurável [Cardoso, 2000].

No processo de compilação existem dois compiladores que atuam em série

para a geração de hardware especializado de forma eficiente. A compilação

é realizada a partir dos bytecodes da linguagem Java, objetivando gerar uma

representação intermediária que explore altos graus de paralelismo.

A forma de atuação dos compiladores e algumas técnicas utilizadas pelos

mesmos podem ser observadas na Figura 5.5 e são descritas a seguir.

O Galadriel é o compilador de front-end responsável por retirar as infor-

mações necessárias do classfile fonte e com estas informações gerar a repre-

sentação intermediária, que consiste em: Grafo de fluxo de controle, grafo de

50

Page 81: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 5.4: Fluxo de desenvolvimento de aplicações no ARISE. [Vassiliadiset al., 2009].

dependência de controle, grafo de dependência de dados,Grafo de dependên-

cias de fusão, grafo hierárquico de dependências de programa e grafo de fluxo

de dados.

O Galadriel não suporta todo o conjunto Java, não permitindo, por exem-

plo, que o código a ser analisado possua invocação de métodos.

Após a geração da representação intermediária, o Nenya é responsável por

executar uma série de transformações nos grafos gerados para otimização do

circuito a ser gerado. Estas transformações envolvem: Re-associação das ope-

rações, redução do custo de operações, aferição do número de bits de repre-

sentação, propagação de padrões de constantes ao nível de bits e aferição do

número de bits em regiões cíclicas.

O código gerado pelo Nenya em VHDL é otimizado para uma arquitetura

constituída por um FPGA e uma ou mais memórias RAM (Random Access

51

Page 82: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 5.5: Fluxo dos compiladores Galadriel e Nenya [Cardoso, 2000].

Memory) [Cardoso & Neto, 2001]. O hardware reconfigurável gerado é formado

por duas unidades: Unidade de dados: responsável por enviar os dados sobre

seu estado à unidade de controle e unidade de controle: responsável pelo

controle dos acessos às memórias, pelo controle de escritas em registradores

e pela execução correta dos ciclos.

Para a geração da unidade de dados o compilador utiliza uma biblioteca

de macrocélulas parametrizáveis. Cada uma destas macrocélulas é respon-

sável pela geração de circuito especializado para a realização de determinada

operação [Cardoso, 2000].

A descrição da unidade de controle é gerada pelo compilador em VHDL-RTL

(Very High Speed Integrated Circuit Hardware Description Language - RegisterTransfer Level) comportamental e por isso é necessário utilizar uma ferra-

menta de síntese lógica para se obter o circuito final.

O Nenya implementa partição temporal com base no algoritmo simulatedannealing [Cardoso, 2000]. A partição temporal consiste em permitir que um

programa que exceda a capacidade do FPGA seja executado. Para esta execu-

ção o circuito gerado é divido em partes chamadas de frações temporais, que

são executadas individualmente.

52

Page 83: ChipCflow: ferramenta para conversão de código C em uma ...

5.6 ROCCC (Riverside Optimizing Configurable Com-

puting Compiler)

O ROCCC [Buyukkurt et al., 2006] [Guo et al., 2005] é um compilador para

sistemas reconfiguráveis, construído a partir do SUIF [Stanford SUIF Compi-

ler Group, 1994], que é um framework de compilação desenvolvida na univer-

sidade de Stanford para a pesquisa colaborativa em técnicas de compilação

[Hall et al., 1998] e do Machine SUIF [Smith & Holloway, 2002], que é um

framework para construção de back-ends de compiladores, desenvolvido pela

Universidade de Harvard, com suporte a várias linguagens de alto nível como:

C/C++, Fortran, Java.

O fluxo de funcionamento do ROCCC pode ser visto na Figura 5.6. O

ROCCC apresenta algumas restrições no código de entrada, associadas à res-

trições ao mapeamento do código para o FPGA, como por exemplo, o uso de

funções recursivas e uso de ponteiros.

Figura 5.6: Fluxo de funcionamento do ROCCC [Buyukkurt et al., 2006].

Este compilador foi desenvolvido com o objetivo de otimizar aplicações de

fluxo intenso de dados, portanto opera melhor com aplicações sem muitos

desvios no fluxo de controle. O ROCCC está dividido em dois componentes

principais: o front-end, responsável por transformações de laços (como por

exemplo movimentação de código ciclo-invariante, abertura total ou parcial de

laços e fusão de laços), e o back-end responsável por otimizações em nível de

procedimentos (como por exemplo propagação de constantes, propagação de

cópias e eliminação de código morto).

Estas otimizações são aplicadas à representação intermediária gerada, que

é denominada CIRRF (Compiler Intermediate Representation for ReconfigurableFabrics), que tem como objetivos explorar ao máximo o paralelismo existente

nos laços, minimizar o acesso à memória, com a reutilização de dados e ge-

rar um pipeline eficiente para minimizar a necessidade de ciclos de processa-

mento.

53

Page 84: ChipCflow: ferramenta para conversão de código C em uma ...

De posse da CIRRF o compilador gera o componente VHDL para cada nó do

CFG que corresponda a partes da aplicação que serão executadas com maior

frequência. Para as partes de código executadas com menor frequência ou

para as quais o FPGA não é eficiente, são geradas as funções em linguagem C.

5.7 Spark

O Spark [Gupta et al., 2004] é o projeto de um compilador de síntese de

alto nível, que tem com objetivo alcançar resultados semelhantes aos obtidos

em projeto desenvolvidos manualmente, por especialistas da área. O Spark

utiliza o ANSI-C como linguagem de entrada e apresenta algumas restrições,

como por exemplo: não permite o uso de ponteiros e funções recursivas.

O Spark foi desenvolvido para facilitar o desenvolvimento de aplicações de

granulação grossa e fina, aplicando técnicas de otimização que podem ser

avaliadas no código VHDL gerado, permitindo otimização do código.

Uma das partes principais do compilador Spark, é chamada de caixa de

ferramentas de transformações, que permite ao programador definir os parâ-

metros das transformações e otimizações que serão aplicadas no código VHDL

a ser gerado. A caixa de ferramenta permite: extrair dependência de dados,

aplicar técnicas de paralelismo de código, aplicar técnicas para renomear va-

riáveis e aplicar como, eliminação de código morto, propagação de constantes

e propagação de cópias.

Para aplicar as transformações e otimizações no código e armazenar toda a

descrição comportamental, o Spark utiliza como representação intermediária

um GHT e um Grafo de fluxo de controle-dados (GFCD), que são acessados

durante todo o processo de compilação, como pode ser visto na Figura 5.7.

5.8 Considerações Finais e Analise Comparativa.

Neste capítulo foram apresentados alguns compiladores para geração de

hardware estudados durante o desenvolvimento deste trabalho. Na tabela 5.1

é apresentada uma comparação entre recursos encontrados nestes compila-

dores.

Pode-se verificar na Tabela 5.1 grande parte dos compiladores estudados

tem como código de entrada a linguagem C, por ser uma linguagem de pro-

pósito geral, com um ótimo conjunto de operadores e estruturas de dados

[Kernighan & Ritchie, 1988]. Pode-se também perceber que os recursos bási-

cos das linguagens, como comando de decisão IF e laços de repetição, estão

presentes em todos os compiladores.

54

Page 85: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 5.7: Fluxo de funcionamento do Spark [Gupta et al., 2004].

Tabela 5.1: Comparação entre os compiladores estudados.Compilador Nenya/

GaladrielSpark ROCCC Trident Molen HThreads

CódigoFonte

Java C C/C++,Fortran,Java

C C C

If Sim Sim Sim Sim Sim SimLaços Sim Sim Sim Sim Sim SimEstruturas Não Não Sim Sim Sim SimPonteiros Não Não Não Não Sim SimFunções Não Não Não Não Sim SimRepres.Interme-diária

GHDP GHT+GFCD

CIRRF LLVM MachineSUIF IR

Gimple

Código Ge-rado

VHDL VHDL VHDL+C VHDL VirtexII Pro +PowerPc

VHDL

55

Page 86: ChipCflow: ferramenta para conversão de código C em uma ...

56

Page 87: ChipCflow: ferramenta para conversão de código C em uma ...

CAPÍTULO

6O Projeto ChipCflow

Neste capítulo é descrito o projeto ChipCflow [Silva, 2006], que é a base

para o desenvolvimento deste trabalho. Durante o desenvolvimento do traba-

lho descrito nesta tese, foi realizado em paralelo o aperfeiçoamento da arqui-

tetura do projeto.

No projeto ChipCflow tem-se como objetivo o desenvolvimento de uma fer-

ramenta para execução de algoritmos descritos em linguagem C, utilizando o

modelo a fluxo de dados em hardware reconfigurável, e vem sendo desenvol-

vido no Laboratório de Computação Reconfigurável (LCR), vinculado ao De-

partamento de Sistemas de Computação do Instituto de Ciências Matemáticas

e de Computação (ICMC) da Universidade de São Paulo. O principal obje-

tivo do projeto aqui apresentado é utilizar o modelo a fluxo de dados estático,

buscando melhorar o desempenho de aplicações descritas em linguagem C. A

ferramenta aproveita o potencial de paralelismo existente implicitamente na

aplicação. A principio toda a aplicação é convertida em hardware a fluxo de

dados estático, visando-se em um segundo estágio, acelerar somente as par-

tes de maior processamento, como loops e cálculos complexos, aproveitando

de forma mais eficiente partes passíveis de grande paralelismo, considerado

natural no modelo a fluxo de dados [Silva et al., 2009].

A pesquisa para desenvolvimento da ferramenta foi iniciada pelo Prof. Dr.

Jorge Luiz e Silva em sua tese de doutorado [Silva, 1992], que tinha como

objetivo propor uma máquina a fluxo de dados dinâmica tolerante a falhas.

No projeto inicial foi proposto o conceito básico utilizado nos operadores da

máquina a fluxo de dados estática do projeto ChipCflow.

57

Page 88: ChipCflow: ferramenta para conversão de código C em uma ...

O fluxo de funcionamento do projeto ChipCflow pode ser visto na Figura

6.1. Para a geração desta arquitetura um programa descrito em linguagem

C é pré-compilado visando a geração de um Control Dataflow Graph (CDFG).

Este CDFG após passar por otimizações necessárias é então convertido em

VHDL utilizando uma base de operadores pré-definidos, gerando-se assim

uma arquitetura a fluxo de dados estática. Após gerado o VHDL o próximo

passo consiste em sintetizar e carregar o programa em um FPGA, utilizando

ferramentas disponibilizadas pelo fabricante do FPGA.

Figura 6.1: Fluxo de execução do ChipCflow, adaptado de [Silva, 2006].

6.1 Operadores propostos para a máquina a fluxo de

dados estática do projeto ChipCflow

Um programa para ser executado em uma máquina a fluxo de dados deve

ser organizado como uma máquina a fluxo de dados [Lopes, 2012]. Neste tipo

de máquina cada nó existente especifica uma instrução a ser executada e os

arcos entre os nós especifica a dependência entre as instruções e o caminho

dos dados entre os nós.

Como definido em [Arvind, 2005] [Dennis & Misunas, 1975] e utilizado em

[Silva, 1992], exitem dois tipos de arcos na descrição da máquina a fluxo de

dados. Como mostra a Figura 6.2, um arco é representado por uma linha

continua e representa os valores a serem enviados entre os nós. O arco re-

presentado por uma linha pontilhada representa os valores booleanos, que

servem como sinais de controle.

Para possibilitar a transformação do código escrito em linguagem de alto

nível em um CDFG, foram definidos operadores específicos para o projeto.

Estes operadores tem a função de realizar todo o processamento e também

gerenciamento do funcionamento da máquina a fluxo de dados. A principio

58

Page 89: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 6.2: Tipos de links utilizados no projeto da máquina a fluxo de dadosestática do projeto ChipCflow [Arvind, 2005].

estes operadores estão divididos em duas categorias: (a) Operadores de pro-

cessamento e (b) operadores de gerenciamento de memória.

A seguir serão descritos todos os operadores e suas funcionalidades.

6.1.1 Operadores de processamento

Para o processamento das instruções definidas em linguagem de alto nível

serão utilizados seis operadores, que são:

• decider: operador que recebe dois valores como entrada e de acordo com

uma função de teste, tem como saída o valor true ou false,

• Non-deterministic merge: operador que recebe dois valores de entrada e o

primeiro a ser recebido é enviado para a saída,

• Deterministic merge: operador que recebe dois valores e de acordo com

um valor de controle define qual das entradas será copiada para a saída,

• Branch: operador que recebe um valor como entrada e de acordo com um

valor de controle recebido, decide para qual saída irá enviar uma cópia

do valor,

• Copy: operador que copia um valor de entrada para duas saídas e

• Operator: operador que implementa operações aritméticas básicas (soma,

subtração, divisão, multiplicação e operações lógicas).

Na Figura 6.3 são descritos os seis operadores e seus respectivos símbolos.

Após o processamento, ou seja, o consumo dos dados, os mesmos são envi-

ados para as saídas dos operadores. O processo de entrada e saída dos dados

dos operadores pode ser visto na Figura 6.4. Na primeira parte da Figura

pode-se ver os dados (círculos preenchidos) de entrada em cada operador, da-

dos para computação ou dados de controle. Após a computação, os dados

(círculos preenchidos) são enviados para as saídas de acordo com o processa-

mento realizado.

59

Page 90: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 6.3: Operadores do projeto da máquina a fluxo de dados estática doprojeto ChipCflow [Silva, 2006].

Figura 6.4: Operadores usados em uma máquina a fluxo de dados com dadoschegando em seus arcos de entrada, e dados saindo após a computação [Teifel& Manohar, 2004].

6.1.2 Operador para controle de Loop

Além dos operadores clássicos de uma máquina a fluxo de dados apre-

sentados anteriormente, para a máquina a fluxo de dados estática do projeto

ChipCflow, foi desenvolvido também um operador para controle de Loops, de

forma a simplificar e diminuir o hardware necessário para a implementação

do comando For. Utilizando os operadores convencionais o controle de um

loop se mostrava bastante complexo, além de consumir uma grande área de

hardware. A diferença entre as implementações do controle de Loops podem

ser vistas na Figura 6.5. Como demonstrado na Figura 6.5, o controle com

60

Page 91: ChipCflow: ferramenta para conversão de código C em uma ...

os operadores convencionais implica na implementação do controle do incre-

mento e também da comparação para término do Loop, além de cópias de

valores simplesmente para que o Loop seja executado um número N de vezes.

No caso do operador For especifico da máquina a fluxo de dados estática do

projeto ChipCflow, o processo de incremento assim como a comparação para

indicar o final do laço são realizadas internamente no operador.

Figura 6.5: (A) Controle de Loops utilizando os operadores convencionais deuma máquina a fluxo de dados. (B) Controle de Loop usando o operador For.

O operador For, como pode ser visto na Figura 6.6, recebe dois valores

principais para iniciar o seu funcionamento, o valor I que indica o contador

inicial do laço e o valor N que indica o numero total de iterações. De posse

destes dois valores o operador pode iniciar seu funcionamento, gerando dois

outros dados, OC que é um valor lógico, que sinaliza True ou False e serve

como sinal de controle para a execução ou não de determinado trecho da

máquina a fluxo de dados, e o valor Si que é o valor atualizado do contador,

caso seja necessário o seu uso na computação dos dados.

Figura 6.6: Operador For.

61

Page 92: ChipCflow: ferramenta para conversão de código C em uma ...

Além das entradas e saídas, citadas anteriormente para o operador for, fez-

se necessário a implementação de mais duas entradas e duas saídas, de 1 bit

cada, visando o aninhamento de dois ou mais operadores.

Para ilustra o problema aqui descrito pode-se analisar o trecho de código

para calculo de tabuada, apresentado na Listagem 2. Neste trecho de código,

após a geração do primeiro valor de i, o for j começa a ser executado, não sendo

possível a geração de um novo valor para i, antes que o for j seja concluído.

Assim que o for j terminar sua execução o controle volta para o for i para que

este gere um novo valor de i e a execução continue.

Código 2: Exemplo de For aninhado.for (i=1; i<=10; i++)

for (j=1; j<=10; j++)

tab=i*j;

Conforme descrito na Figura 6.7, assim que o operador for i gera o primeiro

valor de i, ele entra em modo de espera, e avisa por meio de sua saída FCoutao operador for j que precisa de uma confirmação do consumo do dado. O for j

recebe esta informação por meio de sua entrada FCin e após o termino de sua

execução ele utiliza sua saída FCinAck para informar ao operador for i que

terminou sua execução, liberando o operador e permitindo assim a geração do

proximo valor para i.

Figura 6.7: Exemplo de operadores For aninhados.

Este processo garante que os dados sejam gerados em ordem correta e

mantenham a correta execução, característica fundamental para uma má-

quina a fluxo de dados estática.

Caso estes conjuntos de entradas e saídas (FCInAck, FCIn, FCoutAck e

FCout) não sejam utilizados, as saídas são ligadas a operadores sem função,

para que o valor gerado seja consumido e suas entradas sejam alimentadas

62

Page 93: ChipCflow: ferramenta para conversão de código C em uma ...

com valores padronizados de forma a permitir o correto funcionamento dos

operadores.

6.1.3 Operadores de Memória

Para controle de acesso a memória foram implementados dois operadores

baseados em instruções Load e Store, que são ligados a um operador de con-

trole e acesso a memória. O operador Load tem como função buscar os dados

necessário ao grafo na memória. Para esta função é informado ao operador

qual o endereço de memória no qual o dado pode ser encontrado, e em caso

de vetores ou matrizes, o deslocamento necessário para percorrê-los. Após o

acesso, o operador Load entrega o dado ao operador ligado a ele. O opera-

dor Store tem funcionamento semelhante ao Load, entretanto sua função é

armazenar o dado vindo do grafo na memória. Faz-se necessário informar o

endereço na memória e caso trate-se de um vetor ou matriz, o deslocamento

necessário para o armazenamento do dado enviado pelo operador ligado a ele.

Figura 6.8: Formato dos operadores Load e Store.

6.1.4 Protocolo de comunicação entre operadores

Os operadores controlam o recebimento e envio de tokens por meio de um

protocolo handshake, proposto em [Silva & Marques, 2006]. Neste protocolo

foi definido que para cada entrada ou saída de um operador, como pode ser

visto na Figura 6.9, deve haver um par de sinais (ack e strobe) que são res-

ponsáveis pelo controle de comunicação entre os operadores, realizando a sin-

cronização dos tokens nas entradas e saídas. Assim que o token é enviado

para um operador consumidor, juntamente com ele é enviado, pelo operador

produtor, um sinal de strobe, sinalizando que existe um novo dado no arco.

Após a leitura do dado o sinal de ack é então enviado do operador consumi-

dor para o operador produtor, sinalizando que o dado já foi consumido. Até o

recebimento do sinal de ack, o operador produtor fica impedido de enviar um

63

Page 94: ChipCflow: ferramenta para conversão de código C em uma ...

novo dado para o operador consumidor, permitindo desta forma apenas um

dado por vez em cada arco.

Figura 6.9: Protocolo de Handshake entre operadores.

6.2 Considerações Finais

Neste capítulo foi apresentado o projeto ChipCflow, projeto este no qual se

insere esta proposta de trabalho de doutorado. Foi apresentado o conceito

básico da máquina a fluxo de dados estática e suas características bem como

o desenvolvimento dos operadores necessários para o seu funcionamento. No

próximo capitulo é descrito o conversor de código C para aplicações para a

máquina a fluxo de dados estática do projeto ChipCflow, tratando-se de um

compilador responsável por gerar uma CDFG com os operadores apresentados

neste capítulo.

64

Page 95: ChipCflow: ferramenta para conversão de código C em uma ...

CAPÍTULO

7Conversor de Código C em

Aplicações ChipCflow

A presente tese tem como objetivo o desenvolvimento de uma ferramenta

de geração de código que permita a conversão de aplicativos descritos em lin-

guagem C, para aplicações executáveis, utilizando o modelo de arquitetura a

fluxo de dados estática em FPGA, como parte fundamental do projeto ChipC-

flow. Projeto este que como citado no Capitulo 6, consiste de uma ferramenta

para o desenvolvimento de arquiteturas a fluxo de dados executadas em FPGA,

visando o ganho de desempenho em aplicações, por meio da exploração do

paralelismo implícito, encontrado em código descritos em linguagem C. Além

disso, nesta tese tem-se como objetivo a PROVA DE CONCEITO, sobre a via-

bilidade de conversão de um código em linguagem C para o código VHDL cor-

respondente a uma arquitetura a fluxo de dados estática, a apresentação de

resultados que comprovem a corretude do código gerado, o correto funciona-

mento da arquitetura, e verificar o desempenho apresentado pela arquitetura

desenvolvida.

Na Figura 7.1 pode ser visto o fluxo de funcionamento da ferramenta de-

senvolvida durante o trabalho. Como pode ser visto na Figura 7.1. A partir

do código fonte descrito em linguagem C, é realizada uma análise do código

visando a geração de um código intermediário, a partir deste código inter-

mediário, tem-se a geração de um arquivo com as definições e dados iniciais

da memória da arquitetura. Em paralelo com a geração do arquivo inicial de

memória, tem-se a geração do grafo a fluxo de dados. Este grafo, é então

65

Page 96: ChipCflow: ferramenta para conversão de código C em uma ...

convertido na representação da arquitetura a fluxo de dados estática, em lin-

guagem de descrição de hardware. Este código em linguagem de descrição

de hardware faz referência aos operadores da arquitetura, que necessitam ser

armazenados juntamente com o código gerado.

A seguir será descrita de forma detalhada o processo para conversão de

código C em linguagem VHDL correspondente à arquitetura a fluxo de dados

estática.

Figura 7.1: Fluxo de funcionamento da ferramenta de conversão de código.

7.1 Conversão de Código C

A ferramenta para conversão de código foi divido em um front-end e um

back-end, gerados em linguagem C. O fluxo de funcionamento da ferramenta

esta dividido nas seguintes etapas: leitura das instruções, e quebra destas

instruções em unidades formadas por dois operandos e um operador; gera-

ção de arquivo para carregamento dos dados iniciais na memória; geração de

instruções de três endereços; geração de código intermediário, baseado nas

instruções de três endereços, de acordo com as particularidades dos opera-

dores do projeto ChipCflow e por fim a conversão do código intermediário em

código VHDL compatível com a máquina a fluxo de dados estática.

66

Page 97: ChipCflow: ferramenta para conversão de código C em uma ...

7.1.1 Front-End

O código de entrada é descrito em linguagem C, com restrições, ou seja, um

sub-conjunto da linguagem, descrito na Tabela 7.1. A restrição dos coman-

dos de entrada se deve em parte às características da arquitetura, como por

exemplo, não possibilitar o uso de ponteiros visto que a memória será alocada

sequencialmente, em bancos de memória individuais, para cada vetor ou ma-

triz. Devido a definição de todos os dados de entrada já estarem presentes no

código C, e não sendo possível novas entradas, toda a alocação e leitura de me-

mória é feita pelos operadores load e store, como foi descrito no Capitulo 6. No

caso de laços de repetição e também comandos de decisão, a implementação

foi definida de acordo com os operadores disponíveis no projeto.

Tabela 7.1: Sub-conjunto da linguagem COperações matemáticas +,−, ∗, /, sqrt()Operações booleanas &, |Operação de comparação ==Comandos de decisão if, then, elseLaços de repetição ForComando de atribuição =Tipos de variáveis int, char, int[], char[]

Uma vez definido o sub-conjunto da linguagem a ser utilizado, foi definido

que a código de entrada deveria respeitar a sintaxe exata da linguagem C, de

forma a permitir a migração de aplicações com o mínimo possível de alterações

no código.

O processo de conversão tem inicio com a leitura das instruções e frag-

mentação das mesma, de modo a facilitar o processo de conversão para um

código intermediário. Nesta fase foi utilizada a ferramenta de análise léxica

Flex [Project., 2008], que permite a leitura do código de entrada, separando-

o em tokens, e permitindo uma correta interpretação e tratamento de cada

um dos operadores e operandos existentes. Na Figura 7.2 é apresentado o

diagrama de funcionamento do analisador léxico Flex.

O analisador léxico é responsável pela retirada de espaços em branco e

também comentários existentes no código, além de verificar a corretude do

código, permitindo a criação de rotinas para tratamento de erros.

Para o correto funcionamento do analisador léxico, faz-se necessário a ge-

ração de uma tabela de símbolos, que contém os tokens e lexemas permitidos

na linguagem.

Na Listagem 3 é descrito um trecho da tabela de símbolos utilizada no de-

senvolvimento do gerador de código. Como pode ser visto na Listagem 3, para

67

Page 98: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 7.2: Fluxo de funcionamento do Flex.

cada caractere ou conjunto de caracteres encontrados, que forme um token,

é associado um valor de retorno. Por exemplo, caso o token encontrado pelo

analisador léxico seja o if, esta tabela retornará para a ferramenta de conver-

são o valor seis (6). Com este valor a ferramenta identifica qual a rotina de

tratamento deve ser acionada. Este processo é repetido para todos os tokensencontrados durante o processo de conversão.

A especificação desta tabela de símbolos foi desenvolvida com base no sub-

conjunto da linguagem permitido para uso, ao invés de utilizar a definição

completa da linguagem. Sempre que a compilação de um trecho de código for

realizada, todos os tokens gerados pelo Flex, são validados de acordo com a

tabela de símbolos disponível e um erro será gerado caso algum destes tokensapresente alguma não conformidade.

Código 3: Trecho da tabela de símbolos./*** sessão de regras ***/

main return(888);

[a− zA− Z][a− zA− Z0− 9]* return(2);

if return(6);

else return(7);

for return(9);

pow return(5);

sqrt return(5);

int return(1);

Parte do tratamento dos tokens é verificar o tamanho da expressão e a

declaração de variáveis. Caso seja identificada a declaração de uma variável,

os dados desta declaração, como label da variável e seu valor, além de um

endereço gerado em tempo de compilação, são armazenados em um arquivo

.COE, que é utilizado para o carregamento dos dados iniciais na memória,

68

Page 99: ChipCflow: ferramenta para conversão de código C em uma ...

além de conter configurações de memória. Na Figura 7.3 é apresentado trecho

de arquivo .COE.

Figura 7.3: Exemplo de arquivo .COE.

No trecho do arquivo .COE demonstrado, podemos ver dois parâmetros de

configuração da memória, o parâmetro memory_initialization_radix informa a

forma de armazenamento dos dados, por exemplo em binários ou hexadeci-

mais. O segundo parâmetro, memory_initialization_vector, pode ser utilizado

para informar um vetor de inicialização para a memória, ou seja, criar a me-

mória já carregada com dados.

Este arquivo para geração de memória, é uma das saídas da ferramenta de

geração de código, necessário para a correta implementação da memória da

máquina a fluxo de dados estática.

Em paralelo com a geração das entradas iniciais da memória, as instruções

são verificadas e quebradas em instruções de dois ou três endereços que são

compostas por operações binárias ou unárias e atribuição.

Para este processo, após o recebimento de cada token, a expressão é ana-

lisada e, caso a instrução possua somente três endereços, como por exemplo

a instrução a = b + c, ela á automaticamente convertida para uma instrução

de três endereços (add b,c,a). Caso a expressão possua mais elementos, ela

será montada, como representada na linguagem C. A formatação da expres-

são como esta na linguagem C, é necessária para a avaliação da precedência

de operadores, visando a correta computação da expressão. Após remontada

a expressão é lida e dividida em expressões menores, levando-se em conta a

precedência de operadores.

Para a quebra das instruções, faz-se necessário o uso de operandos tem-

porários, responsáveis por armazenar os valores intermediários da execução

das partes da instrução. Como pode ser visto na Listagem 4, que apresenta

69

Page 100: ChipCflow: ferramenta para conversão de código C em uma ...

um exemplo desta quebra de uma instrução longa em instruções de três en-

dereços, após a computação da multiplicação a ∗ c, o resultado é armazenado

em um operando chamado Temp1. Isso é necessário, para que se possa ar-

mazenar o valor da multiplicação e utilizá-lo em outro trecho de código, neste

exemplo, na linha subsequente (Temp2 = 4 x Temp1).

Código 4: Instrução quebrada em instruções de três endereços.Instrução originald = b2 - 4 * a * c

Instrução quebrada em três endereçosTemp1 = a * c

Temp2 = 4 * Temp1

Temp3 = b * b

d = Temp3 - Temp2

Após a quebra de instruções, como visto na Listagem 4, é gerado o código

intermediário de três endereços. Este formato de representação foi escolhido

por ser um formato conhecido e por facilitar a geração da representação inter-

mediária e sua conversão em operadores da arquitetura a fluxo de dados. O

código intermediário gerado pode ser visto como uma representação linear de

um grafo. Na Listagem 5 é apresentado um trecho do código de três endereços

gerado a partir da quebra da instrução apresentada na Listagem 4.

Código 5: Trecho de instruções de três endereços geradas.mult a, c Temp1

mult 4, Temp1, Temp2

mult b, b, Temp3

sub Temp3, Temp2, d

A representação intermediária em três endereços foi adaptada de forma

a permitir a representação dos operadores da arquitetura a fluxo de dados

estática do projeto ChipCflow, isto porque alguns operadores possuem um

conjunto maior ou menor de entradas e saídas consequentemente mais ou

menos que três endereços. Um exemplo da representação destes operadores

pode ser visto na Listagem 6.

Código 6: Trecho de instruções de três endereços com operadores espe-

cíficos da arquitetura.for i,n;

add first, second, tmp;

atr second, first;

atr tmp, second;

%END FOR;

70

Page 101: ChipCflow: ferramenta para conversão de código C em uma ...

Na Listagem 6 pode-se ver um operador for representado, e suas respec-

tivas entradas, i e n, ou seja o valor inicial do contador e seu valor final,

sendo uma instrução com apenas dois endereços. No caso do operador de atrsomente dois endereços são necessários, por se tratar de uma simples atribui-

ção de valor entre endereços, como por exemplo second = first. Um detalhe na

geração desta representação é a ordem das entradas e saídas dos operadores,

como por exemplo, o operador add possui três endereços representados, os

dois primeiros, first e second, são suas entradas, e tmp sua saída.

Embora na fase inicial os comentários do código sejam retirados, nesta

fase, como pode ser visto na Listagem 6, alguns comentários específicos são

incluídos de forma a apontarem o final de loops e trechos específicos que

podem ou não ser executados, como os presentes em comandos de decisão.

Esta anotação é necessária para a posterior alteração de código, e consequente

geração da máquina a fluxo de dados estática. O ponto-e-vírgula tem como

função indicar o final de cada uma das linhas de instruções, evitando assim a

necessidade de continuar lendo uma linha para a verificação da existência de

novos dados.

Esta representação intermediária gerada, é o ultimo passo do processo de

tradução atribuído ao front-end, e por ser definida em um modelo genérico,

permite o seu reaproveitamento para a geração de código para outras plata-

formas.

7.1.2 Back-End

Para a conversão do código intermediário gerado, foi necessária a definição

de um conjunto de mnemônicos para representar cada um dos operadores.

Na Tabela 7.2 é apresentado o conjunto de mnemônicos e o formato de

cada instrução especifica do ChipCflow.

Tabela 7.2: Representação dos operadores no código intermediárioOperador Mnemônicos FormatoOperator add, sub, mul, div add a,b,cBranch branch branch a,ctrl,b,cMerge merge merge a,b,ctrl,cNon Deterministic Merge Nmerge Nmerge a,b,cCopy copy copy a, a1, a2For for for i,n,ctrl, fi 1

Atr Nmerge atr a,b,cInc add add a,1,bDec sub sub a,1,bNon Operator nop nop a

71

Page 102: ChipCflow: ferramenta para conversão de código C em uma ...

Além das instruções que representam os operadores descritos para a ar-

quitetura a fluxo de dados estática, foram criados mnemônicos para pseudo-

instruções, pois alguns comandos da linguagem C não são representados di-

retamente por operadores, mas podem ser implementados, e tiveram suas

identificações mantidas de forma a permanecerem compativeis com o código

original. Estes comandos são apresentados na Tabela 7.3

Tabela 7.3: Representação das pseudo-instruções no código intermediárioOperador Descrição Mnemônicos FormatoAtr Atribuição de valores (a:=c) Nmerge atr a,nop,cInc Incremento (a++) add add a,1,bDec Decremento (a−−) sub sub a,1,b

Devido a característica de alguns operadores como por exemplo o for, foi

implementado um operador adicional chamado NOP, ou not operator, que tem

como função receber um valor que não será utilizado. Isso é necessário, pois

sempre que um operador dispara, ou seja, envia um valor para sua saída, ele

precisa receber um sinal confirmando o consumo daquele sinal pelo operador

subsequente. No caso do operador for uma de suas saídas é o valor da variável

incrementada. Esta variável, pode ou não ser usada no grafo, dependendo de

sua característica. Caso o dado não seja utilizado ele é ligado a um operador

NOP, de forma que o operador for, receba uma confirmação do recebimento do

dado gerado.

Com a definição do mnemônicos, é realizada a geração do código interme-

diário especifico para a máquina a fluxo de dados estática. Na Listagem 7

pode ser visto um exemplo de código intermediário gerado para a arquitetura.

Código 7: Trecho de instruções de três endereços com operadores espe-

cíficos da arquitetura.for i, n, _L1, if

nop if

copy _L1, _L1, _L1

Nmerge second, fibo, second

Nmerge first, second, first

branch second, _L1, second, second

branch first, _L1, first, first

copy second, second, second

add first, second, tmp

nop first

Nesta fase da representação intermediária, são incluídos todos os operado-

res necessários para que a arquitetura a fluxo de dados estática seja repre-

72

Page 103: ChipCflow: ferramenta para conversão de código C em uma ...

sentada. Como pode ser visto na Listagem 7 foram acrescentadas, ao código

da Listagem 6, as saídas dos operadores, como por exemplo os valores _L1 e

if no operador for, além de operadores para executar o controle do fluxo dos

dados no grafo, como por exemplo as cópias do valores de controle (copy) e

operadores branch, responsáveis por verificar se os dados continuam no loop,

ou se o loop chegou ao fim. Os comandos de atribuição (atr) foram substituí-

dos por operadores Nmerge, que repassam para suas saídas o o primeiro valor

a chegar em suas entradas.

Os operadores de acesso a memória load ou store, são incluídos pelo com-

pilador, mas sua ligação com o operador de gerenciamento de memória é feita

direto no código VHDL.

A inclusão do operador non deterministic merge é necessária sempre que

temos a reentrada de um dado, ou seja, sua reutilização em um grafo. Como

exemplo podemos citar o uso de uma variável para acumular um valor durante

a execução de um laço de repetição. Esta variável inicia o laço com o valor zero

e a cada iteração do laço seu valor é alterado, sendo somada a outro valor, o

que significa que esta variável é reentrante no grafo, e necessita ser mantida

durante toda a execução do grafo. Um exemplo da utilização destes operadores

pode ser visto na Figura 7.4

Figura 7.4: Exemplo de uso dos operadores branch e merge.

O operador branch, controla a execução das instruções contidas no laço,

ou então a saída do laço, e é utilizado como um jump dentro do grafo. Sempre

que o operador for incrementar o valor de sua variável de controle, o operador

branch recebe um sinal em sua entrada de controle, informando se o loop será

executado novamente ou não, como base nesta informação, o operador define

para qual de suas saídas enviará o dado que chegar a sua entrada.

73

Page 104: ChipCflow: ferramenta para conversão de código C em uma ...

Estes operadores são incluídos de acordo com as anotações feitas no có-

digo, quando a representação intermediária é gerada pelo front-end, removendo-

se assim o comentário incluído. Esta anotação permite por exemplo verificar

o final de um laço ou comando de decisão e também definir a existência de

comandos aninhados.

Com a inclusão destes comandos, o código intermediário especifico já re-

presenta uma máquina a fluxo de dados estática do projeto ChipCflow, mas

antes da conversão faz-se necessário renomear variáveis que aparecem várias

vezes dentro do grafo.

Isso se deve ao fato de que na arquitetura a fluxo de dados, os nomes das

variáveis não representam endereços de memória, como em uma arquitetura

convencional, este nomes representam ligações entre operadores que devem

ter nomes únicos dentro do circuito gerado.

Para este processo foi gerado um algoritmo que lê o código existente e com

base no nome de cada variável armazenada na memória, realiza a substituição

nos nomes quando encontrados no grafo. Na primeira ocorrência do nome, o

mesmo será mantido, mas assim que for encontrado novamente, o nome será

alterado. Um modelo desta alteração pode ser visto na Listagem 8.

Código 8: Trecho de código intermediário com a substituição de nomes

das variáveis.add i,1,_i1

add first, second, tmp

atr _second1, NOP, _first1

atr _tmp1, NOP, _second2

O algoritmo após ler a variável pela primeira vez, sai percorrendo o código

e a cada vez que encontra o mesmo valor, altera a variável incluindo um un-derline (_) no inicio de seu nome e um contador sequencial no final, de forma

a distingui-los. A inclusão do caractere underline (_) no inicio, visa evitar que

o processo de troca dos nomes gere algum nome já existente no código. Além

de renomear, este algoritmo também verifica todas as arestas soltas no grafo.

Cada aresta solta deve ser ligada a um operador de store para que tenha o

seu valor armazenado na memória. Este processo tem que ser realizado, pois

como citado anteriormente, um sinal sem resposta faz com que um operador

trave e toda a execução pare por falta de dados. Este tipo de ligação só é re-

alizado caso o valor em questão faça parte das variáveis declaradas no código

entrada, caso isso não ocorra, este sinal é conectado a um operador NOP.

Para renomear as variáveis o algoritmo utiliza uma lista encadeada que

armazena os dados necessários para encontrar e gerar os novos nomes, como

o nome da variável original e um contador para verificar quantas vezes ela já

74

Page 105: ChipCflow: ferramenta para conversão de código C em uma ...

ocorreu no código. Este processo gera o novo código também armazenado em

um lista, que posteriormente é passada para um arquivo de texto que constitui

a segunda saída da ferramenta de geração de código. Este arquivo foi gerado

visando verificar a corretude das instruções geradas.

O próximo passo do processo é a geração de um CDFG, que é uma repre-

sentação do grafo a fluxo de dados. Esta representação já pode ser vista como

o arcos, que representam as trocas de dados entre estes nós.

Com o CDFG gerado de forma correta, o processo de tradução do grafo para

o código VHDL é iniciado. A primeira etapa neste processo é a geração de um

arquivo VHDL com o cabeçalho indicando o nome do arquivo e carregando

as bibliotecas necessárias que correspondem aos operadores já implementa-

dos em VHDL e que serão utilizados no CDFG. Em uma segunda etapa do

processo, uma entidade (Entity) é gerada, bem como seus sinais com seus res-

pectivos tamanhos em bits. Estes sinais podem ser vistos como as variáveis

existentes no código, além dos sinais de ACK e Strobe gerados para controle

dos operadores. Esta é a parte principal do arquivo VHDL, por definir as in-

terfaces com as entradas e saídas da máquina a fluxo de dados estática que

esta sendo gerada.

A terceira etapa do processo consiste em gerar o corpo da máquina a fluxo

de dados estática, ou seja, sua arquitetura (Architecture), onde serão defini-

dos todos os operadores que serão utilizados, bem como a interconexão entre

eles. A descrição de todos os operadores já está definida na ferramenta de

conversão de código e são carregadas após a leitura do mnemônico referente

a cada operador. Este modelo permite a utilização de um banco de dados de

operadores, facilitando o uso e a geração do código.

Ainda nesta terceira etapa do processo, é necessário gerar todos os sinais

que interligaram os operadores. Além dos sinais de entrada e saída de da-

dos, são definidos os sinais de controle (ACK e Strobe) e os sinais de (Clock e

Reset), que são considerados entradas na definição da entidade, citada ante-

riormente, e são utilizados por todos os operadores.

Nas definições dos sinais não pode haver nomes repetidos, por este motivo,

é necessário submeter o código ao processo de renomear variáveis.

Nesta fase cada um dos nomes de operadores definidos durante o processo

de geração do código, é identificado e tem o seu operador correspondente atri-

buído a ele. A definição de todas as suas interfaces é o que define a quem

cada um dos operadores esta conectado, por este motivo é muito importante

que a fase de verificação de todas as entradas e saídas geradas no código in-

termediário seja feita de forma correta. Um exemplo de código VHDL gerado

pode ser visto no Apêndice A.

75

Page 106: ChipCflow: ferramenta para conversão de código C em uma ...

7.2 Considerações Finais

Neste Capítulo foi apresentada a ferramenta de conversão de código de-

senvolvida, suas particularidades e suas limitações iniciais. Esta ferramenta,

parte fundamental do projeto ChipCflow, é o resultado deste trabalho. Neste

Capítulo foi descrito todo o processo necessário para que o código de entrada,

em linguagem de alto nível, seja convertido em uma linguagem de descrição

de hardware, representando uma arquitetura a fluxo de dados estática.

No próximo Capítulo serão apresentados os resultados da prova de conceito

para a geração da arquitetura a fluxo de dados estática do projeto ChipCflow.

76

Page 107: ChipCflow: ferramenta para conversão de código C em uma ...

CAPÍTULO

8Resultados

Neste capítulo são apresentados teste realizados para a conversão de código

em linguagem C diretamente para VHDL. Este testes foram utilizados como

prova de conceito na geração do código equivalente a arquitetura a fluxo de

dados proposta.

Além disso, foi realizada uma análise de desempenho dos resultados obti-

dos com outras plataformas.

8.1 Testes Realizados

Para efeito de teste, foi realizada a conversão de três algoritmos, para a

arquitetura a fluxo de dados proposta, são eles: Fibonacci, Fatorial e Deter-

minante de uma matriz quadrada.

Os algoritmos convertidos para uma máquina a fluxo de dados estática, fo-

ram sintetizados e simulados em um FPGA da família Virtex 7 (7V285TFFG11

57-3), usando a ferramenta ISE 13.2 (Xilinx [2014]), comprovando o correto

funcionamento dos algoritmos, segundo a simulação em hardware.

8.1.1 Algoritmo de Fibonacci

O primeiro teste realizado foi a implementação do algoritmo de Fibonacci,

que apresenta uma dependência de dois níveis entre as iterações. O código

77

Page 108: ChipCflow: ferramenta para conversão de código C em uma ...

fonte do algoritmo utilizado pode ser visto na Listagem 9.

Código 9: Exemplo de Fibonacci descrito em Linguagem C.#define N 10000

int fibonacci(){

int i;

int second=1;

int first=0;

int fibo;

for (i=0; i<N; i++){

fibo=first+second;

second=first;

first=fibo;}

return fibo;

}

Após a execução do código em linguagem C, descrito na Listagem 9, que

comprova os resultados esperados para o algoritmo de Fibonacci, utilizou-se

a ferramenta de conversão de código, gerando assim o código intermediário, já

com as alterações necessárias para a implementação da máquina a fluxo de

dados estática do projeto ChipCflow. Este código pode ser visto na Listagem

10.

Código 10: Código intermediário gerado para o algoritmo de Fibonacci.for i, n, _L1, ifnop ifcopy _L1, _L11, _L12Nmerge second, fibo, _second1Nmerge first, _second4, _first1branch _second1, _L11, _second2, _second3branch _first1, _L12, _first2, _first3copy _second2, _second4, _second5add _first3, _second5, fibonop _first2

Antes da geração do código, uma análise de todas as operações a serem

executadas dentro do loop é realizada. Com base nesta análise os operadores

necessários para a correta execução são criados. Os operadores são ordenados

dentro do código intermediário buscando facilitar a interpretação do código

gerado.

Esta representação intermediária, está pronta para ser convertida no có-

digo VHDL correspondente ao grafo a fluxo de dados. Este grafo pode ser visto

na Figura 8.1.

78

Page 109: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 8.1: Grafo gerado para o algoritmo de Fibonacci.

O ultimo passo é a geração do código VHDL. Neste código cada um dos

operadores é instanciado, com o seu nome definido no VHDL component e

também diferenciado, caso o mesmo operador apareça mais de uma vez no

código, com um numerador sequencial, como acontece no caso de renomear as

variáveis. O código VHDL completo correspondente ao algoritmo de Fibonacci,

implementado no modelo a fluxo de dados estático do projeto ChipCflow, pode

ser visto no Anexo A.

Na Figura 8.2 é descrito uma parte do código do algoritmo de Fibonacci

convertido para VHDL.

Como pode ser visto na Figura 8.2, a entidade (Entity) Fibo possui todas

as entradas e saídas necessárias para a implementação da máquina a fluxo

de dados estática, que corresponde ao algoritmo de Fibonacci, inicialmente

descrita em linguagem C. Como também pode ser visto no final da Figura 8.2,

o componente fori, foi instanciado como fori0. Caso outro componente forifosse utilizado ele seria nomeado como fori1 e assim sucessivamente.

O código gerado foi sintetizado no ambiente ISE obtendo-se os dados apre-

sentados na Tabela 8.1.

Tabela 8.1: Resultados obtidos após a síntese do algoritmo de Fibonacci nomodelo da máquina a fluxo de dados do projeto ChipCflow.

FibonacciFF 143LUT 416Slices 197Frequência 556,514 MHz

79

Page 110: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 8.2: Trecho do código VHDL gerado para o algoritmo de Fibonacci.

8.1.2 Algoritmo de Fatorial

Para efeito de teste o mesmo processo foi realizado com o algoritmo para

calculo de Fatorial. O código correspondente ao algoritmo implementado pode

80

Page 111: ChipCflow: ferramenta para conversão de código C em uma ...

ser visto na Listagem 11.

Código 11: Exemplo de Fatorial descrito em Linguagem C.#define N 5

int fat(){

int fatorial=N;

int i;

int tmp;

for(i = 1 ; i < N; i++){

tmp = fatorial * i;

fatorial = tmp;

}

Da mesma forma que o algoritmo de Fibonacci, o código em linguagem C do

algoritmo de Fatorial, descrito na Listagem 11, foi executado para comprovar

seu funcionamento. Em seguida utilizou-se a ferramenta de conversão de

código, gerando o código intermediário para a implementação da máquina a

fluxo de dados estática do projeto ChipCflow. Este código pode ser visto na

Listagem 12.

Código 12: Código intermediário gerado para o algoritmo de Fatorial.for i, n, _L1, ifNmerge fatorial, tmp, _fatorial1branch _fatorial1, _fatorial2, _fatorial3add if, _fatorial3, tmp

Após a representação intermediária, foi gerado o grafo referente ao algo-

ritmo de Fatorial, como pode ser visto na Figura 8.3.

Figura 8.3: Grafo gerado para o algoritmo de Fatorial.

O código gerado foi sintetizado no ambiente ISE obtendo-se os dados apre-

sentados na Tabela 8.2.

81

Page 112: ChipCflow: ferramenta para conversão de código C em uma ...

Tabela 8.2: Resultados obtidos após a síntese do algoritmo de fatorial noChipCflow.

FatorialFF 88LUT 425Slices 363Frequência 247,537 MHz

Cabe observar que a frequencia máxima da execução do algoritmo de Fa-

torial é praticamente a metade da encontrada na execução do algoritmo de

Fibonacci. Isso se deve ao fato que o algoritmo de Fatorial utiliza um operador

de multiplicação, que atrasa a execução do algoritmo.

8.1.3 Algoritmo Determinante

O ultimo algoritmo implementado para teste foi o algoritmo de Determi-

nante, que permitiu verificar o funcionamento do operador for, após as altera-

ções implementadas neste operador de forma a permitir a implementação de

for aninhado.

Para a execução do algoritmo de determinante, fez-se necessária a criação

de uma memória, direto na ambiente ISE, que armazenasse uma matriz, de

forma a permitir o acesso aos seus dados, assim tanto a memória como os

operadores de acesso a memória foram incluídos manualmente no arquivo

VHDL. Um trecho do algoritmo implementado em linguagem C para calculo

do Determinante pode ser visto na Listagem 8.3.

O grafo gerado para este algoritmo, já com o operador de load para a matriz,

bem como os operadores for aninhados, podem ser vistos na Figura 8.4.

Os resultados da síntese desta implementação no ambiente ISE, podem ser

vistos na Tabela 8.3.

De uma maneira geral, pode-se concluir que, em termos de ocupação do

FPGA, os três algoritmos ocuparam uma porcentagem inferior a 1% de slices,

LUTs e flip-flops do FPGA utilizado. Esta informação demonstra a viabilidade

de implementação de algoritmos complexos, no que diz respeito a área de

FPGA ocupada.

8.2 Análise Comparativa

O objetivo geral deste trabalho é realizar a prova de conceito sobre a viabili-

dade da geração de uma arquitetura a fluxo de dados estática, por meio de um

82

Page 113: ChipCflow: ferramenta para conversão de código C em uma ...

Código 13: Trecho do código do algoritmo de determinante implemen-tado.

for (i=0; i<n; i++) {y=i;for (j=0; j<n; j++){

if(y==n)y=0;

mult=mult * mat [j] [y];y++;

}soma1=soma1+mult;mult=1;}for (i=0; i<n; i++){

y=i;for (j=n-1; j>=0; j–) {

if (y==n)y=0;

mult=mult * [j] [y];y=y+1;

}soma2=soma2+mult;mult=1; }

det=soma1-soma2;

Tabela 8.3: Resultados obtidos após a síntese do algoritmo de determinanteno ChipCflow.

DeterminanteFF 223LUT 272Slices 597Frequência 313,7 MHz

83

Page 114: ChipCflow: ferramenta para conversão de código C em uma ...

Figura 8.4: Grafo gerado para o algoritmo de determinante.

código de entrada em linguagem C, o que foi demonstrado com os resultados

anteriores.

Além da prova de conceito, foi realizada uma análise do desempenho da

arquitetura a fluxo de dados estática do projeto do ChipCflow quando compa-

rado a uma Central Processing Unit (CPU) Core I7 e uma Graphics ProcessingUnit (GPU) NVidia GForce GTX-660.

Para esta comparação os 3 algoritmos foram implementados em linguagem

C, para a máquina a fluxo de dados estática do projeto ChipCflow e para a

CPU Core I7, e em CUDA no caso da GPU.

A CPU utilizada, para a execução dos algoritmos, foi um I7-3820 de 3,60

GHz com 4 núcleos e 8 threads, e 10 MB de memória cache. O sistema opera-

cional utilizado foi o Ubuntu Linux versão 13.4, rodando somente em terminal

sem interface gráfica, memória RAM de 8GB DDR3, e armazenamento externo

em um Solid-State Drive (SSD) de 128GB

A GPU Nvidia GForce GTX-660 utilizada, conta com 960 núcleos de pro-

cessamento com velocidade de 1020 MHz, 2 GB de memória DDR5, com 6008

MHz e 192 Bits. Para a execução dos teste foi utilizado um computador Core

I5, rodando o sistema operacional Ubuntu Linux. A analise dos dados foi

realizada utilizando a ferramenta Visual Profiler da NVidia.

Em todos os testes, os algoritmos foram executados 30 vezes e seus tempos

armazenados para posterior calculo da média. Para avaliar o tempo de execu-

ção na CPU foi utilizado o GNUProf. Na GPU o Visual Profiler é o responsável

pela execução e calculo da média de tempo, e na arquitetura a fluxo de dados

84

Page 115: ChipCflow: ferramenta para conversão de código C em uma ...

os tempos foram obtidos a ferramenta ISE.

A implementação dos algoritmos para a arquitetura a fluxo de dados está-

tica do projeto ChipCflow e para a CPU foi exatamente a mesma. No caso do

algoritmo de determinante no código executado na CPU, a matriz foi iniciali-

zada com os valores dentro do código C, de forma a evitar a necessidade de

leitura de dados externos.

O código C foi alterado de forma a conter as diretivas do CUDA, para a

correta execução na GPU.

Os algoritmos de Fibonacci e Fatorial foram executados na CPU e na GPU

com iterações 100 mil, 300 mil e 500 mil.

Os resultados da execução do algoritmo de Fibonacci pode ser visto na

Tabela 8.4 [Silva & Silva, 2014].

Tabela 8.4: Resultados das execuções do algoritmo de Fibonacci.Fibonacci I7 GPU ChipCflow100000 2 ms 0,27 ms 1,28 ms300000 4 ms 1,06 ms 3,85 ms500000 6,1 ms 1,82 ms 5,43 ms

Na Figura 8.5 é apresentado o gráfico comparativo dos resultados obtidos

na execução do algoritmo de Fibonacci.

Figura 8.5: Resultados das execuções do algoritmo de Fibonacci [Silva & Silva,2014] .

Como pode ser visto na Figura 8.5, para a implementação do algoritmo

de Fibonacci, a execução na máquina a fluxo de dados estática do projeto

85

Page 116: ChipCflow: ferramenta para conversão de código C em uma ...

ChipCflow foi em média aproximadamente 16% mais rápida do que a execução

na CPU, porém foi em média aproximadamente 71% mais lenta do que a GPU.

O segundo algoritmo analisado foi o fatorial, o resultado das execuções

deste algoritmos podem ser vistos na Tabela 8.5 [Silva & Silva, 2014].

Tabela 8.5: Resultados das execuções do algoritmo de para calculo Fatorial.Fatorial I7 GPU ChipCflow100000 2,1 ms 0,42 ms 1,02 ms300000 5 ms 1,29 ms 3,07 ms500000 6,92 ms 2,16 ms 5,12 ms

Na Figura 8.6 é apresentado o gráfico comparativo dos resultados obtidos

na execução do algoritmo para calculo de fatorial.

Figura 8.6: Resultados das execuções do algoritmo de para calculo Fatorial[Silva & Silva, 2014] .

Como pode ser visto na Figura 8.6, para a implementação do algoritmo de

Fatorial, a execução na máquina a fluxo de dados estática do projeto ChipC-

flow foi em média aproximadamente 37% mais rápida do que a execução na

CPU, porém foi em média aproximadamente 57% mais lenta do que a GPU.

Este fato se deve a dificuldade e o crescimento do hardware necessário

para manipular as atribuições de valores, existentes no cálculo de Fibonacci.

O ultimo algoritmo analisado foi o determinante, o resultado das execuções

deste algoritmos podem ser vistos na Tabela 8.6.

Na Figura 8.7 é apresentado o gráfico comparativo dos resultados obtidos

na execução do algoritmo para calculo de determinante.

86

Page 117: ChipCflow: ferramenta para conversão de código C em uma ...

Tabela 8.6: Resultados das execuções do algoritmo de para calculo de Deter-minante.

Determinante I7 GPU ChipCflow20x20 5,3 ms 0,82 ms 2,7 ms100x100 24,5 ms 5,9 ms 13,4 ms200x200 59,6 ms 12,2 ms 28,7 ms

Figura 8.7: Resultados das execuções do algoritmo de para calculo determi-nante.

O resultado da execução do algoritmo de determinante, assim como os ou-

tros algoritmos, teve a melhor execução na GPU. Esta eficiência de execução

da GPU já era esperada nos resultados devido ao alto poder de processamento

apresentando. Neste algoritmo a execução na máquina a fluxo de dados está-

tica do projeto ChipCflow foi em média aproximadamente 50% mais rápida do

que a CPU, e em média aproximadamente 67% mais lenta do que a GPU.

87

Page 118: ChipCflow: ferramenta para conversão de código C em uma ...

88

Page 119: ChipCflow: ferramenta para conversão de código C em uma ...

CAPÍTULO

9Conclusões e Trabalhos Futuros

O principal objetivo nesta tese foi implementar uma ferramenta para co-

dificar códigos escritos em linguagem C para uma linguagem de descrição

de hardware (em particular o VHDL) que utilizasse o modelo a fluxo de da-

dos estáticos como parte fundamental do projeto ChipCflow. Com o projeto

ChipCflow pretende-se gerar um ambiente de desenvolvimento para acelerar

aplicações em um modelo híbrido de funcionamento caracterizado pelo uso de

CPUs interconectados a FPGAs. O Ambiente ChipCflow integra um editor, o

compilador, foco desta tese em questão, a síntese lógica do hardware gerado

em ambiente comercial como ferramentas Eletronic Design Automation (EDA)

Xilinx e Altera, simulação e geração final do arquivo de configuração do FPGA

e partição Hardware/Software da aplicação. Nesta tese portanto foram apre-

sentadas inicialmente os aspectos relacionados ao conceito de Computação

Reconfigurável, particularmente envolvidos com o uso de dispositivos FPGAs,

suas características e potencialidades. Em seguida, ainda em se tratando de

Computação Reconfigurável, uma das formas que vem sendo utilizadas para

acelerar processamento é o uso das tradicionais CPUs associadas aos FPGAs

em um modelo híbrido de funcionamento, ficando para os FPGAs o trabalho

mais intenso de processamento como loops. Com essa característica híbrida

de trabalho, foram apresentadas as primeiras máquinas desenvolvidas com

o conceito de Computação Reconfigurável, em particular as máquinas GARP

[Hauser & Wawrzynek, 1997] e DISC [Wirthlin & Hutchings, 1995], e finali-

zando este item, foram apresentadas as linguagens necessárias para o desen-

volvimento desses sistemas, destacando-se a linguagem VHDL e linguagem

89

Page 120: ChipCflow: ferramenta para conversão de código C em uma ...

Verilog. Em seguida foram apresentados os conceitos envolvidos para a ge-

ração de um Compilador, inicialmente de forma geral, suas fases, a geração

de código intermediário, a fase de síntese, finalizando com o código gerado. A

partir dessas características, foram apresentados os aspectos ligados a Com-

pilador para geração de código em linguagem de hardware, com um objetivo

principal de, a partir de uma linguagem de alto nível como a Linguagem C,

poder gerar código para descrição de hardware como a Linguagem VHDL. Ar-

quiteturas a Fluxo de Dados teve seu início nos anos 70 e descontinuado

no final dos anos 90, mas tornou-se atrativo novamente em função da evo-

lução do hardware, tanto no que diz respeito aos FPGAs como multicores e

as GPUs. Em particular no capítulo 4 desta tese, foi apresentado o modelo

a fluxo de dados, a linguagem gráfica utilizada neste modelo, as primeiras

máquinas implementadas utilizando este modelo, em particular o modelo a

fluxo de dados estático proposto por Dennis & Misunas [1975], a máquina

também estática de Manchester [Gurd et al., 1985], a máquina dinâmica do

MIT [Arvind & Nikhil, 1990] e máquinas contemporâneas como a máquina

dinâmica Wavescalar [Swanson et al., 2007] e a máquina TRIPS [Sankaralin-

gam et al., 2003]. Com o foco em compiladores voltados para aplicações em

hardware, no capítulo 5 desta tese foram apresentados os principais proje-

tos relacionados com a proposta para o projeto ChipCflow. Assim foi descrito

o compilador Trident [Tripp et al., 2007] que gera código VHDL a partir da

linguagem C, o compilador Molen [Panainte et al., 2007] utilizado em uma ar-

quitetura específica que faz uso do modelo híbrido CPU e FPGA, o compilador

HThreads [Andrews et al., 2008b] que gera uma representação intermediária

para o código VHDL a partir da linguagem C, o compilador ARISE [Vassilia-

dis et al., 2009] que também faz uma divisão hardware e software no modelo

híbrido CPU e FPGA, o compilador Nenya/Galadriel [Cardoso, 2000] que gera

código VHDL a partir de bytecodes da linguagem Java e finalmente o compi-

lador ROCCC [Buyukkurt et al., 2006] que também gera código VHDL a partir

de linguagens como C/C++, Fortran e Java. Detalhes do modelo a fluxo de

dados do projeto ChipCflow foi apresentado no capítulo 6 desta tese. Inicial-

mente foi apresentado uma visão geral do projeto ChipCflow para em seguida

ser descrito em detalhes os operadores utilizados no projeto ChipCflow, no

caso, operadores aritméticos e lógicos, operadores condicionais, operadores

de acesso à memória e operadores controladores de loops. Como parte inte-

grante do modelo a fluxo de dados estático descrito neste item foi descrito o

protocolo de comunicação entre os operadores, sem o qual não seria possível

configurar a máquina a fluxo de dados como uma máquina estática. Uma vez

descrito toda a configuração do hardware, passou-se à descrição do conversor

90

Page 121: ChipCflow: ferramenta para conversão de código C em uma ...

de código C para a linguagem VHDL que geraria a máquina a fluxo de da-

dos estática do projeto Chipcflow, sendo este o principal capítulo desta tese,

no caso o capítulo 7. Inicialmente foi descrito a primeira parte do compila-

dor, o Front-End baseado na ferramenta de análise léxica Flex [Project., 2008]

gerando uma representação intermediária no modelo de instruções com três

endereços, representação essa que pode ser utilizada inclusive para outras

plataformas que não a proposta no projeto ChipCflow. Em seguia, passou-se

a implementação do Back-End, tendo como base a representação intermediá-

ria gerada pelo Front-End e os operadores de referência já implementados no

projeto ChipCflow. Uma vez especificado o Back-End, alguns algoritmos fo-

ram implementados e foram descritos em detalhes no capítulo 8. No capítulo

8 portanto, são apresentados os resultados de conversão de código e imple-

mentação dos algoritmos: Fatorial, Fibonacci e cálculo do Determinante de

Matriz Quadrada, sendo em seguida sintetizados em ferramentas EDA Xilinx,

para o FPGA Virtex 7 modelo (7V285TFFG1157-3). Para cada algoritmo são

descrito: o código C original, o código intermediário gerado para aquele algo-

ritmo, uma representação gráfica no modelo a fluxo de dados estático para

cada algoritmo e apenas para o algoritmo Fibonacci o código completo gerado

em VHDL no anexo A da tese. Também são apresentados neste capítulo os

resultados de síntese dos algoritmos no referido FPGA, com tabelas contendo

ocupação para cada algoritmo e a frequência máxima utilizada para executar

aquele algoritmo naquele FPGA. A partir desses dados, e utilizando ferramen-

tas apropriadas, foram feitas análises de desempenho dos resultados obtidos

na execução dos algoritmos em FPGA com os mesmos algoritmos executados

em uma CPU Core I7 e uma GPU NVIDIA GForce GTX-660. Os resultados

mostram um ganho em desempenho do modelo a fluxo de dados estático se

comparado à CPU, que varia de 16% a 50%, dependendo das características

de cada algoritmo.

9.1 Trabalhos Futuros

A ferramenta desenvolvida durante este trabalho permite o seu aprimora-

mento e o desenvolvimento e implementação de novos recursos na arquitetura

proposta. A seguir são relacionadas algumas sugestões para trabalhos futuros

que podem aprimorar a ferramenta de conversão e também a arquitetura.

• Inclusão na ferramenta de compilação de outras estruturas de repetição

e decisões, visando permitir a conversão de uma variedade maior de al-

goritmos sem a necessidade de alteração em seus códigos. Esta inclusão

91

Page 122: ChipCflow: ferramenta para conversão de código C em uma ...

permitiria atingir uma gama maior de aplicações e facilitaria o desenvol-

vimento de novas aplicações.

• Inclusão de operadores de load e store visando o acesso a matrizes e ve-

tores, de acordo com a arquitetura de memória que esta sendo desenvol-

vida e permitindo o desenvolvimento de aplicações distribuídas em mais

de um FPGA. O uso de uma arquitetura desenvolvida e sendo executada

em vários FPGAs permitiria o desenvolvimento de aplicações complexas,

além de permitir testar técnicas como loop Unrolling.

• Realizar pesquisa sobre a organização de memória, que pode ser realizada

no processo de compilação da aplicação. A organização de memória, caso

viável, permite diminuir a quantidade de hardware e com isso o tempo

de execução de aplicações. Esta é uma proposta de trabalho que permite

um amplo campo de pesquisa, pois além de verificar a viabilidade desta

organização, faz-se necessário uma ferramenta de conversão que altere

o código gerado, de forma a eliminar loops que servem para acesso de

dados de forma não sequencial em vetores ou matrizes.

• Desenvolvimento de uma ferramenta visual que permita ao programador

visualizar, ou gerar um arquitetura a fluxo de dados utilizando uma fer-

ramenta visual. Esta ferramenta permitiria ao programador visualizar e

alterar o grafo gerado pelo conversor de código visando a correção de er-

ros ou otimizações. Além de permitir a visualização de um grafo existente

também permitiria a geração de uma arquitetura por meio de um grafo

desenhado pelo programador. Esta ferramenta encontra-se em estagio

inicial de pesquisa para seu desenvolvimento, prevendo-se um protótipo

funcional para o inicio de 2015.

• Pesquisar a paralelização de grafos de forma a buscar desempenho se-

melhante ao obtido com o uso de GPUs.

92

Page 123: ChipCflow: ferramenta para conversão de código C em uma ...

Referências Bibliográficas

Aho, A. V., Lam, M. S., Sethi, R., & Ullman, J. D. (2007). Compilers: Princi-ples, Techniques, and Tools. Reading, Massachusetts: Addison-Wesley, 2st

edition. xiii, xvii, xix, 15, 16, 17, 18, 19, 20, 21

Andrews, D. L., Sass, R., Anderson, E., Agron, J., Peck, W., Stevens, J., Bai-

jot, F., & Komp, E. (2008a). Achieving programming model abstractions for

reconfigurable computing. IEEE Trans. VLSI Syst, 16(1), 34–44. xiv, 48

Andrews, D. L., Sass, R., Anderson, E., Agron, J., Peck, W., Stevens, J., Baijot,

F., & Komp, E. (2008b). Achieving programming model abstractions for

reconfigurable computing. IEEE Trans. VLSI Syst, 16(1). 90

Anellal, S. & Kaminska, B. (1993). Scheduling of a control and data flow graph.

IEEE International Symposium on Circuits and Systems, 3, 1666–1669. 23

Arvind, K. (2005). Dataflow: Passing the token. xiv, 37, 58, 59

Arvind, K. & Nikhil, R. S. (1990). Executing a program on the mit tagged-token

dataflow architecture. IEEE Trans. Comput., 39, 300–318. xiv, 36, 37, 38,

39, 90

Bobda, C. (2007). Introduction to Reconfigurable Computing: Architectures, Al-gorithms, and Applications. Springer Publishing Company, Incorporated.

xiii, 7, 8

Buyukkurt, B., Guo, Z., & Najjar, W. A. (2006). Impact of loop unrolling on

area, throughput and clock frequency in ROCCC: C to VHDL compiler for

FPGAs. In K. Bertels, J. M. P. Cardoso, & S. Vassiliadis (Eds.), ARC, volume

3985 of Lecture Notes in Computer Science (pp. 401–412).: Springer. xiv, 53,

90

93

Page 124: ChipCflow: ferramenta para conversão de código C em uma ...

Cai, Q. & Xue, J. (2003). Optimal and efficient speculation-based partial re-

dundancy elimination. In CGO (pp. 91–104).: IEEE Computer Society. 47

Cappelli, A., Lodi, A., Mucci, C., Toma, M., & Campi, F. (2004). A dataflow con-

trol unit for c-to-configurable pipelines compilation flow. In Proceedings ofthe 12th Annual IEEE Symposium on Field-Programmable Custom ComputingMachines (pp. 332–333). Washington, DC, USA: IEEE Computer Society. 25

Cardoso, J. M. P. e Neto, H. (2003). Compilation for FPGA-based reconfigura-

ble hardware. IEEE Design & Test of Computers, 20(2), 65–75. 1, 20

Cardoso, J. a. M. P. & Dehon, A. (2010). Compiling for Reconfigurable Com-

puting: A Survey . ACM Computing Surveys, 42(4). xiii, 7

Cardoso, J. M. P. (2000). Compilação de algoritmos em Java para sistemascomputacionais reconfiguráveis com exploração do paralelismo ao nível deoperações. PhD thesis, Universidade Técnica de Lisboa. xiv, 1, 22, 50, 52,

90

Cardoso, J. M. P. & Neto, H. C. (2001). Compilation increasing the scheduling

scope for multi-memory-FPGA-based custom computing machines. LectureNotes in Computer Science, 2147. 52

Davis, A. L. (1978). The architecture and system method of ddm1: A recur-

sively structured data driven machine. In Proceedings of the 5th annualsymposium on Computer architecture, ISCA ’78 (pp. 210–215). New York, NY,

USA: ACM. 30

Dennis, J. B. (1974). First version of a data flow procedure language. In

Programming Symposium, Proceedings Colloque Sur La Programmation (pp.

362–376). London, UK, UK: Springer-Verlag. 25

Dennis, J. B. (1980). Data flow supercomputers. Computer, 13, 48–56. xiv, 33

Dennis, J. B. & Misunas, D. P. (1975). A preliminary architecture for a ba-

sic data-flow processor. In In Proceedings of the 2nd Annual Symposium onComputer Architecture (pp. 126–132). xiv, 25, 26, 30, 32, 33, 34, 36, 43, 58,

90

Duarte, F. L. (2006). Phoenix – um framework para trabalhos em síntese de

alto nível de circuitos digitais. xiii, 22

Estrin, G., Bussel, B., Turn, R., & Bibb, J. (1963). Parallel processing on a

restructurable computer system. IEEE Trans. Electronic Computers, EC-12,

747–754. 5, 6

94

Page 125: ChipCflow: ferramenta para conversão de código C em uma ...

Girkar, M. & Polychronopoulos, C. D. (1992). Automatic extraction of functio-

nal parallelism from ordinary programs. IEEE Trans. Parallel Distrib. Syst.,3(2), 166–178. 23

Grafe, V. G., Davidson, G. S., Hoch, J. E., & Holmes, V. P. (1989). The epsilon

dataflow processor. In Proceedings of the 16th annual international sympo-sium on Computer architecture, ISCA ’89 (pp. 36–45). New York, NY, USA:

ACM. 30

Guo, Z., Buyukkurt, B., Najjar, W., & Vissers, K. (2005). Optimized generation

of data-path from c codes for fpgas. 53

Gupta, S., Gupta, R., Dutt, N., & Nicolau, A. (2004). SPARK: A ParallelizingApproach to the High-Level Synthesis of Digital Circuits. Norwell, Massachu-

setts, USA: Kluwer Academic Publishers. xiv, 54, 55

Gurd, J. R., Kirkham, C. C., & Watson, I. (1985). The manchester prototype

dataflow computer. Commun. ACM, 28, 34–52. xiv, 30, 35, 36, 90

Hall, M., Padua, D., & Pingali, K. (2009). Compiler Research: The Next 50

Years. Comunications of the ACM, 52(2), 60–67. 45

Hall, M. W., Anderson, J. M., Amarasinghe, S. P., Murphy, B. R., Liao, S.-W.,

Bugnion, E., & Lam, M. S. (1998). Maximizing multiprocessor performance

with the SUIF compiler. Digital Technical Journal of Digital Equipment Cor-poration, 10(1). 47, 53

Hauck, S. & Dehon, A. (2007). Reconfigurable Computing: The Theory andPractice of FPGA-Based Computation (Systems on Silicon). Morgan Kauf-

mann. xiii, 5, 6, 8

Hauck, S., Fry, T. W., Hosler, M. M., & Kao, J. P. (2004). The chimaera recon-

figurable functional unit. IEEE Trans. VLSI Syst, 12(2), 206–217. 9

Hauser, J. & Wawrzynek, J. (1997). Garp: a MIPS processor with a reconfi-

gurable coprocessor. Proceedings. The 5th Annual IEEE Symposium on Field-Programmable Custom Computing Machines Cat. No.97TB100186), (pp. 24—

-33). xiii, 10, 11, 89

HPCwire (2009). Cray selects drc fpga coprocessors for supercomputers. 9

Johnston, W. M., Hanna, J. R. P., & Millar, R. J. (2004). Advances in dataflow

programming languages. ACM Comput. Surv., 36, 1–34. 25, 29

95

Page 126: ChipCflow: ferramenta para conversão de código C em uma ...

Kernighan, B. W. & Ritchie, D. M. (1988). The C Programming Language. New

Jersey, USA: Prentice Hall, second edition. 54

Lattner, C. & Adve, V. (2004). LLVM: A Compilation Framework for Lifelong

Program Analysis & Transformation. In Proceedings of the 2004 InternationalSymposium on Code Generation and Optimization (CGO’04) (pp. 75– 86). Palo

Alto, California. 46

Laufer, R., Taylor, R. R., & Schmit, H. (1999). PCI-PipeRench and the Swor-

dAPI: A system for stream-based reconfigurable computing. In K. L. Pocek &

J. Arnold (Eds.), IEEE Symposium on FPGAs for Custom Computing Machines(pp. 200–208). Los Alamitos, CA: IEEE Computer Society Press. 9

Lee, B. & Hurson, A. R. (1994). Dataflow architectures and multithreading.

Computer, 27, 27–39. 25

Lopes, J. J. (2012). ChipCflow - Uma ferramenta para execução de algortimosutilizando o modelo a fluxo de dados dinâmico em hardware reconfigurável.PhD thesis, Universidade de São Paulo. 58

Marques, E. (1993). Projeto de um elemento de processamento de um compu-

tador maçivamente paralelo para execução dirigida a fluxo de dados. 26

Merrill, J. (2003). GENERIC and GIMPLE: A new tree representation for entire

functions. In A. J. Hutton, S. Donovan, & C. C. Ross (Eds.), Proceedings ofthe GCC Developers Summit May 25–27, 2003, Ottawa,Ontario Canada (pp.

171–193). 48

Mittal, G., Zaretsky, D., Tang, X., & Banerjee, P. (2007). An overview of a

compiler for mapping software binaries to hardware. IEEE Trans. VLSI Syst,15(11), 1177–1190. 9

Moreno, E. D., Penteado, C. G., & Silva, A. C. R. d. (2005). Microcontroladorese FPGAs. Novatec Editora. 8

Muchnick, S. S. (1997). Advanced compiler design and implementation. San

Francisco, CA, USA: Morgan Kaufmann Publishers Inc. xiii, 16

Namballa, R., Ranganathan, N., & Ejnioui, A. (2004). Control and data flow

graph extraction for high-level synthesis. IEEE Computer Society AnnualSymposium on VLSI, (pp. 187–192). 23

Panainte, E. M., Bertels, K., & Vassiliadis, S. (2007). The molen compiler for

reconfigurable processors. ACM Trans. Embedded Comput. Systems, 6(1). 6,

21, 46, 90

96

Page 127: ChipCflow: ferramenta para conversão de código C em uma ...

Papadopoulos, G. M. & Culler, D. E. (1998). Monsoon: an explicit token-

store architecture. In 25 years of the international symposia on Computerarchitecture (selected papers), ISCA ’98 (pp. 398–407). New York, NY, USA:

ACM. 30

Project., T. F. (2008). Flex: The fast lexical analyzer. Acessado em janeiro de

2011. 67, 91

Quickturn, A. A. C. (1999). Mercurytm design verification system technology

backgrounder. 9

Razdan, R. & Smith, M. D. (1994). A high-performance microarchitecture with

hardware-programmable functional units. In Proceedings of the 27th AnnualInternational Symposium on Microarchitecture (pp. 172–80).: IEEE/ACM. 9

Rutzig, M. B., Beck, A. C. S., & Carro, L. (2007). Transparent dataflow execu-

tion for embedded applications. VLSI, IEEE Computer Society Annual Sym-posium on, 0, 47–54. 43

Sankaralingam, K., Nagarajan, R., Liu, H., Kim, C., Huh, J., Burger, D., Kec-

kler, S. W., & Moore, C. (2003). Exploiting ilp, tlp, and dlp with the poly-

morphous trips architecture. In In Proceedings of the 30th annual internati-onal symposium on Computer architecture (pp. 422–433).: ACM Press. xiv, 1,

42, 43, 90

Silva, A. C. F. & Silva, J. L. e. (2014). The chipcflow: a tool to generate hard-

ware accelerators using a static dataflow machine designed for a fpga. In

MPP2014 (pp. 1–6).: SBAC-PADW. xv, 85, 86

Silva, A. C. F. d. (2009). Compilação para arquitetura reconfigurável. Master’s

thesis, Universidade Estadual Paulista. xiii, 23

Silva, B. D. A. (2011). Gerenciamento de tags na arquitetura ChipCflow – umamáquina a fluxo de dados dinâmica. PhD thesis, USP - Universidade de São

Paulo. xiii, xiv, 31

Silva, J. L. e. (1992). Processamento a Fluxo de Dados Toelrante a Falhas emum Computador Paralelo. PhD thesis, Universidade Estadual de Campinas.

57, 58

Silva, J. L. e. (2006). Execution of algorithms using a Dynamic Dataflow Model

for Reconfigurable Hardware - A purpose for Matching Data. xiv, 57, 58, 60

97

Page 128: ChipCflow: ferramenta para conversão de código C em uma ...

Silva, J. L. e., Da Costa, K. A. P., & Roda, V. O. (2009). The c compiler gene-

rating a source file in vhdl for a dynamic dataflow machine being executed

direct into a hardware. W. Trans. on Comp., 8(12), 1908–1916. xiii, 1, 24,

57

Silva, J. L. e. & Marques, E. (2006). Executing algorithms for dynamic dataflow

reconfigurable hardware - the operators protocol. In R. Cumplido-Parra, C.

Torres-Huitzil, & A. D. García (Eds.), ReConFig (pp. 64–70).: IEEE Computer

Society. 63

Sima, M., Vassiliadis, S., Cotofana, S., van Eijndhoven, J. T. J., & Vissers, K.

(2002). Field-programmable custom computing machines — A taxonomy.

Lecture Notes in Computer Science, 2438, 79–88. 48

Smith, M. D. & Holloway, G. (2002). An Introduction to Machine SUIF and itsPortable Libraries for Analysis and Optimization. Technical report, Harvard

University. 47, 49, 53

Sobek, S. & Burke, K. (1995). Powerpc embedded application binary interface.

47

Stanford SUIF Compiler Group (1994). SUIF: A Parallelizing & Optimizing Re-search Compiler. Technical Report CSL-TR-94-620, Computer Systems La-

boratory, Stanford University. 53

Swanson, S., Michelson, K., Schwerin, A., Oskin, M., & Science, C. (2003).

Dataflow: The Road Less Complex. Workshop on Complexity-effective Design.

41, 42

Swanson, S., Schwerin, A., Mercaldi, M., Petersen, A., Putnam, A., Michelson,

K., Oskin, M., & Eggers, S. J. (2007). The wavescalar architecture. ACMTransactions on Computer Systems (TOCS), 25(2). xiv, 1, 25, 39, 40, 41, 42,

43, 90

Teifel, J. & Manohar, R. (2004). An asynchronous dataflow fpga architecture.

IEEE Transactions on Computers, 53(11), 1376–1392. xiv, 60

Tripp, J. L., Gokhale, M. B., & Peterson, K. D. (2007). Trident: From high-level

language to hardware circuitry. xiv, 20, 45, 47, 90

Tripp, J. L., Jackson, P. A., & Hutchings, B. (2002). Sea cucumber: A

synthesizing compiler for fpgas. In FPL ’02: Proceedings of the Reconfigura-ble Computing Is Going Mainstream, 12th International Conference on Field-

98

Page 129: ChipCflow: ferramenta para conversão de código C em uma ...

Programmable Logic and Applications (pp. 875–885). London, UK: Springer-

Verlag. 46

Vassiliadis, N., Theodoridis, G., & Nikolaidis, S. (2009). An Application De-

velopment Framework for ARISE Reconfigurable Processors. ACM Transac-tions on Reconfigurable Technology and Systems, 2(4), 1–30. xiv, 49, 51,

90

Vassiliadis, S., Gaydadjiev, G., Bertels, K., & Panainte, E. M. (2004). The

molen programming paradigm. In SAMOS, volume 3133 of Lecture Notes inComputer Science: Springer. 46

Veen, A. H. (1986). Dataflow machine architecture. ACM Computing Surveys,

18(4), 365–396. xiii, 25, 26, 27, 28, 29, 31

Vuillemin, J., Bertin, P., Roncin, D., Shand, M., Touati, H., & Boucard, P.

(1996). Programmable active memories: Reconfigurable systems come of

age. IEEE Transactions on VLSI Systems, 4(1), 56–69. 9

Wirthlin, M. J. & Hutchings, B. L. (1995). DISC: The dynamic instruction set

computer. Field Programmable Gate Arrays for Fast Board Development andReconfigurable Computing, (pp. 99—-103). xiii, 11, 12, 13, 89

Wittig, R. & Chow, P. (1996). OneChip: An FPGA processor with reconfigurable

logic. In K. L. Pocek & J. Arnold (Eds.), IEEE Symposium on FPGAs for Cus-tom Computing Machines (pp. 126–135). Los Alamitos, CA: IEEE Computer

Society Press. 9

Xilinx (2014). Ise design suite. Acessado em janeiro de 2014. 77

99

Page 130: ChipCflow: ferramenta para conversão de código C em uma ...

100

Page 131: ChipCflow: ferramenta para conversão de código C em uma ...

APÊNDICE

AApendice1

LIBRARY ieee ;

USE ieee . std_logic_1164 . a l l ;

ENTITY fa to r i a l IS

PORT( clk : IN STD_LOGIC;

reset : IN STD_LOGIC;

i : IN STD_LOGIC_VECTOR(15 DOWNTO 0) ;

istr2in : IN STD_LOGIC;

iack2ack : OUT STD_LOGIC;

n: IN STD_LOGIC_VECTOR(15 DOWNTO 0) ;

nstr2in : IN STD_LOGIC;

nack2ack : OUT STD_LOGIC;

fat : IN STD_LOGIC_VECTOR(15 DOWNTO 0) ;

fatstr2in : IN STD_LOGIC;

fatack2ack : OUT STD_LOGIC −−esta linha não contem ;

) ;

END fa to r i a l ;

ARCHITECTURE structural OF fa to r i a l IS

COMPONENT fo r i IS

PORT( clkin : IN STD_LOGIC;

reset : IN STD_LOGIC;

n: IN STD_LOGIC_VECTOR(15 DOWNTO 0) ;

nin : IN STD_LOGIC;

101

Page 132: ChipCflow: ferramenta para conversão de código C em uma ...

nack : OUT STD_LOGIC;

um: IN STD_LOGIC_VECTOR(15 DOWNTO 0) ;

umin: IN STD_LOGIC;

umack: OUT STD_LOGIC;

oc : OUT STD_LOGIC_VECTOR(15 DOWNTO 0) ;

ocstr : OUT STD_LOGIC;

ocack : IN STD_LOGIC;

oi : OUT STD_LOGIC_VECTOR(15 DOWNTO 0) ;

o is t r : OUT STD_LOGIC;

oiack : IN STD_LOGIC ) ;

END COMPONENT;

−−−−−−−−−−−−−−−−−−−−−−COMPONENT ndmerge IS

PORT( clkin : IN STD_LOGIC;

reset : IN STD_LOGIC;

a : IN STD_LOGIC_VECTOR(15 DOWNTO 0) ;

ain : IN STD_LOGIC;

aack : OUT STD_LOGIC;

b: IN STD_LOGIC_VECTOR(15 DOWNTO 0) ;

bin : IN STD_LOGIC;

back : OUT STD_LOGIC;

v : OUT STD_LOGIC_VECTOR(15 DOWNTO 0) ;

vstr : OUT STD_LOGIC;

vack : IN STD_LOGIC ) ;

END COMPONENT;

−−−−−−−−−−−−−−−−−−−−−−COMPONENT branch IS

PORT( clkin : IN STD_LOGIC;

reset : IN STD_LOGIC;

v : IN STD_LOGIC_VECTOR(15 DOWNTO 0) ;

vin : IN STD_LOGIC;

vack : OUT STD_LOGIC;

c : IN STD_LOGIC_VECTOR(15 DOWNTO 0) ;

cin : IN STD_LOGIC;

cack : OUT STD_LOGIC;

f : OUT STD_LOGIC_VECTOR(15 DOWNTO 0) ;

f s t r : OUT STD_LOGIC;

fack : IN STD_LOGIC;

t : OUT STD_LOGIC_VECTOR(15 DOWNTO 0) ;

ts t r : OUT STD_LOGIC;

tack : IN STD_LOGIC ) ;

102

Page 133: ChipCflow: ferramenta para conversão de código C em uma ...

END COMPONENT;

−−−−−−−−−−−−−−−−−−−−−−COMPONENT mul IS

PORT( clkin : IN STD_LOGIC;

reset : IN STD_LOGIC;

a : IN STD_LOGIC_VECTOR(15 DOWNTO 0) ;

ain : IN STD_LOGIC;

aack : OUT STD_LOGIC;

b: IN STD_LOGIC_VECTOR(15 DOWNTO 0) ;

bin : IN STD_LOGIC;

back : OUT STD_LOGIC;

z : OUT STD_LOGIC_VECTOR(15 DOWNTO 0) ;

zstr : OUT STD_LOGIC;

zack : IN STD_LOGIC ) ;

END COMPONENT;

−−−−−−−−−−−−−−−−−−−−−−COMPONENT nopop IS

PORT( clkin : IN STD_LOGIC;

reset : IN std_logic ;

a : IN STD_LOGIC_VECTOR(15 DOWNTO 0) ;

ain : IN STD_LOGIC;

aack : OUT STD_LOGIC ) ;

END COMPONENT;

−−−−−−−−−−−−−−−−−−−−−−SIGNAL s4 : STD_LOGIC_VECTOR(15 DOWNTO 0) ;

SIGNAL s4str2in : STD_LOGIC;

SIGNAL s4ack2ack : STD_LOGIC;

SIGNAL s5 : STD_LOGIC_VECTOR(15 DOWNTO 0) ;

SIGNAL s5str2in : STD_LOGIC;

SIGNAL s5ack2ack : STD_LOGIC;

SIGNAL s6 : STD_LOGIC_VECTOR(15 DOWNTO 0) ;

SIGNAL s6str2in : STD_LOGIC;

SIGNAL s6ack2ack : STD_LOGIC;

SIGNAL s7 : STD_LOGIC_VECTOR(15 DOWNTO 0) ;

SIGNAL s7str2in : STD_LOGIC;

SIGNAL s7ack2ack : STD_LOGIC;

SIGNAL s8 : STD_LOGIC_VECTOR(15 DOWNTO 0) ;

103

Page 134: ChipCflow: ferramenta para conversão de código C em uma ...

SIGNAL s8str2in : STD_LOGIC;

SIGNAL s8ack2ack : STD_LOGIC;

SIGNAL s9 : STD_LOGIC_VECTOR(15 DOWNTO 0) ;

SIGNAL s9str2in : STD_LOGIC;

SIGNAL s9ack2ack : STD_LOGIC;

SIGNAL for i0c lk : STD_LOGIC;

SIGNAL ndmerge0clk : STD_LOGIC;

SIGNAL branch0clk : STD_LOGIC;

SIGNAL mul0clk : STD_LOGIC;

SIGNAL nopop0clk : STD_LOGIC;

BEGIN

for i0c lk <= clk ;

branch0clk <= clk ;

ndmerge0clk <= clk ;

mul0clk <= clk ;

nopop0clk <= clk ;

for i0 : f o r i PORT MAP( fori0clk , reset , n, nstr2in , nack2ack ,

i , istr2in , iack2ack , s9 , s9str2in , s9ack2ack , s5 , s5str2in , s5ack2ack ) ;

ndmerge0: ndmerge PORT MAP( ndmerge0clk , reset , fat , fatstr2in ,

fatack2ack , s7 , s7str2in , s7ack2ack , s4 , s4str2in , s4ack2ack ) ;

branch0 : branch PORT MAP( brach0clk , reset , s4 , s4str2in , s4ack2ack ,

s9 , s9str2in , s9ack2ack , s8 , s8str2in , s8ack2ack , s6 , s6str2in , s6ack2ack ) ;

nopop0: nopop PORT MAP( nopop0clk , reset , s8 , s8str2in , s8ack2ack ) ;

mul0: mul PORT MAP( mul0clk , reset , s5 , s5str2in , s5ack2ack ,

s6 , s6str2in , s6ack2ack , s7 , s7str2in , s7ack2ack ) ;

END structural ;

104