Forum: FPGA, VHDL & Co. Anodencontroller für LED Matrizen - bitte um Codekritik


von Alex T. (Gast)


Lesenswert?

Hallo!

Ich baue gerade einen XC9572XL basierten Anodencontroller für LED 
Matrizen.

Der CPLD lauscht an der Clockleitung des SPI Busses mit dem die 
Shiftregister mit integrierten Konstantstromsenken mit Daten beladen 
werden.

Jede 2592 Clocks muss folgendes passieren:

Es sollen 8 Anodenausgänge durchgeschaltet werden, es soll jeweils 1 
Anodenausgang low sein, während die anderen High-Z sind.

Der CPLD soll auch ein Latchsignal für die Shiftregister generieren.

Dabei muss der Latch mindestens 10ns nach der letzten ansteigenden 
Clockflanke und mindestens 100ns vor der nächsten ansteigenden 
Clockflanke kommen.

Außerdem soll ein Reset vorhanden sein welcher die Register auf bekannte 
Startwerte setzt und alle Ausgänge auf High-Z schaltet.

Im Moment habe ich das ganze wie folgt implementiert:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
4
entity rgbledmatrix is
5
    Port ( reset : in  STD_LOGIC;
6
        clock : in  STD_LOGIC;
7
           latch : out  STD_LOGIC := '0';
8
           anodes : out  STD_LOGIC_VECTOR (7 downto 0):= "0ZZZZZZZ");
9
end rgbledmatrix;
10
11
architecture Behavioral of rgbledmatrix is
12
constant clock_div:  integer:=2592;    -- 12 Bit pro Kanal * 24 Kanäle * 9 TLC5947 = 2592
13
signal counter:    integer range 0 to clock_div-1;
14
signal output:      STD_LOGIC_VECTOR (7 downto 0):= "0ZZZZZZZ";
15
16
begin
17
18
process(clock,reset)
19
begin
20
  if rising_edge(clock) then
21
    if reset='0' then            -- Synchroner reset: Counter zurücksetzen und Outputregister auf Startwert
22
        counter <= 0;
23
        output <= "0ZZZZZZZ";
24
    elsif counter=clock_div-1 then
25
      counter <= 0;
26
      output(6 downto 0) <= output(7 downto 1);    --Anodenbits vorbereiten
27
      output(7) <= output(0);
28
    else
29
      counter <= counter + 1;
30
    end if;
31
  end if;
32
end process;
33
34
process(clock,reset)
35
begin
36
  if falling_edge(clock) then
37
    if reset='0' then            -- Synchroner reset: Ausgänge auf High-Z
38
      latch <= 'Z';
39
      anodes <= "ZZZZZZZZ";
40
    elsif counter=0 then
41
      anodes(7 downto 0) <= output(7 downto 0); --Anodenbits ausgeben
42
      latch <= '1';
43
    elsif counter=1 then
44
      latch <= '0';
45
    end if;
46
  end if;
47
end process;
48
49
end Behavioral;

Im behavioral Sim scheint es richtig zu funktionieren, da ich aber nicht 
all zu viel Erfahrung mit VHDL und CPLDs habe vermute ich, dass man den 
Code auch eleganter schreiben könnte.

Ich würde mich freuen wenn ihr kurz drüber schauen und mir 
Verbesserungsvorschläge geben könntet.

von Duke Scarring (Gast)


Lesenswert?

Hast Du da noch eine passende Testbench dazu?

Duke

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


Lesenswert?

Alex T. schrieb:
> dass man den Code auch eleganter schreiben könnte.
Man sollte den Reset aus der Sensitivliste rauslassen, weil die Prozesse 
wegen des synchronen Resets nur auf clk sensitiv sind.
(Eine Frage am Rande: ist der Reset wirklich einsynchronisiert?)
Falls nicht: Gratulation!
Du hast einen Zustandsautomaten mit asynchronem Eingang gebaut:
http://www.lothar-miller.de/s9y/archives/64-State-Machine-mit-asynchronem-Eingang.html

Man sollte immer die selbe Taktflanke verwenden, weil sich sonst die 
Laufzeiten für bestimmte Pfade halbieren, und das Design eigentlich für 
den doppelte Taktfrequenz ausgelegt werden muss. Noch schlimmer ist es, 
wenn dann das Tastverhältnis des Takte nicht genau 50% ist...


>       anodes <= "ZZZZZZZZ";
Warum hochohmig? Hängt an dem Bus noch was anderes?


