Forum: FPGA, VHDL & Co. CS4344 I2S Transmitter


von Daniel K. (daniel_k80)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich habe meinen alten I2S-Transmitter mal neu geschrieben und ihn mit 
ein paar Handshake-Signalen versehen. Für das Auslesen der Daten aus 
einem ROM und der Ansteuerung des Transmitters habe ich mir zudem einen 
State-Machine drumherum gebaut (diese möchte ich später noch erweitern 
um auch Daten in einen Speicher reinschreiben zu können).

In der Simulation sieht alles soweit stimmig aus (soweit ich das 
beurteilen kann - siehe Screenshot), aber wenn ich die Schaltung auf 
mein FPGA übertrage erhalte ich nur Rauschen und keinen Sinuston.

Wo habe ich einen Fehler in der Schaltung?
Vielen Dank für die Hilfe!

von Georg A. (georga)


Lesenswert?

Daniel K. schrieb:
> aber wenn ich die Schaltung auf mein FPGA übertrage

... sollte man bei solchen Problemen ein Oszi bzw. LA anschliessen und 
schauen, ob die Simulation was mit der Realität zu tu hat.

von Burkhard K. (buks)


Lesenswert?

Daniel K. schrieb:
> Wo habe ich einen Fehler in der Schaltung?

Keine Ahnung, aber ROM- und übertragene Daten scheinen nicht zueinander 
zu passen und ausserdem ist ein ROM-Word nur 16 Bit breit - bist Du 
sicher, dass Du dem Codec einen Sinus zufütterst?

von Daniel K. (daniel_k80)


Lesenswert?

Hallo,

ein Oszi habe ich bereits dran gehalten. Es kommt kein "vernünftiges" 
Signal raus. Es sind Spuren eines Sinus, aber komplett durcheinander.
Die ROM-Daten und die zu übertragenen Daten sind etwas versetzt, das 
stimmt schon. Die ROM-Daten werden einen Übertragungszyklus später 
gesendet als diese ausgelesen werden (z. B. das Wort an Adresse 1 wird 
erst gelesen und dann übertragen, wenn das Wort von Adresse 2 gelesen 
wird). Das ist bewusst so gemacht und sollte dem CS4344 auch egal sein 
(oder?).
Die Worte sind 16-Bit breit, aber sie stellen nur einen Kanal dar. Daher 
sende ich die Daten 2x in einem Paket. Einmal Links und einmal Rechts.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Daniel K. schrieb:
> ein Oszi habe ich bereits dran gehalten. Es kommt kein "vernünftiges"
> Signal raus. Es sind Spuren eines Sinus, aber komplett durcheinander.
Ich würde mal mit weniger komplexen signalen anfangen:
1. Rechteck(e)
2. Sägezahn/Dreieck
3. komplexere Signale aus dem ROM

> Für das Auslesen der Daten aus einem ROM und der Ansteuerung des
> Transmitters habe ich mir zudem einen State-Machine drumherum gebaut
Es ist ein wenig mühsam, sich die VHDL-Files aus den ganzen 
Verzeichnissen zusammenzuklauben. Könntest du einfach mal die paar 
relevanten Dateien als *.VHD hier anhängen?

von Curby23523 N. (Gast)


Lesenswert?

Der erste Takt auf I2S_DATA nach einem Flankenwechsel auf I2S_LRCK hat 
mal Daten und mal nicht (0/1).

Laut Datenblatt des CS4344 hat dieser aber I2S Standard und der erste 
Takt enthält keine Nutzdaten.

Kann da dein Fehler sein?

von Daniel K. (daniel_k80)


Angehängte Dateien:

Lesenswert?

Hallo,

Nils N. schrieb:
> Der erste Takt auf I2S_DATA nach einem Flankenwechsel auf I2S_LRCK hat
> mal Daten und mal nicht (0/1).
>
> Laut Datenblatt des CS4344 hat dieser aber I2S Standard und der erste
> Takt enthält keine Nutzdaten.
>
> Kann da dein Fehler sein?

