• Keine Ergebnisse gefunden

Seminar Programmiersprachen und Programmiersysteme: Funktionale Hardwarebeschreibung mit Lava

N/A
N/A
Protected

Academic year: 2022

Aktie "Seminar Programmiersprachen und Programmiersysteme: Funktionale Hardwarebeschreibung mit Lava"

Copied!
23
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Seminar Programmiersprachen und Programmiersysteme:

Funktionale Hardwarebeschreibung mit Lava

Bastian Kirchmayr 31. Mai 2010

Basierend auf [CSS03] und [Cla00]

Betreuung: Prof. Dr. Michael Hanus

(2)

Inhaltsverzeichnis

1 Einf¨uhrung 2

1.1 Einfache Schaltungen . . . 2

1.2 Eingebettete Sprachen . . . 4

1.3 Einbettung in Haskell. . . 5

2 Lava 7 2.1 Listen . . . 7

2.2 Parameter und Eingaben . . . 7

2.3 Schaltwerke . . . 8

2.4 Verifikation . . . 9

3 Implementation 11 3.1 Problematik . . . 11

3.2 Direkter Ansatz . . . 12

3.3 Monadischer Ansatz . . . 14

3.4 Shared Observables . . . 16

4 Realisierung in FPGAs 19

5 Zusammenfassung und Ausblick 21

(3)

1 Einf¨ uhrung

Lava ist eine in Haskell eingebettete Sprache, mit der sich Hardware beschreiben l¨asst.

Schaltungen lassen sich im Wesentlichen auf zwei Arten beschreiben: Einerseits in struktureller Form, dabei wird beschrieben, welche Komponenten in welcher Wei- se verbunden sind, diese bilden zusammen die Funktionalit¨at. Andererseits in Form einer Verhaltensbeschreibung, dabei wird die Funktionalit¨at eher wie in einer klassi- schen Programmiersprache beschrieben und mit Werkzeugen in eine strukturelle Be- schreibung synthetisiert. G¨angige Hardwarebeschreibungssprachen, wie VHDL und Verilog, unterst¨utzen oft beide Arten der Programmierung, in Lava wird aber nur mit struktureller Beschreibung gearbeitet.

Im Gegensatz zu diesen Sprachen bietet Lava M¨oglichkeiten an, mit denen kom- plexe Strukturen leichter beschrieben werden k¨onnen, als mit sehr einfachen Metho- den, wie for-Schleifen.

Lava erlaubt keine Kontrolle von Timing und anderen Low-Level-Effekten, son- dern beschreibt idealisierte, synchrone Schaltungen, in denen alle Komponenten ei- nem impliziten, globalen Taktsignal folgen.

Lava selbst ist in die funktionale Sprache Haskell eingebunden. Der Program- mierer erh¨alt damit automatisch Zugriff auf alle Konzepte, die einer funktionalen Programmiersprache zur Verf¨ugung stehen, um die Struktur seiner Schaltungen zu beschreiben. Die Idee, Eigenschaften der funktionalen Programmierung in der Hard- warebeschreibung zu nutzen, ist nicht neu: so gibt es mit µFP [She85] und Ruby [She90] Sprachen, die darauf setzen, Schaltungen mit Verbindungsmustern struk- turiert zu beschreiben. Auch Lava tut dies, ist aber, durch die Ausnutzung der Freiheiten von Haskell, nicht auf diesen Stil und vorgegebene Muster beschr¨ankt.

In Lava beschriebene Schaltungen k¨onnen mit konkreten Eingaben simuliert, auf bestimmte Eigenschaften ¨uberpr¨uft und in reale Schaltungen synthetisiert werden.

Letzteres wird realisiert durch Export in andere Beschreibungsformen, z.B. VHDL oder EDIF. Damit kann Lava auch in FPGAs realisiert werden und wird z.B. von Xi- linx Inc. verwendet, um bestimmte Komponenten in FPGAs zu beschreiben. [CSS03]

1.1 Einfache Schaltungen

Eine Bitleitung, in der einfachsten Interpretation, ist ein Datentyp, der stets entwe- der logisch 1 oder 0 enth¨alt, auch bezeichnet als hohe oder niedrige Spannung. Die entsprechenden Konstantenfunktionen daf¨ur heißen high und low.

Die Namen der Datentypen einer Bitleitung sind f¨ur verschiedene Versionen von Lava unterschiedlich, wie Signal, Signal Bool und Bit, den wir hier verwenden wer- den. Schaltungen selbst sind Funktionen h¨oherer Ordnung, die sich zusammenste- cken lassen. Daf¨ur enth¨alt die Bibliothek Standardgatter, wie and2 und xor2, als Elementarkomponenten.

(4)

Das erste Beispiel ist ein Halbaddierer, der zwei Bits (Leitungen) als Eingabe bekommt, diese zusammenz¨ahlt und ein Summenbit und ein ¨Uberlaufbit ausgibt.

Listing 1: Ein Halbaddierer import Lava

h a l f A d d : : (B i t,B i t) −> (B i t,B i t) h a l f A d d (a,b) = (s,c o u t)

where s = x o r 2 (a,b) c o u t = and2 (a,b)

Im Modul Lava liegen die bereitgestellten Funktionen und Typen. Wir werden die Importanweisung zuk¨unftig weggelassen. Wie jede Schaltung in Lava isthalfAdd eine Funktion, die zu ihren Eingabeleitungen Ausgabeleitungen liefert. Dabei m¨ussen Eingabe und Ausgabe immer genau einen Funktionsparameter darstellen, mehrere Leitungen also zusammengefasst werden, in diesem Fall als Tupel.

Die eigentliche Verschaltung liegt hier in der Benennung von Einzelleitungen s und cout als Summen- und ¨Uberlaufbit. Im where-Teil werden s und cout dann durch Verkn¨upfung der Eingaben in einem XOR- bzw. UND-Gatter beschrieben, die normale Implementation eines Halbaddierers.

Nun kann man die Schaltung mit konkreten Eingaben simulieren, und so die Funktionalit¨at der Schaltung ein erstes Mal testen. Nach der Installation von Lava ist die Datei ein normales Haskellprogamm und kann z.B. mit Hugs oder GHC benutzt werden.

∗Main> s i m u l a t e h a l f A d d (h i g h ,h i g h) (low ,h i g h)

∗Main> s i m u l a t e h a l f A d d (low,h i g h) (h i g h ,l o w)

Die Funktion simulate liefert, nach Eingabe der zu simulierenden Funktion und einer konkreten Eingabe, die Ausgabe der Schaltung. Hier sehen wir, dass halfAdd die erwarteten Ausgaben macht.

