kramann.info
© Guido Kramann

Login: Passwort:










kramann.info
© Guido Kramann

Login: Passwort:




Echtzeitanwendungen auf Basis von HDL 6MT 4EMO im Sommersemester 2023

(EN google-translate)

(PL google-translate)

  • Angaben zu Veranstaltungszeiten und Räumen dieser Lehrveranstaltung finden Sie auf der Startseite kramann.info dieses Portals.
  • Hier bei "day by day" werden chronologisch im Verlauf des Semesters die behandelten Inhalte vermerkt.
  • Meistens werden Links innerhalb von kramann.info angegeben, wo der jeweils behandelte Stoff dargestellt wird.
  • Zur Orientierung finden Sie auf kramann.info auch noch das "day by day" der gleichen Lehrveranstaltung vom vorangehenden Jahr.
  • Die Prüfung in diesem Fach ist eine Klausur in elektronischer Form (E-Test)
  • Die folgenden vorab angegebenen Links stellen Quellen dar, aus denen die hier behandelten Inhalte genommen werden:

kramann.info -- Angaben zu Veranstaltungszeiten und Räumen dieser Lehrveranstaltung
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
  1. Wie hat sich geschichtlich das Erfordernis und die Realisation von FPGAs und HDL ergeben?
  2. Wo stehen FPGA-Bausteine im Vergleich mit diskret aufgebauter digitaler Hardware und Mikrocontrollern?
  • Seit den 50er Jahren bis in die 70er Jahre hinein wurden regelungstechnische Aufgaben vornehmlich mit Hilfe diskreter elektronischer Schaltungen bewältigt.
  • Computer stellten damals keine Alternative zu der diskreten Technik dar.
  • Nachteil diskreter Schaltungen: Hoher Aufwand bei Verbesserungen wie der Entwurf einer neuen Platine, das Bereithalten tausender Datenblätter, Impedanzanpassungen.
  • In den 80ern beauftragte die DARPA die Entwicklung eines Chips, der diskrete Logik-Schaltungen abbilden kann.
  • Nicht die abgebildete Verdrahtung sollte bei der Verwendung beschrieben werden, sondern die Logik, die abgebildet werden soll.
  • Ergebnis der Entwicklung waren FPGA-Chips (FPGA=Field Programmable Gate Array) in Kombination mit der Programmiersprache VHDL (VHDL=VHSIC Hardware Description Language).
  • FPGA-Bausteine machen nur Sinn in Zusammenhang mit einem Compiler, der VHDL in ein Image für den FPGA umsetzt.
  • Eingebettete Computer laufen mittlerweile FPGAs immer mehr den Rang ab.
  • Verbleibende Anwendungsgebiete sind solche mit hohen Anforderungen an die Parallelisierung, wie bei fertig belernten Neuronalen Netzen.
  • Besonders häufig finden sich Anwendungen mit FPGAs im Bereich der Raumfahrt, um Raketenmotoren zu regeln:
Motivation / Diskussion ... Grey Walter's tortoises 1949
Schaltplan zu Grey Walter's tortoises.
Artikel: FPGA-Based Predictive Control System for SpacecraftRendezvous in Elliptical Orbits
Werbefilm von Xilinx: Introducing the Industry's First 20nm Space-Grade FPGA

Wie ist ein FPGA aufgebaut?

FPGAs auf Wikipedia -- https://de.wikipedia.org/wiki/Field_Programmable_Gate_Array
Was ist ein FPGA? -- 69_FPGA/01_Einfuehrung

Vorberetung der ersten Übung mit einem FPGA-Board

  1. Verwendung der (Linux-) Xubuntu-Distribution im PC-Pool IWZ140
  2. Start der Entwicklungs-IDE von Xilinx.
  3. Einrichten eines Projektes
  4. Getting started mit einem Programm, das eine LED blinken läßt
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:

  1. Verwendung der (Linux-) Xubuntu-Distribution im PC-Pool IWZ140
  2. Start der Entwicklungs-IDE von Xilinx.
  3. Einrichten eines Projektes
  4. Getting started mit einem Programm, das eine LED blinken läßt

