FSM+D hardware compiler for C programmeringssproget

Størrelse: px
Starte visningen fra side:

Download "FSM+D hardware compiler for C programmeringssproget"

Transkript

1 FSM+D hardware compiler for C programmeringssproget Mark Ruvald Pedersen Jesper Marrup 25. juni 2010 Mark Ruvald Pedersen s072095@student.dtu.dk Jesper Marrup s062035@student.dtu.dk Vejleder: Projekt: Jan Madsen Dette bachelorprojekt repræsenterer 15 ECTS point. Projektforløb fra 2. febuar til 25. juni, Institut for Informatik og Matematisk Modellering Danmarks Tekniske Universitet

2 Abstract High-level synthesis (HLS) offers a new paradigm in the development of digital systems. HLS allows digital design at a level higher than the conventional RTL-level. Such an EDA-tool for high-level synthesis has been developed in the achievement of our bachelor s degree. We describe herein considerations, algorithms and implementation details of YACHT - Yet Another C-to-Hardware Translator. YACHT translates source code written in a subset of ANSI C to digital hardware. Input source code is parsed using ANTLR and the abstract syntax tree is built. From this tree, an abstract state machine is constructed. Two approaches are described afterwards, for the generation of hardware from the abstract state machine - a resource-unconstrained and a resource-constrained. Finally, VHDL code from the internal hardware representation, is written. With the resource-unconstrained synthesis, YACHT is functional and a series of small examples have been synthesized and run successfully on a Spartan-3E FPGA board. Register allocation is an important part of the resource-unconstrained synthesis. A potentially new method for register allocation has been invented - though it is not yet known whether this method has been described in the literature before. Resumé Høj-niveau syntese (HLS) tilbyder et nyt paradigme i udviklingen af digitale systemer. HLS tillader digital design på niveauet højere end på det konventionelle RTL-niveau. Sådan et EDA-værktøj til høj-niveau syntese er blevet udviklet i opnåelsen af vores bachelorgrad. Vi beskriver heri overvejelser, algoritmer og implementeringsdetaljer vedrørende YA- CHT - Yet Another C-to-Hardware Translator. YACHT oversætter kildetekst skrevet i et subset af ANSI C til digital hardware. Input kildetekst parses via ANTLR og det abstrakte syntakstræ opbygges. Ud fra dette træ bliver den abstrakte tilstandsmaskine konstrueret. To tilgange beskrives herefter, til generering af hardware ud fra den abstrakte tilstandsmaskine - en ressource-ubegrænset og en ressource-begrænset. VHDL kode for den interne hardware repræsentation udskrives til sidst. Med den ressource-ubegrænsede hardwaresyntese er YACHT funktionsdygtig og en række små eksempler er blevet syntetiseret og kørt med succes på et Spartan-3E FPGA board. Registerallokeringen er en vigtig del af den ressourcebegrænsede syntese. En potentiel ny metode til registerallokering er blevet opfundet - det er dog endnu ikke vist om denne metode er blevet beskrevet i litteraturen før. ii

3 Læsevejledning Selvom den vedlagte programkode repræsenterer seneste revision, kan læseren måske have interesse i at studere hvordan denne har udviklet sig siden projektets start. Eftersom vi har anvendt versionsstyring fra start af, er dette muligt. Fuld commit-log med forfatterangivelse, kan findes på hjemmesiden for projektet: - Herfra er det også muligt for læseren at søge i hele projektet, direkte fra browseren. Rapporten Den røde tråd forsøges opretholdt gennem hele rapporten, hvorfor rapporten med fordel kan læses i et sekventielt flow. Rapporten, skrevet i LYX, gør brug af hyperref L A TEX pakken og kan findes som Rapport.pdf på den vedlagte CD-ROM. I kraft af hyperrefpakken, er det muligt at klikke på alle henvisninger til fodnoter og citationer som var de hyperlinks - hvis læseren skulle være interesseret i dette. Vi anbefaler at læse fodnoterne som de mødes, da disse indeholder detaljer og yderligere forklaringer. NB Forfatterere er angivet ved margin-noter udfor titlen af hvert afsnit. Forfatterangivelsen kan dermed også ses i indholdsfortegnelsen. Kildekoden Kildekoden fylder totalt set lige under 34 ksloc, hvoraf 25 ksloc er autogenereret af ANTLR. De resterende 9 ksloc består af egen C++-kode samt C-grammatikken. For at undgå papirspild, er kun C-grammatikken inkluderet som appendiks. For en inspektion af kildekoden anbefaler vi at indlæse denne i en IDE som fx NetBeans, hvorefter man nemt kan hoppe frem og tilbage i definitioner med Ctrl+B - som om klasser og variable var hyperlinks. iii

4 Indholdsfortegnelse 1 Problemanalyse 3 Fælles 1.1 Problemformulering Fælles 1.2 Afgrænsninger Fælles 1.3 Målgruppe Fælles 2 Indledende overvejelser Kravspecifikation Fælles 2.2 Løsningsmetode Fælles 2.3 YACHT etymologi og logo Mark 2.4 Aktivitetsplan Fælles 2.5 Motiverende eksempel: Greatest Common Divisor Mark 3 Parsing af C 15 Jesper 3.1 Research og vurdering af eksisterende parserer Fælles 3.2 Kort om ANTLR Jesper 3.3 C-grammatikken Jesper 4 Abstract Syntax Tree 23 Mark 4.1 Rationale for hierarkisk nedarvning af ASTnoder Mark 4.2 AST modellering Mark 4.3 ASTvisitorNormalize: Mutationer på AST Mark Normaliseringer Mark Reduceringer og optimeringer Mark Errata for konstantfoldning Mark Operatorer: Fra kæde til træstruktur Mark Virkemåde Mark 4.4 stmtblock: Symboltabel og statements Mark 4.5 Eksempel: AST for GCD Jesper 5 Abstract State machine 34 Jesper 5.1 ASM modellering Mark 5.2 Repræsentation af control-flow i ASM Jesper 5.3 ASTvisitor2ASM: Konstruktion af ASM ud fra AST Jesper 5.4 ASMvisitorNorm: Udryddelse af ASMdummy s Mark 5.5 ASMvisitorBB: Gruppering til Basic Blocks Mark Bernsteins betingelser: Dataafhængighedsanalyse Mark Virkemåde af ASMvisitorBB Mark 5.6 Eksempler iv

5 5.6.1 Assimilering Mark Software-pipelining Mark ASM for Duff s device Mark ASM for GCD Jesper 6 Hardware 45 Fælles 6.1 Objektorienteret modellering af hardware Mark Strukturel modellering Mark Komponenter Mark Porte, sources og sinks Mark Signaler Mark Eksempel: Binær adder i C Jesper Modellering af tilstandsmaskine Mark moorenode og moorekant Mark 6.2 Semantik af beregningsoperatorer Mark Afvigelse af relationelle operatorer og unær logisk not Mark 6.3 Hardware generering Mark Naiv hardware generering Mark Virkemåde af ASMvisitor2HW Mark Eksempel: Simpel counter med enable Fælles Eksempel: GCD Mark Ressourcedeling Mark Scheduling og binding Mark Register allokering Mark ASMvisitorProtect: Baglæns levetidsanalyse Mark Virkemåde af ASMvisitor2HWconstrained Mark 7 HWvisitor2VHDL: VHDL output 71 Jesper 8 Afrunding Case: GCD Fælles 8.2 Errata Fælles 8.3 Forbedringsforslag Fælles 8.4 Diskussion Fælles 8.5 Konklusion Fælles Litteratur 80 9 Processbeskrivelse Mark Ruvald Pedersen Mark 9.2 Jesper Marrup Jesper A Visitor mønstret 83 Mark B GraphViz DOT 86 Jesper C Multiplikation og approximativ division med konstant 87 Mark C.1 Multiplikation mellem variabel og konstant Mark C.2 Approximativ division mellem variabel og konstant Mark C.2.1 Afrunding Mark v

6 D Signed/unsigned opførsel af de modulo-aritmetiske operatorer 91 Mark D.1 Eksempler Mark D.2 Adder / subtractor Mark D.3 Multiplier Mark D.4 Divider Mark E ANTLRv3 C-grammatik 94 Fælles vi

7 Nomenklatur Allokering I kontekst af HLS: Betegner det problem at bestemme hvilke og hvor mange ressourcer der er nødvendige. ASIC Application-Specific Integrated Circuit. Realiserer et fikseret elektronisk kredsløb - kan både være digitalt og analogt. En ASIC kan ikke ændres efter produktion og medfører stor NRE ved produktion. ASM AST Abstract State Machine. Stor lighed med CFG (Control Flow Graph). Hver node er en Basic Block, orienterende kanter angiver spring i control flowet. Abstract Syntax Tree; er en træ-repræsentation af kode. Der er 1:1 korrespondance mellem noder i AST et og diverse konstruktioner i kildeteksten. Binding I kontekst af HLS: Betegner det problem at afgøre på hvilken ressource en operation skal udføres. DAG DFG EDA Directed Acyclic Graph. Orienteret acyklisk graf, direkte oversat. Et superset af træer og subset af grafer. Et træ er dermed en DAG som er en graf, men det omvendte gælder ikke nødvendigvis. Data Flow Graph. En graf som gengiver dataafhængighederne mellem operationer. Electronic Design Automation. Betegner software værktøjer til design og produktion af elektroniske systemer. FPGA Field-Programmable Gate Array. En FPGA kan realisere vilkårlige digitale kredsløb, og kan omprogrammeres i modsætning til ASICs. FSM+D Finite State Machine (tilstandsmaskine) + Datapath (datavej). HDL HLS Hardware Description Language. Beskriver digitale elektroniske kredsløb - deres opførsel og struktur. En HDL beskrivelse af et kredsløb kan ofte simuleres og syntetiseres til fysisk hardware i en FPGA eller ASIC chip. High-Level Synthesis, dvs. højniveau syntese. Også kendt som algorithmic synthesis, eller behavioral synthesis. Betegner den automatiske oversættelse (syntese) af en algoritme på højt abstraktionsniveau til en beskrivelse på et lavere abstraktionsniveau. Leaf node En knude som har 1 eller flere indgående kanter men ingen udgående. En leafnode siges da ikke at have nogen børn. Lexer Leksikalsk analysator. Opdeler en strøm af karakterer i logiske tokens. Eksempler på tokens kan være tal, ord, tegnsætning og whitespace (som ofte ignoreres). 1

8 NRE Non-Recurring Engineering [Cost]. Parser En parser udfører syntaktisk analyse, ved at forsøge at pålægge inputtekst en grammatisk struktur. I streng forstand, arbejder en parser udfra tokens leveret af en lexer; der er klar adskillelse. Ofte bruges parser dog sononymt med både parser+lexer som samlet enhed. RAG Register Allocation Graph. Vi betegner det sæt af kanter som left-edge arbejder på, som en RAG. Schedulering I kontekst af HLS: Betegner det problem, at skemaplanlægge hvornår en operation skal udføres, således at så mange operationer som muligt, kan eksekveres parallelt. SLOC Single Line Of Code. std::vector En vector er et dynamisk allokeret array i C++ s standard template library (STL). VHDL VHSIC Hardware Description Language. Er et HDL til simulering og syntese af digitale kredsløb. VHSIC Very-High-Speed Integrated Circuit. XST Xilinx Synthesis Tool. Udfører logik syntese af HDL beskrivelser. 2

9 Kapitel 1 Problemanalyse Fælles Digital elektronik er i dag allestedsnærværende; lige fra fjernsyn, mobiltelefoni og computere til høreapparater og køkkenudstyr. -Og denne tendens er kun stigende; hvad der stadigt er analogt i dag, bliver digitalt i morgen. Digitale radio/tv-signaler, softwaredefineret radio, D-klasse forstærkere, etc., er eksempler herpå. Desværre kan det tage tid og personlig dedikation, at opnå den fornødne ekspertise og erfaring, for at kunne designe komplekse hardware systemer. Set i forhold til hardware design, har software modstykket mange fordele: Software kan udvikles hurtigere, debugges nemmere og har højere fleksibillitet end hardware med tilsvarende funktionalitet. For at udvikle software kræves intet dyrt udstyr, og de fleste værktøjer er helt gratis. Da indlæringskurven for grundlæggende programmering er lille til sammenligning, er softwareudvikling mere tilgængeligt og udbredt. Selv med en hardware designers erhvervede kompetencer, er designprocessen tidskrævende: For selv små HW problemer, kan det tage flere timer fra idé til korrekt implementering: Hvad der kan skrives som 3 linjer C på et minut, tager 150 linjer VHDL og en lille dag. HDL sprog er således meget omstændige - og med flere kodelinjer stiger risikoen for fejl. Selvom denne omstændighed kan justeres, tillader gængse EDA værktøjer ikke at beskrive HW på et abstraktionsniveau som ellers er at finde i konventionelle programmeringssprog. Megen produktudvikling i dag fokuserer derfor på at genbruge generiske hardware platforme, hvor kun softwaren ovenpå differentierer et produkt fra et andet - dette minimerer NRE (non-recurring engineering). NRE kan dog ikke undgås, for senere hen overtager en ny platform. Som hardwaren stiger i kompleksitet, bliver denne problemstilling kun mere udtalt. Højniveau-syntese (HLS) kan adressere disse problemer ved at tillade syntese af algoritmer skrevet i koncise og velkendte sprog. Herved kombineres softwarens hurtige og fleksible udviklingsscyklus, med de fordele som fysisk hardware kan give - således kan NRE minimeres yderligere. De grundlæggende algoritmer for HLS blev udviklet i 80 erne, og selvom området har været genstand for megen forskning, findes kun få implementationer i dag. Den primære årsag til den langsomme adoptions-rate har været manglen på fælles standarder[2, foreword]. HLS åbner op for en række nye muligheder: Set fra et hardwaremæssigt synspunkt: At angribe den stigende kompleksitet af hardware systemer ved at gå op i abstraktionsnivau. Herved lettes designopgaven og produktiviteten øges. Ofte beskrives kode-deltaljeringsgraden i et HDL som værende behavioral, RTL, eller strukturel. 3

10 Anvendes et HDL som VHDL eller Verilog som mellemsprog, bliver hardwareudvikling mere fleksibelt. Da vil HLS kun være en tilføjelse, hvorfor integration og bagudkompatibeltet med eksisterende HDL kode vil være bevaret. Øget kode-genbrug: Med ét bliver enorme mængder af eksisterende kode mere relevant for hardware designere. Set fra et softwaremæssigt synspunkt: Med lille indlæringskurve kan programmører tage en ny massiv parallel platform i brug - omend ikke udnytte den optimalt. Realtids processing eller high-performance computing, bliver mere tilgængligt. Algoritmer med få data afhængigheder (mulighed for høj parallelisme) er oplagte til hardware implementation. 1.1 Problemformulering Fælles HLS er attraktiv teknologi, som tilbyder et nyt paradigme at udvikle hardware under. Da HLS er stadig et aktivt forskningsområde, og teknologien endnu ikke er blevet standard i industrien, fortjener teknologien mere opmærksomhed. Selvom der ikke findes nogen entydig korrespondance mellem software og digital hardware, er det dog ofte muligt at realisere den samme algoritme i begge domæner. For netop sådanne tilfælde, har vi gennem egen praktisk erfaring oplevet, at hardwaredesign er en langt mere involveret process. Gennem HLS vil størstedelen af arbejdet i hardwaredesign kunne automatiseres ud fra en softwaremodel. Projektets hovedproblem bliver således: Hvordan kan et program realiseres, til at oversætte sekventiel C kode til syntetiserbart hardware? - Herunder vil vi behandle problemstillingerne: Hvilket udsnit af C sproget er passende? Hvordan parses og analyseres C kode nemmest? Hvordan identificeres den mulige parallelisme ud fra ellers sekventiel C kode? Hvordan kan en tilstandsmaskine og datavej udledes? Hvordan udskrives HDL koden fra den interne repræsentation? Det resulterende program skal opfylde kravene: Kunne oversætte et subset af C kode til et syntetiserbart VHDL. Og bør være modulært nok til at kunne udviddes til andre HDL sprog. Udskrive en repræsentation af datavejen som blokdiagram. Udskrive en repræsentation af tilstandsmaskinen som tilstandsdiagram. Være POSIX-portabelt og skal kunne eksekveres på de mest populære operativsystemer. Ydermere vil vi inkludere et par illustrative cases. 4

11 1.2 Afgrænsninger Fælles Som allerede antydet i problemformuleringen, er C valgt som det konventionelle programmeringssprog, der skal kunne oversættes til et HDL. C betegnes ofte som et højniveau low-level sprog: Fordi C er meget maskine-nært og håndterer de samme indfødte datatyper som en computer gør, tilbyder C ikke meget mere end en mere menneskevenlig syntax, end hvad assembler gør. Derfor kaldes C også for den portable assembler da sproget kun er niveauet over assembler - men ikke maskinspecifik. Således reflekterer C kode godt, hvad computeren rent faktisk foretager sig, og betegnes derfor som low-level af softwarefolk. -Men set i forhold til HDL s som Verilog eller VHDL, er C højniveau til sammenligning. Hvor andre sprog tilbyder store biblioteker og facilliter for at gøre programmørens liv lettere, er C som sagt mere minimal. C sproget passer derfor fint til vores formål: C er brugbart men hverken for stort eller kompleks. Oversættelse af Java eller C++ til digital logik ville hurtigt blive en uoverkommelig opgave for os - både med hensyn til parsing, run-time support og mapping af OOP-koncepter til hardware. Valget af C retfærdiggøres yderligere af, at C fortsat er et af de mest populære sprog[10]. Det står klart at langt fra hele ANSI C89 specifikationen vil kunne understøttes, da mange begreber i C ikke har en direkte hardware repræsentation. Et relevant og stadig brugbart udsnit af C sproget udvælges derfor i projektstarten. For at undgå forvirring mellem standard ANSI C og vores subset, vil vi betegne vores dialekt som C; altså tilnærmelsesvis C. Eftersom C blot er subset af ANSI C, bør C således stadig være gyldig ANSI C; C kode kan altså kompileres med almindelige compilere. For at parse koden, må leksikal- og syntaksanalyse nødvendigvis udføres, mens dele af den semantiske analyse (type check, warnings og errors) udelades. Vi antager altså at input er valid, hvorfor fejlhåndtering udelades. Dette er en rimelig antagelse, da koden forventes testet før oversættelse. Præprocessoren vil ikke indgå i forbindelse med vores parsing, da input kode tænkes kørt i gennem præprocessoren før oversættelse. Vi er kun selv bekendt med VHDL, men vil tilstræbe HDL-neutralitet indtil sidste stadie. Al VHDL-specifik kode pakkes ned i sin egen klasse; at understøtte et nyt HDL bør da kun tage lille indsats. Da vi netop udskriver VHDL kode, vil vi ikke fortage den egentlige logiksyntese eller routing - dette overlades til værktøjer som XST. Dette sikrer også interoptabillitet mellem oversat C og eksisterende HDL kode, samt forbedrer chancerne for adoptering af teknologien for eksisterende hardwareingeniører. Eftersom HLS danner ramme om et aktivt forskningsområde, vil udvikling og implementering af sindrige optimeringsteknikker være et længere og løbende projekt. Af hensyn til tidspres, vil fokus derfor ligge på grundlæggende funktionalitet, hvor optimeringer spiller en mindre rolle. Det skal dog være muligt for brugeren kvantitativt at beskrive den ønskede datavej: 1 eller 2 ALUs, om datavejen skal være rent kombinatorisk, pipelined eller sekventiel. Herved overlades optimerings-ansvaret til brugeren. Dette kræver dog at brugeren har hardware-indsigt nok til at vurdere hvilken datavej der er passende for det givne problem. Generelt forsøger HLS at minimere cost-funktionen og maksimere performance-funktionen. Resource-begrænset, hvor et loft over resourcer er givet. Fokus på minimering af cost. Tidsbegrænset, hvor en maksimal latency er givet. Fokus på at maksimering af performance. Naturligvis skal den semantiske opførsel og betydning af C kode gengives korrekt. Areal, interconnect, effektforbrug. Throughput, latency 5

12 Vi afgrænser os til resource-begrænsning, da vi ellers måtte inkludere timingmodeller af diverse komponenter. C har desuden ikke noget koncept om timing, hvorfor man måtte opfinde nye direktiver til dette formål. Syntese af C kode låner sig bedre til resourcebegrænsning da resourceloftet kan angives udenfor input kildeteksten. 1.3 Målgruppe Fælles Selvom den endelige løsning bør kunne oversætte en algoritme til hardware, kan værktøjet dog ikke tænke af sig selv: En erfaren designer vil kunne skrive mindst lige så optimeret HDL ud fra samme algoritme - givet tid nok. Den endelige løsning er derfor kun ment som et hjælpemiddel - altså blot endnu et EDA værktøj som kan spare udviklingstid. Anvendelsesmulighederne for hardwareingeniører kan tænkes at være: Hurtig prototyping. Fremskynding af udviklingsprocessen for et større design, ved at tage udgangspunkt i noget der virker, men måske ikke er optimalt - og så optimere på det manuelt. Eliminere trivialiteter: Hvis man lige står og mangler en mindre ikke-kritisk delkomponent; fx en testbench. Kode genbrug: Størstedelen af kendte og vigtige algoritmer har en implementation i C. I modsætning til Java og C#, er C-sproget ikke trendy længere - men finder dog stadig bred anvendelse indenfor systemprogrammering og anvendes i mange open source projekter. Programmører udgør derfor også en væsentlig målgruppe. Anvendelsesmulighederne for softwareingeniører kan tænkes at være: Indfødt realtids processing. Codec s og filterkode kan nemmere porteres til hardware. High-performance computing: På trods af FPGA ernes lave clockfrekvens, kan der opnås meget større parallelisme end i computere som clockes meget hurtigere. Nogle få problemer kan drage nytte af denne parallelisme og derved eksekveres hurtigere på FPGA er end på en PC. -Med dedikeret ASIC hardware kan der clockes endnu hurtigere endnu. HLS indgår som et vigtigt trin i HW/SW co-design. For at kunne udnytte hardwaren bedst muligt, må softwarefolket have basal indsigt i hvilke C konstruktioner mappes til hvilken hardware. Det er som sagt, kun få algoritmer som er velegnet til hardware implementering. Numeriske beregninger med flydende tal overlades nok bedst til computere, hvor FPU en allerede er optimeret. Ydermere, da HDL genereres, skal gængse EDA værktøjer stadig bruges: Grundlæggende kendskab til fx Xilinx ISE og ModelSim er derfor også krævet. Vores primære målgruppe er altså hardware- og softwareingeniører, samt de som befinder sig i grænselaget mellem de to. Der kan naturligvis brændes en FPU ned på en FPGA, men FPU er er kendt for at være langsomme og optage et stort areal. Ved at give afkald på præcision, kan integer fixed-point fint bruges i FPGA. 6

13 Kapitel 2 Indledende overvejelser 2.1 Kravspecifikation Fælles Generelle krav: Kunne oversætte et subset af ANSI C til en synkron tilstandsmaskine med tilhørende datavej, beskrevet i VHDL. Hardware komponenter er template baseret. Dette tillader oversætteren at behandle hardwaremoduler mere abstrakt. Dette tillader at brugeren selv kan udskifte den template der implementerer modulet. Fx kan den binære plus operator i C implementeres på flere måder - som en ripple-carry adder, eller carray-lookahead adder. Alle komponenter antages dog kombinatoriske og der er en 1-til-1 mapping mellem C-operatorer og komponenter. Brugeren skal kunne tilsidesætte defaults for datavejen, for herved at beskrive datavejen kvantitativt. Antallet af allokerede komponenter bør kunne angives. Tilstandsdiagrammet for FSM og strukturdiagrammet for datavejen skal kunne udskrives og vises grafisk. Hardware layout: Udover brugerspecificeret input/output, har FSMD top-level modulet clk og global_reset. Det kan tænkes at brugeren selv vil indføre delvise resets, men en komplet reset bør også være mulig. Alle signaler er aktive høje. FSM skal være minimal, og alle beregningselementer ligger i datavejen. Dette tillader at FSM og datavejen udskrives på ren RTL hhv. strukturelt niveau. Herved minimeres HDL modulernes portbeskriveler. FSM kan være mealy eller moore, hvilket bør kunne vælges af brugeren; moore type er default. Da output af top-level modulet altid er registreret, vil FSMD derfor altid være af moore typen. Følgende elementer af C skal understøttes: Datatyper; implementeres som registre i datavejen. Reelt set kun de komponenter som er beregningselementer. Der kan ikke ændres på det nødvendige antal multiplexere eller registre uden at ændre i C-kildeteksten. 7