Gebaute Schaltungen lassen sich genau wie die Standardkomponenten benutzen, um z.B. aus einem gegebenen Multiplexer einen Demultiplexer aufzubauen, der auf eine Ausgabeleitung high und auf alle anderen low legt. Ein Multiplexer hat als Eingabe ein Steuerbit und ein Paar von Leitungen, von denen der Wert der ers- ten ausgegeben wird, falls die Steuerleitung den Wert low hat, sonst der Wert der zweiten. Lava stellt einen Multiplexer unter dem Namenmux bereit.

(5)

Listing 2: Ein Demultiplexer demux : : (B i t,B i t ,B i t) −> (B i t ,B i t) demux (s ,e,d) = o u t

where o u t = (a0,a1)

a1 = mux (s , (d,e) ) a0 = mux (s , (e,d) )

Die Eingabes ist hier die Steuerleitung,e ist der Wert, der auf die ausgew¨ahlte Leitung gelegt wird, d auf alle anderen, typischerweise wirdd low und e high sein.

Die Ausgabea0 wird nach dem ersten mux e, fallss den Wertlow hat, sonstd. F¨ur a1 ist es umgekehrt, damit ergibt sich genau ein 2-Bit Demultiplexer.

Die Eingabeleitungen sind wieder in einem Tupel zusammengefasst, allerdings h¨atte man hier anstatt alle drei Eingaben in einem Tupel zu gruppieren, auch die Ausgabewertee und d in ein eigenes Tupel geben k¨onnen, wie esmux macht.

Bei dieser Schaltung wird deutlich, dass Leitungen auch nur im where-Bereich genutzt werden k¨onnen, um Subkomponenten zu verbinden, hiera0 unda1, und die Abh¨angigkeiten der Leitungen nicht von der Reihenfolge das Aufschriebs abh¨angen.

1.2 Eingebettete Sprachen

Anstatt eine solche Sprache eingebettet zu implementieren, h¨atte man sie auch ei- genst¨andig bauen k¨onnen. Der Vorteil dieses Ansatzes w¨aren eine gr¨oßere Freiheit in der Syntax der Sprache, um sie optimal an die Anforderungen anzupassen. Die V¨ater von Lava haben sich dagegen entschieden, weil der eingebettete Ansatz auch eine Reihe von Vorteilen bietet.

Eine neue Sprache muss sehr viel weitgehender getestet werden, es m¨ussen Werk- zeuge, wie Interpreter, ¨Ubersetzer, Debugger etc. erschaffen werden, die die Funk- tionalit¨aten, die sonst eine Hostsprache bietet, im Wesentlichen wiederholen. Das ist Arbeit, die

”verpufft“, da sie mit dem eigentlichen Ziel der Sprache kaum etwas zu tun hat.

Die Anwender der Sprache m¨ussen sich entsprechendes Grundwissen aneignen, sich mit der neuen Sprache vertraut machen, wohingegen sie sich in einer bekann- ten Hostsprache sehr viel schneller mit den eigentlich neuen Inhalten besch¨aftigen k¨onnen.

In einer eingebetteten Sprache werden Konzepte der Hostsprache verwendet, um damit Konzepte der Gastsprache darzustellen. So wird die M¨oglichkeit in Haskell mittels where neue Werte zu erschaffen, zu benennen und in der eigentlichen Glei- chung zu verwenden, in Lava benutzt, um die Schaltung in ihre Subelemente aufzu- spalten, die Leitungen zu benennen und Ausgabeleitungen festzulegen. Hier bietet Haskell allerdings noch mehr M¨oglichkeiten, als eigentlich f¨ur Lava n¨otig w¨aren. So ist es auch m¨oglich, dasselbe anders zu formulieren, und es ist f¨ur Lava nicht m¨oglich, einen bestimmten Weg zu unterbinden.

(6)

Die eigentliche Sprache wird in der Hostsprache dann mittels Bibliotheken be- reitgestellt. In Lava ist das der (abstrakte) Typ einer Leitung, Bit, die Elemen- tarfunktionen und -kombinatoren und M¨oglichkeiten zur Weiterverarbeitung der Beschreibung. Das sind Funktionen zur Simulation, Synthese und Verifikation von Schaltungen.

Der Nachteil einer Einbettung in eine andere Sprache ist, dass die Konzepte der Hostsprache m¨oglicherweise nicht gut, oder nur teilweise mit den Eigenschaften der Gastsprache in Einklang zu bringen sind. Im Zweifelsfall muss dann eine bes- sere Hostsprache gew¨ahlt werden, oder doch eine eigenst¨andige Sprache aufgebaut werden. In Lava tritt dieser Mangel bei Listen von Leitungen zutage, auf die keine Einschr¨ankungen formuliert werden k¨onnen, wie z.B. dass zwei Listen die gleiche L¨ange haben m¨ussen. Ein solcher Fehler tritt dann erst bei der Ausf¨uhrung der Beschreibung auf, etwa bei der Simulation, obwohl er schon zur ¨Ubersetzungszeit bekannt sein k¨onnte.

1.3 Einbettung in Haskell

Neben dem Nachteil, dass keine Einschr¨ankungen auf Leitungslisten angeben werden k¨onnen, hat die Einbettung in Haskell auch einige Vorteile, die Lava ausnutzen kann. Listen von Leitungen sind eine sehr allgemeine Art Mengen von Leitungen zusammenzufassen, im Gegensatz zu Tupeln, bei denen die Anzahl der Leitungen von vornherein feststehen muss. Diese Leitungen kann man mit dem Mittel der Rekursion sehr einfach beschreiben, da, durch die variable Anzahl, die Leitungen sowieso in systematischer Form beschrieben werden m¨ussen.

Im folgenden Beispiel ist eine Schaltung angeben, die als Eingabe eine einzelne Leitung und eine Liste von Leitungen erh¨alt. Die Liste repr¨asentiert eine Zahl und die Schaltung inkrementiert diese, falls das Bit 1 ist, sonst nicht.

Listing 3: Ein Inkrementierer i n c r e m e n t : : (B i t , [B i t] ) −> ( [B i t] ,B i t) i n c r e m e n t (c i n , [ ] ) = ( [ ] , c i n)

i n c r e m e n t (c i n ,a : a s) = (s : s s ,c o u t) where (s,c) = h a l f A d d (c i n,a)

(s s ,c o u t) = i n c r e m e n t (c,a s)

Um auch allgemeinere Verbindungsmuster zur Verf¨ugung stellen zu k¨onnen, k¨onnen Haskells Polymorphismus und ¨Uberladung verwendet werden. So kann z.B.

