Hallo,
Ich will ein Bild aus YCrCb in RGB Format konvertieren.
Natürlich in VHDL.Ich bin mit den Datentypen einwenig durcheinander
geraten. Der compiler spukt ein Fehler aus:
Error (10476): VHDL error at packages.vhd(28): type of identifier
"R_int" does not agree with its usage as "natural" type
Könnt ihr bitte schauen, was ich falsch gemacht habe?
Danke
Danke, ich versuche mal deine Lösung.
Ja, das muss auf Hardware auch laufen. Vllt muss ich so eine
Multiplikation lieber mit freundlichere Division ersetzen.
Zeile28 ist die, mi R_int:=......
Hi,
ah, ein alter Klassiker zum Haareraufen :-)
Also erst mal würde ich mir die Fixpoint-Arithmetik "roh" hinschreiben,
also so wie sie nachher auch umgesetzt wird (im Hinblick auf die HW).
Bei YCrCb reicht typischerweise eine signed-2.14-Darstellung (1.0 ist
0x4000).
Dann musst Du dir überlegen, wie Deine Pipeline ausschauen soll, damit
auch alles in einem Taktzyklus per Pixel durchläuft.
Zudem sollte dein FGPA ein paar DSP-Slices haben, sonst wird's ev. noch
kniffliger.
Und der Teil mit dem Haareraufen: Die richtigen Faktoren zu finden, wird
extrem "lustig", je nachdem, welcher YCrCb-'Standard' verwendet wird.
Meistens kriegt man dann auch trotz korrekter Rundung von den 24 Bit
(siehe auch unten) einen Overflow und eine Falschfarbe rein, man baut zu
guter Letzt also auch noch eine "Clamp"-Stage mit rein.
Noch zur Arithmetik: Multiplizierst du 16 (2.14 fractional) mit 8 bit,
kommt 24 bit bei raus, die addierst du in der nächsten Stage irgendwann
und schneidest schlussendlich dein Ergebnis raus, um das
8-Bit-RGB-Resultat zu erhalten. Entweder schaffst Du da die richtige
Wahl von Koeffizienten / Rundung in Abhängigkeit der umgekehrten
Konversion (RGB->YUV), oder du musst eben eine Begrenzung gegen Overflow
("Clamp") einführen. Meistens gehts ohne letzteres nicht, da so einige
Chips kein Dithering bei den YUV-Werten machen.
Viel Glück!
Ich weiß noch nicht genau, ob es genau das tut, was ich brauche, aber
das lässt sich zumindest synthetisieren.
PS
Ich benutze Cyclone V SoC. Der hat so um 80 DSP-Blocks, also sollte es
auch möglich mit floating point zu rechnen, oder? Wie genau ich diese
DSP Block verwende habe ich aber noch nicht herausgefunden...
Böser Kommunist schrieb:> PS> Ich benutze Cyclone V SoC. Der hat so um 80 DSP-Blocks, also sollte es> auch möglich mit floating point zu rechnen, oder?
Die Cyclone V DSP Blöcke haben keine Floating Point Fähigkeiten. Das
wäre für diesen Anwendungsfall aber auch Overkill.
> Wie genau ich diese> DSP Block verwende habe ich aber noch nicht herausgefunden...
So wie du das beschrieben hast könnte es schon sein das Quartus
automatisch die Multiplier benutzt. Einfach mal in einen getakteten
Prozess einbauen und im Synthese-Report nachschauen ob das der Fall ist.
Mit der Division könnte es aber Probleme geben (schon wegen der
Rundung). Ich würde das so beschreiben das sie erst am Ende durchgeführt
wird.
11/8 ergibt ansonsten einfach nur 1 und aus 11/32 wird 0...
Beispiele für die Instanziierung von Multipliern findest unter
"Inferring Multipliers and DSP Functions" in der Anleitung für Quartus.
Alternativ kannst du ja auch einmal mit dem neuen Fixed Point Package
(für VHDL-2008) spielen:
http://www.vhdl.org/fphdl/Fixed_ug.pdfhttp://www.eda-stds.org/fphdl/
Allerdings war bei meinen Versuchen das generierte Design größer und
langsamer als das mit dem selbst programmierten Code. Hat das hier schon
jemand einmal ernsthaft verwendet?
Deine oben hingeschriebene Routine mag zwar synthetisieren, aber ob
dabei das richtige rauskommt? Das Tool kann ev. eine
1-Latency-Matrizenmultiplikation synthetisieren, aber mit Sicherheit
leidet darunter deine Verarbeitungsgeschwindigkeit und Du benötigst mehr
Logik. Du solltest die Rechnung manuell in eine Pipeline aufsplitten,
z.B.
Schritt 1: Multiplikation
Schritt 2: Addition
Schritt 3: Sättigung/Rundung
(Slicing für RGB-Ergebnis)
Ev. musst Du noch weitere Delay-Stages einführen, wenn du die Daten
nicht im 4:4:4-Format bekommst. Teils ist für höhere
Verarbeitungsgeschwindigkeit auch schon so ein Delay nötig, nur um die
lokale Logikspaghetti etwas zu entbündeln.
Böser Kommunist schrieb:> Ich benutze Cyclone V SoC. Der hat so um 80 DSP-Blocks, also sollte es> auch möglich mit floating point zu rechnen, oder? Wie genau ich diese> DSP Block verwende habe ich aber noch nicht herausgefunden...
Du musst nur eine Ganzzahl-Multiplikation hinschreiben, dann
instanziieren die Tools typischerweise einen Multiplier. Wenn die
Bitbreite nicht reicht, werden sie einfach kaskadiert (nicht der Fall
bei bloss 8 bit RGB). Float-Support geschieht ebenfalls über die
Kaskadierung, aber wie oben genannt: Overkill, und bringt eine Menge
Probleme. Besser clever runden/sättigen.
Die Matrizenwerte für die Farbraumkonvertierung würde ich gleich als
16-Bit-Ganzzahlregister implementieren, damit du sie mit dem SoC setzen
kannst. Auf Anhieb kriegt man nämlich in den seltensten Fällen die
richtigen Farben..
Und den 2.14 oder 1.15-Wert (siehe auch
http://phobos.iet.unipi.it/~pieri/EdT2/2181_Manual/Appendix_C.pdf)
kannst Du schnell in Software berechnen.
Für 2.14 z.B. (0x4000 entspr. 1.0) ergibt sich (C oder python style)
fact = (0x4000 * f) & 0xffff)
Das Bild kommt in 4:2:2 Format.
Das ganze Projekt sieht so aus, in etwa:
Beitrag "Konzept für CCD Kamera<=>VGA interfacing mit FPGA"
Die Sache mit dem Pipeline werde ich noch anschauen, noch nie so was
gemacht. Sieht aber einfach aus:
Z.B. wie in diesem Bsp:
PS. Die Farbkonvertierung ist eigentlich egal, da meine Kamera sowieso
S/W ist. Ich denke hier kann einfach der Y Wert genommen werden.
Aber wenn ich schon so ein Projekt mache, dann mal richtig:)
1
2
PROCESS(Clk)
3
BEGIN
4
if(rising_edge(Clk))then
5
--Implement the pipeline stages using a for loop and case statement.
6
--'i' is the stage number here.
7
--The multiplication is done in 3 stages here.
8
--See the output waveform of both the modules and compare them.
Hi,
sag' doch gleich, dass es nur SW ist :-)
Dann kannst du auch auf die Schnelle die '2:2'-Sache überspringen und
die Y-Werte direkt auf je R, G, B zuweisen.
Zur Pipeline noch: hat sich für mich bewährt, die Stages in
unterschiedliche Prozesse zu klatschen und entsprechend zu kennzeichnen.
Solche Loop-Konstrukte würde ich eher weglassen. Meistens führt man ja
noch ein "data valid" signal mit, was jeweils verzögert wird, dann
wird's übersichtlich, und es wird gleich klar, wann welche Werte gültig
sind.
1
-- Erste Stage:
2
3
s1_mul:
4
process(clk)
5
begin
6
ifrising_edge(clk)then
7
s1_valid<=ce;
8
s1_result<=signed(a)*signed(b);
9
endif;
10
endprocess;
11
12
-- Zweite Stage:
13
14
s2_acc:
15
process(clk)
16
begin
17
ifrising_edge(clk)then
18
s2_valid<=s1_valid;
19
s2_acc<=s2_acc+s1_result;
20
endif;
21
endprocess;
Bei den DSP-Units gibt es recht unterschiedliche Architekturen, die
einen addieren recht gut und schnell mit 1-Latenz, bei manchen brauchst
du etwas Delay, um eine Matrizenmultiplikation optimal umzusetzen.
Entweder musst du dann mit der Pipeline "spielen", oder nimmst gleich
die IP-Core-Generatoren, die den optimalen Code ausspucken.