14 Signed og unsigned heltals variable understøttes; dvs. char, int, short, long. Strukturer, arrays, typedef s og variable erklæret static, er muligt at oversætte til hardware, men vi afgrænser os til ikke at understøtte dem. Floating point datatyper, pointers og unions ikke understøttet. Funktioner: Funktioner understøttes ikke i første proof-of-concept version. void mainhw(void) er top-level funktionen: Heri ligger bl.a. portbeskrivelsen. mainhw kan kalde andre funktioner, men kun mainhw må fortage I/O på FSMD interfacet. Input til vores FSMD er naturligvis signaler. Vi må ikke kontrollere vores eget input (const), og de kan være asynkrone (volatile) og derved ændre sig til enhver tid. Alle input signaler markeres ved at postfixe variabelnavne med $in. Vi styrer selv output, og al output er registreret. Output registre markeres ved at postfixe med $out. Hvis mainhw nogensinde returnerer, træder FSM en ind i et uendeligt loop - indtil en global_reset. Kan inlines eller udskilles som selvstændigt FSMD modul med SLT handshake. Kun inlining tænkes understøttet, og funktioner skal derfor være markeret som inline. Rekursion understøttes ikke. Operatorer: Implementeres som beregningskomponenter i datavejen. Alle de aritmetiske og bitvise operatorer, unære og binære, skal understøttes: +, -, *, /, %, &,, ^, ~, <<, >>. Relationelle operatorer: ==,!=, <, >, <=, >=, &&,,!. Betingelses-operatoren,?:. Dette er den eneste trinære operator i C. Denne implementeres som en multiplexer i datavejen. Assignment operatorerne: =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, =. Pre- og postfix (de)inkrementering. Typecasts og sizeof bør understøttes. Kontrolstrukturer: Svarende til en samling af registre (med forskellig størrelse) - kendt som en record i VHDL. To muligheder: A) Svarende til en samling registre af samme størrelse, B) In-chip BlockRAM på en FPGA. Fraværet af typedefines, strukturer og unions forenkler C parseren; hverved reduceres C til en kontekst fri grammatik. I ANSI C er dollartegn gyldige i identifiers, ligesom underscore også er. Derfor er FSMD altid moore-type, mens selve FSM kan være moore eller mealy. FSMD ens output signaler er hardwired til output registrenes udgange. Disse registre skal behandles specielt: Hvis de ikke aflæses efter en assignment i efterfølgende C kode, må de må ikke optimeres væk. 8

15 Implementeres i FSM. Al control flow skal understøttes: if, switch-case, for, while, do-while, goto. En betingelse opfattes som værende true, hvis blot én bit er høj i returværdien; dette svarer til en OR-gate: Fx, for at teste om char en x er true, behøves en 8-input OR gate. Ingen funktioner i standard biblioteket understøttes. Eksempelvis skal matematiske funktioner skrives selv. C-kildetekst antages at være kørt igennem præprocessoren før hardware oversættelse. Optimeringer: Constant folding, Multiplikation med konstant via bitshifts og adds. Ressourcedeling mellem registre og beregningselementer. 2.2 Løsningsmetode Fælles Groft sagt kan projektet inddeles i 4 trin, som også vist i figur 2.1: 1. Parsing af C-kode og dannelse af et abstrakt syntakstræ. AST et er en mere håndtérbar repræsentation af kildeteksten, da konstruktioner er logisk grupperet, operatorpræcedens incl. paranteser, kommentarer, whitespace, og unødvendig syntaks er blevet håndteret på dette tidspunkt. Ydermere kan træet traverseres, søges i, og muteres langt nemmere end hvis man skulle arbejde direkte med C-kildeteksten. Eftersom vores fokus ikke ligger i de datalogiske aspekter vedr. parsing, vil vi for så vidt muligt benytte eksisterende løsninger. Her tænkes på at bruge en af mange compiler-compilere som eksempelvis Bison/Flex sammen med en eksisterende C grammatik. Alternativt kan parser-trinnet fra en egentlig C compiler bruges. Hvilken fremgangsmåde der er nemmest, vil blive vurderet i projektforløbet, da det ikke er umiddelbart klart, hvilken er mest hensigtsmæssig. 2. AST et angiver input-koden komplet, men det er en orienteret acyklisk graf - en træstruktur - og giver derfor ikke et korrekt billede af kontrol-flowet. Det er dog muligt at udlede kontrol-flowet ud fra AST et; dette flow indfanges netop i en ASMgraf. Vi udleder derfor ASM-grafen udfra AST-træet. 3. Hardwaren tænkes strengt delt op i en datavej og tilstandsmaskine. Datavejen og top-level modulet beskrives på strukturelt niveau, og tilstandsmaskinen på RTLniveau. Datavejs- og top-level komponenterne kan beskrives som grafer, hvor noderne er komponenter og signaler repræsenteres som orienterede kanter mellem noderne. Tilstandsmaskinen er en afledt komponent som derudover indeholder tilstandsdiagrammet. Den interne hardwarerepræsentation kan således tænkes som en 2-tupel: Fx via CORDIC algoritmen som kun benytter shifts og adds til at beregne eksponentialfunktioner, logaritmer, multiplikationer, divisioner og kvadratrødder. Brace/tuborg-blokke i C. Bison og Flex er open-source kloner af Yacc og Lex. Dermed er en komponent en graf, som selv kan indeholde subgrafer/subkomponenter. 9

16 (FSM, D). Den egentlige oversættelse fra software til hardware sker da ved oversættelse af ASMgrafen til HW-grafen. Denne oversættelse kan ske med forskellig grader af optimering: (a) PoC : Naiv oversættelse hvor der er stor korrespondance mellem C og genereret hardware. Dette tillader udsættelse af en mere sofistikeret ASM HW oversætter, så en komplet oversætter kan realiseres inden. (b) Ressourcedelt: Her tages afsæt i litteraturen fra DTU kurset Indlejrede systemer samt Jan Madsens forlæsninger om hardware compilere. Denne mere optimerede ASM HW oversætter påbegyndes efter PoC. 4. Generering af VHDL output. Dette kan gøres ved hjælp af en række foruddefinerede templates. HW-grafen traverseres og når en given komponent mødes, udskrives passende VHDL kode. Vores target sprog er ANSI C, men vi vælger at programmere løsningen i C++98. Der er flere grunde hertil: C++ er portabelt, multi-paradigmatisk, bagudkompatibelt med C samt har et rigt standard bibilotek. Desuden har vi eksisterende erfaring med C++ og det er betryggende at vide at BOOST-bibliotekerne kan inkluderes, hvis STL ikke rækker. Til rendering af datavejen og tilstandsdiagrammet kan GraphViz bruges. AST ASM input.ct HW C-tilde parser Front-end AST DOT outputast.dot HW Back-end ASM HW DOT outputd.dot outputfsm.dot VHDL ASM DOT outputasm.dot VHDL filer Figur 2.1: YACHT s arkitektur. Proof of Concept. 10

17 2.3 YACHT etymologi og logo Mark Principperne for HLS blev grundlagt i 1980erne, og der findes derfor flere HLS værktøjer i dag. Vi programmerer netop endnu en C-til-Hardware oversætter, hvorfor det kun er passende at døbe programmet YACHT - Yet Another C-to-Hardware Translator. Slår vi op i hacker jargonen : Yet Another: adj. [From Unix s yacc(1), Yet Another Compiler- Compiler, a LALR parser generator] Figur 2.2: Logo for YACHT. 1. Of your own work: A humorous allusion often used in titles to acknowledge that the topic is not original, though the content is. As in Yet Another AI Group or Yet Another Simulated Annealing Algorithm. 2. Of others work: Describes something of which there are already far too many. En yacht er som bekendt et dyrt og luksuriøst motorskib, hvorfor det nautiske tema fortsættes ved at give YACHT en redningskrans som logo. 2.4 Aktivitetsplan Fælles Aktivitetsplanen fremgår af efterfølgende side. esr/jargon/html/y/yet-another.html 11

18 YACHT Ganttdiagram januar 2010 februar 2010 marts 2010 april 2010 maj 2010 juni 2010 Uge 3 Uge 4 Uge 5 Uge 6 Uge 7 Uge 8 Uge 9 Uge 10 Uge 11 Uge 12 Uge 13 Uge 14 Uge 15 Uge 16 Uge 17 Uge 18 Uge 19 Uge 20 Uge 21 Uge 22 Uge 23 Uge 24 Uge 25 januar 2010 februar 2010 marts 2010 april 2010 Projektramme maj 2010 juni 2010 [ 90 dage ] Uge 3 Uge 4 Uge 5 Projektforberedelse Uge 6 Uge 7 Uge 8 Uge 9 Uge 10 Uge 11 Uge 12 Uge 13 Uge 14 Uge 15 Uge 16 Uge 17 Uge 18 Uge 19 Uge 20 Uge 21 Uge 22 Uge 23 Uge 24 Uge 25 [ 16 dage ] MRP,JM 4 Problemanalyse [ 4 dage ] Problemformulering [ 4 dage ] MRP,JM Afgrænsninger [ 4 dage ] MRP,JM Målgruppe [ 3 dage ] MRP,JM [ 14 dage ] Litteratursøgning MRP,JM [ 14 dage ] Indledende overvejelser [ 14 dage ] Løsningsmetode MRP,JM Kravspecifikationer [ 10 dage ] MRP,JM [ 29 dage ] Proof of concept [ 13 dage ] Parsing af C MRP [ 9 dage ] Primitiv DOT Symboltabel [ 1 dage ] MRP Hardware repræsentation [ 10 dage ] MRP,JM [ 9 dage ] Generel DOT VHDL output [ 4 dage ] MRP [ 5 dage ] AST->HW MRP,JM [ 27 dage ] Videreudvikling af PoC [ 27 dage ] Allokering/binding MRP,JM [ 18 dage ] Schedulering MRP,JM [ 16 dage ] Afslutning [ 16 dage ] Rapportskrivning Tests og cases [ 7 dage ]