demux nicht nur, wie oben, mit Bits, sondern auch mit allgemeinen Eingaben ge- baut werden. Dabei muss der Eingabetypa aufChoice eingeschr¨ankt werden, da mit dem Multiplexer jeweils ein Wert vom Typa ausgew¨ahlt wird. Diese Auswahl wird nicht mit Gattern aufgebaut, sondern wird intern, durch die Funktion ifThenElse vonChoice, bereitgestellt. F¨urBit, Tupel von Bits und andere stellt Lava Instanzen von Choice zur Verf¨ugung.

(7)

Listing 4: Ein allgemeinerer Demultiplexer demux : : C h o i c e a => (B i t ,a,a) −> (a,a)

demux (s ,e,d) = o u t

where a0 = mux (s , (e,d) ) a1 = mux (s , (d,e) ) o u t = (a0,a1)

Durch die Unterst¨utzung von Funktionen h¨oherer Ordnung k¨onnen auch Mus- ter konstruiert werden, die bestimmte Schaltungen in der gew¨unschten Weise auf Mengen von Leitungen anwenden.

Zum Beispiel kann mitrow eine Funktion auf eine Liste von Elementen angewen- det werden und zwischen den Einzelschaltungen noch Werte hochgereicht werden.

Mit diesem Verbindungsmuster kann dann unter anderem auch ein Addierer aufge- baut werden.

Listing 5: Verbindungspattern row row : : ( (c,a) −> (b,c) ) −> (c , [a] ) −> ( [b] ,c) row c i r c (c i n , [ ] ) = ( [ ] , c i n)

row c i r c (c i n,a:a s) = (b:bs ,c o u t) where (b,c) = c i r c (c i n ,a)

(bs,c o u t) = row c i r c (c,a s)

Der Aufbau entspricht genau dem eines Ripple Carry Addierers, nur dass row allgemeiner einsetzbar ist. So kann increment damit direkt implementiert werden, ohne direkt Rekursion einzusetzen.

Listing 6: Alternativer Aufschrieb des Inkrementierers i n c r e m e n t : : (B i t , [B i t] ) −> ( [B i t] ,B i t)

i n c r e m e n t = row h a l f A d d

Durch Lazy Evaluation k¨onnen unendliche Strukturen aufgebaut werden, von denen nur die Teile ausgewertet werden, die auch in der letztendlichen Realisierung der Schaltung verwendet werden. Damit kann dann beispielsweise ein Z¨ahler gebaut werden, der sich pro Takt um eins erh¨oht. Die Definition des Z¨ahlers selber braucht dazu nicht zu wissen, wie viele Bits der Z¨ahler enthalten soll, das wird nur dadurch festgelegt, wie viele Bits der unendlichen Bitliste ausgewertet werden.

(8)

2 Lava

2.1 Listen

Eingaben in Lava m¨ussen immer als ein Parameter an die Funktion gegeben sein. Ne- ben der Kombination von Dr¨ahten zu Tupeln, als Eingaben fester L¨ange, kann man sie auch in allen m¨oglichen anderen in Haskell ausdr¨uckbaren Formen ¨ubergeben.

Da wir mit Tupeln die Anzahl der Leitungen von vornherein festlegen m¨ussen, liegt eine offensichtliche Alternative darin, Listen von Dr¨ahten zu verarbeiten.

Mit Listen lassen sich Schaltungen sehr viel allgemeiner beschreiben, da man verschiedene Eingabel¨angen verwenden kann. Allerdings muss man immer im Auge behalten, dass Lava beim ¨Ubersetzen in Hardware s¨amtliche Listen wieder ausrol- len muss. Bei der Interpretation von Listen als Zahlen gilt die Konvention, dass niederwertige vor h¨oherwertigen Bits stehen.

Listing 7: Ein n-Demultiplexer demuxn : : C h o i c e a => ( [B i t] ,a,a) −> [a] demuxn ( [ ] ,e,d) = [e]

demuxn (s : s s ,e,d) = l s ++ r s where (l ,r) = demux (s,e,d)

l s = demuxn (s s ,l ,d) r s = demuxn (s s ,r,d)

Hier wird mit demux ein Demuxer aufgebaut, der aus einer Liste von Steuer- leitungen eine Liste von Ausgabeleitungen ausgibt, von denen immer genau eine e und alle anderen d ausgeben. Dazu wird das erste Steuerbit verwendet, um zu entscheiden, ob e auf die linke oder rechte Teilliste ausgegeben wird. Die beiden Teillisten werden dann aus den anderen Steuerbits aufgebaut, wobei eine der beiden von vornherein als Eingaben nurd-Werte bekommt, abh¨angig von s.

Auffallend ist an der Schaltung noch, dass die Ausgabe eine Liste ist, deren L¨ange nicht direkt gegeben ist. Aber durch die Liste der Eingabebits und die Art der Rekursion ergibt sich ganz automatisch eine Ausgabeliste der L¨ange 2|n| bei n Steuerbits.

2.2 Parameter und Eingaben

Zwar m¨ussen die Eingabeleitungen immer in einem einzigen Funktionsparameter enthalten sein, damit Schaltungen allgemein als Funktionen Eingabe −> Ausgabe betrachtet werden k¨onnen, dies gilt aber nicht f¨ur andere Parameter. Der Unter- schied zwischen Parametern und Eingaben einer Schaltung ist, dass die Eingaben Leitungen sind, deren Werte aber erst zur

”Laufzeit“ der Schaltung bekannt wer- den. Parameter sind bereits zur Laufzeit des Haskellprogramms bekannt und m¨ussen dort ausgewertet werden. In gewisser Weise haben wir das schon unbewusst benutzt, denn die L¨ange einer Eingabeliste ist ein (impliziter) Parameter an die Schaltung.

(9)

Ein Beispiel daf¨ur ist die Funktionextend, die einen Parameter n und eine Liste bekommt, und eine Liste der Gr¨oße n ausgibt. Dazu wird das oberste Element kopiert, bis genug Elemente vorhanden sind. Die typische Anwendung einer solchen Funktion ist es, aus einer vorzeichenbehafteten Zahl dieselbe Zahl auszugeben, nur mit mehr Bits dargestellt.

Listing 8: Stellenerh¨ohung einer vorzeichenbehafteten Zahl e x t e n d : : Int −> [a] −> [a]

e x t e n d 0 = [ ]

e x t e n d n [x] | n > 0 = r e p l i c a t e n x

e x t e n d n (x:xs@( : ) ) | n > 0 = x : (e x t e n d (n−1) x s)

Falls die Eingabeliste mehr als n Elemente enth¨alt, werden die obersten abge- schnitten.

2.3 Schaltwerke

