Echtzeitanwendungen auf Basis von HDL 6MT 4EMO im Sommersemester 2023
(EN google-translate)
(PL google-translate)
|





#1 Mi 22.03.2023
Vorlesung: Einführung in FPGA und VHDL
Einige gebräuchliche Abkürzungen und Bezeichnungen:
Abkürzung | Bedeutung | Beschreibung |
---|---|---|
FPGA | Field Programmable Gate Array | Chip der diskrete Schaltungen abbilden kann |
VHSIC | Very High Speed Integrated Circuit | Beispiel: FPGA |
VHDL | VHSIC Hardware Description Language | Programmiersprache für FPGAs |
Verilog | Bezeichnung | Alternative Programmiersprache für FPGAs |
DARPA | Defense Advanced Research Projects Agency | Forschungsbehörde des US amerikanischen Militärs |
Xilinx | Firma | Stellt FPGA-Chips her und stellt Entwicklungsumgebungen zu ihrer Programmierung bereit |
Tabelle 0-1: Einige gebräuchliche Abkürzungen und Bezeichnungen
Geschichtlicher Hintergrund
|
|




Wie ist ein FPGA aufgebaut?


Vorberetung der ersten Übung mit einem FPGA-Board
|





Übung: Umgang mit der Entwicklungsumgebung ISE von Xilinx
Führen Sie die in der Vorlesung dargestellten Schritte selber aus, um sich mit der Xilinx IDE und dem dspFPGA-Board vertraut zu machen:
|
#2 Mi 29.03.2023
Themen
|
1. Rückblick auf die praktische Verwendung der Xilinx IDE:
|
2. Rückblick auf den DSPFPGA und die FPGA-Familie Spartan 3E

3. Vertiefung: Struktur von FPGA-Programmen, synchrone und asynchrone Schaltungen

4. Vertiefung: Analyse von Programmen mit und ohne Prozess-Bereich


5. Sprachelemente von VHDL -- Vorstellung einiger häufig verwendeter Datentypen und Programmstrukturen

6. Übungen
ÜBUNG 1
|
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity and_gate is Port ( A : in STD_LOGIC; B : in STD_LOGIC; C : out STD_LOGIC); end and_gate; architecture Behavioral of and_gate is begin C <= A and B; end Behavioral;
Code 0-1: VHDL-Code zum UND-Gatter.
NET "A" LOC = "P139" | PULLUP | IOSTANDARD = LVCMOS33 ; NET "B" LOC = "P135" | PULLUP | IOSTANDARD = LVCMOS33 ; NET "C" LOC = "P131" | IOSTANDARD = LVCMOS33 | SLEW = SLOW ;
Code 0-2: Constraints (.ucf-Datei) zum UND-Gatter.
Variieren Sie dieses Projekt so, dass es dann drei Eingänge hat. Der Ausgang soll dabei dann logisch 1 annehmen, wenn der erste und zweite Eingang im gleichen Zustand sind und der Zustand des dritten Eingangs dazu invers ist.
ÜBUNG 2
Variieren Sie das Beispiel mit der blinkenden LED so, dass diese nun mit 4 Hertz blinkt.
ÜBUNG 3 (Zusatzaufgabe)
Versuchen Sie ein Programm und eine zugehörige Schaltung zu realisieren, bei der über zwei Eingänge A und B die Blinkfrequenz einer LED gesteuert werden kann:
|
#3 Mi 05.04.2023
Themen
|
1. Musterlösungen zu Konfiguration der Blink-LED über digitale Eingänge
Versuchen Sie ein Programm und eine zugehörige Schaltung zu realisieren, bei der über zwei Eingänge A und B die Blinkfrequenz einer LED gesteuert werden kann:
|
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity blinkled is Port ( MEINECLOCK : in STD_LOGIC; MEINELED : out STD_LOGIC; a : in STD_LOGIC; b : in STD_LOGIC); end blinkled; -- Clock-In == 6MHz, Toggeln nach 3000000 Schritten ergibt 1Hz Blinkfrequenz. architecture Behavioral of blinkled is constant w0: integer := 2999999; constant w1: integer := 1499999; constant w2: integer := 999999; constant w3: integer := 749999; signal zaehler : integer range 0 to 2999999 := 0; signal logikpegel : std_logic := '0'; signal asig : std_logic := '0'; signal bsig : std_logic := '0'; begin process begin wait until rising_edge(MEINECLOCK); if ( ((zaehler<w0) and (((not bsig) and (not asig)) = '1')) or ((zaehler<w1) and (((not bsig) and ( asig)) = '1')) or ((zaehler<w2) and ((( bsig) and (not asig)) = '1')) or ((zaehler<w3) and ((( bsig) and ( asig)) = '1')) ) then zaehler <= zaehler+1; else zaehler <= 0; logikpegel <= not logikpegel; end if; end process; MEINELED <= logikpegel; asig <= a; bsig <= b; end Behavioral;
Code 0-3: zu testende Lösung VHD
## GCLK6 ist auf FPGA-Chip Pin 56 NET "MEINECLOCK" LOC = "P56"; ## IO_L05P_0 ist auf FPGA-Chip Pin 125 NET "MEINELED" LOC = "P125"; NET "MEINECLOCK" IOSTANDARD = LVCMOS25; NET "MEINELED" IOSTANDARD = LVCMOS33; NET "a" LOC = "P140" | PULLUP | IOSTANDARD = LVCMOS33 ; NET "b" LOC = "P139" | PULLUP | IOSTANDARD = LVCMOS33 ;
Code 0-4: zu testende Lösung UCF

...weitere Lösung unter Verwendung einer Lookup-Tabelle:
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity blinkled is Port ( MEINECLOCK : in STD_LOGIC; MEINELED : out STD_LOGIC; a : in STD_LOGIC; b : in STD_LOGIC); end blinkled; -- Clock-In == 6MHz, Toggeln nach 3000000 Schritten ergibt 1Hz Blinkfrequenz. architecture Behavioral of blinkled is constant w0: integer := 2999999; constant w1: integer := 1499999; constant w2: integer := 999999; constant w3: integer := 749999; signal zaehler : integer range 0 to 2999999 := 0; signal obergrenze : integer range 0 to 2999999 := 0; signal logikpegel : std_logic := '0'; signal wort : std_logic_vector(1 downto 0); signal wert : integer range 0 to 3 := 0; begin process begin wait until rising_edge(MEINECLOCK); if (zaehler<obergrenze) then zaehler <= zaehler+1; else zaehler <= 0; logikpegel <= not logikpegel; end if; end process; MEINELED <= logikpegel; wort <= b & a; wert <= to_integer(unsigned(wort)); with wert select obergrenze <= w0 when 0, w1 when 1, w2 when 2, w3 when others; end Behavioral;
Code 0-5: VHD-Datei