19 2.5 Motiverende eksempel: Greatest Common Divisor Mark GCD kan implementeres på flere måder - den simpleste udgave er Euclids subtraktionsbaserede algoritme: 1 uint gcd ( uint a, uint b) { 2 if ( a == 0) return b; 3 while (a!= b) { 4 if (a > b) a = a-b; 5 else b = b- a; 6 } 7 return a; 8 } Vi ser at gcd funktionen tager to unsigned integers som input og producerer et output. Et funktionskald på en computer er veldefineret: Funktionen kører kun når kaldt, og når den kaldes er alle argumenter klar til aflæsning. Ligeledes véd omverdenen (caller, som fortog gcd-kaldet) når gcd() er færdig og får automatisk returværdien. a TOP b req global_reset clk GCD c ack Figur 2.3: Entiteten for GCD som hardware modul. Når implementeret i hardware, vil modulet altid køre og vi behøver derfor kontrolsignaler til at fortælle det hvornår argumenter er klar til aflæsning og beregningen kan starte. Ydermere skal GCD modulet fortælle omverdenen hvornår beregningen er færdig og resultatet kan aflæses. Dette kan opnås med et velkendt request/acknowledge handshake. Handshaket er nødvendigt for at opnå veldefineret opførsel. Enten skal handshaket være implicit med en underforstået protokol, ellers skal vi skrive eksplicit C kode som implementerer det. Sidste mulighed er overkommelig, og giver langt højere fleksibillitet: 1 inline uint gcd ( uint a, uint b) { 2 if ( a == 0) return b; 3 while (a!= b) { 4 if (a > b) a = a-b; 5 else b = b- a; 6 } 7 return a; 8 } 9 10 void mainhw ( void ) { 11 const volatile bool req$in ; // Input signaler 12 const volatile uint a$in ; // const volatile uint b$in ; // bool ack$out ; // Output registre 16 uint c$out ; // while (1) { 19 ack$out = 0; // Intet er beregnet færdigt endnu 20 while ( req$in == 0); // Vent til request modtages ( tom while - body ) 21 // Request er nu modtaget, dvs. a og b står klar til at vi aflæser dem 22 13

20 23 c$out = gcd ( a$in, b$in ); // Kopiér a og b, og udfør beregningen 24 // Beregningen er nu færdig, og korrekt resultat står nu på c porten 25 ack$out = 1; // Fortæl omverdenen at resultatet står klar på c porten while ( req$in == 1); // Vent til Master har aflæst c porten 28 // Request er lav nu igen ; vi starter forfra 29 } return ; // Hertil når vi aldrig 32 } Efter inlining fås: 1 void mainhw ( void ) { 2 const volatile bool req$in ; // Input signaler 3 const volatile uint a$in ; //... 4 const volatile uint b$in ; // bool ack$out ; // Output registre 7 uint c$out ; // while (1) { 10 ack$out = 0; // Intet er beregnet færdigt endnu 11 while ( req$in == 0); // Vent til request modtages ( tom while - body ) 12 // Request er nu modtaget, dvs. a og b står klar til at vi aflæser dem { // Inlining introducerer nyt scope 15 uint a = a$in ; // Kopiér a og b, og udfør beregningen 16 uint b = b$in ; 17 if ( a == 0) { c$out = b; goto end ;} 18 while (a!= b) { 19 if (a > b) a = a-b; 20 else b = b- a; 21 } 22 c$out = a; goto end ; 23 end : 24 } 25 // Beregningen er nu færdig, og korrekt resultat står nu på c porten 26 ack$out = 1; // Fortæl omverdenen at resultatet står klar på c porten while ( req$in == 1); // Vent til Master har aflæst c porten 29 // Request er lav nu igen ; vi starter forfra 30 } return ; // Hertil når vi aldrig 33 } NB Gennem hele rapporten anvendes ovenstående inlinede version af GCD med handshaket som løbende eksempel. 14

21 Kapitel 3 Parsing af C Jesper Parsing er det første trin i enhver compiler. Parsing er den proces hvor en tekst tillægges mening. Mere specifikt, forsøges teksten underlagt den grammatiske struktur for et givent formelt sprog - fx C. Hvis input teksten ikke overholder grammatikken, meldes der om syntaksfejl. I tilfælde af korrekt syntaks, dannes ofte en datastruktur kaldet et abstract syntax tree (AST). Et AST er hierarkisk opdelt og repræsenterer blot teksten som et træ. I YACHT vil input C-kildeteksen blive omdannet til et AST. Parsing af C er en nødvendig praktikalitet, men ikke projektets fokus, hvorfor funktionaliteten af en parser kun vil blive gennemgået kort - for helhedens skyld. Vi har outsourcet denne del til parser-generatoren ANTLR, som beskrevet i afsnit 3.1. Oftest anvendes ordet parsing som en paraplyterm for både den leksikalske (lexing) og syntaktiske (parsing) analyse: Den leksikalske analyse grupperer de enkelte karakterer i input teksten i logiske tokens. Den syntaktiske analyse forsøger da at underlægge strømmen af tokens, en grammatisk struktur. Som parseren genkender sætninger, tilføjes de som træform til AST et. Dataflowet for denne process kan ses i figur 3.1. Lexer- og parserregler defineres ofte via regulære udtryk som netop matcher et (uendeligt) sæt af mulige karaktér- eller tokensekvenser. I et programmeringssprog haves fx følgende expression-statement: sum = ;. -Efter leksikalanalysen er foretaget, har vi fået delt teksten delt op i tokens - som vist i tabel 3.1. Tekst Token sum Identifier = Equals 1337 Integer + Plus 2 Integer ; Semicolon Tabel 3.1: Resultat af en leksikal analyse af sum=1337+2;. Lexer Karaktérstrøm Tokenstrøm Parser AST Figur 3.1: De forskellige trin i parsing processen. En parser kan skrives fra bunden af, eller man kan bruge nogle af de allerede udviklede værktøjer som automatisk generer parseren udfra en grammatik. 15

22 3.1 Research og vurdering af eksisterende parserer Fælles Som sagt er det langt nemmere at arbejde med C-kildetekst repræsenteret som et AST end med kildeteksten direkte. Har vi først en fungerende parser, skal den udbygges til at generere AST: Ved invokering af bestemte parserregler, udsendes der samtidigt også AST-noder. Detaljer om disse AST noder beskrives i kapitel 4. ANSI C er vidt udbredt og der findes derfor mange C parsere allerede; det ville være spild af tid, at skrive vores egen. -Dette kan vi tillade os, da projektets fokus - som sagt - ikke ligger på parsing. Vi søger derfor et open-source projekt, der opfylder kravene: Fungerende parser skrevet i C eller C++, Så lille og overskuelig kodebase som muligt, ANSI C-konform, Direkte AST output er foretrukken, BSD licens foretrukken, da YACHT udgives under denne. Vi kan forstille os to tilgange for at opnå en parser til vores formål: 1. Hacke parser-trinnet ud af en eksisterende C compiler. (a) Der findes mange C compilere i variende størrelse og omfang. Da vi nødvendigvis kommer til at foretage ændringer i parseren, er hovedkravet her, at vi kan nå at sætte os ind i compileren og fortage vores ændringer indenfor de 3-uger som der er afsat. -Det er ikke meget tid når compilere er notorisk kendte for at være både store og komplekse stykker software. Fordelen er, at vi direkte får adgang til den interne IR. (b) God dokumentation er derfor nødvendig før compileren tages i betragtning. 2. Anvende en compiler-compiler sammen med en eksisterende C grammatik. (a) Vi er interesserede i compiler-compilers, som generer en lexer og parser for en given grammatik. Det er vigtigt at indse, at den auto-generede parser ikke kan give os AST et med det samme da der ikke er nogen direkte korrespondance mellem AST og parse-tree et. Hvad der indgår i AST et er formålsbestemt, og vi kan derfor ikke forvente at en compiler-compiler kan gives os AST et direkte. For at opnå AST et vil vi selv være nødt til at skrive C++ kode manuelt, som udsender AST-noder ved invokeringen af parserregler. Altså potentielt mere arbejde, end hvis vi heldigvis fandt en lettilgængelig C compiler. (b) Eksistensen af en C grammatik kompatibel med den givne compiler-compiler, er helt nødvendig før compiler-compileren tages i betragtning. Vi har evalueret flere open-source compiler-(compilers): Dog skal man passe på hvis parseren backtracker. Her kan parseren antage at en regel er matched, hvilket senere viser sig at være forkert. Vi skal sørge for at AST-noder kun udsendes når vi er sikre på at reglen er matched. ANTLR parser-generatoren er sikker i denne forstand; actions (brugerdefineret inlinet kode) udføres kun når reglen er garanteret matched. Intermediate Representation - en fællesbetegnelse som kan variere i repræsentation. RTL, AST eller pseudo-assembler er typiske. 16

23 GCC Velkendte GNU Compiler Collection. GCC er en stor og omfattende compiler, med understøttelse af flere sprog. GCC har flere forskellige interne repræsentationer, bl.a. sprog-specifikke ASTs, GIMPLE og RTL. GCC har online dokumentation men det er svært at finde et angribspunkt blandt de store mængder kode. GCC-XML Orienteret mod C++, hvilket er acceptabelt grundet C++ s bagudkompatibeltet med C. Kun variabeldefinitioner udskrives til XML; GCC-XML kan derfor slet ikke bruges. LCC Little C Compiler er en C compiler på 12kSLOC. LCC er kendt for at være nem at portere til andre arkitekturer, da dens backend lader sig nemt udskifte; vi er dog interesseret i front-end en. Den interne opbygning er beskrevet i bogen A Retargetable C Compiler: Design and Implementation, på 592 sider. Kildekoden, uden den tilhørende bog, er ulæselig: Tætskreven C kode med ikke-indlysende variabelnavne, og function-pointers overalt. Vi har desuden ikke kunne få fat i bogen. Hvad vi kan læse af den dokumentation som er online og gratis, har LCC en form for pseudo-assembler som IR, og danner tilsyneladende ingen AST struktur. pycparser Komplet C parser med AST output, cirka 5kSLOC med flere små illustative eksempler. Ville være perfekt, hvis ikke den var skrevet i python: Selvom det er muligt at integrere python med C++, er det ikke nogen pæn løsning. ctool Skrevet i C, producerer AST og opbygger symboltabel - som dog ikke vises i demonstationsprogrammet. Forladt projekt og ingen dokumentation. synopsis Skrevet i en kombination af C++ og python. Dårlig dokumentation. Flex/Bison Open-source kloner af Lex/Yacc. Der findes netop en Yacc/Lex-kompatibel C grammatik, men det står ikke klart hvordan manuelle actions kan udføres ved opbygning af AST et. Cilk2C Skrevet i C og virker når kaldt med./cilk2c -ast test.c fås AST direkte. 23kSLOC, men udgør kun en lille del af det samlede Cilk projekt; der er ingen dokumentation af cilk2c s interne dele. Dog med fungerende tekstuel AST output, er cilk2c en reel kandidat. ANTLR ANother Tool for Language Recognition, en intuitiv LL(*) parser-generator med langt lavere indlæringskurve end Flex/Bison. ANTLR selv er skrevet i Java, men kan udskrive parserer i mange andre sprog, for en given grammatik. Yacc C grammatikken er blevet porteret til ANTLR. (Video) Tutorials og begrænset dokumentation findes online; fuld ANTLR-dokumentation findes i bogen, The Definitive ANTLR Reference, som vi har kunnet låne. Desuden findes en grafisk grammatikdebugger, ANTLRWorks. Clang En frontend for LLVM-infrastrukturen for C-lignende sprog; rival for GCC. Skrevet i C++ med god online dokumentation. Ligesom GCC kan det være svært at finde et angribspunkt. Ligeledes, med en stor kodebase, kan det være svært at tilpasse til vores formål

24 Elsa En komplet C++ parser baseret på egen parser-generator. Ligesom Cilk2C kan AST fås med det samme ved./ccparse -tr printast -tr c_lang test.c. 67kSLOC og god dokumentation. Alle værktøjer har fordele og ulemper og ingen formår at opfylde alle vores krav. Elsa, ANTLR, Cilk2C og pycparser er blandt de som kommer tættest. ANTLR vælges udfra det grundlag, at det typisk er nemmere selv at skrive kode, end at forstå andres. ANTLR er endda udgivet under BSD licensen. Den dertil hørende bog, The Definitive ANTLR Reference, er velskrevet og har en række gode eksempler. -Med ANTLR valgt, skal vi - udover at investere tid i at forstå ANTLR-sproget og C grammatikken - selv tilpasse C grammatikken samt bygge AST og symboltabellen op. 3.2 Kort om ANTLR Jesper ANTLR-projektet startede i 1989 og er stadig under aktiv udvikling. Terence Parr, professor ved University of San Francisco, er ophavsmanden bag ANTLR. ANTLR er en parser-generator som automatiserer opbygningen af sproggenkendelse - kort sagt er det et program som udskriver parser-programmer. Ud fra en formel sprogbeskrivelse vil ANTLR generere et program, som bestemmer om sætninger tilhører det pågældende sprog. Ved at tilføje kodestumper (manuelle actions) til grammatikken, kan parseren udvides til at kunne outputte AST et. Kodestumperne angiver brudstykker af AST et ud fra input sætninger. ANTLR er egnet til simple og komplicerede sproggenkendelses- og oversættelsesproblemer. Generering og genkendelse af sætninger fås ved at gennemgå den implicitte træ struktur - fra de mest abstrakte begreber i roden til ordforråds symboler på bladene. Hvert undertræ repræsenterer en frase af en sætning og mapper direkte til en regl i grammatikken - dvs. at der bliver tjekket om inputtet overholder grammatikkens regler. ANTLRs eget sprog læner sig meget op ad Extended Backus-Naur (EBNF) notationen. EBNF er en metasyntax og en formel måde at beskrive kontekstfrie sprog på. EBNF notationen er den mest udbredte måde at beskrive grammatikken af et programmeringssprog på. Karakter... h ø j d e = ; \n... Tokens... ID = INT ;... Figur 3.2: Token- og karaktérbuffer. Helt grundlæggende for ANTLR har vi to typer af regler, parser- og lexerregler. Hvis en lexerregel matcher det pågældende tekstinput, samles dette i et token - jævnfør figur 3.2. Et token er en lille afgrænset logisk sekvens af tegn, fx et CIFFER: Et ciffer består netop af ASCII-tegnene 0 til 9. Et eksempel på en regl der matcher ét ciffer: 1 CIFFER : ( ) ; Udelades typedefines, bliver ANSI C faktisk kontekstfri: Da afhænger typecasts ikke længere af konteksten, for kun indfødte datatyper understøttes. Dermed er C kontekstfri. 18

25 Interval operatoren.. er specifik for ANTLR. Det betyder, logisk nok, bat vi i eksemplet har tallene fra 0 til og med 9. Et HELTAL består nødvendigvis af 1 eller flere ciffre, svarende til følgende regl: 1 HELTAL : CIFFER +; Kleene cross + siger at der kan være en eller flere. Et andet ækvivalent alternativ for HELTAL s: 1 HELTAL : CIFFER CIFFER *; Kleene star * er en unær operator og betyder - enten nul eller flere. For at beskrive decimaltal i C bruges reglen: 1 DECIMAL_TAL : ( CIFFER *) IntegerTypeSuffix? ; I denne regel bliver vi introduceret for nogle nye operatorer - nemlig pipe og optional?. Pipe-operatoren angiver alternativer, og optional-operatoren betyder at det kan være til stede, altså gentages 0 eller 1 gange. Der kan ydermere grupperes med paranteser, så + og * kan virke på mere end et token eller subregl. Det ses at hver regel defineres ved at starte med : og de forskellige alternative regler separeres med. En grammatisk regel er en navngivet liste af en eller flere alternativer. Man læser HELTAL som en liste af CIFFER-regler. Ligeledes kan man læse DECIMAL_TAL på følgende måde: Følgende input matcher DECIMAL_TAL: Ét enkelt 0, Ét af tallene mellem 1 og 9, Ét af tallene mellem 1 og 9 efterfulgt af 0 eller flere ciffre (f.eks. 39). Et eksempel på en lille gramatik kan ses nedenfor: 1 grammar Expr ; 2 3 prog : stat + ; 4 5 stat : expr NEWLINE 6 ID = expr NEWLINE 7 NEWLINE 8 ; 9 10 expr : multexpr (( + - ) multexpr )* 11 ; multexpr 14 : atom ( * atom )* 15 ; atom : INT 18 ID 19 ( expr ) 20 ; ID : ( a.. z A.. Z )+ ; 23 INT : ; 24 NEWLINE : \r? \n ; 25 WS : ( \t \n \r )+ { skip () ;} ; I ANTLRs grammatik består alle lexerreglerne af store bogstaver og parserreglerne af små. I definitionen af parser reglerne kan vi se at der er et grammatisk design mønster for aritmetiske udtryk. Dette mønster foreskriver en serie af regler - én for hver operators 19

26 præcedens niveau og netop én for det laveste niveau. Det laveste niveau i vores eksempel er atom-reglen, hvilket f.eks. kan være et heltal. I det aritmetiske udtryk starter vi øverst med at have en regel expr som repræsenterer et komplet udtryk. Reglen expr vil matche operatorerne med den svageste præcedence - her plus og minus - og vil referere til en regel som matcher underudtryk for operatorer med den næst højeste præcedens. Her vil det være gange operatoren, som ovenfor kaldes multexpr. På denne måde kan man fornemme den hierarkiske opdeling i grammatikken. Ser vi på den sidste del, nemlig lexical niveauet, så kan vi se at den definerer ordforrådet. Typisk refererer disse regler til karakterer eller bogstaver, ikke tokens, hvilket parser reglerne håndterer. Reglen om WS(mellemrum/whitespace) er den eneste som indeholde handlingen (skip();) som fortæller ANTLR at den skal smide ud hvad den har matchet og kigge efter andre nye tokens. Vha. den lille grammatik ovenfor kan vi nu inputte og får det parse træ som kan ses i figur 3.3. Figur 3.3: Parse træ for 3+4. Parsetræet er lavet med ANTLRWorks og selv for denne simple grammatik og inputsekvens kan det ses at parsetræet er rimelig omstændigt, så for store grammatikker og inputs vil de være uoverskuelig, hvilket betyder at det ikke er noget man bruger til videre analyse - mere til debugging. Det kan ses ud fra figur 3.3 at bladene er input symbolerne og at noderne som ikke er blade er regel navnene. Øverst i grammatikken ses reglen prog, som indikerer at 3+4 overordnet er en prog. Går vi dybere ned i træet kan vi se at det er er stat, hvilket også er en expr efterfulgt af en ny linje, osv. Parsetræet indeholder derfor information om hvordan der bliver navigeret igennem de forskellige grammatik regler som matcher til inputtet. Disse træer kan sammenlignes med de meget simplere AST s som vi vil gøre i det næste afsnit. 3.3 C-grammatikken Jesper ANTLR kan outputte direkte til AST, men kan ikke rigtigt benyttes da ANTLR-AST s kun kan besøges af ANTLR s egen tree walker. Vi hverken kan eller vil skrive al vores kode i ANTLR-miljøet, og vi kan ikke bruge ANTLR s egne generelle AST noder. Vi må derfor oprette og outputte vores egne AST nodes manuelt. På ANTLRs hjemmeside har vi fundet den fulde udgave af grammatikken for ANSI C89. Vi opererer kun med et subset af Netop de tokens som kan dannes ud fra lexival niveauet. (Dog er den øverste node, <grammar eval>, noget ANTLRWors har tilføjet 20

27 C, som vi kalder C, og derfor har vi tilpasset den oprindelige C-grammatik til det YACHT skal kunne håndtere - og denne kan senerehen udbygget, da de dele af ANSI C vi ikke skal bruge - blot er blvet udkommenteret. Som sagt kan vi ikke bruge ANTLR til at outputte AST noderne og vi har indskrevet nogle C++ handlinger (actions) til at danne vores egne AST-noder. I figur3.4 ses en oversigt over matching reglerne for C-grammatikken. Int foo = 2; Declaration Declarator Declaration specifiers Initializer Init_declarator-list Init_declarator Figur 3.4: I figuren ses en oversigt over regel matching for C-grammatikken Et eksempel på et AST træ udfra den lille grammatik fra kapitel 3.2 kan ses i figur 3.5. Vi er ligeglade med parse træet, da vi kun er interesseret i inputtet og ikke behøver at vide hvornår hvilke regler bliver invokeret. Hvis man sammenligner figur 3.3 med figur 3.5 ses det tydeligt at AST et er langt mere konkret end parse træet, og for større problematiker vil parse træet - mildt sagt - være fuldstændig uoverskueligt. Som tidligere nævnt vil den hirakiske opdeling i grammatikken gøre at vores AST vil blive outputtet rigtigt, hvor bladene f.eks. vil være konstanter og vi op igennem træet stiger i præcidens niveau i forhold til de grammatik regler vi har for vores C Figur 3.5: AST af eksemplet med 3+4. Plus-noden har to børn, konstanterne 3 og 4. Ved succesfuld matching af en parser regel invokeres C++-kode som opretter en ny AST-node. Reglen returnerer denne AST-node og det samme gør sub-regler. En regel kan derfor kalde sub-regler og tilføjer retur værdien af disse som børn til dens egen ASTnode. Når en ny AST-node oprettes kaldes konstrukturen med typen og børnene som argumenter. Inde i konstruktoren opdateres børnene således at de ved hvem deres parent er. Nedenfor kan ses et udklip fra vores ANTLR-grammatik med de indførte actions for + og - operatorerne: 1 additive_expression returns [ expr *e] 2 : arg1 = multiplicative_expression {$e = $arg1.e;} 3 ( op= + argn = multiplicative_expression 4 {$e = new exprbinaryop (( const char *) $op.text ->chars, OP_ARITH_BINARY_ADD, $e, $argn.e);} 5 op= - argn = multiplicative_expression 6 {$e = new exprbinaryop (( const char *) $op.text ->chars, OP_ARITH_BINARY_SUB, $e, $argn.e);} 7 )* 8 ; Ser vi på ANTLR s måde at håndtere fejl på sker dette ret intelligent, for hvis vi glemmer et semicolon i vores input.ct-fil vil ANTLR ikke gå i stå. Nedenfor kan ses et eksempel på noget input hvor vi har åbenlyse syntaksfejl: 1 int a // mangler semicolon 2 int b // mangler semicolon 3 int c; ANTLR vil da give følgende fejlmeddelse: 21

28 1./ input. ct (300) : error 10 : Missing token, at offset 0 2 near [ Index : 0 ( Start : 0- Stop : 0) = < missing ; >, type <15 > Line : 300 LinePos :0] 3 : Missing ; 4./ input. ct (300) : error 10 : Missing token, at offset 0 5 near [ Index : 0 ( Start : 0- Stop : 0) = < missing ; >, type <15 > Line : 301 LinePos :0] 6 : Missing ; Som det kan ses, er dette acceptabel fejlbeskrivelse, hvis man tager til eftertanke at vi ikke selv udfører fejlhåndtering og at parseren er autogenereret ud fra vores Cgrammatik. Parseren som ANTLR har genereret, er smart nok til selv at indsætte de forsømte semicolons. Så selv hvis brugeren har lavet en fejl i input.ct, vil YACHT give besked herom og forsøge at forsætte. Til slut er det er værd at bemærke, at ud fra Ctilde.g på cirka 800 linjer, genererer ANTLR en C-parser i C på cirka 25kSLOC: 1 ~/ NetBeansProjects / yacht - dtu / antlr$ wc -l Ctilde *\.{ c, h} sort -n CtildeLexer. h CtildeParser. h CtildeLexer. c CtildeParser. c totalt 22

29 Kapitel 4 Abstract Syntax Tree Mark Et AST kan opfattes som kildeteksten, blot på træ-form, hvorfor der er stor lighed mellem de to. Et AST er abstrakt i den forstand, at det repræsenterer kildeteksten uden nogen eksplicit syntaks. Det samme program, skrevet i flere sprog, har derfor - ideelt set - samme AST. Vi véd at grim formattering af kildeteksten eller underlig syntaks kan ofte forpurre læsbarheden af kildeteksten. -Disse er netop destilleret væk i AST-repræsentationen hvorved en mere klar betydning af kildeteksten opnås. Brugen af ASTs har flere fordele: Tillader os kun at fokusere på det vigtige, nemlig selve koden. Kommentarer og unødig whitespace udelades ofte. Operatorpræcedens og associativitet er blevet afgjort i AST et. Vi behøver ikke længere at slå op i præcedens-tabeller for at afgøre hvilken operation der eksekveres først. Parenteser og block-scopes angives som nestede træer. Den største fordel fås i kraft af sin datastruktur: Fordi det er et træ, kan vi traversere, søge efter bestemte noder og mutere det - langt nemmere end at arbejde med kildeteksten direkte. 4.1 Rationale for hierarkisk nedarvning af ASTnoder Mark Tager vi fx udtrykket 2-3*4, kan vi forstille os tre ASTnode-typer: exprconst, minus og gange. exprconst vil altid være et leaf, da det ingen børn har, mens minus og gange - som alle andre binære (infix) operatorer - altid har præcis to børn. Disse (hypotetiske) ASTnoder har altså uforlignelige forskelle som gør at vi ikke kan konstruere én node som implementerer dem alle. Vi ser dog, at minus og gange har noget tilfælles - de har begge 2 børn. For minus kunne vi kalde børnene minuend og subtrahend; tilsvarende Vigtigt er det dog at pointere, at forskellige sprog har forskellige konstruktioner, og derfor forskellige ASTs. C++ har fx konceptet om klasser, som ikke findes i C. AST et for et C++ program der benytter klasser, kan derfor ikke sammenlignes med ASTet for det ækvivalente program i C. Når vi her skriver samme, menes brugen af de samme grundlæggende koncepter, som fx assignments, loops og control-flow. Euclids gcd-algoritme vil fx have samme AST i Pascal som i C. Dette er netop den grundliggende tanke bag pretty-printing: AST konstrueres ud fra grim/obfuskeret kode, pænere (pretty-printed) kode udskrives derefter fra AST et. 23

30 multiplikator og multiplikand for gange. -Det er dog fordelagtigt, at kalde børnene noget ens og mere generelt, som arg1 og arg2, for alle binære operatorer. Dette tillader os at lave en fælles klasse, exprbinaryop, som netop har de to børn, arg1 og arg2. Vi kunne nu oprette en exprgange som arver fra exprbinaryop, og dermed udspecificerer at der er tale om en multiplikation - dette er dog ikke gjort: Opførslen af de indførte bekvemmeligheds metoder viser sig så ens for de alle binære operatorer, at vi har valgt ikke at opdele i yderligere klasser. For da at kunne skelne mellem exprbinaryop s tilknyttes en enum, etype, til hver ASTnode. En vigtig bekvemmelighedsmetode er eval. Tager vi igen 2-3*4 som eksempel, vil minus-noden være rod i det tilsvarende AST - lad os kalde den n. Vi vil da gerne udføre n->eval(); og få resultatet 10 returneret. eval hos minus-noden, n, går ind og kalder sig selv rekursivt på n s børn - i stil med: 1 return n->arg1 -> eval () - n->arg2 -> eval (); Vi véd at n er en pointer til minusnoden, altså en pointer til en exprbinaryop. Men for at n->arg1->eval() skal lykkes, skal eval være defineret for konstanten 2, en exprconst. Vigtigere er dog, hvad arg1 og arg2 skal være pointere til, exprconst eller exprbinaryop? Svaret er begge dele og mere til; alt hvad der er et expression og derfor også kan evalueres. Det giver derfor mening at oprette en polymorfisk base klasse, expr, som betegner en generel expression-node. expr-klassen foreskriver da - via en pure virtual metode - at alle klasser der arver fra denne, skal implementere deres egen eval-metode, da der ikke kan gives en fælles og generel metode, men at alle expressions har en. Alt dette indikerer, at vi med fordel kan modellere ASTnoder med hierarkisk nedarvning. 4.2 AST modellering Mark Grundlæggende bliver hvert koncept i C mappet til en ASTnode, som vist i figur 4.1. Der gælder for alle ASTnoder: 1. At de véd hvilken type de selv er, vha. en node_t. 2. At de véd hvem deres forælder er (parent); må kun have én forælder. 3. At de kan besøges af en visitor (acceptvisit). Jvf. appendiks A for en beskrivelse af visitor mønstret. 4. At de kan tegnes som noder i DOT og derfor har en label (getdotlabel). 5. At et barn kan erstattes med et andet (replacechild). Dermed fås også replacemewith. En stmt repræsenterer alle statements. 1. En statement kan finde det block-scope som den tilhører (getparentblock). Dette ville ellers have ledt til et mere komplet arvehieraki, på bekostning af mere C++ kode. Omgang med pointers er nødvendig for at børn i ASTnoder kan pege på polymorfiske objekter. Statements, multiplikation, if-statements, løkker, osv. En node_t kan opfattes som en udviddet enum, med et par bekvemmelighedsmetoder som fx isassignmentop(). Vi minder om at der er mange forskellige assignment-operatorer i C, hvorfor det er smart at indføre sådanne metoder. 24

31 dotable ASTnode stmt supernode expr stmtblock stmtbreak stmtcontinue stmtdowhile stmtfor stmtgoto stmtif stmtlabel stmtreturn stmtreturn_exp stmtswitch stmtwhile exprbinaryop exprconst exprlist exprternaryop exprunaryop exprvar Figur 4.1: Arvehieraki: Alle AST-relaterede klasser arver fra ASTnode. stmtblock indeholder lokale variabeldefinitioner - og kan derfor anses som en lokal symboltabel - samt en std::vector af statements. Symboltabellen beskrives i afsnit 4.4. stmt{goto, Label, BREAK, CONTINUE} angiver eksplicit control-flow. Disse forbliver unresolved indtil ASM-grafen konstrueres. stmt{break, CONTINUE} indeholder intet yderligere end hvad de arver. stmtgoto kender kun navnet på den label der skal hoppes til, og stmtlabel kender sit navn og den stmt den peger på. stmtif indeholder if-betingelsen som expr, samt en then-stmt og en potentiel else-stmt. stmt{for, WHILE, DOWHILE} indeholder alle en body-stmt samt en expr-betingelse. stmtfor har desuden en expr til initialisering og en post-body expr. supernode er reelt en dummy-node som der netop eksisterer én af, og som vi manuelt indskyder som root for ASTet. Hvis dette ikke gøres, vil den reelle AST-root node ikke have nogen forælder. Dette er altså for at garantere, at alle stmts har en forælder. 1. Har ét barn. Dvs. vi ikke tager stilling til hvad der hoppes til på AST-niveau. Burde nok omdøbes. Det sværeste ved programmering er jo netop navngivning. 25

32 2. Ignoreres af visitorer, barnet besøges. Der gælder for alle expressions, exprs: 1. At de kan evalueres (eval), med undtagelse af exprvars, som smider en exception i det tilfælde. 2. At de kan udskrives tilbage til en tekstuel repræsentation (tostring). 3. At bitbredde og talrepræsentation kan fås (widthsign). Disse beskrives nærmere i afsnit Øvrige hjælpe-metoder som bruges ved schedulering af hardwareresourcer. Disse beskrives nærmere i afsnit exprunaryop, exprbinaryop, exprternaryop repræsenterer alle de unære, binære og trinære operatorer i C, hvorfor expr specialiseres med hhv. 1, 2 og 3 børn. exprconst, en leaf-node, som repræsenterer konstant-literaler. exprvar, en leaf-node, som repræsenterer variable og indfører metoder til at slå denne op i symboltabellen. Symboltabellen beskrives nærmere i afsnit 4.4. exprlist repræsenterer de lidet kendte expression-lists. 4.3 ASTvisitorNormalize: Mutationer på AST Mark ASTvisitorNormalize benytter visitor mønstret - som beskrevet i appendix A - til at traversere ASTet og fortage mutationer på det. Dette indbefatter normaliseringer, reduceringer og simple optimeringer. Fordi et AST er et træ, og dermed en acyklisk graf (DAG), kan en AST-visitor ikke risikere at ende i et uendeligt besøgs-loop. I modsætning til cykliske grafer som fx ASM, behøver vi derfor ikke at tjekke om en ASTnode er besøgt før. NB Ikke alle de nedenfor beskrevne mutationer er implementeret i YACHT, men kunne blive det. Det er markeret hvilke mutationer som ikke er implementeret Normaliseringer Mark Som sagt er ASTs generelt specifikke fra sprog til sprog. C har en række specielle konstruktioner som vi gerne vil normalisere væk: Vi vil gerne have simple assignments og ingen implicitte sandhedsværdier. Sammensatte assignment-operatorer, normaliseres til almindelige assignments: Eksempel: x += foo; norm = x = (x)+(foo);. Pre- og postfix (de)inkrementeringsoperatorerne - ikke implementeret i YACHT: Eksempel: x = foo++; norm = x = (foo); foo = (foo)+1;. Kun én trinær operator findes; betingelses-operatoren?:. Navne-konventionen for visitorer, er generelt den at den type graf der traverseret nævnes først, efterfulgt af visitor, og sidst, den på grafen påtrykkede funktion. Fx: if(x) {/*...*/} Sammensatte assignment-operatorer: *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, =. 26

33 Eksempel: x = ++foo; norm = foo = (foo)+1; x = (foo);. Nestede assignments - ikke implementeret i YACHT: Eksempel: x = b = 2; norm = b = 2; x = b;. Eksempel: b = (a=1)+ 2; norm = a=1; b=a+2;. Expression-lists - ikke implementeret i YACHT: Eksempel: x = (expr1, expr2, exprn); norm = expr1; expr2; x = exprn; Reduceringer og optimeringer Mark Operationer med nul- og identietselementet: Eksempel: (x)*1 norm = x. Eksempel: (x)*(-1) norm = -(x). Eksempel: x*0 norm = 0. Eksempel: x+0 norm = x. Andre trivielle symbolske reduceringer, unære operatorer: Eksempel: -(-(x)) norm = x. Eksempel: +(x) norm = x. Betragter vi en assignment, a = 2+3;, ses højresiden at være konstant. Hvorfor spilde en adder, når vi ved compile-tid kan se, at resultatet er 5? Constant folding er en almindelig compiler optimeringsteknik, som netop simplificerer disse konstanteudtryk ved compile-tid, for derved at minimere resourceforbruget ved køretid. Da constant folding er så almindeligt og simpelt at implementere, er det også implementeret i YACHT. Eksempel: -(3) norm = -3. Eksempel: 5+(2==2) norm = 6. Eksempel: x = (1==2)? foo : bar; norm = x = bar. Vores implementation af constant folding, er dog ikke perfekt og formår ikke at reducere (x+2)+3 yderligere. Dette beskrives i afsnit Multiplikation og division med 2 n -konstant: Eksempel: x*2 n norm = x<<n, ikke implementeret. Eksempel: x/2 n norm = x>>n, ikke implementeret. Multiplikation med konstant ved addtioner/subtraktioner og shifts; beskrevet i Appendix C.1: Ses sjældent og typisk kun som init-expression i for-løkker, men er gyldige overalt hvor expr s også er. To andre relaterede optimeringsteknikker, Dead code elmination (kan udføres på ASM-niveau) og constant propagation (kan udføres på AST-niveau) er dog ikke implementeret. 27

34 1 { Eksempel: x*5 norm = x<<2 + x, ikke implementeret. Eksempel: x*7 norm = x<<3 - x, ikke implementeret. Konvertering fra kædestruktur til træstruktur af de associative og kommutative operatorer. Dette beskrives i afsnit Eksempel: a+b+c+d norm = (a+b)+(c+d), ikke implementeret. Sæt eventuel 1-faktor udenfor parantes: Dette er baseret på den observation, at symbolsk faktorisering ikke medfører øget træ-dybde. Da paranteser er gratis, og faktorisering netop minimerer antallet af operatorer, kan vi opnå samme latency med færre resourcer. Faktorisering medfører dog introduktionen af multiplikationer som vi netop forsøger at undgå. Dog, er multiplikation med 1 speciel, for her behøves ingen multiplier, kun en negéring. Eksempel: F20+2*F21+F22-F00-2*F01-F02 norm = (((F20+2*F21)+F22))+(-((F00+2*F01)+F02)), ikke implementeret Errata for konstantfoldning Mark Betragt følgende input til YACHT, med det resulterende AST som vist i figur 4.2: 2 int x; 3 int sum1, sum2 ; 4 sum1 = x +(2+3) ; // Reduceres OK 5 sum2 = ( x +2) +3; // Reduceres ikke 6 } Figur 4.2: AST: Constant folding i YACHT misser en reduktionsmulighed. Dette skyldes at ASTvisitorNormalize::visit(exprBinaryOp *n) kun fortager constant folding hvis hele subtræet er konstant og kan evalueres. Indeholder en af børnene en variabel, kan vi naturligvis ikke udføre nogen reduktion da værdien ikke er statisk kendt. Denne problemstilling kan løses ved indførsel af n-ære noder, som beskrevet i afsnit Vi minder om, at en binær operator siges at være associativ, hvis: (x y) z = x (y z). Vi minder om, at en binær operator siges at være kommutativ, hvis: x y = y x. Af associative og kommutative binære operatorer i C, kan følgende identificeres: +, *, &, &&,,, ^. 28

35 1 { Operatorer: Fra kæde til træstruktur Mark Betragt følgende input til YACHT, med resulterende AST som vist i figur 4.3: 2 int a, b, c, d; 3 int sum_chain, sum_ tree ; 4 sum_ chain = a+b+c+d; // Parses til kæde - struktur i AST 5 sum_ tree = ( a+b) +( c+d); // Parses til træ - struktur i AST 6 } Figur 4.3: AST: Højreside af sum_chain-assignment parses som ((a+b)+c)+d, pga. venstre-til-højre associativitet for binær plus. Af figur 4.3 ser vi at højresiden for sum_chain-assignment en fremstår som en kæde af additioner, mens for sum_tree fås en mere balanceret træstruktur. Dette er egentlig forventet - dog uønsket - opførsel: Parseren spytter AST-noder ud som parserreglerne bliver invokeret. Figur 4.3 reflekterer derfor godt hvordan koden faktisk parses. Kæde-strukturen er suboptimal: Den introducerer dataafhængigheder som kan undgås, hvilket kan lede til dårligere hardware: Hvis begge træer blev implementeret direkte kombinatorisk, indses at arealet er ens, at den kritiske vej er længere for kæden. Kæden har derfor ingen forsonende træk, og vi er derfor interesseret i at kunne konvertere denne til en træstruktur. At konvertere fra kæde til balanceret træ, svarer blot til at sætte (strategiske) paranteser; operatoren skal derfor være associativ. At sætte paranteser er en mulighed, men det vil jo netop være at arbejde på det tekstuelle niveau - hvilket er besværligt. Hvis vi indførte n-ære expr-noder ville det være muligt på AST-niveau - med følgende algoritme: 1. Indfang en sammenhægnende gruppe G af den givne associative operator op. 2. Sættet af alle børnene i G tilføjes til en ny-allokeret n-ær op expr-node, O. Interessant nok, er kommutativitet ikke en nødvendighed! De associative operatorer i C er dog også kommutative. String-konkatenering er et eksempel på en operation som er associativ, dog ikke kommutativ. Vi kan nemt overbevise os selv om at kommutativitet ikke er nødvendigt, ved netop at tænke + erstattet med strcat(3) i figur

36 3. Erstat rod-noden i G med O. 4. Inspireret af merge sort, deles O i to halvdele så længe O har mere end 2 børn. Punkt 4 udføres rekursivt for hver halvdel. NB Som sagt er alt dette ikke implementeret i YACHT på nuværende tidspunkt, selvom intet står i vejen for at gøre det. For at opnå træstruktur på nuværende tidspunkt, må brugeren manuelt skrive koden op i træstrukturs-form Virkemåde Mark ASTnoder kan nemt oprettes, da deres payload og børn angives som parametre til constructoren. Alle ASTnoder peger på deres børn ved hjælp af pointers, hvilket tillader at hele subtræer kan byttes om effektivt. At holde styr på pointers er dog besværligt, hvorfor en vigtig bekvemmelighedsmetode er indført, replacemewith: Når vi fortager ændringer på ASTet, ledes der efter bestemte mønstre, eksempelvis to unære negéringer der følger efter hinanden. Mest logisk er det at tjekke for dette tilfælde når vi møder en exprunaryop, altså når ASTvisitorNormalize::visit(exprUnaryOp *n) kaldes. Hvis den mødte exprunaryop-node, n, er en OP_ARITH_UNARY_NEGATE og dens barn også er - så har vi fundet en dobbelt negation, som kan reduceres væk. n->parent Parent kan være alt n->parent Parent kan være alt n - OP_ARITH_UNARY_NEGATE norm n - OP_ARITH_UNARY_NEGATE n->arg - OP_ARITH_UNARY_NEGATE n->arg - OP_ARITH_UNARY_NEGATE n->arg->arg x Sub-expr kan være alt n->arg->arg x Sub-expr kan være alt Figur 4.4: Eliminering af en dobbelt-negation ved udførsel af n->replacemewith(n-> arg->arg);. Når dobbelt negationen er fundet, er det fristende blot at udføre n = n->arg->arg, i den tro om at den første NEGATE er blevet erstattet med sub-træet, x, som kan være alt. -Men da n netop er en lokal pointer, gælder det kun i den metode; denne strategi virker altså ikke. En fungerende strategi, er at lade n s forælder pege på sub-udtrykket - dette er netop hvad replacemewith gør: Når kaldt fra n, fortæller n sin forælder at den selv skal erstattes med argumentet. Efter tjek af dobbelt-negation og replacemewith-kaldet, kan de to nu forbigåede unære NEGATE s, slettes - dette er netop skitseret i figur 4.4. Når n og n s barn er slettet, kan vi ikke umiddelbart besøge videre: Det kunne vi i princippet godt, for vi kunne bare besøge x. -Men hvis vi bare besøgte vidre - og aldrig vendte tilbage til noder - kunne man gå glip af en reduceringsmulighed. x/(3-2) er et Hvis O har et ulige antal børn bliver træet skævt, hvilket er uundgåeligt. Dette forudsætter selvfølgelig, at n har en forælder. Hvis n er rod-noden for ASTet, har den ingen forælder. -Dette er netop forklaringen bag supernode som er en dummy vi indsætter, således at den bliver root i ASTet. Dermed er det garanteret at alle reelle ASTnoder, har en forælder. 30

37 eksempel herpå; uden tilbagevenden ville AST reduceres svarende til x/1, og ikke x, da vi i givet fald ikke vender tilbage til divisions-noden. Efter en ændring er fortaget, er det derfor vigtigt at starte forfra - indtil der ikke kan foretages flere ændringer. Dette opnås ved hjælp af smart misbrug af exceptions, som fanges i ASTvisitorNormalize:: StartVisit(ASTnode *n). 1 { 4.4 stmtblock: Symboltabel og statements Mark Vi giver først et eksempel på nested-scope, der resulterer i ASTet i figur int x; 3 int foo, bar ; 4 x = foo ; // x#1 5 6 { // start på anonymt nested scope 7 int x = bar ; // x#2 8 } // slut på anonymt nested scope 9 } Figur 4.5: AST gengiver scope men ikke variabeldefinitioner. I C introducerer enhver block et nyt (nested) scope. Vi ser af eksempel-koden, at to forskellige variable har samme navn, x. -Dette er tilladt da de hver lever i forskelligt scope. Variabel deklarationer udelades fra ASTet da vi ikke har brug for at vide nøjagtigt hvor variable er deklareret: Hvert sted i kildeteksten, en variabel bruges, allokeres en tilsvarende exprvar dynamisk. exprvars er derfor forskellige fra hinanden - selv indenfor samme scope. exprvars kan altså ikke bruges direkte som reference til en variabel; vi bliver nødt til at resolve variablen ved at slå op i symboltabellen. Vi kan dog ikke have én symboltabel, netop pga. nested scope. Istedet tilknyttes en lokal symboltabel til hver block - derfor medtages STMT_BLOCK s i ASTet. C semantikken er således, at dybt nestede scopes tager præcedens. -Så når vi skal resolve en exprvars variabel, slås først op i den lokale symboltabel - findes variablen ikke deri, slås op i symboltabellen længere oppe, osv. - indtil variablen findes. Den lokale symboltabel findes ved blot at traverere op ad i ASTet fra den givne exprvar, indtil en STMT_BLOCK mødes. -Dette er netop muligt, da alle ASTnodes kender deres forælder. Også kendt som en tuborgblok, populært. Payload kan være ens, men med forskelligt menes pointer-addressen. Ellers meldes der om fejl, at variablen ikke er deklareret. 31

38 Resultatet af en resolve, er en pointer til en info-struktur som indeholder information om variablen. For samme variabel, vil alle exprvars af denne være forskellige fra hinanden, men vil alle resolve til samme info-pointer. std::basic_string< char > std::string text navn specifier dotable node_t ds type dd elements ASTnode parent keys declarator std::vector< ds * > specifiers stmt info elements elements std::vector< stmt * > std::map< string, info > statements symtab stmtblock Figur 4.6: Samarbejdsdiagram for stmtblock. Udover at indeholde den lokale symboltabel, indeholder stmtblock også andre statements. Statements udføres i sekvens i C, hvorfor der gemmes i en std::vector<stmt*> - som vist i figur 4.6. Det ses af figur 4.1, at stmtblock selv er en stmt, hvilket netop tillader nestede stmtblock s. 4.5 Eksempel: AST for GCD Jesper I figur 4.7 ses det komplette AST for det inlinede GCD-eksempel fra afsnit 2.5. I en pointer-aritmetisk forstand. Nærmere betegnet, en såkaldt compound statement. 32

39 Figur 4.7: YACHT-genereret AST for GCD. 33

40 Kapitel 5 Abstract State machine Jesper En abstrakt tilstandmaskine er en tilstandsmaskine som opererer på states og angiver kontrol flowet eksplicit. ASM-grafen består ligesom AST en af noder, som forbindes af kanter. Tilforskel fra en normal ASM, hvor der er 3 forskellige state typer så har vi implementeret vores ASM med 2 - nemlig diamonds og boxes. En ASM state kan ekspandere til en eller flere tilstande i FSM. En ASM state indeholder et sæt af handlinger, som kan køre parralel. Kan noget sekventiel kode ikke køre parallelt vil dette blive opdelt i flere ASM states. Der findes ingen entydig præcis definition af en ASM. Det lige før beskrevne kan karakterisere vores udgave. Et eksempel på en assingment med beregninger i en box kan ses i figur 5.1 A = 3+4 B = i < 10 Figur 5.1: ASM-box for to assignments med beregninger Som det kan ses i figur 5.1 kan en ASMbox indeholde et arbitræt antal handlinger. ASM er en præcis form af pseudo-code, som er en mere koncise ækvivalent udgave af FSM. ASM modellen kan bruges både indenfor software som hardware. ASMdummy s indføres for at have single exit point. Dummies kan normaliseres væk bagefter som beskrevet i afsnit 5.4. Alle ASMnoder skal have en veldefineret next-asmnode, hvorfor ASM-grafen er cyklisk. Umiddelbart kan man dog nemt forstille sig C-kode som ikke har nogen loops, og det kan derfor undre at ASM-grafen er cyklisk alligevel: Der indsættes en tom box som looper på sig selv, hvis dette er tilfældet. 5.1 ASM modellering Mark Som beskrevet i introduktionen, haves kun 3 typer ASMnodes. Alle ASMnodes kan tegnes som noder af dot (dotable), og er besøgbare (ASMvisitable). Hvor ASMdiamond kan have 1 eller flere udgående kanter, har ASMbox og ASMdummy netop 1 udgående kant. Det viser sig fordelagtigt at generalisere ASMbox og ASMdummy til ASMnode_1child, som netop angiver at disse kun har 1 udgående kant. -Dette ses af arvehierakiet i figur 5.2. ASMdummy har ikke noget indhold, dvs. intet payload. Bruges kun som samlingspunkt under opbygningen af ASM fra ASTet, normaliseres derefter væk. Må dog gerne være ASMnoden selv. 34

41 dotable ASMvisitable ASMnode ASMdiamond ASMnode_1child ASMbox ASMdummy Figur 5.2: Arvehieraki for ASMnoder. ASMbox har sekventiel kode i form af expression-statements (exprs), som payload. Hvis en ASMbox har flere exprs som payload, kan de køre parallelt. Har 1 udgående kant. ASMdiamond indeholder branch-betingelsen. Udgående kanter er markeret med konstantværdier (exprconst value); den kant som matcher værdien af branch-betingelsen, tages. I tilfælde af at værdien af branch-betingelsen ikke matcher nogen af de konstantværdier på de udgående kanter, tages else-branchen. Else-branchen er altid tilstede og repræsenteres som en NULL-pointer til en exprconst. Dette tillader at generalisere stmtif og stmtswitch. 5.2 Repræsentation af control-flow i ASM Jesper Generelt kan eksekveringen af diverse C-konstruktioner angives grafisk som en graf af ASMnoder. En exit-node er ofte angivet som en dummy state og er altid halen i head_tail som bliver forklaret senere - det betyder at det er den node der hoppes til når udførselen er færdig. Cond 1 while ( cond ) { 2 body ; 3 } T Body F Figur 5.3: while-løkke i C (venstre) konverteres til ASM (højre) I figur 5.3 ses det tydeligt at afgørelsen af conditionen bestemmer om der skal springes til dummy noden(condition = false) eller om bodyen skal udføres igen(condition = true). Jævnfør figur 5.3 hvor exit-noden kan ses som den lille runde node i bunden af figuren 35

42 Pre cond T 1 for ( pre ; cond ; post ) { 2 body ; 3 } Body F Post Figur 5.4: for-løkke i C (venstre) konverteres til ASM (højre) T Cond F 1 if ( cond ) { 2 then_body ; 3 } else { 4 else_body ; 5 } Then_body Else_body Figur 5.5: if-statement i C (venstre) konverteres ASM (højre) Else-siden er gjort stiplet fordi den er potentiel, dvs. at vi sagtens kan have en ifsætning uden nogen else_body. Else-kanten eksisterer dog altid - uanset om en else-body er skrevet eller ej. Body T 1 do { 2 body ; 3 } while ( cond ); Cond F Figur 5.6: do while-løkke i C (venstre) konverteres ASM (højre) 36

43 Cond 1 switch ( cond ) { 2 case 0: body0 ; 3 case 1: body1 ; break ; 4 case 2: body2 ; break ; 5 default : body_def ; 6 } else Body0 Body1 Body2 Body_def Figur 5.7: switch-case i C (venstre) konverteres til ASM (højre) I figur 5.7 ses det at hvis case 0 er tilfældet vil både body0 og body1 eksekvere inden der brydes ud af switchen - grundet break-statement en som går direkte til exit-node. 5.3 ASTvisitor2ASM: Konstruktion af ASM ud fra AST Jesper For at få konstrueret vores ASM, ser vi først på en lille stump C-kode. Koden vi ser på, er for et for-løkke: 1 { 2 int i, sum$out ; 3 4 for (i = 0; i < 10; i +=1) { 5 sum$out += i; 6 } 7 } Ved at inputte vores C-kode til vores C-parser kan vi få outputtet det til et AST, denne generering vil ikke blive uddybet, da vi allerede har et kapitel som dækker dette, men grafen kan ses i figur 5.8: Figur 5.8: AST-outputtet fra YACHT for for-løkke eksemplet. Grunden til at vi skriver i+=1 er fordi vi pt. ikke normaliserer pre- og postfix (de)inkrementerings operatorererne væk. 37

44 For at få dannet vores ASM ud fra AST en, så skal AST en traverseres. Det ses i figur 5.8 at vores for-løkke har 4 members - pre, cond, post og body, som vi skal bruge til at danne ASM ud fra. Det kan ses i AST et at pre og post er assignments og derfor skal de i ASM pakkes ind i en ASMbox-node. Da cond er en betingelses-expression skal den pakkes ind i en ASMdiamond-node, hvor kanterne markeres enten true eller false, men børnene er stadig ASM-noder. Hvis cond er true gås til body som ofte består af en statement blok der pakkes ind i en box, hvorimod at hvis cond er false hoppes der direkte til exit-noden. I vores ASM-chart ses det at post og body er slået sammen til én enkelt ASMbox, hvilket skyldes at ASMvisitorBB har lavet en assimilering og pakket disse to states ind i samme ASMbox fordi de kan køre parallelt - en nærmere beskrivelse af dette kan findes i afsnit 5.5. I C++ koden er der etableret et head_tail systemtil at sammensætte de små brudstykker af ASM der bliver returneret ved mødet af diverse løkker, statements, etc. head_tail systemets overordnede opgave er at holde styr på hvilken node der er entry og hvilken der er exit. I dette eksempel er entry noden, i = 0, og exit-noden er dummy-noden. Disse bliver appendet til en sammenhængende ASM. Mødes der under traverseringen af AST en, en stmtfor returneres head_tail for denne, dvs. at udefra ses kun entry-noden og exitnoden - head_tail er ligeglad med hvad der ligger imellem de to noder. Mødes en stmt efter stmtfor en vil mødet af denne også returnere en head_tail, hvor efter at tail fra stmtfor vil blive appendet med head fra stmt. Nu haves en ASM-graf bestående af en stmtfor og stmt, hvor den samlede ASM har en samlet head_tail som head noden af stmtfor og tail af stmt. Figur 5.9: ASM-graf outputtet af YACHT for for-løkke eksemplet. 5.4 ASMvisitorNorm: Udryddelse af ASMdummy s Mark Dummies er rare under opbygningen af ASM-grafen, da de blot angiver den næste reelle ASMnode, uden at den næste ASMnode rent faktisk kendes. Når ASM-grafen er færdigkonstrueret, fjernes alle dummies da de ikke har noget med hardware at gøre: Da der er stor lighed mellem ASM-grafen og FSM-grafen, skulle dummies oversættes til unødvendige tomme FSM-tilstande. Det er oplagt at slette dummies når de mødes i ASMvisitorNorm::visit(ASMdummy* n), men når en dummy slettes skal vi sørge for at dens parents (der kan være flere), opdateres til - ikke at pege på dummy en længere, men dummyens barn. Dette er dog en dårlig strategi, for ASMnodes kender ikke umiddelbart deres parents, hvorfor koden vil blive unødigt kompliceret. Da der udover ASMdummy kun findes to andre ASMnodes, kan vi håndtere dummys når vi møder ASMboxes og ASMdiamonds. Dette er dog kun gældende i dette tilfælde - ikke generelt. append-metoden vil sætte den udgående kant fra tail-noden af stmtfor, til at pege på headnoden fra den stmt der følger umiddelbart efter stmtfor. 38

45 Vi møder eksempelvis en ASMbox, og finder at den har en dummy som barn: Vi omgår da dummyen ved at lade ASMboxen pege på dummyens barn. Samtidigt markeres den omgåede dummy til senere sletning. Dummies kan dog ligge i forlængelse af hinanden, hvorfor - efter vi har omgået en dummy - må tjekke om ASMboxens nye barn også er en dummy. Sådan fortsættes indtil en non-dummy findes. 5.5 ASMvisitorBB: Gruppering til Basic Blocks Mark En basic block er karakteriseret ved at være en blok af ren sekventiel kode; en basic block består derfor af 1 eller flere expression-statements (instruktioner) med lineær control flow. Formålet med basic blocks i en traditionel compiler er at gruppere kode, som da påtrykkes diverse optimeringsteknikker. Men expression-statements i en basic block behandles som vil de blive udført enkelvist og i sekvens. I YACHT grupperes expression-statements også - ikke efter om de udføres i rent lineært flow, men efter om de kan udføres parallelt. BB ASMbox Figur 5.10: ASMbox BB. Indtil nu indeholdte ASMboxes i ASM-grafen, kun én expr som payload. -Men ASMboxes har dog potentiale til at indeholde flere exprs, da payload netop er en std::vector af exprs. Der behøves altså ingen ny type ASMnode for at kunne gemme flere (parallele) expression-statements i en ASMbox. Da en ASMbox blot grupperer sekventiel kode der er i stand til at køre parallelt, kan en ASMbox derfor anses som en basic block. -Det omvendte er dog ikke sandt: En basic block behøver ikke at tage stilling til dataafhængigheder, da disse automatisk er opfyldt når koden tænkes eksekveret sekventielt. Relationen mellem ASMboxes og basic blocks, er gengivet i figur Således kan 2 eller flere ASMboxes i forlængelse af hinanden, samles i 1 basic block Bernsteins betingelser: Dataafhængighedsanalyse Mark Bernsteins betingelser[1] udsiger sig om dataafhængighederne mellem to blokke af kode, S 1 og S 2. Betingelserne er simple og velegnede til implementering i C++ med std::set, er derfor implementeret i YACHT. Bernstein indfører 3 sæt: Dummyen kan ikke delete s med det samme: Hvis to ASMnodes peger på samme dummy, og dummyen slettes før vi har nået til den ene af ASMnoderne, ender vi med at dereferere en pointer til noget hukkommelse som er blevet frigjort; leder til crash. Derfor husker vi dummies som skal slettes senere, ved at gemme pointers til dem i et std::set (anden STL container kan ikke bruges, hvor elementer kan angives med multiplicitet, kan ikke bruges - i så fald risikerer vi at slette en dummy mere end 1 gang; leder til crash.). 39

46 RaW = O(S 1 ) I(S 2 ), WaR = I(S 1 ) O(S 2 ), WaW = O(S 1 ) O(S 2 ). - Hvor: O(S i ) betegner sættet af variable skrevet til i kodeblok S i. I(S i ) beregner sættet af variable læst i kodeblok S i. RaW, Read-after-Write: Hvis sættet ikke er tomt, skrives en variabel i S 1 og aflæses i S 2 : Der er findes altså mindst 1 dataafhængighed mellem S 1 og S 2, hvorfor de ikke kan køre parallelt. Dette er en ægte afhængighed. WaR, Write-after-Read: Hvis sættet ikke er tomt, læses en variabel i S 1 og skrives i S 2. Hvis disse kørte parallelt ville den aflæste værdi ikke være veldefineret. -Men i vores hardware model, tillades denne type anti-afhængighed: Samme variabel kan både læses og skrives, i samme FSM-tilstand. Lader vi et register, reg, betegne sådan en variabel; vil FSM-tilstanden sætte next_reg med det samme, men reg er først blevet opdatereret lige efter næste rising_edge(clk). -Altså først i næste state er reg blevet overskrevet. Dette 1-cycle write-delay tillader at en FSM-tilstand både kan læse og skrive til samme variabel; WaR-afhængigheder kan derfor tillades. WaW, Write-after-Write: Hvis sættet ikke er tomt, skrives en variabel i både S 1 og S 2. Hvorfor så udføre en write i S 1 hvis resultatet alligevel overskrives i S 2? Denne afhængighed har faktisk sin berettigelse for outputs: Hvis vi netop implementerer et handshake, er det vigtigt for omverdenen, at protokollen efterleves. Potentiel optimering: For interne variable, som ikke kan ses af omverdenen, kan write i S 1 godt fjernes. Dette er dog ikke implementeret i YACHT. Dog skal man være klar over at både WaW og RaW afhængigheder kan eksistere mellem S 1 og S 2. Det er således i tilfælde af at der kun eksisterer WaW-afhængigheder, at en intern write kan fjernes i S 1. Hvis S 1 peger på S 2, og RaW WaR =, er der ingen dataafhængigheder mellem S 1 og S 2, hvorfor de kan køre parallelt Virkemåde af ASMvisitorBB Mark I YACHT repræsenteres S 1 og S 2 hver som ASMboxes. Finder vi at de kan køre parallelt, assimileres S 2 med S 1 - hvis dette kunne lade sig gøre, kan vi fortsætte med at forsøge at assimilere S 2 s barn med S 1, osv. Følgende pseudokode illustrerer dette: 1 void ASMvisitorBB :: visit ( ASMbox * n) { 2 ASMbox * S1 = n; // Bare et alias 3 set < ASMbox * > visited ; // Sæt af S2 ere som er blevet assimileret med S1 4 5 while (1) 6 { Hvis vi samtidigt skriver til en variabel mens at den aflæses, hvilken værdi aflæses så? ASMnodes mappes netop til moore FSM-tilstande. Andre kombinationer er ikke understøttet i YACHT. 40

47 7 ASMbox *S2 = S1 -> child ; 8 9 if ( S2 ikke er ASMbox ) 10 break ; // skal være box før vi evt. kan slå sammen med S1 11 if ( S2 er besøgt før ) 12 break ; // break hvis besøgt før /* Bestem RaW - og WaR - sættene her */ 15 std :: set bernstein = union ( RaW, WaR ); // bestem bernstein - sættet if ( bernstein. empty ()) { 18 // Ingen datapendency mellem S1 og S2; og kan derfor køre parallelt. 19 // Assimilér : Udvid S1 med indholdet af S2: 20 S1 - > payload. insert ( S2 - > payload ); // tilføj exprs fra S2 til S1 21 S1 - > child = S2 - > child ; // omgå S2: S1 peger på S2 s barn 22 visited. insert ( S2); // markér S2 som besøgt 23 } else { 24 // S2 afhænger af S1; kan vi ikke gøre noget ved. 25 // S2 må forblive i sin egen ASMbox. 26 break ; // Kan ikke tilføje mere til S1 pga. afhængigheder 27 } 28 } visitasm (S1 -> child ); // Besøg vidre 31 } Vi starter altså fra S 1, traverserer ASM ud fra denne mens vi forsøger at assimilere så meget som muligt med S 1 - indtil en dataafhængighed mødes som forhindrer videre assimilering. Dette sikrer at S 1 opnår den mulige parallelisme. S A S B S A S x S B S X ASMvisitorBB S X S Y S Y Figur 5.11: Assimilering af S X med S A. S X eksisterer dog fortsat. Som det ses af ovenstående pseudokode, slettes S 2 -ASMboxes ikke: Indeholdet af dem kopieres bare over i S 1. Dette er vigtigt for at kunne håndtere generelle cases som det vist i figur 5.11: Her er S A og S X istand til at køre parallelt, men S X og S B kan ikke køre parallelt. 5.6 Eksempler Assimilering Mark Betragt følgende input til YACHT, hvorfra YACHT genererer ASM-grafen som vist: 41

48 1 { 2 int a,b,c, cond ; 3 if ( cond ) { 4 a = 11; 5 } else { 6 b = 22; 7 } 8 c = 1; 9 b = 33; 10 b = 44; 11 } Vi ser at c=1 er blevet kopieret op i hver branch da den kan køre parallelt i begge af disse. Tilsvarende kan b=33 og a=11 også køre parallelt. En WaW-afhængighed mellem b=22 og b=33 forhindrer disse i at bliver assimileret Software-pipelining Mark Pipelining kan fortages i selve beregningskomponenterne, datavejen og tilstandsmaskinen som beskrevet i [3, afsnit ]. Pipelining af datavejen er dog interessant for os, da vi netop kan angive dette som C-kildetekst - altså på software-niveau. -Betragt eksempelvis løkken: 1 // Konventionel udgave af for loop, som man normalt ville skrive det i C 2 for (i = 0; i < 10; i ++) { 3 x = ( i+b)*( c+d); // State j, kritisk vej = 2 ops 4 } - Versus en 2-stage pipelined udgave der er ækvivalent: 1 // Pipelined udgave af for loop, som er bedre egnet til hardware realisering 2 tmp1 = 0+b; tmp2 = c+d; // State 1, init 3 for (i = 1; i < 10; i ++) { 4 x = tmp1 * tmp2 ; // State j, steady state, kritisk vej = 1 op 5 tmp1 = i+b; tmp2 = c+d; // State j, steady state, kritisk vej = 1 op 6 } 7 x = tmp1 * tmp2 ; // State N, end -Den lange kombinatoriske vej i det konventionelle loop ses brudt op i pipeline-udgaven uden at påvirke dataafhængighederne. Vi lægger desuden mærke til at for-loopet ikke behøver at udrulles; kun første iteration udskilles. NB Automatisk software-pipelining er ikke implementeret i YACHT, men ASMvisitorBB er en nødvendig forudsætning for at kunne samle det pipelined for-loops body i én state ASM for Duff s device Mark Duff s device er en obskur C konstruktion hvor en løkke er sammenflettet med en switchcase. switch kan opfattes som en multi-goto der blot hopper til case-labels i stedet for almindelige labels. Da det er tilladt at hoppe ind midt i et loop, er nedenstående således gyldig ANSI C og C kode: 1 { 2 int arg, a, pre_do, cond, post_while, done ; 3 arg = 2; // dead code elim. ikke implementeret 4 switch ( arg ) { 5 pre_do = 1; // udføres aldrig 6 do { 7 a = -1; s_device&oldid= #original_version 42

49 8 case 0: a = 0; 9 case 1: a = 10; 10 case 2: a = 20; 11 case 3: a = 30; 12 } while ( cond ); 13 post_while = 1; 14 } 15 done = 1; 16 } YACHT håndterer ovenstående fint, og genererer ASM-grafen i figur Figur 5.12: YACHT-genereret ASM for (modificeret) Duff s device. Forklaring bag protect, findes i afsnit Den succesfulde håndtering af Duff s device, er et vidnesbyrd om at YACHTs frontend er temmelig komplet ASM for GCD Jesper ASM-grafen for GCD-eksemplet fremgår af figur Vi ser netop af figur 5.13, at while(1){...} bliver til en ASMdiamond - på trods af at betingelsen er konstant. Dette skyldes fraværet af dead code elimination. 43

50 Figur 5.13: YACHT genereret ASM-graf for GCD-eksemplet med handshake. 44

51 Kapitel 6 Hardware Fælles Som sagt har vi en streng opdeling mellem tilstandsmaskine (FSM) og datavej (D eller DP). Toplevel komponenten (TOP) er således beskrevet ved FSM+D-modellen, som eksemplificeret i figur 6.1. TOP incr FSM DP incr eq Status eq outp == 1 outp arg1 b + sum a arg2 D global _reset global _reset Kontrol cnt_clk_en cnt_clk_en clk_en clk REG_cnt clk clk clk Q cnt cnt Figur 6.1: Strukturel opbygning af en FSMD som følger vores model. I vores model har tilstandsmaskinen kun kontakt til omverdenen gennem datavejen. Al reel top-level I/O sker altså gennem datavejen. Tilstandsmaskinen kontrollerer datavejen og datavejen melder sin status tilbage til tilstandsmaskinen. Alle beregningselementer ligger i datavejen og tilstandsmaskinen styrer sekvensen af disse beregninger. Denne partionering mellem kontrol og beregninger er nem at designe hardware efter - men som som vist i [11, figur 8-4] findes et overlap, da samme funktion kan udtrykkes control-orienteret eller datavejs-orienteret. Vi vælger en nærliggende opdeling: Alle expressions (expr-træer) og variable (som registre) ligger i datavejen. Al control flow (stmt s) ligger i tilstandsmaskinen. -Med denne opdeling kan vi, nu med C-kode, illustrere det før omtalte overlap: 45

52 Control-orienteret 1 if (a==b) c = foo ; 2 else c = bar ; Datavejs-orienteret 1 c = (a==b)? foo : bar ; -I begge tilfælde vil YACHT lade c-registeret ligge i datavejen og registeret vil få en multiplexer foran sin D-port. Sammenligningen a==b realiseres som en equals-komponent i datavejen der følger C semantikken. Forskellen ligger da i, at i den control-orienterede udgave vil YACHT føre resultat-signalet fra eqauals-komponenten i datavejen, over til tilstandsmaskinen. Tilstandsmaskinen vil være af Moore typen, hvorfor det vil tage (mindst ) 2 cycles at udføre den control-orienterede kodestump. Den datavejs-orienterede udgave vil derimod udføres i (mindst) 1 cycle, da resultat-signalet fra equals-komponenten bliver til select for multiplexeren foran c-registreret. foo og bar vil naturligvis være de to datainputs til denne multiplexer. Funktionen af begge er ækvivalent, men hardwaren er forskellig. YACHT vil ikke transformere den ene udgave til den anden, hvorfor brugeren må tænke på hardwaren mens C-koden skrives. 6.1 Objektorienteret modellering af hardware Mark I følge [5] kan digital hardware modelleres i 3 domæner - det adfærdsmæssige, strukturelle og fysiske. Adfærdsmæssig Systemer Algoritmer Register overførsler Logik Overføringsfunktioner Strukturel Processore ALUs, RAM, etc. Gates, flip-flops, etc. Transistore Transistor layout Celle layout Modul layout Floorplans Fysiske opdelinger Fysisk Figur 6.2: Gajski-Kuhn Y-diagrammet For den resourcedelte hardware generering vil en expr ekspandere til et antal cycles som funktion af de bruger-tildelte resourcer. En øvre grænse kan ikke gives generelt, da denne er brugerafhængig; kun en mindre grænse - med uendelige resourcer antaget - kan gives generelt. En moore state til at udføre sammenligningen og sætte next_state. I den efterfølgende moore state udføres assignment en til c. -Altså 2 cycles. 46

53 I YACHT beskrives næsten al hardware på det strukturelle ALU, RAM, etc. -niveau som vist i figur Dog er FSM-komponenten speciel: Dens ydre beskrives strukturelt i kraft af sine porte, men dens indre opbygning beskrives som tilstandsdiagram. En fælles model kan derfor ikke konstrueres, der dækker alle hardware-dele. Dog skal alle hardwaredele kunne besøges, og arver derfor fra HWvisitable - som set i figur 6.3. HWvisitable ANDOR betingelse fsmnode komponent portgenerel signal sink source AND OR SIG betingelseelse betingelseequals mealynode moorenode adder datapath equals fsm FSMD konstant mult mux nequals reg rel_gt rel_lt subtractor Iport Oport Figur 6.3: Arvehieraki: Alle hardware-relaterede klasser skal være besøgbare, så der arves fra HWvisitable Strukturel modellering Mark Da det endelige HDL output skal beskrive toplevel modulet og datavejen strukturelt, må vores HDL-neutrale interne hardware-datastrukturer være på diagram niveau : Komponenter, porte og signalerne i mellem portene skal kunne repræsenteres Komponenter Mark Tager vi toplevel modulet, TOP, er det en komponent som indeholder tilstandsmaskinen FSM samt datavejen DP som subkomponenter. På samme måde indeholder DP dens egne subkomponenter. Det er nu oplagt at en komponent: Har et unikt navn, Har 0 eller flere input porte, Har 1 eller flere output porte, Indeholder 0 eller flere subkomponenter. For hvilket det er nemt at udskrive VHDL på RTL-niveau for, som en eller flere VHDL process blokke. Tilstandsmaskinen er undtagelsen, da denne beskrives på RTL niveau. Det er muligt at forstille sig en autonom komponent der generer output uden noget input. En counter med intern clock er eksempel herpå. I YACHT har pseudokomponenten konstant 0 input porte og 1 output port. En komponent uden output er værdiløs: På trods af eventuelle interne beregninger, kan vi ikke observere et resultat; komponenten er overflødig og bør fjernes da den ellers optager areal og effekt. 47

54 Navnet er resultatet af konkateneringen mellem en konstant string, specifik for komponenttypen, og et heltal som tælles op for hver objektinstans. Dette garanterer unikke navne til alle komponenter. Samme teknik bruges også for signaler og FSM tilstande. Navnet er desuden konstant; det må ikke ændres. Porte repræsenteres ved et dynamisk std::map mellem portens navn og port-instansen. Vi kan ikke kende alle komponenternes porte ved compile-time: Et godt eksempel er en multiplexer som får tilføjet en input port hver gang vi har en assignment stående i C koden. Istedet for at have separate addinputport() og getinputport()-metoder, kan vi slå dem sammen til én input(): Først tjekker input() om porten allerede findes, hvis den gør, returneres den - ellers oprettes porten internt og returneres. -Tilsvarende gøres for output porte. Subkomponenter Hvor datavejen og toplevel modulet altid indeholder subkomponenter, har blackbox komponenter ingen - for os synlige - subkomponenter: Der er mange måder at konstruere fx binære adders på: Ripple carry, carry lookahead, Kogge-Stone, etc. I virkeligheden består adders af logiske blokke, som består af gates, som består af transistorer. Men fælles for alle disse adders er deres interface og funktion, hvorfor vi kan tillade os at abstrahere deres subkomponenter væk og lade en generisk blackbox adder repræsentere dem alle. Blackbox komponenter modelleres altså ikke dybere end komponenten selv, og de udgør derved de grundlæggende komponent-primitiver i vores interne hardware repræsentation. Alle beregningselementerne, multiplexers, og registre er eksempler på blackbox komponenter. Arkitekturen af disse blackboxes bestemmes først når HDL koden udskrives (af HWvisitor2VHDL). Når en komponent kan indeholde subkomponenter, må den nødvendigvis også kende sine interne signaler. -Det viser sig dog mere hensigtsmæssigt at komponenten ikke direkte kender til de interne signaler, da det blot medfører mere bogholderi: Skal komponenten kende til sine interne signaler, kan den få dem fra sine egne indre porte, og subkomponenternes ydre porte. Signaler kan dog ikke forbindes virkårligt, men skal overholde et sæt veldefinerede regler. -Hvad disse regler er, og hvad der menes med indre og ydre porte, forklares i afsnit og Beskrivelsen af komponent::addsignal() udsættes til afsnit Porte, sources og sinks Mark I YACHT opereres der kun med to typer porte: Input og output porte, hhv. Iport og Oport. Desuden kan output porte ikke flyde. Konsekvensen heraf, er, at bidirektionelle busser med delte I/O-linjer, ikke kan repræsenteres i YACHT. For at maskimere kodegenbrug er denne counter-klasse en template. En adder vil arve getid() fra counter<adder>. getid() inkrementerer en static int hver gang den kaldes. Her udnyttes det faktum at static int s lever i selve klassen og ikke objektinstansen af klassen, samt at counter<adder> er en helt anden klasse end counter<foo>. Porte har ikke brug for unikke navne: Det er tilladt at have en port A hos både adders og multipliers. I mangel af et bedre ord, kalder vi disse subkomponent-løse komponenter for blackboxes. Adders, subtractors, multipliers, equal, not-eqaul, greater-than, etc. Vi husker på at hver (normaliseret) C operator mapper direkte til en komponent. Høj-impedans udgang. Angivet i VHDL som Z. Her tænkes især på 1-wire bussen, Wire&oldid= Langt de fleste busser har separate I/O linjer, og har derfor ikke brug for tri-state buffers. 48

55 HWvisitable dotable + visited + HWvisitable() + acceptvisit() + getdotlabel() + getdotuid() komponent + navn + inputs + outputs + komponenter + komponent() + getdotlabel() + addsignal() + input() + input() + inputafledt() + output() + output() + outputafledt() - inputexists() - outputexists() adder + adder() + acceptvisit() datapath + datapath() + acceptvisit() equals + equals() + acceptvisit() fsm + states + defaults + fsm() + acceptvisit() + addnewmoorestate() FSMD + f + dp + FSMD() + acceptvisit() konstant + val + konstant() + acceptvisit() mult + mult() + acceptvisit() mux + d_lines + unique_source + mux() + acceptvisit() + allocnewinputport() + getinputport() nequals + nequals() + acceptvisit() + reg() reg + acceptvisit() rel_gt + rel_gt() + acceptvisit() rel_lt + rel_lt() + acceptvisit() subtractor + subtractor() + acceptvisit() Figur 6.4: Arvehieraki: Alle komponenter arver fra komponent. Komponenter er besøgbare og kan tegnes af DOT. Vi kan forstille os at signaler forbinder én Oport til en eller flere Iport e. Umiddelbart lyder denne Oport-til-Iport konvention som tilstrækkelig; vi kan eksempelvis fint repræsentere den binære tæller som vist i figur 6.5. a sum D Q + 1 b clk REG_cnt Figur 6.5: Binær tæller. Typisk vil denne binære tæller dog ligge inde i en anden komponent (datavejen), og registerets Q udgang føres vidre - som vist i figur 6.6. a sum D Q + clk 1 b clk REG_cnt cnt Af figur 6.6 ses to problemer: Figur 6.6: Binær tæller som del af en datavej. Signalet der drives af registerets Oport Q, driver - foruden a Iport en hos adderen - en anden Oport! Dette er i modstrid med den før omtalte Oport-til-Iport konvention. 49

56 Datavejens clk er forbundet til registerets clk, og det interne clock-signal i datavejen forbinder ikke til nogen Oport! Dette er også i modstrid med Oport-til-Iport konventionen. Den umiddelbare Oport-til-Iport konvention er altså ikke en god nok model. Løsningen er simpel; synsvinklen skal tages med i betragtning: En port kan ses både ude- og indefra den pågældende komponent. Betragter vi en Oport i figur 6.6, løber signalet ind i den når set indefra, og signalet løber ud når set udefra - samme princip, men omvendt, gælder for Iporte. Inspireret af Integreret analog elektronik, kan vi opfinde såkaldte sources og sinks: Når vi betragter en sink, er informations-flowet altid ind i den. Tilsvarende er informations-flowet altid ud fra en source. -Men ligesom der ikke findes særskilte magnetiske monopoler, giver det heller ikke mening at have særskilte sources eller sinks: Både Iporte og Oporte har netop én source og én sink: Iporte: indersiden er en source, ydersiden er en sink. Oporte: indersiden er en sink, ydersiden er en source. Input og output portene er næsten ens, kun retningen og typerne af inside og outside er forskellig. Det er oplagt at oprette en generel port, portgenerel, som blot specialiseres til Iporte og Oporte - som vist i figur 6.7a. HWvisitable + visited + HWvisitable() + acceptvisit() portgenerel + navn + dir + komp + num + portgenerel() + ~portgenerel() + getsink() + getsource() + getdescr() HWvisitable + visited HWvisitable + visited + HWvisitable() + acceptvisit() + HWvisitable() + acceptvisit() Iport Oport + outside + inside + outside + inside sink source + Iport() + acceptvisit() + getsink() + getsource() + Oport() + acceptvisit() + getsink() + getsource() + port + sig_inbound + sink() + acceptvisit() + port + sig_outbound + source() + acceptvisit() (a) Porte arver fra portgenerel. (b) sink. (c) source. Figur 6.7: Arvehierakier for porte, sinks og sources. En portgenerel har som sagt én sink og én source tilknyttet, men den tilknyttede sink og source har også brug for at vide hvilken port de tilhører: sink og source har På trods af den åbenlyse parallel til current sources og sinks, er denne analogi ikke gyldig: I analog forstand kan en digital udgang (dvs. en source i YACHT) både sink e og source e strøm. Eksempelvis kan en modstand forbindes mellem V DD og udgangen af fx en AND gate: Er udgangen på AND gaten høj ( V DD), går der ingen strøm gennem modstanden (ingen potentialeforskel over den). -Er AND gatens udgang derimod lav ( 0 V), løber der en strøm ind i udgangen på AND gaten; her betegnes udgangen som en current sink. I YACHT, med informations flow-mæssig forstand, vil AND udgangen dog altid karakteriseres som en source. 50

57 derfor en const pointer, port, til at angive dette. Porte og deres source og sink kender altså hinanden Signaler Mark Vi kan med fordel opfatte et signal som en orienteret kant eller pil. Signalets hale er da forbundet til en source og hovedet er forbundet til en sink. Signalet må kun være forbundet til 1 source, men kan forbindes til 1 eller flere sinks. Et signal kan altså bedre opfattes som en pil med 1 hale og flere hoveder. Det er da nærliggende at have følgende signal-members: tail Den source som signalet drives af. heads Sættet af sinks som signalet driver. Der bruges netop et std::set, da det ikke giver mening at forbinde signaler til porte med multiplicitet. setsource(source& out) Sætter tail lig out, og opdaterer samtidigt out, således at out véd at den har dette signal som dets udgående signal. addsink(sink& in) Tilføjer endnu en sink til heads. Samtidigt lader vi in fortælle, at den har dette signal som dets indgående signal. Signalers navne kan ikke sættes direkte, de dannes automatisk udfra tail s navn. - Tilsvarende er signalets widthsign (bitbredde og signedness) baseret på tail s widthsign. Et signal må altså ikke krydsen omridset af en komponent. Er der brug for at signalet skal krydse komponenter, må der oprettes flere signaler og disse skal forbindes gennem porte. Et signal skal altså være komplet indeholdt i netop én komponent. Der indføres derfor en addsignal-metode i komponent-klassen: komponent::addsignal(source& from, sink& to) abstraherer manuelt brug af setsource og addsink væk, ved blot at lade os angive at der skal være signal mellem from og to. addsignal tjekker selv om et signal allerede er forbundet til from, og hvis der er tilføjes to blot via addsink - ellers oprettes et nyt signal og setsource(from) og addsink(to) kaldes på det nye signal. Desuden tjekker addsignal om et signal mellem from og to netop vil ligge komplet inden i den komponent som addsignal blev kaldt fra - dette gøres før noget forbindes eller oprettes. setsource og addsink er nødvendige primitiver, men de fortager ikke nogen tjek - hvorfor de kun kaldes fra addsignal Eksempel: Binær adder i C++ Jesper Vi giver her et eksempel på hvordan den binære adder i figur 6.6 kan opbygges i C++: 1 datapath * dp = new datapath (); 2 adder *a = new adder (); 3 reg *r = new reg (" cnt "); 4 konstant * k = new konstant (1) ; 5 6 // Alle komponenter ligger i DP 7 dp -> komponenter. push_back (a); 8 dp -> komponenter. push_back (r); 9 dp -> komponenter. push_back (k); // Alle signaler ligger i DP Der er stærk tilknytning mellem porte og sink/sources. Iport og Oport indeholder direkte sink og source som members, men sink og source kender kun deres forælder-port gennem en pointer. Pointers kan nemt reseates, så de peger på noget andet. Ved at erklære selve pointeren for const, kan dette undgås og forælder-porten kan stadig muteres. 51

58 12 dp -> addsignal (dp -> input (" clk ") ->inside, r-> input (" clk ") -> outside ); 13 dp -> addsignal (r-> output ("Q") ->outside, dp -> output (" cnt ") -> inside ); 14 dp -> addsignal (r-> output ("Q") ->outside, a-> input ("a") -> outside ); 15 dp -> addsignal (k-> output (" outp ") ->outside, a-> input ("b") -> outside ); 16 dp -> addsignal (a-> output (" sum ") ->outside, r-> input ("D") -> outside ); YACHT udskriver da outputd.dot som renderes af dot til figur som altså er ækvivalent med figur 6.6. Figur 6.8: Binær tæller: Ækvivalent med figur 6.6. Vi ser af figur 6.8 at både porte og signaler er angivet ved labels, og at REGcnt5_Q_53 er ét signal med to hoveder Modellering af tilstandsmaskine Mark fsm komponenten er som sagt lidt speciel: Det er den eneste komponent hvis funktionelle opførsel ikke er kendt på forhånd - den afhænger af C-kildeteksten. Ligesom datavejen beskrives strukturelt kan tilstandsmaskinen også beskrives strukturelt i form af noget outputog next_state-kombinatorik, samt et state-register. -Men veletablerede synteseværktøjer - som XST - er netop gode til logiksyntese fra RTL-beskrivelser. YACHT danner da et tilstandsdiagram udfra ASM-grafen: Som forklaret i [11, side 374] svarer ASM-grafen direkte til en moore tilstandsmaskine. Ud fra tilstandsdiagrammet kan en RTL-beskrivelse nemt udskrives som VHDL. fsm har: states En vector af moorenoder (som arver fra fsmnode). defaults En vector af FSMoutputs der angiver hvordan alle fsm s Oporte skal drives hvis en moorenode ikke selv angiver dette moorenode og moorekant Mark Modelleringen af moore tilstande er ligetil: En moore tilstand angiver output og har 1 eller flere udgående kanter: transitions En vector af udgående moorekanter. En moorekant har en betingelse og en next_state som peger på en moorenode. 52

59 out En vector af FSMoutputs, som er en struktur bestående af en Oport hos fsm samt værdien val som denne Oport skal drives med. Disse FSMoutputs tager præcedens over fsm s default FSMoutputs. En moorenode har desuden et navn, og véd hvilken fsm den tilhører. Som sagt indeholder en moorekant en betingelse. Vi lader betingelse være en abstrakt klasse som arves af de to konkrete betingelseelse og betingelseequals. betingelseelse betegner netop en else-branch og behøver ikke at indeholde yderligere information. betingelseequals betegner en sammenligning mellem lhs og rhs. rhs er blot en boolean: Komponenterne for de relationelle operatorer ligger jo i datavejen, hvorfor fsm blot skal betragte deres resulterende 1-bit signal. Dette begrænser dog fsm til kun vælge mellem maksimalt 2 branches i enhver tilstand. 2 branches er dog ikke nok for switch-case eller hvis if-statements samles, som vist i figur if (x ==2) { 2 foo1 ; 3 } else { 4 if (y==x) { 5 foo2 ; 6 } else { 7 foo3 ; 8 } 9 } x=2 S1 x2 AND else x=y foo1 foo2 foo3 S2 Ækvivalent: x2 AND xy Figur 6.9: Nestede if-statements kan (i det viste tilfælde) samles i én FSM tilstand, S1. Dette forudsætter dog at det nødvendige antal - 2 n i værste tilfælde for nestede ifstatements - komparatorer er allokeret i datavejen - ellers vil alle sammenligningerne som udført af S1 i figur 6.9 ikke kunne udføres i 1 cycle. lhs af betingelseequals tillader da fsm at udføre bitvise operationer - AND og OR - på 1-bit resultater af sammenligninger. NB Dette er modelleret i YACHT men ASMvisitor{2HW, 2HWconstrained} understøtter på nuværende tidspunkt ikke mere end 2 branches per ASMnode. Så selvom switchcase er understøttet på AST- og ASM-niveau, kan ASMvisitor{2HW, 2HWconstrained} ikke håndtere dem. 6.2 Semantik af beregningsoperatorer Mark Da vi har valgt en simpel 1:1-mapping mellem operatorer og komponenter som implementerer disse, er det vigtigt at kende til operatorernes opførsel på bit-niveau. Heraf er bit-bredder og signedness er vigtige parametre. Som tommelfingerregel, er C semantikken for operatorerne, at: 1. Bitbredden opgraderes: Bitbredden af resultatet af en operation, er maximummet af bitbredderne af argumenterne. 2. Nedgradér til unsigned: Hvis blot ét argument er unsigned, udføres operationen som værende unsigned. Altså kun hvis alle argumenter er signed, udføres operationen som værende signed. n-nestede if-statements har maksimalt 2 n branches. Hvis der kun udføres assignments i leaf if-statements, så er det muligt at samle disse branches som udgående kanter fra én tilstand. 53

60 I YACHT samles bitbredde og signedness i en binary_t struktur, og der kræves at alle exprs implementerer en widthsign-metode som netop returnerer en binary_t for returtypen af den give expr. Vi kan først udtale os om widthsign af en expr-node, når widthsign af dens børn er blevet bestemt; widthsign er derfor en depth-first rekursiv metode. Der er dog undtagelser til disse tommelfingerregler, hvorfor vi gengiver en oversigt: exprconst: Semantikken er her forskellig mellem C og C: C: signed int er default for konstant-literaler. Hvis et suffix er angivet, tager denne præcedens. C: C s default er fint hos computere hvor den indfødte datatype netop er (typisk 32-bits) int, men grundet tommelfingerregel 1 vil fx alle adders dermed være 32- bit. Bitbredden for konstanter repræsenteres dermed kun med det nødvendige antal bits i C. Signedness følger C semantikken. exprvar: binary_t findes ved at slå variablen op i symboltabellen. C semantikken følges, sammen med at en char er 8 bits, short er 16 bits, int og long er 32 bits. - Altså samme størrelser som på en normal 32-bit PC. Dermed erklærer int i;, en 32-bit signed variabel - som bindes til (mindst ) et 32-bits register. exprunaryop: +, -, ~: Samme binary_t som argumentet.!: Semantikken er her forskellig mellem C og C. Beskrives i afsnit C typecasts: Ikke implementeret i YACHT. Dermed findes der på nuværende tidspunkt, ingen mulighed for at nedgradere bitbredde eller ændring af signedness. exprbinaryop: +, -, *, /, %, &,, ^: Tommelfingerreglerne følges. <<, >>: Shift-amount (arg2) påvirker ikke widthsign. widthsign er dermed lig widthsign af arg1. &&,, ==,!=, >, <, >=, <=: Ligesom unær logisk not; semantikken er forskellig mellem C og C. Beskrives i afsnit exprternaryop:?:: Select-argumentet har ikke indflydelse på binary_t af resultatet. widthsign afhænger kun af True- og False-argumenterne - som netop følger tommelfingerreglerne. Eksempel på integer-type suffix: 47ul er en unsigned long. Der er dermed forskel på 0 (som er signed int) og 0u (som er unsigned int). Jvf. de globale funktioner supbitsunsigned og supbitssigned i filen util.h. Prefixet sup angiver netop supremum; altså den mindste øvre grænse. I tilfælde af register-deling kan en char-variabel godt bindes til et 16-bits register. 54

61 Bitbredder bestemmes altså via C semantikken og ikke ved de teoretiske bitbredder: Vi véd for fx multiplikation, at bitbredder adderes. C trunkerer uden advarsel, hvorfor vi gør det samme Afvigelse af relationelle operatorer og unær logisk not Mark De relationelle operatorer og den logiske negation, har det tilfælles at de ofte anvendes som betingelses-udtryk i if-statements og løkker. De opererer dermed med sandhedsværdier, og returnerer true eller false. Irriterende for os, repræsenteres true og false som hhv. 1 og 0 - ikke 1u og 0u. Returtypen af de relationelle operatorer (og dermed logisk not) er dermed en signed int. Dette kan vises ved et eksempel: 1 ( -10) / ( -(1u)) = 0 // Unsigned division, da blot ét argument er unsigned 2 ( -10) / ( -(1) ) = a // Signed division, da begge argument er signed 3 ( -10) / ( -(!(0) )) = a // Måske logisk - not bare fører signedness igennem? 4 ( -10) / ( -(!(0 u))) = a // Nej, 0u bliver signed true, altså signed division - Hvor vi netop udnytter signedness-afhængighed for division, som beskrevet i appendiks D. Dette er irriterende da vi - for at bevare denne semantik - må afsætte mindst 2 bits; hvilket er anti-intuitivt: I hardware ville man netop forvente at resutaltet af sammenligningen x==2 er et 1-bit signal. NB Vi bryder derfor med ANSI C semantikken her, og vælger at returtypen af disse relationelle operatorer altid er 1 bit unsigned. Konsekvensen heraf er ikke stor: Kun hvis et signed argument indgår i en uheldig operator, med det andet argument værende en relation - fx: x / (2*(y==3)). 6.3 Hardware generering Mark YACHT er som sagt modulært i kraft af visitor møsteret, hvorfor flere tilgange til hardwaregenerering kan eksistere side om side. Grundet tidspres blev først en simpel og naiv hardware-generator (ASMvisitor2HW) implementeret - således at sidste trin, udskrivning af VHDL, kunne laves før den ressourcedelte hardware-generation (ASMvisitor2HWconstrained). I den naive tilgang antages ubegrænsede resourcer, og i den ressourcedelte er ressourcerne brugerdefineret. Allokering er derfor trivielt i begge tilgange Naiv hardware generering Mark Den grundlæggende antagelse i den naive tilgang, er, at hver ASMnode mappes til én moore-tilstand. Dermed udføres alle beregninger i ASMnoden kombinatorisk og i 1 cycle, hvorfor scheduleringsproblemet forsvinder. Da vi her netop sigter efter det simpleste tænkelige mulige, vil bindingen blot være følgende: Hver gang en operator optræder i input kildeteksten, allokeres og bindes der til den tilsvarende komponent i datavejen. Hver log 2 (foo) angiver (næsten, jvf. supbitsunsigned og supbitssigned i util.h) bitbredden af foo. For x y, fås da: log 2 (x y) = log 2 (x) + log 2 (y). Vi kan netop betragte logisk not som en relationel operator, da:!(x) x==0. Switch er ikke repræsenteret her, da switch kigger på talværdien af sin betingelse ; ikke om den blot opfattes som true eller false. Dette er netop grunden til at switch-betingelsen ikke må normaliseres af ASTvisitorNormalize::conditionNorm. Division eller modulo, som beskrevet i appendiks D. 55

62 operator-komponent har altså kun én binding. Der er således intet loft over resourceforbruget. Desuden får hver variabel sit eget dedikerede register. -Den naive løsning er altså resource-ubegrænset Virkemåde af ASMvisitor2HW Mark ASMvisitor2HW har en række members og metoder til hjælp: read Et boolean flag som markerer for den nuværende expr, om der læses fra den eller skrives til den. Kun exprvar afhænger af dette flag. forcediports En stack af controls. En control angiver 1) en Iport i datavejen som ønskes drevet af FSM, 2) den værdi porten ønskes at blive drevet med i nuværende FSM-state samt 3) default-værdien når udenfor nuværende FSM-state. prepare Reads af registre er essentielt gratis. For writes skal vi have en multiplexer foran registeret hvis det tilskrives værdier fra mere end 1 signal. Da der netop allokeres en ny komponent per operatorer, vil hvert output-signal fra den kombinatoriske højreside af en assignment være forskelligt. Hvis der skrives til variablen fra mere end ét sted i kildeteksten, er der brug for en multiplexer. Multiplexers er derfor implicitte. Hvis der er brug for en multiplexer, tilføjes et input til denne hver gang variablen står på venstre side af en assignment i kildeteksten. prepare véd netop hvor mange steder i input kildeteksten, der skrives til en given variabel. getregister Sammenknytter variable med registere via et map. Hvis variablen eksiterer i mappet, er der allerede allokeret et register som så returneres. Hvis ikke, må vi oprette et nyt register i datavejen, forbinde det op med clock og clock-enable, og returnere det. En output-variabel er dog speciel da det repræsenterer både en Oport hos top samt det register som Oporten drives af. muxify En wrapper metode som når kaldt med en Iport in_port som argument, returnerer den ækvivalente Iport ret på multiplexeren foran in_port. muxify tilføjer desuden controls til forcediports, så FSM kontrollerer multiplexerens select, således at signal flowet løber fra ret gennem multiplexeren og ud til in_port. Virkemåden af ASMvisitor2HW er som følger: 1. Top-level komponenten oprettes med tilstandsmaskinen og datavejen som subkomponenter. Clock- og reset-signaler føres gennem til FSM og datavej. 2. ASM-grafen traverseres: (a) Når vi møder en ASMbox: i. Opret en ny moorenode. ii. Besøg skov af expr-træer i ASMboxen: Ved hvert besøg af en expr, returneres en port fremfor et signal. Men det er god kotyme at assert e, at vi fx ikke skriver til en exprconst. Stor fan-out øger dog propagation delay pga. øget kapacitans. Outputs deklareres i YACHT som fx: int bar$out; Outputs er synkrone og er derfor registreret. Output-porte af top drives af dedikerede registre. Dvs. en portgenerel: Kan kan være en Iport eller Oport. Dette viser sig mere bekvemt ved mødet af en assignment, hvor vi ellers skulle forbinde 2 signaler: YACHT tillader kun at tilføje source og sinks til ét signal - flere signaler kan ikke forbindes. 56