#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
1. Rückblick auf die praktische Verwendung der Xilinx IDE:
  • Wie wird die Entwicklungsumgebung gestartet?
  • Wie wird ein neues Projekt angelegt? (.vhd und .ucf-Datei)
  • Wozu dient die .ucf-Datei?
  • Was ist das besondere an der .vhd-Datei?
  • Was unterscheidet ein VHDL-Programm von beispielsweise einem C-Programm?
  • Können die Zeilen in einem VHDL-Programm (teilweise) vertauscht werden?
  • Wie aktiviert man die USB-Spannungsversorgung für das FPGA-Board?
  • Warum sollte man unbedingt den FPGA erst löschen, bevor man ein neues Programm flasht?
  • Wie wird die .bit-Datei erstellt und auf den FPGA übertragen?
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
  • Nachfolgend finden Sie ein UND-Gatter umgesetzt:
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:

  • B=0, A=0 => 1Hz
  • B=0, A=1 => 2Hz
  • B=1, A=0 => 3Hz
  • B=1, A=1 => 4Hz

#3 Mi 05.04.2023

Themen
  1. Besprechung von Lösungen zur Zusatzaufgabe ÜBUNG3: Konfiguration der Blink-LED über digitale Eingänge
  2. Weitere Elemente in VHDL: Umgang mit Bitwörtern, Lookup-Tabellen, UNISIM-Library
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:

  • B=0, A=0 => 1Hz
  • B=0, A=1 => 2Hz
  • B=1, A=0 => 3Hz
  • B=1, A=1 => 4Hz
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
  • Schreiben Sie ein Programm und bauen Sie eine Schaltung auf, mit denen ein Lauflicht realisiert wird:
  • Vier LEDs sollen der Reihe nach einzeln aufleuchten.
  • Der Vorgang soll sich zyklisch wiederholen.
  • Nehmen Sie nachfolgende Constraint-Datei als Grundlage.
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
  2. D-Flip-Flop aus der Unisim Library
  3. Lookup Table (LUT) aus der Unisim Library
  4. Vorarbeiten Softcomputing: Verbinden eines Mikrocontrollers mit dem FPGA

1. Synchronisieren: D-Flip-Flop

69_FPGA/08_Synchronisieren

Neu: Verwendung selbst geschriebener Module.


Übung 1
  • Setzen Sie das D-Flip-Flop in dem Kapitel 8 um.
  • Setzen Sie dann das 4-fach Schieberegister in Kapitel 9 um:
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
  • Setzen Sie das D-Flip-Flop in dem Kapitel 12 auf Basis der Unisim Library um.

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
  • Setzen Sie den Mustervergleicher auf Grundlage von LUT4 aus Kapitel 13 um.

4. Vorarbeiten Softcomputing

  • Es soll eine Technik vermittelt werden, einen Mikrocontroller mit dem FPGA zu verbinden.
  • So können Mittel wie AD-Wandler, oder die Serielle Schnittstelle des Mikrocontrollers mit verwendet werden.

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

  • Bauen Sie das System so auf, dass die drei Bit von einem Mikrocontroller Arduino Micro überneommen werden.
  • Verwenden Sie "digitaler Input" als logisch 1 und "digitaler Ausgang mit Ground" als logisch 0, auf der Mikrocontrollerseite um den FPGA zu schützen.

#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
  • Auf PORTB des Arduino Micro soll zyklisch im 0,25 Sekundentakt von 0 bis 255 hochgezählt werden
  • Gleichzeitig soll die aktuelle Zahl dezimal im seriellen Monitor angezeigt werden.
  • Verbinden Sie PB0 bis PB7 des Arduino über 1 Kiloohm Widerstände mit IN0 bis IN8 beim FPGA, als mit P10, P12, P29, P31, P36, P38, P41, P47.
  • Das in den FPGA herein kommende 8-Bit-Wort soll mit 8 LEDs mit 2,2 Kiloohm Vorwiderstand beim FPGA bei IO0..IO7 angezeigt werden.

LÖSUNGSHINWEISE:


