Echtzeitanwendungen auf Basis von HDL 6MT 4EMO im Sommersemester 2023
(EN google-translate)
(PL google-translate)
|
69_FPGA
68_nexys
67_Echtzeitsysteme
95_ETEST -- Hinweise zu der elektronischen Prüfungsform "E-Test"
#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?
Was ist ein FPGA? -- 69_FPGA/01_Einfuehrung
Vorberetung der ersten Übung mit einem FPGA-Board
|
FPGA-Board DLP-FPGA -- 69_FPGA/03_DLP_FPGA
Getting started alte Version mit ISE9 -- 69_FPGA/04_Getting_Started
AKTUELL: Verwendung von XSE14 -- 69_FPGA/21_XSE14
Upload des kompilierten Images (Blinkende LED) -- 69_FPGA/02_Einrichtung/02_BitLoadApp
69_FPGA/11_VHDL -- Kleine Referenz zu VHDL
Ü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
69_FPGA/03_DLP_FPGA
3. Vertiefung: Struktur von FPGA-Programmen, synchrone und asynchrone Schaltungen
69_FPGA/01_Einfuehrung
4. Vertiefung: Analyse von Programmen mit und ohne Prozess-Bereich
69_FPGA/05_Beispiele/03_Mustervergleich
69_FPGA/05_Beispiele/01_Blinkende_LED
5. Sprachelemente von VHDL -- Vorstellung einiger häufig verwendeter Datentypen und Programmstrukturen
69_FPGA/11_VHDL
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
steuerled.zip -- Projektordner zur obigen Lösung.
...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
steuerled2.zip -- Projekt zu obiger Lösung.
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
69_FPGA/11_VHDL
Lookup-Tabellen
Siehe zweite Musterlösung oben.
Preview Neuronale Netze
75_Echtzeit3/01_NeuronaleNetze/01_Neuron
69_FPGA/99_day_by_day_SoSe2018 -- siehe "Code 0-10: neuro2.vhd ".
Unisim Library (eher nächster Termin)
69_FPGA/12_UNISIM
Ü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.
69_FPGA/05_Beispiele/03_Mustervergleich -- "Code 0-4: Umsetzung mit VHDL in der Projektdatei mustervergleich2.vhd"
Ü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
69_FPGA/08_Synchronisieren
Neu: Verwendung selbst geschriebener Module.
Übung 1
|
69_FPGA/09_Uebung
2. D-Flip-Flop aus der Unisim Library
69_FPGA/12_UNISIM -- Schieberegister aus 4 D-Flip-Flops aus der UNISIM-Library
Übung 2
|
3. Lookup Table (LUT) aus der Unisim Library
69_FPGA/13_LUT -- Verwendung eines Lookup-Tables (hier UNISIM LUT4) statt logischer Operationen, um ein bestimmtes logisches Verhalten zu realisieren.
Ü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:
96_Arduino/22_Universal/02_LED_Leiste -- Siehe Bild 0-1.
Übersicht der FPGA-Board-Anschlüsse:
69_FPGA/03_DLP_FPGA
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!
69_FPGA/22_Uebung -- Siehe Bild 0-1.
ACHTUNG: Die Digits haben eine gemeinsame Kathode!:
|
Stufe 3
|
|
|
Stufe 4
|
|
|
04_SoSe2023/05_HDL_day_by_day -- Siehe Code 0-5: Lookup, Integer auf Integer mappen.
Flexibler ist eine case-Anweisung:
69_FPGA/22_Uebung -- Siehe Code 0-1: Verwendung einer 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:
|
1. Übersicht Neuronale Netze: 67_Echtzeitsysteme/08_NeuronaleNetze
2. Übersicht Fuzzy-Logik: 67_Echtzeitsysteme/13_Fuzzy
3. Übersicht Evolutionäre Algorithmen: 73_COACH3/09_Algorithmen/03_Evoopt
4. Übersicht zu SOM: 79_Deep_Learning/03_Unueberwachtes_Lernen
Softcomputing mit FPGA, Einführung, siehe: 68_nexys/11_Vorstudien
2. Software-In-the-Loop (SIL) -Ansatz
Bild 0-1: Angestrebtes SIL-Konzept.
3. Java/Processing
Einführung Java/Processing: 78_Processing
...siehe auch: 92_Soundinstallation/03_material/11_Snippets
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.
CodegeneratorLUT4 -- Sketch als .zip-File.
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:
FPGA003_simfuzzy.zip -- Nachfolgender Sketch.
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
|
FPGA004_LookupFuerArduino.zip -- Lookup-Table verfügbar machen.
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.
68_nexys/08_DiskretesNetz -- siehe "with summe0 select sigmo0 <=" in Code 0-2: nndiskret.vhd
Prüfen: Einsatz von ROM256X1, s.Seite 455, spartan3e_hdl.pdf.
02_SoSe2022/03_HDL -- siehe Code 0-16: VHDL der Musterlösung zur Stoppuhr
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:
uebung1.pdf
uebung2.pdf
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
Beschreibung des RS232 Protokolls -- 40_Mikrocontroller/06_UART/03_RS232
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:
|
Musterlösung, siehe Code 0-14 hier: 08_Archiv/03_SoSe2021/03_HDL/01_day_by_day
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)
Invertierendes Pendel, Tafelbilder 0-22 / 0-23 hier: 02_SoSe2022/02_SRT
#11 Mi 31.05.2023
Themen
|
1. Nachtrag: Test Diskretes Neuronales Netz, siehe Code 0-32 weiter oben
Bild 0-6: Testschaltung für diskretes Neuronales Netz, das als Äquivalenzgatter arbeiten soll.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity neuronetz is
Port ( EINGANG0 : in STD_LOGIC;
EINGANG1 : in STD_LOGIC;
AUSGANG : out STD_LOGIC;
AUSGANG2 : out STD_LOGIC);
end neuronetz;
architecture Behavioral of neuronetz is
signal in0 : integer range -32768 to 32767 := 0;
signal in1 : integer range -32768 to 32767 := 0;
signal out0 : integer range -32768 to 32767 := 0;
signal out1 : integer range -32768 to 32767 := 0;
signal out2 : integer range -32768 to 32767 := 0;
signal out3 : integer range -32768 to 32767 := 0;
signal out4 : integer range -32768 to 32767 := 0;
signal out5 : integer range -32768 to 32767 := 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;
signal test : integer range -32768 to 32767 := 0;
begin
with EINGANG0 select in0 <=
1 when '1',
0 when others;
with EINGANG1 select in1 <=
1 when '1',
0 when others;
u0 <= in0*60 - in1*50;
with u0 select out0 <=
1 when 0 to 32767,
0 when others;
u1 <= in0*86 +in1*49;
with u1 select out1 <=
1 when 0 to 32767,
0 when others;
u2 <= in1*38 - in0*33;
with u2 select out2 <=
1 when 0 to 32767,
0 when others;
u3 <= out0*116 - out1*3 - out2*51;
with u3 select out3 <=
1 when 0 to 32767,
0 when others;
u4 <= out1*49 - out2*48 - out0*12;
with u4 select out4 <=
1 when 0 to 32767,
0 when others;
u5 <= out3*37 - out4*104;
with u5 select out5 <=
1 when 0 to 32767,
0 when others;
with out5 select AUSGANG <=
'1' when 1 to 32767,
'0' when others;
--Kontrollausgabe:
test <= in0*30 - in1*30;
with test select AUSGANG2 <=
'1' when 1 to 32767,
'0' when others;
end Behavioral;
Code 0-35: Verbesserter VHDL-Code
NET "EINGANG0" LOC = "P126" | PULLUP | IOSTANDARD = LVCMOS33 ; # IO20 NET "EINGANG1" LOC = "P130" | PULLUP | IOSTANDARD = LVCMOS33 ; # IO21 NET "AUSGANG" LOC = "P125" | IOSTANDARD = LVCMOS33 ; #IO19 NET "AUSGANG2" LOC = "P131" | IOSTANDARD = LVCMOS33 ; #IO22
Code 0-36: Constraints
Notwendige Änderungen:
|
neuronetz.zip -- Xinlinx 14.7-Projekt.
Übung 1: Testen Sie das Projekt neuronetz.
2. Projekt Digitales Musikinstrument weiter bringen
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-37: 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-38: Constraints zu 1. Testumsetzung.
|
Bild 0-7: Schaltung zum Tongebeber.
Bild 0-8: 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-39: 2. Testumsetzung: Sinuston über DA-Wandler R/2R-Netzwerk
Contraints wie zuvor.
sinus.zip -- Xinlinx 14.7-Projekt.
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-40: Codegenerator mit Processing für when-statement.
Übung 2: Testen Sie das Projekt sinus.
Übung 3 -- Obertonmusik
|
Um das Geschehen akustisch wahrnehmbar zu machen, soll folgende Schaltung aufgebaut werden:
Bild 0-9: Schaltung für "Obertonspiel".
Hinweise:
|
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.std_logic_arith.all;
use IEEE.NUMERIC_STD.ALL;
library UNISIM;
use UNISIM.VComponents.all;
entity ober is
Port (MEINECLOCK : STD_Logic;
aus : out STD_LOGIC_VECTOR (7 downto 0));
end ober;
architecture Behavioral of ober is
constant f0: integer := 29999;
constant f1: integer := 14999;
constant f2: integer := 9999;
constant f3: integer := 7499;
constant f4: integer := 5999;
constant f5: integer := 4999;
constant f6: integer := 4285;
constant f7: integer := 3749;
signal zaehler0 : integer range 0 to 29999 := 0;
signal zaehler1 : integer range 0 to 14999 := 0;
signal zaehler2 : integer range 0 to 9999 := 0;
signal zaehler3 : integer range 0 to 7499 := 0;
signal zaehler4 : integer range 0 to 5999 := 0;
signal zaehler5 : integer range 0 to 4999 := 0;
signal zaehler6 : integer range 0 to 4285 := 0;
signal zaehler7 : integer range 0 to 3749 := 0;
signal frequenz : integer range 0 to 2999999 := 0;
signal wort : integer range 0 to 256 := 0;
signal Ausgang: STD_LOGIC_VECTOR (7 downto 0) := "00000000";
signal takt0 : STD_LOGIC := '0';
signal takt1 : STD_LOGIC := '0';
signal takt2 : STD_LOGIC := '0';
signal takt3 : STD_LOGIC := '0';
signal takt4 : STD_LOGIC := '0';
signal takt5 : STD_LOGIC := '0';
signal takt6 : STD_LOGIC := '0';
signal takt7 : STD_LOGIC := '0';
begin
process begin
wait until rising_edge(MEINECLOCK);
if(zaehler0<f0)then
zaehler0 <= zaehler0 + 1;
else
zaehler0 <= 0;
takt0 <= not takt0;
end if;
end process;
process begin
wait until rising_edge(MEINECLOCK);
if(zaehler1<f1)then
zaehler1 <= zaehler1 + 1;
else
zaehler1 <= 0;
takt1 <= not takt1;
end if;
end process;
process begin
wait until rising_edge(MEINECLOCK);
if(zaehler2<f2)then
zaehler2 <= zaehler2 + 1;
else
zaehler2 <= 0;
takt2 <= not takt2;
end if;
end process;
process begin
wait until rising_edge(MEINECLOCK);
if(zaehler3<f3)then
zaehler3 <= zaehler3 + 1;
else
zaehler3 <= 0;
takt3 <= not takt3;
end if;
end process;
process begin
wait until rising_edge(MEINECLOCK);
if(zaehler4<f4)then
zaehler4 <= zaehler4 + 1;
else
zaehler4 <= 0;
takt4 <= not takt4;
end if;
end process;
process begin
wait until rising_edge(MEINECLOCK);
if(zaehler5<f5)then
zaehler5 <= zaehler5 + 1;
else
zaehler5 <= 0;
takt5 <= not takt5;
end if;
end process;
process begin
wait until rising_edge(MEINECLOCK);
if(zaehler6<f6)then
zaehler6 <= zaehler6 + 1;
else
zaehler6 <= 0;
takt6 <= not takt6;
end if;
end process;
process begin
wait until rising_edge(MEINECLOCK);
if(zaehler7<f7)then
zaehler7 <= zaehler7 + 1;
else
zaehler7 <= 0;
takt7 <= not takt7;
end if;
end process;
process begin
wait until rising_edge(MEINECLOCK);
if(frequenz<2999999)then
frequenz <= frequenz + 1;
else
frequenz <= 0;
if(wort>255)then
wort <= 0;
else
wort <= wort +1;
end if;
end if;
end process;
Ausgang <= conv_std_logic_vector(wort,Ausgang'length);
aus(0) <= takt0 and Ausgang(0);
aus(1) <= takt1 and Ausgang(1);
aus(2) <= takt2 and Ausgang(2);
aus(3) <= takt3 and Ausgang(3);
aus(4) <= takt4 and Ausgang(4);
aus(5) <= takt5 and Ausgang(5);
aus(6) <= takt6 and Ausgang(6);
aus(7) <= takt7 and Ausgang(7);
end Behavioral;
Code 0-41: Studentische Lösung zu "Obertonmusik".
NET "aus<0>" LOC = "P126" | IOSTANDARD = LVCMOS33 ; NET "aus<1>" LOC = "P130" | IOSTANDARD = LVCMOS33 ; NET "aus<2>" LOC = "P131" | IOSTANDARD = LVCMOS33 ; NET "aus<3>" LOC = "P132" | IOSTANDARD = LVCMOS33 ; NET "aus<4>" LOC = "P134" | IOSTANDARD = LVCMOS33 ; NET "aus<5>" LOC = "P135" | IOSTANDARD = LVCMOS33 ; NET "aus<6>" LOC = "P139" | IOSTANDARD = LVCMOS33 ; NET "aus<7>" LOC = "P140" | IOSTANDARD = LVCMOS33 ; NET "MEINECLOCK" LOC = "P56" | IOSTANDARD = LVCMOS25;
Code 0-42: Zugehörige Constraint-Datei zu "Obertonmusik".
Bild 0-10: Aufbau zu "Obertonmusik"
Übung 4 -- simulierte Sinusschwingung
PhysModFast003.zip
Obiger Processing-Sketch simuliert in Echtzeit einen linearen Feder-Dämpfer-Masse-Schwinger, dem zyklisch ein Stoß versetzt wird. Die Eigenfrequenz liegt im hörbaren Bereich und wird an einen Soundkarte geschickt. Die Zeitschrittweite der Simulation wurde darum zu 44100Hz gewählt.
Schreiben Sie anlehnend an dieses Beispiel ein VHDL-Programm, das statt einer Tabelle, eine Simulations benutzt, um eine Sinusschwingung zu erzeugen. Testen Sie aus, ob eine diskrete Lösung möglich ist.
Hier kann wieder das R/R2-Netzwerk zum Einsatz kommen.
#12 Mi 07.06.2023
Fortsetzung der Übungen von letzter Woche und...
Demonstration des R/2R-AD-Wandlers:
Bild 0-11: Test des AD-Wandlers (Abspielen einer Sinusschwingung, die als Lookup-Table gespeichert wurde.)
Vorstudie simulierte harmonische Schwingung: Verwendung von Fixed-Kommazahlen in VHDL
|
Es wird der Umstand ausgenutzt, dass beispielsweise das Ergebnis einer Multiplikation zweier Zahlen einmal mit zwei Nachkommastellen und einmal um Faktor 100 vergrößert, die gleichen dezimalen Stellen liefert. Eine Multiplikation zweier Zahlen mit fixem Komma, kann also leicht durch eine Alternative ersetzt werden, bei der die Kommastelle so verschoben ist, dass keine Nachkommastellen übring bleiben. Ein Beispiel:
1,5*3,9=5,85 15 *39 =585 ..also: 1,5*3,9 <=> ( 1,5*10 * 3,9*10 )/100
Code 0-43: Beispiel zum Vergleich zwischen Rechnungen mit Fixed-Komma und ohne Nachkommastellen.
Übung
Machen Sie sich das oben dargestellte Wissen zu Nutze, um mittels arithmetischer Operationen auf Integer-Werte eine numerische Integration mittles des Eulerverfahrens anzuwenden, das eine Sinusschwingung mit 200Hz erzeugt.
Wie im Beispiel zuvor soll der 200Hz Sinuston über ein R/2R-Netzwerk herausgeführt und hörbar gemacht werden.
Hinweise zum Vorgehen: Testen Sie das Verfahren erst mit Java/Processing, bevor Sie es in VHDL übersetzen.
Zusatzaufgabe
|
#13 Mi 14.06.2023 Probe E-Test
Wir treffen uns um 09:00Uhr im PC-Pool D.1.02
Themen
|
#14 Mi 21.06.2023
Themen
|
1. Quiz
|
2. Musterlösung zur simulierten Sinusschwingung:
int x=1000;
int v=0;
int[] xx = new int[1000];
public void setup()
{
for(int i=0;i<xx.length;i++)
{
xx[i]=x;
x+=v;
//v-=x/221;
v=v-(x*37)/8192;
}
int min=Integer.MAX_VALUE;
int max=-Integer.MAX_VALUE;
int perioden = 0;
int xalt=-1;
for(int i=0;i<10000000;i++)
{
x+=v;
//v-=x/221;
v=v-(x*37)/8192;
if(x>max) max = x;
if(x<min) min = x;
if(xalt<0 && x>=0)
{
xalt=1;
perioden++;
}
else if(x<0)
{
xalt=-1;
}
}
int pschritte = 10000000/perioden;
println("min="+min+" max="+max+" Schritte pro Periode:"+pschritte);
size(1100,700);
}
public void draw()
{
for(int i=0;i<xx.length-1;i++)
{
line(i,xx[i]/4+300,i+1,xx[i+1]/4+300);
}
}
Code 0-44: Processing-Sketch für Vorversuche.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity SinusSim is
Port ( MEINECLOCK : in STD_LOGIC;
MEINELED : out STD_LOGIC);
end SinusSim;
-- 6000 Clock-Schritte ergeben 1000 Simulationsschritte pro zwei Sekunden.
architecture Behavioral of SinusSim is
signal zaehler : integer range -32768 to 32767 := 0;
signal logikpegel : std_logic := '0';
signal x : integer range -32768 to 32767 := 1000;
signal v : integer range -32768 to 32767 := 0;
signal h : integer range -32768 to 32767 := 0;
signal hh : integer range -32768 to 32767 := 0;
begin
process begin
wait until rising_edge(MEINECLOCK);
if (zaehler<5999) then
zaehler <= zaehler+1;
if (zaehler<h) then
logikpegel <= '1';
else
logikpegel <= '0';
end if;
else
zaehler <= 0;
x<=x+v;
--v<=v-x/221;
hh<=x*37;
hh<=hh/8192;
v<=v-hh;
--h<=x*3;
--h<=h+3000;
--Fallback: linear steigende PWM-Breite:
h<=h+1;
if (h>5999) then
h<=0;
end if;
end if;
end process;
MEINELED <= logikpegel;
end Behavioral;
Code 0-45: VHDL-Code zur simulierten Sinusschwingung.
## 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-46: Constraints.
3. Übung zur Echtzeit-Simulation einer Sinusschwingung
|
4. Übung zur Umsetzung einer Servosteuerung
|
69_FPGA/10_Servo
Hinweis: Verwenden Sie einen Miniservo und versorgen ihn über die USB-Spannung des FPGAs.