studentische Lösung
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity blinkled is Port ( CLOCK : in STD_LOGIC; I1 : in STD_LOGIC; I2 : in STD_LOGIC; O1 : out STD_LOGIC); end blinkled; -- Clock-In == 6MHz, Toggeln nach 3000000 Schritten ergibt 1Hz Blinkfrequenz. architecture Behavioral of blinkled is signal zaehler : integer range 0 to 2999999 := 0; signal logikpegel : std_logic := '0'; begin process begin if((I1='0') and (I2='0')) then wait until rising_edge(CLOCK); if (zaehler<2999999) then zaehler <= zaehler+1; else zaehler <= 0; logikpegel <= not logikpegel; end if; elsif ((I1='1') and (I2='0')) then wait until rising_edge(CLOCK); if (zaehler<1499999) then zaehler <= zaehler+1; else zaehler <= 0; logikpegel <= not logikpegel; end if; elsif ((I1='0') and (I2='1')) then wait until rising_edge(CLOCK); if (zaehler<999999) then zaehler <= zaehler+1; else zaehler <= 0; logikpegel <= not logikpegel; end if; else -- ((I1='1') and (I2='1')) then wait until rising_edge(CLOCK); if (zaehler<749999) then zaehler <= zaehler+1; else zaehler <= 0; logikpegel <= not logikpegel; end if; end if; end process; O1 <= logikpegel; end Behavioral;
Code 0-6: studentische Lösung zur konfigurierbaren LED
NET "I1" LOC = "P135" | PULLUP | IOSTANDARD = LVCMOS33 ; NET "I2" LOC = "P139" | PULLUP | IOSTANDARD = LVCMOS33 ; NET "CLOCK" LOC = "P56" | IOSTANDARD = LVCMOS33 ; NET "O1" LOC = "P125" | IOSTANDARD = LVCMOS33 | SLEW = SLOW ;
Code 0-7: ucf-Datei zu studentische Lösung zur konfigurierbaren LED
2. Weitere Elemente in VHDL: Umgang mit Bitwörtern, Lookup-Tabellen, UNISIM-Library
BITWÖRTER
-- Umwandlung einer Integerzahl in ein Vierbit-Wort: wort <= conv_std_logic_vector(kleinerzaehler,VIERBIT'length); -- Code 0-5: Beispiele zur Verknüpfung logischer Wörter, um bestimmte Operationen zu realisieren. signal wort1 : std_logic_vector(3 downto 0) := "1010"; signal wort2 : std_logic_vector(3 downto 0) := "1110"; signal wort3 : std_logic_vector(7 downto 0) := "10101111"; -- .... -- .... --Verknüpfung zweier Vierbit-Worte zu einem 8-Bit-Wort wort3 <= wort1 & wort2; --Wiederholung eines Vierbit-Wortes wort3 <= wort1 & wort1; --Bitshift um eine Stelle nach links wort3 <= wort3(6 downto 0) & '0'; --Bitshift um zwei Stelle nach rechts wort3 <= "00" & wort3(7 downto 2); --Rotation nach links wort3 <= wort3(6 downto 0) & wort3(7);
Code 0-8: Umgang mit Bitwörtern

Lookup-Tabellen
Siehe zweite Musterlösung oben.
Preview Neuronale Netze


Unisim Library (eher nächster Termin)

Übung 1
Testen Sie die Musterlösungen zu den konfigurierbaren Blink-LEDs.
Übung 2
Bauen Sie den Mustervergleicher auf und testen und analysieren Sie insbesondere die Variante mit Standard Logic Vektor.

Übung 3
|
NET "clock" LOC = "P56" | IOSTANDARD = LVCMOS33; NET "ledOut<0>" LOC = "P126" | IOSTANDARD = LVCMOS33; NET "ledOut<1>" LOC = "P130" | IOSTANDARD = LVCMOS33; NET "ledOut<2>" LOC = "P131" | IOSTANDARD = LVCMOS33; NET "ledOut<3>" LOC = "P132" | IOSTANDARD = LVCMOS33;
Code 0-9: Constraint-Datei für das Lauflicht.
#4 Mi 12.04.2023
Themen
|
1. Synchronisieren: D-Flip-Flop

Neu: Verwendung selbst geschriebener Module.
Übung 1
|

2. D-Flip-Flop aus der Unisim Library

Übung 2
|
3. Lookup Table (LUT) aus der Unisim Library

Übung 3
|
4. Vorarbeiten Softcomputing
|
Verkabeln Sie Mikrocontroller und FPGA erst NACH dem Flashen des Mikrocontrollers!
Übung 4 -- Demultiplexer mittels FPG
Ein Demultiplexer wandelt ein Binärwort in eine Stelle auf einer Anzeige um. Folgende Tabelle soll das für drei Bit veranschaulichen:
000 00000001 001 00000010 010 00000100 011 00001000 100 00010000 101 00100000 110 01000000 111 10000000
Code 0-10: 4-Bit Demultiplexer
|
#5 Mi 19.04.2023
Beginn ab jetzt immer 08:45 Uhr
Große Projektarbeit: Entwurf, Programmierung und Bau einer digitalen Anzeige
Unter Ausnutzung der bisherigen Programmierkenntnisse, soll zyklisch ein 8-Bit-Wort von einem Arduino Micro empfangen werden und als Dezimalzahl auf einer Vierfach-Digitalanzeige angezeigt werden.
Die Aufgabe soll in mehreren aufeinander aufbauenden Stufen gelöst werden:
Stufe 1
|
LÖSUNGSHINWEISE:
Das Arduino-Programm ohne seriellen Monitor wird gemeinsam mit dem Dozenten entwickelt.
|
Wo die Anschlüsse zu PORTB beim Arduino-Micro liegen sehen Sie hier:

Übersicht der FPGA-Board-Anschlüsse:

ACHTUNG: Bitte je ein Ground-Pin (GND) von FPGA mit einem vom Arduino Micro verbinden!
unsigned char zaehler = 0; void portb2fpga(unsigned char muster) { /* * muster 1 => Bit bei DDRB digitaler Eingang also 0 * muster 0 => Bit bei DDRB digitaler Ausgang also 1 */ DDRB = ~muster; /* * muster 1 => Bit bei PORTB digitaler Eingang OHNE Pullup, deshalb 0 * muster 0 => Bit bei PORTB digitaler Ausgang, der logisch Null sendet, also 0 */ PORTB = 0b00000000; } void setup() { /* * so kann beim FPGA kein versehentlicher Kurzschluss entstehen, denn alle Bits sind so als * Eingang konfiguriert und damit HOCHOHMIG! */ portb2fpga(255); } void loop() { portb2fpga(zaehler); delay(250); //Delay 250 Millisekunden == 1/4 Sekunde Pause zaehler++; }
Code 0-11: Mikrocontroller Programm um 8Bit auf den FPGA zu übertragen, ohne diesen zu beschädigen.
Studentische Lösung zu "Stufe 1"
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity Stufe1 is Port ( IN0 : in STD_LOGIC; IN1 : in STD_LOGIC; IN2 : in STD_LOGIC; IN3 : in STD_LOGIC; IN4 : in STD_LOGIC; IN5 : in STD_LOGIC; IN6 : in STD_LOGIC; IN7 : in STD_LOGIC; IO0 : out STD_LOGIC; IO1 : out STD_LOGIC; IO2 : out STD_LOGIC; IO3 : out STD_LOGIC; IO4 : out STD_LOGIC; IO5 : out STD_LOGIC; IO6 : out STD_LOGIC; IO7 : out STD_LOGIC); end Stufe1; architecture Behavioral of Stufe1 is begin IO0 <= IN0; IO1 <= IN1; IO2 <= IN2; IO3 <= IN3; IO4 <= IN4; IO5 <= IN5; IO6 <= IN6; IO7 <= IN7; end Behavioral;
Code 0-12: VHDL-Code der Studentischen Lösung zu "Stufe 1".
NET "IN0" LOC = "P10" | PULLUP | IOSTANDARD = LVCMOS33 ; NET "IN1" LOC = "P12" | PULLUP | IOSTANDARD = LVCMOS33 ; NET "IN2" LOC = "P29" | PULLUP | IOSTANDARD = LVCMOS33 ; NET "IN3" LOC = "P31" | PULLUP | IOSTANDARD = LVCMOS33 ; NET "IN4" LOC = "P36" | PULLUP | IOSTANDARD = LVCMOS33 ; NET "IN5" LOC = "P38" | PULLUP | IOSTANDARD = LVCMOS33 ; NET "IN6" LOC = "P41" | PULLUP | IOSTANDARD = LVCMOS33 ; NET "IN7" LOC = "P47" | PULLUP | IOSTANDARD = LVCMOS33 ; NET "IO0" LOC = "P58" | IOSTANDARD = LVCMOS33 ; NET "IO1" LOC = "P59" | IOSTANDARD = LVCMOS33 ; NET "IO2" LOC = "P93" | IOSTANDARD = LVCMOS33 ; NET "IO3" LOC = "P94" | IOSTANDARD = LVCMOS33 ; NET "IO4" LOC = "P96" | IOSTANDARD = LVCMOS33 ; NET "IO5" LOC = "P97" | IOSTANDARD = LVCMOS33 ; NET "IO6" LOC = "P103" | IOSTANDARD = LVCMOS33 ; NET "IO7" LOC = "P104" | IOSTANDARD = LVCMOS33 ;
Code 0-13: UCF-Datei der Studentischen Lösung zu "Stufe 1".
Stufe 2
|
Verwenden Sie 470 Ohm als Vorwiderstände, nicht 2200 Ohm wie angegeben!


ACHTUNG: Die Digits haben eine gemeinsame Kathode!:
|
Stufe 3
|
|
|
Stufe 4
|
|
|

Flexibler ist eine case-Anweisung:

Stufe 5
|
LÖSUNGSHINWEISE:
|
#6 Mi 26.04.2023
Beginn ab jetzt immer 09:00 Uhr (ohne Pause bis 12:00Uhr)
Softcomputing auf einem FPGA
Themen:
|
0. Was bedeutet "selbst lernendes System"? ...


1. KI-Techniken resp. Softcomputing
KI-Techniken sind zumeist biologisch inspirierte Methoden zu selbst lernenden Systemen. Dazu gehören insbesondere:
|





2. Software-In-the-Loop (SIL) -Ansatz

Bild 0-1: Angestrebtes SIL-Konzept.
3. Java/Processing


4. Kommunikationsschnittstellen
|
Als Vorstufe kann zunächst der Arduino die Antwort liefern, ohne auf den FPGA zurückzugreifen.
import processing.serial.*; Serial myPort; // Create object from Serial class int val; // Data received from the serial port public void setup() { Serial.list(); String portName = Serial.list()[0]; myPort = new Serial(this, portName, 9600); textSize(30); size(500,500); frameRate(1); } int x=0; int y=0; public void draw() { background(255); fill(0); if( myPort.available() > 0) { // If data is available, val = myPort.read(); // read it and store it in val } text("x="+x+" y="+y+" x*y="+val,10,height/2); x++; if(x>=16) { x=0; y++; } if(y>=16) { y=0; } int z = x + 16*y; myPort.write(z); }
Code 0-14: Processing-Seite: x,y an Arduino schicken, x*y empfangen und zeigen.
void setup() { Serial.begin(9600); } void loop() { if(Serial.available()) { int u = Serial.read(); int x = u%16; int y = u>>4; int z = x*y; Serial.write(z); } }
Code 0-15: Arduino-Seite: Byte [yyyyxxxx] als zwei Variablen trennen und Produkt als Byte zurückschicken.
5. Code-Generator
public void setup() { String[] code1 = loadStrings("code1.txt"); //Erster Teil des VHDL-Programms // mittlerer Teil wird hier unten generiert (hier nur eine Zeile) String[] code2 = loadStrings("code2.txt"); //Hinterer Teil des VHDL-Programms //Vorgabe eines Ausgabe-Pattern für LUT4 für ein AND-Gatter mit vier Eingängen: int[] out = {1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; //Buffer zum Sammeln der Daten StringBuffer sb = new StringBuffer(); //ersten Teil einschreiben: for(int i=0;i<code1.length;i++) sb.append(code1[i]+" "); //Zeilenumbruch hintanfügen //Diese Zeile im Code anpassen: // generic map ( INIT => "1000010000100001" ) sb.append(" generic map ( INIT => \""); for(int i=0;i<out.length;i++) sb.append(""+out[i]); sb.append("\" )"+" "); //hinteren Teil einschreiben: for(int i=0;i<code2.length;i++) sb.append(code2[i]+" "); //Zeilenumbruch hintanfügen //speichern: saveStrings("code3.txt",new String[] {sb.toString()}); } public void draw() { }
Code 0-16: CodegeneratorLUT4 -- Codegenerator-Beispiel in Processing.

6. Beispiel
1. Vorarbeit: dynamisches geregeltes System modellieren:
$ m \cdot \ddot x = -C \cdot x + F_A $
Formel 0-1: Dynamisches geregeltes System.
$ F_A = -P \cdot x -D \cdot v $
Formel 0-2: PD-Regler des Systems zu dessen Beruhigung.
Alle Parameter sind in folgender Umsetzung auf 1 gesetzt:

double dt = 0.05; public void setup() { println("-1 "+int2x(x2int(-1.0))); println("-0.5 "+int2x(x2int(-0.5))); println("0.0 "+int2x(x2int(0.0))); println("0.5 "+int2x(x2int(0.5))); println("1.0 "+int2x(x2int(1.0))); println("-10 "+int2f(f2int(-10.0))); println("-5 "+int2f(f2int(-5.0))); println("0.0 "+int2f(f2int(0.0))); println("5 "+int2f(f2int(5.0))); println("10 "+int2f(f2int(10.0))); makeLookup(); textSize(30); size(500,500); frameRate(20); } double x=1.0; double v=0.0; long T=System.currentTimeMillis(); double Fehler = 0.0; double F_A=0.0; public void draw() { background(255); fill(0); if(System.currentTimeMillis()<T+10000) { F_A=0.0; //F_A =-x-v; //F_A = berechneF(x,v);//-x-v; //int iv = x2int(v); //int ix = x2int(x); //F_A = int2f(lookup[iv][ix]); x+=v*dt; v+=(-x+F_A)*dt; Fehler+=x*x; } ellipse((float)x*(width/2)+width/2,height/2,width/50,height/50); text("Err="+Fehler,50,50); text("F_A="+F_A,50,100); text("x="+x,50,150); } public void keyPressed() { if(key==' ') { x=1.0; v=0.0; T=System.currentTimeMillis(); Fehler=0.0; } }
Code 0-17: FPGA003_simfuzzy Simulation mittels Euler-Integration.

Bild 0-2: Screenshot des Sketches FPGA003_simfuzzy.

Bild 0-3: Simpler Fuzzy-Regler für das System.
public double berechneF(double x, double v) { double regel1 = minimum(positiv(x),positiv(v)); double regel2 = minimum(positiv(x),negativ(v)); double regel3 = minimum(negativ(x),positiv(v)); double regel4 = minimum(negativ(x),negativ(v)); double basis = 10.0; double Amax = 5.0; double m = 5.0; double gewicht1 = Amax - (basis-2.0*regel1/m)*(1.0-regel1)/2.0; double gewicht2 = Amax - (basis-2.0*regel2/m)*(1.0-regel2)/2.0; double gewicht3 = Amax - (basis-2.0*regel3/m)*(1.0-regel3)/2.0; double gewicht4 = Amax - (basis-2.0*regel4/m)*(1.0-regel4)/2.0; //println(gewicht1+" "+gewicht2+" "+gewicht3+" "+gewicht4+" "); double F = (gewicht1*(-5.0)+gewicht2*(0.0)+gewicht3*(0.0)+gewicht4*(5.0))/ (gewicht1+gewicht2+gewicht3+gewicht4); return F; } double minimum(double u, double v) { if(u<v) return u; else return v; } double negativ(double z) { if(z<-1.0/3.0) return 1.0; else if(z>1.0/3.0) return 0.0; else return 1.0 - (3.0/2.0)*(z-(-1.0/3.0)); } double positiv(double z) { if(z<-1.0/3.0) return 0.0; else if(z>1.0/3.0) return 1.0; else return (3.0/2.0)*(z-(-1.0/3.0)); }
Code 0-18: Fuzzy-Part
int[][] lookup = new int[16][16]; public int x2int(double x) { if(x>1.0) x=1.0; if(x<-1.0) x=-1.0; x+=1.0; x/=2.0; x*=16.0; x+=0.5; if(x>=16.0) x=15.0; return (int)x; } public double int2x(int x) { return ((double)x-7.5)/7.5; } public int f2int(double x) { if(x>10.0) x=10.0; if(x<-10.0) x=-10.0; x+=10.0; x/=20.0; x*=256.0; x+=0.5; if(x>=256.0) x=255.0; return (int)x; } public double int2f(int x) { return 10.0*((double)x-127.5)/127.5; } public void makeLookup() { for(int v=0;v<16;v++) { for(int x=0;x<16;x++) { lookup[v][x] = f2int(berechneF(int2x(x), int2x(v))); } } }
Code 0-19: Diskretisierungspart.
7. Übung
|
Hinweis zu 2.: Verwenden Sie die Pinbelegungen von letzter Woche. Zusätzlich geht nun IO0..IO7 (vormals LEDs) vom PC an Digital IO 0..7 beim Arduino. Hierüber wird z vom FPGA an den Arduino geleitet.
Hinweis zu 3.: Es geht darum den Regler auf den Arduino zu bringen und im Processing-Sketch bei jedem draw()-Aufruf den passenden Kraftwert von dort zu holen. Lassen Sie sich dazu alle in int[][] lookup = new int[16][16]; gespeicherten Werte mittels eines Codegenerators in ein eindimensionales C-Array schreiben. Bestimmen Sie den richtigen Index beim Abruf dieses Arrays mit: index = iv*16+ix. ix und iv werden zusammengefasst als ein Byte an den Arduino geschickt und dort wieder separiert.
#7 Mi 03.05.2023
Themen
|

import java.util.Random; /** Gutes Ergebnis der Optimierung: err=6.221646872728446E-17 double[] ww = {80.31866271082562,-31.265850938936456,85.13429379525569,45.23179437588253,-64.49043801364026,82.80217344167164,25.303098407831502,-44.11871152333888,22.46308463857618,115.53667395418526,-35.885562823122015,72.62564317964451,86.44491797680459,-37.66248583715228}; */ Random zufall = new Random(System.currentTimeMillis()); double sigmoid(double u) { return 1.0/(1+Math.exp(-u)); } double netz(double[] in, double[] w) { //Eingangsschicht mit 3 Neuronen: double out0 = sigmoid(in[0]*w[0]+in[1]*w[1]); double out1 = sigmoid(in[0]*w[2]+in[1]*w[3]); double out2 = sigmoid(in[0]*w[4]+in[1]*w[5]); //Zwischenschicht mit zwei Neuronen: double out3 = sigmoid(out0*w[6]+out1*w[7]+out2*w[8]); double out4 = sigmoid(out0*w[9]+out1*w[10]+out2*w[11]); //Ausgangsschicht mit einem Neuron: double out5 = sigmoid(out3*w[12]+out4*w[13]); return out5; } double[][][] lernpattern = { {{0.0,0.0},{1.0}}, {{0.0,1.0},{0.0}}, {{1.0,0.0},{0.0}}, {{1.0,1.0},{1.0}} }; public double fehler(double[] w) { double err = 0.0; for(int i=0;i<lernpattern.length;i++) { double eneu = netz(lernpattern[i][0],w); err+=(eneu-lernpattern[i][1][0])*(eneu-lernpattern[i][1][0]); } return Math.sqrt(err); } public void setup() { //Optimierung: double[] w = new double[14]; for(int i=0;i<w.length;i++) w[i] = 200.0*(zufall.nextDouble()-0.5); //+/-100 double errBest = fehler(w); for(int i=0;i<10000;i++) { int inx = zufall.nextInt(w.length); double w_alt = w[inx]; w[inx]+=2.0*(zufall.nextDouble()-0.5); //+/-0.1 double errNeu = fehler(w); if(errNeu<=errBest) { if(errNeu<errBest) { println(i+".: err="+errNeu); if(errNeu<0.000001) { println(); print("double[] ww = {"); for(int k=0;k<w.length;k++) { print(w[k]); if(k<w.length-1) print(","); } println("};"); } } errBest = errNeu; } else { w[inx] = w_alt; } } } public void draw() { }
Code 0-20: Beispiel eines zufälligen neuronalen Netzes, das in Richtung Äquivalenzgatter optimiert wird.

Prüfen: Einsatz von ROM256X1, s.Seite 455, spartan3e_hdl.pdf.

Musterlösung zu Gemeinsam: 3. (letzte Woche) Realisieren Sie den Regler auf dem Arduino so, dass dort ein Array zur Verfügung steht, bei dem aus jedem übergebenen Byte der diskretisierte Kraftwert abgelegt ist.
Sketch auf dem PC:
import processing.serial.*; Serial myPort; // Create object from Serial class int val; // Data received from the serial port double dt = 0.05; public int x2int(double x) { if(x>1.0) x=1.0; if(x<-1.0) x=-1.0; x+=1.0; x/=2.0; x*=16.0; x+=0.5; if(x>=16.0) x=15.0; return (int)x; } public double int2x(int x) { return ((double)x-7.5)/7.5; } public int f2int(double x) { if(x>10.0) x=10.0; if(x<-10.0) x=-10.0; x+=10.0; x/=20.0; x*=256.0; x+=0.5; if(x>=256.0) x=255.0; return (int)x; } public double int2f(int x) { return 10.0*((double)x-127.5)/127.5; } public void setup() { Serial.list(); String portName = Serial.list()[0]; myPort = new Serial(this, portName, 9600); textSize(30); size(500,500); frameRate(20); } double x=1.0; double v=0.0; long T=System.currentTimeMillis(); double Fehler = 0.0; double F_A=0.0; public void draw() { background(255); fill(0); if(System.currentTimeMillis()<T+10000) { int iv = x2int(v); int ix = x2int(x); int ein_byte = iv*16+ix; myPort.write(ein_byte); if( myPort.available() > 0) { // If data is available, val = myPort.read(); // read it and store it in val F_A = int2f(val); } x+=v*dt; v+=(-x+F_A)*dt; Fehler+=x*x; } ellipse((float)x*(width/2)+width/2,height/2,width/50,height/50); text("Err="+Fehler,50,50); text("F_A="+F_A,50,100); text("x="+x,50,150); } public void keyPressed() { if(key==' ') { x=1.0; v=0.0; T=System.currentTimeMillis(); Fehler=0.0; } }
Code 0-21: Sketch auf dem PC.
Sketch auf dem Arduino
unsigned char tabelle[] = {192, // inxV:0 inxX0 index0 192, // inxV:0 inxX1 index1 192, // inxV:0 inxX2 index2 192, // inxV:0 inxX3 index3 192, // inxV:0 inxX4 index4 192, // inxV:0 inxX5 index5 175, // inxV:0 inxX6 index6 164, // inxV:0 inxX7 index7 156, // inxV:0 inxX8 index8 145, // inxV:0 inxX9 index9 128, // inxV:0 inxX10 index10 128, // inxV:0 inxX11 index11 128, // inxV:0 inxX12 index12 128, // inxV:0 inxX13 index13 128, // inxV:0 inxX14 index14 128, // inxV:0 inxX15 index15 192, // inxV:1 inxX0 index16 192, // inxV:1 inxX1 index17 192, // inxV:1 inxX2 index18 192, // inxV:1 inxX3 index19 192, // inxV:1 inxX4 index20 192, // inxV:1 inxX5 index21 175, // inxV:1 inxX6 index22 164, // inxV:1 inxX7 index23 156, // inxV:1 inxX8 index24 145, // inxV:1 inxX9 index25 128, // inxV:1 inxX10 index26 128, // inxV:1 inxX11 index27 128, // inxV:1 inxX12 index28 128, // inxV:1 inxX13 index29 128, // inxV:1 inxX14 index30 128, // inxV:1 inxX15 index31 192, // inxV:2 inxX0 index32 192, // inxV:2 inxX1 index33 192, // inxV:2 inxX2 index34 192, // inxV:2 inxX3 index35 192, // inxV:2 inxX4 index36 192, // inxV:2 inxX5 index37 175, // inxV:2 inxX6 index38 164, // inxV:2 inxX7 index39 156, // inxV:2 inxX8 index40 145, // inxV:2 inxX9 index41 128, // inxV:2 inxX10 index42 128, // inxV:2 inxX11 index43 128, // inxV:2 inxX12 index44 128, // inxV:2 inxX13 index45 128, // inxV:2 inxX14 index46 128, // inxV:2 inxX15 index47 192, // inxV:3 inxX0 index48 192, // inxV:3 inxX1 index49 192, // inxV:3 inxX2 index50 192, // inxV:3 inxX3 index51 192, // inxV:3 inxX4 index52 192, // inxV:3 inxX5 index53 175, // inxV:3 inxX6 index54 164, // inxV:3 inxX7 index55 156, // inxV:3 inxX8 index56 145, // inxV:3 inxX9 index57 128, // inxV:3 inxX10 index58 128, // inxV:3 inxX11 index59 128, // inxV:3 inxX12 index60 128, // inxV:3 inxX13 index61 128, // inxV:3 inxX14 index62 128, // inxV:3 inxX15 index63 192, // inxV:4 inxX0 index64 192, // inxV:4 inxX1 index65 192, // inxV:4 inxX2 index66 192, // inxV:4 inxX3 index67 192, // inxV:4 inxX4 index68 192, // inxV:4 inxX5 index69 175, // inxV:4 inxX6 index70 164, // inxV:4 inxX7 index71 156, // inxV:4 inxX8 index72 145, // inxV:4 inxX9 index73 128, // inxV:4 inxX10 index74 128, // inxV:4 inxX11 index75 128, // inxV:4 inxX12 index76 128, // inxV:4 inxX13 index77 128, // inxV:4 inxX14 index78 128, // inxV:4 inxX15 index79 192, // inxV:5 inxX0 index80 192, // inxV:5 inxX1 index81 192, // inxV:5 inxX2 index82 192, // inxV:5 inxX3 index83 192, // inxV:5 inxX4 index84 192, // inxV:5 inxX5 index85 175, // inxV:5 inxX6 index86 164, // inxV:5 inxX7 index87 156, // inxV:5 inxX8 index88 145, // inxV:5 inxX9 index89 128, // inxV:5 inxX10 index90 128, // inxV:5 inxX11 index91 128, // inxV:5 inxX12 index92 128, // inxV:5 inxX13 index93 128, // inxV:5 inxX14 index94 128, // inxV:5 inxX15 index95 175, // inxV:6 inxX0 index96 175, // inxV:6 inxX1 index97 175, // inxV:6 inxX2 index98 175, // inxV:6 inxX3 index99 175, // inxV:6 inxX4 index100 175, // inxV:6 inxX5 index101 147, // inxV:6 inxX6 index102 142, // inxV:6 inxX7 index103 136, // inxV:6 inxX8 index104 128, // inxV:6 inxX9 index105 111, // inxV:6 inxX10 index106 111, // inxV:6 inxX11 index107 111, // inxV:6 inxX12 index108 111, // inxV:6 inxX13 index109 111, // inxV:6 inxX14 index110 111, // inxV:6 inxX15 index111 164, // inxV:7 inxX0 index112 164, // inxV:7 inxX1 index113 164, // inxV:7 inxX2 index114 164, // inxV:7 inxX3 index115 164, // inxV:7 inxX4 index116 164, // inxV:7 inxX5 index117 142, // inxV:7 inxX6 index118 133, // inxV:7 inxX7 index119 128, // inxV:7 inxX8 index120 120, // inxV:7 inxX9 index121 100, // inxV:7 inxX10 index122 100, // inxV:7 inxX11 index123 100, // inxV:7 inxX12 index124 100, // inxV:7 inxX13 index125 100, // inxV:7 inxX14 index126 100, // inxV:7 inxX15 index127 156, // inxV:8 inxX0 index128 156, // inxV:8 inxX1 index129 156, // inxV:8 inxX2 index130 156, // inxV:8 inxX3 index131 156, // inxV:8 inxX4 index132 156, // inxV:8 inxX5 index133 136, // inxV:8 inxX6 index134 128, // inxV:8 inxX7 index135 123, // inxV:8 inxX8 index136 114, // inxV:8 inxX9 index137 92, // inxV:8 inxX10 index138 92, // inxV:8 inxX11 index139 92, // inxV:8 inxX12 index140 92, // inxV:8 inxX13 index141 92, // inxV:8 inxX14 index142 92, // inxV:8 inxX15 index143 145, // inxV:9 inxX0 index144 145, // inxV:9 inxX1 index145 145, // inxV:9 inxX2 index146 145, // inxV:9 inxX3 index147 145, // inxV:9 inxX4 index148 145, // inxV:9 inxX5 index149 128, // inxV:9 inxX6 index150 120, // inxV:9 inxX7 index151 114, // inxV:9 inxX8 index152 109, // inxV:9 inxX9 index153 81, // inxV:9 inxX10 index154 81, // inxV:9 inxX11 index155 81, // inxV:9 inxX12 index156 81, // inxV:9 inxX13 index157 81, // inxV:9 inxX14 index158 81, // inxV:9 inxX15 index159 128, // inxV:10 inxX0 index160 128, // inxV:10 inxX1 index161 128, // inxV:10 inxX2 index162 128, // inxV:10 inxX3 index163 128, // inxV:10 inxX4 index164 128, // inxV:10 inxX5 index165 111, // inxV:10 inxX6 index166 100, // inxV:10 inxX7 index167 92, // inxV:10 inxX8 index168 81, // inxV:10 inxX9 index169 64, // inxV:10 inxX10 index170 64, // inxV:10 inxX11 index171 64, // inxV:10 inxX12 index172 64, // inxV:10 inxX13 index173 64, // inxV:10 inxX14 index174 64, // inxV:10 inxX15 index175 128, // inxV:11 inxX0 index176 128, // inxV:11 inxX1 index177 128, // inxV:11 inxX2 index178 128, // inxV:11 inxX3 index179 128, // inxV:11 inxX4 index180 128, // inxV:11 inxX5 index181 111, // inxV:11 inxX6 index182 100, // inxV:11 inxX7 index183 92, // inxV:11 inxX8 index184 81, // inxV:11 inxX9 index185 64, // inxV:11 inxX10 index186 64, // inxV:11 inxX11 index187 64, // inxV:11 inxX12 index188 64, // inxV:11 inxX13 index189 64, // inxV:11 inxX14 index190 64, // inxV:11 inxX15 index191 128, // inxV:12 inxX0 index192 128, // inxV:12 inxX1 index193 128, // inxV:12 inxX2 index194 128, // inxV:12 inxX3 index195 128, // inxV:12 inxX4 index196 128, // inxV:12 inxX5 index197 111, // inxV:12 inxX6 index198 100, // inxV:12 inxX7 index199 92, // inxV:12 inxX8 index200 81, // inxV:12 inxX9 index201 64, // inxV:12 inxX10 index202 64, // inxV:12 inxX11 index203 64, // inxV:12 inxX12 index204 64, // inxV:12 inxX13 index205 64, // inxV:12 inxX14 index206 64, // inxV:12 inxX15 index207 128, // inxV:13 inxX0 index208 128, // inxV:13 inxX1 index209 128, // inxV:13 inxX2 index210 128, // inxV:13 inxX3 index211 128, // inxV:13 inxX4 index212 128, // inxV:13 inxX5 index213 111, // inxV:13 inxX6 index214 100, // inxV:13 inxX7 index215 92, // inxV:13 inxX8 index216 81, // inxV:13 inxX9 index217 64, // inxV:13 inxX10 index218 64, // inxV:13 inxX11 index219 64, // inxV:13 inxX12 index220 64, // inxV:13 inxX13 index221 64, // inxV:13 inxX14 index222 64, // inxV:13 inxX15 index223 128, // inxV:14 inxX0 index224 128, // inxV:14 inxX1 index225 128, // inxV:14 inxX2 index226 128, // inxV:14 inxX3 index227 128, // inxV:14 inxX4 index228 128, // inxV:14 inxX5 index229 111, // inxV:14 inxX6 index230 100, // inxV:14 inxX7 index231 92, // inxV:14 inxX8 index232 81, // inxV:14 inxX9 index233 64, // inxV:14 inxX10 index234 64, // inxV:14 inxX11 index235 64, // inxV:14 inxX12 index236 64, // inxV:14 inxX13 index237 64, // inxV:14 inxX14 index238 64, // inxV:14 inxX15 index239 128, // inxV:15 inxX0 index240 128, // inxV:15 inxX1 index241 128, // inxV:15 inxX2 index242 128, // inxV:15 inxX3 index243 128, // inxV:15 inxX4 index244 128, // inxV:15 inxX5 index245 111, // inxV:15 inxX6 index246 100, // inxV:15 inxX7 index247 92, // inxV:15 inxX8 index248 81, // inxV:15 inxX9 index249 64, // inxV:15 inxX10 index250 64, // inxV:15 inxX11 index251 64, // inxV:15 inxX12 index252 64, // inxV:15 inxX13 index253 64, // inxV:15 inxX14 index254 64 // inxV:15 inxX15 index255 }; void setup() { Serial.begin(9600); } void loop() { if(Serial.available()) { unsigned char u = Serial.read(); unsigned char F = tabelle[u]; Serial.write(F); } }
Code 0-22: Sketch auf dem Arduino.
#8 Mi 10.05.2023
Dieser Termin findet als reine Übung statt
Es sind noch die Fertigstellung zweier Übungen aus vorangehenden Terminen offen:
1. Dezimale Darstellung eines Bytes auf einer 4-fach-7-Segment-Anzeige. Sie dazu: #5 Mi 19.04.2023, Große Projektarbeit: Entwurf, Programmierung und Bau einer digitalen Anzeige, Stufe 1 bis 5.
2. Übertragen eines Fuzzy-Reglers als HIL in Form einer Tabelle. Hinweis: Mit "Tabelle" ist nicht das Sprachelement "Lookup-Table" gemeint, sondern der Umstand, dass kein Fuzzy-System auf dem FPGS implementiert wird, sondern lediglich die Zuordnung der diskretisierten Werte von Input und Output eines Fuzzysystems dort Daten abrufbar sind. Siehe dazu: #6 Mi 26.04.2023, 7. Übung, sowie "Code 0-22: Sketch auf dem Arduino.", bei dem das Konzept zunächst auf einem Arduino realisiert wurde. Es wird wahrscheinlich nötig sein, ein Bytewort in eine Integerzahl umzuwandeln. Hierfür scheint die Funktion CONV_INTEGER(UNSIGNED(WORT)) geeignet zu sein, siehe auch:

|
Kleine Übungen für eine bessere Routine im Arbeiten mit dem FPGA-Board
Die vorgenannten Aufgaben zu meistern ist relativ schwierig und komplex und vielleicht nicht ganz ohne Hilfe umsetzbar. Darum werden ergänzend nachfolgend zwei Übungsblätter bereit gestellt, die einfachere Aufgaben enthalten. Diese Aufgaben können ganz gut bei dem aktullen Wissensstand alleine gelöst werden.
Versuchen Sie also die nachfolgenden Übungen zunächst selsttätig zu bearbeiten. Diskutieren Sie Ihre Lösungsansätze aber gerne untereinander, um ggf. Problemen bei der Lösung beizukommen:


Viel Erfolg ;-)
#9 Mi 17.05.2023
Themen
|
1. Musterlösung und/oder studentische Lösung zu Multiplikation auf dem FPGA
Der Ablauf hier sollte sein:
|
1a) Processing-Sketch auf dem PC (gleich wie Code 0-14)
import processing.serial.*; Serial myPort; // Create object from Serial class int val; // Data received from the serial port public void setup() { Serial.list(); String portName = Serial.list()[0]; myPort = new Serial(this, portName, 9600); textSize(30); size(500,500); frameRate(1); } int x=0; int y=0; public void draw() { background(255); fill(0); if( myPort.available() > 0) { // If data is available, val = myPort.read(); // read it and store it in val } text("x="+x+" y="+y+" x*y="+val,10,height/2); x++; if(x>=16) { x=0; y++; } if(y>=16) { y=0; } int z = x + 16*y; myPort.write(z); }
Code 0-23: Processing-Seite: x,y an Arduino schicken, x*y empfangen und zeigen.
1b) Managen der Weiterleitung zwischen PC und FPGA beim Arduino
Erfolgreich kompiliert aber nicht getestet!
void portb2fpga(unsigned char muster) { /* * muster 1 => Bit bei DDRB digitaler Eingang also 0 * muster 0 => Bit bei DDRB digitaler Ausgang also 1 */ DDRB = ~muster; /* * muster 1 => Bit bei PORTB digitaler Eingang OHNE Pullup, deshalb 0 * muster 0 => Bit bei PORTB digitaler Ausgang, der logisch Null sendet, also 0 */ PORTB = 0b00000000; } void setup() { /* * so kann beim FPGA kein versehentlicher Kurzschluss entstehen, denn alle Bits sind so als * Eingang konfiguriert und damit HOCHOHMIG! */ portb2fpga(255); Serial.begin(9600); pinMode(0,0); pinMode(1,0); pinMode(2,0); pinMode(3,0); pinMode(4,0); pinMode(5,0); pinMode(6,0); pinMode(7,0); } void loop() { if(Serial.available()) { int u = Serial.read(); portb2fpga(u); delay(1); //ev. später weglassen. int z = 0; z+=digitalRead(7);z*=2; z+=digitalRead(6);z*=2; z+=digitalRead(5);z*=2; z+=digitalRead(4);z*=2; z+=digitalRead(3);z*=2; z+=digitalRead(2);z*=2; z+=digitalRead(1);z*=2; z+=digitalRead(0); Serial.write(z); } }
Code 0-24: Arduino-Seite.
1c) Multiplikation der Integer-Zahlen auf dem FPGA, noch OHNE CONSTRAINTS

Bild 0-4: Definition der Ein- und Ausgänge beim Anlegen des Projektes.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; -- ACHTUNG, WURDE HINZUGEFÜGT!!!! use ieee.STD_LOGIC_ARITH.all; -- ACHTUNG, WURDE HINZUGEFÜGT!!!! entity hilmult is Port ( BYTEIN : in STD_LOGIC_VECTOR (7 downto 0); BYTEOUT : out STD_LOGIC_VECTOR (7 downto 0)); end hilmult; architecture Behavioral of hilmult is signal z : integer range 0 to 255 := 0; signal a : integer range 0 to 255 := 0; signal b : integer range 0 to 255 := 0; begin a <= TO_INTEGER(UNSIGNED(BYTEIN(3 downto 0))); b <= TO_INTEGER(UNSIGNED(BYTEIN(7 downto 4))); z <= a*b; BYTEOUT <= conv_std_logic_vector(z,BYTEOUT'length); end Behavioral;
Code 0-25: FPGA-Code
ÜBUNG: Ergänzen Sie die Constraints und bringen das Gesamtsystem zum Laufen.
2. Musterlösung und/oder studentische Lösung zu Fuzzy-Lookup-Table auf dem FPGA
2a) Processing-Sketch (Simulation und Animation)
import processing.serial.*; Serial myPort; // Create object from Serial class int val; // Data received from the serial port double dt = 0.05; public int x2int(double x) { if(x>1.0) x=1.0; if(x<-1.0) x=-1.0; x+=1.0; x/=2.0; x*=16.0; x+=0.5; if(x>=16.0) x=15.0; return (int)x; } public double int2x(int x) { return ((double)x-7.5)/7.5; } public int f2int(double x) { if(x>10.0) x=10.0; if(x<-10.0) x=-10.0; x+=10.0; x/=20.0; x*=256.0; x+=0.5; if(x>=256.0) x=255.0; return (int)x; } public double int2f(int x) { return 10.0*((double)x-127.5)/127.5; } public void setup() { Serial.list(); String portName = Serial.list()[0]; myPort = new Serial(this, portName, 9600); textSize(30); size(500,500); frameRate(20); } double x=1.0; double v=0.0; long T=System.currentTimeMillis(); double Fehler = 0.0; double F_A=0.0; public void draw() { background(255); fill(0); if(System.currentTimeMillis()<T+10000) { int iv = x2int(v); int ix = x2int(x); int ein_byte = iv*16+ix; myPort.write(ein_byte); if( myPort.available() > 0) { // If data is available, val = myPort.read(); // read it and store it in val F_A = int2f(val); } x+=v*dt; v+=(-x+F_A)*dt; Fehler+=x*x; } ellipse((float)x*(width/2)+width/2,height/2,width/50,height/50); text("Err="+Fehler,50,50); text("F_A="+F_A,50,100); text("x="+x,50,150); } public void keyPressed() { if(key==' ') { x=1.0; v=0.0; T=System.currentTimeMillis(); Fehler=0.0; } }
Code 0-26: Processing-Sketch.
2b) Arduino-Sketch (Weiterleitung wie gehabt)
void portb2fpga(unsigned char muster) { DDRB = ~muster; PORTB = 0b00000000; } void setup() { portb2fpga(255); Serial.begin(9600); pinMode(0,0); pinMode(1,0); pinMode(2,0); pinMode(3,0); pinMode(4,0); pinMode(5,0); pinMode(6,0); pinMode(7,0); } void loop() { if(Serial.available()) { int u = Serial.read(); portb2fpga(u); delay(1); //ev. später weglassen. int z = 0; z+=digitalRead(7);z*=2; z+=digitalRead(6);z*=2; z+=digitalRead(5);z*=2; z+=digitalRead(4);z*=2; z+=digitalRead(3);z*=2; z+=digitalRead(2);z*=2; z+=digitalRead(1);z*=2; z+=digitalRead(0); Serial.write(z); } }
Code 0-27: Arduino-Seite.
2c) Lookup-Table auf der FPGA-Seite
|
2c1) Code-Generator mit Processing:
int[] tabelle = {192, // inxV:0 inxX0 index0 192, // inxV:0 inxX1 index1 192, // inxV:0 inxX2 index2 192, // inxV:0 inxX3 index3 192, // inxV:0 inxX4 index4 192, // inxV:0 inxX5 index5 175, // inxV:0 inxX6 index6 164, // inxV:0 inxX7 index7 // .... usw. usw., vergl. Arduino Code 0-22 92, // inxV:15 inxX8 index248 81, // inxV:15 inxX9 index249 64, // inxV:15 inxX10 index250 64, // inxV:15 inxX11 index251 64, // inxV:15 inxX12 index252 64, // inxV:15 inxX13 index253 64, // inxV:15 inxX14 index254 64 // inxV:15 inxX15 index255 }; /** * INPUT ist identisch mit dem INDEX der liste. * OUTPUT ist identisch mit den WERTEN in liste. */ public void generateSelection(int[] liste) { println("with INPUT select OUTPUT <="); int index=0; int start=0; while(start<liste.length) { while(index<liste.length && liste[index]==liste[start]) { index++; } if(index==liste.length) { println(" "+liste[start]+" when others;"); } else if(start<index-1) { println(" "+liste[start]+" when "+start+" to "+(index-1)+","); } else { println(" "+liste[start]+" when "+start+","); } if(index>start) start=index; else start++; } } public void setup() { generateSelection(tabelle); } public void draw() { }
Code 0-28: Code-Generator mit Processing.
2c2) Einbau in das FPGA-Programm:
Erfolgreich kompiliert aber nicht getestet!
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; -- ACHTUNG, WURDE HINZUGEFÜGT!!!! use ieee.STD_LOGIC_ARITH.all; -- ACHTUNG, WURDE HINZUGEFÜGT!!!! entity hilfuzzy is Port ( BYTEIN : in STD_LOGIC_VECTOR (7 downto 0); BYTEOUT : out STD_LOGIC_VECTOR (7 downto 0)); end hilfuzzy; architecture Behavioral of hilfuzzy is signal z : integer range 0 to 255 := 0; signal a : integer range 0 to 255 := 0; begin a <= TO_INTEGER(UNSIGNED(BYTEIN(7 downto 0))); with a select z <= 192 when 0 to 5, 175 when 6, 164 when 7, 156 when 8, 145 when 9, 128 when 10 to 15, 192 when 16 to 21, 175 when 22, 164 when 23, 156 when 24, 145 when 25, 128 when 26 to 31, 192 when 32 to 37, 175 when 38, 164 when 39, 156 when 40, 145 when 41, 128 when 42 to 47, 192 when 48 to 53, 175 when 54, 164 when 55, 156 when 56, 145 when 57, 128 when 58 to 63, 192 when 64 to 69, 175 when 70, 164 when 71, 156 when 72, 145 when 73, 128 when 74 to 79, 192 when 80 to 85, 175 when 86, 164 when 87, 156 when 88, 145 when 89, 128 when 90 to 95, 175 when 96 to 101, 147 when 102, 142 when 103, 136 when 104, 128 when 105, 111 when 106 to 111, 164 when 112 to 117, 142 when 118, 133 when 119, 128 when 120, 120 when 121, 100 when 122 to 127, 156 when 128 to 133, 136 when 134, 128 when 135, 123 when 136, 114 when 137, 92 when 138 to 143, 145 when 144 to 149, 128 when 150, 120 when 151, 114 when 152, 109 when 153, 81 when 154 to 159, 128 when 160 to 165, 111 when 166, 100 when 167, 92 when 168, 81 when 169, 64 when 170 to 175, 128 when 176 to 181, 111 when 182, 100 when 183, 92 when 184, 81 when 185, 64 when 186 to 191, 128 when 192 to 197, 111 when 198, 100 when 199, 92 when 200, 81 when 201, 64 when 202 to 207, 128 when 208 to 213, 111 when 214, 100 when 215, 92 when 216, 81 when 217, 64 when 218 to 223, 128 when 224 to 229, 111 when 230, 100 when 231, 92 when 232, 81 when 233, 64 when 234 to 239, 128 when 240 to 245, 111 when 246, 100 when 247, 92 when 248, 81 when 249, 64 when others; BYTEOUT <= conv_std_logic_vector(z,BYTEOUT'length); end Behavioral;
Code 0-29: Einbau in das FPGA-Programm.
ÜBUNG: Ergänzen Sie die Constraints und bringen das Gesamtsystem zum Laufen.
3. Diskretisiertes Neuronales auf einem FPGA
|
3a) Vorversuch mit Processing
|
import java.util.Random; /** Gutes Ergebnis der Optimierung: err=6.221646872728446E-17 double[] ww = {80.31866271082562,-31.265850938936456,85.13429379525569,45.23179437588253,-64.49043801364026,82.80217344167164,25.303098407831502,-44.11871152333888,22.46308463857618,115.53667395418526,-35.885562823122015,72.62564317964451,86.44491797680459,-37.66248583715228}; 9946.: err=1.1273091438290979E-29 double[] ww = {59.88158563287507,-50.27064144012919,85.76588416877144,49.017939415628014,-32.75870684723511,37.51524576058126,115.67032889427048,-3.2358715143235672,-50.992959804044645,-12.4867037600585,49.22361064403374,-47.630024570244494,37.182614197635395,-103.83774839016222}; */ Random zufall = new Random(System.currentTimeMillis()); double[] ww = {59.88158563287507,-50.27064144012919,85.76588416877144,49.017939415628014,-32.75870684723511,37.51524576058126,115.67032889427048,-3.2358715143235672,-50.992959804044645,-12.4867037600585,49.22361064403374,-47.630024570244494,37.182614197635395,-103.83774839016222}; int[] iw = new int[ww.length]; double r(double x) { return Math.round(x); } double sigmoid(double u) { return r( 1.0/(1+Math.exp(-u)) ); } double netz(double[] in, double[] w) { //Eingangsschicht mit 3 Neuronen: double out0 = sigmoid(in[0]*w[0]+in[1]*w[1]); double out1 = sigmoid(in[0]*w[2]+in[1]*w[3]); double out2 = sigmoid(in[0]*w[4]+in[1]*w[5]); //Zwischenschicht mit zwei Neuronen: double out3 = sigmoid(out0*w[6]+out1*w[7]+out2*w[8]); double out4 = sigmoid(out0*w[9]+out1*w[10]+out2*w[11]); //Ausgangsschicht mit einem Neuron: double out5 = sigmoid(out3*w[12]+out4*w[13]); return out5; } void codegenerator() { //Eingangsschicht mit 3 Neuronen: //double out0 = sigmoid(in[0]*w[0]+in[1]*w[1]); println("u0 <= in0*"+iw[0]+" +in1*"+iw[1]+";"); println("with u0 select out0 <="); println("1 when 0 to 255,"); println("0 when others;"); //double out1 = sigmoid(in[0]*w[2]+in[1]*w[3]); println("u1 <= in0*"+iw[2]+" +in1*"+iw[3]+";"); println("with u1 select out1 <="); println("1 when 0 to 255,"); println("0 when others;"); //double out2 = sigmoid(in[0]*w[4]+in[1]*w[5]); println("u2 <= in0*"+iw[4]+" +in1*"+iw[5]+";"); println("with u2 select out2 <="); println("1 when 0 to 255,"); println("0 when others;"); //Zwischenschicht mit zwei Neuronen: //double out3 = sigmoid(out0*w[6]+out1*w[7]+out2*w[8]); println("u3 <= out0*"+iw[6]+" +out1*"+iw[7]+" +out2*"+iw[8]+";"); println("with u3 select out3 <="); println("1 when 0 to 255,"); println("0 when others;"); //double out4 = sigmoid(out0*w[9]+out1*w[10]+out2*w[11]); println("u4 <= out0*"+iw[9]+" +out1*"+iw[10]+" +out2*"+iw[11]+";"); println("with u4 select out4 <="); println("1 when 0 to 255,"); println("0 when others;"); //Ausgangsschicht mit einem Neuron: //double out5 = sigmoid(out3*w[12]+out4*w[13]); println("u5 <= out3*"+iw[12]+" +out4*"+iw[13]+";"); println("with u5 select out5 <="); println("1 when 0 to 255,"); println("0 when others;"); //return out5; } double[][][] lernpattern = { {{0.0,0.0},{1.0}}, {{0.0,1.0},{0.0}}, {{1.0,0.0},{0.0}}, {{1.0,1.0},{1.0}} }; public double fehler(double[] w) { double err = 0.0; for(int i=0;i<lernpattern.length;i++) { double eneu = netz(lernpattern[i][0],w); err+=(eneu-lernpattern[i][1][0])*(eneu-lernpattern[i][1][0]); } return Math.sqrt(err); } public void setup() { for(int i=0;i<ww.length;i++) ww[i]=r(ww[i]); for(int i=0;i<ww.length;i++) iw[i]=(int)ww[i]; /** int[][] x = {{1,2},{1,2,3},{0}}; // x.length == 3 // x[0].length == 2 // x[1].length == 3 */ //Optimierung: double[] w = ww; double errBest = fehler(w); println("errBest = "+errBest); println("Minimum und Maximum von u:"); double umin = Double.MAX_VALUE; double umax = -Double.MAX_VALUE; for(int i=0;i<ww.length;i++) { for(int k=0;k<ww.length;k++) { double u1 = ww[i]+ww[k]; double u2 = ww[i]; double u3 = ww[k]; if(umin>u1) umin=u1; if(umax<u1) umax=u1; if(umin>u1) umin=u2; if(umax<u1) umax=u2; if(umin>u1) umin=u3; if(umax<u1) umax=u3; } } println("umin="+umin); //-208 println("umax="+umax); //232 for(int i=-208;i<=232;i++) { println("u="+i+" s(u)="+sigmoid((double)i)); //u>=0 =>1, sonst 0. } codegenerator(); } public void draw() { }
Code 0-30: Diskretes NN nebst Codegenerator für den FPGA.
3b) Finale Umsetzung als Übung
|
ÜBUNG: Vervollständigen Sie das FPGA-Programm und das Gesamtsystem.
Hinweis: Nur 2 Eingangs. und 1 Ausgangsbit müssen hier weitergeleitet werden.
u0 <= in0*60 +in1*-50; with u0 select out0 <= 1 when 0 to 255, 0 when others; u1 <= in0*86 +in1*49; with u1 select out1 <= 1 when 0 to 255, 0 when others; u2 <= in0*-33 +in1*38; with u2 select out2 <= 1 when 0 to 255, 0 when others; u3 <= out0*116 +out1*-3 +out2*-51; with u3 select out3 <= 1 when 0 to 255, 0 when others; u4 <= out0*-12 +out1*49 +out2*-48; with u4 select out4 <= 1 when 0 to 255, 0 when others; u5 <= out3*37 +out4*-104; with u5 select out5 <= 1 when 0 to 255, 0 when others;
Code 0-31: Kerncode aus Vorversuch.
4. Übungsaufgaben (ev. zurückstellen)
#10 Mi 24.05.2023
Aktuell

Bild 0-5: Quelle: Der Spiegel vom 20.05.2023.
Themen als Präsentation
|
Themen als Übung
|
1. Besprechung einer Lösung zur Umsetzung des Neuronalen Netzes auf dem FPGA
Umsetzung des Neuronalen Netzes auf dem FPGA: Musterlösung zu "3b) Finale Umsetzung als Übung" von letzter Woche.
---------------------------------------------------------------------------------- -- Company: -- Engineer: -- -- Create Date: 09:00:06 05/23/2023 -- Design Name: -- Module Name: neuronetz - Behavioral -- Project Name: -- Target Devices: -- Tool versions: -- Description: -- -- Dependencies: -- -- Revision: -- Revision 0.01 - File Created -- Additional Comments: -- ---------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use ieee.std_logic_arith.all; -- enthält CONV_INTEGER NICHT STANDARD -- Uncomment the following library declaration if using -- arithmetic functions with Signed or Unsigned values use IEEE.NUMERIC_STD.ALL; -- enthält TO_INTEGER --use IEEE.STD_LOGIC_UNSIGNED.ALL; -- Uncomment the following library declaration if instantiating -- any Xilinx primitives in this code. --library UNISIM; --use UNISIM.VComponents.all; entity neuronetz is Port ( EINGANG : in STD_LOGIC_VECTOR (1 downto 0); AUSGANG : out STD_LOGIC_VECTOR (0 downto 0)); end neuronetz; architecture Behavioral of neuronetz is -- short: -32,768 to 32,767 -- unsigned short: 0 to 65,535 -- int 4 signed -2,147,483,648 to 2,147,483,647 signal eingangint : integer range -32768 to 32767 := 0; signal eingangbyte : STD_LOGIC_VECTOR(1 downto 0) := "00"; signal eingang0 : STD_LOGIC_VECTOR(0 downto 0) := "0"; signal eingang1 : STD_LOGIC_VECTOR(0 downto 0) := "0"; signal in0 : integer range 0 to 1 := 0; -- signal in1 : integer range -32768 to 32767 := 0; -- WARNIMG:line 69: The result of a 6x7-bit multiplication -- is partially used. Only the 11 least significant bits are used. -- If you are doing this on purpose, you may safely ignore this warning. -- Otherwise, make sure you are not losing information, -- leading to unexpected circuit behavior. signal in1 : integer range 0 to 1 := 0; signal out0 : integer range 0 to 1 := 0; signal out1 : integer range 0 to 1 := 0; signal out2 : integer range 0 to 1 := 0; signal out3 : integer range 0 to 1 := 0; signal out4 : integer range 0 to 1 := 0; signal out5 : integer range 0 to 1 := 0; signal u0 : integer range -32768 to 32767 := 0; signal u1 : integer range -32768 to 32767 := 0; signal u2 : integer range -32768 to 32767 := 0; signal u3 : integer range -32768 to 32767 := 0; signal u4 : integer range -32768 to 32767 := 0; signal u5 : integer range -32768 to 32767 := 0; begin eingangbyte <= EINGANG; eingang0 <= eingangbyte(0 downto 0); eingang1 <= eingangbyte(1 downto 1); in0 <= CONV_INTEGER(UNSIGNED(eingang0)); in1 <= CONV_INTEGER(UNSIGNED(eingang1)); u0 <= in0*60 + in1*(-50); with u0 select out0 <= 1 when 0 to 255, 0 when others; u1 <= in0*86 +in1*49; with u1 select out1 <= 1 when 0 to 255, 0 when others; u2 <= in0*(-33) +in1*38; with u2 select out2 <= 1 when 0 to 255, 0 when others; u3 <= out0*116 +out1*(-3) +out2*(-51); with u3 select out3 <= 1 when 0 to 255, 0 when others; u4 <= out0*(-12) +out1*49 +out2*(-48); with u4 select out4 <= 1 when 0 to 255, 0 when others; u5 <= out3*37 +out4*(-104); with u5 select out5 <= 1 when 0 to 255, 0 when others; AUSGANG <= conv_std_logic_vector(out5,AUSGANG'length); end Behavioral;
Code 0-32: Umsetzung des Neuronalen Netzes auf dem FPGA: Musterlösung zu "3b) Finale Umsetzung als Übung" von letzter Woche.
Übung:
|
2. Emulation einer seriellen Schnittstelle

Vorprojekt:
Eine LED auf P125 soll fortwährend den Morsecode SOS aussenden:
...---... Pause ...---... Pause ...---... Pause usw.
Nähere Spezifikation:
|
10101011001100110010101000000000 Umsetzung mit VHDL:
entity morsen is Port ( MEINECLOCK : in STD_LOGIC; MEINELED : out STD_LOGIC); end morsen; architecture Behavioral of morsen is signal zaehler : integer range 0 to 1499999 := 0; signal logikpegel : std_logic := '0'; signal wort : std_logic_vector(31 downto 0) := "10101011001100110010101000000000"; begin process begin wait until rising_edge(MEINECLOCK); if (zaehler<1499999) then zaehler <= zaehler+1; else zaehler <= 0; logikpegel <= wort(31); wort <= wort(30 downto 0) & wort(31); end if; end process; MEINELED <= logikpegel;
Code 0-33: end Behavioral;
## GCLK6 ist auf FPGA-Chip Pin 56 NET "MEINECLOCK" LOC = "P56"; ## IO_L05P_0 ist auf FPGA-Chip Pin 125 NET "MEINELED" LOC = "P125"; NET "MEINECLOCK" IOSTANDARD = LVCMOS25; NET "MEINELED" IOSTANDARD = LVCMOS33;
Code 0-34: Constraints.
Nach dem gleichen Prinzip soll in der Übung zyklisch eine Bitfolge vom FPGA an den seriellen Eingang eines Arduino gesendet werden.
Die Bitfolge soll zyklisch den Buchstaben A mit dem RS232-Protokoll schicken.
Vorbesprechung:
|
Übung:
|

3. Signalverarbeitung: Sinusgenerator
Eine einfache Möglichkeit zu einem Digital-Analog-Wandler besteht in der Verwendung eines R/2R Widerstandsnetzwerks:


Übung: Umsetzung des Sinusgenerators mit einem FPGA und einem R/2R Widerstandsnetzwerk für 8 Bit.
Arbeitsschritte:
|
4. Physical Modeling (Vorschau)

#11 Mi 31.05.2023
Thema: Projekt Digitales Musikinstrument weiter bringen
1. Testumsetzung: Tonausgabe über DA-Wandler R/2R-Netzwerk, nur Rechtecksignal
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; -- WICHTIG, DAMIT CONCURRENT WHEN einen Effekt hat! -- Umsetzung eines DAC am Beispiel eines Rechteckgenerators -- 20Khz Samplerate -- 200Hz Ausgang -- 100 Schritte für 200Hz entity sinus is Port ( MEINECLOCK : in STD_LOGIC; ANALOG : out STD_LOGIC_VECTOR (7 downto 0)); end sinus; architecture Behavioral of sinus is signal zaehler : integer range 0 to 299 := 0; --20kHz signal zaehler2 : integer range 0 to 99 := 0; --200Hz signal wort : std_logic_vector(7 downto 0); begin process begin wait until rising_edge(MEINECLOCK); if (zaehler<299) then zaehler <= zaehler+1; else zaehler <= 0; ---- Beginn zähler 2 if (zaehler2<99) then zaehler2 <= zaehler2+1; else zaehler2 <= 0; end if; end if; end process; --- Zuordnungen zum Ausgang, Test Rechteckschwingung von 200Hz with zaehler2 select wort <= "11111111" when 0 to 50, "00000000" when others; ANALOG <= wort; end Behavioral;
Code 0-35: 1. Testumsetzung: Tonausgabe über DA-Wandler R/2R-Netzwerk, nur Rechtecksignal
## GCLK6 ist auf FPGA-Chip Pin 56 NET "MEINECLOCK" LOC = "P56"; ## IO_L05P_0 ist auf FPGA-Chip Pin 125 NET "MEINECLOCK" IOSTANDARD = LVCMOS25; NET "ANALOG<0>" LOC = "P58" | IOSTANDARD = LVCMOS33 ; NET "ANALOG<1>" LOC = "P59" | IOSTANDARD = LVCMOS33 ; NET "ANALOG<2>" LOC = "P93" | IOSTANDARD = LVCMOS33 ; NET "ANALOG<3>" LOC = "P94" | IOSTANDARD = LVCMOS33 ; NET "ANALOG<4>" LOC = "P96" | IOSTANDARD = LVCMOS33 ; NET "ANALOG<5>" LOC = "P97" | IOSTANDARD = LVCMOS33 ; NET "ANALOG<6>" LOC = "P103" | IOSTANDARD = LVCMOS33 ; NET "ANALOG<7>" LOC = "P104" | IOSTANDARD = LVCMOS33 ;
Code 0-36: Constraints zu 1. Testumsetzung.
|

Bild 0-6: Schaltung zum Tongebeber.

Bild 0-7: Schaltungsaufbau zum Tongebeber (sehr leise).
2. Testumsetzung: Sinuston über DA-Wandler R/2R-Netzwerk
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; -- Umsetzung eines DAC am Beispiel eines Sinusgenerators -- 20Khz Samplerate -- 200Hz Ausgang -- 100 Schritte für 200Hz entity sinus is Port ( MEINECLOCK : in STD_LOGIC; ANALOG : out STD_LOGIC_VECTOR (7 downto 0)); end sinus; architecture Behavioral of sinus is signal zaehler : integer range 0 to 299 := 0; --20kHz signal zaehler2 : integer range 0 to 99 := 0; --200Hz signal wort : std_logic_vector(7 downto 0); begin process begin wait until rising_edge(MEINECLOCK); if (zaehler<299) then zaehler <= zaehler+1; else zaehler <= 0; ---- Beginn zähler 2 if (zaehler2<99) then zaehler2 <= zaehler2+1; else zaehler2 <= 0; end if; end if; end process; --- Zuordnungen zum Ausgang, Test Rechteckschwingung von 200Hz with zaehler2 select wort <= "00000000" when 0, "00000000" when 1, "00000001" when 2, "00000010" when 3, "00000100" when 4, "00000110" when 5, "00001000" when 6, "00001100" when 7, "00001111" when 8, "00010011" when 9, "00011000" when 10, "00011101" when 11, "00100010" when 12, "00101000" when 13, "00101110" when 14, "00110100" when 15, "00111011" when 16, "01000010" when 17, "01001001" when 18, "01010000" when 19, "01011000" when 20, "01011111" when 21, "01100111" when 22, "01101111" when 23, "01110111" when 24, "01111111" when 25, "10000111" when 26, "10001111" when 27, "10010111" when 28, "10011111" when 29, "10100110" when 30, "10101110" when 31, "10110101" when 32, "10111100" when 33, "11000011" when 34, "11001010" when 35, "11010000" when 36, "11010110" when 37, "11011100" when 38, "11100001" when 39, "11100110" when 40, "11101011" when 41, "11101111" when 42, "11110010" when 43, "11110110" when 44, "11111000" when 45, "11111010" when 46, "11111100" when 47, "11111101" when 48, "11111110" when 49, "11111110" when 50, "11111110" when 51, "11111101" when 52, "11111100" when 53, "11111010" when 54, "11111000" when 55, "11110110" when 56, "11110010" when 57, "11101111" when 58, "11101011" when 59, "11100110" when 60, "11100001" when 61, "11011100" when 62, "11010110" when 63, "11010000" when 64, "11001010" when 65, "11000011" when 66, "10111100" when 67, "10110101" when 68, "10101110" when 69, "10100110" when 70, "10011111" when 71, "10010111" when 72, "10001111" when 73, "10000111" when 74, "01111111" when 75, "01110111" when 76, "01101111" when 77, "01100111" when 78, "01011111" when 79, "01011000" when 80, "01010000" when 81, "01001001" when 82, "01000010" when 83, "00111011" when 84, "00110100" when 85, "00101110" when 86, "00101000" when 87, "00100010" when 88, "00011101" when 89, "00011000" when 90, "00010011" when 91, "00001111" when 92, "00001100" when 93, "00001000" when 94, "00000110" when 95, "00000100" when 96, "00000010" when 97, "00000001" when 98, "00000000" when 99, "00000000" when others; ANALOG <= wort; end Behavioral;
Code 0-37: 2. Testumsetzung: Sinuston über DA-Wandler R/2R-Netzwerk
Contraints wie zuvor.
public void setup() { for(int i=0;i<100;i++) { double dt = 1.0/20000.0; double t = (double)i*dt; double x = -Math.cos(2.0*Math.PI*200.0*t); double y = 255.0*((x+1.0)*0.5); int iy = (int)y; int a0 = iy%2; iy/=2; int a1 = iy%2; iy/=2; int a2 = iy%2; iy/=2; int a3 = iy%2; iy/=2; int a4 = iy%2; iy/=2; int a5 = iy%2; iy/=2; int a6 = iy%2; iy/=2; int a7 = iy%2; println(" \""+a7+""+a6+""+a5+""+a4+""+a3+""+a2+""+a1+""+a0+"\" when "+i+","); } }
Code 0-38: Codegenerator mit Processing für when-statement.