Forum: FPGA, VHDL & Co. Genesys 2 (Xilinx Kintex 7): Audio-Codec Implementierung und Filteranbindung


von itse_me_mario (Gast)



Lesenswert?

Hallo zusammen,

ich versuche gerade einen FIR-Filter mit einer Abtastrate von 16 kHz auf 
dem Genesys 2 Board 
(https://reference.digilentinc.com/reference/programmable-logic/genesys-2/reference-manual) 
zu implementieren (Vivado 2019.2, VHDL). Für diesen Zweck muss ich den 
boardinternen Audio-Codec (ADA1761) konfigurieren. Ich habe bereits ein 
FIR-Filter in der transponierten Direktform implementiert und getestet, 
welches ich zu einem späteren Zeitpunkt anbinden möchte.

Ich habe einen Artikel von Stefan Scholl „Audio Interface for the 
Zedboard“ gefunden (TU Kaiserslautern Germany, see 
https://kluedo.ub.uni-kl.de/frontdoor/deliver/index/docId/4034/file/zedboard_audio_doc.pdf). 
Dieses Framwork basiert auf einem Framework von Mike Field (alias 
hamster).
Die VHDL-files sind auch dabei 
(https://github.com/ems-kl/zedboard_audio).

Ich habe das alte Framework in ein angepasstes Framework für das Genesys 
2 umgewandelt, da es sich um denselben Audio-Codec handelt. Leider 
funktioniert etwas nicht, und ich weiß NOCH nicht wie ich den Fehler 
behebe (Critical Timing Problem).

Als erstes möchte ich den LINE IN Input direkt an den HP OUT Ausgang 
weitergeben. Als nächsten Schritt möchte ich dann meinen FIR-Filter 
(Festkommarealisierung) anbinden. Für die Funktionsanalyse benutze ich 
dann das Analog Discovery 2 von Digilent (Frequenzgang messen mit 
Sinussweeps).


Critical Warnings:
[Timing 38-282] The design failed to meet the timing requirements. 
Please see the timing summary report for details on the timing 
violations.

[Board 49-67] The board_part definition was not found for 
digilentinc.com:genesys2:part0:1.1. This can happen sometimes when you 
use custom board part. You can resolve this issue by setting 
'board.repoPaths' parameter, pointing to the location of custom board 
files. Valid board_part values can be retrieved with the 
'get_board_parts' Tcl command.


Ich dachte erst mein Framework funktioniert, aber wenn man hphone_out_l 
und hphone_out_r auf 0 setzt höre ich immer noch die Musik von meinem 
IPod über die Kopfhörern (aufgrund des defaultmäßigen 
Startup-Programms).

Wenn das Design funktioniert, möchte ich das Codec-Interface hochladen. 
Vielleicht kann auch jemand anderes dieses Interface benötigen.

Wenn mir jemand helfen könnte, oder ein anderes Audio-Codec Interface 
hat, würde ich mich sehr freuen.

Ich habe eine Modulübersicht, das Block Design und die vhdl-Dateien 
angehängt. Auch die Codec Konfiguration (docx) sowie die Fehlermeldungen 
befinden sich im Anhang. Wenn erwünscht, kann ich weitere Files 
hochladen.

Vielen Dank im Voraus.

Bleibt gesund und beste Grüße
Mario

von itse_me_mario (Gast)


Angehängte Dateien:

Lesenswert?

Weitere Dateien

Ich werde jetzt mal versuchen den Systemtakt auf 50 MHz zu reduzieren. 
Vielleicht verschwinden die Timing-Probleme ja dann...

von Mario A. (itse_me_mario)



Lesenswert?

VHDL-Dateien.

von Klakx -. (klakx)


Lesenswert?

In der STA steht ein Requirement von 0.8 ns. Das kommt mir zu klein vor.

Asynchroner Taktübergang? Wahrscheinlich springst du ohne Vorkehrungen 
zwischen 48 MHz und 100 MHz. Die würde ich wie asynchrone Takte 
behandeln.

Oder du stellst diese auf 48 und 96 MHz (oder 48 und 144 MHz), falls 48 
MHz fix bleiben muss. Dann können die Übergänge wieder synchron bleiben.

Wie sieht das Constraint-File aus? ... und schau dir die Taktübergänge 
genauer an.

von Mario A. (itse_me_mario)


Lesenswert?

Danke für deine Anmerkungen!

Was meinst du mit STA?

Das Constraints File sieht so aus 
(https://github.com/Digilent/digilent-xdc/blob/master/Genesys-2-Master.xdc):

#### This file is a general .xdc for the Genesys 2 Rev. H
#### To use it in a project:
#### - uncomment the lines corresponding to used pins
#### - rename the used ports (in each line, after get_ports) according 
to the top level signal names in the project

## Clock Signal
set_property -dict { PACKAGE_PIN AD11  IOSTANDARD LVDS     } [get_ports 
{ sysclk_n }]; #IO_L12N_T1_MRCC_33 Sch=sysclk_n
set_property -dict { PACKAGE_PIN AD12  IOSTANDARD LVDS     } [get_ports 
{ sysclk_p }]; #IO_L12P_T1_MRCC_33 Sch=sysclk_p



## Audio Codec
set_property -dict { PACKAGE_PIN AH19  IOSTANDARD LVCMOS18 } [get_ports 
{ aud_adc_sdata }]; #IO_L8N_T1_32 Sch=aud_adc_sdata
set_property -dict { PACKAGE_PIN AD19  IOSTANDARD LVCMOS18 } [get_ports 
{ aud_adr0 }];#{ aud_adr[0] }]; #IO_L10P_T1_32 Sch=aud_adr[0]
set_property -dict { PACKAGE_PIN AG19  IOSTANDARD LVCMOS18 } [get_ports 
{ aud_adr1 }];#{ aud_adr[1] }]; #IO_L8P_T1_32 Sch=aud_adr[1]
set_property -dict { PACKAGE_PIN AG18  IOSTANDARD LVCMOS18 } [get_ports 
{ aud_bclk }]; #IO_L11N_T1_SRCC_32 Sch=aud_bclk
set_property -dict { PACKAGE_PIN AJ19  IOSTANDARD LVCMOS18 } [get_ports 
{ aud_dac_sdata }]; #IO_L7P_T1_32 Sch=aud_dac_sdata
set_property -dict { PACKAGE_PIN AJ18  IOSTANDARD LVCMOS18 } [get_ports 
{ aud_lrclk }]; #IO_L9P_T1_DQS_32 Sch=aud_lrclk
set_property -dict { PACKAGE_PIN AK19  IOSTANDARD LVCMOS18 } [get_ports 
{ aud_mclk }]; #IO_L7N_T1_32 Sch=aud_mclk
set_property -dict { PACKAGE_PIN AE19  IOSTANDARD LVCMOS18 } [get_ports 
{ aud_scl }]; #IO_L10N_T1_32 Sch=aud_scl
set_property -dict { PACKAGE_PIN AF18  IOSTANDARD LVCMOS18 } [get_ports 
{ aud_sda }]; #IO_L11P_T1_SRCC_32 Sch=aud_sda



## IIC Bus
#set_property -dict { PACKAGE_PIN AE30  IOSTANDARD LVCMOS33 } [get_ports 
{ SYS_SCL }]; #IO_L16P_T2_13 Sch=sys_scl
#set_property -dict { PACKAGE_PIN AF30  IOSTANDARD LVCMOS33 } [get_ports 
{ SYS_SDA }]; #IO_L16N_T2_13 Sch=sys_sda

Ich habe die nicht verwendeten Ports gleich mal weggelassen.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

Okay --> STA = Static Timing Analysis.
Requirement 0.833.

von Markus F. (mfro)


Lesenswert?

Mario A. schrieb:
> Das Constraints File sieht so aus

da sind nur PIN-Assignments drin?

Wenn Du die asynchronen Clock-Domänen nicht aktiv für die Timing-Analyse 
auftrennst, wird die Synthese (trotz evt. vorhandenen Synchronizern) 
versuchen, die sich aus der CDC ergebenden Timing-Requirements zu 
optimieren.
Das kann nicht gutgehen.

von Mario A. (itse_me_mario)


Lesenswert?

Danke für den Tipp! Das wusste ich bisher nicht. Solange arbeite ich 
leider auch noch nicht mit FPGAs.

Hab mir gerade nochmal das vorher verwendete XDC für das Zedboard 
angesehen.
Unter Anderem sieht es folgendermaßen aus:

# timing constraints
create_clock -period 10.000 -name clk_100 [get_ports clk_100]

set_false_path -from [get_clocks zed_audio_clk_48M] -to [get_clocks 
clk_100]
set_false_path -from [get_clocks clk_100] -to [get_clocks 
zed_audio_clk_48M]


# 100 mhz clock
set_property PACKAGE_PIN Y9 [get_ports clk_100]
set_property IOSTANDARD LVCMOS33 [get_ports clk_100]




Die von dir beschriebene Auftrennung, die ich vornehmen muss ist dann 
wohl set_false_path ... ?

Ich werde das morgen probieren und die 2 Zeilen set_false_path anpassen 
und einbinden.

von Mario A. (itse_me_mario)


Lesenswert?

Ich habe mal versucht das Constraintsfile anzupassen, aber bislang ohne 
Erfolg.

Unter Anderem habe ich es mit

set_false_path -from [get_clocks zed_audio_clk_48M] -to [get_clocks
clk_100]
set_false_path -from [get_clocks clk_100] -to [get_clocks
zed_audio_clk_48M]

--> [Vivado 12-4739] set_false_path:No valid object(s) found for '-to 
[get_clocks clk_100]'. [Genesys2Master.xdc:13]

und

set_false_path -from [get_clocks zed_audio_clk_48M] -to [get_clocks
sysclk_p]
set_false_path -from [get_clocks sysclk_p] -to [get_clocks
zed_audio_clk_48M]

--> The design failed to meet the timing requirements. Please see ...

und

set_false_path -from [get_clocks zed_audio_clk_48M] -to [get_clocks
sysclk_p]
set_false_path -from [get_clocks sysclk_p] -to [get_clocks
zed_audio_clk_48M]
set_false_path -from [get_clocks zed_audio_clk_48M] -to [get_clocks
sysclk_n]
set_false_path -from [get_clocks sysclk_n] -to [get_clocks
zed_audio_clk_48M]

--> [Vivado 12-4739] set_false_path:No valid object(s) found for '-to 
[get_clocks sysclk_n]'. [Genesys2Master.xdc:19]

versucht, nachdem ich clocking.vhd (Originaldatei) wieder eingebunden 
und die (Clock-)Signale wieder in den Ausgangszustand des Autors 
zurückgesetzt habe.


Da ich nur sysclk_p und sysclk_n über das XDC ansprechen kann, muss ich 
mir über den Clocking-Wizard meinen Clock (differential input) erzeugen 
oder?
Ich kann ja nicht einfach, wie der Autor:

create_clock -period 10.000 -name clk_100 [get_ports clk_100]
set_property PACKAGE_PIN Y9 [get_ports clk_100]
set_property IOSTANDARD LVCMOS33 [get_ports clk_100]

schreiben.

Leider muss ich jetzt bis zum Wochenende erstmal ein paar andere Dinge 
erledigen, bevor ich wieder viel Zeit investieren kann.

von Markus F. (mfro)


Lesenswert?

Ich sehe in deinem Toplevel nur diese Clocks:
1
clk_48_MHz             : in STD_LOGIC;
2
clk_100_MHz            : in STD_LOGIC;

Die sehe ich aber wiederum in deinen Constraints nicht?

von Mario A. (itse_me_mario)


Lesenswert?

Hallo Markus,

das eigentliche Toplevel ist audio_testbench (die Namensbezeichnung ist 
etwas unglücklich, aber entspricht dem Originalprojekt --> werde ich 
besser bald mal ändern!). Dort verwende ich sysclk_p und sysclk_n in der 
Entity. Aus denen erzeuge ich mir über den Clocking Wizard die weiteren 
Clocks.
Eine Ebene darunter kommt dann audio_top. Dort verwende ich die vom 
Clocking Wizard erzeugten Signale.

von Klakx -. (klakx)


Lesenswert?

wenn die Clocks noch durch einen Clock-Manager laufen hilft es die 
generierten Clocks mit einzubeziehen:
1
get_clocks -include_generated_clocks clk1

Natürlich kann man die Taktübergänge generell von der Prüfung 
abschalten.

Ich rate dir dies nicht kopflos zu tun. Schau dir betroffenen Signale 
ganz gut an und stell dir die Frage, ob du in jeder Phasenlage mit dem 
Signal umgehen kannst.

Will man vorsichtig sein, reicht es aus nur die betroffenen Signale per 
set_false_path (oder set_max_delay) zu constrainieren.

von Mario A. (itse_me_mario)


Lesenswert?

Danke für deine Hilfe!

Also unter Report Timing Summary zeigt es mir den Fehler bei 
clk_out1_clk_wiz_0 to clk_out2_clk_wiz_0
und
clk_out2_clk_wiz_0 to clk_out1_clk_wiz_0
an.

Erst einmal nur diese beiden mit set_false_path setzen?

Variante 1 in xdc:

get_clocks -include_generated_clocks clk_out1_clk_wiz_0
get_clocks -include_generated_clocks clk_out2_clk_wiz_0

set_false_path -from [get_clocks clk_out1_clk_wiz_0] -to [get_clocks 
clk_out2_clk_wiz_0]
set_false_path -from [get_clocks clk_out2_clk_wiz_0] -to [get_clocks 
clk_out1_clk_wiz_0]

--> In den Messages steht nur noch [Board 49-67] The board_part 
definition was not found for digilentinc.com:genesys2:part0:1.1. This 
can happen sometimes when you use cumstom board part. You can resolve 
this issue by setting 'board.repoPaths' parameter, pointing to the 
location of custom board files. Valid board_part values can be retrieved 
with the 'get_board_parts' Tcl command.

Unter Implementation --> Report Timing Summary zeigt es mir allerdings 
immer noch denselben Fehler an.

Variante 2 in xdc:

get_clocks -include_generated_clocks clk_out1
get_clocks -include_generated_clocks clk_out2

set_false_path -from [get_clocks clk_out1] -to [get_clocks clk_out2]
set_false_path -from [get_clocks clk_out2] -to [get_clocks clk_out1]

--> set_false_path: No valid object(s) found for '-from [get_clocks 
clk_out1]'.

Leider schaffe ich es voraussichtlich nicht vorm Wochenende mich 
intensiv mit false_paths zu beschäftigen. Ich kann es aber kaum erwarten 
und hoffe, dass es der entscheidende Fehler ist und der Codec dann 
endlich funktioniert. Eine Stunde am Tag versuche ich dennoch bis dahin 
einzuräumen.

von Markus F. (mfro)


Lesenswert?

Klakx -. schrieb:
> Will man vorsichtig sein, reicht es aus nur die betroffenen Signale per
> set_false_path (oder set_max_delay) zu constrainieren.

Kenne Vivado nicht, aber

set_clock_groups -asynchronous -group <clock1> -group <clock2> ...

ist meist die bessere Wahl (anstatt x set_false_path Statements)?

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hallo,

ich hab heute mal wieder versucht die asynchronen Clocks zu setzen.

Unter anderem im XDC mit:
get_clocks -include_generated_clocks clk_out1_clk_wiz_0
get_clocks -include_generated_clocks clk_out2_clk_wiz_0
set_clock_groups -asynchronous -group {clk_out1_clk_wiz_0} -group 
{clk_out2_clk_wiz_0}

nachdem ich mir in der Tcl Console mit report_clocks die Clocks 
angesehen habe. Die Ergebnisse von report_cdc hänge ich euch auch noch 
an. Außerdem habe ich mir mit Report Clock Interaction die 
Abhängigkeiten angesehen.

Ein Critical Warning bzgl. Timing habe ich jetzt nicht mehr.
Allerdings: [Vivado 12-4739] set_clock_groups:No valid object(s) found 
for '-group clk_out1_clk_wiz_0'. 
["C:/Users/aim34424/Desktop/Forum/Genesys2AudioCodecPlayground/FIRFilter 
withCodec.srcs/constrs_1/new/Genesys2Master.xdc":56]

und

[Board 49-67] The board_part definition was not found for 
digilentinc.com:genesys2:part0:1.1. This can happen sometimes when you 
use custom board part. You can resolve this issue by setting 
'board.repoPaths' parameter, pointing to the location of custom board 
files. Valid board_part values can be retrieved with the 
'get_board_parts' Tcl command.



Ich habe dann noch einmal versucht statt
hphone_l <= line_in_l;
hphone_r <= line_in_r;
nur 0en an den Ausgang zu geben:
hphone_l <= (others => '0');
hphone_r <= (others => '0');
--> man hört immer noch die Musik vom IPod über die angeschlossenen 
Kopfhörer. Sche

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Dateien

von Mario A. (itse_me_mario)


Lesenswert?

set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]

habe ich noch ins XDC eingefügt. Jetzt sind wieder 2 Warnings weg.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Okay, ich habe nun set_clock_groups wie folgt angewandt:
https://www.xilinx.com/video/hardware/clock-group-constraints.html

Jetzt kennt Vivado auch auf einmal mein Constraints-File. Daraufhin 
wurde set_clock_groups -asynchronous -group [get_clocks *clk_out1*] 
-group [get_clocks *clk_out2*] automatisch eingefügt in ein neues 
(leeres) XDC-File. Anschließend habe ich noch meine Pin-Assignments 
eingefügt.

Nun habe ich noch:
[Board 49-67] The board_part definition was not found for 
digilentinc.com:genesys2:part0:1.1. This can happen sometimes when you 
use custom board part. You can resolve this issue by setting 
'board.repoPaths' parameter, pointing to the location of custom board 
files. Valid board_part values can be retrieved with the 
'get_board_parts' Tcl command.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Aktuelle Warnings.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Jetzt habe ich noch zusätzliche Board-Files hinzugefügt.
https://reference.digilentinc.com/reference/software/vivado/board-files.

Die Warning:

[Board 49-67] The board_part definition was not found for ...

hat sich nun auch erledigt.

Es bleiben die angehängten Warnings. Wenn ich den Bitstream auf das 
Board spiele hört man wieder die Musik vom IPod über die Kopfhörer. Wenn 
ich dann wieder hphone_l und hphone_r auf 0 setze höre ich leider immer 
noch die Musik vom IPod.

Die Warning "No constraints selected or write" kann man denke ich 
ignorieren.
https://www.xilinx.com/support/answers/73510.html

Ich denke ich werde nun mal versuchen den ILA einzubauen.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

leider ist schon wieder ein Problem aufgetreten. Wenn ich den ILA 
einbinde, bekomme ich eine Warning (siehe Anhang). Wenn ich aber ein 
anderes (bereits getestetes) Programm auf dem Genesys2 zum laufen bringe 
erhalte ich zwar die selbe Fehlermeldung, aber der ILA zeigt das 
richtige Ergebnis an und triggert auch. Ist diese Warning evtl. der 
Grund, warum es nicht funktioniert, oder beeinflusst diese die 
Funktionsweise nicht?

Der Clock ist direkt mit dem Ausgang des clocking wizards verbunden.
Der Link https://www.xilinx.com/support/answers/64764.html hat mir 
bisher noch nicht weitergeholfen.

Als Trigger habe ich new_sample=1 und die steigende Flanke von 
sample_clk_48k versucht. Leider triggert der ILA nicht automatisch. Als 
Eingangssignal (Line In) habe ich sowohl meinen IPod, als auch das 
Analog Discovery verwendet (Sinussweeps). Seltsamerweise hat sich die 
Variable count manchmal nach dem ersten erzwungenen Trigger erhöht, 
danach aber nicht mehr (das Signal ist defaultmäßig auf 0 gesetzt).

Code zum Einbinden es ILAs:
COMPONENT ila_0
PORT (
  clk : IN STD_LOGIC;
  probe0 : IN STD_LOGIC_VECTOR(0 DOWNTO 0);
  probe1 : IN STD_LOGIC_VECTOR(23 DOWNTO 0);
  probe2 : IN STD_LOGIC_VECTOR(23 DOWNTO 0);
  probe3 : IN STD_LOGIC_VECTOR(23 DOWNTO 0);
  probe4 : IN STD_LOGIC_VECTOR(23 DOWNTO 0);
        probe5 : IN STD_LOGIC_VECTOR(0 DOWNTO 0);
  probe6 : IN STD_LOGIC_VECTOR(0 DOWNTO 0);
  probe7 : IN STD_LOGIC_VECTOR(5 downto 0)

);
END COMPONENT  ;



    signal sample_clk_48k_vec   : STD_LOGIC_VECTOR (0 downto 0):= "0";
    signal new_sample_vec       : STD_LOGIC_VECTOR (0 downto 0):= "0";
    signal hphone_valid_vec     : STD_LOGIC_VECTOR (0 downto 0):= "0";


-- keep signals for tracking them with the Logic Analyzer
    attribute keep : string;

    attribute keep of sample_clk_48k : signal is "true";
    attribute keep of hphone_l : signal is "true";
    attribute keep of hphone_r : signal is "true";

    attribute keep of line_in_l : signal is "true";
    attribute keep of line_in_r : signal is "true";

-- neu eingefügt
    attribute keep of new_sample   : signal is "true";
    attribute keep of hphone_valid : signal is "true";

    attribute keep of counter : signal is "true";
-- neu eingefügt ende

...

sample_clk_48k_vec(0)   <= sample_clk_48k;
new_sample_vec(0)       <= new_sample;
hphone_valid_vec(0)     <= hphone_valid;

ila : ila_0
PORT MAP (
  clk => s_clk_100_MHz,
  probe0 => sample_clk_48k_vec,
  probe1 => line_in_l,
  probe2 => line_in_r,
  probe3 => hphone_l,
  probe4 => hphone_r,
  probe5 => new_sample_vec,
  probe6 => hphone_valid_vec,
  probe7 => std_logic_vector(counter)

);

Hat jemand eine Idee, was ich machen kann? Leider komme ich gerade nicht 
mehr weiter... Ich bin für jede Hilfe dankbar!
Ich erstelle gerade noch eine Zeichnung zur Übersicht mit allen 
Signalen, die ich anschließend hochladen werde.

Beste Grüße
Mario

: Bearbeitet durch User
von Klakx -. (klakx)


Lesenswert?

ist das Signal s_clk_100_MHz angeschlossen? Im top sah ich sonst immer 
nur das signal clk_100_MHz.

von Mario A. (itse_me_mario)


Lesenswert?

Ja das Signal ist angeschlossen.

clk_wiz : clk_wiz_0
port map (
          -- Clock out ports
           clk_out1 => s_clk_48_MHz,
           clk_out2 => s_clk_100_MHz,
           -- Clock in ports
           clk_in1_p => sysclk_p,
           clk_in1_n => sysclk_n
         );

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Ich habe mal die aktuelle Beschaltung zur Übersichtlichkeit angehängt.

Grad noch gesehen: line_in_l und _r sind natürlich nicht in der Entity.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Nach 2 Wochen Urlaub beschäftige ich mich nun wieder intensiv mit der 
Codec-Anbindung.
Zu den angehängten Warnings habe ich nicht großartig was gefunden. Ich 
denke man kann diese ignorieren(?).

Hat jemand eine Idee wie ich die Signale (ohne ILA) sonst noch 
analysieren/debuggen kann?
Ich werde mal versuchen Testbenches für die einzelnen Module zu 
schreiben...
Vielen Dank im Voraus!

von Gustl B. (-gb-)


Lesenswert?

Was mich etwas wundert ist die Tatsache, dass deine Testbench selbst 
noch Ein- und Ausgänge nach Außen hat. Normalerweise, also so wie ich 
das kenne, nutzt man die Testbench NUR zur Simulation und nicht auf der 
Hardware. Die Testbench tut also so, als sei sie die Hardware rund um 
das FPGA. Dazu müsstest du in der Testbench zumindest die zwingend 
notwendigen Bestandteile vom FPGA Board nachbilden wie den Taktgeber, 
den Audio Codec, ...

von Mario A. (itse_me_mario)


Lesenswert?

Hallo Gustl,

audio_testbench, ist keine Testbench. Das File wird als Top-Modul 
verwendet. Sorry für die Unklarheit, ich hab das so vom 
Originalframework übernommen und war auch erst verwundert/irritiert. Ich 
denke auch nicht, dass der Autor des alten Frameworks die Datei 
audio_testbench als Testbench verwendet hat.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

kann es sein, dass im Zusammenhang mit IOBUF etwas nicht stimmt? I wird 
im Schematic auf Ground gerouted.
Das Signal i2c_sda_o wird allerdings nirgends auf '1' gesetzt, also 
könnte es schon passen...

Auszug aus adau1761_izedboard.vhd

i_i2s_sda_obuf : IOBUF
   port map (
      IO => AC_SDA,   -- Buffer inout port (connect directly to 
top-level port)
      O => i2c_sda_i, -- Buffer output (to fabric)
      I => i2c_sda_o, -- Buffer input  (from fabric)
      T => i2c_sda_t  -- 3-state enable input, high=input, low=output
   );

von FPGA-Experte (Gast)


Lesenswert?

itse_me_mario schrieb:
> Dieses Framwork basiert auf einem Framework von Mike Field (alias
> hamster).

Dessen Webseite verschwunden ist, weil sich jemand über geklauten Code, 
den er verbreitet haben soll, beschwert hat.

von Mario A. (itse_me_mario)


Lesenswert?

Ah okay. Das wusste ich nicht.

von Mario A. (itse_me_mario)


Lesenswert?

Hat jemand ein paar Tipps, wo man sonst noch Hilfe bekommen kann? Leider 
komme ich seit geraumer Zeit nicht mehr weiter... Vielen Dank im Voraus!

von Gustl B. (-gb-)


Lesenswert?

Sorry, ich habe mich da jetzt nicht tiefer eingearbeitet, so wie ich das 
verstanden habe möchtest du Audio vom ADA1761 bekommen, durch einen FIR 
Filter schicken und dann irgendwie weiterverarbeiten.

Was du aktuell versuchst ist das Audio von einem ADA1761 Eingang wieder 
über den ADA1761 ausgeben zu wollen? Dazu musst du den ADA1761 irgendwie 
konfigurieren und das klappt nicht?

Wenn du später das Audio sowieso nicht über den ADA1761 ausgeben willst, 
dann lass das doch jetzt auch weg.

Nehme das Audio von dem ADA1761 und gebe das erstmal digital aus. Z. B. 
über UART zum PC mit nur wenigen Bits je Sample oder so.
Dann im nächsten Schritt setzt du zwischen UART und ADA1761 den FIR. Und 
dann guckst du was du mit dem gefilterten Audio am Ende machen möchtest.

Bei Digitelnt gibt es auch Hardwaredemobeschreibungen, auch mit Audio. 
Nur Audio vom ADA1761 bekommen und filtern braucht nicht viel HDL, das 
sollte recht leicht gehen und ohne komplizierte Konfiguration des 
ADA1761.

von Mario A. (itse_me_mario)


Lesenswert?

Hallo zusammen,
ich bin gerade auf eine Idee gebracht worden und habe dazu was passendes 
gefunden: 
https://store.digilentinc.com/pmod-i2s2-stereo-audio-input-and-output/
Somit spare ich mir die Konfiguration des Codecs über I2C und müsste 
mich wohl nur um das I2S-Interface kümmern. Ich werde mir das Bauteil 
gleich mal bestellen. Ich hoffe das funktioniert so, wie ich mir das 
vorstelle.

Gruß Mario

von Gustl B. (-gb-)


Lesenswert?

Ja das kannst du ntürlich auch machen. Aber die Frage ist eigentlich was 
dein Ziel ist. Was soll mit den gefilterten Daten passieren? Willst du 
das wieder am Ende als Audio ausgeben oder soll das zu einem PC oder ...

Wenn du das nur zum Debugging ausgeben möchtest als Audio, dann kanst du 
das auch zum PC schicken und dort ausgeben oder anzeigen lassen. Der 
UART auf deinem Board kann maximal 3 MBaud. Das reicht für 16 Bit Audio 
mit so 15 kSamples/s. Oder für 8 Bit Audio bei so 30 kSamples/s. Klar, 
nicht optimal, aber zum Testen ob da was rauskommt reicht das 
vielleicht.
Du könntest auch einen IO nehmen und da mit PWM Audio ausgeben und 
danach analog tiefpassfiltern. Oder du nimmst noch BRAM dazu, dann 
kannst du Werte aufnehmen und dann über UART ausgeben.
Du hast ausserdem einen FT2232H auf dem Board, damit kannst du die Daten 
mit voller USB 2.0 Datenrate ausgeben.

Ein kleines I2S Audio Modul ist auch eine Lösung. Wenn du später für 
dein langfristiges Zeil den Audio Codec nicht konfigurieren musst, dann 
würde ich das auch zwischendrin vermeiden wenn es geht.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hallo Gustl, vielen Dank für deine ausführlichen Tipps!

Hauptziel:
Audio-Daten über Audioanschluss einlesen, filtern und anschließend per 
Audioanschluss wieder ausgeben (in Echtzeit). Die Funktion der Filter 
kann ich einfach mit dem Analog Discovery 2 überprüfen 
(Frequenzgangsmessung mit Sinussweeps). Danach will ich ein adaptives 
Filter zur Störgeräuschunterdrückung darauf implementieren. Gut wäre es, 
wenn ich hierfür später noch die Daten für die Analyse mit z.B. Matlab 
(gerade für das adaptive Filter --> ERLE-Messungen) auf dem PC hätte.

Wäre es dann aus deiner Sicht sinnvoller die gefilterten Daten mittels 
UART (FT232R) oder PC-FPGA Data Transfer DPTI/DSPI (FT2232) an den PC zu 
schicken (siehe Bilder)?

Den Codec muss ich nicht speziell konfigurieren. Es reicht, wenn ich ein 
paar Abtastraten (16 kHz, 32 kHz, 48 kHz) einstellen kann.

Vielen Dank im Voraus!

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Audio-Daten über Audioanschluss einlesen, filtern und anschließend per
> Audioanschluss wieder ausgeben (in Echtzeit).

OK, du willst das Audio also wieder als Audio ausgeben mit diesem FPGA 
Board. Gut, dann solltest du in der Tat herausfinden wie man über diesen 
Codec Audio aufnimmt/einliest und wie man es über diesen Codec auch 
wieder ausgibt.
Das Einlesen kannst du ja schon. Klappt es denn auch Audio auszugeben? 
Z. B. einen Sinus den du im FPGA erzeugst?

Mario A. schrieb:
> Die Funktion der Filter
> kann ich einfach mit dem Analog Discovery 2 überprüfen
> (Frequenzgangsmessung mit Sinussweeps).

Naja, das ist zwar praktisch, aber ich würde das Filter mit einem 
Filterdesigner wie Pyfda entwerfen und dann im Simulator (z. B. dem in 
Vivado) testen ob es auch funktioniert. Auf die Hardware würde ich nur 
im letzten Schritt gehen.

Mario A. schrieb:
> Wäre es dann aus deiner Sicht sinnvoller die gefilterten Daten mittels
> UART (FT232R) oder PC-FPGA Data Transfer DPTI/DSPI (FT2232) an den PC zu
> schicken (siehe Bilder)?

Hm, kommt drauf an was du mit dem Audio machen willst. Eigentlich musst 
du dir das ja nicht angucken. Den Frequenzgang und so 
Filtereigenschaften zeigt dir der Filterdesigner und ob das auch so 
funktioniert siehst du in der Simulation.

Mario A. schrieb:
> Den Codec muss ich nicht speziell konfigurieren. Es reicht, wenn ich ein
> paar Abtastraten (16 kHz, 32 kHz, 48 kHz) einstellen kann.

Du willst aber ja auch gefiltertes Audio ausgeben. Kannst du das schon?

Das hier

itse_me_mario schrieb:
> Als erstes möchte ich den LINE IN Input direkt an den HP OUT Ausgang
> weitergeben.

würde ich nicht machen. Weil du das ja später nicht brauchst. Ich würde 
als Ziele definieren:

1. Audio ausgeben über den Codec. Und zwar nicht das Audio vom Line 
Eingang (weil du dafür den Codec irgendwie konfigurieren müsstest), 
sondern z. B. einen Sinus den du im FPGA erzeugst.
2. Audio mit dem Codec einlesen.
3. Filter entwerfen mit dem Filterdesigner.
4. Filter mit Vivado oder so simulieren. Du kannst für das Filter den 
Xilinx IP verwenden, den FIR Compiler. Da wirfst du nur die 
Koeffizienten rein und das filtert dann sehr fein, kann man auch 
simulieren. In der Simulation, also der Testbench, erzeugst du dir den 
Sinussweep.
5. Alles zusammenleben und auf die Hardware bringen.

von Mario A. (itse_me_mario)


Lesenswert?

Hallo Gustl,

nochmal danke für deine Antwort. Manches ist mir allerdings noch unklar.

Warum kann ich nicht mit dem o.g. Modul mein Audio einlesen, es 
bearbeiten (z.B. FIR-Filtern) und wieder über den Line Out ausgeben? Das 
sollte doch funktionieren? Oder versteh ich dich gerade falsch?

The Digilent Pmod I2S2 (Revision A) features a Cirrus CS5343 Multi-Bit 
Audio A/D Converter and a Cirrus CS4344 Stereo D/A Converter, each 
connected to one of two audio jacks. These circuits allow a system board 
to transmit and receive stereo audio signals via the I2S protocol. The 
Pmod I2S2 supports 24 bit resolution per channel at input sample rates 
up to 108 kHz and output sample rates up to 200 kHz.

Quick Start
To set up a simple 44.1 kHz audio passthrough, three control signals 
need to be generated by the host system board.
1. A master clock (MCLK) at a frequency of approximately 22.579 MHz.
2. A serial clock (SCLK), which fully toggles once per 8 MCLK periods.
3. A Left/Right Word Select signal, which fully toggles once per 64 SCLK 
periods.

>>Das Einlesen kannst du ja schon.
Ich habe bisher noch nichts eingelesen.

Für den Filterentwurf verwende ich Matlab. Welchen Vivado Simulator 
meinst du? Einen graphischen? Oder in der Testbench?

>>Eigentlich musst du dir das ja nicht angucken.
Bei normalen IIR- und FIR-Filtern nicht. Für das adaptive Filter muss 
ich den Ausgang schon betrachten. Das ist für die anschließende 
Auswertung (ERLE-Kurven) wichtig. Dafür brauch ich die Werte.

>>Du willst aber ja auch gefiltertes Audio ausgeben. Kannst du das schon?
Nein, dafür will ich ja das Bauteil (mit AD und DA Wandlern) verwenden: 
https://store.digilentinc.com/pmod-i2s2-stereo-audio-input-and-output/


Halt du hast das Bauteil, welches ich verwenden will dabei nicht 
einbezogen, oder? Ich habe nämlich deine Nachricht um 11.49 Uhr 
übersehen. Entschuldigung...


Also nochmal zusammenfassend:
- ich möchte Audiosignale über einen Audioanschluss einlesen
- ich möchte das Signal filtern
- ich möchte das gefilterte Signal anschließend über einen
  Audioanschluss ausgeben
- später (für das adaptive Filter) würde ich gerne die gefilterten Werte
  zus. an den PC senden.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Warum kann ich nicht mit dem o.g. Modul mein Audio einlesen, es
> bearbeiten (z.B. FIR-Filtern) und wieder über den Line Out ausgeben? Das
> sollte doch funktionieren? Oder versteh ich dich gerade falsch?

Du meinst dieses PMOD Teil?
Klar. Aber wieso? Der audio Codec auf deinem FPGA Board sollte das auch 
können. Also würde ich mit diesem Codec schrittweise dem Ziel näher 
kommen.

Zuerst damit Audio einlesen.
Dann damit Audio ausgeben.
Dann das mit dem Filter dazwischen versuchen.

Mario A. schrieb:
> Ich habe bisher noch nichts eingelesen.

Du hast also noch nicht mit dem Codec auf dem FPGA Board Audio 
eingelesen? Dann würde ich das als erstes Zwischenziel setzen.

Mario A. schrieb:
> Für den Filterentwurf verwende ich Matlab. Welchen Vivado Simulator
> meinst du? Einen graphischen? Oder in der Testbench?

Welcher Simulator ist eingentlich egal. Er sollte aber deine 
Hardwarebeschreibung simulieren können. Die Testbench füttert das Filter 
mit einem Sweep und gibt dann das Ergebnis aus.

Mario A. schrieb:
> Für das adaptive Filter muss
> ich den Ausgang schon betrachten. Das ist für die anschließende
> Auswertung (ERLE-Kurven) wichtig. Dafür brauch ich die Werte.

Um so besser, das kann man in der Simulation wunderbar sehen.

Mario A. schrieb:
> Nein, dafür will ich ja das Bauteil (mit AD und DA Wandlern) verwenden:
> https://store.digilentinc.com/pmod-i2s2-stereo-audio-input-and-output/

Ja, das kannst du dafür nutzen. Aber warum das und nicht den Codec der 
auf dem FPGA Board sitzt? Der sollte das doch ebenfalls können?
Also eigentlich ist das natürlich egal, nimm das was leichter aussieht. 
Ich bin selber eher gegen Zusatzhardware wenn das sowieso schon auf dem 
Board drauf ist.

Mario A. schrieb:
> Also nochmal zusammenfassend:
> - ich möchte Audiosignale über einen Audioanschluss einlesen
> - ich möchte das Signal filtern
> - ich möchte das gefilterte Signal anschließend über einen
>   Audioanschluss ausgeben
> - später (für das adaptive Filter) würde ich gerne die gefilterten Werte
>   zus. an den PC senden.

Wunderbar. Dann würde ich zuerst das Einlesen alleine ans Laufen 
bekommen. Dann das Ausgeben alleine. Und dann das Filter.

Die Verbindung mit dem PC würde ich aber ganz an den Anfang stellen. Du 
nimmst im FPGA etwas Speicher. Z. B. 64 kByte BRAM. Dann nimmst du Audio 
auf und schreibst das in diesen Speicher. Dann liest du den Speicher aus 
und guckst am PC ob das Signal OK ist.
Für die Ausgabe kannst du dir im FPGA einen Sinus erzeigen und den über 
den Line out ausgeben lassen und angucken.

von Mario A. (itse_me_mario)


Lesenswert?

Danke für die schnelle Antwort!

>> Aber wieso?
Weil das ursprüngliche Framework ja leider nicht funktioniert und ich 
den Fehler nicht finde. Die I2C-Initialisierung entfällt dadurch 
zusätzlich und ich kann mein eigenes Framework schreiben.

>>Du hast also noch nicht mit dem Codec auf dem FPGA Board Audio
eingelesen? Dann würde ich das als erstes Zwischenziel setzen.
Naja das sagst du so leicht. :P Aber wenn der Codec nicht funktioniert 
und ich den Fehler einfach nicht finde, wird das schwierig. :D :D

>>Die Testbench füttert das Filter mit einem Sweep und gibt dann das Ergebnis aus.
Eine graphische Ausgabe wäre hierfür natürlich schön, aber mans kann´s 
natürlich auch so leicht überprüfen, wenn man sich ein kleines 
C-Programm (mit demselben Filter) schreibt und sich die Ergebnisse 
ausgeben lässt.

>>Um so besser, das kann man in der Simulation wunderbar sehen.
Genau. Und für weitere Matlab-Berechnungen/-Auswertungen brauche ich die 
Daten am PC.

>>Ich bin selber eher gegen Zusatzhardware wenn das sowieso schon auf dem
Board drauf ist.
Da stimme ich dir zu 100% zu. Aber leider bin ich nun seit über 2 
Monaten dran ein Framework aus dem Internet zu Debuggen/zum Laufen zu 
bringen und es funktioniert immer noch nicht. Wenn ich dann selber "nur" 
noch ein I2S-Interface schreiben muss (so scheint es zu sein, wenn ich 
die externe Hardware verwende), erleichtert das die Fehlersuche doch 
enorm.

>>Dann würde ich zuerst das Einlesen alleine ans Laufen
bekommen. Dann das Ausgeben alleine. Und dann das Filter.
Das wäre der Plan.

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Weil das ursprüngliche Framework ja leider nicht funktioniert und ich
> den Fehler nicht finde. Die I2C-Initialisierung entfällt dadurch
> zusätzlich und ich kann mein eigenes Framework schreiben.

Ich würde das "Framework" nicht nutzen, das sieht nicht wirklich gut 
aus. Der Codec ist doch ordentlich dokumentiert, die IOs am FPGA sind 
klar, da kann man schon selber was schreiben das aus dem Codec die 
Audiowerte rausholt.

Mario A. schrieb:
> Aber wenn der Codec nicht funktioniert
> und ich den Fehler einfach nicht finde, wird das schwierig. :D :D

Klar funktioniert der Codec. Du stocherst da ziemlich im Nebel. Ich 
würde als Anfangspunkt mal die Demo vom Hersteller Digilent nehmen. Da 
wird Audio mit dem Codec erfasst und ins RAM geschrieben. Den Teil mit 
dem RAM und so würde ich weglassen oder eben in einen kleinen BRAM 
Speicher schreiben und dann über UART zum PC ausgeben.

Mario A. schrieb:
> Eine graphische Ausgabe wäre hierfür natürlich schön, aber mans kann´s
> natürlich auch so leicht überprüfen, wenn man sich ein kleines
> C-Programm (mit demselben Filter) schreibt und sich die Ergebnisse
> ausgeben lässt.

Natürlich mit graphischer Ausgabe. Das macht der Vivado Simulator 
wunderbar.

Mario A. schrieb:
> Genau. Und für weitere Matlab-Berechnungen/-Auswertungen brauche ich die
> Daten am PC.

Klar, da hast du die auch. Sowohl bei der Simulation mit Vvado, auch da 
kannst du die gefilterten Werte wegschreiben für Matlab. Als auch mit 
dem UART. Da kannst du dir die Abtastwerte vom FPGA schicken lassen und 
dann am PC weiterverarbeiten/untersuchen.

Mario A. schrieb:
> Aber leider bin ich nun seit über 2
> Monaten dran ein Framework aus dem Internet zu Debuggen/zum Laufen zu
> bringen und es funktioniert immer noch nicht.

Das verstehe ich. Aber ich finde es ist deutlich schwerer etwas zu 
debuggen was man nicht selbst geschrieben hat als etwas von Null an neu 
zu schreiben.

Wobei jetzt erstmal die Frage ist ob du den Codec auf dem Board nutzen 
solltest oder einen anderen IC.
Der Codec auf dem Board kann ziemlich viel. Im Datenblatt 
https://www.analog.com/media/en/technical-documentation/data-sheets/ADAU1761.pdf 
ist eine Registerübersicht am Ende. Da müsstest du abschätzen wie 
aufwändig das wird. Oft muss man nur wenige Register beschreiben weil 
die Register oft sinnvolle/passende Defaultwerte haben. Das müsstest du 
nachgucken. Dann ist auch zu rüfen ob du die Registerwerte selber 
herausfinden musst, oder ob dir da eine Software helfen kann. Da gibt es 
das Sigmastudio 
https://www.analog.com/en/design-center/evaluation-hardware-and-software/software/ss_sigst_02.html#software-overview 
das kenne ich nicht. Aber vielleicht kannst du da den Codec auswählen, 
die Einstellungen machen die du haben willst, also Ausgeben/Einlesen und 
dann wirft dir das den Inhalt der Register aus den du in die Register 
schreiben musst. Ich weiß nicht ob das so ist, würde ich aber mal 
angucken.

Es kann auch sein, dass alle Register schon sinnvolle Defaultwerte 
haben, dann würde es reichen
ADC_SDATA/GPIO1
DAC_SDATA/GPIO0
LRCLK/GPIO3 und
BCLK/GPIO2
zu bespaßen. Also über ADC_SDATA/GPIO1 kommt das Audio vom Codec und 
über DAC_SDATA/GPIO0 schickst du Audiowerte zum Codec. LRCLK/GPIO3 sagt 
dir ob das links oder rechts ist und BCLK/GPIO2 ist der Takt. MCLK muss 
natürlich auch anliegen.

Zitat Digilent:
------------------------------------------------------
At the very least an audio-aware FPGA design should do the following:

Provide MCLK for the audio codec.
Use an I2C master controller to configure the core clocking, sample 
rates, serial interface format and audio path.
Send or receive audio samples over the serial audio data channel for 
playback or record.

More advanced users might want to try additional features of the 
ADAU1761. For example, the on-chip SigmaDSP core can be programmed to do 
user-defined digital signal processing.
------------------------------------------------------

Aber ja, andere Codecs sind da vielleicht leichter zu verwenden wenn du 
die vielen Features nicht brauchst. Ich würde also mal versuchen selbst 
eine passende Hardwarebeschreibung zu bauen um dem Codec Abtastwerte zu 
entlocken. Für einen ersten Test kannst du die Abtastwerte ja als PWM 
auf einem IO Pin ausgeben und glätten. Dann siehst du zumindest ob Audio 
ankommt auch wenn es nicht ideal klingt.

von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

Zu dem Filter und der Simulation:

Im Anhang ist ein Bildchen wie das im Filterdesigner aussieht, bei dir 
wäre das dann Matlab. Dann ein Bildchen der Simulation. Die dauert hier 
lange (lange Zeit die simuliert werden muss) weil ich die Samplerate auf 
44.1 kHz gesetzt habe. Der Xilinx IP braucht dann nur einen 
Multiplizierer bei den 256 Filterkoeffizienten.
Ausserdem sieht das nicht sehr toll aus, weil hier nur 64 Werte für die 
Sinustabelle verwendet werden. Das reduziert aber die Simulationszeit.

Für eine schnellere Simulation müsste man mit einer höheren Frequenz 
Abtastwerte an das Filter übergeben, dau aber auch dem Filter die höhere 
Abtastfrequenz mitteilen. Dann würde das Filter mit mehr Multiplizierern 
gebaut werden.

In dem ZIP ist das Vivado Projekt drinnen. Da kommen die Abtastwerte von 
der Testbench und gehen gefiltert in die Testbench. Du müsstest das 
ersetzen durch eine Beschreibung um die Werte vom Codec einzulesen und 
über den Codec oder/und einen UART auszugeben.

Aber auch das kann man erst mal simulieren.

von Burkhard K. (buks)


Lesenswert?

Ich hab diesen Thread seit einiger Zeit mitgelesen, mir das Datenblatt 
vom ADA1761 angeschaut - und gedacht: oha - was für ein Overhead, um ein 
paar Audio-Daten auszugeben. Der CS4344 (der Codec auf dem PmodI2S) 
dagegen ist ein "schlanker" IC, ohne I2C-Konfiguration und mit frei 
wählbarer MCLK bzw. Datenrate bis 200 kSp/s. Von daher halte ich die 
Idee erstmal mit einem externen Pmod weiterzumachen durchaus für 
sinnvoll.

Mein I2S-Interface für den CS4344 habe ich vor ein paar Jahren 
entwickelt und fand das ziemlich straight-forward. Extrem hilfreich war 
dabei, dass ich meinen Logic-Analysator direkt an die Pmod-Pins hängen 
konnte - bei dem intern verbauten AD1761 müsstest Du dazu den 
"Debug-Output" herausführen.

Just my 2 cent.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Naja, der Codec auf dem Board kann auch anscheinend laut Digilent ohne 
Konfiguration betrieben werden. Dann kann man über den seriellen Port 
sowohl Daten vom ADC abholen als auch über den DAC ausgeben.

Aber ja, das Datenblatt 
https://www.analog.com/media/en/technical-documentation/data-sheets/ADAU1761.pdf 
ist eher schlecht.

Auf Seite 42/43 ist der serielle Port beschrieben:

Register R15 and Register R16 (serial port control registers, Address 
0x4015 and Address 0x4016) allow control of clock polarity and data 
input modes. The valid data formats are I2S, left-justified, 
right-justified (24-/20-/18-/16-bit), and TDM.

Tja aber wo stelle ich das in den Registern ein? Da finde ich diese 
Einstellung zu den Input Modes nicht.

Dann ist in Register 58 und 59 jeweils der ADC zum DSP und der DSP zum 
DAC geroutet. Macht ja auch Sinn wenn man den DSP verwenden möchte, aber 
der DSP ist per Default aus R61/R62. Bekommt also der DAC die Daten vom 
seriellen Port oder muss ich dazu entweder den DSP einschalten oder das 
Routing umkonfigurieren? Und bekommt der serielle Post das Audio vom ADC 
ohne aktiven DSP und mit Defaultrouting?

Also ja, viel Overhead, aber vielleicht trotzdem einfach zu benutzen.

Digilent hat dieses Demoprojekt:
https://reference.digilentinc.com/learn/programmable-logic/tutorials/genesys-2-dma-audio-demo/start

Da kann man auf Knopfdruck Audio aufnehmen und über UART zum PC 
schicken. Anders herum kann man vom PC über UART Audio ausgeben.
Für mich sieht das reichlich komplex aus. Ja, das mag man so machen mit 
AXI hier und uBlaze dort, aber das ginge auch einfacher.
Die Beschreibung für den seriellen Port ist hier:
https://github.com/Digilent/Genesys-2-DMA/blob/master/repo/local/ip/d_axi_i2s_audio_v2_0/src/i2s_ctl.vhd

Ob der Codec zusätzlich noch konfiguriert wird weiß ich nicht, könnte 
aber gut sein.

Burkhard K. schrieb:
> Extrem hilfreich war
> dabei, dass ich meinen Logic-Analysator direkt an die Pmod-Pins hängen
> konnte - bei dem intern verbauten AD1761 müsstest Du dazu den
> "Debug-Output" herausführen.

Das ist vielleicht gar nicht so verkehrt die Idee. Er könnte mal diese 
Demo von Digilent auf das Board laden und gucken ob das 
Konfiguratonsinterface für den Codec überhaupt verwendet wird. Also Oszi 
direkt beim Start mit Normaltrigger ranhalten an SCL/CCLK Pin 32.

Und wenn sich da nix tut, dann kann er sich noch den seriellen Port, die 
Pins 26, ... , 29 angucken. Da sieht er wie Audio übertragen wird.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

Vielen Dank euch!

@Gustl Vielen Dank für die Simulationsbilder. Jetzt weiß ich was du 
gemeint hast. :) Ich steh ab und zu auch ein bisschen aufm Schlauch.