ich habe mich bei der I2S-Spezifikation hieran

https://www.sparkfun.com/datasheets/BreakoutBoards/I2SBUS.pdf

orientiert. Dort steht, dass der erste Takt nach einem Wechsel von WS 
(also hier LRCLK) von Low nach High, bzw. von High nach Low, das LSB des 
Datenwortes n - 1 überträgt. Das MSB des Datenwortes n wird erst beim 
zweiten Takt des Flankenwechsels von LRCLK übertragen. Ich habe meinen 
Sender nach diesem PDF aufgebaut und dann "nur" noch die spezifischen 
Komponenten für den CS4344 hinzugefügt (wie z. B. MCLK).

Lothar M. schrieb:
> Daniel K. schrieb:
>> ein Oszi habe ich bereits dran gehalten. Es kommt kein "vernünftiges"
>> Signal raus. Es sind Spuren eines Sinus, aber komplett durcheinander.
> Ich würde mal mit weniger komplexen signalen anfangen:
> 1. Rechteck(e)
> 2. Sägezahn/Dreieck
> 3. komplexere Signale aus dem ROM
>
>> Für das Auslesen der Daten aus einem ROM und der Ansteuerung des
>> Transmitters habe ich mir zudem einen State-Machine drumherum gebaut
> Es ist ein wenig mühsam, sich die VHDL-Files aus den ganzen
> Verzeichnissen zusammenzuklauben. Könntest du einfach mal die paar
> relevanten Dateien als *.VHD hier anhängen?

sry, ich dachte ein komplettes Projekt wäre einfacher :).
Anbei die einzelnen VHDL-Files und die Testbench. Einzig die 
ClockingWizard IP kann ich nicht hochladen. Die muss dann ggf. in den 
Files ausgetauscht werden.

von Curby23523 N. (Gast)


Lesenswert?

Daniel K. schrieb:
> Dort steht, dass der erste Takt nach einem Wechsel von WS
> (also hier LRCLK) von Low nach High, bzw. von High nach Low, das LSB des
> Datenwortes n - 1 überträgt.

Dort steht: "The WS line changes one clock period before the MSB is 
transmitted."

Bei dir sehe ich aber das MSB scheinbar schon direkt nach dem 
Flankenwechsel.

Zusätzlich will der CS4344 das MSB zuerst haben. Beides will also zurst 
einen takt Pause und dann das MSB, also komplett gegensätzlich zu deiner 
Meinung. Oder?

Bzw. du kannst nicht das LSB in dieser taktwartezeit schicken oder? 
Diese Pause iat ja dazu gedacht, die Nutzdaten auszuwerten. Der CS4345 
hat diese Taktpause nicht.

von Daniel K. (daniel_k80)


Angehängte Dateien:

Lesenswert?

Nils N. schrieb:
> Daniel K. schrieb:
>> Dort steht, dass der erste Takt nach einem Wechsel von WS
>> (also hier LRCLK) von Low nach High, bzw. von High nach Low, das LSB des
>> Datenwortes n - 1 überträgt.
>
> Dort steht: "The WS line changes one clock period before the MSB is
> transmitted."
>
> Bei dir sehe ich aber das MSB scheinbar schon direkt nach dem
> Flankenwechsel.
>
> Zusätzlich will der CS4344 das MSB zuerst haben. Beides will also zurst
> einen takt Pause und dann das MSB, also komplett gegensätzlich zu deiner
> Meinung. Oder?
>
> Bzw. du kannst nicht das LSB in dieser taktwartezeit schicken oder?
> Diese Pause iat ja dazu gedacht, die Nutzdaten auszuwerten. Der CS4345
> hat diese Taktpause nicht.

Hallo,

anbei noch mal das ein besserer Screenshot der Simulation (für das 
Datenwort 0x0805).
Das Wort hat die Bitfolge

0000 1000 0000 0101