>  anodes(7 downto 0) <= output(7 downto 0); --Anodenbits ausgeben
So gehts kürzer:
 anodes <= output; --Anodenbits ausgeben

von Alex T. (Gast)


Lesenswert?

Erstmal vielen Dank für die Antworten!

> Hast Du da noch eine passende Testbench dazu?

Ich habe noch nie eine geschrieben. Werde es mir demnächst anschauen.
Getestet habe ich es in Isim mit "Force constant" und "Force Clock".

> Eine Frage am Rande: ist der Reset wirklich einsynchronisiert?

Habe mal auf deiner Seite die Infos darüber nachgelesen, tatsächlich war 
der reset nicht richtig einsynchronisiert. Werde das korrigieren.

> Man sollte immer die selbe Taktflanke verwenden, weil sich sonst die
Laufzeiten für bestimmte Pfade halbieren, und das Design eigentlich für
den doppelte Taktfrequenz ausgelegt werden muss. Noch schlimmer ist es,
wenn dann das Tastverhältnis des Takte nicht genau 50% ist...

Der Grund für das Verwenden beider Taktflanken ist dass ich nicht weiß 
wie ich das Timing für den Latch erfülle: "Dabei muss der Latch 
mindestens 10ns nach der letzten ansteigenden
Clockflanke und mindestens 100ns vor der nächsten ansteigenden
Clockflanke kommen."

Wenn ich den bei der letzten ansteigenden Clockflanke hoch setzte kommt 
er möglicherweise zu früh, wenn ich den bei der ersten ansteigenden 
Clockflanke setze kommt er zu spät.

Bei der Abtastung an der fallenden Flanke kann ich den Latch nach einer 
halben Clockperiode setzen. Sprich solange meine SPI Clock kleiner als 
5MHz ist kann ich die Timings für den Latch einhalten.

Gibt es da eine Lösung bei der ich dem CPLD nicht einen eigenen von SPI 
unabhängigen clock zuführen muss?


> Warum hochohmig? Hängt an dem Bus noch was anderes?

Die Ausgänge hängen an Gates der P-MOSFETs die wiederum mit einem Pullup 
an maximal 5V hängen. Damit nutze ich die 5V Toleranz des CPLD aus und 
spare Treibertransistoren um die P-MOSFETs zu treiben.

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


Lesenswert?

Alex T. schrieb:
> Getestet habe ich es in Isim mit "Force constant" und "Force Clock".
Schreib eine Testbench. Das geht für dieses Projekt unglaublich einfach. 
siehe dort zweiter Teil:
http://www.lothar-miller.de/s9y/archives/80-Hello-World!.html
Und nochmal Schritt für Schritt in der zweiten Hälfte der Anleitung:
http://www.lothar-miller.de/s9y/archives/81-Xilinx-ISE-Step-by-Step.html

Alex T. schrieb:
> Der Grund für das Verwenden beider Taktflanken ist dass ich nicht weiß
Das ist in der Regel der schlechteste Grund!

> wie ich das Timing für den Latch erfülle: "Dabei muss der Latch
> mindestens 10ns nach der letzten ansteigenden
> Clockflanke und mindestens 100ns vor der nächsten ansteigenden
> Clockflanke kommen."
Woher hast du diese Zeiten?

> Wenn ich den bei der letzten ansteigenden Clockflanke hoch setzte kommt
> er möglicherweise zu früh, wenn ich den bei der ersten ansteigenden
> Clockflanke setze kommt er zu spät.
Ratestunde?

In einem FPGA ist es bei einem synchronen Design so, dass sofort nach 
und wegen einer positiven Taktflanke Unruhe hersscht, weil neue 
Zustände, Zählerstände usw... ermittelt werden. Das dauert eine Weile 
und muss rechtzeitig vor der nächsten positiven Taktflanke muss Ruhe 
herrschen. Und ob es reicht oder nicht, dass können die Tools selber 
ausrechnen, du sagt in einem Constraints-File nur, welche Taktfrequenz 
du willst.

von Alex T. (Gast)


Lesenswert?

> Woher hast du diese Zeiten?

Die sind aus dem Datenblatt des Schieberegisters/Stromsenke: 
http://www.ti.com/lit/ds/symlink/tlc5947.pdf

Die relevanten zeiten sind:

Th1 ist die Zeit zwischen der letzten ansteigenden Clockflanke und dem 
Latch - mindestens 10ns.
Tsu1 ist die Zeit zwischen dem ansteigendem Latch und der nächsten 
Clockflanke - mindestens 100ns.

> Ratestunde?

Möglicherweise weil: Wenn ich den Latch bei der letzten Clockflanke 
setze und der mehr als 10ns im CPLD braucht wird das Timing des 
Schieberegisters eingehalten. (sind die Timings im CPLD determenistisch? 
oder handelt es sich um Maximalwerte?)

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


Lesenswert?

Alex T. schrieb:
> Der CPLD lauscht an der Clockleitung des SPI Busses mit dem die
> Shiftregister mit integrierten Konstantstromsenken mit Daten beladen
> werden.
>
> Jede 2592 Clocks muss folgendes passieren:
Das ist übrigens ein recht kritischer Ansatz, denn wenn du nur die Takte 
mitzählst und sonst keinen Sync-Mechanismus hast, dann hagelt dich ein 
einziger Störimpuls für den Rest des Tages raus... :-o

Alex T. schrieb:
> (sind die Timings im CPLD determenistisch?
> oder handelt es sich um Maximalwerte?)
Zweimal ja.
Weil die Maximalwerte angegeben werden, sind die Zeiten 
deterministisch. Dein eigentliches Problem hier ist, dass du Zeiten 
generieren willst und keinen Takt hast. Denn jede Zeit in einem 
programmierbaren Baustein mird idR. über Zähler realisiert.

> Dabei muss der Latch mindestens 10ns nach der letzten ansteigenden
> Clockflanke und mindestens 100ns vor der nächsten ansteigenden
> Clockflanke kommen.
Das kannst du dann bestenfalls über Hardwaregebastel hinbiegen...

Der richtige und einzig sinnvolle Ansatz ist, den Latch-Impuls vom uC 
aus zu generieren. So ist das eigentlich gedacht...

von Alex T. (Gast)


Lesenswert?

> Das ist übrigens ein recht kritischer Ansatz, denn wenn du nur die
> Takte mitzählst und sonst keinen Sync-Mechanismus hast, dann hagelt
> dich ein einziger Störimpuls für den Rest des Tages raus... :-o
Eventuell könnte ich dem Entgegenwirken indem ich nach jedem Frame 
(20736 Clocks) den CPLD resette.

> Dein eigentliches Problem hier ist, dass du Zeiten
> generieren willst und keinen Takt hast. Denn jede Zeit in einem
> programmierbaren Baustein mird idR. über Zähler realisiert.
Sprich idealerweise hätte der CPLD einen eigenen Takt mit dem ich die 
entsprechenden Delays generieren kann?

> Der richtige und einzig sinnvolle Ansatz ist, den Latch-Impuls vom uC
> aus zu generieren. So ist das eigentlich gedacht...
Klar, ich würde im Prinzip auch ohne CPLD auskommen nur: Ich möchte die 
ganze Bildausgabe durch einen DMA im uC realisieren, welcher quasi 
endlos den Speicherbereich mit dem Frame auf dem SPI ausgibt. Sprich das 
Chipselect kann automatisch nur ausgegeben werden wenn der DMA am Ende 
des Frames ankommt.

Der wichtigere Grund ist aber: Ich wollte schon immer mal mit CPLDs 
rumspielen und dachte eine praktische Verwendung für den CPLD gefunden 
zu haben.

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


Lesenswert?

Alex T. schrieb:
>> Das ist übrigens ein recht kritischer Ansatz, denn wenn du nur die
>> Takte mitzählst und sonst keinen Sync-Mechanismus hast, dann hagelt
>> dich ein einziger Störimpuls für den Rest des Tages raus... :-o
> Eventuell könnte ich dem Entgegenwirken indem ich nach jedem Frame
> (20736 Clocks) den CPLD resette.
Könnte man machen, das ist dann ja auch ein Sync-Impuls...

> Sprich idealerweise hätte der CPLD einen eigenen Takt mit dem ich die
> entsprechenden Delays generieren kann?
Ja, so um die 50MHz sind da üblich. Nur blöd, dass in einem CPLD so 
wenige Flipflops sind...

von Alex T. (Gast)


Lesenswert?

Gut, dann probiere ich es mal mit einem extra Takt umzusetzen. Mal sehen 
wie weit ich mit dem CPLD den ich habe komme.

Vielen Dank für deine Hilfe, ich hab einiges gelernt!

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.