Das Arduino-Programm ohne seriellen Monitor wird gemeinsam mit dem Dozenten entwickelt.


  • Das FPGA-Programm kommt ohne Prozess aus.
  • Arbeiten Sie wo immer es geht beim FPGA mit dem Datentyp STD_LOGIC_VECTOR.

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
  • Belassen Sie die bisherige Verschaltung so, wie sie ist.
  • Die digitale LED-Anzeige mit 4 Digits LTC-2723WC wird wie nachfolgend zu sehen mit dem FPGA verschaltet:

Verwenden Sie 470 Ohm als Vorwiderstände, nicht 2200 Ohm wie angegeben!


69_FPGA/22_Uebung -- Siehe Bild 0-1.
https://www.ret.hu/media/product/14125/472479/LTC-2623WC.pdf -- Datenblatt von LTC-2723WC.

ACHTUNG: Die Digits haben eine gemeinsame Kathode!:


  • Schreiben Sie ein Programm, bei dem Digit 1 zyklisch die Ziffern von von 0 bis 9 anzeigt.
  • Sehen Sie die Möglichkeit vor, zwischen Digit 1 und den anderen vier Digits durch eine kleine Programmänderung umschalten zu können.
Stufe 3
  • Wie Stufe 2, jedoch soll der Zählvorgang von 0 bis 9 auf den Digits 1 bis 3 scheinbar gleichzeitig angezeigt werden.
  • LÖSUNGSHINWEISE:
  • Das funktioniert durch sehr schnellen Wechsel derjenigen gemeinsamen Kathode, die jeweils ein Digit versorgt.
Stufe 4
  • Nur die kleinste Ziffer des vom Mikrocontroller beim FPGA ankommenden 8-Bit-Wortes soll auf Digit 3 angezeigt werden.
  • LÖSUNGSHINWEISE:
  • Eine Modulo-Division mit 10 kann die untere Ziffer aus der Integer-Zahl extrahieren.
  • Die Umwandlung der Ziffer (Integerzahl von 0 bis 9) kann über einen Lookup-Table einem Bitmuster bei dem Digit zugewiesen werden.
  • Wenn das Umschalten zwischen den Digits zu schnell geht, ist nichts zu sehen! (Geht es zu langsam, so flackert die Anzeige)
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
  • Schließlich soll das erarbeitete Wissen zu einer Gesamtlösung vereintwerden und das 8-Bit-Wort dezimal beim FPGA angezeigt werden.

LÖSUNGSHINWEISE:

  • Um an die einzelnen Ziffern heranzukommen, kann die Gesamtzahl mehrfach durch 10 geteilt werden.
  • Als die bessere Lösung gilt diejenige, die mit weniger Programmteilen im Process-Bereich auskommt.

#6 Mi 26.04.2023


Beginn ab jetzt immer 09:00 Uhr (ohne Pause bis 12:00Uhr)


Softcomputing auf einem FPGA

Themen:

  1. KI-Techniken resp. Softcomputing
  2. Software-In-the-Loop (SIL) -Ansatz
  3. Java/Processing
  4. Kommunikationsschnittstellen
  5. Code-Generator
  6. Beispiel
  7. Übung

0. Was bedeutet "selbst lernendes System"? ...

...ein Beispiel zur Motivation: https://www.youtube.com/watch?v=V1eYniJ0Rnk
Reinforced Learning: https://www.youtube.com/watch?v=SX08NT55YhA

1. KI-Techniken resp. Softcomputing

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

  1. Neuronale Netze (Lerntechniken: Backpropagation / Deep Learning, Lermethoden: überwachtes, bestärkendes, unüberwachtes Lernen)
  2. Fuzzy-Logik und Fuzzy-Regler (Quantifizierung von Menschen formulierter Regeln)
  3. Evolutionäre Algorithmen (Optimierer, der nach dem Prinzip der Evolution arbeitet)
  4. Selbst lernende Karten (SOM, insb. Kohonen-Netze zur Klassifizierung von Daten durch unüberwachtes Lernen, wird hier nicht besprochen)
  5. Künstliche Immunsysteme (zur Mustererkennung, wird hier nicht besprochen.)
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

