Implementierung einer diskretisierten sigmoiden Funktion als Lookup-Table auf einem FPGA
(EN google-translate)
(PL google-translate)
Aufgabe
68_nexys/06_Sfunction_diskret -- Musterlösung liefert Lookup-Table als Java-Methode (Quelltext).
Übung
Nehmen Sie obige Musterlösung als Ausgangspunkt und schreiben einen VHDL-Quelltextgenerator, der die diskretisierte sigmoide Funktion als Lookup-Table implementiert.
Über die 8 Schalter des nexys video sollen die Zahlen +/-127 eingegeben werden können. Sie dienen als Input für den Lookup-Table. Die 8 LEDs sollen den Ausgabewert des Lookup-Tables anzeigen.
Ergänzen Sie ein Java-Programm, das alle möglichen Input-Output-Paare so darstellt, wie sie auf dem nexys video eingestellt, bzw. angezeigt werden, damit das Ergebnis auf dem nexys leichter auf Korrektheit überprüft werden kann.
Für die Realisierung eines Lookup-Tables mit VHDL unter Verwendung von Integer-Zahlen, orientieren Sie sich bitte wegen der VHDL-Syntax an folgendem Beispiel:
69_FPGA/99_day_by_day_SoSe2018 -- siehe ab "Machbarkeitsstudie: Neuronales Netz mit fünf Neuronen auf Integer-Basis in FPGA: "
TIP: Arbeiten Sie vielleicht nicht gleich an dem "grossen" Projekt, sondern zergliedern Sie es in aufeinander aufbauende Teilaufgaben und machen mehrere VHDL-Projekte draus:
|
Teillösung 1: 8 Schalter auf 8 LEDs 1:1 abbilden
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity schalter2led is
Port ( ALLELEDS : out STD_LOGIC_VECTOR (7 downto 0);
ALLESCHALTER : in STD_LOGIC_VECTOR (7 downto 0));
end schalter2led;
architecture Behavioral of schalter2led is
begin
ALLELEDS(7) <= ALLESCHALTER(7);
ALLELEDS(6) <= ALLESCHALTER(6);
ALLELEDS(5) <= ALLESCHALTER(5);
ALLELEDS(4) <= ALLESCHALTER(4);
ALLELEDS(3) <= ALLESCHALTER(3);
ALLELEDS(2) <= ALLESCHALTER(2);
ALLELEDS(1) <= ALLESCHALTER(1);
ALLELEDS(0) <= ALLESCHALTER(0);
end Behavioral;
Code 0-1: VHDL-Code für nexys-video zu 8 Schalter auf 8 LEDs 1:1 abbilden.
NET "ALLELEDS<7>" LOC = "Y13"; NET "ALLELEDS<6>" LOC = "W15"; NET "ALLELEDS<5>" LOC = "W16"; NET "ALLELEDS<4>" LOC = "V15"; NET "ALLELEDS<3>" LOC = "U16"; NET "ALLELEDS<2>" LOC = "T16"; NET "ALLELEDS<1>" LOC = "T15"; NET "ALLELEDS<0>" LOC = "T14"; NET "ALLELEDS<7>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<6>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<5>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<4>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<3>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<2>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<1>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<0>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<7>" LOC = "M17"; NET "ALLESCHALTER<6>" LOC = "K13"; NET "ALLESCHALTER<5>" LOC = "J16"; NET "ALLESCHALTER<4>" LOC = "H17"; NET "ALLESCHALTER<3>" LOC = "G22"; NET "ALLESCHALTER<2>" LOC = "G21"; NET "ALLESCHALTER<1>" LOC = "F21"; NET "ALLESCHALTER<0>" LOC = "E22"; NET "ALLESCHALTER<7>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<6>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<5>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<4>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<3>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<2>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<1>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<0>" IOSTANDARD = LVCMOS25;
Code 0-2: .ucf-Datei (Constraints) für nexys-video zu 8 Schalter auf 8 LEDs 1:1 abbilden.
Bild 0-1: Board nach dem Übertragen der Bitdatei und einem mit den Schaltern eingestellten Bitwort "10101100".
Musterlösung
//Diese Version erzeugt FPGA-Quelltext für
//einen Integer-Lookup-Table
public void setup()
{
double d = 1.0;
int[] alle = new int[128];
int anzBest=0; //für Kontrollausgabe
int durchlauf = 0;
while(true)
{
for(int u=-32768;u<=32767;u++)
{
double out = 128.0 / ( 1.0 + Math.exp(-d*(double)u) );
int oint = (int)Math.floor(out); //Nachkommstellen abschneiden, d.h. für
// >0 nach unten, für <0 nach oben hin runden.
// gewährleistet einen Wertebereich von maximal +/-127 INKLUSIVE.
if(oint>127) oint=127;
if(oint<0) oint=0;
alle[oint]=1;
}
int anz = 0;
for(int k=0;k<alle.length;k++)
anz+=alle[k];
if(anz>anzBest)
{
println("Nr."+durchlauf+": d="+d+" "+anz+"/"+alle.length);
anzBest = anz;
}
if(anz==alle.length-1)
break;
for(int k=0;k<alle.length;k++)
alle[k]=0;
d*=0.999;
durchlauf++;
}
int ovor = -1;
String umdrehen = "";
println("with SUMME select SIGMO <=");
int[][] arr = new int[127][3];
arr[0][0]=-32768;
arr[0][1]=0;
int inx=1;
for(int u=-32768;u<=32767;u++)
{
double out = 128.0 / ( 1.0 + Math.exp(-d*(double)u) );
int oint = (int)Math.floor(out); //Nachkommstellen abschneiden, d.h. für
// >0 nach unten, für <0 nach oben hin runden.
// gewährleistet einen Wertebereich von maximal +/-127 INKLUSIVE.
if(oint>127) oint=127;
if(oint<0) oint=0;
//alle[oint]=1;
if(oint!=ovor)
{
if(u==143)//"Hack!"
{
//umdrehen = " if(u>="+u+") out="+oint+";\n"+umdrehen;
arr[inx][0]=u;
arr[inx][1]=oint;
inx++;
}
else if(u>=-142)
{
arr[inx][0]=u;
arr[inx][1]=oint;
inx++;
//umdrehen = " else if(u>="+u+") out="+oint+";\n"+umdrehen;
}
ovor=oint;
}
}
for(int i=0;i<arr.length;i++)
{
//"bis-Wert:"
if(i<arr.length-1)
arr[i][2] = arr[i+1][0]-1;
else
arr[i][2] = 32767;
println("sigmo="+arr[i][1]+" wenn "+arr[i][0]+" bis "+arr[i][2]);
}
println("FPGA-Code:");
println("");
println("with SUMME select SIGMO <=");
for(int i=0;i<arr.length-1;i++)
{
if(arr[i][0]==arr[i][2])
{
println(" "+arr[i][1]+" when "+arr[i][0]+",");
}
else
{
println(" "+arr[i][1]+" when "+arr[i][0]+" to "+arr[i][2]+",");
}
}
println(" 127 when others;");
}
Code 0-3: Java-Code-Generator für VHDL-FPGA-Lookup-Table, der die diskrete sigmoide Funktion repräsentiert
with SUMME select SIGMO <=
0 when -32768 to -143,
1 when -142 to -122,
2 when -121 to -110,
3 when -109 to -102,
4 when -101 to -95,
5 when -94 to -89,
6 when -88 to -84,
7 when -83 to -80,
8 when -79 to -76,
9 when -75 to -73,
10 when -72 to -70,
11 when -69 to -67,
12 when -66 to -65,
13 when -64 to -62,
14 when -61 to -60,
15 when -59 to -58,
16 when -57 to -56,
17 when -55 to -54,
18 when -53 to -52,
19 when -51 to -50,
20 when -49 to -48,
21 when -47,
22 when -46 to -45,
23 when -44,
24 when -43 to -42,
25 when -41,
26 when -40 to -39,
27 when -38,
28 when -37,
29 when -36 to -35,
30 when -34,
31 when -33,
32 when -32,
33 when -31 to -30,
34 when -29,
35 when -28,
36 when -27,
37 when -26,
38 when -25,
39 when -24,
40 when -23,
41 when -22,
42 when -21,
43 when -20,
44 when -19,
45 when -18,
46 when -17,
47 when -16,
48 when -15,
49 when -14,
50 when -13,
51 when -12,
52 when -11,
53 when -10,
54 when -9,
55 when -8,
56 when -7,
57 when -6,
58 when -5,
59 when -4,
60 when -3,
61 when -2,
62 when -1,
64 when 0,
65 when 1,
66 when 2,
67 when 3,
68 when 4,
69 when 5,
70 when 6,
71 when 7,
72 when 8,
73 when 9,
74 when 10,
75 when 11,
76 when 12,
77 when 13,
78 when 14,
79 when 15,
80 when 16,
81 when 17,
82 when 18,
83 when 19,
84 when 20,
85 when 21,
86 when 22,
87 when 23,
88 when 24,
89 when 25,
90 when 26,
91 when 27,
92 when 28,
93 when 29,
94 when 30 to 31,
95 when 32,
96 when 33,
97 when 34,
98 when 35 to 36,
99 when 37,
100 when 38,
101 when 39 to 40,
102 when 41,
103 when 42 to 43,
104 when 44,
105 when 45 to 46,
106 when 47,
107 when 48 to 49,
108 when 50 to 51,
109 when 52 to 53,
110 when 54 to 55,
111 when 56 to 57,
112 when 58 to 59,
113 when 60 to 61,
114 when 62 to 64,
115 when 65 to 66,
116 when 67 to 69,
117 when 70 to 72,
118 when 73 to 75,
119 when 76 to 79,
120 when 80 to 83,
121 when 84 to 88,
122 when 89 to 94,
123 when 95 to 101,
124 when 102 to 109,
125 when 110 to 121,
126 when 122 to 142,
127 when others;
Code 0-4: Erzeugter Lookup-Table
Umsetzung als VHDL-Programm
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
--use IEEE.NUMERIC_STD.ALL;
use IEEE.std_logic_arith.ALL;
--Wichtiges Ziel: wg. geplanter Parallelisierung keine Prozesse benutzen!
-- ... aber einen bequemen Weg wählen, der nicht unbedingt der effizienteste ist.
entity sigmodiskret is
Port ( ALLESCHALTER : in STD_LOGIC_VECTOR (7 downto 0);
ALLELEDS : out STD_LOGIC_VECTOR (7 downto 0));
end sigmodiskret;
architecture Behavioral of sigmodiskret is
--Output des diskreten Neurons:
signal SIGMO : integer range 0 to 127 := 0; -- 7Bit Variable
-- u, also gewichtete Summe aller Eingangssignale am Eingang, d.h. Argument u der sigm. Fkt.
signal SUMME : integer range -32768 to 32767 := 0; -- 16Bit Variable
-- Hilfsvariablen für Zwischenergebnisse bei der Umwandlung, wenn das
-- höchste Bit als Vorzeichen verwendet wird, statt Zweierkomplement zu bilden:
signal XXX : integer range -32768 to 32767 := 0; -- 16Bit Variable
signal YYY : integer range -32768 to 32767 := 0; -- 16Bit Variable
begin
-- erste Signalumwandlung:
-- Bit 0..6 von ALLESCHALTER als positive Integerzahl von 0..127 in XXX ablegen:
XXX <= conv_integer(unsigned(ALLESCHALTER(6 downto 0)));
-- in YYY Bit 7 aus ALLESCHALTER als Integer-Zahl ablegen:
YYY <= conv_integer(unsigned(ALLESCHALTER(7 downto 7)));
-- SUMME = (1-2*YYY)*XXX, so wird aus XXX -XXX wenn Bit 7 gesetzt ist.
SUMME <= (1-2*YYY)*XXX;
-- SUMME ist Input für den Lookup-Table:
with SUMME select SIGMO <=
0 when -32768 to -143,
1 when -142 to -122,
2 when -121 to -110,
3 when -109 to -102,
4 when -101 to -95,
5 when -94 to -89,
6 when -88 to -84,
7 when -83 to -80,
8 when -79 to -76,
9 when -75 to -73,
10 when -72 to -70,
11 when -69 to -67,
12 when -66 to -65,
13 when -64 to -62,
14 when -61 to -60,
15 when -59 to -58,
16 when -57 to -56,
17 when -55 to -54,
18 when -53 to -52,
19 when -51 to -50,
20 when -49 to -48,
21 when -47,
22 when -46 to -45,
23 when -44,
24 when -43 to -42,
25 when -41,
26 when -40 to -39,
27 when -38,
28 when -37,
29 when -36 to -35,
30 when -34,
31 when -33,
32 when -32,
33 when -31 to -30,
34 when -29,
35 when -28,
36 when -27,
37 when -26,
38 when -25,
39 when -24,
40 when -23,
41 when -22,
42 when -21,
43 when -20,
44 when -19,
45 when -18,
46 when -17,
47 when -16,
48 when -15,
49 when -14,
50 when -13,
51 when -12,
52 when -11,
53 when -10,
54 when -9,
55 when -8,
56 when -7,
57 when -6,
58 when -5,
59 when -4,
60 when -3,
61 when -2,
62 when -1,
64 when 0,
65 when 1,
66 when 2,
67 when 3,
68 when 4,
69 when 5,
70 when 6,
71 when 7,
72 when 8,
73 when 9,
74 when 10,
75 when 11,
76 when 12,
77 when 13,
78 when 14,
79 when 15,
80 when 16,
81 when 17,
82 when 18,
83 when 19,
84 when 20,
85 when 21,
86 when 22,
87 when 23,
88 when 24,
89 when 25,
90 when 26,
91 when 27,
92 when 28,
93 when 29,
94 when 30 to 31,
95 when 32,
96 when 33,
97 when 34,
98 when 35 to 36,
99 when 37,
100 when 38,
101 when 39 to 40,
102 when 41,
103 when 42 to 43,
104 when 44,
105 when 45 to 46,
106 when 47,
107 when 48 to 49,
108 when 50 to 51,
109 when 52 to 53,
110 when 54 to 55,
111 when 56 to 57,
112 when 58 to 59,
113 when 60 to 61,
114 when 62 to 64,
115 when 65 to 66,
116 when 67 to 69,
117 when 70 to 72,
118 when 73 to 75,
119 when 76 to 79,
120 when 80 to 83,
121 when 84 to 88,
122 when 89 to 94,
123 when 95 to 101,
124 when 102 to 109,
125 when 110 to 121,
126 when 122 to 142,
127 when others;
-- SIGMO auf Bit 0..6 der LED-Leiste übertragen:
ALLELEDS <= conv_std_logic_vector(SIGMO,ALLELEDS'length);
end Behavioral;
Code 0-5: nexys_neuron_002 -- VHDL-Code, um die diskrete sigmoide Funktion am nexys video testen zu können.
NET "ALLELEDS<7>" LOC = "Y13"; NET "ALLELEDS<6>" LOC = "W15"; NET "ALLELEDS<5>" LOC = "W16"; NET "ALLELEDS<4>" LOC = "V15"; NET "ALLELEDS<3>" LOC = "U16"; NET "ALLELEDS<2>" LOC = "T16"; NET "ALLELEDS<1>" LOC = "T15"; NET "ALLELEDS<0>" LOC = "T14"; NET "ALLELEDS<7>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<6>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<5>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<4>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<3>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<2>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<1>" IOSTANDARD = LVCMOS25; NET "ALLELEDS<0>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<7>" LOC = "M17"; NET "ALLESCHALTER<6>" LOC = "K13"; NET "ALLESCHALTER<5>" LOC = "J16"; NET "ALLESCHALTER<4>" LOC = "H17"; NET "ALLESCHALTER<3>" LOC = "G22"; NET "ALLESCHALTER<2>" LOC = "G21"; NET "ALLESCHALTER<1>" LOC = "F21"; NET "ALLESCHALTER<0>" LOC = "E22"; NET "ALLESCHALTER<7>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<6>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<5>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<4>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<3>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<2>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<1>" IOSTANDARD = LVCMOS25; NET "ALLESCHALTER<0>" IOSTANDARD = LVCMOS25;
Code 0-6: nexys_neuron_002 -- .ucf-Datei
nexys_neuron_002.zip -- ise14.7-Projekt zu obigem Quelltext.
Bild 0-2: Funktionsnachweis (eine Stichprobe von vielen): Input u ist -64 (11000000, Bit 7 = negatives Vorzeichen). Output o ist (richtigerweise) 13 (00001101).
studpdf.zip -- zwei studentische Lösungsvarianten, behelfsweise als pdf.