>>Ich würde das "Framework" nicht nutzen, das sieht nicht wirklich gut
aus.
War wohl etwas naiv zu glauben, dass es nach ein paar Anpassungen 
einwandfrei funktioniert. Eigentlich schreibe ich auch immer gerne 
meinen eigenen Code. Allerdings stammt das überarbeitete Framework auch 
von einer Uni und ich dachte das wird schon funktionieren. 
Höchstwahrscheinlich funktioniert es auch, nur hab ich scheinbar 
irgendwo etwas falsch gemacht, auch wenn der Fehler nicht ganz 
offensichtlich ist (zumindest für mich).

>>Aber ja, andere Codecs sind da vielleicht leichter zu verwenden wenn du
die vielen Features nicht brauchst.
Genau. Wenn ich doch noch den ADAU verwenden will, brauche ich erst mal 
wieder einen I2C Mastercontroller. Auf Digikey gibt es an sich ein 
Modul, welches einen guten Eindruck macht. Das wollte ich damals schon 
fast verwenden, aber dann bin ich auf das Zedboard-Framework gestoßen. 
Das Datenblatt des ADAUs ist teilweise echt sehr unpräzise formuliert. 
Ich habe es schon mehrmals durchgelesen und trotzdem sind mir noch 
einige Dinge unklar... Die Initialisierung über Sigma Studio kann 
funktionieren, oder eben wieder Probleme machen. Ich hätte gerne erst 
mal so wenige ? wie möglich.

Was ich aktuell möchte, ist eine schnelle Lösung, die meine 
Anforderungen erfüllt. Das PMOD-Modul scheint hierbei die einfachste 
Möglichkeit zu sein. Was mir auch sehr gefällt ist die Option, dieses 
Modul auch auf anderen Boards zu verwenden.

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Das PMOD-Modul scheint hierbei die einfachste
> Möglichkeit zu sein. Was mir auch sehr gefällt ist die Option, dieses
> Modul auch auf anderen Boards zu verwenden.

Ah! Ich dachte du wärst auf dieses eine Board festgelegt. Klar wenn du 
das nicht bist, dann nimm das PMOD Teil. Dazu gibt es bestimmt auch Code 
bei Digilent.

Mario A. schrieb:
> Wenn ich doch noch den ADAU verwenden will, brauche ich erst mal
> wieder einen I2C Mastercontroller.

Vielleicht nein. Vielleicht reicht es aus diesen seriellen Port zu 
bedienen mit den 4 Signalen + MCLK.

von Mario A. (itse_me_mario)


Lesenswert?

>> Vielleicht nein. Vielleicht reicht es aus diesen seriellen Port zu
bedienen mit den 4 Signalen + MCLK.
Naja spätestens wenn ich eine andere Abtastrate einstellen will, komm 
ich nicht drum herum, denke ich.

Ich kann das Board hernehmen, muss es aber nicht. Das PMOD-Modul werde 
ich erstmal an das Genesys 2 anbinden, da es einfach mehr DSP-Slices hat 
für die spätere Performancemessung.

Alles klar. ;) Kein Problem. Ich hoffe, dass die Module diese Woche noch 
kommen.

von Burkhard K. (buks)


Angehängte Dateien:

Lesenswert?

Mario A. schrieb:
> Das PMOD-Modul werde
> ich erstmal an das Genesys 2 anbinden

Hallo Mario, ich hab mal meinen alten Code für den CS4344/PmodI2S 
angehängt. Ist schon eine Weile her und der Controller ist insofern 
speziell, als das ich zwischen 4 verschiedenen, nicht 
Standard-Samplerates umschalten kann (200/100/50/25 kSp/s per Signal 
"sr_sel").

Zu beachten ist natürlich, dass der Controller in einer eigenen 
Takt-Domäne läuft, d.h. die eingehenden Daten und umgekehrt die 
ausgehenden Statussignale (l_txd, r_txd) müssen in die jeweilige Domäne 
einsynchronisiert werden. In meiner damaligen Anwendung habe ich das (in 
einem Wrapper) für die Daten mit einem asynchronen FiFo gemacht. 
Testdaten lassen sich mit einer einfachen Sinus-LUT oder einem NCO 
generieren.

Ist wie gesagt alter Code (den ich heute wohl anders schreiben würde) - 
und noch mit ISE entwickelt - d.h. keine AXI-Anbindung.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

Hallo,
danke für deine Dateien! Ich werde sie mir die Tage mal ansehen.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Soo ein I2S-Interface habe ich heute eingebunden und getestet: Es 
scheint auf den ersten Blick zu funktionieren. Ich habe das Interface 
aus dem ursprünglichen Framework (i2s_data_interface) verwendet. Wenn 
ich die Hardware habe und es funktioniert (oder nicht :D) dann lade ich 
euch den Code hoch.

Ich habe einen MCLK von 8,192 MHz, einen LRCK von 32 kHz und einen SCLK 
von 2,048 MHz verwendet. Dafür habe ich den MCLK mit dem Clocking Wizard 
erzeugt. Die anderen beiden Clocks habe ich davon abgeleitet (clock 
divider):

entity clock_divider_4 is
port(
            CLK_IN  : IN STD_LOGIC;
            RESET   : IN STD_LOGIC;
            CLK_OUT : OUT STD_LOGIC
     );
end clock_divider_4;

architecture Behavioral of clock_divider_4 is

begin

process(CLK_IN,RESET)
        VARIABLE COUNT : integer :=0;
        VARIABLE TEMP : std_logic := '0';
        begin
            if RESET ='1' then
                count := 0;
                temp := '0';
            elsif rising_edge(clk_in) then
                count := count+1;
                if count = 2 then -- count = 2 --> :4, 4 --> :8,
                                             8 --> : 16, ... 32 --> : 64
                    temp := not temp;
                    count := 0;
                end if;
            end if;
            clk_out <= temp;
    end process;

end Behavioral;

: Bearbeitet durch User
von Audiomann (Gast)


Lesenswert?

Gustl B. schrieb:
> so wie ich das
> verstanden habe möchtest du Audio vom ADA1761 bekommen, durch einen FIR
> Filter schicken und dann irgendwie weiterverarbeiten.