WS geht bei CounterDelay2 = 30 runter und ist somit bei dem LSB des 
Datenwortes, welches zuvor gesendet wurde, auf Low. Dann beginnt das 
eigentliche Datenwort. Bit 15 - 12 sind 0, Bit 11 ist 1, Bit 10 - 3 sind 
0, 2 ist 1, 1 ist 0 und 0 ist 1. Während das Bit 0 aber 1 ist, ist WS 
bereits auf 1 gesprungen (das ganze dann noch mal, weil ich das selbe 
Datenwort für beide Kanäle sende).
Das ist doch das gewünschte Verhalten, wie es in der Spezifikation 
beschrieben ist. Oder sehe ich das gerade falsch (ggf. habe ich was 
falsch interpretiert...will das nicht ausschließen)?

: Bearbeitet durch User
von Curby23523 N. (Gast)


Lesenswert?

Du schickst also das LSB während des ersten Taktes eines neuen Frames? 
Ich bin nicht soo I2S geläufig - ist das denn erlaubt? Aber wenn schon, 
dann müsste das LSB doch einfach abgeschnitten werden.

von Daniel K. (daniel_k80)


Angehängte Dateien:

Lesenswert?

Hallo,

laut dieser Spezifikation

https://www.sparkfun.com/datasheets/BreakoutBoards/I2SBUS.pdf

soll das wohl so sein (siehe Bild) :).

: Bearbeitet durch User
von Burkhard K. (buks)


Lesenswert?

Daniel K. schrieb:
> laut dieser Spezifikation
>
> https://www.sparkfun.com/datasheets/BreakoutBoards/I2SBUS.pdf
>
> soll das wohl so sein (siehe Bild) :).

Welches Verhältnis MCLK/SCLK/LRCLK verwendest Du? Laut Datenblatt hängt 
die Zahl der zu übertragenen Bits beim I2S-Format vom Verhältnis 
MCLK/LRCLK ab (gilt analog auch bei externer SCLK):

I²S, 16-Bit data and INT SCLK = 32 Fs if MCLK/LRCK = 1024, 512, 256, 
128, or 64
I²S, Up to 24-Bit data and INT SCLK = 48 Fs if MCLK/LRCK = 768, 384, 
192, or 96
I²S, Up to 24-Bit data and INT SCLK = 72 Fs if MCLK/LRCK = 1152

Alternativ gibt es ein left- oder right-justified Format, da fallen 
LRCLK-Flanke und MSB zusammen.

von Bernhard K. (bkom)


Lesenswert?

Aus I2S_Top.vhd:
>(...)
> StateChange : process(Clock, Reset, CurrentState)
>        variable NextState : STATES;
>    begin
>        if(Reset = '1') then
>            NextState := ResetState;
> (...)
Du verwendest einen asyncronen Reset, ist der einsyncronisiert?
Siehe dazu auch:
Beitrag "Re: Detailfrage Reset"

und sehe ich das Richtig: hier wird ein geteilter Clock verwendet?

aus "I2S-Transmitter.vhd"
>  -- Generate the I2S clock
>    I2S_Clock_Divider : ClockDivider generic map (Prescaler => >(MCLK_PRESCALER / 
(2 * DATA_WIDTH_PER_CHANNEL)))
>                                port map (  Clock_In => Clock,
>                                            Reset => Reset,
>                                            Clock_Out => *Clock_Int*
>                                            );
>(...)
>Load_data : process(Clock_Int, Reset, Strobe, Data)
>    begin
>        if(Reset = '1') then
>            DataBuffer <= (others => '0');
>        elsif(falling_edge(Clock_Int)) then

Und in I2S_Top.vhd wird der eingangs Takt der Clockdividers verwendet?
> -- State network
>    StateChange : process(Clock, Reset, CurrentState)
>        variable NextState : STATES;
>    begin
>        if(Reset = '1') then
>            NextState := ResetState;
>        elsif(rising_Edge(Clock)) then
ist das so beabsichtigt und ok? nun dazu auch:
https://www.mikrocontroller.net/articles/Taktung_FPGA/CPLD