Angestrebtes SIL-Konzept.

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

  • Zwei Werte [0..15] als Input für den Regler müssen seriell zusammengefasst als ein Byte vom PC gesendet und beim Arduino empfangen werden.
  • Die zwei Werte sollen zusammengefasst als ein Byte auf den FPGA übertragen werden.
  • Der FPGA schickt ein Byte als Antwort parallel an den Arduino zurück.
  • Das Byte wird dann seriell als Zahl [0..255] an den PC übertragen.

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.

Screenshot des Sketches FPGA003_simfuzzy.

Bild 0-2: Screenshot des Sketches FPGA003_simfuzzy.

Simpler Fuzzy-Regler für das System.

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

  1. Testen Sie Das Kommunikationsbeispiel zwischen PC und Arduino.
  2. Die Anfrage z=x*y soll nun vom Arduino an den FPGA weitergeleitet werden und dort gelöst und via Arduino an den PC zurück gehen.
  3. 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.
  4. Schreiben Sie ein FPGA-Programm, das die Aufgabe von 3. übernimmt.

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

  1. Einführung Neuronale Netze und Evolutionäre Algorithmen
  2. Wiederholung zu Fuzzy-Reglern
  3. 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.
  4. Gemeinsam: Ansatz zu Code-Generator für Fuzzy-Regler mit VHDL
  5. Nachtrag zu 4-fach-digitale 7-Segment LED Anzeige: Gemeinsame Analyse von "Code 0-16: VHDL der Musterlösung zur Stoppuhr".
  6. Fortsetzung der Übung von letzter Woche, wenn fertig, Fortsetzung von Übung von vorletzter Woche.
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:

https://stackoverflow.com/questions/35441718/use-of-conv-integer-in-vhdl
  • Wenn Sie eine der Übungen fertig haben, macht es Sinn die Schaltung für nächste Woche aufgebaut zu lassen und sich für die weitere Arbeit ein neues weiteres FPGA-Board zu holen.
  • Mailen Sie das Ergebnis-Projekt dem Dozenten gerne als .zip-File zu, gerne auch mit Foto / Video des Aufbaus.
  • Wenn Sie ganz fertig sind, helfen Sie gerne den anderen Studierenden.
  • Arbeiten Sie ansonsten aber am besten in ihrem eigenen Tempo und versuchen eine eigenständige Lösung zu finden.
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
  2. Musterlösung und/oder studentische Lösung zu Fuzzy-Lookup-Table auf dem FPGA
  3. Diskretisiertes Neuronales auf einem FPGA
  4. Übungsaufgaben (ev. zurückstellen)
1. Musterlösung und/oder studentische Lösung zu Multiplikation auf dem FPGA

Der Ablauf hier sollte sein:

  1. PC schickt zwei miteinander zu multiplizierende 4-Bit-Zahlen an den Arduino.
  2. Der Arduino leitet die Anfrage weiter an den FPGA
  3. Der FPGA führt die Multiplikation aus und leifert das Ergebnis an den Arduino.
  4. Der Arduino leitet das Ergebnis weiter an den PC.
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
Definition der Ein- und Ausgänge beim Anlegen des Projektes.

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
  • Im Wesentlichen muss die Zuordnungsliste im Arduino-Programm Code 0-22 auf einen FPGA übertragen werden.
  • Das Rahmenprogramm auf dem FPGA entspricht dem vorangehenden mit der Multiplikation.
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
  • Das zuvor als Äquivalenzgatter belernte Neuronale Netz soll diskretisiert werden, um es leichter auf einen FPGA übertragen zu können.
  • Zu beachten ist, dass multiplizierte Werte von n Byte Länge maximal einen Wert von 2n ergeben: 8bit: 255*255 ergibt 16bit: 65025, entspricht 16bit, maximal: 2^16-1=65535.
  • Zunächst wird alles in Processing getestet, dann auf dem Arduino, zuletzt auf dem FPGA.
3a) Vorversuch mit Processing
  • Nachfolgendes Processing-Programm entspricht der diskretisierten Version des Neuronalen Netzes.
  • Es wird auch unvollständiger FPGA-Code ausgegeben, der dem Netz entspricht.
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
  • Nachfolgende Ausgabe des vorangehenden Processing-Sketches entspricht weitestgehend dem benötigten FPGA-Programm.