und so wie ich es verstanden habe, möchte er mit 16kHz arbeiten. Kann 
der ADC das überhaupt?`Oder muss man da nicht auch mit einer 
"offiziellen" Audiorate ran? Also z.B. 48000?

von Mario A. (itse_me_mario)


Lesenswert?

Ob 16 kHz möglich sind, ist mir auch noch nicht völlig klar. 32 kHz 
gehen auf jeden Fall (laut Datenblatt). Hierfür sind die Einstellungen 
für MCLK und SCLK gegeben. Wenn es jetzt 32 kHz werden ist es auch erst 
einmal kein Problem. Aber es wäre schon cool, wenn auch kleinere 
Abtastraten gehen würden.

: Bearbeitet durch User
von Mario A. (itse_me_mario)



Lesenswert?

Hallo zusammen,

heute ist der I2S-PMOD-Adapter gekommen! Mein Interface scheint zu 
funktionieren (laut Logic Analyzer und Kopfhörern). Wichtig war auch 
hier die Clocks "asynchron" zu setzen.

Über die Kopfhörer höre ich nun endlich die Musik vom IPod (allerdings 
verrauscht --> evtl. Sättigung wegen den 11 dB Offset im Frequenzgang). 
Die Frequenzgangmessung mit dem Analog Discovery liefert ein typisches 
tiefpassähnliches Verhalten.

Trotz Warning funktioniert der ILA.
Im Anhang befindet sich ein Screenshot ohne s_audio_l_in <= (others => 
'0'); und s_audio_r_in <= (others => '0'); und ein Screenshot mit.

Erst habe ich im Process die Signale s_audio_l_in und s_audio_r_in immer 
auf 0 gesetzt und mich gewundert, warum ich nichts höre... :P

process (s_clk_100_MHz)
    begin
        if (s_clk_100_MHz'event and s_clk_100_MHz = '1') then
            s_hphone_valid <= '0';
--            s_audio_l_in <= (others => '0');
--            s_audio_r_in <= (others => '0');

            if s_new_sample = '1' then
                s_counter <= s_counter + 1;
                s_hphone_valid <= '1';
                s_audio_l_in <= s_audio_l_out;
                s_audio_r_in <= s_audio_r_out;
            end if;

        end if;
    end process;

Ich muss noch rausfinden, wo dieser Offset im Frequenzgang herkommt. 
Wird ja vermutlich irgendwo im Datenblatt stehen.
Außerdem möchte ich testen, ob auch 16 kHz (Abtastrate) möglich sind. 
Vielen Dank nochmal an alle für die tolle Hilfe!

Als nächsten Schritt binde ich mein FIR-Filter ein.

Danach möchte ich den I2C Mastercontroller von Digikey verwenden, um den 
Codec des Zybo Z7020 zu initialisieren. Dieser ist nicht so umfangreich 
und man muss kaum etwas initialisieren.

Wenn das funktioniert, will ich den Codec auf dem Genesys 2 zum laufen 
bringen.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hier noch das XDC-File.

von Duke Scarring (Gast)


Lesenswert?

Mario A. schrieb:
> Über die Kopfhörer höre ich nun endlich die Musik vom IPod (allerdings
> verrauscht --> evtl. Sättigung wegen den 11 dB Offset im Frequenzgang).
11 dB Offset? Eher 11 dB Verstärkung.
Du kannst ja das Eingangssignal mal durch vier teilen und gucken ob es 
dann passt.

> Trotz Warning funktioniert der ILA.
Manchmal macht einem der ILA das Timing kaputt, vielleicht kommen die 
Störungen daher.

>         if (s_clk_100_MHz'event and s_clk_100_MHz = '1') then
Wo lernt man denn so altmodisches VHDL?
Die Funktion rising_edge() gibt es meines Wissens seit VHDL'93...

> Ich muss noch rausfinden, wo dieser Offset im Frequenzgang herkommt.
Sind die Wortbreiten identisch?
Was zeigt das Analog Discovery an, wenn Du direkt den Eingang mit dem 
Ausgang verbindest?

Duke

von Mario A. (itse_me_mario)


Lesenswert?

>>11 dB Offset? Eher 11 dB Verstärkung. Du kannst ja das Eingangssignal mal durch 
vier teilen und gucken ob es dann passt.
Ja es ist aber ein 11 dB Offset zur 0 dB Linie. :) Natürlich passt es 
dann, wenn ich es manuell rausrechne, aber mich interessiert noch woher 
genau die Verstärkung kommt. Das wird sicher im Datenblatt zu finden 
sein. Ist auch erst mal egal.

>>Manchmal macht einem der ILA das Timing kaputt, vielleicht kommen die
Störungen daher.
Gerade habe ich es so implementiert, dass keine 11 dB Verstärkung 
auftritt und ohne ILA. Leider ist das Signal immer noch verrauscht. 
Trotzdem danke für den Tipp.

mit
s_audio_l_in <= std_logic_vector(shift_right(unsigned(s_audio_l_out, 
2));
s_audio_r_in <= std_logic_vector(shift_right(unsigned(s_audio_r_out, 
2));
bleiben dann noch ca. -0.8 dB im Frequenzgang.

>>Wo lernt man denn so altmodisches VHDL?
Old but gold. :P Ist doch eh völlig egal, welche Funktion ich dafür 
verwende. Sollte jedem bekannt sein.

>>Sind die Wortbreiten identisch?
Die Wortbreiten sollten identisch sein.
>>Was zeigt das Analog Discovery an, wenn Du direkt den Eingang mit dem
Ausgang verbindest?
0 dB.

Danke dir,
Gruß Mario

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Ich bin mir noch nicht ganz sicher, ob der Frequenzgang wirklich passt. 
Laut Datenblatt sollte der Frequenzgang nicht schon so früh abfallen 
(siehe Screenshot_TT). Als ich mal mein FIR-Filter eingebunden habe, kam 
seltsamerweise derselbe Frequenzgang raus.

Ich werde nochmal die ganzen Taktraten und Anschlüsse prüfen. Vielleicht 
verwende ich das I2S-PMOD Interface auch mal im Master-Mode. Heute habe 
ich einen Zwischenstecker für das PMOD-I2S bekommen und kann so die 
Signale mit dem Analog Discovery messen. Allerdings schaffe ich es erst 
wieder am Mittwoch weiterzuarbeiten.

Gruß Mario

: Bearbeitet durch User
von weltbester FPGA-Pongo (Gast)


Lesenswert?

Mario A. schrieb:
>>>Manchmal macht einem der ILA das Timing kaputt, vielleicht kommen die
> Störungen daher.
> Gerade habe ich es so implementiert, dass keine 11 dB Verstärkung
> auftritt und ohne ILA.

Wenn das Timing getroffen wird, macht der ILA keinen Ärger. Wenn der ILA 
aber drin ist und etwas nicht geht, dann fehlen da constraints.

von weltbester FPGA-Pongo (Gast)


Lesenswert?

ach ja, an dem ADU Interface muss noch gearbeitet werden, meine ich :-) 
Bissl wenig drinne ...

von Mario A. (itse_me_mario)



Lesenswert?

Hi,
danke für deine Antwort! Ich arbeite gerade an einer Testbench für das 
Audio-Interface. Ich habe mein FIR-Filter dafür schon eingebunden. 
Irgendetwas stimmt auf jeden Fall noch nicht. Ich habe ein paar 
verschiedene Filter getestet und manchmal stimmt der Frequenzgang so in 
etwa... :P Mal sehen, ob ich den Fehler finde.

>>ach ja, an dem ADU Interface muss noch gearbeitet werden, meine ich
Meinst du da etwas spezielles?

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Ich lade nun mal die Ergebnisse des Talkthroughs hoch. Ich habe 
i2s_data_interface.vhd noch angepasst (Grund: siehe Timing_neu):
Hierfür wurde
if i2s_lr = '1' and i2s_lr_last = '0' then
in
if i2s_lr = '0' and i2s_lr_last = '1' then
geändert.

Die Eingangsdaten s_AD_SDOUT für beide Channels habe ich simuliert (1, 
2, 3, 4). Ich denke am Ausgang s_DA_SDIN müsste der Wert um einen SCLK 
früher auf 1 gesetzt werden. Vllt liegt das aber auch an meiner 
Simulation. Wenn man i2s_d_out <= sr_out(sr_out'high) in i2s_d_out <= 
sr_out(62) ändert, wird der Wert 1 SCLK früher ausgegeben.

Mein Filter (welches in diesem Fall auskommentiert ist) arbeitet mit 16 
Bit Daten (den 16 MSBs des Audio-Signals). Deshalb sind 16 Bit Signale 
enthalten. Nach dem Filtern sehe ich mir das Vorzeichen an und führe 
dann eine Vorzeichenerweiterung aus. D.h. die 16 LSBs des Audiosignals 
enthalten das Filterergebnis und die Bits 23-17 enthalten lediglich das 
VZ.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich stehe gerade auf der Leitung. Vllt kann mir ja jemand helfen.

Irgendwie erhalten die Werte in meinem Array a_sop (Sum of Products) 
zwischenzeitlich falsche Werte (siehe FehlerhafteWerte.png und 
Zoom.png).

Eigentlich sollte es so aussehen wie in Richtig.png.

Irgendetwas stimmt da mit dem Timing nicht und ich komm gerade nicht 
drauf was falsch ist... Der Screenshot Richtig.png stammt aus der 
Testbench des FIR-Filters allein (vorheriges Projekt, welches 
funktioniert). Vom Prinzip her wird das Filter in gleicher Weise 
genutzt.

Wenn s_hphone_valid <= '1' ist werden die 16 MSB´s des Audiosignals in 
das parallele Datenregister (reg_par) geladen. Diese Daten werden von 
den einzelnen Multiply Adder Blöcken (für jeden Koeffizienten wird 1 
Block benötigt) verwendet.
Die Berechnung der Multiply Adder Blöcke wird mit s_ready_1 gestartet. 
Anschließend werden die 16 Bits des 48 Bit Ergebnisses in a_sop(0) (-> 
zus. Guard-Bits) extrahiert und wieder über ein paralleles Datenregister 
ausgegeben (wenn s_ready_2 = '1') ist.

Ich würde mich sehr freuen, wenn mir jemand helfen könnte.

Vielen Dank im Voraus!

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Irgendwie erhalten die Werte in meinem Array a_sop (Sum of Products)
> zwischenzeitlich falsche Werte (siehe FehlerhafteWerte.png und
> Zoom.png).

Nein, XXXXX bedeutet nicht falsche Werte, sondern ein Konflikt.

Verwendest du irgendwo in deinem Design einen FIFO von Xilinx? Oder 
sonst Xilinx IP? Wenn ja: Die (also mache der IPs) brauchen einen Reset 
am Anfang. Ja, für die Simulation. Auf der Hardware laufen die auch ohne 
Reset wunderbar.

von Mario A. (itse_me_mario)


Lesenswert?

RTLFIRFilter.png veranschaulicht nochmal das eben beschriebene. Den 10 
MHz Process für die Erzeugung von s_fs_tic_16_kHz gibt es nicht mehr. 
Anstelle von s_fs_tic_16_kHz wird nun hphone_valid verwendet.

>>Nein, XXXXX bedeutet nicht falsche Werte, sondern ein Konflikt.
Danke Gustl!
Ja ich verwende die Multiply Adder Blöcke, den Clocking Wizard, den ILA 
und Block Memory Generator.

Hm das ist aber seltsam, weil ich dieselben IPs auch in meinem 
FIR-Projekt verwendet habe und da scheint alles zu funktionieren... Nur 
der ILA ist neu.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Du kannst in der Simulation auf das Signal klicken, Rechtsklick und dann 
Report Drivers. Da siehst du die verschiedenen Treiber die im Konflikt 
sind. Guck dir das mal an.

Mario A. schrieb:
> Multiply Adder Blöcke, den Clocking Wizard, den ILA
> und Block Memory Generator.

Also der Clocking IP braucht einen Reset für die Simulation, aber nur 
wenn du den Takt an dessen Eingang änderst.
Den ILA habe ich noch nie verwendet weil wenn es in der Simulation läuft 
und das Timing passt, dann läuft es auch auf der Hardware.
Der BRAM braucht keinen Reset.

von Mario A. (itse_me_mario)



Lesenswert?

Hallo Gustl,

danke für den Tipp. In meinem anderen Projekt hatte der Clocking Wizard 
tatsächlich einen Reset. Den habe ich nun auch hinzugefügt. Leider sieht 
die Simulation noch immer gleich aus...

Report Drivers hat ergeben:
: Driver Encrypted process: no data available
  0 : Net /tb_AudioInterface/uut/FIR_Left_Channel/a_sop[0][46]
     : Driver Encrypted process: no data available
  0 : Net /tb_AudioInterface/uut/FIR_Left_Channel/a_sop[0][45]
     : Driver Encrypted process: no data available
  0 : Net /tb_AudioInterface/uut/FIR_Left_Channel/a_sop[0][44]
... (siehe Anhang)

Eigentlich müssten die Daten anlegen (siehe FehlerhafteWerte_neu2)...

Theoretisch hätte der Block Memory Generator noch einen Reset, aber beim 
anderen Projekt hat es auch so funktioniert.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Jetzt hab ich nochmal was versucht... Aber das Ergebnis ist wieder 
dasselbe.

Außerdem habe ich nochmal die funktionierende FIRFilter.vhd-Datei 
eingebunden und lediglich die Signale der Parallelregister und der FSM 
entsprechend angepasst. Hat nichts gebracht.

Zur Übersichtlichkeit habe ich mal das RTL von AudioInterface angehängt.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Nun, ich verstehe das nicht ganz. Du hast einen Strom aus Abtastwerten. 
Die fütterst du in den FIR und bekommst dahinter wieder einen Strom aus 
gefilterten Abtastwerten. Ich verstehe nicht wieso das Format von a_sop 
2D ist und nicht 1D. Ich hätte da einfach nur einen SLV erwartet aber 
kein Array aus SLVs. Was ist das genau und warum hast du das Array?

Sinnvoll wären auch Screenshots deiner Einstellungen im FIR IP Compiler. 
Oder hast du den FIR selbst geschrieben?

Ach so, ich finde a_sop nicht in deinem Überblick-Bildchen, ist das also 
ein internes Signal im FIR?

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

>>Was ist das genau und warum hast du das Array?
Da sind alle Werte des Filters enthalten (für jeden Koeffizienten 1 
Wert). Dies ist für die transponierte Direktform notwendig.

>>Sinnvoll wären auch Screenshots deiner Einstellungen im FIR IP Compiler.
>>Oder hast du den FIR selbst geschrieben?
Das FIR habe ich selbst geschrieben.

>>Ach so, ich finde a_sop nicht in deinem Überblick-Bildchen, ist das Signal also 
ein internes Signal im FIR?
Das Signal ist im FIRFilter-Modul. Ein einfaches Array.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Ah, OK. Dann teste den FIR doch erstmal ohne den Rest. Also Testbench 
schreiben, den FIR mit einem Sweep füttern und gucken was rauskommt.

Was macht der FIR bei dir noch alles? Sieht reichlich kompliziert aus 
...

Hier hatte ich FIR geschrieben:
Beitrag "FIR parallel und seriell braucht zu viele LUTs."

Mittlerweile nutze ich aber den FIR IP von Xilinx weil der sehr gut 
optimiert und weniger DSPs braucht.

von Mario A. (itse_me_mario)


Lesenswert?

Hi danke für deine Hilfe,
der Screenshot Richtig.png bezieht sich auf das FIR-Filter alleine (also 
vorheriges Projekt). Ich füttere das Filter mit genau denselben Werten. 
An sich sollte es also funktionieren.

>>Mittlerweile nutze ich aber den FIR IP von Xilinx weil der sehr gut
optimiert und weniger DSPs braucht.
Das wäre später definitiv auch eine Option. Mit AXI4-Anbindung quasi, 
oder?

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Ich füttere das Filter mit genau denselben Werten.
> An sich sollte es also funktionieren.

Das mag sein, aber er funktioniert ja nicht wie er soll. Ist im FIR ein 
Xilinx IP der einen Reset brauchen könnte?

Mario A. schrieb:
> Mit AXI4-Anbindung quasi,
> oder?

Das ist kein richtiges AXI.
1
component AD7960_FIR is Port(
2
    aclk: in std_logic;
3
    s_axis_data_tvalid: in std_logic;
4
    s_axis_data_tready: out std_logic;
5
    s_axis_data_tdata: in std_logic_vector(23 downto 0);
6
    m_axis_data_tvalid: out std_logic;
7
    m_axis_data_tdata: out std_logic_vector(15 downto 0));
8
end component;

Das legt man einen Abtastwert an und setzt ein Bit wenn der valide ist 
und auf der anderen Seite bekommt man einen gefilterten Wert und ein Bit 
das sagt wann der vailde ist.

von Mario A. (itse_me_mario)


Lesenswert?

>>Was macht der FIR bei dir noch alles? Sieht reichlich kompliziert aus
...
Als Eingang werden 16 Bit Daten verwendet (Q1.15: 1 VZ Bit und 15 für 
den Nachkommaanteil). Die Koeffizienten sind ebenfalls 16 Bits groß 
(Q1.15). Das Produkt zweier Q1.15 Zahlen ergibt eine Q2.30 Zahl (2 VZ 
Bits und 30 Bits für den Nachkommaanteil).
Das Array a_sop enthält zus. Guard-Bits falls zwischenzeitlich ein 
Überlauf auftreten sollte beim MAC-Befehl.
Anschließend wird das Ergebnis noch überprüft (ob es im gültigen 
Wertebereich liegt). Wenn das Ergebnis zu groß oder zu klein ist wird es 
gesättigt. Abschließend wird das Filterergebnis als Q1.15 Zahl wieder 
ausgegeben.

von Mario A. (itse_me_mario)


Lesenswert?

>>Das mag sein, aber er funktioniert ja nicht wie er soll. Ist im FIR ein
Xilinx IP der einen Reset brauchen könnte?

Das vorherige Projekt schon. Nur nach den kleinen Änderungen und dem 
Einfügen in das AudioInterface Projekt nicht mehr. Aber ich verstehe 
einfach nicht warum. Woher kommen diese XXXX...XX es liegt immer ein 
gültiger Wert an.

Also Clocking Wizard, Block Memory Generator und Multiply Adder sind 
enthalten. Der Clocking Wizard hat mittlerweile einen Reset bekommen.

Soll ich dem Block Memory Generator auch einen Reset verpassen? Im 
FIR-Projekt hat es auch ohne funktioniert.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Hm, ich verstehe das Schaltbild nicht.

Aber ganz grob: Du machst da was mit Audio, also sehr niedrige Frequenz. 
Da reicht dann eigentlich ein Multiplizierer. Oder bei dir 
brd_clk/Sample_clk ergibt die Anzahl dder maximal möglichen 
Koeffizienten die mit einem DSP möglich sind. Das läuft dann nicht 
parallel sondern seriell. So eine Beschreibung wird recht einfach ...

Aber gut zu den XXXXXX, das ist ein Konflikt, also irgendwo wird dein 
Array von mehreren Treibern zeitgleich beschrieben.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Ich habe gerade für s_filter_input_data_l und s_filter_input_data_r 
konstante Werte angegeben (0x00FF). Das Phänomen bei a_sop tritt dann 
nicht mehr auf. Dann wirds ja doch was mit den beiden Signalen (welche 
als Eingang für das FIR dienen) zu tun haben.  Nur was? :D

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Also Clocking Wizard, Block Memory Generator und Multiply Adder sind
> enthalten. Der Clocking Wizard hat mittlerweile einen Reset bekommen.
>
> Soll ich dem Block Memory Generator auch einen Reset verpassen? Im
> FIR-Projekt hat es auch ohne funktioniert.

Huiuiui, das ist ziemlich viel Zeug für einen einfachen FIR.
Der Clock und BRAM brauchen eigentlich für die Simulation keinen Reset - 
ausser du änderst den Eingangstakt vom Clock IP.
Bei Multiply Adder habe ich keine Ahnung.

Du kannst Multiplikationen auch einfach als * schreiben und BRAM als 
Array in VHDL, das sind beides IPs die ich nicht oft verwende.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

>>Du machst da was mit Audio, also sehr niedrige Frequenz. Da reicht dann 
eigentlich ein Multiplizierer.
Ja später soll das auch für höhere Frequenzen dienen. Maximale 
Ausnutzung der Parallelität ist eigtl. das Ziel, damit die Berechnung 
schnellstmöglich erfolgen kann.

Ich würds dir auch gerne mal per Screensharing zeigen, wenn Interesse 
besteht. :D

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Gut, also schnellst möglich ist so eine Sache, oft genügt es wenn es 
einfach rechtzeitig fertig wird. Noch schneller bringt dann nix.
In dem Xilinx IP kann man das alles einstellen. Aber auch selber zu Fuß 
mit relativ wenig VHDL geht das wunderbar. Man kann auch schöne 
Mischformen aus parallel und seriell schreiben.

Mario A. schrieb:
> Ich würds dir auch gerne mal per Screensharing zeigen, wenn Interesse
> besteht. :D

Sowas habe ich hier nicht. Aber du kannst das doch schön simulieren. 
Hier https://www.mikrocontroller.net/attachment/highlight/427492 ist 
eine Testbench die einen Sweep anlegt.

von Mario A. (itse_me_mario)


Lesenswert?

Wenn ich das alte FIR-Projekt und das aktuelle Projekt mit konstanten 
Werten (0x00FF) versorge, erhalte ich dasselbe Ergebnis. Wenn jetzt noch 
die XXX..XX verschwinden würden, müsste das Filter eigentlich endlich 
funktionieren.

Danke für den Tipp mit der Testbench für den Filter Block aus dem IP 
Catalog.

>>Sowas habe ich hier nicht.
Für Screensharing über Zoom bräuchtest du auch nichts. :)

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Für Screensharing über Zoom bräuchtest du auch nichts. :)

Stimmt, aber ich finde das bringt nix. Ich bin da auch nicht Profi genug 
um anderer Leute Code zu verstehen.

Mario A. schrieb:
> Danke für den Tipp mit der Testbench für den Filter Block aus dem IP
> Catalog.

Da kannst du auch deinen FIR mit testen, musst du halt etwas anpassen. 
So wie ich dein FIR Blockschaltbild verstanden habe hat der sowieso also 
Eingänge den Takt und Daten und als Ausgang die Daten.
Für mein Verständnis fehlen da die Valid oder Ready Signale. Oder kommt 
da wirklich mit jedem Takt von brd_clk ein neuer Abtastwert?
Also ja, das kann man schon so machen aber so ein paralleles Design 
macht man eigentlich nur aus Not, denn wenn der FIR in einem Takt fertig 
sein muss, dann werden da viele DSPs verwendet. Wenn du aber Audio oder 
sonst was langsames feilterst, dann hast du für jeden Abtastwert viele 
Takte Zeit. Da würde sich dann eine serielle Form oder wenn die Anzahl 
der Takte kleiner als die Zahl der Koeffizienten ist eine Mischform 
anbieten.

Im oben verlinkten Thread filtert mein FIR ein Signal mit 5 MHz 
Abtastrate. Dazu wird ein Systemtakt von 100 MHz verwendet. Bedeutet ich 
habe 20 Takte je Abtastwert. Rein Seriell könnten da maximal 20 
Koeffizienten verwendet werden (ohne Beachtung der Symmetrie). Ich 
verwende 200 Koeffizienten und rechne in jedem Takt 10 Multiplikationen.
Das ist also ein Kompromiss. Rein parallel bräuchte das 200 DSPs, rein 
seriell 1 DSP aber es wären nur 20 Koeffizienten möglich. Mischform 
braucht 10 DSPs und kann 200 Koeffizienten.

Wenn dein Audio mit z. B. 44.1 kHz ankommt und deine brd_clk 100 MHz 
hat, dann bleiben dir 100M/44.1k grob 2267 Takte je Abtastwert. Du 
könntest also mit nur einem DSP im FPGA einen FIR mit 2267 Koeffizienten 
rechnen lassen. Ganz ohne Mischform oder sonstige Parallelität, rein 
seriell.

von Mario A. (itse_me_mario)


Lesenswert?

Ja wenn ich DSP-Slices einsparen wollte würde ich es auch anders machen. 
Aber aktuell möchte ich eine komplett parallele Berechnung (unabhängig 
von der Abtastrate).

Eine Testbench habe ich ja. Das einzige Problem ist noch, dass beim 
neuen Projekt dieses XXX...XX bei a_sop vorkommt.

von Mario A. (itse_me_mario)


Lesenswert?

>>So wie ich dein FIR Blockschaltbild verstanden habe hat der sowieso also
Eingänge den Takt und Daten und als Ausgang die Daten.

Genau.
Eingänge:
- 16 Bit (ungefilterte) Daten
- 100 MHz Clock
- Signal für "Daten sind verfügbar" (hphone_valid)

Ausgang:
- gefilterter Wert

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Eine Testbench habe ich ja. Das einzige Problem ist noch, dass beim
> neuen Projekt dieses XXX...XX bei a_sop vorkommt.

Gut, aber da hilft es in der Testbench herumzustochern. So wie das 
aussieht wird das Signal mit den XXXX nur von den Multiply Addern 
geschrieben.
Tja ... da wäre mal spannend die wie konfiguriert sind.
Sonst kannst du die IP MACs mal durch einfache Multiplikation * 
ersetzen. Wenn das möglich ist. Oder nutzt du z. B. Pipelining bei den 
MACs?

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Einstellungen habe ich dir angehängt. Denke aber dass das passt.

Hm fraglich ob´s an der Testbench liegt. Die richtigen Werte liegen an 
(an s_AD_SDOUT) und die entsprechenden std_logic_vectors für die 
Berechnung werden erzeugt.

Ich hab noch ein anderes Filter (in welchem ich die Berechnung in einem 
Process mit einfachen Multiplikationen durchführe). Ich denk ich versuch 
das mal einzubinden. Vivado generiert daraus auch eine parallele 
Filterstruktur. Sollte es wirklich an den Multiply Addern liegen wird 
mans hoffentlich sehen.

von Mario A. (itse_me_mario)


Lesenswert?

@Gustl
Ich hab gerade die Testbench angepasst. Du hattest recht, da war was 
"falsch".
Ich habe nicht sofort meine AD_SDOUTs gesetzt. Erst einen LRCK später. 
Dadurch hol ich mir wohl einen ungültigen Wert ins Array a_sop...
Also die Simulation läuft jetzt. Bin gespannt ob´s auch auf dem Board 
funktioniert. Danke dir schon mal! :)

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Also da die Testbench funktioniert habe ich nun den Bitstream generiert.

Ich habe die Signale direkt mit einem Analog Discovery gemessen. Die 
Clocks (Master Clock, Serial Clock, Left/Right Clock) kann ich messen. 
Die seriellen Daten (DA_SDIN und AD_SDOUT) zeigt es mir allerdings nicht 
an.

In meiner Testbench (für die Simulation ob das Filter funktioniert) habe 
ich für AD_SDOUT feste Werte vorgegeben, da ich ja keinen Eingang von 
außen habe (in der Simulation). DA_SDIN wird dann auch generiert.

Vllt stimmt dann etwas mit i2s_data_interface.vhd nicht. Ich denke da 
muss evtl. noch was rein (z. B. was aus audio_top.vhd aus dem 
Originalframework von Stefan Scholl).

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Auch das kannst und solltest du simulieren. Erstmal alle Blöcke einzeln 
und dann zusammen wenn es Fehler gibt. Wenn Signale von ausserhalb des 
FPGAs benötigt werden, dann erzeugt man sich diese in der Testbench. 
Also schreib dir z. B. eine Quelle die einen Sinus sweept und den dann 
über I2S ausgibt. Das fütterst du dann in deinen I2S Block als Eingang.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

So sieht das Ergebnis der Testbench aus. Sollte soweit eigentlich 
passen.

Einbindung des PMOD-Moduls 
(https://reference.digilentinc.com/reference/pmod/pmodi2s2/reference-manual) 
wie folgt:
    DA_LRCK     : OUT STD_LOGIC;
    DA_MCLK     : OUT STD_LOGIC;
    DA_SDIN     : OUT STD_LOGIC;
    DA_SCLK     : OUT STD_LOGIC;

    AD_LRCK     : OUT STD_LOGIC;
    AD_MCLK     : OUT STD_LOGIC;
    AD_SDOUT    : IN STD_LOGIC;
    AD_SCLK     : OUT STD_LOGIC


--*************************alle PMODS zuweisen*******
    DA_LRCK     <= s_clk_32_kHz_LRCK;
    DA_MCLK     <= s_clk_8_192_MHz_MCLK;
    DA_SCLK     <= s_clk_2_048_MHz_SCLK;
    ------------------------------------
    AD_LRCK     <= s_clk_32_kHz_LRCK;
    AD_MCLK     <= s_clk_8_192_MHz_MCLK;
    AD_SCLK     <= s_clk_2_048_MHz_SCLK;
--***************************************************

i2s_interface : i2s_data_interface
    port map (
                   clk           => s_clk_8_192_MHz_MCLK,
                   audio_l_in    => s_audio_l_in,
                   audio_r_in    => s_audio_r_in,
                   audio_l_out   => s_audio_l_out,
                   audio_r_out   => s_audio_r_out,
                   new_sample    => s_new_sample,
                   i2s_bclk      => s_clk_2_048_MHz_SCLK,
                   i2s_d_out     => DA_SDIN,
                   i2s_d_in      => AD_SDOUT,
                   i2s_lr        => s_clk_32_kHz_LRCK
             );

Auszug meines XDCs:
## PMOD Header JA
set_property -dict {PACKAGE_PIN U28 IOSTANDARD LVCMOS33} [get_ports { 
DA_LRCK }]; #IO_L13N_T2_MRCC_14 Sch=ja_n[1]
set_property -dict {PACKAGE_PIN U27 IOSTANDARD LVCMOS33} [get_ports { 
DA_MCLK }]; #IO_L13P_T2_MRCC_14 Sch=ja_p[1]
set_property -dict {PACKAGE_PIN T27 IOSTANDARD LVCMOS33} [get_ports { 
DA_SDIN }]; #IO_L12N_T1_MRCC_14 Sch=ja_n[2]
set_property -dict {PACKAGE_PIN T26 IOSTANDARD LVCMOS33} [get_ports { 
DA_SCLK }]; #IO_L12P_T1_MRCC_14 Sch=ja_p[2]
set_property -dict {PACKAGE_PIN T23 IOSTANDARD LVCMOS33} [get_ports { 
AD_LRCK }]; #IO_L5N_T0_D07_14 Sch=ja_n[3]
set_property -dict {PACKAGE_PIN T22 IOSTANDARD LVCMOS33} [get_ports { 
AD_MCLK }]; #IO_L5P_T0_D06_14 Sch=ja_p[3]
set_property -dict {PACKAGE_PIN T21 IOSTANDARD LVCMOS33} [get_ports { 
AD_SDOUT }]; #IO_L4N_T0_D05_14 Sch=ja_n[4]
set_property -dict {PACKAGE_PIN T20 IOSTANDARD LVCMOS33} [get_ports { 
AD_SCLK }]; #IO_L4P_T0_D04_14 Sch=ja_p[4]


Auszug Original XDC:
## PMOD Header JA
#set_property -dict { PACKAGE_PIN U28   IOSTANDARD LVCMOS33 } [get_ports 
{ ja[0] }]; #IO_L13N_T2_MRCC_14 Sch=ja_n[1]
#set_property -dict { PACKAGE_PIN U27   IOSTANDARD LVCMOS33 } [get_ports 
{ ja[1] }]; #IO_L13P_T2_MRCC_14 Sch=ja_p[1]
#set_property -dict { PACKAGE_PIN T27   IOSTANDARD LVCMOS33 } [get_ports 
{ ja[2] }]; #IO_L12N_T1_MRCC_14 Sch=ja_n[2]
#set_property -dict { PACKAGE_PIN T26   IOSTANDARD LVCMOS33 } [get_ports 
{ ja[3] }]; #IO_L12P_T1_MRCC_14 Sch=ja_p[2]
#set_property -dict { PACKAGE_PIN T23   IOSTANDARD LVCMOS33 } [get_ports 
{ ja[4] }]; #IO_L5N_T0_D07_14 Sch=ja_n[3]
#set_property -dict { PACKAGE_PIN T22   IOSTANDARD LVCMOS33 } [get_ports 
{ ja[5] }]; #IO_L5P_T0_D06_14 Sch=ja_p[3]
#set_property -dict { PACKAGE_PIN T21   IOSTANDARD LVCMOS33 } [get_ports 
{ ja[6] }]; #IO_L4N_T0_D05_14 Sch=ja_n[4]
#set_property -dict { PACKAGE_PIN T20   IOSTANDARD LVCMOS33 } [get_ports 
{ ja[7] }]; #IO_L4P_T0_D04_14 Sch=ja_p[4]


Beschreibung i2s:
i2s_data_interface wandelt die seriellen Daten (AD_SDOUT) in die 
entsprechenden std_logic_vectors (audio_l_out und audio_r_out) um.
Die 16 MSBs der beiden Vektoren dienen dann als Eingang für das Filter. 
Das gefilterte Ergebnis dient wiederum als Eingang für 
i2s_data_interface (audio_l_in, audio_r_in) und wird dort für die 
Ausgabe in einen seriellen Datenstrom umgewandelt.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

Ich habs auch mal mit

set_clock_groups -asynchronous -group [get_clocks *clk_out1*] -group 
[get_clocks *clk_out2*]
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 5 [current_design]

statt

set_clock_groups -asynchronous -group [get_clocks *clk_out1*] -group 
[get_clocks *clk_out2*]
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]

probiert. Aber bisher auch ohne Erfolg.

von Mario A. (itse_me_mario)


Lesenswert?

Ich hab ein Warning übersehen... Wenn ich
set_property CONFIG_VOLTAGE 5.0 [current_design] anstatt
set_property CONFIG_VOLTAGE 3.3 [current_design] schreibe, erhalte ich 
leider eine Warning.

Weiß jemand wie ich die Spannung an den Pins direkt auf 5V setze? 
Scheinbar reichen 3.3V nicht aus, siehe 
https://forum.digilentinc.com/topic/20297-pmodi2s2/#comment-56983.

"You will likely need a higher voltage for the Pmod I2S2; the Analog to 
Digital converter chip (CS5343) has a minimum recommended operating 
voltage of 3.1V, so it will likely not operate correctly when you are 
trying to debug/listen to incoming analog signals."

Eigentlich werden die PMODs ja mit 3.3V versorgt (LVCMOS33)... Oder 
verstehe ich die Lösung im Link falsch?

[Netlist 29-154] Cannot set property 'CONFIG_VOLTAGE' because incorrect 
value '5.0' specified. Expecting type 'enum' with possible values of 
',1.5,1.8,2.5,3.3'. 
["C:/Users/aim34424/Desktop/Gen2_PMOD_FIR2_5V/Genesys2ExternalAudioInter 
face.srcs/constrs_1/new/Genesys2Master.xdc":3]

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Ich bin gerade verwirrt... Mein Talkthrough (ohne Filter) funktioniert 
nur, wenn ich die Spannung fälschlicherweise auf 5V setze... Trotz 
Critical Warning...

Mit CONFIG_VOLTAGE 3.3, bzw. komplett ohne
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
funktioniert nichts. Wenn ich das Filter einbinde (in dem 
funktionierenden Design) funktioniert wieder nichts.

: Bearbeitet durch User
von Duke Scarring (Gast)


Lesenswert?

Üblicherweise wird die Spannung nicht im Constraint/Config-File 
einstellt, sondern an die Pins mit der Bankspannung angelegt.
Im Zweifelsfall schaut man in den Schaltplan, was der Entwickler da 
vorgesehen hat.
Im Constraint-File teilt man dem Tool nur mit, welche Spannung man genau 
angelegt hat. Das Tool kann dann prüfen, ob alle Pins und Buffer an 
dieser Bank auf die richtige Spannung eingestellt sind.

Duke

von Gustl B. (gustl_b)


Lesenswert?

Ja, die PMODs haben 3.3V oder 5V als Versorgungsspannung. Die konnte man 
bei älteren Digilent Boards mit Jumper umstellen.

Bei deinem PMOD geht es aber nicht um die Versorgung, sondern die 
Signale. Die kommen aus dem FPGA und haben bei logisch high 3.3V. Oder 
sollten haben. Das kannst du ja selber nach messen.

Mit den Constraints die du da schreibst änderst du nix an der Spannung, 
du teils dem Vivado nur mit welche Spannung am FPGA angeschlossen ist. 
Da solltest du den korrekten Wert schreiben, was der ist steht im 
Schaltplan.

von Mario A. (itse_me_mario)


Lesenswert?

Danke für eure Antworten!

Laut Schematic liegen 3.3V an den PMODs an. Im XDC wird das bei den Pins 
auch standardmäßig so angegeben.

>>sondern an die Pins mit der Bankspannung angelegt.
Aber ich kann jetzt quasi nicht einfach die Spannung auf 5V erhöhen 
(außer ich versorge die Module extern mit einer Spannungsquelle)?

>>Die konnte man bei älteren Digilent Boards mit Jumper umstellen.
Das kann gut sein! Ich finde jedenfalls nichts, womit ich die Spannung 
auf 5V umstellen kann.

>>Oder sollten haben. Das kannst du ja selber nach messen.
Leider habe ich aufgrund der aktuellen Lage nur mein Analog Discovery 
zur Verfügung... Aber das kann das glaube ich auch. Werde ich mal 
prüfen, danke.

Aber ihr geht nicht davon aus, dass es an der 3,3V Versorgung liegt?
Vielen Dank!

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Aber ich kann jetzt quasi nicht einfach die Spannung auf 5V erhöhen
> (außer ich versorge die Module extern mit einer Spannungsquelle)?

Richtig. Und auch das würde nichts bringen. Denn die Signale die vom 
FPGA zum PMOD gehen haben dann weiterhin die 3,3 V des FPGAs. Da aber 
dann die Signale vom PMOD zum Signal 5 V sind könntest du dir sogar die 
FPGA IOs schlachten.

Mario A. schrieb:
> Aber ihr geht nicht davon aus, dass es an der 3,3V Versorgung liegt?

Genau. Beide Bausteine auf der PMOD Platine sind für 3,3 V ausgelegt und 
sollten daher wunderbar funktionieren. Aber du kannst natürlich die 
Spannung nachmessen mit einem Multimeter.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

Danke! Ich war aufgrund es Links 
(https://forum.digilentinc.com/topic/20297-pmodi2s2/#comment-56983.) nur 
sehr skeptisch. :)

Ich versuchs dann mal weiter!

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Ich komme aktuell leider nicht weiter.
In der Testbench passt alles, aber auf der Hardware nicht. Wenn man sich 
die Signale mit dem ILA ansieht, fällt auf, dass 
filtered_output_r_16_bit und filtered_output_l_16_bit konstant bei 0 
bleiben. Deshalb wird auch kein serieller Datenstrom über DA_SDIN 
ausgegeben.

Ich habe auch mal versucht das Filter direkt einzubinden (ohne Auslagern 
in FIRFilter.vhd). Leider auch ohne Erfolg...

von Gustl B. (-gb-)


Lesenswert?

Da kann ich auch nicht weiterhelfen. Ohne das Filter funktioniert alles? 
Dann lade doch mal den Code für das Filter hoch. Oder noch besser: 
Ersetze das Filter durch eines das ganz sicher funktioniert, z. B. das 
von Xilinx.

von Mario A. (itse_me_mario)



Lesenswert?

Hallo Gustl,
danke für deine Antwort und Hilfe. Ich versuche morgen mal das Filter 
von Xilinx einzubinden.

Ich habe mittlerweile ziemlich viele verschiedene Versionen. Ich lade 
mal eine davon hoch.

Kurz zu den Dateien:

AudioInterface.vhd
-> Erzeugung der Clocks (LRCK, SCLK, MCLK, ...)
-> Versorgt Filter mit 16 Bit Daten und erhält 16 Bit Daten vom Filter

FIRFilter.vhd
-> erhält 16 Bit Daten
-> gibt ein 16 Bit Ergebnis zurück

i2s_data_interface.vhd
-> wandelt serielle Daten vom ADC in Signale um (audio_l_out, 
audio_r_out)
-> wandelt audio_l_in und audio_r_in in ein serielles Signal für die 
Ausgabe

tb_AudioInterface.vhd
-> Werte für AD_SDIN werden vorgegeben

Wenn in AudioInterface.vhd im Process
                s_audio_l_in <= s_audio_l_out;
                s_audio_r_in <= s_audio_r_out;
steht funktioniert zumindest Talkthrough.

Ich denke es ist irgendeine Kleinigkeit aber ich bin irgendwie blind 
dafür. An sich müssten audio_l_in und audio_r_in gesetzt werden... Wie 
auch in der Testbench...

von Mario A. (itse_me_mario)


Lesenswert?

Einen Fehler habe ich gefunden... Das dürfte so auch nicht auf der 
Hardware funktionieren.
In der Statemachine „fsm_fir_get_data.vhd“ taucht im Case-Statement eine 
„Rising-Edge“-Bedingung auf, die noch dazu nicht auf den Systemtakt 
taktet.

Hab das jetzt mal geändert und überprüfe das.

Neu:

entity fsm_fir_get_data is
port (
          ck              : in  STD_LOGIC;
          start_fs_tic_f  : in  STD_LOGIC;
          reg_ready_f     : OUT  STD_LOGIC := '0'
      );
end fsm_fir_get_data;

architecture arch_fsm_fir_get_data of fsm_fir_get_data is
signal    d_out_reg_ready_f  : STD_LOGIC ;
type states is (
                 s_idle,
                 s_read
               );
signal state,nxt_state    : states;
begin
calc_next_state: process (
                            state,start_fs_tic_f
                         )
begin
  case state is
    when s_idle => if (start_fs_tic_f='1') then
                         nxt_state <= s_read;
                   end if;

--******************************
--vorher: if (start_fs_tic_f='1' AND rising_edge(start_fs_tic_f) ) then
--nxt_state <= s_read;  end if;
--*****************************
      when s_read => nxt_state <= s_idle;
  end case;
end process calc_next_state;
-- Auscodierung Ausgaenge;
with nxt_state select
   d_out_reg_ready_f <=
      '1' when s_read,
      '0' when s_idle;
clkd:Process(ck)
Begin
    if rising_edge(ck) THEN
          state <= nxt_state ;
          reg_ready_f <= d_out_reg_ready_f;
    end if;
END PROCESS clkd;
end arch_fsm_fir_get_data;

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Nun ... ich bekomme das natürlich nicht ans Laufen weil die ganzen 
Xilinx IPs fehlen. Aber ich habe mir den Code mal angeguckt. Und da 
finde ich viele Kritikpunkte:

1. Viele Signale haben keinen Startwert. Was die Simulation da also 
macht ist vermutlich ein U.

2. start_fs_tic_f='1' AND rising_edge(start_fs_tic_f) mag ich nicht um 
eine steigende Flanke zu erkennen. rising_edge(start_fs_tic_f) sollte 
genügen.

3.
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.numeric_std.all;

Da genügt es die Letztere zu verwenden. Eigentlich reicht fast meistens:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

Und generell:
Du machst da ziemlich viele Baustellen gleichzeitig auf mit deinem 
Projekt. Koeffizienten aus ROM lesen, Filter verwenden, ...
Hast du diese einzelnen Komponenten denn schon mal unabhängig getestet? 
Wie wäre es mal mit einer Testbench nur für das FIR Filter? Und zwar 
nicht nur mit einem statischen Wert, sondern mit einem Sinus oder noch 
besser einen Sweep?

Mario A. schrieb:
> Wenn in AudioInterface.vhd im Process
>                 s_audio_l_in <= s_audio_l_out;
>                 s_audio_r_in <= s_audio_r_out;
> steht funktioniert zumindest Talkthrough.

Dann funktioniert schon mal das I2S Interface, das ist doch fein.

Ich baue da mal ein kleines Minimalprojekt ...

von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

So und da ist es.

Was musst du noch machen? Vielleicht die XDC anpassen, vielleicht -size 
64 in der Bitstream_post.tcl anpassen (das muss zu der Größe des 
Bitstreams passen). Auf jeden Fall musst du aber das korrekte FPGA 
einstellen, das geht bei meiner gratis Version von Vivado nicht.

Was habe ich gemacht?
- Viel gelöscht.
- ADC und DAC für die Testbench geschrieben. Der ADC liefert einen Sinus 
Sweep als I2S, der DAC empfängt I2S und gibt die Werte aus.
- Xilinx FIR Filter eingebaut. Linker Kanal ist Tiefpass, rechter Kanal 
Hochpass.

von Mario A. (itse_me_mario)


Lesenswert?

Hallo Gustl,

wenn es dich interessiert kann ich dir gerne die Einstellungen und mein 
.coe File schicken.

>>1. Viele Signale haben keinen Startwert. Was die Simulation da also
macht ist vermutlich ein U.
Die Simulation passt allerdings. Aber trotzdem sollte ich das noch 
ändern, da geb ich dir Recht.


>>Koeffizienten aus ROM lesen, Filter verwenden, ...
Habe ich alles schon getestet im Zusammenspiel mit dem Filter (in der 
Simulation).

>>nicht nur mit einem statischen Wert, sondern mit einem Sinus oder noch
besser einen Sweep?
Ich habe ja sozusagen einen Sweep mit meinen Daten an ADC_SDOUT. Ich 
lese die Zahlen 1,2,3,4,5,...,15,16,16,16,...16 ein und filtere diese.

>>Ich baue da mal ein kleines Minimalprojekt ...
Danke!

von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

Mario A. schrieb:
> Die Simulation passt allerdings.

Kann ich nicht nachvollziehen. Mag sein, aber ein Screenshot bringt da 
reichlich wenig. Kann ja sein, dass zu einem späteren Zeitpunkt dein 
Filter keine neuen Werte mehr liefert oder so. Weiß ich aber nicht.

Wenn du dein Filter selber bauen willst, dann lade mal alles hoch was zu 
diesem Filter gehört. Also alles was da eingebunden wird in deiner 
FIRFilter.vhd .
Ich werde da aber nicht tief rumstochern weil ich das deutlich zu 
komplex finde für ein FIR Filter. Aber ich würde eine Testbench 
schreiben und nur dieses Filter mal testen.

Mario A. schrieb:
> Ich
> lese die Zahlen 1,2,3,4,5,...,15,16,16,16,...16 ein und filtere diese.

Ja, aber was wenn dein Filter nach mehreren us oder ms irgendwann 
aufgibt und die Füße hochlegt? Oder wenn irgendwelche Werte überlaufen 
und dann dauerhaft eine Konstante ausgegeben wird? Das siehst du so 
alles nicht.

Oben in dem Minimalprojekt sind ja zwei FIRs drinnen. Die kannst du 1:1 
durch deinen FIR ersetzen. Da bekommst du am Eingang einen Sinussweep 
und kannst den Ausgang beobachten.

Im Anhang noch zwei FIRs von mir geschrieben. Einmal seriell, also je 
Multiplikation ein Takt und einmal parallel, also mit jedem Takt ein 
neuer Abtastwert. Ja, Code ist nicht hybsch, ist schon etwas her. Aber 
funktioniert.

: Bearbeitet durch User
von Mario A. (itse_me_mario)



Lesenswert?

Hallo Gustl,

vielen Dank schon mal für alles! Das mit dem ADC und DAC ist eine sehr 
gute Idee. Ich hänge dir mal die Einstellungen meiner IPs an.
Ich schaue mir morgen Vormittag deinen Code an und binde ihn ein. Danke!

von Gustl B. (-gb-)


Lesenswert?

Lade die IPs doch einfach hoch. Aus dem jeweiligen Ordner wird glaube 
ich nur die .xml und die .xci benötigt. Mein ganzes Minimalprojekt oben 
sind nur wenige kByte.

von Mario A. (itse_me_mario)


Lesenswert?

Noch ein kurzes Update:
Der ILA funktioniert nun auch mit den Filterversionen (ich erhalte Werte 
für DA_SDIN, etc.). Den Frequenzgang muss ich allerdings noch 
überprüfen.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hier die IP Dateien. Hab deinen Beitrag leider übersehen.

von Mario A. (itse_me_mario)



Lesenswert?

Hallo Gustl,

ich habe jetzt mal dein Projekt zum Laufen gebracht. Vielen Dank 
hierfür! Gefällt mir sehr gut und kann ich auch so noch gebrauchen! 
Danke.

Hast du die Simulation von IN_RIGHT, IN_LEFT, OUT_RIGHT, OUT_LEFT auch 
mit Vivado gemacht? Also Run Simulation? Das rechnet ewig bei mir, bis 
ich da mal ein paar Werte habe.

Ich habe mal den Bitstream für dein Projekt generiert und die 
Frequenzgänge mit dem Analog Discovery 2 gemessen (siehe Anhang). 
Tiefpass- und Hochpassverhalten sind zu erkennen. Außerdem hab ich mir 
den Frequenzgang mit deinen Koeffizienten mit Matlab plotten lassen.


Ich möchte jetzt noch mein FIR-Filter einbinden und schauen ob´s 
funktioniert.

Dein serielles und dein Pipeline Filter schau ich mir auch demnächst 
noch an.
Recht herzlichen Dank schon mal!

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Hast du die Simulation von IN_RIGHT, IN_LEFT, OUT_RIGHT, OUT_LEFT auch
> mit Vivado gemacht? Also Run Simulation? Das rechnet ewig bei mir, bis
> ich da mal ein paar Werte hab

Ja, Ja und Ja (-: Ist eben so, 32 kHz Samplerate braucht viel Echtzeit. 
Ausserdem brauchen die Xilinx Blöcke viel Rechenleistung.

Mario A. schrieb:
> Ich habe mal den Bitstream für dein Projekt generiert und die
> Frequenzgänge mit dem Analog Discovery 2 gemessen (siehe Anhang).
> Tiefpass- und Hochpassverhalten sind zu erkennen. Außerdem hab ich mir
> den Frequenzgang mit deinen Koeffizienten mit Matlab plotten lassen.

Fein. Filterdesign geht gut mit Pyfda ( 
https://github.com/chipmuenk/pyfda ).

Mario A. schrieb:
> Recht herzlichen Dank schon mal!

Bitte! Viel Erfolg!

von Mario A. (itse_me_mario)



Lesenswert?

>>Ja, Ja und Ja (-:
Alles klar klar klar. :)

>>Filterdesign geht gut mit Pyfda
Danke für den Tipp. Habe bisher Matlab verwendet, aber das ist halt 
leider kostenpflichtig.

Ich hab mal mein Filter eingebunden und das im Anhang kam dabei raus. 
Ich habe für den linken Kanal ein Filter mit Fpass 2000 und Fstop 2200 
und für den rechten Kanal ein Filter mit Fpass 500 und Fstop 1000 
verwendet (jeweils 15ter Ordnung). Könnte schon passen.

Die Messung mit dem AD2 ist allerdings nicht so aufschlussreich (siehe 
Anhang). Ich werde das mal am Montag mit Filtern höherer Ordnung prüfen.

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Die Messung mit dem AD2 ist allerdings nicht so aufschlussreich (siehe
> Anhang).

Doch das passt schon. Beachte zwei Dinge:

1. In der Simulation geht die Frequenz mit der Zeit linear. (Also wenn 
du den Sinus Sweep von mir verwendest.) Im Bildchen vom AD ist das aber 
log aufgetragen.

2. Die Amplitude ist in der Simulation ebenfalls linear, beim AD aber in 
dB. Halbe Amplitude bedeutet -3 dB. In der Simulation kann man schön 
sehen, dass sich zwar die Amplitude ändert, aber auch nicht so sehr. 
Zumindest keine -80 dB oder so.

Mario A. schrieb:
> jeweils 15ter Ordnung

Das ist einfach sehr wenig, sollte aber schon reichen um -40 dB und 
etwas mehr zu erreichen. Mein Filter (das Bildchen aus meinem Post 
weiter oben) hatte so knapp 200 Koeffizienten.

: Bearbeitet durch User
von Mario A. (itse_me_mario)



Lesenswert?

>>Doch das passt schon. Beachte zwei Dinge:
Dessen bin ich mir bewusst.

>>Das ist einfach sehr wenig, sollte aber schon reichen um -40 dB und
etwas mehr zu erreichen.
Man sollte dennoch einen deutlichen Unterschied erkennen bei Fpass 
500/Fstop 1000 und Fpass 2000/Fstop 2200 denke ich.

Ich habe mir jetzt mal mit dem filterDesigner die Koeffizienten für 
dieselben Filter für einen Signalprozessor generieren lassen und den 
Frequenzgang anschließend überprüft. Das sieht definitiv anders aus. Die 
Berechnung ist auch in Fixed-Point (INT16-Koeffizienten) realisiert.

Die Koeffizienten des coe-Files und der C-Headerdateien kann man auch 
einwandfrei ineinander umrechnen, das sollte also passen. Außerdem habe 
ich auch mal versucht die Koeffizienten direkt einzufügen (als Vector) 
und nicht per COE-File: Keine Veränderung.

Passt evtl. an meinen Einstellungen für den PMOD oder den FIR-Compiler 
irgendetwas nicht..?

Slave:
MCLK: 8,192 MHz
SCLK: 2,048 MHz
LRCK: 32 kHz

Das sollte doch einer Abtastrate von 32 kHz entsprechen. Und je Kanal 
sollten 24 Bit für die Daten verwendet werden.

Bei deinen Koeffizienten für den TP und den HP (in Kombination mit dem 
FIR-Compiler) messe ich wenigstens mal einen "anderen Frequenzgang" mit 
dem Analog Discovery.

Ich werd mal den PMOD im Mastermodus verwenden und schauen, ob sich was 
ändert.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Ich glaub ich muss das Verhältnis von MCLK/LRCK anpassen, damit die 
Daten im 24 Bit Format übertragen werden. Vllt sieht deshalb auch die 
Simulation gut aus, aber auf dem FPGA sieht´s anders aus.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Davon habe ich leider keine Ahnung. In der Testbench den ADC und DAC 
habe ich für 24 Bits geschrieben und das sollte eigentlich auch 1:1 so 
auf der Hardware funktionieren.
Ich empfehle dir aber weiterhin mal die Abtastwerte zum PC auszugeben. 
Das geht relativ einfach. Du nimmst dir im FPGA etwas BRAM, in den 
nimmst du Audio auf bis der voll ist und dann schickst du das über UART 
zum PC. Dann könntest du dir das Signal schön plotten und gucken ob 
alles passt.
Mit einer hohen Baudrate kannst du dir sogar den BRAM sparen und das 
Audio direkt herausstreamen.

24 Bit ⋅ 2 Kanäle ⋅ 32 kHz Abtastrate ⋅ 1,4 (wegen Start und Stoppbit 
und Pause) = 2150400 Baud. Das bekommt der FTDI auf den Board locker 
hin. Ich würde gleich 4 Bytes je Abtastwert nehmen damit du da ein 
Framing einbauen und dazuschreiben kannst welcher Kanal das ist. Ein 
Paket auf 24 Datenbits und einem Kanalbit könnte dann so aussehen:

K000DDD1
DDDDDDD0
DDDDDDD0
DDDDDDD0

Das braucht dann 2867200 Baud.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Ich denke meine Clocks für den PMOD passen doch.

>>Ich empfehle dir aber weiterhin mal die Abtastwerte zum PC auszugeben.
Das geht relativ einfach. Du nimmst dir im FPGA etwas BRAM, in den
nimmst du Audio auf bis der voll ist und dann schickst du das über UART
zum PC.

Über den UART-Anschluss meinst du? (USB-UART-Bridge, Anschluss 1: 
https://reference.digilentinc.com/reference/programmable-logic/genesys-2/reference-manual)
set_property -dict { PACKAGE_PIN Y23   IOSTANDARD LVCMOS33 } [get_ports 
{ uart_rx_out }]; #IO_L1P_T0_12 Sch=uart_rx_out
set_property -dict { PACKAGE_PIN Y20   IOSTANDARD LVCMOS33 } [get_ports 
{ uart_tx_in }]; #IO_0_12 Sch=uart_tx_in

Und den BRAM mithilfe des Block Memory Generators erzeugen?

Wie würdest du die Daten am PC auslesen (welches Programm?).

Vielen Dank im Voraus! Das klingt vielversprechend!

: Bearbeitet durch User
von Duke Scarring (Gast)


Lesenswert?

Mario A. schrieb:
> Wie würdest du die Daten am PC auslesen (welches Programm?).
Im Zweifelsfall erstmal mit HTerm. Das lann die Daten in Binär, Hex und 
ASCII ausgeben. So kann man erstmal schauen, ob da überhaupt was 
verwertbares kommt.

Duke

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Über den UART-Anschluss meinst du?

Genau den.

Mario A. schrieb:
> Und den BRAM mithilfe des Block Memory Generators erzeugen?

Du kannst das einfach als Array schreiben. 
http://www.lothar-miller.de/s9y/archives/20-RAM.html

Aber: Wenn du beim UART die Baudrate hoch genug wählst, dann kannst du 
das einfach streamen und brauchst keinen BRAM. Dein FTDI kann IIRC bis 
12 MBaud. Nimm einfach mal 8 MBaud und es wird funktionieren.

Mario A. schrieb:
> Wie würdest du die Daten am PC auslesen (welches Programm?).

Ich verwende Python:

import serial
ser = serial.Serial('COM8',8000000,timeout=0)
data = ser.read(1000000) #einfach alles lesen was ankommt

Natürlich liest du in einer Schleife periodisch. Und die Daten die du 
bekommst kannst du erstmal stumpf an die alten anhängen. Dann speicherst 
du die oder zerlegst die gleich in Samplewerte und plottest dir das 
Signal.

Duke Scarring schrieb:
> Im Zweifelsfall erstmal mit HTerm. Das lann die Daten in Binär, Hex und
> ASCII ausgeben. So kann man erstmal schauen, ob da überhaupt was
> verwertbares kommt.

Exakt! Einfach Datenpakete ausgeben mit dem FPGA über UART und dann mal 
ein paar Sekunden Datenstrom aufnehmen. Das kannst du dann gerne hier 
posten als Anhang und wir plotten das, oder du plottest das selber.

Ich empfehle da cutecom unter Linux oder realterm unter Windows.

von Mario A. (itse_me_mario)


Lesenswert?

Danke für die Tipps! Ich weiß leider noch nicht genau wie ich beginnen 
soll. Soll ich einen IP Core für den UART verwenden (AXI UART16550, AXI 
Uartlite)?

Also wenn ich es richtig verstanden habe: Meine empfangenen und 
gefilterten 24 Bit Audiodaten jeweils in einen BRAM schreiben. Diesen 
dann über die UART-Bridge senden, ODER direkt OHNE BRAM die Daten 
streamen (auch mit IP Block?).

Und dann erst mal mit HTerm schauen, ob was entsprechendes rauskommt. 
Die Daten von HTerm könnte ich dann auch mit MATLAB analysieren, oder? 
Hab leider noch keine Erfahrung mit Python gemacht und möchte auch 
gerade kein weiteres Fass aufmachen, wenn es nicht zwingend notwendig 
ist. :P

>>Mit einer hohen Baudrate kannst du dir sogar den BRAM sparen und das
Audio direkt herausstreamen.
Wo stellt man denn die Baudrate ein? Du meinst in den IP Blöcken?

Maximale Baudrate AXI Uartlite bei 100 MHz Clock (8 Datenbits, ohne 
Paritätsbits): 230400

Soll ich die 1en und 0en des seriellen Eingangs-/Ausgangssignals direkt 
über uart_tx_in senden?

Vielen Dank!

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Soll ich einen IP Core für den UART verwenden (AXI UART16550, AXI
> Uartlite)?

Kannst du machen, geht aber einfacher. Hier ist eine UART Komponente:
http://www.lothar-miller.de/s9y/categories/42-RS232
Die funktioniert, verwende ich auch selbst aber ich habe die noch etwas 
modifiziert.

Mario A. schrieb:
> Also wenn ich es richtig verstanden habe: Meine empfangenen und
> gefilterten 24 Bit Audiodaten jeweils in einen BRAM schreiben. Diesen
> dann über die UART-Bridge senden, ODER direkt OHNE BRAM die Daten
> streamen (auch mit IP Block?).

Richtig. Ich würde der Einfachheit halber den BRAM weglassen.

Mario A. schrieb:
> Und dann erst mal mit HTerm schauen, ob was entsprechendes rauskommt.
> Die Daten von HTerm könnte ich dann auch mit MATLAB analysieren, oder?

Ja. Ich kenne HTerm nicht. Lass dir die Daten wenn möglich nicht als 
irgendwelches ASCII oder so speichern sondern schön die rohen Bytes.

Du kannst das aber auch hier in den Anhang hochladen was du aufgenommen 
hast, ich gucke mir das dann gerne mal an.

Mario A. schrieb:
> Wo stellt man denn die Baudrate ein? Du meinst in den IP Blöcken?

In dem oben verlinkten UART von Lothar kann man das per Generic 
einstellen. Sonst kannst du das in dem IP Block einstellen.

Mario A. schrieb:
> Maximale Baudrate AXI Uartlite bei 100 MHz Clock (8 Datenbits, ohne
> Paritätsbits): 230400

Das ist zu wenig.

Mario A. schrieb:
> Soll ich die 1en und 0en des seriellen Eingangs-/Ausgangssignals direkt
> über uart_tx_in senden?

Nein. Du baust aus den Abtastwerten Pakete die man im Datenstrom als 
Paket erkennen kann. Z. B. so wie ich es oben vorgeschlagen hatte. Das 
waren 4 Bytes/Abtastwert. Und diese 4 Bytes schickst du dann 
nacheinander über den UART.

Wenn du etwas Zeit hast schreibe ich das Minimalprojekt um und erweitere 
es um einen UART.

von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

Zu lange gebraucht, also noch ein Post:

Leider ist der UART "nur" ein FT232R, das limitiert auf 3 MBaud. Ich 
verwende daher nicht 4 Bytes je Samplewert, sondern 7 Bytes für zwei 
Abtastwerte.

Paketformat:

0LLLLLL1
LLLLLLL0
LLLLLLL0
LLLLRRR0
RRRRRRR0
RRRRRRR0
RRRRRRR0

L: Linker Kanal, R: Rechter Kanal. Ein Paket kann durch die LSBs erkannt 
werden. Eingestellt sind 3 MBaud.

von Mario A. (itse_me_mario)


Lesenswert?

>>Hier ist eine UART Komponente:
Perfekt, danke!

>>Richtig. Ich würde der Einfachheit halber den BRAM weglassen.
Alles klar.

>>Du kannst das aber auch hier in den Anhang hochladen was du aufgenommen
hast, ich gucke mir das dann gerne mal an.
Danke! :)

>>Du baust aus den Abtastwerten Pakete die man im Datenstrom als
Paket erkennen kann.
Alles klar.

>>Wenn du etwas Zeit hast schreibe ich das Minimalprojekt um und erweitere
es um einen UART.
Wenn es dir keine allzu großen Umstände macht, wäre das natürlich sehr 
toll!
Ich versuch mich auf jeden Fall auch mal. Vielleicht schaff´ ich es. ;) 
Vielen Dank dir wieder mal! :)

Okay das ging schnell. :) :P

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

Ich habe mir jetzt doch Python heruntergeladen. Mit Matlab hat es jetzt 
leider nicht wie erhofft auf Anhieb funktioniert.

von Gustl B. (gustl_b)


Lesenswert?

OK, kommen Daten über UART am PC an? Steht die Baudrate auf 3 MBaud? 
Kannst du die empfangenen Daten abspeichern? Wenn du magst dann lade sie 
hier hoch als Anhang. Da reichen ja wenige 100 kBytes bis so wenige 
MBytes.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Mal eine Frage. Du hast in AudioInterface eine Baudrate von 3 000 000 
eingestellt. Im Gerätemanager für den entsprechenden COM-Anschluss kann 
ich nur max. 921 600 Bits pro Sekunde einstellen. Dann muss ich noch was 
anpassen oder?

MATLAB:

s = serial('COM11', 'BaudRate', 3000000);
fopen(s)

[A,count,msg] = fread(s, 1000, ...) %muss ich noch ergänzen

 fclose(s);

 b = de2bi(A);

Mit de2bi kann ich die Daten dann nach binär umwandeln.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

1
  data = ser.read(100000)
2
  if len(data) > 0:
3
    sr.extend(data)
4
    while len(sr) >= 7:
5
      if (sr[0] & 1 == 1) and (sr[1] & 1 == 0) and (sr[2] & 1 == 0) and (sr[3] & 1 == 0) and (sr[4] & 1 == 0) and (sr[5] & 1 == 0) and (sr[6] & 1 == 0):
6
        L = (sr[0] >> 1)*2**18 + (sr[1] >> 1)*2**11 + (sr[2] >> 1)*2**4 + (sr[3] >> 4)
7
8
        R = (sr[3] & 14)*2**21 + (sr[4] >> 1)*2**14 + (sr[5] >> 1)*2**7 + (sr[6] >> 1)

Den Code kannst du in einer Schleife ausführen.  Und dann brauchst du 
noch ein Array für L und R das du dann befüllst und plottest. Aber lade 
das gerne wirklich hier noch.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Also ich hab jetzt in MATLAB:

s = serial('COM11', 'BaudRate', 3000000);

fopen(s)

[A,count,msg] = fread(s, 100, 'uchar')

fclose(s);


 b = dec2bin(A);

Soll ich jetzt noch die Einstellungen im Geräte Manager anpassen, oder 
es so lassen?

Vllt verwende ich mal deinen Pythonansatz von gerade und konvertier den 
in MATLAB Code.

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Im Gerätemanager für den entsprechenden COM-Anschluss kann
> ich nur max. 921 600 Bits pro Sekunde einstellen. Dann muss ich noch was
> anpassen oder?

Ja. Der Gerätemanager zeigt dir das nicht an. Wenn du Python zum lesen 
vom UART verwendest, kannst du selber eine Baudrate einstellen, auch 3 
MBaud.

Mario A. schrieb:
> [A,count,msg] = fread(s, 1000, ...) %muss ich noch ergänzen

Gut, kenne mich da nicht so aus. Aber lese nicht nur 1000 Werte. Lese 
mal für ein paar Sekunden. Also in einer Schleife periodisch lesen. Denn 
da kommen immer einige Bytes an, und es passen nicht irre viele Bytes in 
den Empfangspuffer. Also so alle 10 ms oder so solltest du schon vom 
UART lesen.
Und da liest du immer so viel wie möglich. Also nicht 1000 sondern den 
Maximalwert der erlaubt ist.
Die gelesenen Daten hängst du dann einfach an die alten Daten an.

Hier der Code, müsste so laufen, der liest mehrmals in einer Schleife 
vom UART alles was angekommen ist und speichert das dann binär in einer 
Datei:
1
import serial
2
import time
3
4
ser = serial.Serial('COM11',3000000,timeout=0)
5
sr = bytearray()
6
for i in range(0,1000):
7
  data = ser.read(1000000)
8
  if len(data) > 0:
9
    sr.extend(data)
10
  time.sleep(0.01)
11
ser.close()
12
aufnahme = open("Aufnahme.bin",'wb')
13
aufnahme.write(sr)
14
aufnahme.close()

Mario A. schrieb:
> Soll ich jetzt noch die Einstellungen im Geräte Manager anpassen, oder
> es so lassen?

Einfach lassen.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Danke!
Muss ich da noch was installieren?

>>> import serial
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'serial'


Ich sollte wohl noch einen Texteditor für Python installieren? Habs 
einfach hier heruntergeladen:
https://www.python.org/downloads/

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Ne, du musst noch das Modul https://pypi.org/project/pyserial/ 
installieren.
Am einfachsten mit dem Befehl:

pip install pyserial

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Das mit dem Befehl funktioniert so leider nicht...

von Gustl B. (-gb-)


Lesenswert?

Ne, nicht aus Python heraus.
Also erst aus dem Python Terminal raus mit quit() und dann den Befehl.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Ich habe nur dieses Terminal oder sehe ich das falsch?

Shell und das Terminal. Bei beiden kann ich den Befehl nicht eingeben. 
Ich schau mir jetzt mal ein Tutorial an..

von Gustl B. (-gb-)


Lesenswert?

Den Befehl gibst du in das normale Windowsterminal ein.
Windowstaste, dann "CMD", Enter, Befehl eingeben, Enter.

von Mario A. (itse_me_mario)


Lesenswert?

Ich lade mir mal schnell Anaconda herunter. Ich hoffe es funktioniert 
dann. Danke Gustl!

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Ja gut, aber brauchst du wohl nicht. Ich verwende hier Winpython.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Also ich konnte jetzt die Bibliothek installieren.

Aufnahme.bin befindet sich im Anhang.

SerialException: Attempting to use a port that is not open

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> SerialException: Attempting to use a port that is not open

Ist auch klar, du hast da im Code noch den Block

Data = ser.read ... drinnen, der gehört da nicht rein. Der wäre zum 
dekodieren, aber das müsste man anders schreiben.

Aber irgendwas passt da noch nicht. Wenn ich mir die LSBs angucke, dann 
passt das nicht zu meinem Paketformat. Hast du was am Takt geändert oder 
so? Jedenfalls ist die Aufnahme unbrauchbar.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hm okay. Das FPGA läuft und der Port ist angesteckt. Ich hab lediglich 
statt deinem 200 MHz Takt einen 100 MHz Takt verwendet bei 
AudioInterface.vhd.

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Ich hab lediglich
> statt deinem 200 MHz Takt einen 100 MHz Takt verwendet bei
> AudioInterface.vhd.

OK, den hast du auch in der PLL geändert?

MMCM: CLK_I2S port map(
  clk200 => s_clk_200_MHz,
  clk8_192 => s_clk_8_192_MHz_MCLK,
  clk_in1_p => sysclk_p,
  clk_in1_n => sysclk_n);

Da ist jetzt ein 100 MHz Ausgang?

Und das hast du auch dem UART mitgeteilt?

UART_FT232R: UART
    Generic map(
  clk_freq => 100000000,

?

Sonst könnte es noch sein, dass der Empfangspuffer vom UART überläuft 
während der 10 ms Schlafphase. Setze daher die Zeile
time.sleep(0.01)
mal auf
time.sleep(0.001)
.

von Mario A. (itse_me_mario)



Lesenswert?

>>Da ist jetzt ein 100 MHz Ausgang?
Ich verwende den Clocking Wizard (sysclk_p, sysclk_n) und da kommt dann 
mein 100 MHz Takt raus.
>>Und das hast du auch dem UART mitgeteilt?
Ja.

>>Setze daher die Zeile time.sleep(0.01) mal auf time.sleep(0.001)
Hab dir das vorher nachher .bin in den Anhang gepackt.
Danke!

: Bearbeitet durch User
von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

Hm. Ja also die Aufnahme ist weiterhin kaputt.

Woran das liegen kann weiß ich nicht, du hast ja den Clockmanager 
hoffentlich korrekt konfiguriert.

Vielleicht mag der FT232R keine 3 MBaud? Aber wenn man mit der Datenrate 
runter geht dann geht kein Streaming mehr mit vollen 24 Bits je Kanal 
und zwei Kanälen.

Man könnte auf Streaming verzichten und BRAM verwenden, also z. B. 2^16 
Werte in den BRAM aufnehmen und dann ausgeben.

Oder man streamt weiterhin aber nur mit einem Kanal oder mit niedrigerer 
Auflösung.

Die Datei im Anhang streamt nur den linken Kanal bei 2 Mbaud.

Bonusfrage an die Profis:
Ich verwende hier den FT232R problemlos mit 921600 Baud. Weil das der 
höchste Wert ist der in vielen Terminalprogrammen eingestellt werden 
kann. Allerdings kann der FT232R keine 921600 Baud laut Datenblatt. Was 
macht da die Software wenn ich da trotzdem einfach 921600 verwende und 
wieso bekomme ich da keinen Fehler?

: Bearbeitet durch User
von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

So, und hier noch eine Version die mit 921600 Baud streamt. Linker Kanal 
und nur 14 Bits/Sample, aber weiterhin volle Abtastrate.

von Mario A. (itse_me_mario)


Lesenswert?

>>du hast ja den Clockmanager hoffentlich korrekt konfiguriert.
Ich versuch es morgen auch nochmal mit deiner Originaldatei, um das 
sicher ausschließen zu können, aber ich denke das sollte passen.

>>Die Datei im Anhang streamt nur den linken Kanal bei 2 Mbaud.
Werde ich auch ausprobieren danke!

Ansonsten werde ich wohl den BRAM einbinden müssen.

Also auf dem Genesys kann ich es leider erst wieder am Montag probieren, 
da ich nur an einem PC die Lizenz habe.. Hätte aber ein Zybo 7020 dabei.
Vielen Dank!

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Ich versuch es morgen auch nochmal mit deiner Originaldatei, um das
> sicher ausschließen zu können, aber ich denke das sollte passen.

Simulation wäre zielführend.

Mario A. schrieb:
> Hätte aber ein Zybo 7020 dabei.

Da ist der UART ein FT2232H. Der kann 12 MBaud, habe ich schon gemacht, 
funktioniert. Damit sollte das wunderbar funktionieren.

von Mario A. (itse_me_mario)


Lesenswert?

>>Simulation wäre zielführend.
Wird morgen gemacht! Heute bin ich leider nicht dazugekommen.

>>Da ist der UART ein FT2232H. Der kann 12 MBaud, habe ich schon gemacht,
funktioniert. Damit sollte das wunderbar funktionieren.
Klingt vielversprechend! :)

von Gustl B. (-gb-)


Lesenswert?

Ja, aber deine XDC musst du anpassen und deine Takte ebenfalls. Du 
könntest den 125 MHz Takt an Pin K17 verwenden.

von Mario A. (itse_me_mario)


Lesenswert?

Hallo Gustl,

bin gerade dabei mein Projekt für das Zybo 7020 anzupassen.

Ich weiß nur gerade nicht, welchen Pin ich für den UART-Transfer nehmen 
soll.
Es gibt 2 Anschlüsse auf dem Board:
PROG UART J12 und J10

Im XDC finde ich nichts passendes.
https://github.com/Digilent/digilent-xdc/blob/master/Zybo-Z7-Master.xdc

Über Callout 3 lade ich die Hardwarebeschreibung auf das FPGA, oder?
https://reference.digilentinc.com/reference/programmable-logic/zybo-z7/reference-manual
Auf der anderen Seite von Callout 6 wäre noch der selbe Anschluss wie 
bei PROG UART (Anschluss J10) aber ich finde den nicht im XDC.


Morgen möchte ich dann noch einmal das Genesys verwenden und mit den 
anderen Modikfikationen von dir testen (nur 1 Channel, etc).

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Ich weiß nur gerade nicht, welchen Pin ich für den UART-Transfer nehmen
> soll.
> Es gibt 2 Anschlüsse auf dem Board:
> PROG UART J12 und J10

Ja da kann ich leider nicht helfen. Da ist auf jeden Fall ein FT2232H 
drauf und der müsste auch mit dem UART am FPGA angeschlossen sein. Aber 
ob das "nur" am PS Teil ist oder am PL Teil weiß ich nicht. Im 
Schaltplan ist die Seite mit dem FTDI leider leer.

Hast du noch einen USB-UART? Dann verwende einfach zwei Pins (oder für 
nur TX nur einen) von einem PMOD.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

>>Im Schaltplan ist die Seite mit dem FTDI leider leer.
Ja sehr ärgerlich...

>>Hast du noch einen USB-UART? Dann verwende einfach zwei Pins (oder für
nur TX nur einen) von einem PMOD.
Nur das was man auf den Bildern sieht. Im XDC finde ich leider nichts 
passendes.

Morgen versuch ich es auf dem Genesys 2.

von Mario A. (itse_me_mario)



Lesenswert?

Hallo Gustl,

ich habe jetzt die beiden anderen Versionen von dir auf das Genesys 2 
geladen. Das was rauskommt und wie es zustande kommt habe ich dir in den 
Anhang gepackt.

UART: 3 MBAUD, beide Kanöle
UART2: 2 MBAUD, linker Kanal
UART3: 921600 Baud, linker Kanal

Leider komme ich erst Ende der Woche wieder vermehrt zu meinem Projekt, 
da unerwartet was sehr Wichtiges reingekommen ist.

Ich bin außerdem dabei mein Filter so umzuschreiben, dass ich 16/21/24 
Bit Koeffizienten habe und 24 Bit Daten als Filterinput und 24 Bit Daten 
als Filteroutput habe. Ein erster Test war schon einmal 
vielversprechend.
Vielen Dank! :)

von Gustl B. (-gb-)


Lesenswert?

Seltsam. Mir scheint du machst alles korrekt, aber die Aufnahmen sind 
wieder alle kaputt. Mir fällt nur der Takt ein, also dass du da zwar 100 
MHz verwenden willst, aber noch 200 MHZ eingestellt hast oder so. Aber 
sonst bin ich da recht ratlos, sorry.

Du kannst gerne nochmal dein komplettes Projekt hochladen, dann gucke 
ich ob das in der Simulation gut aussieht.

Also zur Erklärung:

In der Version mit den 921600 Baud

packet <=   s_audio_l_out(23 downto 17) & '1'
            & s_audio_l_out(16 downto 10) & '0';

sollte das LSB von jedem zweiten Byte 1 und von jedem zweiten Byte 0 
sein. Und das ist nicht der Fall.

Wenn du ein Oszi/Logicanalyzer hast, dann kannst du dir mal den UART Pin 
angucken und die minimale Pulsweite bestimmen. Die sollte 1,085 us sein.

: Bearbeitet durch User
von Mario A. (itse_me_mario)



Lesenswert?

Hallo Gustl,

ich lade mal nochmal das Projekt samt Binary hoch. Leider schaffe ich 
erst am Freitag wieder mich mit dem Projekt länger als ein paar Minuten 
auseinander zu setzen...

In der Simulation sind es 100 MHz.

Kannst du mal bitte kurz prüfen, ob Aufnahme_neu_UART3_921600Baud_Right 
ein richtiges Ergebnis liefert? Wenn ja, sind die Channels in meinem 
Programm vertauscht. Das ist meine Befürchtung. Ich habe jetzt beide 
Channels separat mit dem AD2 gemessen und aufgenommen. Mit dem AD2 kann 
ich leider nur immer den linken oder den rechten Kanal messen. Kann also 
gut sein, dass ich den falschen Kanal eingelesen habe.

Ich denke Aufnahme_neu_UART3_921600Baud_Right könnte richtig sein!

Sollte das funktionieren, teste ich auch nochmal die Versionen mit 2 und 
3 MBaud.

Vielen Dank im Voraus!!

von Mario A. (itse_me_mario)



Lesenswert?

Hier noch die Dateien für das 2 und 3 MBaud Projekt (UART2: 2 MBaud, 
UART: 3 MBaud).

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

So, zwei Sachen:

1. Wenn du ein Projekt anhängst, dann bitte den ganzen Ordner als zip. 
Sonst müsste ich das alles händisch zusammenbauen. Auch zu den IPs, 
einfach die .xci und die .xml.

2. Fehler - oder zumindest einen Fehler - gefunden.

Mario A. schrieb:
> In der Simulation sind es 100 MHz.

Nicht nur dort, leider. In dem Bildchen Clock1.png hast du als 
Taktfrequenz von deinem externen Taktgeber 100 MHz eingestellt. Das ist 
falsch. Der Taktgeber auf deiner Platine hat 200 MHz und lässt sich auch 
nicht ändern. Schreibe dort 200 MHz rein.
Dort wie in Bildchen Clock2.png, da kannst du eintragen was du gerne 
hättest, also welche Tektfrequenz erzeugt werden soll. Also meinetwegen 
die 100 MHz, aber im Bildchen Clock1.png muss die Frequenz drinnen 
stehen die der Taktgeber tatsächlich hat. Das teilst du damit dem Vivado 
mit. Wenn du da 100 MHz (wie jetzt) reinschreibst, und 100 MHz haben 
willst, dann macht Vivado da 1:1 draus. In der Realität hat der externe 
Takt aber 200 MHz, 1:1 bedeutet, dass dein "100 MHz Takt" im FPGA in 
Wirklichkeit ebenfalls 200 MHz hat.
Wenn du Vivado aber sagst der externe Takt hat 200 MHz und du willst 100 
MHz, dann wird da 2:1 draus gemacht.
Jedenfalls:
Auf der ersten Seite vom Clock Wizard schreibt man was man hat, also was 
auf dem Board ist, das ist bei dir der 200 MHz Takt. Und auf der zweiten 
Seite schreibt man rein was man gerne hätte. Also die 8.192 MHz und 100 
MHz.
Ich hoffe das war verständlich.

Mario A. schrieb:
> Kannst du mal bitte kurz prüfen, ob Aufnahme_neu_UART3_921600Baud_Right
> ein richtiges Ergebnis liefert?

Ne, ist leider wieder kaputt, ist aber auch klar weil der Takt nicht 
passt.
Ob die Aufnahme OK war siehst du im Hexeditor wenn du dir die LSBs 
mehrerer Bytes in Folge anguckst. In der 921600er Version sollten die 
immer abwechselns 1 und 0 sein.

: Bearbeitet durch User
von Mario A. (itse_me_mario)



Lesenswert?

>>Wenn du ein Projekt anhängst, dann bitte den ganzen Ordner als zip.
Alles klar. Hab dir den ganzen Ordner für das Projekt mit einer Baudrate 
von 921600 angehängt.

>>In dem Bildchen Clock1.png hast du als Taktfrequenz
Oh Mist... Danke! Das wär mir so schnell nicht aufgefallen... Hab das 
leider unterbewusst komplett ignoriert. Bei meinem Vorgängerboard waren 
es nämlich 100 MHz... Deshalb konnte der Frequenzgang auch nicht 
stimmen. Die Koeffizienten wurden für eine Abtastrate von 32 kHz 
entworfen.

>>Ob die Aufnahme OK war siehst du im Hexeditor wenn du dir die LSBs
mehrerer Bytes in Folge anguckst.
Habs mir jetzt mal mit dem https://hexed.it/ angesehen. Das LSB ist 
abwechselnd 1 und 0 bei der 921600er Version! Und
das würdest du jetzt mit Python umwandeln, oder?

Vielen Dank dir!! Den Fehler hab ich total übersehen... Danke danke 
danke :)

: Bearbeitet durch User
von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

So, das sieht jetzt mal gut aus!

Die 921600er habe ich angeguckt und geplottet, das ist ja jeweils nur 
ein Kanal und 14 Bits.

Und die beiden Aufnahme_neu_UART_LeftChannel.bin und 
Aufnahme_neu_UART_RightChannel.bin habe ich mal geplottet. Das verstehe 
ich aber nicht wieso du die Left und Right nennst, die enthalten doch 
jeweils zwei Kanäle?! Oder ist ein Kanal vor dem Filter und einer hinter 
dem Filter?
Um die Filtercharakteristik sehen zu können musst du das Audio 
durchsweepen und währenddessen aufnehmen. Das dauert vermutlich ein paar 
Sekunden oder so. Das müsstest du im Aufnahmeskript also anpassen, dass 
das länger aufnimmt. Also die Schleife öfter laufen lassen. Das 
Aufnahmeskript von mir macht nur 1000 Schleifendurchläufe, das ist ganz 
grob 1 s Aufnahmezeit.

Ausserdem sind bei den 3 MBaud doch einige Fehler drinnen. Nicht irre 
viele, aber doch einige. 4275 Fehler und 52673 korrekte Pakete. Die 
Fehlerzahl muss man aber durch ca. 7 teilen weil wenn ein Fehler dabei 
ist wird erst ca. 6 mal geschoben im "Schieberegister" bis wieder ein 
korrektes Paket erkannt wird.

Im Anhang mal Plots und das Pythonskript für die Aufnahmen mit den 3 
MBaud.

Edit:
In den Bildchen ist nur ein Ausschnitt zu sehen den ich passend skaliert 
habe. Aber kann ja jetzt Jeder selber komplett plotten, die .bin Dateien 
und das Pythonskript sind ja da.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

Hallo Gustl,

die Datei audio_decode.py bezieht sich jetzt nur auf die Version mit 3 
MBaud und dem folgenden Format, oder?

#Format:
#0LLLLLL1
#LLLLLLL0
#LLLLLLL0
#LLLLRRR0
#RRRRRRR0
#RRRRRRR0
#RRRRRRR0

Ich versuche gerade mal die verschiedenen Versionen in Matlab zu 
decodieren. Am liebsten wärs mir nämlich, dass ich die decodierten Daten 
dann wieder mit Matlab bearbeite/analysiere, weil ich damit einfach 
schon mehr Erfahrung hab.

Zu deinem Code:
Mit

if (sr[0] & 1 == 1) and (sr[1] & 1 == 0) and (sr[2] & 1 == 0) and (sr[3] 
& 1 == 0) and (sr[4] & 1 == 0) and (sr[5] & 1 == 0) and (sr[6] & 1 == 
0):

prüfst du auf das Format der letzten Spalte, oder?
Würdest du das dann bei der Version mit 14 Byte auch in der Form machen?
Dort haben wir ja

#L L L L L L L 1
#L L L L L L L 0

Mit
L_sign = int((sr[0] & 64)/64)
R_sign = int((sr[3] & 8)/8)
ermittelst du das Vorzeichen der einzelnen Channels, oder?



>>Allerdings kann der FT232R keine 921600 Baud laut Datenblatt.
https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232R.pdf
3 MBaud kann er laut Datenblatt.
Also könnte ich ja an sich beide Channels streamen?
32 Bit (mit Codierung) x 2 Kanäle x 32 kHz x 1,4 = 2,8672 MBaud

von Mario A. (itse_me_mario)


Lesenswert?

Achso ich habe immer Right and Left hochgeladen, weil ich nur jeweils an 
einen Channel den Sinussweep anschließen kann (zumindest glaube ich 
das). Der andere Channel ist vermutlich Floating.

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> die Datei audio_decode.py bezieht sich jetzt nur auf die Version mit 3
> MBaud und dem folgenden Format, oder?

Ja.

Mario A. schrieb:
> Zu deinem Code:
> Mit
>
> if (sr[0] & 1 == 1) and (sr[1] & 1 == 0) and (sr[2] & 1 == 0) and (sr[3]
> & 1 == 0) and (sr[4] & 1 == 0) and (sr[5] & 1 == 0) and (sr[6] & 1 ==
> 0):
>
> prüfst du auf das Format der letzten Spalte, oder?

Ja.

Mario A. schrieb:
> Würdest du das dann bei der Version mit 14 Byte auch in der Form machen?
> Dort haben wir ja
>
> #L L L L L L L 1
> #L L L L L L L 0

Ja, das sollte man immer machen.
Dort mit:

if (sr[0] & 1 == 1) and (sr[1] & 1 == 0):

Mario A. schrieb:
> Mit
> L_sign = int((sr[0] & 64)/64)
> R_sign = int((sr[3] & 8)/8)
> ermittelst du das Vorzeichen der einzelnen Channels, oder?

Ja.

Mario A. schrieb:
> 3 MBaud kann er laut Datenblatt.
> Also könnte ich ja an sich beide Channels streamen?
> 32 Bit (mit Codierung) x 2 Kanäle x 32 kHz x 1,4 = 2,8672 MBaud

Ja, wird aber knapp. Ich würde statt 2 x 32 Bit = 8 Bytes lieber wie 
oben

Mario A. schrieb:
> #Format:
> #0LLLLLL1
> #LLLLLLL0
> #LLLLLLL0
> #LLLLRRR0
> #RRRRRRR0
> #RRRRRRR0
> #RRRRRRR0

nehmen. Das sind nur 7 Bytes. Da wird die Pause zwischen den Bytes etwas 
länger, ob das einen Unterschied macht weiß ich nicht, ich glaube nicht. 
Du könntest also auch Folgendes machen:

Nehme ein 4-Byte-Paket, z. B.:

Format:
000KDDD1
DDDDDDD0
DDDDDDD0
DDDDDDD0

Dabei stehen die D für die Daten und das K für den Kanal. Dann sendest 
du eben zwei der Pakete. Eines für L und eines für R. Sollte gehen bei 3 
MBaud. Wenn du nur einen Kanal übertragen willst, dann schickst du nur 
Pakete des entsprechenden Kanals.

Aber:
Weil du ja bei den 3 MBaud ab und zu Fehler drinnen hast, würde ich da 
runter gehen. Auf 2 MBaud z. B.. Was der FT232R kann steht hier: 
https://www.ftdichip.com/Support/Knowledgebase/index.html?whatbaudratesareachieveabl.htm

Bei 2 MBaud kannst du aber nicht mehr beide Kanäle bei vollen 24 
Bit/Kanal streamen. Ich würde da auch 20 Bits runtergehen wenn das 
akzeptabel ist.

Da kannst du ein Paket für einen Kanal so bauen:
Format:
KDDDDDD1
DDDDDDD0
DDDDDDD0

Da kannst du bei 2 MBaud immer noch beide Kanäle knapp streamen bei 2 x 
3 Bytes. Besser wäre dann aber vermutlich ein Paket für beide Kanäle. 
Das hier wären 17 Bits/Kanal und zusammen 5 Bytes:

0LLLLLL1
LLLLLLL0
LLLLRRR0
RRRRRRR0
RRRRRRR0

Da musst du etwas ausprobieren was bei dir fehlerfrei funktioniert oder 
welche Kompromisse du eingehen willst.

Mario A. schrieb:
> weil ich nur jeweils an
> einen Channel den Sinussweep anschließen kann

Ist OK. Aber den Sweep kann man nicht sehen in den Samples. Vermutlich 
dauert dein Sweep mehrere Sekunden und die Samples enthalten nur einen 
kurzen Aussschnitt dieser Zeit. Da müsstest du für eine längere Zeit 
aufnehmen.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Habs jetzt geschafft das .bin-File mit Matlab zu decodieren.

Erst mal für das Format (3 MBaud)
      -- 0LLLLLL1
      -- LLLLLLL0
      -- LLLLLLL0
      -- LLLLRRR0
      -- RRRRRRR0
      -- RRRRRRR0
      -- RRRRRRR0

Weitere Dateien folgen.:)

von Gustl B. (-gb-)


Lesenswert?

Habe kein Matlab, sieht etwas umständlich aus, aber wenn es 
funktioniert, dann wunderbar (-: Gratulation!

von Mario A. (itse_me_mario)


Lesenswert?

Ja geht bestimmt einfacher.

Wenn ich
for i in range(0,10000):
statt
for i in range(0,1000):
schreibe, rechnet Matlab irgendwie ewig und kommt nicht zu einem Ende. 
Bei 1000 gehts nóch recht zügig. Irgendwie seltsam. Das funktioniert mit 
deinem Pythonskript deutlich besser.
Hab bei meinem Code auch erst einmal nicht auf Effizienz geachtet...

Ich werd morgen mal versuchen die Werte für die beiden Kanäle von deinem 
Python-Skript in ein File zu schreiben und diese dann mit Matlab 
grafisch auszuwerten.

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Das funktioniert mit
> deinem Pythonskript deutlich besser.

Komisch, Python ist eigentlich eher langsam. Aber ja, deine Methode 
jedes Bit einzeln herauszufischen ist nicht gerade der Performancekönig.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Ich hab jetzt die Daten von Python mit der Datei im Anhang in ein 
Textfile geschrieben und kann das nun einfach mit Matlab analysieren.

Die Daten wurden mit den Einstellungen im Anhang aufgenommen (3 MBaud, 
24 Bit, 7 Byte Paket, beide Kanäle, wobei bei den Messungen nur ein 
Kanal aussagekräftig ist).

Oben ist jeweils der linke Kanal zu sehen und unten der rechte Kanal. 
Der rechte Kanal sollte in diesem Fall keine richtigen Werte aufweisen, 
da die Sinussweeps nur am linken Kanal gemacht werden. Das unten sieht 
nach Rauschen aus.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Was mir gerade auffällt:

packet <=   '0' & s_audio_l_out(23 downto 18) & '1'
                & s_audio_l_out(17 downto 11) & '0'
    & s_audio_l_out(10 downto 4) & '0'
    & s_audio_l_out(3 downto 0)

                & s_audio_r_out(23 downto 21) & '0'
    & s_audio_r_out(20 downto 14) & '0'
                & s_audio_r_out(13 downto 7) & '0'
                & s_audio_r_out(6 downto 0) & '0';

audio_l_out und audio_r_out enthalten jeweils die Werte der gewandelten 
Eingangsdaten VOR dem Filter.
audio_l_in und audio_r_in enthalten hingegen die gefilterten Daten.

Ich habe mal den linken Kanal vor und nach dem Filter mittels UART 
übertragen und analysiert (siehe INvsOUT_LeftChannel). Die 
Filtercharakteristik ist sehr schön zu erkennen.

Ein Sweep von 100 Hz bis 16 kHz entspricht in etwa 2,5*10^5 Werten laut 
Grafik. Abtastrate 32 kHz.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Lob und Anerkennung! Das sieht tatsächlich schön aus, Gratulation!

von Mario A. (itse_me_mario)


Lesenswert?

Danke! :) Das ist das Ergebnis mit dem von dir vorgeschlagenen 
Datenformat mit den 7 Bytes (davon sind 48 Datenbits) bei 3 MBaud.

Sieht ja gut aus. Denkst du ich sollte dann überhaupt nochmal mit der 
Baudrate runtergehen, oder es so belassen? Die 24 Bits für die Daten 
sind an sich nicht schlecht, wenn ich nachher noch Berechnungen 
durchführen will.

Was jetzt natürlich für spätere Zwecke evtl. interessant wäre:
Beide Kanäle vor und nach dem Filter zu streamen. Also 4x24 Bits reine 
Daten. Gerade wenn ich Stereosignale von außen analysieren möchte.

Mit den 3 MBaud komme ich da ja leider nicht hin.

Selbes Format wie zuvor:
14 Byte -> 112 Bits
112 x 32000 x 1,4 = 5,0176 MBaud.
Z.B. so:

                        -- Daten vor Filter
      -- KLLLLLL1
      -- LLLLLLL0
      -- LLLLLLL0
      -- LLLLRRR0
      -- RRRRRRR0
      -- RRRRRRR0
      -- RRRRRRR0

                        -- Daten nach Filter
      -- KLLLLLL1
      -- LLLLLLL1
      -- LLLLLLL1
      -- LLLLRRR1
      -- RRRRRRR1
      -- RRRRRRR1
      -- RRRRRRR0

Da müsste ich dann BRAM verwenden, oder? Es wäre doch evtl. sinnvoll 
sowieso BRAM zu verwenden, damit ich dann IMMER meine 4x24 Bit auswerten 
kann und weniger Fehler habe?

>>Richtig. Ich würde der Einfachheit halber den BRAM weglassen.
Wird das recht kompliziert? Den Link hast du schon mal gepostet:
http://www.lothar-miller.de/s9y/archives/20-RAM.html

Wäre vielleicht sinnvoll, das jetzt in einem Wisch zu machen. :)
Was meinst du Gustl? Vielen Dank im Voraus! :)

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Denkst du ich sollte dann überhaupt nochmal mit der
> Baudrate runtergehen, oder es so belassen?

Kommt drauf an wie viele Fehler du bekommst und ob das für dich OK ist. 
Wenn du zufrieden bist, dann lass es.

Mario A. schrieb:
> Also 4x24 Bits reine
> Daten. Gerade wenn ich Stereosignale von außen analysieren möchte.

Tja, das geht leider nicht. Aber du kannst dir einen anderen UART 
kaufen. Der FT2232H und FT232H können bis 12 MBaud. Da gibt es fertige 
Module von FTDI 
https://www.ftdichip.com/Products/Modules/DevelopmentModules.htm und 
anderen Herstellern. Da müsstest du dann nur TX und Masse verbinden. Du 
kannst auch das Zybo verwenden das du ja hast, da ist ebenfalls ein 
FT2232H drauf.

Wenn du beim FT232R bleiben willst, dann ist das Limit bei 3 MBaud. Ein 
Paket darf dann maximal 7 Bytes groß sein. Das sind ohne die LSBs 49 
Nutzbits. Da passen als 12 Bits/Kanal rein. Das wären dann 48 Bits.
Format:
Kanäle A,B,C,D

0AAAAAA1
AAAAAAB0
BBBBBBB0
BBBBCCC0
CCCCCCC0
CCDDDDD0
DDDDDDD0

Tja wie ist das mit dem BRAM, das würdest du zum Aufnehmen verwenden. 
Also Aufnahme starten, warten bis der Speicher voll ist, dann Speicher 
über UART auslesen.
Wie viel von diesem BRAM hat dein XC7K325T auf dem Board und wie lange 
könnte man da aufnehmen? Rechnen wir das mal aus:

Das FPGA hat 16020 kBit BRAM. Das sind dann ca. 2 MByte.
Bei vollen 24 Bits und 32 kSample/s benötigt ein Kanal 96 kByte/s. Du 
kannst also bei
1 Kanal ca. 20 Sekunden
2 Kanäle ca. 10 Sekunden
...
aufnehmen. Wenn dir 5 Sekunden bei 4 Kanälen reichen dann wäre das ein 
möglicher Weg.

Welche Möglichkeiten gibt es noch?
Du hast da noch viel externes RAM drauf, du kannst die Daten nicht nur 
über UART sondern auch über andere Schnittstellen an den PC schicken.

von Mario A. (itse_me_mario)


Lesenswert?

>>Aber du kannst dir einen anderen UART kaufen.
Das wäre eine Option.

>>Du kannst auch das Zybo verwenden das du ja hast
Da gabs ja leider das Problem mit den Pins im XDC.

>>Wenn dir 5 Sekunden bei 4 Kanälen reichen
Das ist schon etwas knapp ehrlich gesagt, aber könnte reichen für 
spätere Messungen mit dem adaptiven Filter.

>>auch über andere Schnittstellen an den PC schicken.
Okay das wäre auch eine Option, wenn es wieder Dateien zum Einbinden 
(wie diese von Lothar Miller) gibt. Das soll jetzt nämlich nicht meinen 
Schwerpunkt darstellen, aber es wäre halt sehr cool! :)

Das Modul würde ja wahrscheinlich von den Daten her funktionieren oder?
https://ftdi-uk.shop/collections/usb-uart-module/products/ft2232h-56q-mini-module
https://www.ftdichip.com/Support/Documents/DataSheets/Modules/DS_FT2232H-56Q_Mini_Module.pdf
Aber kann ich das dann nicht einfach anschließen, also nur draufstecken, 
oder? RXD und TXD müsste ich dann vermutlich löten? Ich seh einen 
USB-Anschluss.

Vielen Dank dir!

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Okay das wäre auch eine Option, wenn es wieder Dateien zum Einbinden
> (wie diese von Lothar Miller) gibt. Das soll jetzt nämlich nicht meinen
> Schwerpunkt darstellen, aber es wäre halt sehr cool! :)

So einfach wird das nicht. Du hast da noch Ethernet drauf und du hast da 
einen FT2232H drauf der ein FIFO Interface und ein SPI Interface hat. 
Von FPGA Seite ist das nicht so irre kompliziert, nur minimal 
schwieriger als UART. Aber auf der PC Seite musst du vermutlich 
irgendeine API nutzen.

Mario A. schrieb:
> Das Modul würde ja wahrscheinlich von den Daten her funktionieren oder?
> 
https://ftdi-uk.shop/collections/usb-uart-module/products/ft2232h-56q-mini-module

Ja, würde es. Das hat eine USB Buchse (Micro-USB) und RX/TX sind eben 
auf so Pfosten. Löten musst du da nix, du nimmst dir einen Draht und 
verbindest einen Pin im PMOD mit dem RX Pin auf dem FTDI Board. Und 
Masse solltest du noch verbinden.

-------------------------
Als weitere Möglichkeit könnte man den FT2232H auf dem FPGA Board 
umflashen, also das EEPROM, damit der statt dem FIFO Interface und dem 
SPI ein UART darstellt. Aber das ist legal nicht möglich weil man da die 
JTAG Lizenz von Digilent mitclonen müsste. Ist aber möglich, ich hatte 
das hier getan, werde aber sonst keine Tipps dazu geben:
Beitrag "FT2232H JTAG clonen?"
Lass da die Finger weg wenn du nicht weißt was du tust.

von Mario A. (itse_me_mario)


Lesenswert?

>>Das hat eine USB Buchse (Micro-USB) und RX/TX sind eben auf so Pfosten.
Das klingt sehr vielversprechend!

>>du nimmst dir einen Draht und verbindest einen Pin im PMOD mit dem RX Pin auf 
dem FTDI Board. Und Masse solltest du noch verbinden.
Stimmt ich könnte das ja einfach auf den PMOD routen. Ich hätte noch so 
Standard Arduino-Leitungen.

Gibt nur 1 Modul in diesem Shop: 
https://ftdi-uk.shop/collections/usb-uart-module/products/ft2232h-56q-mini-module

Danke für den Tipp!

: Bearbeitet durch User
von Mario A. (itse_me_mario)



Lesenswert?

Ich habe mir jetzt dieses Modul bestellt:
https://ftdi-uk.shop/collections/usb-uart-module/products/ft2232h-56q-mini-module
Bin gespannt, danke schon mal für den Tipp!


Ich bin gerade dabei mein eigens erstelltes FIR-Filter zu testen. Als 
Vergleich nutze ich dieselben Filterkoeffizienten mit dem Xilinx 
FIR-Compiler Projekt und schaue mir den Frequenzgang an.

24 Bit Daten, 16 Bit Koeffizienten
Linker Kanal: Fpass 1000 Fstop 2000
Rechter Kanal: Fpass 4000 Fstop 5000
Filterordnung: 81 -> 82 Koeffizienten

Der Vergleich mit den selben Filterkoeffizienten weist eine Verschiebung 
auf (siehe VglXilinx32kHzundMeinFIR32kHz.PNG).

Ich habe nun mal die Koeffizienten für eine Abtastrate von 16 kHz 
designed und bei meinem FIR-Filter eingebunden. Nun sehen sich die 
Frequenzgänge des Xilinx FIR Compilers mit den für eine Abtastrate von 
32 kHz designten Koeffizienten und meines FIR Filters mit den für 16 kHz 
designenten Koeffizienten sehr ähnlich. Ich denke also, dass ich 
versehentlich statt mit einer Abtastrate von 32 kHz nur mit 16 kHz 
arbeite.

Ich werde jetzt mal versuchen eine FSM in meine Filtermodule für den 
Datenaustausch einzubauen. Wahrscheinlich braucht meine Version aktuell 
2x32 kHz Durchläufe, also schlussendlich 16 kHz.

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Ich denke also, dass ich
> versehentlich statt mit einer Abtastrate von 32 kHz nur mit 16 kHz
> arbeite.

Möglich, wenn du wieder was am Takt verändert hast.

Gen2_FIR_D24_C16.zip (93,5 MB, 0 Downloads)

Lass das besser mit so irre fetten Anhängen.

von Mario A. (itse_me_mario)


Lesenswert?

>>Möglich, wenn du wieder was am Takt verändert hast.
Eigentlich habe ich da nichts verändert. Es hat davor auch noch nicht 
richtig funktioniert.

Ich hoffe das lässt sich mit einer FSM in den Filter-Dateien lösen.

>>Lass das besser mit so irre fetten Anhängen.
Ja habs zu spät gesehen, dass der Bitstream auch noch dabei ist... 
Einfach nur die einzelnen Dateien und die xci und xml-Files reichen, 
oder?

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Einfach nur die einzelnen Dateien und die xci und xml-Files reichen,
> oder?

Ja. Und das brauchst du auch nur anhängen wenn du eine Frage dazu hast. 
Bei deinen Bildchen finde ich die Darstellung irgendwie komisch. Vor 
allem die y-Achse oder schafft das Filter wirklich nur eine so geringe 
Dämpfung?

von Mario A. (itse_me_mario)


Lesenswert?

Habe jetzt mal noch eine FSM und zwei parallele Datenregister 
eingebunden. Führt leider zum selben Ergebnis wie ohne FSM, also lad ich 
es gar nicht erst hoch... Irgendwo hab ich noch einen Fehler drin. 
Scheint ich kreiere mir ein 16 kHz Filter. :D Die Abtastrate sollte ja 
meinem LRCK (32 kHz) entsprechen, denke ich.
Werd mir das morgen nochmal ansehen.

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Die Abtastrate sollte ja
> meinem LRCK (32 kHz) entsprechen, denke ich.

Das könntest du in der Simulation sehen.

von Mario A. (itse_me_mario)


Lesenswert?

So war das nicht gemeint. :) Ich meine die Abtastrate sollte ja an sich 
32 kHz sein, wenn LRCK 32 kHz hat (und LRCK hat 32 kHz).
Ich vergleiche gerade die Xilinx-FIR-Compiler Version mit meiner 
Version.

von Mario A. (itse_me_mario)


Lesenswert?

Die Koeffizienten werden falsch in meinem Koeffizientenarray 
abgespeichert. Das änder ich morgen ab. Hoffentlich funktionierts dann!

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

Irgendwie spinnt Vivado bei mir... Meine Änderungen werden in der 
Testbench oftmals nicht aktualisiert, auch wenn ich die Simulation neu 
starte (Relaunch Simulation). Manchmal muss ich das Projekt dann 
umbenennen und an einem anderen Ort speichern...

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Das Problem kenne ich auch. Was hilft ist:
Vivado oder zumindest Projekt schließen und im Projektordner den Ordner 
mit der Endung .sim löschen. Abar ja, ist extrem nervig und ich kenne 
keine bessere Lösung.

von Mario A. (itse_me_mario)



Lesenswert?

Leider funktioniert mein Filter noch nicht so wie es soll. Ich habe die 
Rundung mit Bias und die Overflow-/Underflowerkennung weggelassen - 
leider ohne Veränderung.

Außerdem habe ich die Koeffizienten nun mal direkt als Konstantenarray 
eingebunden.

Muss ich vllt den ungefilterten Wert erst verzögert mit dem gefilterten 
Wert ausgeben? Ich schaue mir nachher nochmal an, wie das hier gemacht 
wurde: 
https://github.com/ems-kl/zedboard_audio/blob/master/hdl/audio_testbench.vhd






@Gustl
Das UART-Modul ist heute gekommen!
Darf ich dich nochmal fragen, ob das so passt? Möchte nichts falsch 
machen. :)

Folgende Schritte:

1. Ich weise jetzt quasi an 2 PMOD-Pins (z.B. PMOD Port B, Pin 1 und 2) 
2 Signale für das Senden und Empfangen im Code zu (also 1x Out und 1x 
In). Die Signale weise ich im Code dem UART-Modul wie im vorherigen 
UART-Projekt zu (siehe AudioInterface.vhd).

2. Mit 2 Leitungen schließe ich diese Signale vom PMOD an das UART-Modul 
an.

3. Den PMOD Massepin (Pin 5 oder 11) verbinde ich zus. noch mit dem 
Massepin des UART-Moduls. Frage: Brauche ich diese Masseverbindung 
überhaupt?

4. Das UART Modul verbinde ich über den Micro USB Anschluss mit dem PC.


Ich bin mir nachdem ich das Datenblatt gelesen habe leider noch nicht 
100% sicher, wie das Modul richtig anzuschließen ist.

Ich hätte jetzt mal beide Jumper (JP1 und JP2) draufgelassen, damit 
sollte das Modul "USB Bus-powered" sein (siehe Seite 8: 
http://www.farnell.com/datasheets/2035221.pdf)


>>du nimmst dir einen Draht und verbindest einen Pin im PMOD mit dem RX Pin auf 
dem FTDI Board.

https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT2232H.pdf
Ich suche gerade, welche Pins ich verbinden muss. Ich denke es ist Pin 
16 (TxD) und 17 (RxD) des FT2232H und somit VIO = Pin 16 und AD4 = Pin 
17 auf dem UART-Modul, siehe DS_FT2232H, S. 11.

Also müsste ich jetzt den PMOD Pin 2 (Tx) mit dem Pin 17 (AD4) verbinden 
und den PMOD Pin 1 (Rx) mit dem Pin 16 (VIO)? Für VIO gibt es sogar 
mehrere Pins auf dem UART-Modul.


Ich habe als Treiber den VCP (x64 (64-bit) für Windows heruntergeladen 
(siehe Treiber.png). Muss ich da jetzt noch was machen..? In dem 
heruntergeladenen Ordner ist nämlich keine exe.

Meinen angepassten Code für das Senden von 14 Bytes, also beide Kanäle 
vor und nach dem Filter, habe ich schon mal angehängt 
(AudioInterface.vhd). Da muss aber noch die Baudrate geändert werden.
14 Byte x 8 x 32 kHz x 1,4 = 5,0176 MBaud. Könnte ja mal 6 MBaud 
einstellen.

Treiberseite: https://www.ftdichip.com/FTDrivers.htm
Ich lese mir morgen mal den Installation Guide durch. 
https://www.ftdichip.com/Support/Documents/InstallGuides/AN_396%20FTDI%20Drivers%20Installation%20Guide%20for%20Windows%2010.pdf

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Frage: Brauche ich diese Masseverbindung
> überhaupt?

Ja. Ohne Bezugspotential geht das nicht.

Mario A. schrieb:
> Ich hätte jetzt mal beide Jumper (JP1 und JP2) draufgelassen, damit
> sollte das Modul "USB Bus-powered" sein

Ja, klingt OK.

Mario A. schrieb:
> siehe DS_FT2232H, S. 11.

Für dich ist aber Seite 18 relevant.

Daher ist das am FT2232H-56Q die Pins 12 und 13 oder 32 und 33. Also 
ADBUS0 und ADBUS1 oder BDBUS0 und BDBUS1 und somit an deinem Modul die 
Pins CN2-7 AD0 und CN2-10 AD1 oder CN3-26 BD0 und CN3-25 BD1.

Mario A. schrieb:
> In dem
> heruntergeladenen Ordner ist nämlich keine exe.

Ganz normal. Verbinde das Modul mit dem PC und gucke erstmal ob das 
nicht automatisch erkannt wird. Wenn nein, dann lass Windows einen 
Treiber im Internet suchen. Wenn das nicht geht, dann sage Windows, dass 
es einen Treiber aus deinem Ordner da installieren soll.
Wenn das Modul installiert wurde sollte es mit FT_Prog 
https://www.ftdichip.com/Support/Utilities.htm#FT_PROG gefunden werden 
und der Gerätemanager im Windows sollte die zwei serielle 
Schnittchenstellen anzeigen.

Mario A. schrieb:
> Könnte ja mal 6 MBaud einstellen.

Zitat:
Please notethe FT2232H doesnot support the baud rates of 7 Mbaud 9 
Mbaud, 10Mbaud and 11 Mbaud.

Also beachte, dass manche Baudraten nicht unterstützt werden. Ich habe 
erfolgreich 12 MBaud, 8 MBaud und 921600 Baud verwendet. Andere habe ich 
nicht probiert. Das PySerial aus Python kann diese Baudraten auch, 
welche noch weiß ich nicht.

Und dann noch das mit der Filterei:

Du kannst schön die Daten an den Filterkoeffizienten vorbeischieben. 
Dabei musst du ebenfalls jeweils die Summe mit den Daten verschieben.

Du kannst aber auch anders herum die Koeffizienten an den Daten 
vorbeischieben und ebenfalls je eine Summe vorbeischieben.

Ich weiß gerade nicht ob du alle Multiplikationen in einem Takt machen 
willst oder ob du das seriell nacheinander machst. Jedenfalls ist es so:

Du hast n Koeffizienten und n Abtastwerte, wobei der Abtastwert 1 der 
Neueste ist und der Abtastwert N der Älteste. Wenn ein neuer Abtastwert 
reinkommt, dann muss folgendes passieren:

- Die Abtastwerte werden um 1 verschoben indem du den Abtastwert n 
wegwirfst und am anderen Ende den neuen Abtastwert hinzufügst.

Abtastwerte <= neuer_Abtastwert & Abtastwerte(2 ... n)

- Du beginnst mit einer neuen Summe, also beim Summenwert Null.
- Du multiplizierst und addierst jetzt für alle m in 1 ... n:
Summe <= Summe + Abtastwert(m)*Koeffizienten(m)

- Du skalierst die Summe, das wird ja eine große Zahl indem du sie durch 
eine Zweierpotenz teilst.

Wenn du konkreten Pseudocode haben möchtest, dann wüsste ich gerne ob du 
das als Pipeline, in einem Takt oder seriell rechnen lassen möchtest.

von Mario A. (itse_me_mario)



Lesenswert?

>>Ja. Ohne Bezugspotential geht das nicht.
Ah ja stimmt... Micro USB geht ja auf den PC nicht aufs FPGA.. 
Denkfehler. :D

>>Für dich ist aber Seite 18 relevant.
Perfekt, danke!

>>Ganz normal. Verbinde das Modul mit dem PC und gucke erstmal ob das
nicht automatisch erkannt wird.
Hast Recht, es zeigt mir 2 USB-Ports an (COM17 und COM18), siehe 
USBAnschluesse.PNG. Danke.

>>Ich habe erfolgreich 12 MBaud, 8 MBaud und 921600 Baud verwendet.
Alles klar, danke! Ich verwende jetzt mal 8 MBaud.

Ich habe jetzt mal alles angeschlossen:
PMOD Pin 1 (FT2232H_RX_PMOD) geht auf AD1 (RxD)
PMOD Pin 2 (FT2232H_TX_PMOD) geht auf AD0 (TxD)
PMOD Pin 5 auf GND

Jetzt muss ich noch das Pythonskript anpassen (neues Datenformat und 
neue Baudrate). Mal schauen, ob ich es vor Mittag noch schaffe. :) Muss 
leider später weg...



>>Du kannst schön die Daten an den Filterkoeffizienten vorbeischieben.
Ich möchte alle Multiplikationen parallel in einem Takt haben. Meine 
Mutliply Adder Blöcke haben an sich alle dasselbe Startsignal und 
schreiben ihr Ergebnis alle in das Array a_sop. Eigentlich müssten die 
Werte so automatisch immer weitergeschoben werden. Aber vllt sollte ich 
mal ein zus. Schieberegister einbauen.

Derselbe Eingangswert liegt auch an allen Blöcken gleichzeitig an. Nur 
das Array a_sop (Summe der Produkte) sollte sich nach jeder Berechnung 
ändern. Und die Berechnung von allen Blöcken wird eigtl. gleichzeitig 
gestartet.

Hab dir nochmal ein Bild angehängt (die Datenformate sind allerdings 
veraltet). Vllt sollte ich wirklich noch ein zusätzliches 
Schieberegister einbauen, welches die Werte a_sop weiterschiebt, wobei 
es ja so eigtl. automatisch klappen müsste.

Scheint irgendwie so zu sein, dass ich mir aus den 32 kHz Abtastrate 
intern versehentlich eine 16 kHz Abtastrate mache.

Vielen Dank Gustl! Hast mir mal wieder sehr gut geholfen.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

Ich habe gerade mal versucht die Daten mit Python einzulesen und als 
.bin zu speichern, aber hat leider nicht auf Anhieb geklappt. Habe es 
mit COM17 und COM18 versucht, aber beide male war das Bin-File leer. Ich 
muss mir das morgen nochmal genauer anschauen. 8 MBaud habe ich im Code 
angegeben. Vllt haben die Leitungen auch keinen guten Kontakt oder mein 
gebasteltes Framework hat noch einen Fehler.

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Ich möchte alle Multiplikationen parallel in einem Takt haben.

Ja, in der Tat, das könnte klappen. Die Multiplikationen sind ja 
unabhängig voneinander, die laufen gut in einem Takt. Aber die Summe ist 
nicht unabhängig von der Summe vorher. Das bedeutet, die Summe müsste 
ebenfalls in einem Takt gebildet werden und damit das funktioniert 
natürlich erst wenn die Multiplikationen fertig sind.

Wenn du dir in deiner Kette den zweiten Mutliply Adder anguckst. Der 
bekommt irgendwann ein Startsignal - so wie die neben ihm auch. Was 
leigt zu diesem Zeitpunkt an seinem C Post an? Das ist noch nicht das 
Ergebnis der Multiplikation vom ersten Mutliply Adder. Sondern das ist 
das Ergebnis des ersten Mutliply Adder aus dem vorherigen Takt.

Wenn du das wirklich so machen willst, dann würde ich die Summe 
kombinatorisch bilden. Und damit das tatsächlich funktioniert, das wird 
ja eine Summe über viele Werte in einem Takt, solltest du das 
constrainen. Es reicht ja wenn die mit 32 kHz fertig wird. Das ist 
machbar, aber nicht innerhalb eines Taktes bei 200 MHz. Wenn du das 
willst musst du die Summe schrittweise berechnen.

Ich finde das mit dem Vorbeischieben von Koeffizienten, Abtastwerten 
oder Summe recht einfach, geht aber auch anders.

Du kannst auch jetzt schreiben bei N Koeffizienten:
1
type sum_array_type is array (0 to N-1) of signed(47 downto 0); 
2
signal sum_array: sum_array_type;
3
4
process begin
5
   wait until rising_edge(clk);
6
   for I in 0 to N-1 loop
7
      sum_array(I) <= Koeffizienten(I) * Abtastwert;
8
   end loop;
9
end process;

Und dann entweder kombinatorisch eine Summe bilden:
1
signal Summe: signed(63 downto 0):=(others => '0');
2
3
process(sum_array)
4
   variable Laufende_Summe: signed(63 downto 0);
5
begin
6
   Laufende_Summe:= (others => '0');
7
   for I in sum_array'range loop
8
      Laufende_Summe := Laufende_Summe + sum_array(I);                         
9
   end loop;
10
   Summe <= Laufende_Summe;    
11
end process;

Das ist dann aber langsam und funktioniert garantiert nicht in einem 
Takt bei 200 MHz.

Oder du machst die Summenbildung getaktet, dauert aber viele Takte.

Ich finde das mit der Stückweisen Berechnung einfacher, da wird quasi 
die Summe geschoben.
1
type sum_array_type is array (0 to N-1) of signed(47 downto 0); 
2
signal sum_array: sum_array_type;
3
4
signal Ausgabewert: signed(23 downto 0):=(others => '0');
5
6
process begin
7
   wait until rising_edge(clk);
8
   sum_array(0) <= Koeffizienten(0) * Abtastwert
9
   for I in 1 to N-1 loop
10
      sum_array(I) <= sum_array(I-1) + Koeffizienten(I) * Abtastwert;
11
   end loop;
12
   Ausgabewert <= (sum_array(N-1)*ganze_Zahl)/2**exponent;
13
end process;

Wobei ganze Zahl und Exponent so zu wählen sind dass die Amplitude am 
Ausgang wieder passt.

Mario A. schrieb:
> aber beide male war das Bin-File leer.

Mario A. schrieb:
> Ich habe jetzt mal alles angeschlossen:
> PMOD Pin 1 (FT2232H_RX_PMOD) geht auf AD1 (RxD)
> PMOD Pin 2 (FT2232H_TX_PMOD) geht auf AD0 (TxD)
> PMOD Pin 5 auf GND

Genau. Vertausche mal die Pins, also verbinde die über Kreuz. Denn der 
RX Pin auf deinem FT2232H Board will ja die Daten von dem TX Pin des 
FPGA Boards.

: Bearbeitet durch User
von Mario A. (itse_me_mario)



Lesenswert?

>>Sondern das ist das Ergebnis des ersten Mutliply Adder aus dem vorherigen Takt.
Das würde ja an sich passen? Hab dir das mal in den Anhang gehängt. Der 
Ansatz müsste doch der transponierten Direktform 2 entsprechen.


>>Denn der RX Pin auf deinem FT2232H Board will ja die Daten von dem TX Pin des 
FPGA Boards.
Oh stimmt, vielen Dank!! Scheint jetzt zu funktionieren. Hab mal die 
beiden Binaries angehängt.

Ich schreibe jetzt mal den Pythoncode für die Decodierung um. :)

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Das würde ja an sich passen? Hab dir das mal in den Anhang gehängt. Der
> Ansatz müsste doch der transponierten Direktform 2 entsprechen.

Aus dem Bildchen verstehe ich das nicht, aber oben hast du ja das 
Schematic Bild FIR Zoom gezeigt. Hm, ja das könnte schon passen, bin mir 
aber nicht sicher. Die Summe wandert da tatsächlich der Reihe nach 
durch. Hast du das schonmal simuliert?

Mario A. schrieb:
> Hab mal die
> beiden Binaries angehängt.

Die gucke ich mir nachher mal an, werde mich melden.

von Mario A. (itse_me_mario)


Lesenswert?

>>Hast du das schonmal simuliert?
Ich hab das schon mal simuliert, aber die Werte könnte ich trotzdem 
nochmal prüfen.
Ich setze später einfach mal den Filterwert auf 1 und setze alle 
Koeffizienten auch auf 1. Dann sollte man sehr schön sehen, wie sich der 
Wert schrittweise erhöht.

Bin gerade dabei die Decodierung umzuschreiben. Da muss ich mich erst 
mal noch einlesen.

von Gustl B. (-gb-)


Lesenswert?

Das ist das Datenformat?
1
packet <=   '0' & s_audio_l_out(23 downto 18) & '1'
2
                & s_audio_l_out(17 downto 11) & '0'
3
                & s_audio_l_out(10 downto 4)  & '0'
4
                & s_audio_l_out(3 downto 0)   & 
5
                
6
                s_audio_l_in(23 downto 21)    & '0'
7
                & s_audio_l_in(20 downto 14)  & '0'
8
                & s_audio_l_in(13 downto 7)   & '0'
9
                & s_audio_l_in(6 downto 0)    & '0' &
10
11
            '0' & s_audio_r_out(23 downto 18) & '0'
12
                & s_audio_r_out(17 downto 11) & '0'
13
                & s_audio_r_out(10 downto 4)  & '0'
14
                & s_audio_r_out(3 downto 0)   & 
15
                
16
                s_audio_r_in(23 downto 21)    & '0'
17
                & s_audio_r_in(20 downto 14)  & '0'
18
                & s_audio_r_in(13 downto 7)   & '0'
19
                & s_audio_r_in(6 downto 0)    & '0';

Was ich nicht verstehe sind die Dateinamen:
UART_FT2232H_8MBaud_Signals_Right.bin
UART_FT2232H_8MBaud_Signals_Left.bin

Dein Datenformat enthält doch schon beide Kanäle, wieso also zwei 
Dateien?

Mario A. schrieb:
> Bin gerade dabei die Decodierung umzuschreiben. Da muss ich mich erst
> mal noch einlesen.

Geduld, Geduld, kommt gleich (-:

von Mario A. (itse_me_mario)


Lesenswert?

Also es ist ja so, dass ich nur immer an einem Kanal die Sweeps machen 
kann. Deswegen brauche ich 2 Dateien. An sich würde eine reichen, wenn 
ich für beide Channels sweepen könnte.

Muss nur noch rausfinden was du bei L_bits und R_bits machst. :) Der 
Rest steht schon.

Datenformat stimmt ja!

Hab ein falsches Binary hochgeladen. Änder ich noch! Da sind keine 
Sweeps dabei.

: Bearbeitet durch User
von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

Mario A. schrieb:
> Also es ist ja so, dass ich nur immer an einem Kanal die Sweeps machen
> kann.

Einfach elektrisch verbinden mit einem Draht^^

Mario A. schrieb:
> Muss nur noch rausfinden was du bei L_bits und R_bits machst. :) Der
> Rest steht schon.

Naja, ich fische die jeweiligen Bits raus.

#Format:
#0AAAAAA1 -- Byte 0
#AAAAAAA0 -- Byte 1
#AAAAAAA0 -- Byte 2
#AAAABBB0 -- Byte 3

Wie hole ich also die Bits für A?

Das Vorzeichen ist das MSB, also das Bit 6 aus Byte 0. Das kann man 
abfragen durch Maskierung, es hat den Wert 64.
Wir machen also eine AND Verknüpfung:

Byte_0 & 64.

Ist das Bit 6 gesetzt dann bekommt man 64 als Wert zurück wenn nicht 
dann Null. Zwei Beispiele:

Byte 0 = 01111001
01111001 & 01000000 = 01000000

Byte 0 = 00111001
00111001 & 01000000 = 00000000

Jetzt will ich aber statt 64 oder 0 die Werte 1 und 0. Also teile ich 
noch durch 64.

A_sign = int((sr[0] & 64)/64)

Wie bekomme ich die anderen 23 Bits von A?

Die übrigen Bits von A in Byte 0 kann man ebenfalls maskieren mit dem 
Wert 62. Denn 62 = 00111110

Byte_0 & 62 liefert dann die maskierten Bits zurück. Beispiele:

Byte 0 = 01111001
01111001 & 00111110 = 00111001

Byte 0 = 00010111
00010111 & 00111110 = 00010110

Das sind aber 5 mmaskierte Bits, und das LSB wird nicht maskiert, der 
Wert ist also um Faktor 2 zu hoch. Muss man noch bedenken.

Bei den anderen Bytes wie Byte 1 ist das einfacher.
#AAAAAAA0 -- Byte 1
Da werden alle Bits für A verwendet bis auf das LSB. Also kann man 
einfach alle Bits um 1 nach rechts verschieben und dann den 7-Bit-Wert 
verwenden.

Wert = Byte1 >> 1

Bei Byte_3 muss dann um 4 Bits nach rechts verschoben werden.

Und dann kommen noch die Zweierpotenzen dazu, die sind nötig um die Bits 
mit der passenden Wertigkeit innerhalb des 24 Bit Wertes zu 
darzustellen.

Also zusammen:
Vorzeichen(A) = (Byte_0 & 64)/64
Wert(Byte_0) = Byte_0 & 62
Wert(Byte_1) = Byte_1 >> 1
Wert(Byte_2) = Byte_2 >> 1
Wert(Byte_3) = Byte_3 >> 4

Gesamtwert = Wert(Byte_0)*2**17 + Wert(Byte_1)*2**11 + Wert(Byte_2)*2**4 
+ Wert(Byte_3)

Und dann mit dem Vorzeichen muss man eben gucken ob das positiv ist oder 
negativ.

So, im Anhang Code und Bildchen, sieht doch gut aus, Gratulation! Aber 
beachte auch die y-Skala, das ist teilweise ein sehr geringer 
Wertebereich und wohl eher leise.

Edit:
Und du hast weiterhin einige Fehler in den Übertragungen. Woran das 
liegt weiß ich nicht. Es sind 534746 korrekte Pakete und 28904 Fehler. 
Also grob 5 %, finde ich viel. Kann man Python liegen, da müsstest du 
öfter mehr vom UART lesen damit der nicht überläuft, kann aber auch an 
der Elektrik liegen, das würde man mit dem Oszi sehen.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

So hätte ich das jetzt gemacht. Scheint noch etwas falsch zu sein.

Ah ich seh grad du warst schneller. :D :D :D Vielen Dank! Danke für die 
ausführliche Erklärung! Also haben die Binaries gepasst.

Ich schaus mir gleich an. Mal schauen wo mein Fehler ist. ;)

: Bearbeitet durch User
von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

So, jetzt hab ich mal die Fehler geplottet. Sieht sehr periodisch aus. 
Das könnte also sein, dass der UART Empfangspuffer überläuft weil das 
Python zu selten/zu wenige Daten abholt.

von Mario A. (itse_me_mario)


Lesenswert?

Hab schon ein paar kleine Fehler im Code gefunden.

>>Das könnte also sein, dass der UART Empfangspuffer überläuft weil das
Python zu selten/zu wenige Daten abholt.
Oh ok. Danke für den Hinweis. Soll ich an den Einstellungen was ändern?

von Gustl B. (-gb-)


Lesenswert?

Der/ein Fehler in deinem Python ist, dass das jetzt 14 Bytes sind von 
Byte 0 bis Byte 13. Rin_bits muss also auch die Bytes 10,11,12 und 13 
umfassen.

Und:
Code am besten als Text anhängen und nicht als Bild.

Mario A. schrieb:
> Soll ich an den Einstellungen was ändern?

Klar, sonst bleiben die Fehler ja weiterhin. Du sollst also häufiger 
Daten vom UART abholen. Also zwischendurch kürzer schlafen gehen
sleep(0.001)
oder gar nicht schlafen. Vielleicht geht das auch anders, weiß ich aber 
nicht. Du kannst das auch in C und vermutlich auch in Matlab machen wenn 
du die D2XX von FTDI verwendest, hier 
https://www.ftdichip.com/Support/Documents/ProgramGuides/D2XX_Programmer%27s_Guide(FT_000071).pdf 
ist die API schön mit Beispielen beschrieben.

von Mario A. (itse_me_mario)



Lesenswert?

Ja habs schon gesehen, danke. :) Ich wusste, dass ich´s ändern muss aber 
hab versehentlich nur das am Zeilenanfang angepasst.

Also die Aufnahmen wurden mit sleep(0.001) gemacht. Dann versuche ich es 
mal mit sleep(0.0001) oder noch weniger. Danke!

Hab nochmal die 2 Binaries angehängt (sleep ist hier noch auf 0.001). 
Bei dem einen Kanal war leider noch kein Sweep drauf, hab vergessen 
umzustecken. Deswegen waren die Werte so gering.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Ich wusste, dass ich´s ändern muss aber
> hab versehentlich nur das am Zeilenanfang angepasst.

Sowas passiert mir auch oft. Da merkt man sich etwas, findet eine 
weitere Baustelle und kommt dann nicht zurück weil man es vergessen hat.

Mario A. schrieb:
> Also die Aufnahmen wurden mit sleep(0.001) gemacht. Dann versuche ich es
> mal mit sleep(0.0001) oder noch weniger. Danke!

Dann nimm das sleep mal komplett raus.

Mario A. schrieb:
> Hab nochmal die 2 Binaries angehängt

Da hast du was anderes hochgeladen ...

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

Die neuen haben an jedem Channel 1 Sweep. Also du brauchst beide Dateien 
damit du dir den linken und rechten Channel ansehen kannst. Habs mir 
gerade mit deinem Code plotten lassen. :) Danke!

Ja ich mach nochmal die Aufnahmen.

von Mario A. (itse_me_mario)



Lesenswert?

Hier die neuen Binaries. Einmal mit sleep(0.0001) und einmal ohne sleep. 
:) Morgen geht´s weiter! :) Vielen Dank Gustl!!

: Bearbeitet durch User
von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

Mario A. schrieb:
> und einmal ohne sleep.

Naja, da müsstest du natürlich länger laufen lassen. Deine Aufnahmen 
sind sonst extrem kurze Zeit weil die Schleife sehr schnell durchläuft. 
Vielleicht solltest du die Aufnahme auch nicht an Hand der 
Schleifendurchläufe beenden sondern an Hand der empfangenen Datenmenge.

Das sähe so aus für die while-Schleife:

Aufnahmedauer_in_Sekunden = 1
...
while len(sr) < 14*32000*Aufnahmedauer_in_Sekunden:
...

dann nimmt der also bei
Aufnahmedauer_in_Sekunden = 1
so lange auf bis
14*32000*1 = 448000 Bytes empfangen wurden.

Mario A. schrieb:
> Vielen Dank Gustl!!

Bitte, viel Erfolg!

Mario A. schrieb:
> einmal ohne sleep

Auch da sind periodisch Fehler drinnen, seltsam. Muss ich mir mal 
genauer durchsimulieren.

: Bearbeitet durch User
von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

So, mir war langweilig^^ nein, ich möchte nur gucken ob nicht in dem 
Code von mir ein Fehler steckt. Daher habe ich jetzt das Python in VHDL 
übersetzt und in die Simulation gesteckt, das sieht jetzt so aus:

ADC erzeugt Sweep -> I2S -> FIR -> I2S -> DAC

Und der UART gibt die ungefilterten und die gefilterten Werte aus mit 8 
MBaud in Paketen mit je 14 Bytes.

In der Testbench wenden die Werte mit einem zweiten UART eingelesen, in 
einem Schieberegister (wie im Python) geschoben, die LSBs geprüft, 
Pakete erkannt, die Abtastwerte A,B,C,D zusammengebaut aus den Bits der 
14 Bytes eines Pakets und dann werden die in ein Textfile UART.txt unter
.\Forum_Audio\Forum_Audio.sim\sim_1\behav\xsim
geschrieben. Die kann man dann mit dem Python im Anhang plotten.
Wenn Fehler auftauchen wird das reportet.
(Ein Fehler wäre, wenn 14 Bytes nach einem korrekten Paket kein 
korrektes Byte erkannt wird.)

Tja weil Simulieren lange dauert habe ich nur 100 ms simuliert. Und da 
war kein Fehler drinnen.

Im Anhang Projekt als .zip, Testbench, Python um das erzeugte UART.txt 
zu plotten, Bildchen vom Plot.

: Bearbeitet durch User
von Mario A. (itse_me_mario)



Lesenswert?

>>Und da war kein Fehler drinnen.
Danke Gustl! Die Simulation läuft gerade. Also läuft bei der Decodierung 
mit Python noch etwas falsch?

Ich habe jetzt nochmal Aufnahmen vom linken und rechten Kanal gemacht 
(jeweils 10 Sekunden) und habe das Ergebnis geplottet. Fehler treten 
weiterhin auf laut Python.

Rechts: 319048 17247
Links:  319048 17248

Warum sich die Plots (vor dem Filter) unterscheiden weiß ich noch nicht.

Ich teste jetzt mal mein Filter und setze alle Koeffizienten auf 1.

: Bearbeitet durch User
von Mario A. (itse_me_mario)



Lesenswert?

Hab jetzt mal mein Filter simuliert:

16 Koeffizienten
alle Koeffizienten sind 1
Eingangswert ist konstant 1

Man sieht sehr schön, wie sich die Werte ändern. Es scheint allerdings 
so zu sein, dass die Werte zu langsam berechnet werden. Wie man sieht 
dauert die Berechnung 2x s_new_sample an. Daraus ergibt sich dann 
natürlich eine Abtastfrequenz von nur 16 kHz statt 32 kHz.
Ich check mal was ich tun kann.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hab jetzt zus. eine FSM und 2 parallele Datenregister eingebaut.
Die Berechnung scheint zu funktionieren. Meine FSM muss ich allerdings 
noch anpassen, da die Berechnung zu oft ausgeführt wird (siehe 
FilterMitFSM_Zoom).

Ich bin gerade am überlegen wie ich es am besten mache.
Mein Signal s_new_sample ist 1 / (8,192 MHz) auf 1. Mit dem Signal würde 
ich eine FSM starten, welche zuerst ein Signal für das Laden meines 
Parallelregister mit dem neuen Filtereingangswert generiert, danach ein 
Signal für die Berechnung der Multiply Adder Blöcke generiert und 
abschließend ein Signal für das Laden des Parallelregisters generiert.

brd_clk ist in dem Fall 100 MHz. Am sinnvollsten wäre es wohl, wenn ich 
den Puls hphone_valid (=s_new_sample) verkürzen würde.

fsm_manage_data : fsm_fir_manage_data
    PORT MAP (
                  ck              => brd_clk,
                  new_data_f      => hphone_valid,
                  load_data_f     => s_load_data,
                  start_calc_f    => s_start_calc,
                  write_data_f    => s_write_data
        );


reg_par_read_data : reg_par
   generic map(reg_width => 24)
   port map (
               ck       =>     brd_clk,
               ld_par   =>     s_load_data,
               data_in  =>     datain,
               data_out =>     s_filter_data
            );

reg_par_write_data : reg_par
   generic map(reg_width => 24)
   port map (
               ck       =>     brd_clk,
               ld_par   =>     s_write_data,
               data_in  =>     s_filtered_data,
               data_out =>     firout
            );

: Bearbeitet durch User
von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

Mario A. schrieb:
> Also läuft bei der Decodierung
> mit Python noch etwas falsch?

Könnte sein, glaube ich aber nicht.

Ich habe das jetzt mal für 100 ms simuliert und mir die empfangenen 
Bytes vom UART in die Datei UART.bin schreiben lassen.
Mit dem Python kannst du das dekodieren, das stellt keine Fehler fest.

Wo die Fehler herkommen weiß ich also nicht, könnte an deinem VHDL 
liegen, an irgendwelchen seltsam konfigurierten Takten, und eben am UART 
selbst, also an der Hardware.

Du kannst ja mal deine AudioInterface.vhd und den CLK_I2S.xci von deinem 
MMCM/PLL hochladen. Gerne auch das CLK_I2S_clk_wiz.v

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hallo Gustl, hab dir mal die Dateien angehängt. Ich verwende den 
Clocking Wizard. Aktuell mach ich mir aus den 200 MHz noch 100 MHz aber 
an sich könnte man es auch bei 200 MHz belassen. Dein Projekt von 
gestern (mit den 2 UARTs) läuft auch bei mir, inklusive der zugeh. 
Python-Datei. Danke!

Ich bin gerade dabei etwas bei meinem FIR-Filter zu testen. Ich versuche 
mal ein Clock Domain Crossing einzubauen. Das Problem sind die 
unterschiedlichen Takte in meinen Filter-Dateien. Ich kann gerne noch 
das Projekt hochladen.

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Ich versuche mal ein Clock Domain Crossing einzubauen.

Das ist gar nicht sooo einfach. Aber es gibt dazu ein paar XPMs und du 
kannst für deine Daten einfach einen FIFO verwenden der mit zwei 
unabhängigen Takten arbeitet. Ein Takt zum Schreiben und ein anderer 
Takt zu Lesen.

Mario A. schrieb:
> Ich kann gerne noch das Projekt hochladen.

Das mit dem MMCME2_ADV den dir der Clock Wizard erzeugt/einverdrahtet 
hat sieht OK aus und das mit dem UART ebenfalls.

Du könntest jetzt tatsächlich noch versuchen nicht mit Python und 
pySerial zu lesen sondern direkt über die D2XX API aus einem C Programm 
heraus. Geht vermutlich auch mit Matlab oder Python diese API zu 
verwenden.

Und falls du ein Oszilloskop hast das UART dekodieren kann:
Das kann dir vermutlich auch anzeigen wenn da Fehler drinnen sind wegen 
zu langen oder zu kurzen Bits auf der Leitung oder falschen Start/Stopp 
Bedingungen.
Sonst spiele auch gerne etwas mit den pySerial Parametern herum
https://pythonhosted.org/pyserial/shortintro.html

Und hier die pySerial API:
https://pythonhosted.org/pyserial/pyserial_api.html

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

>>Das ist gar nicht sooo einfach.
Habs schon fast befürchtet. :)

>>du kannst für deine Daten einfach einen FIFO verwenden der mit zwei
unabhängigen Takten arbeitet. Ein Takt zum Schreiben und ein anderer
Takt zu Lesen.
Verwendest du hierfür den FIFO Generator von Xilinx?
Habe ich dann mein Timingproblem (wie beschrieben) nicht trotzdem noch? 
Ich habe dir mal mein "Basis"-Projekt ohne CDC angehängt, damit du´s 
besser nachvollziehen kannst. s_new_sample ist für 1/8,192 MhHz '1' und 
wird alle 1/32 kHz gesetzt.


>>direkt über die D2XX API aus einem C Programm heraus.
>>spiele auch gerne etwas mit den pySerial Parametern herum
Ja das klingt nach einem guten Plan! Ich notier mir das gleich für 
später. Zuerst möchte ich noch mein Filter zum laufen bringen. :) Das 
nervt mich schon zu lange... Mein erster Versuch mit CDC funktioniert 
leider nicht. Ich les mich morgen weiter ein.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Verwendest du hierfür den FIFO Generator von Xilinx?

Ja.

Mario A. schrieb:
> Habe ich dann mein Timingproblem (wie beschrieben) nicht trotzdem noch?

Kommt drauf an weilche Timing Probleme, da gibt es ja mehrere viele.
Normalerweise hast du zwei unabhängige Taktdomänen und willst Daten von 
einer in die andere bringen. Da schreibst du also mit Takt A in der 
einen Domäne in das FIFO und in der anderen Domäne liest du mit Takt B 
aus dem FIFO.
Da kann es auch Probleme geben, wenn du mehr Daten schreibst als du auf 
der anderen Seite liest, dann läuft der FIFO voll und du verlierst 
Daten.
Und dann solltest du die Takte noch constrainen, also dem Vivado sagen, 
dass die Takte voneinander unabhängig sind wenn sie es auch sind.

Wenn aber deine Takte gar nicht echt verschieden sind, sondern aus der 
gleichen Quelle stammen und nur ganzzahlige Vielfache sind, dann kannst 
du die Taktübergänge auch einfacher gestalten.

Tja aus dem Code wird mir nicht klar zwischen welchen Takten du welche 
Daten austauschen möchtest. Kannst du das mal erklären?

Mario A. schrieb:
> Ja das klingt nach einem guten Plan! Ich notier mir das gleich für
> später.

Ist nicht sehr komplex, das sind nur ein paar wenige Funktionen die du 
brauchst um vom UART zu lesen und der 
https://www.ftdichip.com/Support/Documents/ProgramGuides/D2XX_Programmer%27s_Guide(FT_000071).pdf 
enthält sehr viele funktionierende Beispiele.

Edit:
Ich kann dein Projekt nicht bauen weil zwei .coe Dateien fehlen.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

So, die .coe Dateien habe ich hier im Thread gefunden.
Ich kann dein Projekt bauen, aber nicht für den dicken Kintex, denn 
dafür habe ich keine Vivado Lizenz. Aber ich habe das jetzt mal für 
einen Artix gebaut. Und es gibt Timingprobleme zwischen den beiden 
erzeugten Takten:

clk_out1 => s_clk_100_MHz,
clk_out2 => s_clk_8_192_MHz_MCLK,

Tja was hilft? Signale die die Grenze überschreiten einsynchronisieren 
und false-path constraint setzen oder eben dual clock FIFO verwenden.
Welche Signale das sind siehst du im Implemented Timing Report.

---------------------------------------------------------

Aber ich habe noch eine Frage an die Profis:

Zwei Taktdomänen mit den Takten A und B. Jetzt will ich in Domäne A 
erkennen ob ein Bit in Domäne B gesetzt ist. Also takte ich das Bit in 
Domäne A ein und prüfe dann das eigetaktete Signal.

Aber wie ist das wenn ich Daten mit Valid Signal abgreifen will?
Also wieder Domäne A und B. Das Valid Signal mit Daten werden in B 
erzeugt. Ich takte also das Valid Signal in A ein und prüfe das 
eingetaktete Valid.
Was mache ich mit den Daten? Wenn die Daten mit dem Valid synchron 
erscheinen und danach weiterhin anliegen, dann müsste es doch genügen 
nur das Valid einzutakten. Ist das eingetaktete Valid in A dann gesetzt 
kann ich die Daten aus B in A direkt übernehmen und zwar weil die Daten 
aus B dann schon für mehrere Takte von A stabil anliegen. Oder ist das 
falsch und ich muss auch die Daten aus B eintakten?

So wie ich ds bisher verstanden habe passiert Metastabilität nur, wenn 
die Daten zur Taktflanke nicht definiert high oder low sind. Wenn ich 
also in der HDL sicherstellen kann, dass die Daten zur Flanke schon eine 
Zeit lang anliegen (weil das Valid einen Takt früher abgefragt wird und 
die Daten mehrere Takte gesetzt werden) dann sollte man die Daten auch 
ohne Eintakten sampeln können.

Danke!

: Bearbeitet durch User
von Mario A. (itse_me_mario)



Lesenswert?

>>Kommt drauf an welche Timing Probleme
Timingproblem ist vllt das falsche Wort. Ich möchte die Berechnung mit 
s_new_sample starten (s_new_sample = hphone_valid). s_new_sample ist 
1/8,192MHz lang auf '1' und das periodisch alle 1/32kHz. Anschließend 
soll das Ergebnis zum nächsten 32 kHz Takt ausgegeben werden. Das sollte 
dann eigtl. das Filterverhalten für eine Abtastrate von 32 kHz 
widerspiegeln.

Ich könnte ja an sich den einen FIFO mit einem 32 kHz Takt laden und den 
zweiten (Ausgeben der Werte) mit einem verschobenen/negierten 32 kHz 
Takt. Ich denke das geht aber auch einfacher.


>>Und dann solltest du die Takte noch constrainen
Das war eigtl. im XDC. Vllt hast du´s übersehen, oder meinst du was 
anderes?
set_clock_groups -asynchronous -group [get_clocks *clk_out1*] -group 
[get_clocks *clk_out2*]

Grad noch gesehen: Beim Clocking Wizard sollten es nur 2 Ausgänge sein 
(100 MHz und 8,192 MHz)

Wenn ich die FSM, die Parallelregister und die Multiply Adderblöcke mit 
8,192 MHz versorge, benötigt die Berechnung die doppelte Zeit.
Mit 16,384 MHz ist es dasselbe.

Ich habe jetzt die FSM und die Parallelregister mit 8,192 MHz versorgt 
und die Multiply Adder Blöcke mit 16,384 MHz. Laut Testbench scheint es 
nun zu funktionieren (siehe FIR_Testbench).

Den 8,192 MHz Takt erzeuge ich mir nun direkt aus dem 16,384 MHz Takt 
vom Clocking Wizard. Die beiden Clocks muss ich dann nicht extra als 
asynchron setzen, oder? Mein Clocking Wizard erzeugt mir nun einen 100 
MHz Takt und einen 16,394 MHz Takt.

Ich werd es nachher mal auf dem Board testen. :)

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Timingproblem ist vllt das falsche Wort. Ich möchte die Berechnung mit
> s_new_sample starten (s_new_sample = hphone_valid).

Das macht keine Timingprobleme, du verwendest das nur als Enable Signal. 
Kannst du machen. Aber verwende s_new_sample nicht als Takt.
Den FIFO oder ein CDC brauchst du da nicht.

Mario A. schrieb:
> Den 8,192 MHz Takt erzeuge ich mir nun direkt aus dem 16,384 MHz Takt
> vom Clocking Wizard. Die beiden Clocks muss ich dann nicht extra als
> asynchron setzen, oder? Mein Clocking Wizard erzeugt mir nun einen 100
> MHz Takt und einen 16,394 MHz Takt.

Nun ähm ... doch, die sind asynchron wenn die Flanken nicht schön 
übereinander liegen. Guck dir an was deine PLL/MMCM genau macht.
clk_wiz_0_clk_wiz.v sagt:

  MMCME2_ADV
  #(.BANDWIDTH            ("OPTIMIZED"),
    .CLKOUT4_CASCADE      ("FALSE"),
    .COMPENSATION         ("ZHOLD"),
    .STARTUP_WAIT         ("FALSE"),
    .DIVCLK_DIVIDE        (1),
    .CLKFBOUT_MULT_F      (4.750),
    .CLKFBOUT_PHASE       (0.000),
    .CLKFBOUT_USE_FINE_PS ("FALSE"),
    .CLKOUT0_DIVIDE_F     (9.500),
    .CLKOUT0_PHASE        (0.000),
    .CLKOUT0_DUTY_CYCLE   (0.500),
    .CLKOUT0_USE_FINE_PS  ("FALSE"),
    .CLKOUT1_DIVIDE       (116),
    .CLKOUT1_PHASE        (0.000),
    .CLKOUT1_DUTY_CYCLE   (0.500),
    .CLKOUT1_USE_FINE_PS  ("FALSE"),
    .CLKOUT2_DIVIDE       (19),
    .CLKOUT2_PHASE        (0.000),
    .CLKOUT2_DUTY_CYCLE   (0.500),
    .CLKOUT2_USE_FINE_PS  ("FALSE"),
    .CLKIN1_PERIOD        (5.000))

Die externen 200 MHz werden mit 4.75 multipliziert ( = 950 MHz) und dann 
einmal durch 9.5 geteilt, das ergibt die 100 MHz.
Und einmal durch 116 geteilt. Das ergibt die 8.192 MHz ( = 8.189655172 
MHz).
Wenn du dir 16.394 MHz erzeugen lässt, dann wird vermutlich durch 58 
geteilt, muss aber nicht sein, es könnte auch vorher der Multiplikator 
verändert werden.
Jedenfalls sind deine 8.192 MHz (oder 16.394 MHz) asynchron zu den 100 
MHz.

Ich würde empfehle sehr nur einen Takt im System zu haben, also die 100 
MHz oder vielleicht sogar die 200 MHz die ja extern reinkommen. Ja, du 
brauchst die 8.192 MHz für das I2S, da kommst du nicht drum rum so wie 
dein I2S geschrieben ist, und da musst du dann auch ein Clock Domain 
Crossing machen. Aber nur zwischen deinem I2S Transmitter und dem Rest 
im FPGA. Die Filterei und so würde ich alles mit einem Systemtakt laufen 
lassen und gegebenenfalls Enable Signale verwenden die du ja vom I2S 
Transmitter sowieso bekommst. Das s_new_sample könntest du wunderbar als 
so ein Signal verwenden - aber eben nicht als Takt.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

>>die sind asynchron wenn die Flanken nicht schön übereinander liegen.
Ich meinte den 8,192 und 16,384 MHz Takt. Den 8,192 MHz Takt baue ich 
mir aus dem 16,384 MHz Takt vom Clocking Wizard. Die Ausgänge vom 
Clocking Wizard (100 MHz und 16,384 MHz) setze ich weiterhin asynchron 
zueinander.


>>da musst du dann auch ein Clock Domain Crossing machen.
Es scheint jetzt auch ohne zu funktionieren. Die Frequenzgänge sehen gut 
aus. Die Koeffizienten sind Konstanten im Code.
Das mit dem CDC klingt aber sehr interessant. Wäre sicher die schönere 
Lösung. Danke für den Tipp! Könnte ich auch anschließend noch 
implementieren.

Ich schreibe jetzt mal mein Projekt noch ein klein wenig um und füge den 
Block Memory Generator wieder ein. Dann würde ich mich dem Einlesen der 
Daten vom UART mit C widmen wie du es beschrieben hast. :) Hierfür 
nochmal danke!
>>direkt über die D2XX API aus einem C Programm heraus.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Es scheint jetzt auch ohne zu funktionieren.

In der Simulation schon, da gibt es keine Metastabilitäten. Und mit dem 
Auge siehst du an der Signalform nicht ob mal ein Abtastwert fehlt.

Mario A. schrieb:
> Ich schreibe jetzt mal mein Projekt noch ein klein wenig um und füge den
> Block Memory Generator wieder ein.

Viel Erfolg!

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

>>In der Simulation schon, da gibt es keine Metastabilitäten.
Kann ich das auch mit einer Timinganalyse in Vivado sehen/ermitteln?

>>Ich würde empfehle sehr nur einen Takt im System zu haben
Ok, ich könnte ja mal alles mit den 200 MHz machen (bis auf das I2s).

>>Ja, du brauchst die 8.192 MHz für das I2S [...] und da musst du dann auch ein 
Clock Domain Crossing machen. Aber nur zwischen deinem I2S Transmitter und dem 
Rest im FPGA.
Ja das könnte ich versuchen! Ich sehe mir das CDC nochmal genauer an.

>>[...] und gegebenenfalls Enable Signale verwenden die du ja vom I2S
Transmitter sowieso bekommst.

>>Das s_new_sample könntest du wunderbar als so ein Signal verwenden - aber eben 
nicht als Takt.
Ich habe s_new_sample nicht als Takt verwendet. Vllt meinst du was 
anderes? Also z. B. aus s_new_sample mit meiner FSM (mit 200 MHz Takt) 
ein Signal mit kürzerer Pulsdauer machen und dieses dann für die 
Multiply Adder Blöcke verwenden?

Vielen Dank dir!

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Kann ich das auch mit einer Timinganalyse in Vivado sehen/ermitteln?

Weiß ich nicht.

Mario A. schrieb:
> Ok, ich könnte ja mal alles mit den 200 MHz machen (bis auf das I2s).

Das könntest du tun. Wobei dann nur 5 ns zwischen den Takten sind. Ich 
würde eine Frequenz verwenden die so hoch wie nötig ist, aber auch so 
niedrig wie möglich. Prinzipiell kannst du für Alles die 8.192 MHz 
verwenden, aber das würde beim UART probleme machen. Aber 200 MHz sind 
vielleicht schon unnötig hoch. 100 MHz finde ich da völlig OK, dann ist 
da schön Zeit für die Kombinatorik.
Ist aber natürlich eigentlich egal solange das Timing eingehalten wird. 
Höherer Takt führt aber zu höherer Leistungsaufnahme.

Mario A. schrieb:
> Ich sehe mir das CDC nochmal genauer an.

Ja, wobei das da recht einfach ist. Es reicht die Takte asynchron zu 
setzen und das s_new_sample mit dem Systemtakt einzutakten. In die 
andere Richtung taktet der I2S Transmitter schon sein Valid Flag ein.

Mario A. schrieb:
> Ich habe s_new_sample nicht als Takt verwendet.

Genau, das passt schon. Ich wollte nur sagen, dass du das auch nicht als 
Takt verwenden solltest.

Mario A. schrieb:
> Also z. B. aus s_new_sample mit meiner FSM (mit 200 MHz Takt)
> ein Signal mit kürzerer Pulsdauer machen und dieses dann für die
> Multiply Adder Blöcke verwenden?

Wenn du das willst, dann kannst du ja die steigende Flanke von 
s_new_sample verwenden - aber nicht als Takt!
Quasi so:

signal s_new_sample_sr: std_logic_vector(1 downto 0):="00";

Dann getaktet mit den 200 MHz:

s_new_sample_sr <= s_new_sample_sr(0) & s_new_sample;

Und dann kannst du die Flanke erkennen:

if s_new_sample_sr = "01" then
...
Da kommt alles rein was passieren soll wenn eine steigende Flanke von 
s_new_sample_sr da war.
...
end if;

Man verwendet dann s_new_sample_sr quasi als Enable.

Edit:
Hast du in deinem Timingreport auch Fehler oder wird das Timing erfüllt?

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hallo Gustl, danke für deine Antwort!

Ich hab gerade mal mein Projekt (ohne CDC!) umgeschrieben auf 21 Bit 
Koeffizienten und dann ist mir auch ein Fehler aufgefallen (siehe 
Fehler.png). Beim Projekt mit den 16 Bit Koeffizienten tritt dieser 
Fehler in der Simulation nicht auf. Das spricht also auch gegen diese 
Variante.


Eine Frage Gustl: Hast du in deinem Post jetzt 2 Methoden beschrieben? 
1x mit FSM und 1x ohne? Bin mir gerade nicht ganz sicher.

>>Ja, wobei das da recht einfach ist. Es reicht die Takte asynchron zu
setzen und das s_new_sample mit dem Systemtakt einzutakten.
Also FSM raus, s_new_sample eintakten und als Start für meine Multiply 
Adder Blöcke verwenden?


Also so z.B. (Version mit meiner FSM)?

In AudioInterface:
NEW_SAMPLE_PROC : process (s_clk_100_MHz)
    begin
        if (s_clk_100_MHz'event and s_clk_100_MHz = '1') then

            s_new_sample_sr <= s_new_sample_sr(0) & s_new_sample;
            s_begin_filter <= '0';

            if s_new_sample_sr = "01" then
                s_begin_filter <= '1';
            end if;
        end if;
end process;

s_begin_filter dient dann als Eingang für meine beiden 
links-/rechtsseitigen Filter. Darin befindet sich noch die FSM und die 
Parallelregister, die alle mit 100 MHz getaktet sind.
Hier aber zusätzlich noch CDC einbauen, oder nicht?

Ich seh mir gerade ein Video zum CDC an. :)


>>Hast du in deinem Timingreport auch Fehler oder wird das Timing erfüllt?
Ich habe auf Report Timing Summary geklickt. Design Timing Summary zeigt 
nichts verdächtiges an. Nur Check Timing (wie im vorherigen Post zu 
sehen).

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Hast du in deinem Post jetzt 2 Methoden beschrieben?
> 1x mit FSM und 1x ohne? Bin mir gerade nicht ganz sicher.

Ne, ich habe das unabhängig von der FSM beschrieben. Ich weiß nicht 
genau was deine FSM machen soll.

Wenn es so ist:
Du verwendest einen 200 MHz Takt, bekommst aber nur selten/manchmal 
einen neuen Abtastwert zusammen mit s_new_sample = '1', dann ist es 
sinnvoll das Filter und alle Berechnungen nicht in jedem Takt rechnen zu 
lassen, sondern nur wenn ein neuer Abtastwert reinkommt.
Wenn deine Filterung sogar mehrere Takte des schnellen Taktes braucht, 
dann kannst du s_new_sample verwenden um einen "Durchlauf" durch ein 
seriell berechnetes Filter anzustoßen.

Und da würde ich das s_new_sample eben verwenden wie beschrieben. Also 
eintakten und dann auswerten ob da eine steigende Flanke vorliegt mit 
"01".
Das mit deinem Zusatzsignal s_begin_filter hast du richtig gemacht, das 
ist bei steigender Flanke von s_new_sample (also kurz danach) für einen 
Takt des schneellen Taktes '1'.

von Mario A. (itse_me_mario)


Lesenswert?

>>Ich weiß nicht genau was deine FSM machen soll.
Also die FSM hat nur, wenn new_sample = 1 ist, die Filtereingangsdaten 
in ein paralleles Datenregister geladen. 1 Takt später hat es dann die 
Berechnung mit den Multiply Addern gestartet und noch 1 Takt später hat 
es wieder ein paralleles Datenregister (mit den dann gefilterten Daten) 
geladen.

>>sondern nur wenn ein neuer Abtastwert reinkommt.
Ja das möchte ich auch. Alle 32 kHz habe ich einen neuen Wert und dann 
soll die Berechnung natürlich nur 1x durchgeführt werden.

>>Wenn deine Filterung sogar mehrere Takte des schnellen Taktes braucht
Eigtl. sollte die Berechnung nur 1 Takt brauchen.

>>Also eintakten und dann auswerten ob da eine steigende Flanke vorliegt mit
"01".
Okay dann muss ich das jetzt nur noch eintakten, danke!

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

So hätte ich das CDC jetzt eingebunden/angewendet 
(https://www.youtube.com/watch?v=eyNU6mn_-7g):

NEW_SAMPLE_PROC : process (s_clk_100_MHz)
    begin
        if rising_edge(s_clk_100_MHz) then
            s_new_sample_sr <= s_new_sample_sr(0) & r2_Data;
            s_begin_filter <= '0';

            if s_new_sample_sr = "01" then
                s_begin_filter <= '1';
            end if;
        end if;
end process;


-- Crossing Data from Slow to Fast Domain (8.192 MHz to 100 MHz)
CDC_FAST_CLOCK_PROC : process (s_clk_100_MHz)
    begin
        if rising_edge(s_clk_100_MHz) then
            r1_Data <= s_new_sample; --r1_Data is metastable
            r2_Data <= r1_Data; -- r2_Data is stable!
         end if;
end process;

Die Simulation sieht gut aus. Allerdings haben auch ohne CDC beide 
Varianten mit 16 und 21 Bit Koeffizienten in der Simulation gut 
ausgesehen. Alleine die Flankenerkennung hat geholfen. Aber CDC sollte 
ja trotzdem rein. Ich hoffe das passt jetzt. Danke Gustl!

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Okay dann muss ich das jetzt nur noch eintakten, danke!

Mario A. schrieb:
> s_new_sample_sr <= s_new_sample_sr(0) & s_new_sample;

Das ist schon das Eintakten.

Mario A. schrieb:
> r1_Data <= s_new_sample; --r1_Data is metastable
>             r2_Data <= r1_Data; -- r2_Data is stable!

Das macht eigentlich nichts anders als das Schieben von s_new_sample_sr.

Mario A. schrieb:
> Die Simulation sieht gut aus. Allerdings haben auch ohne CDC beide
> Varianten mit 16 und 21 Bit Koeffizienten in der Simulation gut
> ausgesehen.

Genau, in der Simulation ändert sich da nix.

Mario A. schrieb:
> Ich hoffe das passt jetzt. Danke Gustl!

Bitte, viel Erfolg!

von Mario A. (itse_me_mario)


Lesenswert?

>>Das macht eigentlich nichts anders als das Schieben von s_new_sample_sr.
Oh stimmt das entspricht ja schon dem CDC... Danke! Dann kann ich meins 
wieder rausnehmen, bzw. mein CDC ohne deins testen.

von Mario A. (itse_me_mario)


Lesenswert?

Der Frequenzgang mit dem NEW_SAMPLE_PROC (s. oben) scheint leider doch 
nicht zu stimmen. Ich schau´s mir morgen nochmal an...

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

@Gustl
Das Filter mit deiner Methode sieht in der Simulation sehr gut aus. Wenn 
ich aber die für eine Abtastraste von 32 kHz entworfenen Koeffizienten 
nehme, ist der Frequenzgang wieder verschoben. Erst wenn ich die 
Filterkoeffizienten für eine Abtastrate von 16 kHz entwerfe stimmt der 
Frequenzgang mit den Koeffizienten überein.

Es sieht in der Simulation so aus, dass der gefilterte Wert erst 
verzögert rauskommt. Ich sehe mir das nachher noch an, indem ich wieder 
alle Koeffizienten auf 1 und den Eingang auch auf 1 setze.

Entweder ich versuche die Berechnung der Multiply Adder Blöcke mit der 
doppelten Frequenz durchzuführen, oder ich baue vllt doch einen FIFO 
ein? Wenn ich die Multiply Adder mit einem anderen Clock versorgen 
möchte (z.B. 100 MHz) und alles andere mit 50 MHz läuft, sollte ich in 
FIRFilter_...vhd auch eine Flankenerkennung einbauen, damit der Multiply 
Adder nur einmal gestartet wird, oder?

Die Flankenerkennung in AudioInterface.vhd würde so bleiben (nur der 
Takt ändert sich zu 50 MHz).

Den 50 MHz Takt hätte ich mir jetzt intern aus dem 100 MHz Takt erzeugt:

process(s_clk_100_MHz)
   begin
      if rising_edge(s_clk_100_MHz) then
         s_clk_50_MHz <= not s_clk_50_MHz;
      end if;
   end process;

@Gustl Wie würdest du da vorgehen? Und kannst du vllt (bitte) mal einen 
Blick drüber werfen? :) Vielen Dank im Voraus!

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Es sieht in der Simulation so aus, dass der gefilterte Wert erst
> verzögert rauskommt.

Das ist ganz normal, das ist ja ein FIR.

Zum Anhang:
Gen2_FIR.zip (39,2 MB, 1 Downloads)

Es reicht im Projektordner der .srcs Unterordner und die .xpr Datei. Der 
Rest wird erzeugt. Die beiden .coe Dateien sollten eigentlich ebenfalls 
im .srcs Ordner irgendwo beheimatet sein.

Dann zur Simulation:
Da hast du "Disabled Sources" das brauchst du eigentlich nicht. Du 
kannst ja auswählen mit Rechtsklick > "Set as Top" welche der 
Testbenchdateien das Toplevel der Simulation seien soll.

----------------------------------------------------

Mario A. schrieb:
> @Gustl Wie würdest du da vorgehen? Und kannst du vllt (bitte) mal einen
> Blick drüber werfen? :) Vielen Dank im Voraus!

Das mache ich gerade. Für mich sieht das teilweise unnötig kompliziert 
aus. Klar, man kann für jede Kleinigkeit (ich meine reg_par) eine 
Komponente schreiben, das am Ende führt das zu eher mehr 
Unübersichtlichkeit wie ich finde. Auch dein fsm_fir_manage_data ist 
zwar nett, aber wie ich finde deutlich zu kompliziert. Das sind bei dir 
ohne Leerzeilen vielleicht 50 Zeilen Code, mit FSM, concurrent 
Statements, beide Sauce und scharf.

Aber was macht das eigentlich?
new_sample kommt rein, ist also für einen Takt '1' und danach werden der 
Reihe nach s_load_data, s_start_calc, s_write_data je für einen Takt auf 
'1' gesetzt.

Da kann man auch ein 3-Bit-Register nehmen das man mit new_sample = '1' 
auf "001" setzt und in jedem Takt um 1 nach links schiebt. Man kann auch 
in jedem Takt schieben und eben auch gleich new_sample hineinschieben:
1
signal state_reg: std_logic_vector(2 downto 0):="000";
2
...
3
process begin
4
wait until rising_edge(clk);
5
   state_reg <= state_reg(1 downto 0) & new_sample;
6
   s_load_data <= state_reg(0);
7
   s_start_calc <= state_reg(1);
8
   s_write_data <= state_reg(2);
9
end process;

Und dafür braucht es dann auch wirklich keine eigene Komponente.

Ich schreibe dir das jetzt mal etwas um wie ich das bauen würde. Ja, ich 
lasse dir die xbip_multadd drinnen auch wenn man das schön mit * und + 
schreiben könnte (-:

Edit:
Weil das ja für links und rechts zwei identische Filter-Hardwaren^^ 
werden sollen würde ich die auch nicht unterschiedlich benennen sondern 
das dann bei der Instanziierung machen. Die unterscheiden sich ja am 
Ende nur durch das .coe File das ins BRAM geladen wird.

Die reg_par hast du als CDC eingebaut?
Ja, kann man machen, geht auch einfacher. Und ich bin mir gar nicht 
sicher ob du die brauchst, denn es sollte reichen wenn du sicherstellst, 
dass sich die Daten am Eingang während der Filterei nicht ändern.

Und zu den s_load_data, s_start_calc, s_write_data von oben:
Das kann man auch über einen Zähler machen den von 0 bis 2 zählt. Da 
spart man noch ein Bit.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

>>Das ist ganz normal, das ist ja ein FIR.
1 Takt Verzögerung schon. :) Da war glaub ich ein Fehler im anderen 
(Vergleichs)Projekt.

>>Das mache ich gerade.
Danke!

>>Für mich sieht das teilweise unnötig kompliziert aus.
Da geb ich dir vollkommen Recht, vieles könnte man einfacher lösen.

>>Ja, ich lasse dir die xbip_multadd drinnen auch wenn man das schön mit * und + 
schreiben könnte (-:
Das ist halt schön für die Generate-Anweisung im Zusammenhang mit der 
transponierten Direktform. ;) Spricht halt direkt die DSP-Slices an.

>>Weil das ja für links und rechts zwei identische Filter-Hardwaren
Ja ich wollt es erst mal allgemein machen (falls ich mal verschiedene 
Filter gleichzeitig testen will). Danke dir schon mal! Ich setz mich 
morgen früh gleich wieder ran.

: Bearbeitet durch User
Beitrag #6479367 wurde vom Autor gelöscht.
von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

Mario A. schrieb:
> Das ist halt schön für die Generate-Anweisung im Zusammenhang mit der
> transponierten Direktform. ;) Spricht halt direkt die DSP-Slices an.

Ja, stimmt, aber heutzutage gilt eigentlich generell:

Ein Compiler/Synthesizer kann besser optimieren als der Mensch.

Ein Profi ist da vermutlich noch besser, mag ein, aber mein Fazit daraus
ist:

Schreibe deinen Code so, dass den der Compiler/Synthesizer verstehen
kann und achte dann vor allem auf Lesbarkeit und Wartbarkeit. Was nützt
der kryptischste C Code oder das verschachteltste VHDL Konstrukt wenn
man das auch viel lesbarer hätte schreiben können und am Ende doch exakt
der gleiche ASM/Netlist bei rauskommt?
Da hat man eigentlich eher verloren weil der kryptische Code deutlich
mehr Denkleistung erfordert um ihn zu verstehen.

Mario A. schrieb:
> Ja ich wollt es erst mal allgemein machen (falls ich mal verschiedene
> Filter gleichzeitig testen will).

Dafür hast du ja die beiden .coe Dateien. Du brauchst also nur zwei
unterschiedliche BRAM Blöcke.

-------------------------------------------------------------------

So, ich habe das jetzt mal umgeschrieben ohne die Funktion zu ändern und
danach simuliert mit dem externen ADC und DAC in der Testbench.
Und das sieht gut aus!

Dein Filter hat seine Grenzfrequenz grob bei 1,5*f_sample und das sieht
man auch in der Simulation, ich habe 0,5*f_sample nach ca. 32 ms,
1,5*f_sample ist also bei ca. 9.6 ms und in der Tat, das würde passen.

Im Anhang jetzt mal die umgeschriebenen Dateien und Bildchen.

Oh und die IEEE.STD_LOGIC_UNSIGNED.ALL habe ich herausoperiert.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;

Genügt völlig.

: Bearbeitet durch User
von Mario A. (itse_me_mario)



Lesenswert?

Erst mal vielen vielen Dank Gustl!
Ich habe deine Dateien eingebunden und den Bitstream generiert. Der 
Frequenzgang ist leider wieder verschoben (siehe Anhang). In ähnlicher 
Weise wie du es jetzt auch gelöst hast, hab ich das gestern Abend auch 
mal probiert (nur mit zus. FSM und Parallelregister). Ergebnis war 
folglich dasselbe.

Ich weiß nicht ob es an den Koeffizienten liegt... Mit dem FIR Compiler 
und denselben Koeffizienten stimmt der Frequenzgang, sollte also passen. 
Was auch komisch ist, ist diese Erhöhung im Frequenzgang bei höheren 
Frquenzen.
Der FIR Compiler verwendet zus. einen FIFO. Denkst du das würde in dem 
Fall helfen? FIR Compiler mit und ohne FIFO liefert dasselbe Ergebnis.

Macht der FIR Compiler irgendwas anders? Ansonsten akzeptier ich jetzt 
einfach, dass mein Filter die Abtastrate nochmal halbiert... Ist da 
irgendwo eine zus. Abtastung drin? Ich könnte es auch mal so versuchen, 
wie du vorgeschlagen hast (mit * und + in einem Process). Sollte aber 
eigtl. daran nichts ändern.

Zur Logik:
Ich multipliziere eine Q0.23 Festkommazahl mit einer Q0.15 
Festkommazahl. An sich müsste da eine Zahl im 2.38 Format rauskommen (2 
Vorzeichenbits und 38 Bits für den Nachkommaanteil). Wenn ich dann 38 
downto 15 entnehme sollte es eigentlich stimmen. Hätte auch mal 39 
downto 16 und 37 downto 14 versucht, aber das bringt nichts.

>>Ein Compiler/Synthesizer kann besser optimieren als der Mensch.
Das stimmt schon, ja.

>>Dafür hast du ja die beiden .coe Dateien. Du brauchst also nur zwei
unterschiedliche BRAM Blöcke.
Ich meinte mit unterschiedlicher Anzahl an Bits für die Koeffizienten 
und unterschiedlicher Filterordnung. Ansonsten ja. :)

Vielen Dank dir!

von Mario A. (itse_me_mario)



Lesenswert?

Hallo Gustl,

ich habe jetzt mal schnell ein altes "serielles Filter" implementiert 
und getestet. Damit stimmt der Frequenzgang.

Dann macht mir wohl mein Multiply Adder Block irgendwas kaputt. 
Wahrscheinlich hat der eine Verzögerung. Vllt einen FIFO einbauen?

Hättest du die Berechnung mit + und * in einem Process gemacht, oder 
hättest du auch Blöcke mit Generate erzeugt?

Ich könnte auch mal versuchen die DSP Slices direkt anzusprechen über 
die Makros im IP Catalog.

Meine Einstellungen für den Multiply Adder waren A:B - P Latency -1 und 
C - P Latency -1.
Ich hab das mal in 0 und 0 geändert. Hat auch nichts gebracht. Vielen 
Dank im Voraus!

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Der Frequenzgang ist leider wieder verschoben (siehe Anhang).

Da sind ziemlich viele Bildchen im Angang und ich verstehe nich welches 
wozu gehört und was zeigen soll.

Mario A. schrieb:
> Mit dem FIR Compiler
> und denselben Koeffizienten stimmt der Frequenzgang, sollte also passen.

Genau, die passen.

Mario A. schrieb:
> Was auch komisch ist, ist diese Erhöhung im Frequenzgang bei höheren
> Frquenzen.

Stimmt, aber da weiß ich nicht wo das her kommt.

Mario A. schrieb:
> Der FIR Compiler verwendet zus. einen FIFO. Denkst du das würde in dem
> Fall helfen? FIR Compiler mit und ohne FIFO liefert dasselbe Ergebnis.

Nein, den FIFO brauchst du nicht.

Mario A. schrieb:
> Macht der FIR Compiler irgendwas anders?

Keine Ahnung, vermutlich baut der das aber anders als du.

Mario A. schrieb:
> Ansonsten akzeptier ich jetzt
> einfach, dass mein Filter die Abtastrate nochmal halbiert...

Hm, also ich kann das in der Simulation nicht sehen.

Mario A. schrieb:
> Ist da irgendwo eine zus. Abtastung drin?

Sehe ich auch nicht.

Mario A. schrieb:
> Ich könnte es auch mal so versuchen,
> wie du vorgeschlagen hast (mit * und + in einem Process). Sollte aber
> eigtl. daran nichts ändern.

Genau. Es sieht dann aber übersichtlicher aus und erleichtert vielleicht 
die Fehlersuche.

Mario A. schrieb:
> Ich meinte mit unterschiedlicher Anzahl an Bits für die Koeffizienten
> und unterschiedlicher Filterordnung. Ansonsten ja. :)

Dafür hast du doch die Generics. Ja, die xbip_multadd hast du dir mit 
festen Werten generieren lassen, aber auch die kannst du "nativ" 
einbinden:
1
   DSP48E1 #(
2
      // Feature Control Attributes: Data Path Selection
3
      .A_INPUT("DIRECT"),               // Selects A input source, "DIRECT" (A port) or "CASCADE" (ACIN port)
4
      .B_INPUT("DIRECT"),               // Selects B input source, "DIRECT" (B port) or "CASCADE" (BCIN port)
5
      .USE_DPORT("FALSE"),              // Select D port usage (TRUE or FALSE)
6
      .USE_MULT("MULTIPLY"),            // Select multiplier usage ("MULTIPLY", "DYNAMIC", or "NONE")
7
      .USE_SIMD("ONE48"),               // SIMD selection ("ONE48", "TWO24", "FOUR12")
8
      // Pattern Detector Attributes: Pattern Detection Configuration
9
      .AUTORESET_PATDET("NO_RESET"),    // "NO_RESET", "RESET_MATCH", "RESET_NOT_MATCH" 
10
      .MASK(48'h3fffffffffff),          // 48-bit mask value for pattern detect (1=ignore)
11
      .PATTERN(48'h000000000000),       // 48-bit pattern match for pattern detect
12
      .SEL_MASK("MASK"),                // "C", "MASK", "ROUNDING_MODE1", "ROUNDING_MODE2" 
13
      .SEL_PATTERN("PATTERN"),          // Select pattern value ("PATTERN" or "C")
14
      .USE_PATTERN_DETECT("NO_PATDET"), // Enable pattern detect ("PATDET" or "NO_PATDET")
15
      // Register Control Attributes: Pipeline Register Configuration
16
      .ACASCREG(1),                     // Number of pipeline stages between A/ACIN and ACOUT (0, 1 or 2)
17
      .ADREG(1),                        // Number of pipeline stages for pre-adder (0 or 1)
18
      .ALUMODEREG(1),                   // Number of pipeline stages for ALUMODE (0 or 1)
19
      .AREG(1),                         // Number of pipeline stages for A (0, 1 or 2)
20
      .BCASCREG(1),                     // Number of pipeline stages between B/BCIN and BCOUT (0, 1 or 2)
21
      .BREG(1),                         // Number of pipeline stages for B (0, 1 or 2)
22
      .CARRYINREG(1),                   // Number of pipeline stages for CARRYIN (0 or 1)
23
      .CARRYINSELREG(1),                // Number of pipeline stages for CARRYINSEL (0 or 1)
24
      .CREG(1),                         // Number of pipeline stages for C (0 or 1)
25
      .DREG(1),                         // Number of pipeline stages for D (0 or 1)
26
      .INMODEREG(1),                    // Number of pipeline stages for INMODE (0 or 1)
27
      .MREG(1),                         // Number of multiplier pipeline stages (0 or 1)
28
      .OPMODEREG(1),                    // Number of pipeline stages for OPMODE (0 or 1)
29
      .PREG(1)                          // Number of pipeline stages for P (0 or 1)
30
   )
31
   DSP48E1_inst (
32
      // Cascade: 30-bit (each) output: Cascade Ports
33
      .ACOUT(ACOUT),                   // 30-bit output: A port cascade output
34
      .BCOUT(BCOUT),                   // 18-bit output: B port cascade output
35
      .CARRYCASCOUT(CARRYCASCOUT),     // 1-bit output: Cascade carry output
36
      .MULTSIGNOUT(MULTSIGNOUT),       // 1-bit output: Multiplier sign cascade output
37
      .PCOUT(PCOUT),                   // 48-bit output: Cascade output
38
      // Control: 1-bit (each) output: Control Inputs/Status Bits
39
      .OVERFLOW(OVERFLOW),             // 1-bit output: Overflow in add/acc output
40
      .PATTERNBDETECT(PATTERNBDETECT), // 1-bit output: Pattern bar detect output
41
      .PATTERNDETECT(PATTERNDETECT),   // 1-bit output: Pattern detect output
42
      .UNDERFLOW(UNDERFLOW),           // 1-bit output: Underflow in add/acc output
43
      // Data: 4-bit (each) output: Data Ports
44
      .CARRYOUT(CARRYOUT),             // 4-bit output: Carry output
45
      .P(P),                           // 48-bit output: Primary data output
46
      // Cascade: 30-bit (each) input: Cascade Ports
47
      .ACIN(ACIN),                     // 30-bit input: A cascade data input
48
      .BCIN(BCIN),                     // 18-bit input: B cascade input
49
      .CARRYCASCIN(CARRYCASCIN),       // 1-bit input: Cascade carry input
50
      .MULTSIGNIN(MULTSIGNIN),         // 1-bit input: Multiplier sign input
51
      .PCIN(PCIN),                     // 48-bit input: P cascade input
52
      // Control: 4-bit (each) input: Control Inputs/Status Bits
53
      .ALUMODE(ALUMODE),               // 4-bit input: ALU control input
54
      .CARRYINSEL(CARRYINSEL),         // 3-bit input: Carry select input
55
      .CLK(CLK),                       // 1-bit input: Clock input
56
      .INMODE(INMODE),                 // 5-bit input: INMODE control input
57
      .OPMODE(OPMODE),                 // 7-bit input: Operation mode input
58
      // Data: 30-bit (each) input: Data Ports
59
      .A(A),                           // 30-bit input: A data input
60
      .B(B),                           // 18-bit input: B data input
61
      .C(C),                           // 48-bit input: C data input
62
      .CARRYIN(CARRYIN),               // 1-bit input: Carry input signal
63
      .D(D),                           // 25-bit input: D data input
64
      // Reset/Clock Enable: 1-bit (each) input: Reset/Clock Enable Inputs
65
      .CEA1(CEA1),                     // 1-bit input: Clock enable input for 1st stage AREG
66
      .CEA2(CEA2),                     // 1-bit input: Clock enable input for 2nd stage AREG
67
      .CEAD(CEAD),                     // 1-bit input: Clock enable input for ADREG
68
      .CEALUMODE(CEALUMODE),           // 1-bit input: Clock enable input for ALUMODE
69
      .CEB1(CEB1),                     // 1-bit input: Clock enable input for 1st stage BREG
70
      .CEB2(CEB2),                     // 1-bit input: Clock enable input for 2nd stage BREG
71
      .CEC(CEC),                       // 1-bit input: Clock enable input for CREG
72
      .CECARRYIN(CECARRYIN),           // 1-bit input: Clock enable input for CARRYINREG
73
      .CECTRL(CECTRL),                 // 1-bit input: Clock enable input for OPMODEREG and CARRYINSELREG
74
      .CED(CED),                       // 1-bit input: Clock enable input for DREG
75
      .CEINMODE(CEINMODE),             // 1-bit input: Clock enable input for INMODEREG
76
      .CEM(CEM),                       // 1-bit input: Clock enable input for MREG
77
      .CEP(CEP),                       // 1-bit input: Clock enable input for PREG
78
      .RSTA(RSTA),                     // 1-bit input: Reset input for AREG
79
      .RSTALLCARRYIN(RSTALLCARRYIN),   // 1-bit input: Reset input for CARRYINREG
80
      .RSTALUMODE(RSTALUMODE),         // 1-bit input: Reset input for ALUMODEREG
81
      .RSTB(RSTB),                     // 1-bit input: Reset input for BREG
82
      .RSTC(RSTC),                     // 1-bit input: Reset input for CREG
83
      .RSTCTRL(RSTCTRL),               // 1-bit input: Reset input for OPMODEREG and CARRYINSELREG
84
      .RSTD(RSTD),                     // 1-bit input: Reset input for DREG and ADREG
85
      .RSTINMODE(RSTINMODE),           // 1-bit input: Reset input for INMODEREG
86
      .RSTM(RSTM),                     // 1-bit input: Reset input for MREG
87
      .RSTP(RSTP)                      // 1-bit input: Reset input for PREG
88
   );

Ist aber eher unübersichtlich. Ich würde da lieber * und + schreiben.

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Wahrscheinlich hat der eine Verzögerung. Vllt einen FIFO einbauen?

Ne, FIFO brauchst du da nicht. Aber du kannst ja nach dem start der 
Filterei ein paar Takte warten bis du das Ergebnis abholst.

Mario A. schrieb:
> Hättest du die Berechnung mit + und * in einem Process gemacht, oder
> hättest du auch Blöcke mit Generate erzeugt?

+ und *

Mario A. schrieb:
> Ich könnte auch mal versuchen die DSP Slices direkt anzusprechen über
> die Makros im IP Catalog.

Siehe letzter Post:
Kann man machen wenn man irgendwas optimieren will und glaubt besser als 
der Synthesizer zu sein, würde ich aber an der Stelle nicht machen.

von Mario A. (itse_me_mario)


Lesenswert?

Kurz zu den Screenshots:
Wenn 16kHz dahinter steht, sind wurden die Koeffizienten für eine 
Abtastrate von 16 kHz verwendet.

Wenn Xilinx dabei steht wurde der FIR Compiler verwendet.


>>Dafür hast du doch die Generics.
Ja man könnte es generischer schreiben, das stimmt schon. Bei untersch. 
Anzahl an Koeffizienten, Länge, etc, muss ich auch die Signale MAX und 
die Adressen für die Block Memory Generatoren anpassen. Kann man alles 
machen.

>>Aber du kannst ja nach dem start der Filterei ein paar Takte warten bis du das 
Ergebnis abholst.
Das könnte ich mal versuchen.
Edit: Ich hab es mal mit
signal state_reg: std_logic_vector(9 downto 0):="0000000000";
alias s_write_data : std_logic is state_reg(9);
und
...
state_reg <= state_reg(8 downto 0) & new_sample;
versucht. Das liefert auch das selbe (falsche Ergebnis). Vllt eine noch 
größere Verzögerung verwenden?

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Ich hab es mal mit
> signal state_reg: std_logic_vector(9 downto 0):="0000000000";
> alias s_write_data : std_logic is state_reg(9);
> und
> ...
> state_reg <= state_reg(8 downto 0) & new_sample;
> versucht. Das liefert auch das selbe (falsche Ergebnis). Vllt eine noch
> größere Verzögerung verwenden?

Genau der Versuch ist gut, bringt aber hier nichts, weil die 
Multiplikation nur einen Takt braucht. Also eine zusätzliche Verzögerung 
ist nicht nötig.

Ich würde das mal alles mit * und + schreiben und normal signed als 
Datentyp verwenden. Wenn das funktioniert kannst du das immer noch Stück 
für Stück zurückbauen.

von Mario A. (itse_me_mario)


Lesenswert?

Das serielle Filter mit * und + scheint ja zu funktionieren (siehe 
FIR_Seriell.zip).

von -gb- (Gast)


Lesenswert?

Dann passt doch alles. Du kannst das auch parallel oder als Pipeline 
schreiben.

von Mario A. (itse_me_mario)


Lesenswert?

>>Du kannst das auch parallel oder als Pipeline schreiben.
Ein FIR mit Pipeline hast du eh schon mal hochgeladen wenn ich mich 
nicht täusche.

Parallel mit der Generate-Anweisung meinst du? Würdest du dafür die 
Berechnung der der einzelnen Taps mit * und + auslagen und dann wieder 
Arrays verwenden wie mit den Multiply Addern? Mit einem zus. Enable?

Pseudocode für die ausgelagerte Komponente:

component MAC_Component
clk : in
enable : in
INPUT : in
COE : in
Carry : in
Output : out

Logik:
if rising_edge(clk) then
   if enable = '1' then
       Output = INPUT * COE + Carry
   end if;

end if;


Danke Gustl!

von -gb- (Gast)


Lesenswert?

Mario A. schrieb:
> Parallel mit der Generate-Anweisung meinst du?

Dieses generate ist nur Schreibersparnis. Ja kann man machen, würde ich 
auch machen.

Mario A. schrieb:
> Würdest du dafür die Berechnung der der einzelnen Taps mit * und +
> auslagen

Wozu denn diesen kurzen Code

Mario A. schrieb:
> if rising_edge(clk) then
>    if enable = '1' then
>        Output = INPUT * COE + Carry
>    end if;

In eine extra Komponente auslagern? Das macht es doch nur 
unübersichtlich. Nein, ich würde da nix auslagern.

von Mario A. (itse_me_mario)


Lesenswert?

Alles klar, danke. Ich versuche das am Montag! :) Hab leider erst dann 
wieder Zugriff auf das FPGA. Bin gespannt, ob der Frequenzgang dann 
stimmt.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Ich habe jetzt mal meinen alten PC aufgesetzt und ein paar Simulationen 
gemacht.

Ich habe es jetzt doch erst einmal mit dem Auslagern in eine Komponente 
versucht (siehe MAC_Instruction.vhd). Als s_filtered_data erhalte ich 
damit allerdings nur XXXX...XXX. Vielleicht liegt es auch an den 
unterschiedlich großen Datentypen (24 Bit * 16 Bit + 48 Bit). Außerdem 
habe ich für die Komponente Signed-Vektoren verwendet und die 
Koeffizienten entsprechend deklariert. Umwandlungen von std_logic_vector 
nach signed habe ich direkt bei der Initialisierung innerhalb der 
Generate-Anweisung mit signed(...) durchgeführt.
Sollte ich vielleicht vor der Berechnung für alle Signale (A, B, C) eine 
Vorzeichenerweiterung machen und diese gleich groß machen (48 Bit)?

Ich habe auch mal s_write_data verzögert auf '1' gesetzt. Hat leider 
auch nicht geholfen.

ABER:
Ohne Auslagern erhalte ich auch ohne zusätzliche Verzögerung Werte (der 
Code ist in FIRFilter.vhd enthalten). Ob die Werte stimmen muss ich erst 
noch prüfen. Hast du das in etwa so gemeint Gustl? Also diese Variante 
der Generate-Anweisung? :) Ich ändere jetzt mal die Koeffizienten und 
die Eingangsdaten zu 1.

von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

Mario A. schrieb:
> Hast du das in etwa so gemeint Gustl?

Grob so habe ich das gemeint. Wobei ich das mit Loop machen würde. Ich 
habe dir jetzt mal ein Minimalbeispiel geschrieben mit Testbench.

von Mario A. (itse_me_mario)


Lesenswert?

Also die Versionen aus dem vorherigen Post funktionieren laut Analog 
Discovery 2 beide und liefern den richtigen Frequenzgang. :)

>>Ich habe dir jetzt mal ein Minimalbeispiel geschrieben mit Testbench.
Das habe ich auch gleich noch implementiert und es funktioniert 
ebenfalls. :) Danke!

Also alles ohne Multiply Adder Block funktioniert nun. ;)

von Gustl B. (-gb-)


Lesenswert?

Wunderbar, dann auf zu größeren Aufgaben^^

von Mario A. (itse_me_mario)


Lesenswert?

Genau. :D Danke dir! Erst mal noch ein paar Programme umschreiben und 
dann checken, wie ich die Daten vom UART mit C einlesen kann. :)

: Bearbeitet durch User
von Mario A. (itse_me_mario)



Lesenswert?

Ich habe gerade noch die Programme umgeschrieben und die UART-Anbindung 
für die eigens erstellten Filter ergänzt.
Die Aufnahmen mit dem Xilinx-FIR und DEINEN Koeffizienten liefern ein 
schönes Ergebnis, welches auch mit dem Frequenzgang des AD2 
übereinstimmt.

Mit meinen Koeffizienten sehen die aufgenommenen Daten auch mit dem FIR 
Compiler nicht ganz so gut aus. Ich weiß nicht, ob diese Erhöhung da 
sein dürfte? Überschwingen an den Kanten ja, aber das scheint schon sehr 
groß zu sein. Zumindest sieht man das nicht im Frequenzgang des Analog 
Discoverys. Der Frequenzgang mit dem Analog Discovery sieht an sich gut 
aus.

Ich habe die Daten mit den "alten" Python-Skripten aufgenommen und 
decodiert. Im Anschluss an die Anbindung des UART-Moduls an mein Filter 
wollte ich mit der Aufnahme der Daten in "C" beginnnen.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Mit meinen Koeffizienten sehen die aufgenommenen Daten auch mit dem FIR
> Compiler nicht ganz so gut aus.

Welches der Bildchen ist das?
Filterdaten_XilinxFIR.PNG

Mario A. schrieb:
> Ich weiß nicht, ob diese Erhöhung da
> sein dürfte? Überschwingen an den Kanten ja, aber das scheint schon sehr
> groß zu sein.

Was bedeutet denn groß? Ist ist noch deutlich kleiner 1 dB. Das kann man 
normalerweise im Filterdesigner einstellen wie flach das Passband sein 
soll.

von Duke Scarring (Gast)


Angehängte Dateien:

Lesenswert?

Mario A. schrieb:
> Ich weiß nicht, ob diese Erhöhung da
> sein dürfte?
Ca. 15% Überschwingen. Es gibt bestimmt irgendwo eine Übersicht wieviele 
Taps man benötigt, um einen bestimmtes Ripple/Überschwingen nicht zu 
überschreiten.

> Zumindest sieht man das nicht im Frequenzgang des Analog Discoverys.
Vielleicht liegt es am Antialiasing-Filter?
Wie ist denn die maximale Samplerate bzw. Bandbreite vom Analog 
Discovery?
Und welche Samplerate verwendest Du? Und bei welcher Frequenz liegen 
Pass- und Stop-Band von Deinem Filter?

Duke

von -gb- (Gast)


Lesenswert?

Die Daten zu diesem Bildchen hat er digital im FPGA hinter dem Filter 
abgegriffen. Am Eingang lag also ein Sinus Sweep an. Die Amplitude des 
Sweeps war konstant wie man am zweiten Plot im Bild sehen kann.

Diese Überhöhung kommt also wirklich durch das Filter. Da wären jetzt 
die Koeffizienten interessant.

von Gustl B. (-gb-)



Lesenswert?

So, jetzt habe ich mal die Koeffizienten aus deinen .coe Dateien geholt, 
in pyfda geladen und Bildchen gemacht. Wie man sieht geht das schon 
deutlich nach oben in Richtung Grenzfrequenz.

von Mario A. (itse_me_mario)



Lesenswert?

>>Es gibt bestimmt irgendwo eine Übersicht
Werd ich mal recherchieren. :)

Ich verwende eine Abtastrate von 32 kHz (siehe 
MATLAB_filterDesigner.png).
Apass: 1 dB
Astop: 80 dB

Also die Aufnahme FrequenzgangLinkerKanal.png und 
FilterdatenXilinxFIR.png sollten an sich die beiden Werte haben.
Der Matlab filterDesigner zeigt auch eine KLEINE Erhöhung.

Am AD2 sollte es nicht scheitern 
(https://reference.digilentinc.com/_media/reference/instrumentation/analog-discovery-2/ad2_rm.pdf).

Network analyzer – Bode, Nyquist, Nichols transfer
diagrams of a circuit. Range: 1Hz to 10MHz

- Waveform generator drives circuits with swept sine waves up to 10 MHz
- Input waveforms settable from 1 Hz to 10 MHz, with 5 to 1000 steps
- Settable input amplitude and offset
- Analog input records response at each frequency
- Response magnitude and phase delay displayed in Bode, Nichols, or 
Nyquist formats


Oh gerade gesehen, dass du was geschrieben hast Gustl. War während 
meines Posts unterwegs. :D Danke!

Hm laut deinem Bildchen müsste es ja dann mehrmals rauf und runter 
gehen. Nach Filterdaten_XilinxFIR.png tut es das aber nicht.

: Bearbeitet durch User
von -gb- (Gast)


Lesenswert?

In einem deiner Filter und im Matlab Screenshot geht es auch nur einmal 
hoch.

von Mario A. (itse_me_mario)



Lesenswert?

Stimmt jetzt sehe ich es erst. Das sind ja 2 verschiedene im Bild von 
Gustl! :) Dachte erst das eine war nur gezoomt. Danke!

Hab jetzt den rechten Kanal nochmal betrachtet. Es scheint schon zu 
passen. Ich habe jetzt auch einen viel kleineren Bereich mit dem AD2 
betrachtet und man kann nun auch dort die Schwingungen gut erkennen.

Den linken Kanal hab ich auch nochmal gezoomt aufgezeichnet. Scheint 
alles zu passen! Dankeschön.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Ja, sieht gut aus, aber die Amplitude könntest du dir nochmal angucken.

24 Bit signed geht von -2**23 bis +2**23-1, also von -8388608 bis 
+8388607. Bei dir sieht das Signal vor und nach dem Filter um ca. den 
Faktor 8 zu niedrig. Aber es könnte auch sein, dass du von aussen analog 
nur einen sehr kleinen Pegel hineinfütterst.

Du darfst an den Eingang deines ADCs 0.568*3.3V = 1.8744 V_pp anlegen. 
Im Schaltplan 
https://reference.digilentinc.com/_media/reference/pmod/pmodi2s2/pmodi2s2_sch.pdf 
ist ein Spannungsteiler vor dem AC-gekoppelten Eingang. Du kannst also 
tatsächlich ca. 3.7 V_pp anlegen um den ADC voll auszusteuern.

Edit:
In deinen Screenshots steht was von 100 mV Amplitude, da kannst du 
deutlich nach oben gehen wenn du den ADC voll aussteuern möchtest.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

>>In deinen Screenshots steht was von 100 mV Amplitude
Genau, ansonsten sieht es so aus wie in Amplitude_1V.png. Dasselbe 
Problem habe ich auch, wenn ich ein Filter auf dem DSP implementiere. 
Die analogen Eingänge des AD-Wandlers werden sonst übersteuert.

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Die analogen Eingänge des AD-Wandlers werden sonst übersteuert.

Eben das sollte ja bis ca. 3.7 V_pp nicht passieren laut ADC Datenblatt 
und Schaltung.

Wie sieht denn das Signal über UART aus wenn du da einen 1 V oder 2 V 
Sinus reinfütterst?

Edit:
Was auch auffällt ist, dass diese Software WaveForms ziemlich mies ist. 
Deine damit aufgenommenen Spektren sehen alles andere als gut aus, die 
Skalierung der y-Achse ist für mich unklar und rechts im Bildchen bei 
den höheren Frequenzen sieht das Spektrum immer ziemlich kaputt aus.
Deine Hardware die du mit WaveForms verwendest, also das Analog 
Discovery 2 sollte eigentlich gut genug aus, trotzdem sieht das seltsam 
aus, fast so, als würde die Abtastrate nicht reichen um das Signal 
korrekt zu erfassen.

Hast dieses WaveForms auch einen Oszi Modus? Dann könntest du den 
Eingang sweepen und den Ausgang mit dem Oszi angucken. Oder vielleicht 
geht das auch in dem Bode Modus. Der Spektrumanalyser ist dafür eher 
schlecht zu gebrauchen wenn du den Eingang sweepst und der aber das mit 
einer FFT berechnet.
Wenn du den Spektrumanalyzer verwenden willst, dann gebe doch mal ein 
Rauschen auf den Eingang. So ein 2 V-pp weißes Rauschen. Da müsste man 
dann mit jedem Spektrumanalyzer das Filter schön erkennen können.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Ich habe mal den linken Kanal (1000/2000 Filter) für eine Amplitude von 
1V aufgenommen. Diesen deutlichen Überschwinger sieht man dann nicht 
mehr.


>>Hast dieses WaveForms auch einen Oszi Modus?
Hat es an sich. Ich habe einen BNC-Adapter drauf. Um damit auf die 
Audioanschlüsse des PMOD I2S Adapters zu kommen, habe ich noch ein 
zusätzliches Bauteil zwischen BNC-Adapter und FPGA.
https://reference.digilentinc.com/reference/instrumentation/bnc-adapter-board/reference-manual
https://reference.digilentinc.com/_media/reference/instrumentation/bnc-adapter-board/discovery_bnc_sch.pdf

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Diesen deutlichen Überschwinger sieht man dann nicht
> mehr.

Es könnte sein, dass das clippt, das würdest du sehen wenn du mal 
reinzoomst. Sonst sieht das auch nicht so toll aus. Im Stoppband hast du 
schon deutliche Störungen drinnen. Die sollten da nicht sein wenn du 
beim Sweep unter f_sample/2 bleibst.

Aber du kannst ja mal das mit dem Rauschen probieren und du könntest 
deinen Frequenzbereich auch mit ein paar festen Frequenzen durchmessen. 
Also von 0 Hz bis 10 kHz in 1 kHz Schritten oder feiner. Wenn du da eine 
feste Frequenz mit z. B. 5 kHz anlegst solltest du nicht die Störungen 
sehen die du im Sweep siehst. Wenn die trotzdem da sind dann würde ich 
sagen passt was nicht.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Irgendwie klappt das gerade nicht so, wie ich mir das vorstelle. Also, 
dass ich einen Sinus durchschicke und den Ausgang mit dem Scope messe.
Ich hätte dazu mal die Eingangsdaten ungefiltert wieder ausgegeben 
(s_unfiltered_data anstatt s_filtered_data an i2s_data_interface 
angeschlossen).
Ob ich Wavegen aktiviere oder nicht macht keinen Unterschied. Ich muss 
mich da noch einlesen. Ich glaube ich habs falsch angeschlossen.

: Bearbeitet durch User
von Mario A. (itse_me_mario)



Lesenswert?

Jetzt siehts besser aus. Eine Amplitude von 1V hat einen schöneren 
Verlauf zur Folge als eine Amplitude von 0,1V (weniger Rauschen).

Ich habe auch mal einen 1 kHz und einen 1 Hz Puls durchgeschickt und 
eine Sinusweep von 1Hz bis 16kHz.

Auf den Bilden sind Signale ohne und mit Filter zu sehen.

Auf einmal funktionierts. Vllt hat es sich vorhin irgendwie aufgehängt.

: Bearbeitet durch User
von Gustl B. (gustl_b)


Lesenswert?

Hm. Ich wäre damit noch lange nicht zufrieden. Das ist ein Audio ADC und 
kein schlechter 8Bit ADC in einem billigen uC.
Aber ich weiß auch nicht wie du den Signalgenerator mit dem Audio PMOD 
verbunden hast und so. Die Massen hast du verbunden? Auch keine langen 
ungeschirmten Kabel die die Kabel zu deinem UART Modul kreuzen?

Ich hätte da jetzt einen zumindest für den menschlichen Betrachter 
glatten Sinus erwartet.

Wir wissen aber auch nicht wie gut dieser Sinusgenerator ist. Du 
könntest also mal den Ausgang des Generators direkt mit dem Eingang des 
Oszis verbinden und gucken wie das Signal da aussieht.

von Mario A. (itse_me_mario)



Lesenswert?

Hallo Gustl,
also vom PMOD gehts über 2 Klinkenstecker auf BNC-Buchsen (ca. 25cm 
lang) und die gehen mittels Koax-Kabel auf den BNC-Adapter.

Die Masse des UART-Moduls ist mit dem PMOD-Massepin verbunden. Beim 
PMOD-Adapter selbst sind sowieso auch die Massen angeschlossen. Die 
Leitungen zum UART-Modul sind nur einfache Arduinoleitungen, ca. 20cm 
lang. Das ist sicher nicht ideal.

Programm läuft und die Eingangsdaten werden ungefiltert wieder 
ausgegeben. Anschlussmethode V2.
Anlegen eines 1kHz Sinus´ an Channel 2: 
Anschluss_V2_1kHz_Sinus_anChannel2
Anlegen eines 1kHz Sinus´ an Channel 1: 
Anschluss_V2_1kHz_Sinus_anChannel1
Manchmal sieht der Sinus auch ideal aus... Z.B. nach dem Laden des 
Programms. Außerdem zeigt es auch ohne Programm einen Sinus an. Leider 
habe ich kein "richtiges" Oszilloskop zur Hand.

Programm läuft und die Eingangsdaten werden ungefiltert wieder 
ausgegeben. Anschlussmethode V1.
Anlegen eines 1kHz Sinus´ an Channel 2: 
Anschluss_V1_1kHz_Sinus_anChannel2
Anlegen eines 1kHz Sinus´ an Channel 1: 
Anschluss_V1_1kHz_Sinus_anChannel1

Wenn ich CH1 und CH3 direkt verbinde und einen 1kHz Sinus anlege zeigt 
es irgendwie gerade gar nichts an (Anschluss V3). Irgendwie spinnt das 
AD2 gerade. Mal funktionierts, mal wieder nicht...




Ich würde mich jetzt dann mal den periodischen Fehlern bei der 
UART-Aufnahme widmen.
Kann ich beim Lesen mit

...
while len(sr) < 14*32000*Record_Time_in_Seconds:
  data = ser.read(1000000)
  if len(data) > 0:
    sr.extend(data)
ser.close()
...

bei data = ser.read(1000000) nicht auch eine kleinere Zahl verwenden? 
Ich probier das mal.

Mit data = ser.read(1000) scheint es zu passen (siehe Errorcount.png). 
4480933 Bytes/14 Bytes = 320066,6429 Pakete

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> also vom PMOD gehts über 2 Klinkenstecker auf BNC-Buchsen (ca. 25cm
> lang) und die gehen mittels Koax-Kabel auf den BNC-Adapter.

Dann zeichne das mal mit der Masseverbindung. Also dein AnalogDiscovery 
sollte ebenfalls eine Masseverbindung zum FPGA Board haben. Idealerweise 
eine Masseverbindung zu jedem Massekontakt in der Klinkenbuchse.

Mario A. schrieb:
> Die Masse des UART-Moduls ist mit dem PMOD-Massepin verbunden. Beim
> PMOD-Adapter selbst sind sowieso auch die Massen angeschlossen. Die
> Leitungen zum UART-Modul sind nur einfache Arduinoleitungen, ca. 20cm
> lang. Das ist sicher nicht ideal.

Das ist digitales Zeug, und das sieht ja bei dir gut aus, das Analoge 
sieht komisch aus.

Mario A. schrieb:
> Manchmal sieht der Sinus auch ideal aus... Z.B. nach dem Laden des
> Programms. Außerdem zeigt es auch ohne Programm einen Sinus an.

Das Bildchen das hier den Sinus zeigt sieht nicht gut aus. Nur die 
zweite Periode ist OK.

Zu der Schaltung aus AnschlussGrundlage_V3.png würde mich noch ein Bild 
vom Signal interessieren. Also mit dem AnalogDiscovery einen Sinus 
erzeugen und gleich wieder mit dem AnalogDiscovery angucken. Der sollte 
dann gut aussehen.

Mario A. schrieb:
> Irgendwie spinnt das
> AD2 gerade. Mal funktionierts, mal wieder nicht...

Das ist nicht gut.

Mario A. schrieb:
> kleinere Zahl verwenden?
> Ich probier das mal.

Klar geht das. Du solltest für maximalen Durchsatz und keinen 
Datenverlust aber den gesamten Empfangspuffer auslesen. Wie groß der ist 
weiß ich nicht, daher habe ich da bisher immer eine große Zahl 
verwendet.

Mario A. schrieb:
> Mit data = ser.read(1000) scheint es zu passen (siehe Errorcount.png).
> 4480933 Bytes/14 Bytes = 320066,6429 Pakete

Genau das sieht fehlerfrei aus. Sollte aber bei größeren Zahlen wie 
ser.read(100000) gleich bleiben. Es könnte nur sein, dass wenn die Zahl 
zu klein wird Fehler kommen. Sprich: Größere Zahlen haben keinen 
Nachteil, aber zu kleine schon. Da ich die Grenze nicht kenne habe ich 
große Zahlen verwendet.

von Mario A. (itse_me_mario)


Lesenswert?

>>Also dein AnalogDiscovery sollte ebenfalls eine Masseverbindung zum FPGA Board 
haben. Idealerweise eine Masseverbindung zu jedem Massekontakt in der 
Klinkenbuchse.
Danke für die Antwort! Also das AD2 ist theoretisch über den PMOD 
I2S-Adapter mit GND verbunden. Die Klinkenstecker sollten eine 
Masseverbindung haben (https://de.wikipedia.org/wiki/Klinkenstecker). 
Ich kann aber noch mit einer extra Leitung vom AD2 auf einen PMOD GND 
Pin des FPGAs gehen.


>>Zu der Schaltung aus AnschlussGrundlage_V3.png würde mich noch ein Bild
vom Signal interessieren.
Das hat vorhin irgendwie nicht funktioniert. Ich probier das morgen 
Vormittag nochmal und lad es dann hoch. Dann schließe ich auch den GND 
Pin vom AD2 noch zus. an den PMOD an. :)

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Also das AD2 ist theoretisch über den PMOD
> I2S-Adapter mit GND verbunden. Die Klinkenstecker sollten eine
> Masseverbindung haben (https://de.wikipedia.org/wiki/Klinkenstecker).

Ja, das ist richtig, aber wie genau hast du den Klinkenstecker mit dem 
AnalogDiscovery verbunden?

Mario A. schrieb:
> Ich probier das morgen
> Vormittag nochmal und lad es dann hoch. Dann schließe ich auch den GND
> Pin vom AD2 noch zus. an den PMOD an. :)

Sehr gut!

Jedenfalls würde ich tatsächlich erstmal diese analogen Probleme lösen, 
das sieht nämlich nicht gut aus. Daten über UART bekommst du jetzt 
fehlerfrei, das ist super.

Interessant wäre auch mal wenn du einen Sinus erzeugst, z. B. 1 kHz und 
dann anguckst wie der ungefiltert im FPGA aussieht. Also über UART die 
Daten ausliest und nicht die ganze Zeit plottest, sondern nur einen 
kleinen Aussschnitt, damit man den Signalverlauf gut sehen kann. Du 
kannst auch die Daten vom UART hier hochladen.
Dann könnte man nämlich Schlüsse ziehen ob das Einlesen mit dem ADC oder 
das Ausgeben über den DAC die Fehler verursacht.

von Mario A. (itse_me_mario)


Lesenswert?

Von Klinke gehts auf BNC. Die Massen sind miteinander verbunden.

Hab jetzt gerade nochmal CH1 und CH3 verbunden. Das Scope zeigt leider 
nichts an, wenn ich einen 1kHz Sinus anlege... Das hat es aber schon mal 
gemacht. Aber eigtl. hab ich nur ein paar Koaxkabel hin- und 
hergesteckt. Da dürfte nichts kaputt gehen. Hab leider gerade nicht mal 
ein Multimeter da...

>>Interessant wäre auch mal wenn du einen Sinus erzeugst, z. B. 1 kHz und
dann anguckst wie der ungefiltert im FPGA aussieht.
Sobald das mit dem Scope funktioniert mach ich das mal.

von Mario A. (itse_me_mario)


Lesenswert?

Ich implementiere jetzt mal ein adaptives Filter auf einem 
Mikrocontroller. Zunächst mal ohne Echtzeitanbindung. Bei Interesse kann 
ich das auch gerne hier posten.

Dann will ich den Algorithmus (LMS vllt auch NLMS) auf dem FPGA 
implementieren.

Für das FPGA will ich die Daten zunächst aus einem txt-File mit einer 
Testbench einlesen und die Daten nach dem Filter wieder in ein txt-File 
schreiben.

von Mario A. (itse_me_mario)


Lesenswert?

Ich hab jetzt mal einen LMS und NLMS Algorithmus in C implementiert 
(erst mal offline). Dafür habe ich aus Matlab die notwendigen Signale 
für das adaptive Filter - welche ich als .wav-Dateien zur Verfügung 
hatte - in ein Textfile geschrieben. Jede Zeile in meinen Textfiles 
enthält nun einen Double-Wert. Die Signale wurden allerdings mit 16 kHz 
aufgenommen. Diese muss ich also noch Upsamplen, da mein PMOD I2S 
Adapter erst ab 32 kHz arbeitet.

Idee: Spracherkenner (z. B. Auto)
Musik wird abgespielt und der Fahrer möchte währenddessen sein 
Fahrerassistenzsystem per Sprache bedienen. Also der Lautsprecher gibt 
das Musiksignal aus und ein Mikrofon nimmt quasi das Signalgemisch aus 
Musik (verändert über die Übertragungsfunktion des Raumes) und Sprache 
auf. Ziel: Musik aus Signalgemisch filtern.

Die verwendeten Signale waren 1x das Referenzsignal (nur Musik) und 1x 
das Signalgemisch bestehend aus Musik und Sprache unter Einbeziehung 
einer zus. Übertragunsfunktion (des Raums). Ziel des adaptiven Filters 
ist es nun das (veränderte) Musiksignal aus dem Signalgemisch weitgehend 
zu minimieren, sodass am Ende die Sprache erkannt werden kann. Annahme: 
Musik und Sprache sind unkorreliert und die Musiksignale vor und nach 
der Veränderung sind korreliert.

Die Daten aus den Textdateien habe ich dann mit fscanf für jede 
Iteration in eine Variable eingelesen (da es je nach Signal teilweise 
ziemlich viele Werte sind). Für kleinere/kürzere Signale könnte man 
natürlich ein Array anlegen.
Die berechneten Daten habe ich dann mit fprintf wieder in ein Textfile 
geschrieben und ich kann diese nun mithilfe von Matlab auswerten (z. B. 
ERLE-Kurven).

Eine Festkommarealisierung des LMS und NLMS in C muss ich auch noch 
machen.


Ich seh´ mir jetzt zwischenzeitlich mal an, wie ich die Daten in einer 
Testbench einlesen kann.
Meine Eingangsdaten, die ich für das den C-Algorithmus verwendet habe 
waren vom Typ Double. Für die Verarbeitung im FPGA habe ich diese nun 
mit dem Faktor 2^15 multipliziert, damit ich die Daten im Q15-Format 
habe. Anschließend will ich diese nun in der Testbench Abtastwert für 
Abtastwert einlesen. Am liebsten wärs mir, wenn ich diese direkt als 
Ganzzahl (mit Vorzeichen) einlesen kann. Aber ich könnte die Ganzzahlen 
auch zuvor in eine Binärzahl umwandeln und dann einlesen.

@Gustl
Weißt du zufällig, wo das ganze Zeug um: 
file_open(disp_in,"disp_in.dta",READ_MODE); gut dokumentiert ist? Ich 
hab meine alten Unterlagen gerade nicht vor Ort. Vielen Dank im Voraus!

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Weißt du zufällig, wo das ganze Zeug um:
> file_open(disp_in,"disp_in.dta",READ_MODE); gut dokumentiert ist?

Du kannst die Datei auch implizit öffnen lassen:

file DATEINAME: DATEITYP open read_mode is "DATEINAME";

Und dann musst du dir aussuchen wie deine Daten in der Datei stehen.
VHDL kann von Hause aus Dateien lesen und schreiben. Hier ein 
Minimalbeispiel, das kopiert die Datei IN.bin nach OUT.bin und gibt den 
Wert der einzelnen Bytes während der Simulation aus.
1
entity sim_io is
2
end sim_io;
3
4
architecture Behavioral of sim_io is
5
6
type byte_file is file of character;
7
file bytefile_in: byte_file open read_mode is "IN.bin";
8
file bytefile_out: byte_file open write_mode is "OUT.bin";
9
10
begin
11
12
process
13
  variable byte: character;
14
begin
15
  while not endfile(bytefile_in) loop
16
    read(bytefile_in, byte);
17
    write(bytefile_out, byte);
18
    report "ByteValue: " & integer'image(character'pos(byte));
19
    wait for 10 ns;
20
  end loop;
21
  report "Done";
22
  wait;
23
end process;
24
25
end Behavioral;

(Nein, am Anfang muss hier wirklich keine Bibliothek stehen. Was aber 
seltsam ist:
integer'image
funktioniert hier ohne die numeric_std.)

Wenn du da lesbaren Text in der Datei stehen haben möchtest, dann guck 
dir mal die Beiden

use std.textio.all;
use IEEE.std_logic_textio.all;

an.

: Bearbeitet durch User
von Gustl B. (-gb-)


Lesenswert?

Und weil mich das interessiert hat hier noch eine Beschreibung die Text 
(Integers) kopiert und schön in Spalten formatiert.
Das kommt klar mit:
- Unterschiedlich vielen Zahlen je Zeile,
- Leerzeilen.

Es macht als Beispiel aus

IN.txt
1
0 1
2
-23 456
3
4
7890 123 -4567890

die Datei

OUT.txt
1
         0         1
2
       -23       456
3
4
      7890       123  -4567890

mit je 10 Zeichen und rechtsbündig je Zahl.
1
use std.textio.all;
2
3
entity sim_io_txt is
4
end sim_io_txt;
5
6
architecture Behavioral of sim_io_txt is
7
8
file textfile_in: text open read_mode is "IN.txt";
9
file textfile_out: text open write_mode is "OUT.txt";
10
11
begin
12
13
process
14
  variable line_in: line;
15
  variable line_out: line;
16
  variable number_in: integer;
17
  variable number_in_valid: boolean;
18
  variable number_out: integer;
19
begin
20
  while not endfile(textfile_in) loop
21
    number_in_valid := TRUE;
22
    readline(textfile_in, line_in);
23
    while number_in_valid loop
24
      read(line_in, number_in, number_in_valid);
25
      exit when not number_in_valid;
26
      report "Wert: " & integer'image(number_in);
27
      number_out := number_in;
28
      write(line_out, number_out, right, 10);
29
    end loop;
30
    writeline(textfile_out ,line_out);
31
    wait for 10 ns;
32
    report "Next Line";
33
  end loop;
34
  report "Done";
35
  wait;
36
end process;
37
38
end Behavioral;

Das braucht wirklich nur diese eine Bibliothek.
Viel Spaß!

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

Hallo Gustl, vielen Dank für die sehr sehr schnelle Rückmeldung und das 
tolle Beispiel! Das probiere ich morgen nach der Arbeit gleich mal aus. 
:)

Meine Textdateien enthalten eh Integerwerte. Die kann ich dann direkt 
einlesen und dann muss ich noch schauen, ob ich diese wieder in 
std_logic_vector´en umwandle für die Berechnung. :) Klingt alles sehr 
vielversprechend! Danke dir! :)

von Mario A. (itse_me_mario)


Lesenswert?

Hallo Gustl, ich hab dein Beispiel eingebunden: Funktioniert 
einwandfrei, danke! :) Morgen geht´s weiter. :) Dann schau ich mir an, 
wie ich die Daten vom txt-File in datain schreibe und die gefilterten 
Daten wieder in ein txt-File schreibe.

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

Ich hab jetzt eine Kleinigkeit geändert, sodass ich std_logic_vectors 
als Input und Output habe. Die Methode von dir funktioniert super Gustl!

Für die Umwandlung der Datentypen ist diese Grafik echt sehr 
anschaulich: https://www.bitweenie.com/listings/vhdl-type-conversion/

...
process
    ...
    begin
      while not endfile(textfile_in) loop
        number_in_valid := TRUE;
        readline(textfile_in, line_in);
        while number_in_valid loop
          read(line_in, number_in, number_in_valid);
          exit when not number_in_valid;
          report "Wert: " & integer'image(number_in);
          s_datain <= std_logic_vector(to_signed(number_in, 24));
          number_out := to_integer(signed(s_firout));
          write(line_out, number_out, right, 20); --10);
        end loop;
        writeline(textfile_out ,line_out);
        wait for 10 ns; -- entspricht quasi "Abtastrate"
        report "Next Line";
      end loop;
      report "Done";
      wait;
end process;

Den Code kann ich dann später in ähnlicher Form für meine ersten 
Berechnungen mit dem adaptiven Filter verwenden. :)

von Gustl B. (-gb-)


Lesenswert?

Mario A. schrieb:
> Für die Umwandlung der Datentypen ist diese Grafik echt sehr
> anschaulich: https://www.bitweenie.com/listings/vhdl-type-conversion/

Exakt, die erklärt eigentlich wunderbar wie man die numeric_std benutzen 
sollte.

Lob und Anerkennung! Weiter so!

von Mario A. (itse_me_mario)


Lesenswert?

Vielen Dank wieder mal für deine Hilfe. :) Jetzt gibts erst mal noch 
bissl Theorie zum adaptiven Filter und dann wird ein erstes Filter in 
VHDL implementiert. :)

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,
ich bin gerade dabei den LMS-Code im Anhang in VHDL zu implementieren. 
Ich habe jetzt mal versucht den C-Algorithmus direkt 1zu1 in VHDL 
umzusetzen.

Ich habe mein Modul "LMS_Filter.vhd" nun statt meinem 
FIRFilter.vhd-Modul eingebunden. In AudioInterface ändert sich dadurch 
nicht viel.

Mein Problem sind die verschiedenen Datenformate:
Eingangsdaten sind 24 Bit
Ausgangsdaten sind 24 Bit
Koeffizienten sind 16 Bit

Ich hätte die Daten im Q0.15 bzw. im Q0.23 Format interpretiert. Also 1 
VZ-Bit und 15 bzw. 23 Nachkommabits (VZ, 1/2, 1/4, 1/8, ...).
Q0.15 * Q0.15 --> Q2.30 --> für Q0.15 --> 30 downto 15 nehmen
Q0.15 * Q0.23 --> Q2.38 --> für Q0.15 --> 38 downto 15 nehmen


In der Schleife mit arr_w_acc kann ich mir allerdings nicht die 
richtigen Daten mit 38 downto 15 extrahieren, da ich dann einen 
Syntaxfehler habe: "Error: Cannot index the result of a type conversion"

Eigentlich wollte ich Folgendes in einer Zeile machen:
   acc <= acc   + Q0.15 *Q0.23 * Q0.23
-> acc <= acc   + Q2.38(38 downto 23) * Q0.23
-> acc <= acc   + Q0.15 * Q0.23
-> acc <= acc   + Q2.38
-> acc <= Q2.38 + Q2.38 -- acc = 47..0 -> 8 Guardbits


Jetzt hätte ich mal alles auf einmal berechnet:
acc <= acc   + Q0.15 * Q0.23 * Q0.23
acc <= Q3.61 + Q3.61  -- acc = 71..0 -> 8 Guardbits

Darf ich das einfach so machen? Es sind ja jetzt quasi 3 Vorzeichenbits. 
Leider habe ich noch keine genauere Beschreibung zur Fixed-Point 
Arithmetik gefunden.

Wie hättet ihr den Algorithmus implementiert? Findet ihr das zu 
umständlich?
Vielen Dank im Voraus und einen schönen 3. Advent! :)

Ich schreibe dazu jetzt mal eine passende Testbench. :)

von Gustl B. (-gb-)


Lesenswert?

Moin,
ich kann dir da leider nicht helfen. Mit so Filtereisachen bin ich nicht 
sehr vertraut.

Mario A. schrieb:
> Ich schreibe dazu jetzt mal eine passende Testbench. :)

Das ist die Idee schlechthin! Mach das und guck dir an wie sich deine 
Beschreibung verhält.

Sonst warte hier auf kompetente Hilfe, aber lange Threads werden weniger 
häufig gelesen. Es spricht nichts dagegen einen neuen Thread zu diesem 
speziellen Spezialthema aufzumachen.

Viel Erfolg!

von Mario A. (itse_me_mario)


Lesenswert?

Hallo Gustl,

kein Problem! :) Vllt trotzdem eine Frage an dich: Hättest du den 
Algorithmus in ähnlicher Weise implementiert? Oder wärst du das ganz 
anders angegangen? Hättest du dieses ganze Q... Zahlenformatzeug 
überhaupt verwendet? Oder einfach z.B. Integers verwendet?

>>Das ist die Idee schlechthin!
Bin dabei. :)

>>einen neuen Thread zu diesem speziellen Spezialthema aufzumachen.
Ja das wäre vermutlich wirklich am geschicktesten! :) Vielen Dank dir!

von Gustl B. (-gb-)


Lesenswert?

Mir sind da noch zu viele Dinge unklar. w, N und BETA fallen irgendwie 
aus der Luft?!
Ich würde das erstmal in einer normalen Sprache schreiben. Bei mir wäre 
das Python. Oder nimm den Code und tippe den ab. Und dann guck dir an 
wie sich die Zahlen verhalten. Vielleicht geht das alles mit Integer. 
Wenn du das in einer Hochsprache hast, dann kannst du das auch in VHDL 
übersetzen. Als Zwischenschritt kann man das ja in der Hochsprache so 
schreiben, dass da Takte simuliert werden. Also eine Schleife die quasi 
den Takt darstellt und innerhalb darfst du nur auf Dinge zugreifen, die 
im vorherigen Schleifenzugriff gesetzt wurden. Außer du würdest 
Variablen im VHDL später verwenden. Kann man machen, ist auch nicht 
weiter schwer, aber man muss sich immer klar sein was man da gerade 
macht und wie sich das in Hardware verhalten wird. Da gibt es große 
Unterschiede zwischen Signal und Variable.
Und dann gibt es den Unterschied zwischen der einfachen Simulation und 
der Hardware. Man kann problemlos in VHDL sehr komplexe Ausdrücke 
hinschreiben. Das sieht dann auch in der Simulation gut aus, aber in der 
Hardware kann das schlicht zu lange dauern dass das Timing nicht 
geschafft wird.

-------------------------------------------------
Falls du Variablen verwenden solltest:
Variablen verhalten sich, soweit ich weiß, nur wie Drähte. Die haben 
also kein Speicherverhalten. Während Signale auch speichern können wenn 
man sie entsprechend verwendet.

Braucht man überhaupt Variablen?
Nein. Man kann alles mit Signalen schreiben, es wird aber dann 
vielleicht sehr unübersichtlich. Und man muss das dann ggf. so 
schreiben, dass kein Speicherverhalten auftritt.

------------------------------------------------
Ja ... also wenn du mir beschreibst was der Code aus dem Foto machen 
soll und mir beschreibst wo welche Buchstaben/Variablen in dem Code 
herkommen und welchen Typ die haben (steht leider nicht dabei) dann 
würde ich mal versuchen den nachzubasteln. Aktuell ist mir da noch zu 
vieles unklar.

von Mario A. (itse_me_mario)



Lesenswert?

Hallo Gustl,

Danke für die ganzen Tipps! Ich versuch das umzusetzen.

Ich hab den LMS und NLMS schon mal probeweise in C geschrieben. Ich 
hänge dir mal die .c Datei an. Ich hab dafür erst mal Codeblocks 
verwendet. Ich lese die Daten (Achtung, das sind 544000 Werte!) über 
eine Textdatei ein und schreibe die gelesenen Daten wieder in eine 
txt-Datei.

Nochmal die Idee dahinter: Spracherkenner (z. B. Auto)
Musik wird abgespielt und der Fahrer möchte währenddessen sein
Fahrerassistenzsystem per Sprache bedienen. Also der Lautsprecher gibt
das Musiksignal x aus und ein Mikrofon nimmt quasi das Signalgemisch z 
aus
Musik (verändert über die Übertragungsfunktion des Raumes) und Sprache
auf. Ziel: Musik aus Signalgemisch filtern.
Ich habe dir mal eine Zeichnung mit vielen Erklärungen gemacht.

Die verwendeten Signale sind 1x das Referenzsignal x (nur Musik: 
XData_FULL.txt) und 1x das Signalgemisch z bestehend aus Musik und 
Sprache unter Einbeziehung einer zus. Übertragunsfunktion (des Raums: 
ZData_FULL.txt). Ziel des adaptiven Filters ist es nun das (veränderte) 
Musiksignal aus dem Signalgemisch weitgehend zu minimieren, sodass am 
Ende die Sprache erkannt werden kann. Hört man sich nachher das Signal e 
(also den Fehler) an, ist im Idealfall nur noch die Sprache zu hören.
Annahme:
Musik und Sprache sind unkorreliert und die Musiksignale vor und nach
der Veränderung sind korreliert.

Erst mal mit float/double:
XData_FULL.txt: Musik ohne Übertragungsfkt. des Raums
ZData_FULL.txt: Gemisch von Sprache und Musik (inklusive 
Übertragungsfkt. des Raums)

Als Festkommarealisierung (diese muss ich allerdings noch weiter 
überprüfen: Ich arbeite parallel daran :D ):
DataX_All_Q15_Fixed.txt: --> vorherige txt-File-Daten mit 2^15 
multiplizieren (für Q0.15)
DataZ_All_Q15_Fixed.txt: --> vorherige txt-File-Daten mit 2^15 
multiplizieren (für Q0.15)

Am leichtesten ist erst Mal main_LMS_Float.c zu verstehen.

Die Auswertung der txt-Dateien mache ich dann in MATLAB.
LMS-Algorithmus und NLMS-Algorithmus möchte ich jetzt dann eben noch auf 
dem FPGA zum laufen bringen. :)

Wenn noch irgendwas unklar ist, meld dich einfach! :) Vllt schaff ich es 
morgen, dass ich auch noch einen spezifischeren Thread für die 
Festkommarealisierung aufmache. Das ist schon sehr speziell. :D Vielen 
Dank dir schon mal!

: Bearbeitet durch User
von Mario A. (itse_me_mario)


Lesenswert?

Ich muss gerade auf ein neues UART-Modul warten... Es scheint den 
Transport nicht überlebt zu haben. Wenn ich das Modul früher an den 
Computer angesteckt habe, ging der Lüfter des FPGAs an, auch wenn es 
ausgeschaltet war. Das macht es jetzt nicht mehr. Daten werden keine 
mehr aufgenommen... :( Am USB-Kabel liegt es nicht. Auch mit einem 
zweiten hat es nicht funktioniert.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich habe ein LMS-Filter in VHDL implementiert (siehe Anhang). Die ERLE 
Kurven sehen sehr gut aus:

ERLE = 10*log10(Pd/Pe)

Pd ist die Leistung des gewünschten Signals und Pe die Leistungs des 
Fehlersignals. Das Fehlersignal wird mit e = d-y berechnet. y ist mein 
berechneter FIR-Filterausgang.

Ich habe verschiedene LMS-Filter implementiert (16/24 Bit Daten, 16/24 
Bit Koeffizienten) und alle funktionieren (im Anhang ist ein Filter mit 
16 Bit Koeffizienten und 24 Bit Daten). Ich lese jeweils 32000 Werte für 
x und d ein und berechne daraus dann e (und y). Die Werte für e und y 
schreibe ich dann wieder in ein txt-File. Da es 32000 Werte sind und die 
Abtastrate 32kHz beträgt, muss die Simulation 1s lang laufen, wenn alle 
Werte berechnet werden sollen (das dauert... :D). An dieser Stelle 
nochmal danke an @Gustl. :) Die Dateien für d und x im Anhang müssen 
hierfür in den sim Ordner kopiert werden.

Das i2s_data_interface liefert alle 32kHz neue Werte (die ich aber 
gerade nicht verwende). Außerdem liefert es alle 32kHz ein '1' Signal. 
Die steigende Flanke davon verwende ich, um mein LMS-Filter zu starten.



Mein nächstes Ziel ist es nun ein NLMS-Filter zu implementieren.

Meine Koeffizienten w wurden bisher folgendermaßen (von der Logik her) 
im Laufe der Zeit verändert (allerdings als Fixed-Point):

for (i = N-1; i >= 0; i--)
{
 w[i] += (BETA*e*x[i]);  // update filter weights
 if(i!=0)
 {
  x[i] = x[i-1];      // shift data in delay line
 }
}

Nun sollen diese wie folgt berechnet werden:
for (i = N-1; i >= 0; i--)
{
  w[i] += ( ((BETA*e*x[i])/input_signal_power) );  // update filter 
weights
  if(i!=0)
  {
   x[i] = x[i-1];      // shift data in delay line
  }
}

Wobei input_signal_power Folgendes ist:
for(j=0; j<N;j++)
{
 input_signal_power = input_signal_power + (x[j] * x[j]);
}

Ich wollte mich mal erkundigen, wie ihr die Division durch 
input_signal_power durchführen würdet und ob ihr Tipps habt? Die 
Division mit "/" funktioniert ja leider lediglich in der Simulation. Ich 
würde es erst mal mit dem Divider Generator aus dem IP Catalog 
versuchen. :)
Ich würde mich sehr freuen, falls jemand einen Tipp hat. :) Bin gerade 
dabei meine FSM anzupassen. Ich denke ich werde mit Generate mehrere 
Divider-Blöcke erzeugen, damit die Division parallel berechnet werden 
können (für jeden Filterkoeffizienten brauch ich eine Division).

Vielen Dank im Voraus!

: Bearbeitet durch User
von -gb- (Gast)


Lesenswert?

Mario A. schrieb:
> Ich wollte mich mal erkundigen, wie ihr die Division durch
> input_signal_power durchführen würdet und ob ihr Tipps habt?

Moin!

Ist der Divisor konstant?

Wenn ja, dann kannst du die Division umwandeln in eine Multiplikation 
und eine Division durch eine Zweierpotenz.
Dabei wandelst du den Bruch 1/Divisor in einen Bruch um, bei dem im 
Nenner eine Zweierpotenz steht.

Beispiel:
Statt *1/3 kannst du schreiben *1365/2^12
Und die Division durch eine Zweierpotenz kannst du einfach hinschreiben.

Du kannst auch eine beliebige Division hinschreiben, Vivado baut dir 
dann einen kombinatorischen Dividierer ein.

Wenn sich der Divisor ändert, dann kannst du das entweder in 
Kombinatorik rechnen lassen, oder auch als Pipeline. Da bekommst du zwar 
Latenz dazu, aber hast weiterhin in jedem Takt ein Ergebnis. Wenn du die 
32 kSample/s verwendest, also nur 32k mal in der Sekunde ein Ergebnis 
brauchst, dann hast du bei 100 MHz Systemtakt für jede Division 3125 
Takte Zeit.

von Mario A. (itse_me_mario)


Lesenswert?

Hi, danke für deine Antwort! Also der Divisor ändert sich leider mit 
jedem Abtastwert. Dann wäre der Divider Generator wohl gut geeignet.

von Mario A. (itse_me_mario)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich hab jetzt mal versucht das NLMS-Filter zu implementieren. Leider 
bisher ohne Erfolg. Problematisch ist auch die Zeit für die Berechnung. 
Die Simulation dauert nochmal deutlich länger als für den LMS 
Algorithmus... Ich vermute dass bei der Division irgendwas schief geht. 
Das Projekt habe ich mal angehängt. Über Tipps würde ich mich sehr 
freuen. :) Vielen Dank im Voraus! Gruß Mario :)

von Gustl B. (-gb-)


Lesenswert?

Ich finde das sehr spannend, habe aber leider nicht die Zeit das 
komplett zu verstehen.

Dass die Simulation dauert fände ich noch OK, IPs brauchen oft viel 
Rechenzeit und auch in deinem Code wird ja doch viel gerechnet.

Um zu gucken ob deine VHDL Lösung funktioniert empfehle ich generell 
diesen Weg:

1. Erzeuge dir einen Signalverlauf, also Wertetabelle.
2. Berechne das Ergebnis deines Filters in einer Programmiersprache.
3. Schreibe eine Testbench die exakt diese Wertetabelle aus 1. deinem 
VHDL füttert.
4. Schreibe ebenfalls in einer Testbench das Ergebnis deines VHDL 
Filters wieder in eine Datei.
5. Vergleiche die beiden Ergebnisse in einer Programmiersprache.

Ich empfehle wirklich das std.textio Package und Python. So mache ich 
das mit meiner Filterei.

Statt Wertetabelle kannst du auch den Sinusgenerator für das I2S 
verwenden wenn das reicht und dann eben die Signalform in der Simulation 
angucken.

Viel Erfolg!

von Mario A. (itse_me_mario)


Lesenswert?

Hallo Gustl,

danke für die Tipps! So in etwa mache ich das aktuell auch. Den LMS und 
NLMS habe ich davor schon in Matlab und C implementiert (Fest- und 
Gleitkomma). Genau dieselben Daten für x und d lese ich nun in der 
Testbench ein. Ich seh mir immer die ERLE Kurven an, da diese auch den 
berechneten Fehler e enthalten. Ich hoffe, dass ich am Wochenende wieder 
vermehrt dazukomme. Gerade steht leider viel anderes Zeug an. Gruß Mario 
:)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.