Eine Hardwarebeschreibungssprache, mit der sich nur Schaltnetze beschreiben lassen w¨are nur eingeschr¨ankt nutzbar, und so kann Lava auch mit Zust¨anden umgehen.

Das Basiselement, das Lava daf¨ur zur Verf¨ugung stellt, heißt delay. Wie der Name vermuten l¨asst, stellt es ein flankengesteuertes Latch dar, damit sind automatisch alle Schaltungen in Lava flankengesteuert.

Daraus l¨asst sich zum Beispiel ein rs-Flipflop, das ein g¨angiges Schaltungselement ist, konstruieren, allerdings nur in der flankengesteuerten Version.

Listing 9: Ein flankengesteuertes rs-Flipflop rsFF : : (B i t ,B i t) −> B i t

rsFF (r,s) = a

where a = d e l a y l o w new new = and2 (i n v r,a s)

a s = or2 (a,s)

Der Parameter von delay gibt den Wert an, der im ersten Takt ausgegeben wer- den soll, danach wird die Eingabeleitung, stets um einen Takt verz¨ogert, ausgegeben.

F¨ur das Flipflop wurde der Ausgang vondelay zum Eingang zur¨uckgef¨uhrt und mit den Eingaben r und s verschaltet. Anders als das reale rs-Flipflop produziert die- ses allerdings stets eine definierte Ausgabe, das Anlegen von (high,high) f¨uhrt zum selben Ergebnis, wie (high,low).

W¨ahrend es bei Schaltnetzen noch umst¨andlich schien stets von Leitungen, an- statt von Werten zu sprechen, wird mit der Einf¨uhrung von Zust¨anden klar, warum das n¨otig ist. Ein Bit ist jetzt tats¨achlich eine Leitung, auf der Werte laufen, und nicht, wie der Name es impliziert, einfach ein Bit, denn f¨ur jeden Takt kann sich der Wert der Leitung nun ¨andern.

(10)

Dasimulatenur aus einer Eingabe eine Ausgabe produziert, kann es in zustands- behafteten Schaltungen nicht mehr funktionieren. F¨ur Schaltwerke stellt Lava des- halb simulateSeq bereit. Dieses bekommt eine Liste von Eingaben und produziert daraus eine Liste von Ausgaben.

∗Main> s i m u l a t e S e q rsFF [ (low,h i g h) , (low,l o w) , (h i g h ,l o w) ] [low ,h i g h ,h i g h]

Die erste Ausgabe haben wir in der Definition von rsFF als low festgelegt, der Zustand nach dem letzten Setzen wird nicht mehr ausgegeben.

2.4 Verifikation

Eine wichtige Eigenschaft von Lava ist die M¨oglichkeit, in den gebauten Schaltungen bestimmte Eigenschaften zu beweisen. Dazu baut man eine normale Schaltung auf, deren Name typischerweise mit prop beginnt, und die ein Bit ausgibt. Die Ausgabe kann dann darauf ¨uberpr¨uft werden, ob sie immer high ist.

Listing 10: Verifikation von demux prop DemuxOutputsOneHighBit : : B i t −> B i t prop DemuxOutputsOneHighBit s = ok

where (l ,r) = demux (s,h i g h ,l o w) ok = x o r 2 (l ,r)

Dazu wird die Schaltung symbolisch ausgewertet und die daraus resultierende Be- schreibung in einem externen Beweiser ¨ubergeben. Dieser Vorgang wird dem Nutzer durch die Funktion verify bereitgestellt, die ausgibt, ob die Eigenschaft bewiesen werden konnte.

∗Main> v e r i f y prop DemuxOutputsOneHighBit P r o v i n g: . . . V a l i d.

F¨ur Listen von Eingaben kann diese Art der Verifikation nicht funktionieren, da diese konzeptuell beliebig lang sein k¨onnen. Aber Listen vorgegebener L¨ange lassen sich genauso beweisen. Dazu wird von Lava eine Funktion forAll bereitgestellt, die alle Eingaben f¨ur Listen bestimmter L¨ange erzeugt.

Listing 11: Verifikation von demuxn prop DemuxnOutputsOneHighBit : : [B i t] −> B i t prop DemuxnOutputsOneHighBit s s = ok

where o u t s = demuxn (s s ,h i g h ,l o w) ok = x o r l o u t s

(11)

p r o p D e m u x n O u t p u t s O n e H i g h B i t F o r S i z e : : Int −> P r o p e r t y p r o p D e m u x n O u t p u t s O n e H i g h B i t F o r S i z e n =

f o r A l l ( l i s t n) $ \s s −>

prop DemuxnOutputsOneHighBit s s

prop DemuxnOutputsOneHighBit modelliert den allgemeinen Fall, der sich aber nicht beweisen l¨asst, dass in der Ausgabe von demuxn nur genau ein Bit high ist.

Die Schaltung xorl verbindet eine ganze Liste von Eingaben mit XOR, produziert also genau das gew¨unschte Ergebnis.

Diese Funktion wird von prop DemuxnOutputsOneHighBit ForSize verwendet, um dasselbe f¨ur eine bestimmte Listenl¨ange zu beweisen. Der R¨uckgabetyp ist hier vom allgemeineren Typ Property, der aber sonst dieselbe Rolle spielt, wie Bit.

∗Main> v e r i f y (prop DemuxnHasOddHighBits ForSize 5 ) P r o v i n g: . . . V a l i d.

(12)

3 Implementation

3.1 Problematik

Eine erste, sehr einfache M¨oglichkeit Lavaschaltungen in Haskell zu implementieren, ist es Bit als eine unendliche Liste von Bool zu definieren. Der Wert an der i-ten Stelle der Liste bedeutet dann, dass die Leitung im i-ten Takt diesen Wert hat.

Elementarfunktionen k¨onnen dann ebenfalls sehr einfach mit den entsprechenden Haskellfunktionen berechnet werden.

Listing 12: Einfachste Implementierung von Lava type B i t = [Bool]

i n v a s = map not a s

d e l a y d e = (head d) : e

and2 (as ,b s) = zipWith (&&) a s b s or2 (as,b s) = zipWith (| |) a s b s x o r 2 (as ,b s) = zipWith (/=) a s b s l o w = repeat False

h i g h = repeat True

Mit diesen Basisgattern sind bereits alle anderen Schaltungselemente erzeugbar und der weiter oben definierte Halbaddierer ist direkt nutzbar.

∗Main> h a l f A d d (h i g h ,h i g h)

( [F a l s e ,F a l s e , . . . ] , [True,True , . . . ] )