von Daniel K. (daniel_k80)


Lesenswert?

Burkhard K. schrieb:
> Daniel K. schrieb:
>> laut dieser Spezifikation
>>
>> https://www.sparkfun.com/datasheets/BreakoutBoards/I2SBUS.pdf
>>
>> soll das wohl so sein (siehe Bild) :).
>
> Welches Verhältnis MCLK/SCLK/LRCLK verwendest Du? Laut Datenblatt hängt
> die Zahl der zu übertragenen Bits beim I2S-Format vom Verhältnis
> MCLK/LRCLK ab (gilt analog auch bei externer SCLK):
>
> I²S, 16-Bit data and INT SCLK = 32 Fs if MCLK/LRCK = 1024, 512, 256,
> 128, or 64
> I²S, Up to 24-Bit data and INT SCLK = 48 Fs if MCLK/LRCK = 768, 384,
> 192, or 96
> I²S, Up to 24-Bit data and INT SCLK = 72 Fs if MCLK/LRCK = 1152
>
> Alternativ gibt es ein left- oder right-justified Format, da fallen
> LRCLK-Flanke und MSB zusammen.

Hallo,

danke für die Antworten. Ich werde es morgen mal ausprobieren und 
berichten.

Bernhard K. schrieb:
> Aus I2S_Top.vhd:
>>(...)
>> StateChange : process(Clock, Reset, CurrentState)
>>        variable NextState : STATES;
>>    begin
>>        if(Reset = '1') then
>>            NextState := ResetState;
>> (...)
> Du verwendest einen asyncronen Reset, ist der einsyncronisiert?
> Siehe dazu auch:
> Beitrag "Re: Detailfrage Reset"
>
> und sehe ich das Richtig: hier wird ein geteilter Clock verwendet?
>
> aus "I2S-Transmitter.vhd"
>>  -- Generate the I2S clock
>>    I2S_Clock_Divider : ClockDivider generic map (Prescaler => >(MCLK_PRESCALER 
/
> (2 * DATA_WIDTH_PER_CHANNEL)))
>>                                port map (  Clock_In => Clock,
>>                                            Reset => Reset,
>>                                            Clock_Out => *Clock_Int*
>>                                            );
>>(...)
>>Load_data : process(Clock_Int, Reset, Strobe, Data)
>>    begin
>>        if(Reset = '1') then
>>            DataBuffer <= (others => '0');
>>        elsif(falling_edge(Clock_Int)) then
>
> Und in I2S_Top.vhd wird der eingangs Takt der Clockdividers verwendet?
>> -- State network
>>    StateChange : process(Clock, Reset, CurrentState)
>>        variable NextState : STATES;
>>    begin
>>        if(Reset = '1') then
>>            NextState := ResetState;
>>        elsif(rising_Edge(Clock)) then
> ist das so beabsichtigt und ok? nun dazu auch:
> https://www.mikrocontroller.net/articles/Taktung_FPGA/CPLD

Danke für die Links.
Der Reset ist nicht einsynchronisiert. Vielmehr wende ich hier Wissen 
aus dem Studium an, was VHDL angeht und dort wurde uns eingetrichtert 
den Reset IMMER asynchron einzubauen (interessanterweise auch auf Xilinx 
FPGAs). Ich werde den entsprechenden Thread mal an meinen Prof 
weiterleiten :)

Mit dem Takt hast du Recht. Auch dazu lese ich mir den entsprechenden 
Artikel mal durch.

Vielen Dank für die Hinweise :)

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Daniel K. schrieb:
> dort wurde uns eingetrichtert den Reset IMMER asynchron einzubauen
Du kannst zwar den Reset asynchron aktivieren, musst ihn aber auf jeden 
Fall synchron wegnehmen.
Dazu den fast zehnjährigen 
Beitrag "Re: Reset für mehrere Komponenten"

von Burkhard K. (buks)