63 A. Hvis vi besøger højreside-subtræet (arg2) af en assignment - dvs. read==true - returneres Oporten for den expr. Hvis arg1 af en assignment besøges, returneres Iporten for den expr. Signal flowet er da fra arg2 til arg1, hvorfor vi opretter et internt signal i datavejen som forbinder disse to porte. B. Mødes en normal binær beregningsoperator, besøger vi først dennes to børn, arg1 og arg2; herved fås to Oporte. Afhængigt af operatorens type, allokeres en ny tilsvarende komponent k i datavejen. To interne signaler oprettes i datavejen, fra arg{1,2} s Oporte til k s to Iporte. Oporten af k returneres. C. Mødes en exprvar, slår vi først op i symboltabellen. Følgende tilfælde håndteres, i rækkefølge: Input Read Write En input- variabel er ikke en reel variabel, men blot et input-signal til toplevel komponenten. Noget egentlig hukkommelse repræsenterer det ikke. En port oprettes i top og datavejen, signal føres igennem. Den ny-oprettede Iport i datavejen, returneres. Hvis exprvar ikke er en input-variabel, repræsenterer det et rigtigt register. Hvis vi blot skal aflæse registerets værdi, kræves intet yderligere end at kalde getregister for variablen, og blot returnere registerets Q-port. Vi finder det tilsvarende register via getregister. Da vi netop fortager en write, skal vi sørge for at nuværende moorenode driver clock-enable høj. Dette opnås ved at pushe en control op på forcediports. Endnu et input tilføjes til en multiplexeren foran registerets D-port, hvis der er mere end 2 writes. Dermed returneres enten Iporten for registerets D-port, eller en Iport for multiplexeren foran D-porten. iii. Tøm stack af porte i datavejen (forcediports) som ønsker at blive drevet af FSM. Opret interne signaler fra FSM til DP, og tilføj FSMoutputs til moorenoden. iv. Tilføj en ubetinget udgående kant på moorenoden. Besøg næste ASMnode og få en pointer til dens moorenode (visitasm_getfsmstate). Link nuværende moorenode til den næste. (b) Når vi møder en ASMdiamond: i. Opret en ny moorenode. ii. Besøg betingelse, som er ASMdiamonds eneste expr-træ. A. Besøg expr-træ ligesom i punkt 2(a)iiB til 2(a)iiC. Punkt 2(a)iiA bliver ikke udført da assignments ikke indgår i betingelse. Af samme grund, skal ingen registre opdateres i denne cycle - hvorfor forcediports må være tom. Kun exprvar s kan returnere både en Iport eller Oport. Vi minder om at assignment også er en binær operator. Inputs deklareres i YACHT som fx: const volatile int foo$in; Et input repræsenterer blot et udefrakommende signal. 57