F¨ur die reine Simulation ist die Lavabibliothek damit praktisch vollst¨andig, ver- dient aber kaum den Namen Bibliothek. Allerdings soll Lava ja auch Schaltungen verifizieren und in anderen Formaten ausgeben k¨onnen, das ist mit den bisheri- gen Strukturen nicht m¨oglich. Wir haben nur Funktionen ¨uber Wahrheitswerte zur Verf¨ugung, mit denen kann man kaum mehr anfangen, als sie auszurechnen.

Um mit Werkzeugen wieder an das Schaltungsinnere heranzukommen, muss man es symbolisch ausf¨uhren, das heißt, anstatt Werte zu berechnen, werden mit den Funktionen Strukturen aufgebaut, die danach weiter bearbeitet werden k¨onnen.

Dazu ist eine andere Definition vonBit n¨otig, es muss entweder aus symbolischen Eingaben, oder aus Elementargattern bestehen:

Listing 13: Implementierung zur symbolischen Auswertung data B i t = I n p u t String

| Comp String [B i t] deriving (Eq,Show)

(13)

i n v b = Comp ” i n v ” [b]

d e l a y d e = Comp ” d e l a y ” [d,e] and2 (a,b) = Comp ” and ” [a,b] or2 (a,b) = Comp ” o r ” [a,b] x o r 2 (a,b) = Comp ” x o r ” [a,b] l o w = Comp ” low ” [ ]

h i g h = Comp ” h i g h ” [ ]

Mit dieser Definition wird bei Ausf¨uhrung der Schaltung die Verkn¨upfung ihrer Elemente aufgebaut. Um die Schaltung zu simulieren, m¨usste nun eine Funktion

¨uber diesen Baum vonBitslaufen und jeden Konstruktor wieder als Haskellfunktion ausf¨uhren.

Die Ausf¨uhrung, nicht Simulation, eines Halbaddierers w¨urde dann folgenden Baum aufbauen:

∗Main> h a l f A d d ( (I n p u t ” a ” ) , (I n p u t ”b” ) )

(Comp ” x o r ” [I n p u t ” a ” ,I n p u t ”b” ] ,Comp ” and ” \ [I n p u t ” a ” ,I n p u t ”b” ] )

Bei dieser Implementation ergeben sich aber weitere Probleme. Bei der mehrfa- chen Nutzung von Leitungen w¨urde an jeder Stelle der Nutzung der Aufbau neu stattfinden. Diesem Problem k¨onnte man vielleicht durch Optimierung in nach- folgenden Werkzeugen beikommen, aber f¨ur zyklische Beziehungen in Schaltungen w¨urde sich ein unendlich großer Baum aufbauen.

∗Main> rsFF (I n p u t ” r ” ,I n p u t ” s ” )

