Guten Abend Community
Ich suche für ein kleiners Projekt, einen fertigen Modulo-Algorithmus (
x mod 7) für unsigned Variablen mit max. 8 bit.
vielleicht hat ja jemand sowas schon mal geschrieben und würde es mit
mir Teilen?
Gruß Steven
Ganz so einfach, das man einfach den Typ wechselt, ist es leider nicht.
Das Maximum für die Mod-Berechnung in VHDL (ISE) liegt leider bei 2
sprich ( x mod 2)
Sprich man braucht einen Divisionsalgorithmus + die Ausgabe des Restes
Steven H. schrieb:> Ganz so einfach, das man einfach den Typ wechselt, ist es leider nicht.> Das Maximum für die Mod-Berechnung in VHDL (ISE) liegt leider bei 2> sprich ( x mod 2)
Mitnichten. Wenn man %7 hinschreibt und alles in 1 Takt erledigt haben
will, dann macht der Synthesizer einen kombinatorischen Divider.
Siehe z.B. den Beitrag "Re: Rechnen mit unsigned vs. signed und einer division"> Sprich man braucht einen Divisionsalgorithmus + die Ausgabe des Restes
Man kann auch "hinreichend genau" auf eine Multiplikation mit
anschließendem "Abschneiden" (aka Division duch Zweierpotenz)
ausweichen:
mod7 <= x-7*((x*9362)/65536);
Der Trick dabei 9362/65536 = 1/7,000213
Burkhard K. schrieb:> Etwas mehr geht schon, tatsächlich kann der XST für Series 7 u.a.:> rem - Supported if the right operand is a constant power of 2> mod - Supported if the right operand is a constant power of 2
Das geht schon immer. Es ist ja nur ein "Weglassen" der unteren Bits.
Im Prinzip funktioniert das aber nur bei Unsigned Vektoren korrekt...
Hagen R. schrieb:> @Yalu: geht dein beschriebener Trick so zu generalisieren?> y = x mod (2^k-1)>> wiederhole> x = (x / 2^k) + (x and (2^k-1))> solange bis x <= (2^k-1)>> wenn x = (2^k-1) dann y = 0 ansonsten y = x.
Ja, genau.
Genauso wie der Neunerrest im Dezimalsystem über die Quersumme berechnet
werden kann (https://de.wikipedia.org/wiki/Neunerrest), ist dies für den
Siebenerrest im Oktalsystem oder allgemein für den Rest modulo n-1 im
Stellenwertsystem zur Basis n möglich. Man macht sich dabei zunutze,
dass
n^i mod (n-1) = 1 für alle i ∈ ℕ
Ist n eine Zweierpotenz (n=2^k), dann wird die Zerlegung einer im
Binärsystem gegebenen Zahl in Ziffern besonders einfach, da dann jede
Ziffer einer Gruppe von k Bits entspricht. Man kann damit leicht die
Reste modulo 3, 7, 15 usw. berechnen.
Den Algorithmus für das Ganze hast du ja oben in sehr eleganter Form
hingeschrieben.
> Wobei natürlich die Division von x / 2^k ein simpler rechts Shift ist.
Auf dem FPGA muss nicht einmal unbedingt geshiftet werden. Da die Anzahl
der benötigten Additionen bei gegebener Bitbreite der zu bearbeitenden
Zahl nach oben beschränkt ist, kann die Berechnung rein kombinatorisch
(also ohne Flipflops) erfolgen.
> Kennst du noch andere solche Tricks, zb. modulo (x^y +- z)
Modulo n+1 in der Basis n geht auch relativ leicht, nämlich über die
alternierende Quersumme. Damit können mit einem ähnlichen Verfahren die
Reste modulo 2^k+1 (5, 9, 17 usw.) berechnet werden. Hier macht man sich
zunutze, dass
n^i mod (n+1) = (-1)^i für alle i ∈ ℕ
Möglicherweise wird die Umsetzung für ein FPGA wegen der negativen
Zwischenergebnisse etwas kniffliger.
Yalu X. schrieb:> Möglicherweise wird die Umsetzung für ein FPGA wegen der negativen> Zwischenergebnisse etwas kniffliger.
Nicht unbedingt, man müsste sie nach meinem Verständnis als unsigned
halten können, um bei den Berechnungen nicht schief zu laufen, weil sie
"restklassenmäßig" dann stimmen müssten.
Lothar M. schrieb:> Man kann auch "hinreichend genau" auf eine Multiplikation mit> anschließendem "Abschneiden" (aka Division duch Zweierpotenz)> ausweichen:> mod7 <= x-7*((x*9362)/65536);>> Der Trick dabei 9362/65536 = 1/7,000213
Auch das lässt sich generalisieren:
https://homepage.divms.uiowa.edu/~jones/bcd/divide.html (oder, wenn man
lieber Bücher liest, "Hacker's Delight, 2nd Edition"): Multiplikationen
mit dem binären Kehrwert, die man anschliessend in Shifts und Additionen
zerlegt:
1
libraryieee;
2
useieee.std_logic_1164.all;
3
useieee.numeric_std.all;
4
5
entitytb2is
6
endtb2;
7
8
architecturertloftb2is
9
begin
10
p_initial:process
11
variablen:unsigned(7downto0);
12
variableu_div:unsigned(7downto0);
13
variableu_mod:unsigned(7downto0);
14
begin
15
foriin0to255loop
16
n:=to_unsigned(i,8);
17
u_div:=shift_right(n,1)+shift_right(n,4);
18
u_div:=shift_right(u_div+shift_right(u_div,6),2);
19
u_mod:=n-(shift_left(u_div,3)-u_div);
20
u_div:=u_div+shift_right(u_mod+1,3);
21
ifu_mod>6then
22
u_mod:=u_mod-7;
23
endif;
24
report"i = "&integer'image(i)&" div 7 = "&integer'image(to_integer(u_div))&","&
25
" mod 7 = "&integer'image(to_integer(u_mod))severitynote;
26
endloop;
27
wait;
28
endprocessp_initial;
29
endarchitecturertl;
u_div ist der Quotient und u_mod der Rest. Der bei der angenäherten
Division entstehende Fehler lässt sich leicht korrigieren.
Markus F. schrieb:> Multiplikationen mit dem binären Kehrwert, die man anschliessend in> Shifts und Additionen zerlegt
Es stimmt natürlich, dass man Multiplikationen durch Additionen und
Subtraktionen von Zweierpotenzen ersetzen kann. Aber wenn noch ein
Multipizierer übrig ist (und das ist er typischerweise bei denen, die
solche Fragen stellen... ;-) dann muss man die Multiplikation nicht
weiter optimieren.