Ü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
Quelle: Der Spiegel vom 20.05.2023.

Bild 0-5: Quelle: Der Spiegel vom 20.05.2023.

Themen als Präsentation
  1. Besprechung einer Lösung zur Umsetzung des Neuronalen Netzes auf dem FPGA
  2. Emulation einer seriellen Schnittstelle
  3. Signalverarbeitung: Sinusgenerator
  4. Physical Modeling (Vorschau)
Themen als Übung
  1. Test des Neuronalen Netzes auf dem FPGA
  2. Test der seriellen Schnittstelle
  3. Umsetzen einer Variante der seriellen Schnittstelle: Zyklisch ein Zeichen senden, das über 8 DIP-Schalter kodiert wird.
  4. Umsetzung Sinusgenerator

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:
  • Testen Sie oben stehendes Programm.

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:

  • Um die Signalfolge zu realisieren wird eine Zeiteinheit von 0,25 Sekunden als Einheit festgelegt.
  • Wenn 1 bedeutet, dass die LED für 0,25 Sekunden leuchtet und 0, dass sie für 0,25 Sekunden aus ist, dann kann ein Zyklus der gewünschten Signalfolge folgendermaßen beschrieben werden:

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:

  • Wie muss die Vorteilung gewählt werden?
  • Wie sieht die Bitfolge aus?
  • Was kann man machen, um flexibel zwischen verschiedenen Buchstaben umschalten zu können?
Übung:
  • Testen Sie das Morseprogramm.
  • Schreiben Sie ein passendes Arduino-Programm als Schnittstelle zwischen PC und FPGA.
  • Schreiben und testen Sie das serielle Programm. Die Übertragungsrate soll 1200 Baud sein.
  • Schreiben und testen Sie das flexible serielle Programm.
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:

https://www.nomad.ee/micros/r2rdac.shtml
https://www.mikrocontroller.net/articles/DA-Wandler
Übung: Umsetzung des Sinusgenerators mit einem FPGA und einem R/2R Widerstandsnetzwerk für 8 Bit.

Arbeitsschritte:

  • Code-Generator, um eine Sinuswelle auf die Werte 0..255 zu mappen und als Folge von Konstanten Bitmustern zurück zu geben.
  • Zyklisches Abrufen der Konstanten beim FPGA und Einstellen einer passenden Vorteilung, damit das Ergebnis hörbar ist.
  • Anschluss eines Lautsprechers beim Schaltungsaufbau.

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
  2. Projekt Digitales Musikinstrument weiter bringen
  3. Physical Modeling mit invertierendem Pendel

1. Nachtrag: Test Diskretes Neuronales Netz, siehe Code 0-32 weiter oben

Testschaltung für diskretes Neuronales Netz, das als Äquivalenzgatter arbeiten soll.

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:
  1. ALLE Integer-Werte auf den Brereich -32768 bis 32767 legen. (Sonst unsigned-Werte inkompatibel zu anderen)
  2. Statt u2 <= in1*38 + in0*(-33); besser u2 <= in1*38 - in0*33; (sonst Warnings, dass Bitanzahlen der Ergebnisse nicht passen)
  3. Statt Konvertierungen Strukturen mit when.. verwenden (nicht zwingend, aber simpler)
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.

  • Das R/R2 Netzwerk wurde bei IO0..IO7 angeschlossen.
  • Es wurden 1kOhm und 2kOhm Widerstände kombiniert.
  • Vorwiderstand beim Lautsprecher: 1kOhm.
Schaltung zum Tongebeber.

Bild 0-7: Schaltung zum Tongebeber.

Schaltungsaufbau zum Tongebeber (sehr leise).

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
  • Auf IO0 bis IO7 sollen jeweils die Frequenzen 200, 400, 600, 800, 1000, 1200, 1400, 1600Hz als Rechteckschwingungen ausgegeben werden.
  • Jedoch nach einem zyklischen Muster, das einem Binärzähler entspricht:
  • erst 200Hz auf IO0, dann 400Hz auf IO1, dann beides, dann 600Hz auf IO2 usw.
  • Die Wechsel sollen im Tempo von 2Hz erfolgen.

Um das Geschehen akustisch wahrnehmbar zu machen, soll folgende Schaltung aufgebaut werden:

Schaltung für

Bild 0-9: Schaltung für "Obertonspiel".

Hinweise:

  • Es kann mehr als einen Prozess in einem VHDL-Programm geben.
  • Prozesse arbeiten "concurrent".
  • Es gibt aber auch Lösungen mit einem Prozess auszukommen.
  • Entscheiden Sie sich für eine Variante und setzen sie dann um.
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".

Aufbau zu

Bild 0-10: Aufbau zu "Obertonmusik"

https://youtu.be/ccnHqVTUeIA -- Video 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:

Demonstration des R/2R-AD-Wandlers https://youtu.be/XI7CkRk3ki4
Test des AD-Wandlers (Abspielen einer Sinusschwingung, die als Lookup-Table gespeichert wurde.)

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

  • Die Verwendung von Fließkommazahlen wäre adäquat, um eine numerische Simulation umzusetzen.
  • Jedoch sind die Algorithmen zur arithmetischen Verknüpfung von Fließkommazahlen aufwändig.
  • Eine Alternative dazu stellt die Verwendung von Fixed-Kommazahlen dar:

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
  • Um den Schaltungstechnischen Aufwand des D/A-Wandlers zu umgehen, besteht prinzipiell die Möglichkeit auf der Grundlage von PWM-Signalen ein quasi analoges Signal zu erzeugen.
  • Über eine wechselnde Dichte des High-Anteils eines gepulsten Signals sollte es möglich sein ein quasi analoges Signal zu erzeugen.
  • Soll eine Sinusschwingung hörbar gemacht werden, so muss aber gewährleistet sein, dass die Frequenz der Pulse weit über der Hörschwelle liegt.
  • Die mechanische Trägheit der Lautsprechersysteme kann dann auch für eine Filterung der hohen Frequenzen sorgen (Tiefpass).
  • Entwickeln Sie ein System, bei dem durch einen hochfrequent pulsierenden digitalen Ausgang eine hörbare Sinusschwingung erzeugt wird.

#13 Mi 14.06.2023 Probe E-Test


Wir treffen uns um 09:00Uhr im PC-Pool D.1.02


Themen

  • Probe E-Test im PC-Pool
  • Fortsetzung im Mechatronik-Labor D.2.08 Pro Person eine Bauteil-Box für die Prüfung zusammenstellen
  • Weitere praktische Übungsaufgaben

#14 Mi 21.06.2023

Themen

  1. Quiz zur Wiederholung
  2. Echtzeit-Simulation einer Sinusschwingung
  3. Übung zur Echtzeit-Simulation einer Sinusschwingung
  4. Übung zur Umsetzung einer Servosteuerung
1. Quiz
  1. Was war historisch der Beweggrund gewesen, die FPGA-Technik in Kombination mit VHDL zu entwickeln?
  2. Können mehrere Prozesse parallel auf einem FPGA laufen?
  3. Wie kann eine Bitshift-Operation mit VHDL realisiert werden?
  4. Warum ist reiner VHDL-Quelltext von einem FPGA auf einen anderen portierbar?
  5. Worin liegen Vor- und Nachteile beim Einsatz der Elemente der UNISIM-Library?
  6. Lassen sich mit einem FPGA Bussysteme realisieren?
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
  • Rekonstruieren Sie das dem Processing-Sketch zugrunde liegende dynamische System und bestimmen Sie dessen Eigenwerte und interpretieren das Ergebnis.
  • Testen Sie den Processing-Sketch
  • Testen Sie das FPGA-Programm
  • Analysieren Sie die Fehler, wenn die Simulation im FPGA-Programm aktiviert wird und versuchen Sie den VHDL-Quellcode zu korrigieren.
4. Übung zur Umsetzung einer Servosteuerung
  1. Testen und analysieren Sie den Aufbau und das Programm unter folgendem Link.
  2. Optimieren Sie das Programm dahingehend, dass nur ein Prozess zum Einsatz kommt.
69_FPGA/10_Servo

Hinweis: Verwenden Sie einen Miniservo und versorgen ihn über die USB-Spannung des FPGAs.


#15 Mi 28.06.2023 E-Test