Comp ” d e l a y ” [Comp ” low ” [ ] ,Comp ” and ” [Comp ” i n v ” \ [I n p u t ” r ” ] ,Comp ” o r ” [Comp ” d e l a y ” [Comp ” low ” [ ] , . . .

Mit diesem Ansatz ist so eine Schaltung tats¨achlich nicht darstellbar, da es in Haskell keine M¨oglichkeit gibt, zwischen zwei Ausdr¨ucken gleichen Inhalts zu unter- scheiden. In Haskell ist dieser Effekt durchaus gew¨unscht und wird als referenzielle Transparenz bezeichnet, aber f¨ur die symbolische Auswertung von Schaltung muss er umgangen werden.

3.2 Direkter Ansatz

In einem direkten Ansatz wird jede Leitung explizit mit einer eindeutigen Bezeich- nung versehen. Der Baum, der sich aufbaut, wird zwar immer noch potentiell un- endlich groß, aber nun kann eine Funktion, die diesen Baum interpretiert, anhand der Bezeichnungen erkennen, welche Teile sich wiederholen und schl¨usselt sie nicht nochmals auf.

Dazu wird Bit eine Bezeichnung Tag mitgegeben und es wird eine Funktion (!)

(14)

definiert, die aus einem eindeutigen Tag einen neuen eindeutigen Tag baut.(!) muss sich dabei so verhalten, dass auch zwischen unabh¨angigen Unterschaltungen die Be- zeichnungen noch eindeutig bleiben, daher wird es als Liste vonInts implementiert, die in jeder Stufe um eine Zahl erweitert wird.

Listing 14: Implementierung mit expliziten Tags (1) data B i t = I n p u t String

| Comp Tag String [B i t] deriving (Eq,Show)

type Tag = [Int]

( ! ) : : Tag −> Int −> Tag ( ! ) t i = i : t

Nun m¨ussen alle Elementarschaltungen mit Tags definiert werden, aber auch jede abgeleitete Schaltung, wiersFF.

Listing 15: Implementierung mit expliziten Tags (2) i n v b t a g = Comp t a g ” i n v ” [b]

d e l a y d e t a g = Comp t a g ” d e l a y ” [d,e] and2 (a,b) t a g = Comp t a g ” and ” [a,b] or2 (a,b) t a g = Comp t a g ” o r ” [a,b] l o w t a g = Comp t a g ” low ” [ ]

rsFF : : (B i t ,B i t) −> Tag −> B i t rsFF (r,s) t = a

where l w = l o w (t! 1 )

a = d e l a y l w new (t! 2 ) nr = i n v r (t! 3 )

new = and2 (nr,a s) (t! 4 ) a s = or2 (a,s) (t! 5 )

Auch hier ergibt sich f¨ur rsFF bei der Ausgabe ein unendlicher Baum, aber das zweite Vorkommen vonComp [2] kann jetzt sicher mit dem Beginn identifiziert werden, es handelt sich also um eine Schleife.

∗Main> rsFF (I n p u t ” r ” ,I n p u t ” s ” ) [ ]

Comp [ 2 ] ” d e l a y ” [Comp [ 1 ] ” low ” [ ] ,Comp [ 4 ] ” and ” \ [Comp [ 3 ] ” i n v ” [I n p u t ” r ” ] ,Comp [ 5 ] ” o r ” \

[Comp [ 2 ] ” d e l a y ” [ . . ] ] ] ]

(15)

Der Nachteil dieses direkten Ansatzes ist, dass der Programmierer sich mit der Identifikation von Komponenten besch¨aftigen muss, indem er explizit die Tags ver- gibt. Weiterhin kann das Programm keine automatische ¨Uberpr¨ufung der Bezeich- nungen vornehmen, wenn der Programmierer also zweimal dieselbe Bezeichnung vergibt, k¨onnen auch unterschiedliche Schaltungen vom Programm als gleich be- handelt werden. Dadurch ergeben sich Fehler, die nur sehr schwer nachvollziehbar sind.

3.3 Monadischer Ansatz

Ein nat¨urlicher Ansatz zur Umgehung dieser Probleme sind Monaden. Mit Monaden k¨onnen Vorg¨ange im Hintergrund verborgen werden, damit sich der Programmierer nur um die wichtigen Konzepte k¨ummern muss und keine zus¨atzlichen Fehler in un- wichtigen macht. In diesem Fall bietet sich dieState Monade an, mit der Zust¨ande zwischen unterschiedlichen Programmabschnitten geteilt werden k¨onnen, der Zu- stand ist hier das n¨achste freie Tag.

Listing 16: Implementierung mit Monaden (1) import C o n t r o l.Monad.S t a t e

type Tag = Int type M = S t a t e Tag

data B i t = I n p u t String

| Comp Tag String [B i t] deriving (Eq,Show)

Als Tags werden weiterhin Ints verwendet, M ist nur eine Abk¨urzung f¨ur State Tag.State hat als weiteren Parameter den Inhaltstyp, meistBit, oder eine Variation davon, sodassM Bit eine Zustandsmonade mit ZustandTag und InhaltBit ist. Die Definition vonBit stimmt mit der des letzten Ansatzes ¨uberein.

Listing 17: Implementierung mit Monaden (2) comp : : String −> [B i t] −> M B i t

comp s b = do t <− g e t p u t (t+1)

return $ Comp t s b i n v : : B i t −> M B i t i n v b = comp ” i n v ” [b]

d e l a y d e = comp ” d e l a y ” [d,e] and2 (a,b) = comp ” and ” [a,b] or2 (a,b) = comp ” o r ” [a,b] l o w = comp ” low ” [ ]

(16)

Die Elementargatter funktionieren alle nach demselben Prinzip, die eigentliche Funktionalit¨at haben wir daher in comp implementiert. Da wir Monaden nutzen, m¨ussen jetzt alle Schaltungen in do-Bereichen gebaut werden, oder sie m¨ussen ma- nuell mit der Monadenfunktion>>= verkn¨upft werden.

In comp wird als erstes der aktuelle Zustand aus der Zustandsmonade mit get geholt und mit put, um eins erh¨oht, wieder gespeichert. F¨ur diesen Ansatz reicht es aus, den Zustandsz¨ahler zu erh¨ohen, anstatt eine Liste von Ints aufzubauen, da die Bezeichnung nun direkt von Gatter zu Gatter ¨ubergeben wird und nicht mehr hierarchisch, von Ebene zu Ebene, wie beim direkten Ansatz. Anschließend muss nur noch die Komponente mit der aktuellen Bezeichnung gebaut und zur¨uckgegeben werden.

Ein Halbaddierer ist jetzt ¨ahnlich formulierbar wie vorher, allerdings m¨ussen nun s¨amtliche Elemente in eine k¨unstliche Reihenfolge gebracht und alle explizit benannt werden.

Listing 18: Ein Halbaddierer im monadischen Ansatz h a l f A d d : : (B i t,B i t) −> M (B i t ,B i t)

h a l f A d d b = do s <− x o r 2 b c o u t <− and2 b return (s,c o u t)

Nun zeigt sich ein unerwartetes Problem dieses Ansatzes, wenn wir versuchen einen rs-Flipflop aufzubauen. Die Rekursion, die sich vorher ¨uber let-Ausdr¨ucke leicht beschreiben ließ, ist im monadischen Ansatz nicht mehr m¨oglich, weil alle Elemente in einer expliziten Reihenfolge stehen m¨ussen.

Die einzige L¨osung ist es, manuell einen Fixpunktoperator zu definieren, der diese Rekursion ausdr¨uckt.

Listing 19: Implementierung mit Monaden (3) l o o p : : (a −> M a) −> M a

l o o p f = do s <− g e t

l e t a = e v a l S t a t e ma s ma = f a

return a

Der Operator erm¨oglicht es, in einer gegebenen Schaltung f, den Eingang mit dem Ausgang zu verbinden, und gibt gleichzeitig die Ausgangsleitung zur¨uck.

Dazu nimmtloop den aktuellen Zustand, nutzt ihn undma, in der die Schaltung repr¨asentiert ist, um mit evalState den Ausgang a zu berechnen. Ausa kann dann, mit Hilfe der gegeben Schaltung, ma erzeugt werden. Das heißt a ist die Leitung, die vom Ausgang wieder zur¨uck an den Eingang der Schaltung f¨uhrt, diese ist auch gleichzeitig das Ergebnis von loop.

(17)

Damit kann rsFF wie folgt definiert werden:

Listing 20: Ein rs-Flipflop im monadischen Ansatz rsFF : : (B i t ,B i t) −> M B i t

rsFF (r,s) = l o o p (\a −> do a s <− or2 (a,s)

nr <− i n v r

new <− and2 (nr,a s) l w <− l o w

a’ <− d e l a y l w new return a’ )

Innerhalb vonloop wird das rs-Flipflop wie ¨ublich aufgebaut, nur das Verkn¨upfen vom Ausganga’ mit dem Eingang a ¨ubernimmt jetzt der Fixpunktoperator.

Gegen¨uber dem direkten scheint der monadische Ansatz vorteilhaft, da sich der Programmierer nicht um Bezeichnungen von Baumelementen k¨ummern muss. Daf¨ur wird allerdings eine Sequenzialisierung der Komponenten vorgenommen, die nicht der Intuition einer Schaltung entspricht, in der die meisten Elemente parallel arbei- ten. Auch Rekursion ist nicht mehr so nat¨urlich formulierbar, wie es urspr¨unglich m¨oglich war.

3.4 Shared Observables

Die letzte L¨osung basiert auf einer Erweiterung von Haskell, die allerdings einen kleinen Bruch mit der funktionalen Idee beinhaltet. Dabei wird die referentielle Transparenz f¨ur genau den gew¨unschten Bereich aufgeweicht, indem vergleichbare Referenzen zur Verf¨ugung gestellt werden.

Listing 21: Der Referenztyp type Ref = [ . . . ]

r e f : : a −> Ref a d e r e f : : Ref a −> a

(<=>) : : Ref a −> Ref a −> Bool

Auf die Implementation von Ref gehen wir sp¨ater ein. Mit ref kann man einen gegebenen Wert in eine Referenz einpacken, mit deref wieder auspacken und mit

<=>Referenzen vergleichen.

Damit ist die referenzielle Transparenz f¨ur Referenzen aufgehoben, an unter- schiedlicher Stelle konstruierte, aber ansonsten gleiche Wert k¨onnen nun unterschie- den werden.

(18)

∗Main> l e t x = 42 i n l e t r = r e f x i n r <=> r True

∗Main> l e t x = 42 i n r e f x <=> r e f x F a l s e

Da im zweiten Ausdruck zwei unterschiedliche Referenzen mit selbem Inhalt erstellt werden, ergibt sichFalse. W¨ahrend im ersten Fall dieselbe Referenz mit sich selbst verglichen wird, daraus muss sich dann True ergeben.

Das kann jetzt benutzt werden, umBit zu definieren, in einer Schleife wiederho- len sich dieselben Referenzen, andernfalls unterscheiden sie sich. Damit sind Kreise und Wiederholungen genauso erkennbar wie bei der expliziten Bezeichnung von Teilb¨aumen.

Listing 22: Implementation mittels Shared Observables data B i t = Var String

| Comp Ref ( (String, [ B i t] ) ) deriving (Eq,Show)

comp : : String −> [B i t] −> B i t comp s b = Comp (r e f (s,b) )

i n v : : B i t −> B i t

i n v b = comp ” i n v ” [b]

d e l a y d e = comp ” d e l a y ” [d,e] and2 (a,b) = comp ” and ” [a,b] or2 (a,b) = comp ” o r ” [a,b] l o w = comp ” low ” [ ]

Wieder fungiert comp als eigentliche Konstruktionsfunktion, Bit ist wie im Ur- sprungsansatz definiert, nur dass ¨uber eine Referenz auf den Inhalt zugegriffen wird.

Diese Referenz l¨asst sich dann mit bisherigen Referenzen verglichen und gegebenfalls wird auf die Unterkomponenten zugegriffen.

Das rs-Flipflop kann dadurch wieder auf gew¨ohnliche Weise, mit direkt ausge- dr¨uckter Rekursion, aufgeschrieben werden, siehe Listing 9.

Mit der Aufgabe der referentiellen Transparenz muss sich der Programmierer nun im Klaren dar¨uber sein, wann ein bestimmter Ausdruck neu berechnet und wann derselbe genommen wird. Das ist jetzt ein Unterschied, denn im ersten Fall sind die Ergebnisse zwei unterschiedliche Referenzen, im zweiten Fall ist es nur eine einzige. In den g¨angigen Haskellimplementationen wird nachcall-by-need gehandelt, konstante Ausdr¨ucke sind also alle gleich, nicht-konstante sind unterschiedlich.

(19)

Listing 23: Beispiel call-by-need c1 ( ) = a

where s = i n v a r = a

a = rsFF (r ,s) c2 ( ) = r

where s = i n v (rsFF (r,s) ) r = (rsFF (r,s) )

Beide,c1 und c2, ergeben eine Leitung, die mit jedem Takt den Wert wechselt.

W¨ahrend aberc1 nur aus einem einzigen, r¨uckgekoppelten Flipflop mit Inverter vor dem s-Eingang besteht, enth¨altc2 zwei Flipflops. Diese zwei Flipflops erhalten genau die gleichen Eingaben und sind entstanden, weil inc2 rsFF (r,s) tats¨achlich zweimal ausgef¨uhrt wurde, nicht einmal, wie man es nach funktionaler Lesart erwarten w¨urde.

Implementiert werden k¨onnte Ref zum Beispiel mit der Hugs-BibliothekIOExts, diese bietet bereits Referenzen basierend auf der IO-Monade an, zusammen mit der FunktionunsafePerformIO. Mit dieser k¨onnen IO-Monaden verborgen und als scheinbar normale Funktion genutzt werden.

Listing 24: Implementation von Ref import IOExts

newtype Ref a = MkRef (IORef a) r e f : : a −> Ref a

r e f x = MkRef (u n s a fe P e r f o r m IO (newIORef x) ) d e r e f : : Ref a −> a

d e r e f (MkRef r e f) = u n s a fe P e r f o r m IO (readIORef r e f) (<=>) : : Ref a −> Ref a −> Bool

(MkRef r e f 1) <=> (MkRef r e f 2) = r e f 1 == r e f 2

Der Hauptvorteil dieses Ansatzes liegt darin, dass die intuitive Syntax der Schal- tungen beibehalten werden kann. Es wird kein spezieller Operator f¨ur Rekursionen ben¨otigt, sie sind direkt mit benannten Leitungen im where-Bereich ausdr¨uckbar.

Daf¨ur gibt man allerdings die reine funktionale Programmierung auf, sodass nicht mehr alle aus Haskell bekannten Transformationen m¨oglich sind. ¨Uber diesen Umstand muss sich der Programmierer im Klaren sein.

(20)

4 Realisierung in FPGAs

Eine, auch in der Praxis verwendete, Anwendung f¨ur Lavaschaltungen ist die Rea- lisierung in FPGAs.

Ein Field Programmable Gate Array FPGA besteht aus vielen, sehr einfachen Zellen, die sich programmieren lassen und zusammen eine Funktionalit¨at bereitstel- len. Dabei kann jede Zelle eine einfache logische Funktion implementieren, einen Wert speichern und Ausgaben an andere Zellen weitergeben.

Mit Lava beschriebene Schaltungen lassen sich in FPGAs implementieren und so relativ einfach realisieren.

Eine Schaltung kann mit der Funktion writeVhdl in VHDL-Code umwandelt werden, der dann wie andere Beschreibungen verwendet werden kann. Dabei gilt nat¨urlich, wie bei der Verifikation, dass Eingaben konkrete Leitungen sein m¨ussen, wie einfacheBits, und nicht in Listenform sind.

Listing 25: 4-Bit Demuxer

demuxn4 : : ( (B i t ,B i t) ,B i t ,B i t) −> (B i t ,B i t ,B i t ,B i t) demuxn4 ( (s0 ,s 1) ,d,e) = (a0,a1,a2,a3)

where [a0,a1,a2,a3] = demuxn ( [s0,s 1] ,d,e)

∗Main> w r i t e V h d l ”demux4” demuxn4

W r i t i n g t o f i l e ”demux4 . vhd ” . . . Done.

F¨ur demux4 ergibt sich damit der VHDL-Code in Listing 26, der sich mit g¨angigen Werkzeugen in einem FPGA realisieren ließe. Auch speziellere Werkzeuge zur Realisierung in FPGAs sind verf¨ugbar, zum Beispiel von Xilinx Inc.

(21)

Listing 26: demux4 in VHDL, gek¨urzt

−− G e n e r a t e d by Lava 2000 use work.a l l ;

entity demuxn4 i s port ( c l k : in b i t

−− i n p u t s

; i n p 1 1 : in b i t ; i n p 1 2 : in b i t

; i n p 2 : in b i t ; i n p 3 : in b i t

−− o u t p u t s

; o u t p 0 : out b i t ; o u t p 1 : out b i t

; o u t p 2 : out b i t ; o u t p 3 : out b i t ) ;

end entity demuxn4;

architecture s t r u c t u r a l o f demuxn4 i s s i g n a l w1 : b i t ;

s i g n a l w2 : b i t ; [ . . . ]

s i g n a l w28 : b i t ; begin

c w3 : entity i d port map (c l k , i n p 1 2 , w3) ; c w4 : entity i d port map (c l k , i n p 3 , w4) ; c w2 : entity and2 port map (c l k , w3, w4, w2) ; c w6 : entity i n v port map (c l k , w3, w6) ;

c w9 : entity i d port map (c l k , i n p 1 1 , w9) ; c w8 : entity and2 port map (c l k , w9, w4, w8) ; c w11 : entity i n v port map (c l k , w9, w11) ; c w12 : entity i d port map (c l k , i n p 2 , w12) ; c w10 : entity and2 port map (c l k , w11, w12, w10) ; c w7 : entity or2 port map (c l k , w8, w10, w7) ; [ . . . 18 w e i t e r e Z e i l e n]

−− naming o u t p u t s

c o u t p 0 : entity i d port map (c l k , w1, o u t p 0 ) ; c o u t p 1 : entity i d port map (c l k , w13, o u t p 1) ; c o u t p 2 : entity i d port map (c l k , w17, o u t p 2) ; c o u t p 3 : entity i d port map (c l k , w25, o u t p 3) ; end s t r u c t u r a l;

(22)

5 Zusammenfassung und Ausblick

Lava bietet eine M¨oglichkeit an Schaltungen sehr kompakt zu formulieren und un- terst¨utzt die Realisierung und die Verifikation gleichermaßen. Durch den funktio- nalen Ansatz k¨onnen alle Elemente sehr allgemein und auf sehr hohem Abstrakti- onsniveau formuliert werden, allerdings kann die Fehlersuche dadurch aufwendiger werden. M¨ochte man den Wert einer internen Leitung erfahren, muss diese von ihrer Nutzung bis zur

”Hauptfunktion“ durchgereicht werden, anstatt sie kurzzeitig per Seiteneffekten ausgeben zu k¨onnen.

Das Ausrollen der Listen beliebiger L¨ange funktioniert nicht immer reibungslos.

In Situationen mit Kreisen dieser Listen kann es n¨otig sein alle Werte explizit her- auszuholen und wieder hineinzustecken, selbst wenn es eigentlich m¨oglich w¨are, die L¨ange durch andere Eingaben zu erkennen. Dadurch wird der Vorteil der allgemeinen Formulierung teilweise wieder zunichtegemacht.

Die Vorteile von Schaltungsprogrammierung in funktionaler Sprache ist f¨ur den Anwender sichtbar, wird allerdings ein wenig ad absurdum gef¨uhrt, dadurch, dass in der Implementierung entweder Vorteile der Formulierung von Schaltungen aufge- geben werden, oder das funktionale Prinzip selbst aufgeweicht wird.

In diesem Dokument wurden die Implementation der Verifikation [Cla00], sowie weitergehende Beispiele der Nutzung von Lava nicht behandelt.

Mit, in Lava elegant formulierbaren, Schmetterlingsnetzen lassen sich Sortier- netze, Kopplungsnetze und schnelle Fouriertransformation kompakt beschreiben.

[CSS03] [Cla00] [BCSS98]

Es gibt unterschiedliche Versionen von Lava, wie die von Xilinx Inc., in der Filter, Bezierkurvenzeichner und Schaltungen zur digitalen Signalverarbeitung beschrieben und realisiert werden. [CSS03]

Nat¨urlich kann Lava auch noch erweitert werden, zum Beispiel gibt es Wired, mit dem sich die Verlegung von Leitungen direkt beeinflussen l¨asst, und so die Ein- schr¨ankungen von Lava im Low-Level-Bereich etwas aufgeweicht werden. [ACS05]

(23)

Literatur

[ACS05] Emil Axelsson, Koen Claessen, and Mary Sheeran. Wired: Wire-aware circuit design. In Proc. of Conference on Correct Hardware Design and Verification Methods (CHARME), volume 3725 ofLecture Notes in Com- puter Science. Springer Verlag, October 2005.

[BCSS98] Per Bjesse, Koen Claessen, Mary Sheeran, and Satnam Singh. Lava:

Hardware design in Haskell. In Proc. of International Conference on Functional Programming (ICFP). ACM SIGPLAN, 1998.

[Cla00] Koen Claessen. An embedded language approach to hardware description and verification, August 2000. Dept. of Computer Science and Enginee- ring, Chalmers University of Technology. Lic. thesis.

[CSS03] Koen Claessen, Mary Sheeran, and Satnam Singh. Functional hardware description in Lava. In The Fun of Programming, Cornerstones of Com- puting, pages 151–176. Palgrave, 2003.

[She85] Mary Sheeran. Designing regular array architectures using higher order functions. InProc. of a conference on Functional programming languages and computer architecture, pages 220–237, New York, NY, USA, 1985.

Springer-Verlag New York, Inc.

[She90] Mary Sheeran. Describing butterfly networks in ruby. In Proceedings of the 1989 Glasgow Workshop on Functional Programming, pages 182–205, London, UK, 1990. Springer-Verlag.

Referenzen

ÄHNLICHE DOKUMENTE

Axiome sind Pr¨ adikate ¨ uber den Operationen der Signatur Elementare Pr¨ adikate P. Gleichheit s

• oder ein Rechteck, gegeben durch zwei Eckpunkte,. • oder ein Polygon, gegeben durch Liste

➥ im Bsp.: Eigent ¨umer darf alles, Benutzer der Eigent ¨umer- gruppe darf nicht schreiben, f ¨ur alle anderen kein Zugriff. ➥ Rechte werden bei jedem (relevanten) Zugriff vom BS

Einem Prozeß, der im Besitz einer Ressource ist, kann diese nicht gewaltsam entzogen werden?.

➥ Priorit ¨at kann laufend ge ¨andert werden, abh ¨angig vom Verhalten des Threads (Feedback Scheduling). ➥ z.B.: Priorit ¨at bestimmt durch L ¨ange des

➥ Versenden von Link State Paketen an alle anderen Router (Reliable Flooding). ➥ Berechnung der k ¨urzesten Wege

Um f¨ ur dieses Konzept ein wenig mehr Intuition zu gewinnen, sei erw¨ ahnt, dass man sich eine solche Transformation auch wie folgt vorstellen kann: A l¨ asst sich auf B

In Zeile 2 wird ange- ordnet, dass diese Datei immer wieder auf reelle Zahlen durchsucht werden