Lesenswert?

Lothar M. schrieb:
> Du kannst zwar den Reset asynchron aktivieren, musst ihn aber auf jeden
> Fall synchron wegnehmen.

Wenn man sich den MCLK-Takt per DCM/MMCM/PLL etc. erzeugt, kann das 
negierte "locked"-Signal auch als synchronisierter Reset dienen, 
zumindest bei Xilinx scheint "locked" immer synchron gesetzt zu werden. 
Laut Simulation dauert es bei 100 MHz Systemtakt ca. 7.5 us, bis 
"locked" aktiv wird (Könnte von der generierten Frequenz abhängen).

Bernhard K. schrieb:
> ist das so beabsichtigt und ok? nun dazu auch:
> https://www.mikrocontroller.net/articles/Taktung_FPGA/CPLD

Spricht eigentlich etwas dagegen, eine "Derived Clock" mit einem 
passenden Puffer-Primitiv (z.B.: BUFG, BUFGMUX) zu einer "guten" 
Taktquelle zu adeln?

von Daniel K. (daniel_k80)


Lesenswert?

Hallo,

Burkhard K. schrieb:
> Lothar M. schrieb:
>> Du kannst zwar den Reset asynchron aktivieren, musst ihn aber auf jeden
>> Fall synchron wegnehmen.
>
> Wenn man sich den MCLK-Takt per DCM/MMCM/PLL etc. erzeugt, kann das
> negierte "locked"-Signal auch als synchronisierter Reset dienen,
> zumindest bei Xilinx scheint "locked" immer synchron gesetzt zu werden.
> Laut Simulation dauert es bei 100 MHz Systemtakt ca. 7.5 us, bis
> "locked" aktiv wird (Könnte von der generierten Frequenz abhängen).
>

also kann ich das Locked des ClockingWizards verwende habe ich meinen 
synchronen Reset? In den VHDL Vorlesungen wurde der Reset auch immer mit 
dem Locked verknüpft (wahrscheinlich sogar aus dem Grund).
Meinen Clockteiler werde ich die Tage dann mal umbauen.

von Duke Scarring (Gast)


Lesenswert?

Daniel K. schrieb:
> also kann ich das Locked des ClockingWizards verwende habe ich meinen
> synchronen Reset?
Ich würde es noch einsynchronisieren, der Aufwand ist minimal.
Wenn man 'locked' nicht beachtet, kann Dir ggf. das hier passieren:
Beitrag "Spartan 6, dem DCM-Ausgang auf die Finger geschaut"

Duke

von Daniel K. (daniel_k80)


Lesenswert?

Hallo,

also zusammengefasst sollte ich dann grundsätzlich so etwas hier für den 
Reset verwenden (frei Hand geschrieben, daher ggf. Syntaxfehler). Das 
Locked kommt ggf. weg, je nachdem ob ich nen DCM benutze oder nicht.
1
arch ...
2
  signal Reset_Int : STD_LOGIC := '0';
3
  signal Locked : STD_LOGIC := '0'; -- Reset Signal für die restliche Schaltung
4
begin
5
6
  process(Clock, Reset)
7
  begin
8
    if(rising_edge(Clock)) then
9
      Reset_Int <= Locked and Reset;
10
    end if;
11
  end process;
12
end arch ...

Dazu dann noch mal eine kleine Offtopicfrage...
Kann man sich das Routing bzw. die FFs etc., die für eine Schaltung 
genutzt werden, genau anschauen? Ich habe im Vivado noch nicht so ganz 
durchschaut wie und ob man sich das visualisieren kann. Bisher habe ich 
nur gesehen, dass die benutzten CLBs angezeigt werden können, nicht aber 
die Routingressourcen.
Ich würde mir nämlich gerne mal den Unterschied (rein 
Schaltungstechnisch) zwischen einem Synchronen und einem Asynchronen 
Reset anschauen. Hat da vielleicht jemand eine Anleitung oder 
Beschreibung zu?

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.