Omslag stagerapport & -werkplan
Haagse Hogeschool – Rotterdamseweg 137 - 2628 AL - Delft - Telefoon 015 2606200 - Fax 015 2606201
x
Stagewerkplan
Opleiding
Tussenrapport
Afstudeervariant*
Eindrapport
Stageperiode
Voorletters & naam student B. van den Aardweg Naam Bedrijf
Technische Informatica
2010-1.2 Studentnummer 6000088
Haagse Hogeschool Delft
Naam bedrijfsmentor: Harry Broeders
Door de hogeschool in te vullen Stagecoach
Opmerkingen:
Beoordeling 1. Indeling/opzet 2. Technische inhoud 3. Verzorging presentatie 4. Taalgebruik 5. Totaal beoordeling
Goed Voldoende Matig Onvoldoende
Opmerkingen n.a.v. bovenstaande punten:
Datum: 9-7-2010 gezien accoord
Datum beoordeling:
In te vullen door de stage-administratie: Ingekomen datum:
* Invullen indien bekend. Anders dit veld leeg laten
Verwerkt door:
Datum:
Stageverslag SystemC omzetten naar een diagram
Student: Bas van den Aardweg Studentnummer: 06000088 Datum: 9-7-2010 Stagebedrijf: De Haagse Hogeschool, Academy for Technology, Innovation & Society Delft Afdeling: Opleiding Elektrotechniek Bedrijfsmentor: Harry Broeders
Voorwoord Dit is mijn verslag over mijn stage periode op de Haagse Hogeschool Delft waar ik aan een opdracht voor de docent Harry Broeders heb gewerkt. Bij deze wil ik Harry Broeders bedanken voor het aanbieden van deze stageopdracht en de begeleiding tijdens het uitvoeren van de opdracht. Bas van den Aardweg
Samenvatting Er zijn geen bestaande open-source tools die SystemC volledig om kunnen zetten naar een control- en dataflow diagram. Tijdens de stage is er een begin gemaakt aan een open-source tool die dit wel kan. Deze tool is een plugin voor GCC en kan daardoor uit de datastructuur die GCC gebruikt control- en dataflow halen.
Inhoudsopgave Inhoudsopgave Voorwoord......................................................................................................................................... i Samenvatting.................................................................................................................................... ii 1. Inleiding........................................................................................................................................ 4 2. Opdracht...................................................................................................................................... 5 2.1 Oorspronkelijke opdracht.......................................................................................................5 2.2 Uiteindelijke opdracht.............................................................................................................5 3. Aanpak......................................................................................................................................... 7 4. Onderzoek.................................................................................................................................... 8 5. GCC........................................................................................................................................... 10 6. Ontwikkeling............................................................................................................................... 11 6.1 GCC plugin.......................................................................................................................... 11 6.2 FIR filters............................................................................................................................. 11 6.3 Datastructuur....................................................................................................................... 12 6.4 Uitvoer................................................................................................................................. 13 6.5 Invoer................................................................................................................................... 13 7. Ontwerp...................................................................................................................................... 14 7.1 Visitor Design Pattern.......................................................................................................... 14 7.2 Datastructuur....................................................................................................................... 14 7.3 GIMPLE AST omzetten naar eigen boomstructuur..............................................................15 7.3.1 GIMPLE_ASSIGN........................................................................................................ 15 7.3.2 GIMPLE_CALL............................................................................................................. 16 7.3.4 GIMPLE_RETURN.......................................................................................................16 7.3.5 GIMPLE_COND........................................................................................................... 16 7.3.6 processTreeNode.........................................................................................................16 7.4 Visitors................................................................................................................................. 18 8. Evaluatie.................................................................................................................................... 20 Bronnen.......................................................................................................................................... 21 Bijlage A: How to Model a FIR Filter in SystemC.pdf......................................................................22
1. Inleiding Dit document beschrijft de stage die is uitgevoerd door Bas van den Aardweg. De stageopdracht gaat over control- en dataflow informatie verkrijgen uit een SystemC model. Om dit te te kunnen doen is er tijdens de stage een begin gemaakt aan een open-source tool. Dit document gaat ervan uit dat de lezer bekend is met SystemC en C++. De code die tijdens deze stage geschreven is, is hier te vinden: http://code.google.com/p/systemccdfd/source/checkout . Hoofdstuk 2 beschrijft de opdracht. In hoofdstuk 3 wordt de aanpak beschreven. Hoofdstuk 4 beschrijft het onderzoek waarmee de stage gestart is. Hoofdstuk 5 geeft kort wat informatie over hoe GCC in elkaar zit. Deze informatie is nodig om hoofdstuk 6 te begrijpen waarin de ontwikkeling van de open-source tool beschreven wordt. Hoofdstuk 7 gaat over het ontwerp van de tool. Hoofdstuk 8 bevat een evaluatie van de student over de stage. Bijlage A is een document geschreven door Harry Broeders. Dit document gaat over verschillende manier om een FIR filter te modelleren in SystemC. Deze modellen zijn tijdens de stage gebruikt als invoerbestanden voor de tool.
2. Opdracht Dit hoofdstuk beschrijft de stageopdracht. De eerste paragraaf beschrijft de oorspronkelijke opdracht zoals omschreven bij de aanvang van de stage. De tweede paragraaf beschrijft de uiteindelijke opdracht die tijdens de stage tot stand is gekomen.
2.1 Oorspronkelijke opdracht Hier volgt de oorspronkelijke opdracht zoals omschreven door de opdrachtgever. “De opleiding Elektrotechniek verzorgt, samen met de opleiding Technische Informatica de minor Embedded Systems. Bij deze minor wordt gebruik gemaakt van de system-level modelleertaal SystemC. Harry Broeders doet als afstudeeropdracht voor zijn Master opleiding aan de TU-Delft onderzoek naar parsers voor deze taal. Er is op dit moment geen open-source parser voor SystemC beschikbaar. Een SystemC parser moet in staat zijn om de structuur van het in SystemC gemodelleerde systeem uit het SystemC model te herleiden. Deze structuur bestaat uit hiërarchische modules die via channels met elkaar zijn verbonden. Het probleem bij het vaststellen van deze structuur is dat het SystemC model gedeeltelijk uitgevoerd moet worden omdat de model hiërarchie tijdens de zogenoemde elaboratie fase van de uitvoering van een SystemC model wordt opgebouwd. Op dit moment is het (bijna) mogelijk om dit met behulp van gnu debugger (gdb) te doen. Als we een SystemC module (automatisch) willen implementeren in hard- of software dan moet het gedrag van deze module uit het SystemC model worden afgeleid. In tegenstelling tot de module hiërarchie kan het gedrag van een module worden bepaald zonder het SystemC model uit te voeren. Het gedrag van een module wordt in SystemC beschreven door middel van één of meer SC_THREAD’s, SC_CTHREAD’s en/of SC_METHOD’s. Als we een bepaalde module uit een SystemC model willen synthetiseren in hardware dan is het noodzakelijk om de zogenoemde control- en dataflow tussen de in- en uitgangen van deze module te bepalen. Dit is geen eenvoudige zaak omdat in de gedragsbeschrijvingen elke geldige C++ taalconstructie kan worden gebruikt. Sommige C++ taalconstructies kunnen zelfs helemaal niet in hardware worden geïmplementeerd, bijvoorbeeld het dynamisch alloceren van geheugen. Om deze reden is een subset van SystemC gedefinieerd. De draft versie 1.3 1van deze standaard is in augustus vorig jaar (2009) vastgesteld en te downloaden vanaf: http://www.systemc.org/. Bas zal in overleg met zijn begeleider een programma gaan maken waarin SystemC modules die voldoen aan de SystemC Synthesizable Subset Draft 1.3 omgezet kunnen worden in een control- dataflow diagram. Dit programma zal ontwikkeld worden in C++ maar er zal zoveel mogelijk gebruik gemaakt worden van bestaande tools die gebruikt worden om compilers mee te bouwen (bijvoorbeeld flex en bizon). Ook moet gekeken worden in hoeverre bestaande open-source compilers (bijvoorbeeld gcc) gebruikt kunnen worden. Het SUIF compiler systeem is misschien ook bruikbaar: http://suif.stanford.edu/.”
2.2 Uiteindelijke opdracht De uiteindelijke opdracht was om een plugin te schrijven voor GCC. Deze plugin moet instaat zijn om genoeg informatie uit een SC_METHOD of SC_CTHREAD te halen om gemakkelijk een control-/dataflow diagram (CDFD) te kunnen maken. Dit houdt in dat de uitvoer van de plugin niet noodzakelijk een CDFD hoeft te zijn zolang de benodigde informatie om zo'n diagram te maken maar duidelijk aanwezig is. De uitvoer van de plugin moet in XML formaat zijn. De plugin hoeft niet de volledige SystemC taal te ondersteunen, zolang de plugin maar uit te breiden is zodat hij het in de toekomst wel zou kunnen.
De opdrachtgever zal zelf werken aan een tool die informatie over de modules kan verzamelen. Deze tool zal kunnen zien welke modules er zijn en hoe deze aan elkaar gekoppeld zijn. Ook zal deze tool de processen van modules herkennen (SC_METHOD's en SC_CTHREAD's). Na het herkennen van deze processen moet de tool gebruik kunnen maken van de plugin om informatie over de control- en dataflow te verzamelen. De uiteindelijke opdracht is pas tot stand gekomen na een onderzoek naar bestaande tools.
3. Aanpak Dit hoofdstuk beschrijft de aanpak van de stage. De stage is begonnen met een onderzoek en vervolgens is er een ontwikkel methodiek toegepast om de opdracht uit te voeren. Onderzoek De stage is begonnen met een onderzoek naar bestaande tools om mogelijk de benodigde informatie uit een SystemC model te halen. Dit onderzoek wordt beschreven in het volgende hoofdstuk. Er is begonnen met een onderzoek omdat o.a. de mogelijkheid aanwezig was dat er al een bestaande tool open-source tool is die alle benodige informatie uit een SystemC model of een C++ functie kon halen. Een andere reden voor het onderzoek was voor indien er geen bestaande open-source tool beschikbaar was. In deze situatie zou er onderzocht moeten worden hoe deze informatie wel uit een SystemC model gehaald kon worden en welke tools hiervoor nodig zijn. Methodiek Na het onderzoek zijn er enkele princiepes van 'Agile Developement' toegepast. Omdat er met 'Agile Developement' met een team aan een project wordt gewerkt, is dus niet de gehele methodiek toegepast. De dingen die wel toegepast zijn, zijn het test-driven developement en het plannen. Bij 'Agile Developement' is er veel contact met de opdrachtgever en de wensen en eisen kunnen gedurende het project elk moment veranderen. Dit was ook het geval bij de stageopdracht. Daarom was ervoor gekozen om deze methodiek gedeeltelijk toe te passen. Test-driven developement Test-driven developement houdt in dat er eerst testcases geschreven worden en vervolgens wordt de code geschreven die deze testcases laten slagen. Ook houdt het in dat er van te voren niet een ontwerp gemaakt wordt. De testcases zijn de handleiding voor de code en de code is het ontwerp bij 'Agile Developement'. Plannen Bij 'Agile Developement' worden de eisen en wensen van de opdrachtgever steeds op bepaalde intervallen geformuleerd en omgezet naar taken hiervoor uitgevoerd moeten worden met een schatting van de benodigde tijd erbij. De opdrachtgever geeft vervolgens prioriteiten aan deze taken, terwijl hij rekening houd met de tijd die beschikbaar is, en daarna worden de taken aan de hand van die prioriteiten uitgevoerd. Tijdens laatste weken van de stage is deze manier van plannen toegepast. De weken ervoor is deze manier van werken wel toegepast tijdens de ontwikkeling maar zonder tijdsschatting bij de taken. Er was ervoor gekozen om geen tijdsschatting te maken bij de start van de ontwikkeling omdat de taken die toen uitgevoerd werden absoluut afgemaakt moesten worden, ongeacht de benodigde tijd.
4. Onderzoek Dit hoofdstuk beschrijft het onderzoek dat is uitgevoerd gedurende de eerste weken van de stage. Het eerste doel van het onderzoek was om erachter te komen of er bestaande opensource tools gebruikt konden worden om een CDFD te maken gegeven een SystemC model. Het resultaat hiervan was echter dat dit niet het geval was. Vervolgens richtte het onderzoek zich op het vinden van open-source tools die met aanpassingen of een zelfgeschreven programma's gebruikt kunnen worden om de benodigde informatie voor een CDFD wel uit een SystemC model te halen. Dit is een aanvulling op het onderzoek dat Harry Broeders heeft uitgevoerd. Bestaande open-source tools De opdrachtgever heeft zelf al een onderzoek gedaan naar bestaande open-source tools die informatie halen uit een SystemC model. Bij het onderzoek van de stagiar lag de focus dus op het vinden van andere tools. PinaVM Tijdens het onderzoek is er een tool gevonden die gedeeltelijk voldoet aan de wensen van de opdrachtgever. Deze tool heet PinaVM. PinaVM is geschreven voor verificatie van SystemC modellen. PinaVM zou echter wel kunnen dienen als een basis voor visualisatie van een SystemC model. Het maakt gebruik van de LLVM compiler infrastructuur. Uiteindelijk is er voor gekozen om deze tool toch niet te gebruiken omdat er door de stagiar veel werk verricht zou moeten worden om een back-end te schrijven voor deze tool om een CDFD te maken. Een andere reden is dat het niet duidelijk was in hoeverre PinaVM dataflow kan vinden. GAUT De opdrachtgever heeft zelf al gekeken naar GAUT, een open-source tool die o.a. gegeven C code een dataflow diagram kan genereren. GAUT zou mogelijk gebruikt kunnen worden als een SC_METHOD of SC_CTHREAD omgezet kan worden naar C code. Het omzetten naar C code zou in dat geval eventueel weer door een andere tool gedaan kunnen worden. Hierdoor zou de stageopdracht eventueel voltooid kunnen worden door het aan elkaar koppelen van meerdere open-source tools. GAUT heeft echter een paar grote nadelen. GAUT kan geen controlflow verwerken en er zitten veel beperkingen aan de C code die GAUT als invoer kan gebruiken. Er is dus besloten om geen gebruik te maken van GAUT. Bison Ook zijn er tools in de richting van Flex en Bison onderzocht. Deze tools kunnen gebruikt worden om zelf compilers te schrijven. Het idee was om deze tools te gebruiken om de SystemC code te parsen. Maar omdat SystemC bestaat uit C++ code zou dat betekenen dat er in feite een C++ compiler geschreven moest worden en dit zou haast onmogelijk zijn. LLVM Er is ook gekeken naar LLVM. LLVM zou gebruikt kunnen worden om de SystemC code om te zetten naar een datastructuur waarin control en dataflow informatie te vinden is. LLVM maakt gebruik van LLVM-GCC, een aangepaste versie van GCC. Deze afhankelijkheid zou
in de toekomst mogelijk problemen kunnen opleveren als bijvoorbeeld een nieuwe versie van SystemC afhankelijk is van een bepaalde versie van GCC. Aangepaste versie van GCC Uit het onderzoek is naar voren gekomen dat het mogelijk is om zelf een aangepaste versie van GCC te schrijven. Hierdoor moet het mogelijk zijn om direct toegang te krijgen tot de Abstract Syntax Tree (AST) die GCC intern gebruikt. Deze AST bevat alle informatie die nodig is om een CDFD te maken van een stuk code. Het probleem met een aangepaste versie van GCC schrijven is dat de code afhankelijk is van een specifieke versie van GCC en het zou veel werk zijn om als er een nieuwe versie van GCC is deze vervolgens ook aan te passen. Plugin voor GCC Aan het einde van het onderzoek was naar voren gekomen dat het mogelijk is om een plugin te schrijven voor GCC. Dit is een redelijk nieuwe feature van GCC. Met een plugin voor GCC is het mogelijk om direct toegang te krijgen tot alle interne functies en datastructuren van GCC, zonder dat GCC zelf aangepast moet worden. De nadelen van deze oplossing zijn dat GCC redelijk slecht gedocumenteerd is (bijvoorbeeld vergeleken met LLVM) en dat het schrijven van plugins voor GCC redelijk nieuw is en er dus weinig voorbeelden te vinden zijn. Er was besloten dat het gebruik maken van een plugin ondanks deze nadelen toch het beste eindresultaat zou kunnen opleveren. Dehydra en Treehydra Dehydra en Treehydra zijn plugins voor GCC. Met deze tools is het mogelijk om statische analyses uit te voeren op code. Dit is eigenlijk precies wat nodig is voor de opdracht omdat het met behulp van statische analyse van de code mogelijk is om de control- en dataflow te vinden. Uiteindelijk is er toch besloten om zelf een plugin voor GCC te schrijven om er zo zeker van de zijn dat daadwerkelijk alle informatie die eventueel nodig kan zijn voor een CDFD te vinden is en met Dehydra en Treehydra is dat niet zeker. Een ander nadeel van deze plugins is dat de informatie alleen met javascript te benaderen is en niet met een programmeertaal.
5. GCC Dit hoofdstuk beschrijft de interne werking van GCC voor zover nodig om de rest van dit document te begrijpen. De volgende hoofdstukken beschrijven ook onderdelen van de interne werking van GCC op een lager niveau. De focus van dit hoofdstuk is om slechts een globaal overzicht te geven. Generic GCC bestaat uit front-ends voor meerdere programmeertalen. Deze front-ends zetten de code van hun specifieke programmeertaal om naar GENERIC. GENERIC is een taal onafhankelijke manier om functies in een AST te vertegenwoordigen. GIMPLE GIMPLE is een AST afgeleid van GENERIC. Bij GIMPLE is de structuur versimpeld ten opzichte van GENERIC waardoor het voor de compiler makkelijker is om optimalisaties uit te voeren. GIMPLE is opgebouwd uit zogenaamde basic blocks met edges ertussen. Een basic block bestaat uit een of meerdere GIMPLE statements. Dit zijn bijvoorbeeld assign statements of function call statements. Deze statements bevatten boomstructuren met daarin de juiste variabelen en waardes die nodig zijn voor het uitvoeren van de statement. De edges tussen de basic blocks stelt de control flow voor tussen de basic blocks. Passes Tijdens het compileren wordt een groot aantal passes uitgevoerd. Deze passes wijzigen de GENERIC AST en later GIMPLE AST. Enkele voorbeelden van passes zijn het omzetten van GENERIC naar GIMPLE en bijvoorbeeld een optimalisatie pass die dode code elimineerd. De passes worden voor elke individuele functie uitgevoerd. Single Static Assignment (SSA) Om optimalisaties gemakkelijk uit te kunnen voeren wordt de GIMPLE AST door GCC in de SSA pass omgezet naar een SSA vorm. SSA houdt in dat elke variabele in een functie precies een keer ge-assigned wordt. SSA zit ingewikkelder in elkaar dan hier beschreven wordt, maar voor het begrijpen van dit document is een simpele omschrijving voldoende.
6. Ontwikkeling Dit hoofdstuk beschrijft de ontwikkeling van de GCC plugin en de keuzes die daarbij gemaakt zijn.
6.1 GCC plugin Aan het begin van de ontwikkeling is er het plugin gedeelte van de code geschreven. De eerste keuze die hierbij gemaakt moet worden is wanneer de plugin geactiveerd moet worden. Het is met GCC mogelijk om op verschillende moment tijdens het compileren code van de plugin uit te voeren. Op de verschillende momenten kan er verschillende informatie beschikbaar zijn. Het meest geschikte moment om de benodigde informatie uit het geheugen van GCC te halen is wanneer de passes worden uitgevoerd. Elke pass van GCC wordt uitgevoerd voor elke functie in de code en op het moment van uitvoeren heeft de pass toegang tot de AST van de betreffende functie. Er is een eigen pass geschreven die met behulp van de pass manager van GCC toegevoegd kan worden tussen elke willekeurige pass. SSA pass Er is ervoor gekozen om de code uit te voeren na de SSA pass. De SSA pass wordt uitgevoerd nadat GENERIC is omgezet naar GIMPLE en nadat alle COND statements (if's) een 'then' en een 'else' hebben. Het voordeel van de code direct uit voeren na de SSA pass betekent dat er verder nog geen optimalisaties zijn uitgevoerd. Hierdoor lijkt de GIMPLE AST nog het meest op de oorspronkelijke code. Na enkele optimalisatie passes kan de GIMPLE AST af gaan wijken van de oorspronkelijke code. Het effect is dan nog wel hetzelfde maar het is toch beter om zo dicht mogelijk bij de oorspronkelijke code te zitten omdat GCC optimaliseert voor een gewone processor en niet voor hardware. Hierdoor kunnen enkele optimalisaties van GCC juist een negatieve invloed hebben als er later besloten zou worden om aan de hand van het CDFD hardware te synthetiseren. Loop unroll pass De opdrachtgever had op een later moment de wens om eventueel loopjes te kunnen unrollen. Dit betekent dat bijvoorbeeld de volgende code: for (int i = 0; i < 2; ++i) a[i]=1; omgezet wordt naar: a[0]=1; a[1]=1; GCC heeft zelf passes die loops unrollen, het probleem hiermee is echter dat deze passes alleen uitgevoerd kunnen worden na een groot aantal optimalisaties en vervolgens moeten er daarna nog enkele optimalisaties worden uitgevoerd om de AST op te schonen. Dit betekent dus als de loopjes ge-unrolled worden dat er dan eventueel ongewenste optimalisaties plaatsgevonden kunnen hebben.
6.2 FIR filters De plugin is ontwikkeld aan de hand van enkele SystemC testcases die de plugin moet supporten. Deze testcases komen uit een document van de opdrachtgever en het zijn allemaal verschillende manieren om hetzelfde FIR filter te modelleren.
Een FIR filter is een digitaal filter voor signalen. Voor meer informatie over de FIR filters en de SystemC code waarmee FIR filters gemodelleerd kan worden zie Bijlage A. Cir Om te kunnen testen of de plugin de SystemC code goed kan interpreteren is er in het eerste stadium van de ontwikkeling gebruik gemaakt van een cir uitvoer formaat. Een cir formaat is een formaat dat gebruikt wordt door een tool geschreven door de TUDelft. Deze tool kan onder andere dataflow visualiseren aan de hand van de invoer. Er is voor dit tijdelijke uitvoer formaat gekozen omdat de verschillende testcases van FIR filters die zijn gebruikt, zijn afgeleid van hetzelfde .cir bestand. Dit betekent als de plugin bij een FIR filter test-case een vergelijkbaar .cir bestand kan produceren dat de test-case geslaagd is. Dit uitvoer formaat is echter alleen gebruikt bij het testen van de FIR filters zonder controlflow. Voor meer informatie over cir bestanden en de tool van TUDelft zie Bijlage A.
6.3 Datastructuur Tijdens de ontwikkeling is er voor de datastructuur van de plugin en de bewerkingen die hierop uitgevoerd worden veel gebruik gemaakt van testcases. Dit zorgde ervoor dat fouten die later geïntroduceerd werden veel sneller gelocaliseerd konden worden. Ook maakte dit het refactoren sneller. Omdat er tijdens de ontwikkeling steeds kleine dingen werden toegevoegd om de FIR filter testcases te laten slagen moest de datastructuur regelmatig gerefactored worden. De GIMPLE AST van GCC wordt door de plugin omgezet naar de interne datastructuur van de plugin. Deze datastructuur is een versimpelde AST waarin SystemC function calls zoals read, write en wait herkenbaar zijn. In de GIMPLE AST zijn deze namelijk niet direct te onderscheiden van andere function calls. Daarom kijkt de plugin tijdens het omzetten naar de namespaces, classes en namen van de functies die aangeroepen worden. En afhankelijk van welke namespaces, class en naam van een functie kan de plugin zien of er bijvoorbeeld een SystemC read, write of wait plaatsvindt. Ook is het mogelijk om SystemC datatypes zoals een SC_FIXED het zelfde te behandelen als bijvoorbeeld een double. Voorbeeld: SC_FIXED<64,32> a = 1.0; a = 1.1 * a; Dit moet dezelfde dataflow opleveren als: double a = 1.0; a = 1.1 * a; Het verschil zit hem alleen in de type informatie. Er is ervoor gekozen om tijdens de stage nog niks te doen met de type informatie, omdat de opdrachtgever dit geen prioriteit vond hebben. Het is op een later moment nog wel mogelijk om type informatie toe te voegen aan de plugin. Visitor Er is voor een op het visitor design pattern gebaseerde datastructuur gekozen om zo gemakkelijk bewerking te uit te kunnen voeren op de datastructuur. De reden dat er bewerkingen op de datastructuur uitgevoerd moeten worden is omdat na het direct omzetten van GIMPLE naar de datastructuur van de plugin er veel overbodige informatie is. Deze informatie moet gefilterd kunnen worden en daarom moeten er bewerkingen op de
datastructuur uitgevoerd kunnen worden. De belangrijkste bewerking die uitgevoerd moet worden zal nu uitgelegd worden aan de hand van een voorbeeld. In een SystemC module is het volgende gedefinieerd: sc_out
a; Gegeven de volgende regel SystemC code in een SC_METHOD of SC_CTHREAD van de code: a.write(1); Deze regel wordt door GCC omgezet naar iets vergelijkbaars met: D.0 = 1; D.1 = &a; D.2 = D.1; write(D.2, D.0); Er zitten hier onnodig veel assignments in. Deze assignments moeten door de plugin eruit gehaald worden.
6.4 Uitvoer In het eerste stadium van de ontwikkeling is er gebruik gemaakt van een .cir uitvoer formaat, maar dit was slechts een tijdelijk uitvoer formaat. In het laatste stadium van de ontwikkeling is er gebruik gemaakt van een XML uitvoer formaat dat overeen komt met de interne datastructuur van de plugin. De XML uitvoer was een wens van de opdrachtgever maar ook de meest logische keuze als uitvoer formaat, omdat het later makkelijk uit te breiden is en omdat het makkelijk is voor andere programma's om wat met dit uitvoer formaat te doen.
6.5 Invoer Aan het einde van de stage was er de keuze om de invoer van de plugin te laten verlopen via command-line parameters of een invoer bestand. Er is gekozen voor een XML invoer bestand waarin alle functies staan die omgezet moeten worden naar een CDFD. Deze keuze is gemaakt door de opdrachtgever. Dit is ook de meest logische keuze omdat het later makkelijk uit te breiden is. Hier volgt een voorbeeld van de xml: ::fir::entry output/test2.xml
7. Ontwerp Dit hoofdstuk beschrijft het ontwerp. De eerste paragraaf beschrijft het visitor design pattern. De tweede paragraaf beschrijft de datastructuur die door de plugin gebruikt wordt. De derde paragraaf beschrijft hoe de GIMPLE structuur omgezet wordt naar de datastructuur van de eerste paragraaf. De vierde paragraaf beschrijft de algoritmes die uitgevoerd worden op de datastructuur. Zoals eerder beschreven is met 'Agile Developement' de code het ontwerp. Dus om een beter beeld te krijgen bij sommige beschrijvingen in dit hoofdstuk kan het handig zijn om de code bij de hand te hebben (http://code.google.com/p/systemc-cdfd/source/checkout).
7.1 Visitor Design Pattern
Afbeelding 1: Visitor Design Pattern [http://en.wikipedia.org/wiki/Visitor_patter n] Er is gebruik gemaakt van het visitor design pattern (afbeelding 1) om bewerkingen uit te kunnen voeren op de boomstructuur. Bij het visitor design pattern zijn er elementen. Deze elementen implementeren een accept functie die een visitor object als argument meekrijgt. Visitor is een interface met een visit functie voor elke element class. De accept functie van een element roept de visit functie van de visitor aan. Hierdoor is het mogelijk om bij een class die visitor implementeert voor elk type element ander gedrag te programmeren. Het is bij het visitor design pattern mogelijk om eventuele elementen binnen andere elementen te itereren vanuit de accept functies of vanuit de visitors. Er is bij de plugin gekozen om dit te doen bij de visitors, omdat dit de visitor de meeste controle geeft over wat er gebeurd.
7.2 Datastructuur De datastructuur is opgezet als een boomstructuur. In deze boomstructuur komen nodes voor. Alle nodes implementeren de interface INode. INode is vergelijkbaar met de element class van het visitor design pattern. Alle inner nodes moeten TreeNode of MapTreeNode extenden. TreeNode bevat protected functies om nodes toe te voegen aan een lijst. MapTreeNode bevat protected functies om nodes toe te voegen aan een map, dit is handig in situatie waarin bekend is dat een node een vast aantal childnodes heeft (bijvoorbeeld een WriteNode met een port en een source als childnode). Er is ervoor gekozen dit soort functies protected te maken zodat het bij afgeleide classes hiervan niet mogelijk is om elk type node
aan de lijst of map toe te voegen. Voorbeeld: Een BlockNode extend TreeNode en mag alleen nodes bevatten van het type IActionNode. De datastructuur bevat veel interfaces om ervoor te zorgen dat er geen nodes op verkeerde plaatsen aan de boom kunnen worden toegevoegd. De volgende paragraaf zal meer inzicht geven in de boomstructuur aan de hand van het omzetten van GIMPLE AST naar de boomstructuur.
7.3 GIMPLE AST omzetten naar eigen boomstructuur Het omzetten van de GIMPLE AST naar de eigen boomstructuur begint met het aanmaken van een ControlFlowRoot object. De ControlFlowRoot is de root voor de boomstructuur. Itereren van basic blocks Voor elk basic block van de functie die omgezet wordt naar de eigen boomstructuur wordt een BasicBlock node aangemaakt en toegevoegd aan de ControlFlowRoot. Elk basic block heeft een of twee edges. Deze edges worden ook toegevoegd aan de ControlFlowRoot. Itereren van statements Ieder basic block bevat een of meerdere statement. Voor iedere statement wordt gekeken naar de gimple_code van deze statement. De gimple_code stelt het type statement voor. Enkele voorbeelden van gimple_codes zijn: GIMPLE_ASSIGN, GIMPLE_CALL, GIMPLE_COND, GIMPLE_RETURN. Deze voorbeelden zijn ook de enige GIMPLE statements die op het moment ondersteund worden door de plugin.
7.3.1 GIMPLE_ASSIGN Een GIMPLE_ASSIGN stelt een simpele assignent voor zoals: a = b; a = b + c; a = !b; Een GIMPLE_ASSIGN bevat geen complexere syntax trees. Als de gimple_code van een statement GIMPLE_ASSIGN is dan wordt er een AssignNode aangemaakt. Deze AssignNode wordt toegevoegd aan de BlockNode. Een AssignNode krijgt een target en een operand. De target wordt gevonden door de AST die teruggeven wordt door de gimple_assign_lhs functie om te zetten naar een VarNode met behulp van de processTreeNode van de plugin. Een VarNode wordt gebruikt om variabelen uit de GIMPLE AST te vertegenwoordigen in de eigen datastructuur. Een VarNode implementeert de IValueNode interface. De processTreeNode functie wordt aan het einde van deze paragraaf beschreven. Voor het vinden van de op waarde moet er eerst gekeken worden naar wat voor type assignment de GIMPLE_ASSIGN voorstelt. Er zijn drie type assignments: een unary-, binary- en een single assignment. Bij een unary assignment wordt er een UnaryExpressionNode aangemaakt en met de setOp functie toegevoegd aan de eerder aangemaakte AssignNode. De UnaryExpressionNode heeft zelf ook een op, deze waarde wordt gevonden door de GIMPLE boom die
teruggegeven wordt door de gimple_assign_rhs1 functie om te zetten naar een IValueNode met behulp van de processTreeNode functie. Bij een binary assignment wordt er een BinaryExpressionNode aangemaakt en met setOp toegevoegd aan de AssignNode. Een BinaryExpressionNode heeft echter twee operands: op1 en op2. Op1 wordt gevonden door het verwerken van gimple_assign_rhs1 en op2 door het verwerken van gimple_assign_rhs2. Bij een single assignment wordt de boom van gimple_assign_rhs1 direct verwerkt en met setOp toegevoegd aan de AssignNode.
7.3.2 GIMPLE_CALL Een GIMPLE_CALL stelt een function call voor. Als de gimple_code van een statement een GIMPLE_CALL is wordt er gekeken naar wat voor soort function call het is. Bij verschillende soorten SystemC function calls worden andere type nodes toegevoegd aan de BlockNode. Bij een function call naar een read op een port of signal wordt een ReadNode aangemaakt. Bij een write op een port of signaal wordt er een WriteNode aangemaakt. Bij een wait wordt een WaitNode aangemaakt. Net zoals bij de GIMPLE_ASSIGN zijn er bij de GIMPLE_CALL ook speciale functies zoals gimple_call_lhs en nog meer om AST's op te vragen. Afhankelijk van wat voor type node aangemaakt moet worden, worden de juiste AST's omgezet naar VarNodes of IValueNodes. Hier wordt gebruik gemaakt van een switch om de functie naam afte vangen. In het geval dat de GIMPLE_CALL een function call is naar een niet SystemC functie dan wordt er CallNode aangemaakt met daarin zoveel mogelijk informatie over de function call.
7.3.4 GIMPLE_RETURN Een GIMPLE_RETURN stelt een return statement voor. Deze worden door de plugin genegeerd want de plugin hoeft nu alleen control en dataflow te halen uit een SC_METHOD of SC_CTHREAD te halen en deze geven nooit een waarde terug.
7.3.5 GIMPLE_COND Een basic block kan eindigen met een GIMPLE_COND statement. Dit stelt een conditie voor. Voor deze statements wordt een ConditionNode aangemaakt en toegevoegd aan de ControlFlowRoot, ook krijgen de EdgeNodes die bij deze BlockNode horen en verwijzing naar deze ConditionNode. Een ConditionNode is een BinaryExpressionNode met een ID zodat edges ernaar kunnen verwijzen en het implementeert de IControlFlowNode interface om zo toegevoegd te kunnen worden aan de ControlFlowRoot. De ConditionNode wordt niet toegevoegd aan de BlockNode. Dit heeft te maken met de manier waarop de controlflow en dataflow gescheiden zijn in de datastructuur.
7.3.6 processTreeNode processTreeNode is een functie geschreven om bomen uit de GIMPLE AST om te kunnen zetten naar IValueNodes. Deze functie heeft een GIMPLE AST als argument (vanaf nu: tree). De tree heeft een code (tree code). Het type IValueNode dat teruggeven moet worden hangt af van deze tree code. Hier volgt een korte omschrijving van enkele van de handelingen die verricht worden bij bepaalde tree codes:
VAR_DECL Bij de VAR_DECL tree code wordt er een VarNode aangemaakt en teruggeven. De VarNode krijgt een type waaraan te zien is dat de VarNode is aangemaakt vanwege een VAR_DECL. Er is ervoor gekozen om VarNodes types te geven afhankelijk van waar ze zijn aangemaakt om het debuggen makkelijker te maken, maar ook voor eventuele bewerkingen. ADDR_EXPR en INDIRECT_REF Als de tree code van een tree een ADDR_EXPR of een INDIRECT_REF is wordt de processTreeNode voor de bijbehorende operand van de tree uitgevoerd. Dit betekent dat informatie over adressen en referenties als overbodig wordt beschouwd door de plugin. In de toekomst is het nog wel mogelijk om deze informatie toe te voegen, maar voor de FIR filter test-cases was deze informatie niet relevant. REAL_CST en INTEGER_CST Als de tree code van een tree gelijk is aan REAL_CST of INTEGER_CST betekent het dat er een floating point of integer constant waarde gevonden is. Een REAL_CST kan een float of een double voorstellen en een INTEGER_CST kan een bool, char, int, etc. voorstellen. Afhankelijk van welke tree code wordt er een IntNode of RealNode aangemaakt en teruggegeven. Beide implementeren de IValueNode interface. Tijdens de ontwikkeling waren er wat problemen met het vinden van de juiste waarde bij een REAL_CST. De documentatie van GCC is onvolledig en het is lastig om duidelijke voorbeelden te vinden. Uiteindelijk is er met behulp van de opdrachtgever een manier gevonden om de juiste waarde te vinden. SSA_NAME Bij een tree met SSA_NAME als code wordt eerst gecontroleerd of de node een GIMPLE_PHI node is. Dit betekent dat de node een van meerdere waardes heeft en het is onbekend welke waarde. In dit geval wordt er een PHINode aangemaakt en teruggegeven. Een PHINode extend VarNode en heeft als childnodes alle mogelijke waardes die de GIMPLE_PHI node kan hebben. Als de SSA_NAME geen GIMPLE_PHI node is wordt er een VarNode aangemaakt en teruggegeven. COMPONENT_REF Een tree met COMPONENT_REF als code stelt een veld voor binnen een class- of structachtige constructie. De tree heeft in deze situatie twee relavante operands. De eerste operand is een tree die het object vertegenwoordigd en de tweede is een tree die het veld binnen het object vertegenwoordigd. Deze trees worden verwerkt met processTreeNode en samengevoegd tot een enkele VarNode. Er is echter een lastig probleem bij een COMPONENT_REF. De eerste operand kan uitkomen op een tree met SSA_NAME als tree code. Deze SSA_NAME wordt niet altijd binnen de functie assigned. In dat geval is het object het 'this' object. Deze constructie wordt herkent door bij het verwerken van de SSA_NAME een NullNode terug te geven. Het is noodzakelijk om speciaal rekening te houden met deze situatie. Het gebruik van een VarNode die niet assigned wordt (in dit geval de SSA_NAME van het 'this' object) maakt het genereren van dataflow lastig omdat er niet bekend is waar de flow begint.
7.4 Visitors Er zijn gedurende de ontwikkeling meerdere visitors geschreven om bewerkingen uit te voeren op de datastructuur. Enkele hiervan zijn overbodig geworden en zullen niet beschreven worden, met uitzondering van de CirVisitor. MergeVisitor De MergeVisitor heeft als doel zoveel mogelijk AssignNodes waarvan getOp() een VarNode geeft weg te optimaliseren. De eerste stap in dit proces is om te kijken of een AssignNode wel weggehaald mag worden. Dit mag alleen als de target van de assign slechts een keer als target gebruikt wordt. Ondanks dat de code pas uitgevoerd wordt als GIMPLE in de SSA vorm is, kan het voorkomen dat een variabele meerdere keren assigned wordt. Dit is iets wat GCC doet op het moment dat een variabele in de code als een reference of pointer wordt meegegeven aan een andere functie. In het geval van SystemC werkt de write functie van porten en signalen met references. Om te controleren of een target van een assign meerdere keren als target gebruikt wordt is er een visitor geschreven die bij het uitvoeren een multiset gegenereerd met daarin het aantal keren dat elke variabele assigned wordt. Nadat de merge visitor heeft bepaald dat een AssignNode weggehaald mag worden dan wordt er gebruik gemaakt van een ReplaceVisitor. Deze visitor zoekt in de boomstructuur naar VarNodes met als waarde de getTarget() van de AssignNode en vervangt deze met de waarde van de getOp() van de assign node. Na het vervangen van een VarNode geeft de ReplaceVisitor de VarNode een nieuw type waardoor in de uitvoer te zien is er een bewerking op is uitgevoerd. Na het uitvoeren van de ReplaceVisitor wordt de AssignNode verwijderd. RemoveUselessAssignVisitor Als de GIMPLE AST omgezet naar de boomstructuur van de plugin dan worden function calls naar de destructors van SystemC objecten niet meegenomen, deze zijn niet relevant voor de control en dataflow en ze zijn gemakkelijk te filteren. Het probleem is echter dat er in de GIMPLE AST wel GIMPLE_ASSIGN nodes staan waarvan de variabele die assigned wordt alleen gebruikt wordt door de destructor. Daarom is er een visitor geschreven om deze AssignNodes achteraf uit de boomstructuur te verwijderen. De RemoveUselessAssignVisitor haalt AssignNodes uit de boomstructuur waarvan de waarde van de getTarget() verder niet gebruikt wordt. Deze visitor maakt een set van alle waardes van VarNodes die als target van een AssignNode gebruikt worden en een set van alle waardes van VarNodes die op andere plaatsen gebruikt worden. Vervolgens worden alle AssignNodes van de target in de eerste set voorkomt en niet in de tweede set voorkomt verwijderd uit de boom. AssignToReadVisitor In SystemC zijn bij signalen en porten meerdere = operatoren gedefineerd. Een van deze operatoren gaat ervan uit dat links en rechts van de = hetzelfde type staat. Voorbeeld: sc_buffer a, b; a = b; Dit betekent dat buffer b uitgelezen wordt en de waarde hiervan naar buffer a geschreven wordt. Bij = operatoren die niet hetzelfde type links en rechts hebben komt er een expliciete function call naar de read functie te staan in de GIMPLE AST. Maar in deze situatie komt er
alleen een function call naar operator= te staan. De AssignToReadVisitor lost dit probleem om door een AssignNode die hierbij hoort om te zetten naar een ReadNode. CirVisitor In het eerste stadium van de ontwikkeling is er gebruik gemaakt van een CirVisitor om de te kijken of de FIR filter testcases slaagden. De CirVisitor genereert een cir bestand. De CirVisitor heeft als voorwaarde dat er alleen dataflow aanwezig is. De eerste handeling die de CirVisitor uitvoert is kijken of er impliciete buffers zijn. Dit zijn variabelen die in de boom eerst gebruikt worden en later assigned worden. Dit kan als de variabele binnen de module gedefinieerd is. Er worden read en write nodes toegevoegd met behulp van een visitor. Deze visitor doorloopt de boom en voegt elke waarde die als target van een AssignNode gebruikt wordt toe aan een lijst. Komt de visitor een node tegen die gebruik maakt van een waarde die nog niet assigned is dan wordt er direct een ReadNode toegevoegd en na de AssignNode later in de boom die deze waarde assigned, wordt een WriteNode toegevoegd. Nadat de impliciete buffers bekend zijn doorloopt de CirVisitor de boom. Als er een ReadNode gevonden wordt dan wordt deze in een read lijst gestopt als er een WriteNode gevonden wordt dan wordt deze in een write lijst gestopt. Elke AssignNode wordt direct vertaald naar tekst. Voorbeeld: een AssignNode met als target een VarNode met waarde 1 en als op een BinaryExpression met als type '+' en op1 een VarNode met waarde 2 en op2 een IntNode met waarde 3 wordt omgezet naar: n1 = n2 + c0; De c0 stelt een constante waarde voor. Een cir bestand hoeft niet te weten welke waarde dit is. De eerst volgende constante waarde in die in de boom gevonden wordt, wordt omgezet naar c1, de waarde daarna c2, enz. Als een VarNode aan de op kant van een AssignNode voorkomt in de read lijst wordt er een 'i' gebruikt in plaats van een 'n'. Voor elke WriteNode die gevonden wordt, wordt de volgende regel tekst toegevoegd: oPortNaam = nSourceWaarde; Als laatste voegt de CirVisitor een delay-element toe voor elke waarde die in de read lijst en de write lijst voorkomt.
8. Evaluatie Dit hoofdstuk beschrijft de persoonlijke mening van de stagiair over de stage. Onderzoek Er is tijdens de stage veel tijd besteed aan het onderzoek. Het onderzoek had de simpele conclusie dat er een GCC plugin geschreven moest worden. De meerwaarde van het onderzoek was echter de kennis die de stagiair heeft meegekregen van het onderzoek. Deze kennis heeft betrekking tot onder andere de werking van een compiler maar ook veel dingen gerelateerd aan SystemC en hardware synthese. Ontwikkeling Het nadeel van het gedeeltelijk toepassen van 'Agile Developement' is dat er geen gebruik is gemaakt van pair programming. Dat houdt in dat er met twee programmeurs achter een computer wordt gewerkt. Dit kon ook niet omdat de stagiair de plugin alleen moest schrijven. Pair programming had echter wel betere code en betere testcases kunnen opleveren. Het voordeel van het gedeeltelijk toepassen van 'Agile Developement' was dat er weinig tussentijdse documentatie geschreven moest worden en er hard doorgewerkt kon worden. Want de handleiding voor de code zijn de geschreven testcases. Samenwerking Ondanks dat de plugin alleen door de stagiair is geschreven is er wel veel communicatie geweest met de opdrachtgever. Dit zorgde ervoor dat de ontwikkeling soepel verliep. Eindresultaat Het eindresultaat is een plugin voor GCC waarmee er een simpele AST gegeneerd kan worden van een SC_METHOD of SC_CTHREAD. De plugin ondersteund nog niet alle mogelijke expressies en SystemC constructies. De plugin is wel zo opgezet dat deze dingen er zonder teveel moeite aan toegevoegd kunnen worden. Het zou leuk zijn als de plugin gedurende de komende jaren door andere stagiairs, een projectgroep of door de opdrachtgever verder ontwikkeld kan worden.
Bronnen Boek: Agile Software Development, Principles, Patterns, and Practices. Geschreven door Robert C. Martin. Sites: http://en.wikipedia.org/wiki/Visitor_pattern http://gcc.gnu.org/onlinedocs/gccint/
Bijlage A: How to Model a FIR Filter in SystemC.pdf Auteur: Harry Broeders. Zie bijgevoegd pdf bestand.