64 iii. forcediports er tom, så ingen kontrolsignaler tilføjes til FSM. -Men FSM skal dog betragte det statussignal fra betingelse. Oporten fra betingelse forbindes da op til FSM. iv. Det samme antal udgående kanter fra nuværende ASMdiamond tilføjes til moorenoden. Disse kanter er netop betingede og afspejler ASMdiamond ens kanter. Børne ASMnoder besøges, og der linkes til deres moorenoder Eksempel: Simpel counter med enable Fælles Giver vi nedenstående C-kildetekst til YACHT fås hardwaren som givet i figur Istedet for goto s kunne vi sagtens have brugt en while(1)-løkke, men da vi ikke har dead code elimination ville der blive genereret en unødvendig sammenligning 1!=0 i hardware. - Derved undgås en ASMdiamond som mappes til en moorenode, vi faktisk kan undvære. Vi får derfor en 1 state mindre i FSMen med goto s. 1 { 2 const volatile unsigned char incr$in ; 3 volatile unsigned char cnt$out ; 4 5 cnt$out = 0; // initialisér 6 7 igen : 8 if ( incr$in ) 9 cnt$out = cnt$out + 1; 10 goto igen ; 11 } Diagrammet i figur 6.10a er ikke helt intuitivt og behøver en forklaring: TOP1 er toplevel og indeholder FSM1 og DP1 som subkomponenter. FSM1 repræsenteres ikke dybere i figur 6.10a, da den er en blackbox. DP1 er ikke en blackbox hvorfor vi kan se dens subkomponenter. Bedst ville have været hvis vi kunne indlejre subkomponenter inden i deres parent-komponent - altså subgrafer i DOT outputtet. Det er også muligt at indlejre grafer i DOT, men DOT tillader ikke kanter fra node inde i en subgraf, at gå til omridset af subgrafen. DOT er altså vores begrænsning, og det næst-bedste vi kan gøre, er at lade parent komponenter fremstå lidt mere specielle: Omridset tegnes koncentrisk Eksempel: GCD Mark Som set og forklaret i eksemplet med den simple counter, fremgår diagrammet i figur 6.10a allerede kompliceret. -Mest på grund af at alle signaler samles i DP1, men også at alle porte og signaler vises med navne. For GCD-eksemplet og større designs, bliver denne problemstilling kun mere udtalt. Normalt er vi blot interesserede i hvordan komponenterne indbyrdes er forbundet i outputd.dot - labels på porte og signaler er egentligt bare i vejen for et hurtigt overblik. Eftersom YACHT udskriver outputd.dot som en ren tekstfil, kan vi nemt fjerne disse labels med det udbredte unix-værktøj sed(1). Vi kan desuden bede dot om at placere noder lidt tættere: 1 sed -e s /\(.*\) - >\(.*\) \[\(.*\) \];/\1 - >\2 [];/ outputd. dot dot - Gnodesep =0.1 - Granksep =0.2 - Tpng display Det inlinede GCD-eksempel fra afsnit 2.5 compiles da af YACHT til hardwaren i figur

65 (a) outputd.dot (b) outputfsm.dot Figur 6.10: Simpel binær counter: Diagram og tilstandsdiagram Ressourcedeling Mark Som sagt er YACHT ment til at udføre resource-begrænset HLS. Den naive tilgang er simpel men resource-ubegrænset, hvorfor vi introducerer endnu en hardware-generator - ASMvisitor2HWconstrained. -Her skal både registre og komponenter til beregninger kunne deles. Dermed tilføjes der generelt også multiplexere på inputs af beregningskomponenter og hver ASMnode ekspanderer til 1 eller flere FSM-tilstande i sekvens. Da YACHT tænkes brugerdrevet, skal brugeren angive antallet af det maks-tilladelige antal komponenter. Det faktiske antal af allokerede komponenter kan dog være mindre end det brugerspecificerede: Hvis brugeren har råd til 100 adders, men der kun er brug for en enkelt, vil også kun 1 adder blive allokeret. Dermed kan kun loftet brugerspecificeres - det eksakte antal kan ikke Scheduling og binding Mark Schedulering afgører for alle beregningsoperatorerne, i hvilken clock cycle de skal eksekvere. Med givne resource-begrænsninger, forsøger schedulering da at forkorte eksekveringstiden af en DFG ved at udnytte de allokerede hardware resourcer bedst muligt. Flere Undtagen reset. Vi minder om at YACHT ikke har noget specifkt koncept om DFGs: I stedet behandles exprsubtræer fra ASTet som DFG. 59

66 (a) outputd.dot (b) outputfsm.dot Figur 6.11: GCD med handshake: Diagram og tilstandsdiagram. scheduleringsalgoritmer findes; vi benytter den dynamiske list-schedulings algoritme som beskrevet i [4, side 233]. Til at assistere os med scheduleringen, udstyres alle exprs med: Et scheduled-flag som angiver om expr-noden allerede er blevet scheduleret; altså om den er blevet sat til at eksekvere i en bestemt clock cycle. En isready-metode som returnerer true hvis alle dens børn har scheduled flaget sat. Hvis noden selv er scheduleret, er den ikke ready. Leaf-exprs, som expr{const,var}, er ikke rigtige operatorer hvorfor disse ikke betragtes i scheduleringen: De er derfor altid markeret som scheduled, da kun unscheduled exprs forsøges at blive scheduleret - desuden ville operatorer ellers aldrig blive ready. Vi skal da schedulere (den tidligere kombinatoriske) højreside af assignments. Kun ready operatorer kan scheduleres i den i te clock cycle: Hvis en operator op har alle sine børn scheduleret i cycle i 1 eller før, kan op tidligst scheduleres i cycle i. Almindelig kausalitet gælder. Grundet begrænsede hardware resourcer, strides ready operatorer om de tilgængelige beregningskomponenter; fx kan en adder ikke udføre flere additioner i en cycle. Derfor kan op tidligst scheduleres i cycle i, men kan risikere at blive scheduleret senere. -Om dette bliver tilfældet afgøres af op s prioritet. Flere heuristiske prioritetsfunktioner findes; vi bruger mobillitet som beskrevet i [4, kap. 7]. Mobilliteten er essentielt 60

67 differensen mellem expr-nodernes placering i ASAP - og ALAP -scheduleringerne. Disse er simple scheduleringsalgoritmer, som ikke tager hensyn til resource-begrænsninger. Kort fortalt, vil ASAP schedulere så tidligt som dataafhængighederne tillader, og ALAP så sent som dataafhængighederne tillader. Vi udfører ikke ASAP eller ALAP hver for sig for derefter at tage differensen. Vores ækvivalente tilgang er baseret på den observation, at en operator arver minimaet af børnenes mobillitet. Mobilliteten af leaf-exprs bestemmes derfor først. Den maksimale dybde af DFG en bestemmes (maxdepth). Mobilliteten af leaf-exprs findes da som differensen mellem maxdepth og afstanden til deres respektive rod-expr. Mobilliteten af non-leaves bestemmes da ved at kalde setderivedmobillity for hver af disse. Virkemåde Den basale algoritme for dynamisk list-schedulering: Koden findes i 1. Bestem mobiliteter og indsæt alle operatorer i sættet V. 2. INSERT_READY_OPS: Find ready nodes i V og flyt over i hver respektive prioritets kø baseret på deres type: OP_ARITH_BINARY_{ADD, MULT}, etc. har hver deres prioritets kø. 3. Sålænge blot én prioritets kø ikke er tom: (a) Tæl clock cycle (Cstep ) op. (b) Loop hver prioritets kø igennem: i. funit : Loop det brugerdefinerede antal komponenter (op til loftet) igennem: A. Hvis prioritets køen for denne komponenttype er tom, så gå til næste prioritets kø i punkt 3b. B. Aflæs og fjern operator op med lavest mobillitet fra prioritets køen. C. Tilføj til schedule: op eksekverer i cycle Cstep og bindes til funit. D. Markér op som scheduleret. (c) INSERT_READY_OPS. Da der til hver operator-type tilknyttes en prioritets kø, haves i praksis i C++ et std ::map med operatorernes enum-type som nøgle, og std::priority_queue som værdi. Diffeq eksempel Med følgende input til YACHT fra [6, slide 11] (oprindeligt fra [8, side 665]): 1 { 2 int x, x1, y, y1, u, u1, c, a, dx; 3 c = x < a; 4 x1 = x + dx; 5 u1 = (u - (3* x)*(u*dx)) - (3* y)*dx; // u*dx 6 y1 = y + u* dx; // u* dx 7 } list_sched.cpp. Akronym for As Soon As Possible. Akronym for As Late As Possible. Dynamisk list-schedulering kan derfor betragtes som ASAP plus resource-begrænsninger. Vi husker på at DFG en kan indeholde en skov af expr-træer. Disse expr-træer er forskellige og deler ikke en fælles rod-expr. 61

68 - udskriver YACHT scheduleringen tekstuelt (printschedule): 1 cycle 0: type = 1, binding = 0, udfører : x+dx 2 cycle 0: type = 3, binding = 0, udfører : 3* x 3 cycle 0: type = 3, binding = 1, udfører : u* dx 4 cycle 0: type = 27, binding = 0, udfører : x<a 5 cycle 1: type = 3, binding = 0, udfører : (3* x)*( u* dx) 6 cycle 1: type = 3, binding = 1, udfører : u* dx 7 cycle 2: type = 1, binding = 0, udfører : y+( u* dx) 8 cycle 2: type = 2, binding = 0, udfører : u -((3* x)*( u* dx)) 9 cycle 2: type = 3, binding = 0, udfører : 3* y 10 cycle 3: type = 3, binding = 0, udfører : (3* y)* dx 11 cycle 4: type = 2, binding = 0, udfører : (u -((3* x)*( u* dx))) -((3* y)* dx) - Hvor vi har sat loftet til at være 2 multipliers, 1 adder, 1 subtractor og 100 mindre-end komponenter. Hvis dette tegnes op grafisk, fås netop figur * 0 * < 0 3 x u dx x dx x * * * u * dx + < x1 c a 3 * y y u + - * dx Bedre binding y1 - u1 Figur 6.12: Grafisk repræsentation af YACHTs schedule. Vi ser flere interessante ting i figur 6.12: 1. Kun 1 mindre-end operator optræder på trods af at vi har råd til der er nemlig kun brug for 1. Allokering af beregningskomponenterne er således en konsekvens af schedulering. schedule::actualusage udskriver det faktiske antal beregningskomponenter som skal allokeres. 2. Beregningen u*dx udføres to gange. Vi kunne nøjes med én, men YACHT er begrænset her. Det er dog et tilfælde at det er den samme multiplier#1 som udfører begge multiplikationer. Det er ikke muligt i DOT at specificere positioner af noder, så YACHT kan højest give en tekstrepræsentation af schedule. Dette ville kræve at vores DFG blev repræsenteret med en DAG, og ikke blot AST-exprs. 62

69 3. Komponenter med lave indices vil altid få scheduleret flere operationer grundet vores simple binding. Dette er ikke optimalt da input-multiplexers vil vokse sig store for disse komponenter. En bedre binding kan udføres hvis vi ballancerer mellem de komponenter som brugeren har råd til. Af figur 6.12 ser vi at multiplier#2 faktisk ikke behøver multiplexers på sine to inputs mens multiplier#1 får en 4-input mux foran sit arg2 input. Hvis vi istedet bandt rod-operationen i (3*y)*dx til multiplier#2 fremfor multiplier#1 i cycle 3, ville multiplexers blive fordelt en smule mere ligeligt - hvorved den maks-tilladelige clockingfrekvens f max øges. NB: Dette er dog ikke implementeret i YACHT Register allokering Mark Variable og temporære værdier som produceres i en cycle og skal bruges i en næste, må nødvendigvis blive gemt i registre for at overleve: Vi kan ikke forvente at den kombinatoriske komponent som producerede værdien, holder sit output indtil sidste read - andre operationer er scheduleret. RAG en angiver netop levetiderne for variable og temporære værdier. Input og output variable i C kildetekst er dog specielle variable: Inputs medfører ikke noget register, og outputs skal have dedikerede registre. RAG opstilles derfor kun for interne variable som ikke kan aflæses af omverdenen. Ud fra figur 6.12 dannes en suboptimal RAG via schedule::getkanter - jvf. figur Virkemåden af getkanter udelades da denne også bruger baglæns levetidsanalyse som forklaret i afsnit Cycle * 0 * < 0 Suboptimal RAG x u dx x dx x * * * u * dx + < x1 c a x u a y 3*x u*dx * * y dx + - y1 y u - u1 dx x1 c y1 u*dx (3*x)*(u*d) 3*y u-(3*x)*(u*dx) (3*y)*dx u1 Figur 6.13: Suboptimal RAG dannet ud fra scheduleret DFG. Vi kan tildele hver kant i RAGen fra figur 6.13 sit eget register - 16 registre skal da bruges. De levetids-kanter som ikke overlapper, kan dog dele et register. Vi ser desuden i figur 6.13 at der på intet tidspunkt bruges mere end 7 registre på samme tid - RAG en er tykkest i cycle 1 og 2. Left-edge algoritmen pakker netop disse kanter så tæt som muligt, hvorved antallet af registre kan reduceres. Efter left-edge er kørt, udskriver YACHT den optimerede RAG som tekst: 1 reg0 reg1 reg2 reg3 reg4 reg5 reg6 Multiplier#2 får dog nok alligevel brug for multiplexers på sine to inputs når vi besøger andre DFGs fra andre ASMnoder. 63

70 2 cycle 0: dx u y a x # # 3 cycle 1: dx u y x<a x+dx 3*x u*dx 4 cycle 2: dx u y x<a x+dx (3* x)*(u*dx) u*dx 5 cycle 3: dx y+(u*dx) u -((3* x)*(u*dx)) x<a x+dx 3*y # 6 cycle 4: (3*y)*dx y+(u*dx) u -((3* x)*(u*dx)) x<a x+dx # # 7 cycle 5: (u -((3* x)*(u*dx))) -((3* y)*dx) y+(u*dx) # x<a x+dx # # - Som svarer til højre side i figur Cycle reg0 reg1 reg2 reg3 reg4 reg5 reg u x y a u1 y1 c x1 dx u*dx 3*x u*dx (3*x)*(u*d) 3*y u-(3*x)*(u*dx) (3*y)*dx dx (3*y)*dx u1 u y1 y u-(3*x)*(u*dx) a c x x1 3*x (3*x)*(u*d) 3*y u*dx u*dx Figur 6.14: RAG/livstidstabel før og efter left-edge er blevet påtrykket. Vi skal dog nævne at: 1. {x1,c,y1,u1} er output-variable som med vilje når 1 cycle udover den schedulerede DFG. Dette er alene for at sikre at input-variable for DFGen i den efterfølgende ASMnode, ved i hvilke registre variablene ligger. 2. {x,u,dx,a,y} er input-variable som antages at eksistere i kraft af at punkt 1 antages håndteret korrekt for ASMnoderne lige før denne. Da disse variable bliver aflæst må de nødvendigvis være skrevet til før. Når vi aflæser sådan en input-variabel skal vi derfor vide i hvilket register som den ligger i. Dette komplicerer left-edge en smule da edges mellem RAGs i to efterfølgende DFGs skal flugte: Det skal være statisk kendt i hvilket register variablen ligger i - både i den DFG hvor variablen skrives og i den DFG hvor læses. -Denne edge-alignment, kan passende ske vha. ombytninger efter left-edge er kørt. NB: Dette er ikke implementeret i YACHT ASMvisitorProtect: Baglæns levetidsanalyse Mark Left-edge arbejder kun på én DFG (og dermed en ASMnode) ad gangen. Left-edge producerer en beviseligt optimal register allokering for lineære control flows som det i basic Oprindeligt blev left-edge anvendt til routing i EDA-værktøjer, og har senerehen fundet anvendelse inden for register allokering. Dvs. DFGen kan repræsenteres som en DAG (Directed Acyclic Graph). 64

71 blocks og ASMnodes, men kan ikke håndtere branches eller loops. Sådan en generel register allokering er kompleks - både konceptuelt og i køretid. [7] indeholder et resumé af forskellige tilgange til register allokering med enten loops eller branches: Håndtering af branches i DFG kan gøres via graffarvningsmetoder. Loops kan håndteres ved forstille sig figur 6.14 cirkulær. Herved fås ikke en livstidstabel som i figur 6.14, men en livstidscirkel. Denne model bryder dog sammen ved branches. Variable splitting: Loops kan brydes og en variabel som krydser det brudte loop, håndteres som to særskilte variable. [7] beskriver derefter deres egen tilgang som håndterer både loops og branches. Fælles for alle disse tilgange er at DFGen behandles som en samlet graf med loops og branch i. -Men hvorfor behandle DFGen som en samlet graf, når vi kan dekomponere en DFG-graf til flere DFG-DAGs? -Det er jo lige præcis hvad vi allerede har i ASM-grafen. -Vi foreslår derfor en konceptuelt langt simplere metode: Left-edge algoritmen kan generaliseres til irregulær control flow med loops og branches, hvis vi blot indfører konceptet om kunstige edges til beskyttelse af variable: Hvis en variabel er markeret protected, skal vi sørge for at variablen har en edge i RAGet som spænder hele RAGet. -Dette forhindrer variablen i at blive overskrevet og optimeret væk af left-edge. Vi giver et eksempel på hvad beskyttelse indebærer og hvorfor vi må håndtere det, i figur Af ASMbox#2 i figur 6.15 ser vi netop koden fra Diffeq-eksemplet. Diffeq-eksemplet bruger 7 registre som vi så i figur Koden er ens, hvorfor vi vil ende med 7 registre igen for RAGen af ASMbox#2 i figur Det er da nemt at se, at 7 registre er det maksimalt nødvendige for hele figur Men det betyder jo netop at foo bliver overskrevet i ASMbox#2 - netop fordi foo ikke bliver taget med i betragtning af left-edge. For at forhindre dette, skal vi - som der står antydet i ASMbox#2 - beskytte foo imod left-edge. Dette gøres nemmest - og uden modifikation af den grundlæggende left-edge algoritme - ved at tilføje en kunstig kant for foo i det uoptimerede RAG. Denne kunstige kant skal spænde hele RAGet, da foo lever både før og efter de cycles som angivet i RAGet. Left-edge kan nu udføres, og 8 registre skal da bruges. Som nævnt, skal vi dog - efter left-edge er kørt - sørge for at foo-kanten i ASMbox#2-RAGet flugter med foo-kanten i ASMbox#1-RAGet. -Herved undgår vi at flytte foo fra et register til et andet; vi kan lige så godt beholde foo i det samme. Denne flugtning kan fortages ved hele ombytninger af de kanter som to registre i ASMbox#2-RAGet har fået tildelt, efter left-edge er kørt - dette er ikke implementeret i YACHT. Dvs. at DFGen ikke kan repræsenteres som en DAG, men kun som en (cyklisk) graf. Normalt fortages register allokering via konfliktgrafer og graffarvning. I YACHT har vi begrænset udviklingstid og kun grundlæggende funktionalitet er nødvendig - køretid indgår ikke i vores overvejelser. Men er en nødvendighed for at vores register allokering vil virke i praksis. 65

72 1 { 2 int x,x1,y,y1,u,u1,c,a,dx,foo,z; 3 4 // Skriv til foo 5 foo = 0 xde1e7ed ; 6 7 // Ligegyldige beregninger herunder 8 x = 0; 9 c = x < a; 10 x1 = x + dx; 11 u1 = u -(3* x)*(u*dx) -(3*y)*dx; 12 y1 = y + u* dx; 13 x = x1; 14 u = u1; 15 y = y1; // foo skal overleve ovenstående 18 y = foo +1; 19 } Figur 6.15: Linært control flow: foo må overleve indtil sidste read. Men hvordan bestemmer vi hvornår en variabel skal markeres som protected? Dette opnås ved levetidsanalyse, med en lille forskel: Vi ser af figur 6.15, at når foo læses for sidste gang, er den ikke længere markeret som protected. protect og life af en variabel er stærkt relaterede koncepter: 1. En variable betegnes som værende live efter den ASMbox hvor den bliver (over)skrevet. Grundet getkanter er vi garanteret at variablen lever i slutningen af den ASMbox som (over)skriver variablen. 2. En variabel betegnes live i alle de ASMnoder på enhver sti mellem en write og en read. 3. En variabel betegnes protect i alle de ASMnoder på enhver sti mellem en write og en read, undtagen sidste read: I den sidste read vil getkanter garantere at variablen indgår som kant. Variablen lever dog ikke længere efter denne sidste read, hvorfor vi ikke behøver at lade variablen leve i resten af RAGet; ingen kunstige kanter behøves. Disse sidste-read noder kalder vi for EOL -noder. Vi indeler desuden ASMnoder i 4 kategorier: N Neutral: Udfører ingen operationer på variablen, hverken reads eller write. W Write: Skriver til variablen, da den står på venstresiden af en assignment. R Read: Læser fra variablen, da den står på højresiden af en assignment eller indgår i en betingelse. RW Read+Write: Variablen står på både venstre- og højresiden af en assignment. Fx: x=x+1. Vi opbygger da sættet af R-noder, traverserer baglæns herfra og markerer variablen som protect i alle R og N noder som vi møder på vores vej - indtil en {W,WR,allerede-besøgt}-node mødes som vi stopper traverseringen ved. -Det var blot én traversering, en traversering End Of Life 66

73 for hver R-node udføres. Stopper vi nu - efter alle de mange traverseringer - har vi netop bestemt life for variablen i alle ASMnoder. protect er lidt mere end life, hvorfor vi herefter bestemmer EOL-noderne: En R-node er EOL hvis variablen er markeret som live i den, men de efterfølgende noder ikke har den markeret som live. Sættet af EOL-noder opbygges, og vi går da ind og fjerner live-mærkaten hos alle disse EOL-noder - tilbage bliver netop protect-sættet. Det er ASMvisitorProtect som udfører denne baglæns levetidsanalyse og markerer variable som protected - en gennemkørsel er forsøgt illustreret i figur Grøn traversering starter først. Og fra denne R-node protect R R Rød-traversing går ikke vidre, da R-noden allerede er besøgt under grøntraversering. protect N Sidste read af variablen (EOL): Intet protect. N protect N Write; vi stopper W protect R protect N Rød går ikke vidre, dar-noden allerede er besøgt af grøn. R Sidste read af variablen (EOL): Intet protect. Figur 6.16: Baglæns levetidsanalyse af ASM med irregulært flow. Endeligt gives et detaljeret eksempel fra ex1 -eksemplet i [7, figur 1a] - jvf. figur 6.17 og Variablen kan ikke blive markeret protect flere gange, da denne information gemmes i et std::set. 67

74 1 { 2 int a, b, c, d, x, y, z, foo ; 3 4 // Lad a, b leve inden if - statement 5 // -som angivet i ex1 6 a = 12; 7 b = 14; 8 9 if (a > b) { 10 x = a +1; 11 c = x* x; 12 } else { 13 do { 14 y = -b; 15 z = b +1; 16 c = y* z; 17 b = z+c; 18 } while (b > 0); 19 } // Brug b, c som angivet i ex1 22 foo = b+c; 23 } Figur 6.17: Ækvivalent C kode (venstre) for ex1 -eksemplet i [7, figur 1a] (højre) Virkemåde af ASMvisitor2HWconstrained Mark NB Grundet tidspres er ASMvisitor2HWconstrained er ikke færdigskrevet! Vi vurderer at 70% er skrevet: Allokering, binding og schedulering af beregningskomponenter samt elaborering af multiplexers er gjort - bortset set fra specialtilfælde er opbygning af datavejen således komplet. Det som mangler er, at færdiggøre getregister og håndtere ASMdiamonds. Derfor beskrives her blot hvordan ASMvisitor2HWconstrained - på et højt niveau - tænkes implementeret. Ligesom hos ASMvisitor2HW er 2 gennemløb er nødvendige : 1. Allokering af komponenter i datavejen, samt elaborering af multiplexers foran Iporte. Det er nødvendigt for det efterfølgende gennemløb, at vide om der er brug for en multiplexer foran et input til en komponent. 2. Med kendskab til multiplexers, kan FSM konstrueres og de resterende signaler oprettes. Ydermere er prepare ikke længere tilstrækkelig til at bestemme multiplexers: prepare indsamlede netop information om hvor mange steder i kildeteksten, variable blev skrevet til; men multiplexers optræder nu også foran beregningselementer. Vi initialiserer ligesom i ASMvisitor2HW, og: 1. Når vi møder en ASMbox eller ASMdiamond: (a) Schedulér og bind operatorer og udfør register allokering for denne ASMnode. Gem resultaterne af disse i std::map-members således at schedule og RAG for denne ASMnode kan læses ved besøget af andre. ASMvisitor2HW kunne gøres ved 1 gennemløb, dog bruger prepare også et gennemløb på at samle information om multiplexers - altså reelt set 2 gennemløb. 68

75 D Q D Q D Q D Q D Q D Q D Q D Q D Q D Q D Q D Q D Q Cycle hvor FSM er i state S0: Write til reg0 og reg1. a og b lever først efter S0: I S1 og nedefter S Rising clock edge Register 0 til venstre i RAG Register 1 til højre i RAG Lad efterfølgende state vide i hvilke registre a ogb er gemt. a b b a Cycle hvor FSM er i state S1: Intet write udføres. Lad ikke a og b dø: Fordi a og b bruges herunder (og derfor er protected) skal efterfølgende state vide i hvilke registre a og b er gemt. S1 D Q D Q a b > 0 Til FSM 1 b a 0 Vi ved at fx b ligger i reg1 for der kan vi se at S0 lagde den Kanter i RAG forlænget da a ogb er protected B-kanten her skal flugte med b-kanten fra S1 og SB5 SA2 D Q a a b b bruges slet ikke her, men vi beskytter den alligevel for b bruges senere. SB2-0 b b b x x y z z y SA3 D Q x x SB3 D Q y z D Q * 0 c b x c * 0 c z y c SB4 z c + 0 b c z b D SB5 Q b > 0 Til FSM 0 b c 0 1 S2 b c + 0 D Q foo b c foo (a) ASM fra YACHT. (b) Detaljeret CDFG/ASM med kunstige edges indsat. Figur 6.18: ex1 -eksemplet fra [7]. 69

76 (b) Traversér ASM-grafen (visitasm). Da dette sker rekursivt, vil schedule og RAG for alle ASMnoder være gemt i de nævnte std::map-members, når visitasm returnerer. (c) Besøg skov af expr-træer i ASMnoden. En portgenerel returneres ved besøget af en expr. Men i modsætning til ASMvisitor2HW returneres Q-Oporten af det register som resultatet af expr-komponenten gemmes i. -Dette register findes ved at slå expr en op i RAG et for denne ASMnode (getregister). Ved besøget af sådan en expr-node, tilføjes til forcediports, at dette register skal clockes af FSM i den cycle hvor expr en er scheduleret. Hvis dette er gennemløb #1 tilføjes der til et std::map over multiplexers, portene for expr s børn. Hvis gennemløb #2 og denne expr har multiplexers, kan vi yderligere tilføje til forcediports, at disse multiplexers skal drives tilsvarende. (d) Efter al expr-payload er besøgt, er moorenodes fyldt op med kontrolsignaler som FSM skal sætte. Sidste moorenode for denne ASMnode linkes til første moorenode for efterfølgende ASMnode. Hvis denne ASMnode er en diamond, håndteres de udgående kanter ligesom i ASMvisitor2HW. Her menes den til expr svarende komponent. For at finde denne komponent, slås expr en op i schedule. 70

77 Kapitel 7 HWvisitor2VHDL: VHDL output Jesper For at få outputtet VHDL, bruges visitor mønstret til at traversere HW-grafen. Under traverseringen mødes de forskellige hardware dele(komponenter, porte etc.) hvor hver komponent er en node i hardware grafen. Én komponent udskrives til en selvstændig VHDL-fil, hvilket betyder at mødes en adder-komponent bliver der udskrevet en VHDL-fil kaldet adder.vhd. Vi bruger numeric_std pakken, som tillader alle de nummeriske funktioner vi skal bruge i YACHT. Pakken gør yderligere at vi kan nøjes med at bruge eksempelvis +-operatoren for en adder - i stedet for at vi skal initialisere vores egen +-operator, på enten gate eller RTL-niveau. Når VHDL outputtes bliver den egentlige arkitektur bestemt. Har vi igen eksemplet for en adder, så kan denne designes på forskellige måder og den endelige komponent bliver bestemt af numeric_std pakken. For mindre designs defaultes til ripply carrie adderen - ønskes der en Carry-Loakahed adder, må denne selv skrives. To addere instantieres fra hver deres adder-komponent - ikke fra en fælles generisk adder. HDL-genereringen vil simplificere dette betragteligt. Der sker dog en begrænsning ved at bruge numeric_std pakken, da den ikke tillader division - definitionen for divsion kan ses i Appendix C.2. Er konstante bitskift ikke tilstrækkeligt og har man derimod brug for en reel divider, må brugeren skrive denne selv - vi har i dette projekt dog valgt ikke at definere nogle artimetiske funktioner. Divideren skal dog være ren kombinatorisk, da YACHT antager at alle beregningskomponenter er kombinatoriske. Dog vil konstante divisions udtryk blive håndteret pga. constant foldingnormaliseringen som nævnt tidligere, hvor f.eks. 8/3 bliver til 2.66, der bliver rundet ned til 2 pga. floor. Multiplikation derimod, kan anvendes grundet FPGA en har en eller flere dedikeret muliplier. Nedenfor kan ses en funktion som er udtaget fra vores C++ kode fra hw_visitor.cpp. Funktionen beskriver hvordan hardware visitoren udskriver en adder til VHDL: 1 void HWvisitor2VHDL :: visit ( adder * n) { 2 using boost :: str ; 3 using boost :: format ; 4 5 string ret ; // Samlet som skal tilføjes til VHDL filen 6 string body ; 7 8 body = str ( format ("\t %1% <= std_logic_vector (%2% + %3%) ;") 9 % n-> output (" outp ") -> navn 10 % Typecast (n-> input ("a"), n-> output (" outp ") ->num ) 11 % Typecast (n-> input ("b"), n-> output (" outp ") ->num ) 12 ); 13 std_logic_arith og std_logic_unsigned bruges ikke, da disse ikke er standard. Kun numeric_std og std_logic(_vector) bruges. Jævnfør kapitel 4.3 som omhandler normalisering af AST visitoren. 71

78 14 assert (n- > inputs. size () == 2); // adder har kun 2 inputs 15 assert (n- > outputs. size () == 1); // og 1 output ret += Libraries ( n); // Libraries som initialiseres for en adder. 18 ret += "\n"; 19 ret += entity ( n); // Deklaration af entiteten. 20 ret += "\n"; 21 ret += arch (n, body ); // Architecturen tofile (n- >navn, ret ); // Skriv inholdet i stringen ret til en VHDL fil. 24 visit_children_of ( n); // Besøg børnene. 25 } Det kan ses at vi igen anvender boost format i stedet for snprintf. Stringen ret indeholder den samlede VHDL fil for adderen som skal udskrives. Body en er defineret som selve operationen der skal udføres, hvor vi i dette eksempel adderer to inputs. Fra numeric_std bruges resize funktionen til at typsecaste input størrelserne til outputstørrelsen, hvilket beytyder: at haves et input på 8-bit og 16-bit - vil 8-bit-delen opgraderes til 16-bit, hvor efter de to input størrelser lagt sammen netop vil give en maksimal output størrelse på 16-bit. Det er her vigtigt at nævne at C-semantikken netop opgraderer på denne måde. Efter body-definitionen laves to test, som tester at vores input og output har det rigtige antal inputs hhv. output. Der haves tre member metoder for hardware visitoren, nemlig Libraries, entity og arch. Libraries-metoden inkluderer IEEE, std_logic_1164 og numeric_std bibliotekerne, som er nødvendige for alle vores VHDL-filer. Entity-metoden er dog lidt mere kompliceret, da den er generel for alle vores komponenter og den outputter entiteten til en string, jævnfør hw_visitor.cpp linje 882 til 911 for yderlige specificering. Til sidst haves arch-metoden som udskriver arkitekturen for alle komponenterne i en string, hvor der først erklæres interne signaler som derefter forbindes til komponentens porte. Til sidst instantieres subkomponenterne og disse forbindes op til signalerne i port map. Stringen ret udskrives til sidst til en VHDL-fil og adder-komgponentens børn besøges. Det er anbefalet at der ikke bruges hverken std_logic_arith eller std_logic_unsigned, hvorfor vi har valgt at bruge numeric_std. Kilde: 72

79 Kapitel 8 Afrunding 8.1 Case: GCD Fælles YACHT er modulært opbygget af diverse visitorer. Der haves to forskellige tilgange til hardwaregenerering, ASMvisitor2HW og ASMvisitor2HWconstrained. Kun ASMvisitor2HW er testet, da ASMvisitor2HWconstrained endnu ikke er komplet. Vi giver derfor kun et eksempel på syntese-resultaterne af ASMvisitor2HW. Både GCD-modulet og stimulus-generatoren kan skrives i C som vist i figur 8.2. YA- CHT køres altså to gange og de resulterende TOP1-moduler forbindes i en test_bench.vhd fil som skrives manuelt. En behavioral simulation af denne testbench, fortaget i ModelSim, kan ses i figur 8.1. Figur 8.1: Timingdiagram af behavioral simulation af GCD-testbenchen i ModelSim. Periodetiden sat til 50 ns. GCD-modulet (venstre side af figur 8.2) er blevet syntetiseret af XST med få ændringer i VHDL-koden og lagt ned på en Xilinx Spartan-3E FPGA. RTL-schematic af DP1 som renderet i Xilinx ISE 10.1, kan ses i figur 8.3. XST s synteserapporten melder om f max 151 MHz hvoraf interconnect udgør lidt under 20% af den kritiske vej. -Dette er ikke dårligt når man tænker på at target FPGA en clockes med 50 MHz; for Virtex-II chips vil f max være højere. GCD-modulet optager 2% af de tilgængelige slices. Linux time(1) kommando rapporterer at YACHT er cirka 300 ms om syntesen af GCD eksemplet. 73

Syntaks og syntaksgenkendelse, særligt regulære udtryk og tilstandsmaskiner og lidt om anvendelser i bioinformatik

Syntaks og syntaksgenkendelse, særligt regulære udtryk og tilstandsmaskiner og lidt om anvendelser i bioinformatik Datalogi C, RUC Forelæsning 22. november 2004 Henning Christiansen Syntaks og syntaksgenkendelse, særligt regulære udtryk og tilstandsmaskiner og lidt om anvendelser i bioinformatik Dagens program Hvad

Læs mere

Speciale. Evaluering af Java til udvikling af indlejrede realtidssystemer ved brug af en eksisterende Java Optimized Processor (JOP)

Speciale. Evaluering af Java til udvikling af indlejrede realtidssystemer ved brug af en eksisterende Java Optimized Processor (JOP) Speciale Evaluering af Java til udvikling af indlejrede realtidssystemer ved brug af en eksisterende Java Optimized Processor (JOP) Speciale efterår 2005 Teknisk Informationsteknologi Jan Lauritzen & Mads

Læs mere

Sproget Six. Til brug i rapportopgaven på kurset Oversættere. Vinter 2006. Abstract

Sproget Six. Til brug i rapportopgaven på kurset Oversættere. Vinter 2006. Abstract Sproget Six Til brug i rapportopgaven på kurset Oversættere Vinter 2006 Abstract Six er baseret på det sprog, der vises i figur 6.2 og 6.4 i Basics of Compiler Design. Den herværende tekst beskriver basissproget

Læs mere

DATALOGI 1E. Skriftlig eksamen torsdag den 3. juni 2004

DATALOGI 1E. Skriftlig eksamen torsdag den 3. juni 2004 Københavns Universitet Naturvidenskabelig Embedseksamen DATALOGI 1E Skriftlig eksamen torsdag den 3. juni 2004 Opgaverne vægtes i forhold til tidsangivelsen herunder, og hver opgaves besvarelse bedømmes

Læs mere

DM507 Algoritmer og datastrukturer

DM507 Algoritmer og datastrukturer DM507 Algoritmer og datastrukturer Forår 2016 Projekt, del I Institut for matematik og datalogi Syddansk Universitet 29. februar, 2016 Dette projekt udleveres i tre dele. Hver del har sin deadline, således

Læs mere

Programmering i C. Lektion september 2009

Programmering i C. Lektion september 2009 Programmering i C Lektion 1 8. september 2009 Målgruppe Motivation Indhold Form Materiale 2 / 47 Kursusintroduktion 1 Målgruppe 2 Motivation 3 Indhold 4 Form 5 Materiale Målgruppe Motivation Indhold Form

Læs mere

Åben uddannelse, Efterår 1996, Oversættere og køretidsomgivelser

Åben uddannelse, Efterår 1996, Oversættere og køretidsomgivelser 3/10/96 Seminaret den 26/10 vil omhandle den sidste fase af analysen og de første skridt i kodegenereringen. Det drejer sig om at finde betydningen af programmet, nu hvor leksikalsk og syntaktisk analyse

Læs mere

DM507 Algoritmer og datastrukturer

DM507 Algoritmer og datastrukturer DM507 Algoritmer og datastrukturer Forår 2018 Projekt, del II Institut for matematik og datalogi Syddansk Universitet 13. marts, 2018 Dette projekt udleveres i tre dele. Hver del har sin deadline, således

Læs mere

ECE 551: Digital System * Design & Synthesis Lecture Set 5

ECE 551: Digital System * Design & Synthesis Lecture Set 5 ECE 551: Digital System * Design & Synthesis Lecture Set 5 5.1: Verilog Behavioral Model for Finite State Machines (FSMs) 5.2: Verilog Simulation I/O and 2001 Standard (In Separate File) 3/4/2003 1 ECE

Læs mere

Introduktion til funktioner, moduler og scopes i Python

Introduktion til funktioner, moduler og scopes i Python Denne guide er oprindeligt udgivet på Eksperten.dk Introduktion til funktioner, moduler og scopes i Python Denne artikel er fortsættelsen af "I gang med Python", som blevet publiceret her på sitet for

Læs mere

Start af nyt schematic projekt i Quartus II

Start af nyt schematic projekt i Quartus II Start af nyt schematic projekt i Quartus II Det følgende er ikke fremstillet som en brugsanvisning der gennemgår alle de muligheder der er omkring oprettelse af et Schematic projekt i Quartus II men kun

Læs mere

DM507 Algoritmer og datastrukturer

DM507 Algoritmer og datastrukturer DM507 Algoritmer og datastrukturer Forår 2019 Projekt, del I Institut for matematik og datalogi Syddansk Universitet 27. februar, 2019 Dette projekt udleveres i tre dele. Hver del har sin deadline, således

Læs mere

Arduino Programmering

Arduino Programmering Microcontroller, Arduino I teknologi skal vi lære at lave programmer til uc for at have muligheden til eksamen at kunne lave intelligente el-produkter. I hvert fald skal vi have set mulighederne, og forstået

Læs mere

DM507 Algoritmer og datastrukturer

DM507 Algoritmer og datastrukturer DM507 Algoritmer og datastrukturer Forår 2018 Projekt, del II Institut for matematik og datalogi Syddansk Universitet 20. marts, 2019 Dette projekt udleveres i tre dele. Hver del har sin deadline, således

Læs mere

IT opgave. Informationsteknologi B. Vejleder: Karl. Navn: Devran Kücükyildiz. Klasse: 2,4

IT opgave. Informationsteknologi B. Vejleder: Karl. Navn: Devran Kücükyildiz. Klasse: 2,4 IT opgave Informationsteknologi B Vejleder: Karl Navn: Devran Kücükyildiz Klasse: 2,4 Dato:03-03-2009 1 Indholdsfortegnelse 1. Indledning... 3 2. Planlægning... 3 Kommunikationsplanlægning... 3 Problemstillingen...

Læs mere

Programmering for begyndere Lektion 2. Opsamling mm

Programmering for begyndere Lektion 2. Opsamling mm Lektion 2 Opsamling mm God tone Der er indlagt spørge sessioner Lektion 2 - Agenda Programmering for Lidt ændringer til teknikken, herunder hvordan du genser en lektion Lidt generelle tilbagemeldinger

Læs mere

Grådige algoritmer. Et generelt algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer.

Grådige algoritmer. Et generelt algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer. Grådige algoritmer Grådige algoritmer Et generelt algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer. Grådige algoritmer Et generelt algoritme-konstruktionsprincip ( paradigme ) for

Læs mere

DM507 Algoritmer og datastrukturer

DM507 Algoritmer og datastrukturer DM507 Algoritmer og datastrukturer Forår 2016 Projekt, del III Institut for matematik og datalogi Syddansk Universitet 20. april, 2016 Dette projekt udleveres i tre dele. Hver del har sin deadline, således

Læs mere

Design by Contract Bertrand Meyer Design and Programming by Contract. Oversigt. Prædikater

Design by Contract Bertrand Meyer Design and Programming by Contract. Oversigt. Prædikater Design by Contract Bertrand Meyer 1986 Design and Programming by Contract Michael R. Hansen & Anne Haxthausen mrh@imm.dtu.dk Informatics and Mathematical Modelling Technical University of Denmark Design

Læs mere

Sproget Rascal (v. 2)

Sproget Rascal (v. 2) Sproget Rascal (v. 2) Til brug i K1 på kurset Oversættere Opdateret 29/11 2004 Abstract Rascal er et simpelt Pascal-lignende imperativt sprog. Dette dokument beskriver uformelt Rascals syntaks og semantik

Læs mere

Abstrakte datatyper C#-version

Abstrakte datatyper C#-version Note til Programmeringsteknologi Akademiuddannelsen i Informationsteknologi Abstrakte datatyper C#-version Finn Nordbjerg 1/9 Abstrakte Datatyper Denne note introducerer kort begrebet abstrakt datatype

Læs mere

Bits DM534. Rolf Fagerberg, 2012

Bits DM534. Rolf Fagerberg, 2012 Bits DM534 Rolf Fagerberg, 2012 Resume af sidst Overblik over kursus Introduktion. Tre pointer: Datalogi er menneskeskabt og dynamisk. Tidslinie over fremskridt mht. ideer og hardware. Algoritme er et

Læs mere

Grådige algoritmer. Et generelt algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer.

Grådige algoritmer. Et generelt algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer. Grådige algoritmer Grådige algoritmer Et generelt algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer. Grådige algoritmer Et generelt algoritme-konstruktionsprincip ( paradigme ) for

Læs mere

Lær Python dag 1 - modul 1

Lær Python dag 1 - modul 1 Lær Python dag 1 - modul 1 Introduktion, basis python Steffen Berg Klenow Jonas Bamse Andersen Syddansk Universitet Indhold 1. Velkommen 2. Programmering i python 3. Typer, variabler og udtryk 1 Velkommen

Læs mere

DM507 Algoritmer og datastrukturer

DM507 Algoritmer og datastrukturer DM507 Algoritmer og datastrukturer Forår 2017 Projekt, del III Institut for matematik og datalogi Syddansk Universitet 6. april, 2017 Dette projekt udleveres i tre dele. Hver del har sin deadline, således

Læs mere

DM507 Algoritmer og datastrukturer

DM507 Algoritmer og datastrukturer DM507 Algoritmer og datastrukturer Forår 2019 Projekt, del III Institut for matematik og datalogi Syddansk Universitet 10. april, 2019 Dette projekt udleveres i tre dele. Hver del har sin deadline, således

Læs mere

Simulering af en Mux2

Simulering af en Mux2 Simulering af en Mux2 Indhold Start QuartusII op start et nyt projekt.... 2 Fitter opsætning... 6 Opstart af nyt Block diagram... 8 ModelSim... 14 Hvis man vil ændre data grafisk kan det også lade sig

Læs mere

Grådige algoritmer. Et generelt algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer.

Grådige algoritmer. Et generelt algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer. Grådige algoritmer Grådige algoritmer Et generelt algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer. Grådige algoritmer Et generelt algoritme-konstruktionsprincip ( paradigme ) for

Læs mere

DM507 Algoritmer og datastrukturer

DM507 Algoritmer og datastrukturer DM507 Algoritmer og datastrukturer Forår 2012 Projekt, del II Institut for matematik og datalogi Syddansk Universitet 15. marts, 2012 Dette projekt udleveres i tre dele. Hver del har sin deadline, således

Læs mere

Algoritmeskabeloner: Sweep- og søgealgoritmer C#-version

Algoritmeskabeloner: Sweep- og søgealgoritmer C#-version Note til Programmeringsteknologi Akademiuddannelsen i Informationsteknologi Algoritmeskabeloner: Sweep- og søgealgoritmer C#-version Finn Nordbjerg 1/9 Indledning I det følgende introduceres et par abstrakte

Læs mere

Design by Contract. Design and Programming by Contract. Oversigt. Prædikater

Design by Contract. Design and Programming by Contract. Oversigt. Prædikater Design by Contract Design and Programming by Contract Anne Haxthausen ah@imm.dtu.dk Informatics and Mathematical Modelling Technical University of Denmark Design by Contract er en teknik til at specificere

Læs mere

BRP Tal. Om computer-repræsentation og -manipulation. Logaritmer

BRP Tal. Om computer-repræsentation og -manipulation. Logaritmer BRP 13.9.2006 Tal. Om computer-repræsentation og -manipulation. Logaritmer 1. Opgaverne til i dag dækker det meste af stoffet 2. Resten af stoffet logaritmer binære træer 3. Øvelse ny programmeringsopgave

Læs mere

Programmering i C Intro og grundlæggende C 5. marts 2007

Programmering i C Intro og grundlæggende C 5. marts 2007 Programmering i C Intro og grundlæggende C 5. marts 2007 Mads Pedersen, OZ6HR mads@oz6hr.dk Plan for kurset Ma. 5/3: Ma. 19/3: Ma. 2/4: To. 12/4: Formål, intro, grundlæggende Videre, sprogkonstruktioner

Læs mere

University of Southern Denmark Syddansk Universitet. DM502 Forelæsning 3

University of Southern Denmark Syddansk Universitet. DM502 Forelæsning 3 DM502 Forelæsning 3 Indlæsning fra tastatur Udskrift til skærm Repetition Beregning af middelværdi Gentagelse med stop-betingelse (while) Heltalsdivision Division med nul Type-casting ( (double) ) Betinget

Læs mere

DM507 Algoritmer og datastrukturer

DM507 Algoritmer og datastrukturer DM507 Algoritmer og datastrukturer Forår 2013 Projekt, del I Institut for matematik og datalogi Syddansk Universitet 5. marts, 2013 Dette projekt udleveres i to dele. Hver del har sin deadline, således

Læs mere

DM536. Rapport og debug

DM536. Rapport og debug DM536 Rapport og debug Kilder Vigtig.it (Felix Palludan Hargreaves) http://vigtig.it/dm502/howto_report.pdf http://vigtig.it/blog/teaching/#toc-relevant-tips Peter Schneider-Kamp http://imada.sdu.dk/~petersk/dm536/project2.pdf

Læs mere

Database for udviklere. Jan Lund Madsen PBS10107

Database for udviklere. Jan Lund Madsen PBS10107 Database for udviklere Jan Lund Madsen PBS10107 Indhold LINQ... 3 LINQ to SQL og Arkitektur... 3 O/R designere... 5 LINQ Den store introduktion med.net 3.5 er uden tvivl LINQ(udtales link): Language-INtegrated

Læs mere

Python programmering. Per Tøfting. MacFest

Python programmering. Per Tøfting. MacFest Python programmering MacFest 2005 Per Tøfting http://pertoefting.dk/macfest/ Indhold Måder at afvikle Python program på Variabler Data typer Tal Sekvenser Strenge Tupler Lister Dictionaries Kontrolstrukturer

Læs mere

Programmering C Eksamensprojekt. Lavet af Suayb Köse & Nikolaj Egholk Jakobsen

Programmering C Eksamensprojekt. Lavet af Suayb Köse & Nikolaj Egholk Jakobsen Programmering C Eksamensprojekt Lavet af Suayb Köse & Nikolaj Egholk Jakobsen Indledning Analyse Læring er en svær størrelse. Der er hele tiden fokus fra politikerne på, hvordan de danske skoleelever kan

Læs mere

Microcontroller, Arduino

Microcontroller, Arduino Microcontroller, Arduino Programmerbar elektronik. uc Vi skal lære at lave programmer til uc for at kunne lave el-produkter. Forstå princippet i programmering af en uc og se mulighederne. Programmeringen

Læs mere

A Profile for Safety Critical Java

A Profile for Safety Critical Java A Profile for Safety Critical Java Martin Schoeberl Hans Søndergaard Bent Thomsen Anders P. Ravn Præsenteret af: Henrik Kragh-Hansen November 8, 2007 Forfatterne Martin Schoeberl Udvikler af JOP processoren

Læs mere

Optimering af fraværsregistrering

Optimering af fraværsregistrering Journal Optimering af fraværsregistrering Eksamensprojekt i Programmering C, klasse 3.4, 2011 AFLEVERET 09-05-2014 Indhold Abstract... Fejl! Bogmærke er ikke defineret. Problemformulering... 2 Produktet...

Læs mere

22 Hobe. Noter. PS1 -- Hobe. Binære hobe. Minimum-hob og maximum-hob. Den abstrakte datatype minimum-hob. Opbygning af hobe. Operationen siv-ned.

22 Hobe. Noter. PS1 -- Hobe. Binære hobe. Minimum-hob og maximum-hob. Den abstrakte datatype minimum-hob. Opbygning af hobe. Operationen siv-ned. 22 Hobe. Binære hobe. Minimum-hob og maximum-hob. Den abstrakte datatype minimum-hob. Opbygning af hobe. Operationen siv-ned. Indsættelse i hobe. Sletning af minimalt element i hobe. Repræsentation. 327

Læs mere

Skriftlig eksamen, Programmer som Data Onsdag 6. januar Spørgsmål 1 (20 %): Regulære udtryk og automater

Skriftlig eksamen, Programmer som Data Onsdag 6. januar Spørgsmål 1 (20 %): Regulære udtryk og automater Skriftlig eksamen, Programmer som Data Onsdag 6. januar 2010 Dette eksamenssæt har 5 sider. Tjek med det samme at du har alle siderne. Eksamens varighed er 4 timer. Der er fire spørgmål. For at få fuldt

Læs mere

Bits, bit operationer, integers og floating point

Bits, bit operationer, integers og floating point Denne guide er oprindeligt udgivet på Eksperten.dk Bits, bit operationer, integers og floating point Denne artikel beskriver hvordan data gemmes som bits og hvordan man kan manipulere med bits. Den forudsætter

Læs mere

Noter til C# Programmering Selektion

Noter til C# Programmering Selektion Noter til C# Programmering Selektion Sætninger Alle sætninger i C# slutter med et semikolon. En sætning kontrollerer sekvensen i programafviklingen, evaluerer et udtryk eller gør ingenting Blanktegn Mellemrum,

Læs mere

LØKKER METODER C S HISTORIE. Grundlæggende programmering Lektion 4

LØKKER METODER C S HISTORIE. Grundlæggende programmering Lektion 4 LØKKER METODER C S HISTORIE Grundlæggende programmering Lektion 4 1 LEKTIER UML struktur diagrammer om biler 2 OPERATORER Syntaks til at udføre forskellige beregninger og handlinger Booleans og hvorfor

Læs mere

Software Dokumentation

Software Dokumentation Software Dokumentation Jan Boddum Larsen Teknologi B og A på HTX Dokumentation af software i Teknologi I samfundet sker der en bevægelse mod mere digitale løsninger i teknologi. Det betyder at software

Læs mere

Kontrol-strukturer i PHP

Kontrol-strukturer i PHP Denne guide er oprindeligt udgivet på Eksperten.dk Kontrol-strukturer i PHP Denne artikel gennemgår kontrolstrukturer i PHP. 'if', 'switch', 'while' og 'for' bliver gennemgået. Den forudsætter lidt grundlæggende

Læs mere

Sortering. Eksempel: De n tal i sorteret orden

Sortering. Eksempel: De n tal i sorteret orden Sortering 1 / 34 Sortering Input: Output: Eksempel: n tal De n tal i sorteret orden 6, 2, 9, 4, 5, 1, 4, 3 1, 2, 3, 4, 4, 5, 9 2 / 34 Sortering Input: Output: Eksempel: n tal De n tal i sorteret orden

Læs mere

Netværksalgoritmer 1

Netværksalgoritmer 1 Netværksalgoritmer 1 Netværksalgoritmer Netværksalgoritmer er algoritmer, der udføres på et netværk af computere Deres udførelse er distribueret Omfatter algoritmer for, hvorledes routere sender pakker

Læs mere

Greenfoot En kort introduktion til Programmering og Objekt-Orientering

Greenfoot En kort introduktion til Programmering og Objekt-Orientering Greenfoot En kort introduktion til Programmering og Objekt-Orientering Greenfoot er et computer-program, som kan benyttes til at skrive andre computer-programmer, i et programmeringssprog kaldet Java.

Læs mere

Skriftlig eksamen i Datalogi

Skriftlig eksamen i Datalogi Roskilde Universitetscenter Skriftlig eksamen i Datalogi Modul 1 Vinter 1998/99 Opgavesættet består af 5 opgaver, der ved bedømmelsen tillægges følgende vægte: Opgave 1 16% Opgave 2 12% Opgave 3 10% Opgave

Læs mere

Journal JTAG: Udarbejde af: Benjamin Grydehøj I samarbejde med PDA Projektgruppen. Elektronikteknologafdelingen på Erhvervsakademi Fyn.

Journal JTAG: Udarbejde af: Benjamin Grydehøj I samarbejde med PDA Projektgruppen. Elektronikteknologafdelingen på Erhvervsakademi Fyn. Journal JTAG: Udarbejde af: Benjamin Grydehøj I samarbejde med PDA Projektgruppen Elektronikteknologafdelingen på Erhvervsakademi Fyn. Journal JTAG Xilinx XC9536 29-9-3 Generel beskrivelse af JTAG: JTAG:

Læs mere

Processer og tråde. dopsys 1

Processer og tråde. dopsys 1 Processer og tråde dopsys 1 Motivation.. parallelle processer udnytter hardwaren bedre: Batch operativsystemer (50 erne) hhv. små systemer: Multiprogrammering og time-sharing (fra 60 erne og frem): dopsys

Læs mere

Fagets IT Introduktion til MATLAB

Fagets IT Introduktion til MATLAB Fagets IT Introduktion til MATLAB Mads G. Christensen mgc@kom.auc.dk Afdeling for Kommunikationsteknologi, Aalborg Universitet. MATLAB 2002 p.1/28 Kursusoversigt 1. Introduktion, matrix-indeksering, -operationer

Læs mere

Repræsentation af tal

Repræsentation af tal Repræsentation af tal DM534 Rolf Fagerberg 1 / 18 Mål Målet for disse slides er at beskrive, hvordan tal repræsenteres som bitmønstre i computere. Dette emne er et uddrag af kurset DM548 Computerarkitektur

Læs mere

Skriftlig eksamen, Programmer som Data 2. 3. januar 2014

Skriftlig eksamen, Programmer som Data 2. 3. januar 2014 Skriftlig eksamen, Programmer som Data 2. 3. januar 2014 Dette eksamenssæt har 5 sider. Tjek med det samme at du har alle siderne. Eksamenssættet udleveres elektronisk fra kursets hjemmeside torsdag 2.

Læs mere

JavaScript. nedarvning.

JavaScript. nedarvning. JavaScript er et sprog, der kan give en hjemmeside mere funktionalitet og gøre den interaktiv, så den reagerer på læsernes handlinger. CGI (Common Gateway Interface) har hidtil været de protokoller, man

Læs mere

Start på Arduino og programmering

Start på Arduino og programmering Programmering for begyndere Brug af Arduino Start på Arduino og programmering EDR Hillerød Knud Krogsgaard Jensen / OZ1QK 1 Start på Arduino og programmering Sidste gang (Introduktion) Programmeringssproget

Læs mere

Online kursus: Programming with ANSI C

Online kursus: Programming with ANSI C Online kursus 365 dage DKK 1.999 Nr. 90198 P ekskl. moms Denne kursuspakke giver dig et bredt kendskab til sproget C, hvis standarder er specificeret af American National Standards Institute (ANSI). Kurserne

Læs mere

Indhold. Maskinstruktur... 3. Kapitel 1. Assemblersprog...3. 1.1 Indledning...3 1.2 Hop-instruktioner... 7 1.3 Input og output...

Indhold. Maskinstruktur... 3. Kapitel 1. Assemblersprog...3. 1.1 Indledning...3 1.2 Hop-instruktioner... 7 1.3 Input og output... Indhold Maskinstruktur... 3 Kapitel 1. Assemblersprog...3 1.1 Indledning...3 1.2 Hop-instruktioner... 7 1.3 Input og output... 9 Kapitel 2. Maskinkode... 13 2.1 Den fysiske maskine... 13 2.2 Assemblerens

Læs mere

DATALOGI 1E. Skriftlig eksamen fredag den 7. juni 2002

DATALOGI 1E. Skriftlig eksamen fredag den 7. juni 2002 Københavns Universitet Naturvidenskabelig Embedseksamen DATALOGI 1E Skriftlig eksamen fredag den 7. juni 2002 Opgaverne vægtes i forhold til tidsangivelsen, og hver opgaves besvarelse bedømmes som en helhed.

Læs mere

Skriftlig Eksamen Algoritmer og Datastrukturer (dads)

Skriftlig Eksamen Algoritmer og Datastrukturer (dads) Skriftlig Eksamen Algoritmer og Datastrukturer (dads) Datalogisk Institut Aarhus Universitet Mandag den 27. maj 2002, kl. 9.00 13.00 Opgave 1 (25%) Denne opgave handler om multiplikation af positive heltal.

Læs mere

Indholdsfortegnelse Indledning... 2 Projektbeskrivelse... 2 Dette bruger vi i projektet... 2 Komponenter... 2 Software... 2 Kalibrering...

Indholdsfortegnelse Indledning... 2 Projektbeskrivelse... 2 Dette bruger vi i projektet... 2 Komponenter... 2 Software... 2 Kalibrering... Indholdsfortegnelse Indledning... 2 Projektbeskrivelse... 2 Dette bruger vi i projektet... 2 Komponenter... 2 Software... 2 Kalibrering... 3 Kildekoden... 4 Variabler... 4 Setup... 4 Loop... 4 Indledning

Læs mere

Projekt arbejde til kurset Advanced Models and Programs, SASP-AMP 2008 Poul Brønnum, IT Universitetet i København, Maj 2008. Indholdsfortegnelse

Projekt arbejde til kurset Advanced Models and Programs, SASP-AMP 2008 Poul Brønnum, IT Universitetet i København, Maj 2008. Indholdsfortegnelse Udvidelse af MicroC compileren til at kunne håndtere struct- og union typer samt switch-, break- og continue statement. Yderligere er ovenstående konstruktioner anvendt til at indføre malloc og free kald

Læs mere

Sortering. Eksempel: De n tal i sorteret orden

Sortering. Eksempel: De n tal i sorteret orden Sortering 1 / 32 Sortering Input: Output: Eksempel: n tal De n tal i sorteret orden 6, 2, 9, 4, 5, 1, 4, 3 1, 2, 3, 4, 4, 5, 9 2 / 32 Sortering Input: Output: Eksempel: n tal De n tal i sorteret orden

Læs mere

Eksempel: Skat i år 2000

Eksempel: Skat i år 2000 Kursus 02199: Programmering afsnit 2.1-2.7 Anne Haxthausen IMM, DTU 1. Værdier og typer (bl.a. char, boolean, int, double) (afsnit 2.4) 2. Variable og konstanter (afsnit 2.3) 3. Sætninger (bl.a. assignments)

Læs mere

Sortering af information er en fundamental og central opgave.

Sortering af information er en fundamental og central opgave. Sortering Sortering Input: Output: Eksempel: n tal De n tal i sorteret orden 6, 2, 9, 4, 5, 1, 4, 3 1, 2, 3, 4, 4, 5, 9 Mange opgaver er hurtigere i sorteret information (tænk på ordbøger, telefonbøger,

Læs mere

Oversættere Skriftlig eksamen onsdag d. 19. april 2006

Oversættere Skriftlig eksamen onsdag d. 19. april 2006 Københavns Universitet Naturvidenskabelig Embedseksamen Oversættere Skriftlig eksamen onsdag d. 19. april 2006 Eksamenstiden er to timer. Opgavernes vægt i procent er angivet ved hver opgave. Den skriftlige

Læs mere

Målet for disse slides er at diskutere nogle metoder til at gemme og hente data effektivt.

Målet for disse slides er at diskutere nogle metoder til at gemme og hente data effektivt. Merging og hashing Mål Målet for disse slides er at diskutere nogle metoder til at gemme og hente data effektivt. Dette emne er et uddrag af kurset DM507 Algoritmer og datastrukturer (2. semester). Mål

Læs mere

26 Programbeviser I. Noter. PS1 -- Programbeviser I. Bevis kontra 'check af assertions' i Eiffel. Betingelser og bevisregler.

26 Programbeviser I. Noter. PS1 -- Programbeviser I. Bevis kontra 'check af assertions' i Eiffel. Betingelser og bevisregler. 26 Programbeviser I. Bevis kontra 'check af assertions' i Eiffel. Betingelser og bevisregler. Hvad er programverifikation? Bevisregel for 'tom kommando'. Bevisregel for assignment. Bevisregler for selektive

Læs mere

Datalogi OB, Efterår 2002 OH er, forelæsning 3/9-2002 - forstå datastrukturer og algoritmer (teoretisk forståelse og intuition)

Datalogi OB, Efterår 2002 OH er, forelæsning 3/9-2002 - forstå datastrukturer og algoritmer (teoretisk forståelse og intuition) Datalogi OB, Efterår 2002 OH er, forelæsning 3/9-2002 Datastrukturer og algoritmer Henning Christiansen henning@ruc.dk http://www.ruc.dk/~henning Formål: at kunne - forstå datastrukturer og algoritmer

Læs mere

Danmarks Tekniske Universitet

Danmarks Tekniske Universitet side af sider Danmarks Tekniske Universitet Skriftlig prøve, den 6. maj 0. Kursusnavn: Algoritmer og datastrukturer I Kursus nr. 005. Tilladte hjælpemidler: Skriftlige hjælpemidler. Varighed: timer Vægtning

Læs mere

Grådige algoritmer. Et algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer.

Grådige algoritmer. Et algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer. Grådige algoritmer Grådige algoritmer Et algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer. Grådige algoritmer Et algoritme-konstruktionsprincip ( paradigme ) for optimeringsproblemer.

Læs mere

Programmering i C. Lektion 4. 5. december 2008

Programmering i C. Lektion 4. 5. december 2008 Programmering i C Lektion 4 5. december 2008 Funktioner Eksempel Fra sidst 1 Funktioner 2 Eksempel Funktioner Eksempel Eksempel: 1 / f u n k t i o n s p r o t o t y p e r / i n t i n d l a e s ( void )

Læs mere

DATALOGI 1E. Skriftlig eksamen mandag den 23. juni 2003

DATALOGI 1E. Skriftlig eksamen mandag den 23. juni 2003 Københavns Universitet Naturvidenskabelig Embedseksamen DATALOGI 1E Skriftlig eksamen mandag den 23. juni 2003 Opgaverne vægtes i forhold til tidsangivelsen herunder, og hver opgaves besvarelse bedømmes

Læs mere

Studiepraktik. Thomas Bøgholm Mikkel Hansen Jacob Elefsen

Studiepraktik. Thomas Bøgholm Mikkel Hansen Jacob Elefsen Studiepraktik Thomas Bøgholm boegholm@cs.aau.dk Mikkel Hansen mhan@cs.aau.dk Jacob Elefsen jelefs12@student.aau.dk 1 Studiepraktik -- program Program onsdag 10.00 10.15 Registrering af fremmøde og gennemgang

Læs mere

Programmering i C. Lektion oktober 2008

Programmering i C. Lektion oktober 2008 Programmering i C Lektion 2 20 oktober 2008 Historie Processen At kompilere 2 / 23 Fra sidst 1 Historie 2 Processen 3 At kompilere Historie Processen At kompilere 3 / 23 ALGOL 60 1963 CPL 1966 BCPL 1969

Læs mere

Software Construction 1. semester (SWC) januar 2014 Spørgsmål 1

Software Construction 1. semester (SWC) januar 2014 Spørgsmål 1 Spørgsmål 1 Grundlæggende objektorienterede begreber o Klasse (class) o Objekt (object) o Metode (method), herunder return type og parametre o Instansvariable (instance variables) & egenskaber (properties),

Læs mere

Sortering af information er en fundamental og central opgave.

Sortering af information er en fundamental og central opgave. Sortering 1 / 36 Sortering Input: Output: Eksempel: n tal De n tal i sorteret orden 6, 2, 9, 4, 5, 1, 4, 3 1, 2, 3, 4, 4, 5, 6, 9 Mange opgaver er hurtigere i sorteret information (tænk på ordbøger, telefonbøger,

Læs mere

DM507 Algoritmer og datastrukturer

DM507 Algoritmer og datastrukturer DM507 Algoritmer og datastrukturer Forår 2015 Projekt, del I Institut for matematik og datalogi Syddansk Universitet 3. marts, 2015 Dette projekt udleveres i to dele. Hver del har sin deadline, således

Læs mere

Oversættere Skriftlig eksamen onsdag d. 24. januar 2007

Oversættere Skriftlig eksamen onsdag d. 24. januar 2007 Københavns Universitet Naturvidenskabelig Embedseksamen Oversættere Skriftlig eksamen onsdag d. 24. januar 2007 Eksamenstiden er to timer. Opgavernes vægt i procent er angivet ved hver opgave. Den skriftlige

Læs mere

Michael Jokil 11-05-2012

Michael Jokil 11-05-2012 HTX, RTG Det skrå kast Informationsteknologi B Michael Jokil 11-05-2012 Indholdsfortegnelse Indledning... 3 Teori... 3 Kravspecifikationer... 4 Design... 4 Funktionalitet... 4 Brugerflade... 4 Implementering...

Læs mere

Programmering C RTG - 3.3 09-02-2015

Programmering C RTG - 3.3 09-02-2015 Indholdsfortegnelse Formål... 2 Opgave formulering... 2 Krav til dokumentation af programmer... 3 ASCII tabel... 4 Værktøjer... 5 Versioner af ASCII tabel... 6 v1.9... 6 Problemer og mangler... 6 v2.1...

Læs mere

Kursusgang 11. Oversigt: Sidste kursusgang Værktøjer til udvikling og implementering af HCI-design Oversigt over Java Swing

Kursusgang 11. Oversigt: Sidste kursusgang Værktøjer til udvikling og implementering af HCI-design Oversigt over Java Swing Kursusgang 11 Oversigt: Sidste kursusgang Værktøjer til udvikling og implementering af HCI-design Oversigt over Java Swing Design af brugerflader 11.1 Samme sted Forskellige steder Sidste kursusgang Samtidigt

Læs mere

Induktive og rekursive definitioner

Induktive og rekursive definitioner Induktive og rekursive definitioner Denne note omhandler matematiske objekter, som formelt er opbygget fra et antal basale byggesten, kaldet basistilfælde eller blot basis, ved gentagen brug af et antal

Læs mere

Struktureret Test og Værktøjer Appendiks til bogen Struktureret Test

Struktureret Test og Værktøjer Appendiks til bogen Struktureret Test Struktureret Test og Værktøjer Appendiks til bogen Struktureret Test Struktureret Test og Værktøjer... 1 Appendiks til bogen Struktureret Test... 1 1. Definition og formål... 2 2. Kategorisering... 2 2.1

Læs mere

Kapitel 4 Løkker i C#

Kapitel 4 Løkker i C# Kapitel 4 Løkker i C# Løkker en vigtig del af alle programmeringssprog, og C# er ikke andeles. En løkke er en måde at udføre en del af koden gentagne gange. Ideen er at du fortsætter med at udføre en opgave

Læs mere

Boolsk algebra For IT studerende

Boolsk algebra For IT studerende Boolsk algebra For IT studerende Henrik Kressner Indholdsfortegnelse 1 Indledning...2 2 Logiske kredsløb...3 Eksempel:...3 Operatorer...4 NOT operatoren...4 AND operatoren...5 OR operatoren...6 XOR operatoren...7

Læs mere

dcomnet-nr. 8 Simpel aritmetik på maskinniveau Computere og Netværk (dcomnet)

dcomnet-nr. 8 Simpel aritmetik på maskinniveau Computere og Netværk (dcomnet) dcomnet-nr. 8 Simpel aritmetik på maskinniveau Computere og Netværk (dcomnet) Efterår 2009 1 Simpel aritmetik på maskinniveau I SCO, appendix A, er det beskrevet, hvordan man adderer ikke-negative heltal

Læs mere

Skriftlig eksamen i Datalogi

Skriftlig eksamen i Datalogi Roskilde Universitetscenter side 1 af 11 sider Skriftlig eksamen i Datalogi Modul 1 Sommer 2000 Opgavesættet består af 6 opgaver, der ved bedømmelsen tillægges følgende vægte: Opgave 1 10% Opgave 2 10%

Læs mere

Succesfuld implementering af automatiseret test

Succesfuld implementering af automatiseret test Succesfuld implementering af automatiseret test Forudsætningerne og faldgruberne John Fodeh john.fodeh@hp.com 2006 Hewlett-Packard Development Company, L.P. The information contained herein is subject

Læs mere

Programmering og Problemløsning, 2017

Programmering og Problemløsning, 2017 Programmering og Problemløsning, 2017 Træstrukturer Part III Martin Elsman Datalogisk Institut Københavns Universitet DIKU 3. November, 2017 Martin Elsman (DIKU) Programmering og Problemløsning, 2017 3.

Læs mere

App til indmelding af glemt check ud

App til indmelding af glemt check ud App koncept til indmelding af glemt check ud App til indmelding af glemt check ud 5. mar. 2015 Side 1 App koncept til indmelding af glemt check ud 1 Introduktion Flg. er en besvarelse til en idekonkurrence

Læs mere

Test af It-komponent

Test af It-komponent Test af It-komponent I programmeringssproget Java Programmet Login service Elev: Mads Funch Klasse 2.4 Mat, It, Programmering Skole: Roskilde Tekniske Gymnasium HTX Underviser: Karl Dato: 31-08-2016 Side

Læs mere

Hvem er vi? Kursus Introduktion. Kursuslærerne. Agenda for i dag

Hvem er vi? Kursus Introduktion. Kursuslærerne. Agenda for i dag Hvem er vi? Kursus Introduktion Anne Haxthausen ah@imm.dtu.dk Informatics and Mathematical Modelling Technical University of Denmark 100 studerende med forskellig baggrund: software teknologi It og Kom

Læs mere

Mandags Chancen. En optimal spilstrategi. Erik Vestergaard

Mandags Chancen. En optimal spilstrategi. Erik Vestergaard Mandags Chancen En optimal spilstrategi Erik Vestergaard Spilleregler denne note skal vi studere en optimal spilstrategi i det spil, som i fjernsynet går under navnet Mandags Chancen. Spillets regler er

Læs mere

Skriftlig Eksamen Algoritmer og Datastrukturer (DM507)

Skriftlig Eksamen Algoritmer og Datastrukturer (DM507) Skriftlig Eksamen Algoritmer og Datastrukturer (DM507) Institut for Matematik og Datalogi Syddansk Universitet, Odense Mandag den 7. juni 00, kl. 9 Alle sædvanlige hjælpemidler (lærebøger, notater, osv.)

Læs mere