Hallo, ich bräuchte mal ein paar Ideen, vielleicht wisst ihr ja was. Das Design um das es sich handelt ist ein Softcore. Darauf läuft ein Programm. Das Programm läuft auch auf einer PC Simulation mit dem gleichen Befehlssatz, fehlerfrei. Der FPGA Softcore jedoch macht regelmäßig Probleme und zwar "verrechnet" er sich dann. Die genaue Ursache rauszufinden ist recht schwierig, jedoch war es mehrmals so, das einem Register ein Wert zugewiesen wurde und dieses Register im nächsten Takt als Zieladdresse für eine Lese- oder Schreiboperation genutzt wird. Nur das es die genutzte Adresse nicht gibt. Danach klemmt der Bus. Jetzt aber das merkwürdige: es ist komplett Buildabhängig. Etwa 2/3tel aller FPGA Builds zeigen diesen Verhalten nicht. Diese funktionieren dauerhaft, egal ob warm, kalt, auch über Tage hinweg. Bei 1/3tel aller Builds ist das Verhalten da. Und zwar absolut reproduzierbar für den jeweiligen Build! Der Prozessor hängt immer an der gleichen Stelle, er verrechnet sich immer auf die gleiche Art und Weise in der gleichen Codezeile. Beim nächsten "kaputten Build" verrechnet er sich woanders. Beim gleichen Programm im Debugmodus(leicht anderer Assembler Code) verrechnet er sich woanders. Bei Builds, in denen er sich schon früh im Programm verrechnet habe ich die Simulation bis dahin laufen lassen, welche wie erwartet tadellos funktioniert. Das Design ist vollständig constraint, es gibt im gesamten Core keinen Taktübergang und es ist "weit weg" von jedem Pin. Das Timing ist erfüllt, wenn auch immer äußerst knapp. Der Füllstand des FPGAs macht keinen Unterschied, der Fehler tritt bei 30% Füllstand und bei 90% Füllstand gleichermaßen auf. Retiming an oder aus bewirkt keinen Unterschied. Der Fehler tritt sowohl mit Quartus 11 als auch 18.1 auf. Hat noch jemand eine Idee was ich versuchen kann? Ich bin mittlerweile ratlos. Danke und Gruß
:
Bearbeitet durch User
"Äußerst knapp" könnte schön das Problem sein. Ein wenig Clockjitter, und schon gehts schief. Versuch mal, beim Clock 10% Overconstraining und schau, was herauskommt. bzw kannst du die Frequenz verringern? Ist das ein kommerzieller Core, oder opensource? Normalerweise sind solche Probleme immer auf ein Timingproblem zurückzuführen.
Robert P. schrieb: > Und zwar absolut reproduzierbar für den jeweiligen Build! Auch auf unterschiedlichen FPGAs? Unabhängig davon, ob du ein Reload initierst oder einen (partiellen) Reset machst? > Hat noch jemand eine Idee was ich versuchen kann? Sieh dir mal den Reset-Pfad an. > jedoch war es mehrmals so, das einem Register ein Wert zugewiesen wurde > und dieses Register im nächsten Takt als Zieladdresse für eine Lese- > oder Schreiboperation genutzt wird. Kann dazwischen jemand auf den Bus gehen (Interrupt, DMA, Injected Code...)? > Hat noch jemand eine Idee was ich versuchen kann? Ich würde auch mal so ein reproduzierbar "defektes" Design einfach mit der halben Taktfrequenz laufen lassen. Vancouver schrieb: > "Äußerst knapp" könnte schön das Problem sein. Ein wenig Clockjitter, > und schon gehts schief. Oder wenn ein Schlauer steigende und fallende Flanken verwendet und der Takt z.B. mit 60/40 ein wenig asymmetrisch ist.
:
Bearbeitet durch Moderator
Hast du den sourcecode parat? Lade ihn bitte hoch, dann lässt sich die Quelle des Timingproblems möglicherweise identifizieren (zB Asynchrones Verhalten in der Alu oder sowas)
Vielen Dank schonmal für die Denkanstöße. Also tippt ihr auch am ehesten auf Timing. Den Takt zu verringern wird nicht so einfach, weil sehr viele Komponenten dran hängen die dann nicht mehr funktionieren. Overcontraining kann ich versuchen, mal sehen was die Synthese schafft. Ansonsten kann ich mal einen zusätzlichen, halbierten Takt aus der PLL holen, nur den Core damit betreiben und schauen ob der Fehler dann überhaupt jemals auftritt. Würde mich nicht so sehr stören das einfach eine Weile so zu nutzen. Lothar M. schrieb: > Auch auf unterschiedlichen FPGAs? Unabhängig davon, ob du ein Reload > initierst oder einen (partiellen) Reset machst? Habe leider nur das eine Board zum testen. Ein vollständiger Reset ist nicht möglich, kann nur den Core zurücksetzen. Register welche dieser schon geschrieben hat sind nicht rücksetzbar. Entsprechend ist das Verhalten je nach Fall nicht immer identisch zwischen neu runterladen und nur Core resetten. Lothar M. schrieb: > Kann dazwischen jemand auf den Bus gehen (Interrupt, DMA, Injected > Code...)? An sich nicht. Das ASM-Binary wird über den Bus ins RAM transportiert, aber nach dem einschalten des Cores kommt da nichts mehr. Was aber sein kann, ist das sich das Zeitverhalten unterscheidet, weil z.b. Zugriffe aufs SDRam vom Core aus unterschiedlich lange dauern können wegen Refresh. Hängt dann davon ab wann ich den Core einschalte, nicht vom Build. Lothar M. schrieb: > fallende Flanken Sind alles steigende. blub schrieb: > Hast du den sourcecode parat? Lade ihn bitte hoch, dann lässt sich die > Quelle des Timingproblems möglicherweise identifizieren (zB Asynchrones > Verhalten in der Alu oder sowas) Hab es mal angehangen, ich bezweifle allerdings das man da so einfach etwas findet. Die Logik, welche versagt, befindet sich zudem konsequent in einem getakteten Prozess. (execute stage)
Robert P. schrieb: > Hat noch jemand eine Idee was ich versuchen kann? Die allokierten M9K-Blöcke manuell zuweisen, manchmal ist einer davon madig. Signaltap rausschmeissen.
Signaltap nutze ich nicht. Der Fehler tritt auch nicht in Zusammenhang mit Blockram auf. Trotzdem Danke!
Mach ein Testprogramm (benchmark sozusagen) und wenn Fehler auftauchen gib sie aus
Können wir mal dein Constraints-File sehen? 'vollständig constrained' muß ja nicht heißen, dass das alles richtig ist. Hast Du Taktübergänge im Design?
Robert P. schrieb: > blub schrieb: >> Hast du den sourcecode parat? Lade ihn bitte hoch, dann lässt sich die >> Quelle des Timingproblems möglicherweise identifizieren (zB Asynchrones >> Verhalten in der Alu oder sowas) > > Hab es mal angehangen, ich bezweifle allerdings das man da so einfach > etwas findet. Das ist ja auch nur ein Teil des Sources, zum nachvollziehen braucht es noch das constraintfile (*.sdc) und das settingfile (*.qpf) sowie alle anderen built-scripte aus denen ersichtlich ist welche settings aktiv sind. Und natürlich sollte man alle logfiles durchgelesen haben, da findet sich oft die entscheidende Warning. (*.rpt in /outputs o.ä.) >Der Prozessor hängt immer an der gleichen Stelle, er verrechnet sich >immer auf die gleiche Art und Weise in der gleichen Codezeile. >Beim nächsten "kaputten Build" verrechnet er sich woanders. Klingt nach einem Programmspeicher-ROM problem. Verschieb mal mit nem Linkerscripte das codesegment und schau ob das Problem mitwandert. Anderes Board verwenden wäre auch ne Idee. VHDL-rumgestochere würde ich da erst mal lassen, frag mal einen Embedded Softwareheini wie man Probleme mit bestimmten Codezeilen eingrenzt. https://www.mikrocontroller.net/forum/gcc
Ich verwende keinen gcc, sondern meinen eigenen Compiler und kenne daher den ausgeführten Code bis auf die Instruktion runter. Mit Codezeile ist hier CPU Instruktion gemeint. Welche fehlschlägt sehe ich recht einfach daran, das der Bus hängt, damit die CPU hängt, ich den ProgramCounter auslesen kann und per Debugger auch weiß wie die Register stehen. Der Programmspeicher(SRAM + Blockram Cache) ist ok. Zum einen weil ich den zurückgelesen habe, zum anderen wenn ich die Instruktion die zum Aufhängen führt (read/write register from/to bus) gegen eine unverfängliche ersetze (move register), dann läuft es weiter und ich sehe das er an eine Murx Adresse schreiben wollte. Markus F. schrieb: > Können wir mal dein Constraints-File sehen? > Hast Du Taktübergänge im Design? Ist angehangen. Allerdings schon auf 18ns Period statt 20, auch wenn es leider nichts gebracht hat. Das Timing ignore habe ich deshalb drinne, weil ein Pfad von 100 nach 108 Mhz natürlich nichts wird und ich deshalb jeden Übergang selbst 3 mal eintakte oder über Dualclock Blockrams führe. Taktübergänge habe ich auch nur Richtung VGA und Audio, beides schließe ich hierfür aus, weil kein Zugriff auf Register aus dem Bereich passiert wenn es crasht.
Sowas:
1 | if (cache_write_done = '1') then |
2 | cache_write_enable <= '0'; |
3 | stall_pipe <= '0'; |
4 | request_cmd_exe <= '1'; |
5 | end if; |
6 | |
7 | if (cache_read_done = '1') then |
8 | result <= signed(cache_read_data); |
9 | cache_read_enable <= '0'; |
10 | stall_pipe <= '0'; |
11 | request_cmd_exe <= '1'; |
12 | end if; |
ist immer schlecht, weil keine Priorisierung vorliegt. Wenn beide Signale gleichzeitig kommen kriegt man da schnell einen deadlock. Kann aber nicht sagen, ob hier sowas vorkommen kann. Immer ELSIF nehmen!
Werden CLOCK2_50 und CLOCK3_50 nur für die PLL genutzt? Was hängt an clk12 dran? Duke
Alle Input Clocks sind 50Mhz und gehen direkt in eine PLL. Aus der ersten werden 100 Mhz für nahezu das ganze FPGA gemacht sowie 120° versetzte 100 Mhz für die SDRam Clock. Aus Clock2_50 werden 108 Mhz für VGA Aus Clock3_50 werden 12 MHz für Audio @ VHDL-Polizei: Danke. Das elsif schreibe ich absichtlich nicht, weil der Cache nur eine Operation gleichzeitig ausführen kann. Bei einem elsif würde der Pfad für "result" länger werden, natürlich nur falls die Synthese nicht merkt das nicht beides gleichzeitig auftreten kann und es selbst optimiert. Der "result" Pfad ist kritischste im ganzen Core. Mir ist klar, das der Code vielleicht schwieriger zu verstehen ist, weil ich vom Überschreiben der Signale an späterer Stelle im Code regen Gebrauch mache.
Robert P. schrieb: > weil > ich vom Überschreiben der Signale an späterer Stelle im Code regen > Gebrauch mache. eine super Strategie, um gut lesbaren Code zu erzeugen ... und sich dann selber darin zu verfangen
Ich persönlich finde das
1 | a <= '0' |
2 | if (b = '1') then |
3 | a <= '1'; |
4 | end if; |
besser lesbar als das.
1 | if (b = '1') then |
2 | a <= '1'; |
3 | else
|
4 | a <= '0' |
5 | end if; |
Gerade für Signale die nur einen Takt aktiv sein sollen finde ich es sehr übersichtlich die am Anfang eines Prozesses definiert auf inaktiv zu setzen und später zu überschreiben wenn nötig. Mir ist aber schon klar dass das kein allgemeiner Konsens ist, deswegen mache ich sowas auch nur in einem Hobbyprojekt was ich voraussichtlich eh nur alleine ankucke. Auf Arbeit schreibe ich solchen Code nicht. Genau das war aber meine Befürchtung wenn ich meinen Code hier einstelle: das der Stil kritisiert wird, auch er mit dem Problem absolut nichts zu tun hat. In einem synchronen Prozess kann ich schreiben was ich will, das Syntheseergebnis sollte nicht vom Seed des Fitters abhängig sein. Habe den Code eigentlich nur eingestellt, damit man sieht ob der betroffene Code weder asynchrone Berechnungen drin hat, noch Taktübergänge oder sonstiges was sonst Probleme machen könnte. Vielleicht wäre ja jemandem noch ein Konstrukt aufgefallen das (syntheselaufabhängige) Probleme machen könnte, mir aber noch unbekannt ist.
Ein .sdc-File, das keine set_[input|output]_delay Statements enthält, würde ich nicht als vollständig constrained erachten (und Quartus meines Wissens nach auch nicht). Immerhin hängt ja anscheinend (zumindest) RAM dran (natürlich weiss ich nicht, ob das beim beschriebenen Problem überhaupt eine Rolle spielt/spielen kann). Das '\gcores' erweckt den leisen Verdacht, das wir hier potentiell von einer per generate erzeugten Mehrkern-Implementierung reden?
Ja, sind 12 von den Schätzchen drin. Aktiv ist aber nur der erste bei diesem Problem hier. Hatte auch schon überlegt den Code auf den Anderen laufen zu lassen, wäre aber ein großer Umbau im FPGA, weil es bestimmte Register gibt, an die nur der Erste darf, festverdrahtet. Meine Testskripte prüfen alle Cores, da ist bisher leider(?) noch kein Fehler aufgetreten, nur bei den tatsächlichen Programmen. Wie das halt so ist... Bin da aber auch dran. Habe extra eine Sammlung von "kaputten" Binaries auf die ich einen neuen Satz von Tests loslassen will. Markus F. schrieb: > Ein .sdc-File, das keine set_[input|output]_delay Statements enthält, > würde ich nicht als vollständig constrained So gesehen stimmt das natürlich, ich nutze "nur" IO-FFs für die externen Komponenten. Hier ist sicher potenziell ein Risiko das etwas schief geht. In Frage kommt aber nur das SRAM, alles andere ist nicht Ausführungsrelevant. Auf der anderen Seite sollten IO-FFs keine Schwankungen erlauben. Habe im qsf zusätzlich fürs SRAM stehen: - set_global_assignment -name FMAX_REQUIREMENT "100 MHz" -section_id sram - set_instance_assignment -name CLOCK_SETTINGS sram -to sram_* - set_instance_assignment -name FAST_OUTPUT_REGISTER ON -to sram_* - set_instance_assignment -name FAST_INPUT_REGISTER ON -to sram_* - set_instance_assignment -name FAST_OUTPUT_ENABLE_REGISTER ON -to sram_* DENNOCH: beim durchsehen gerade ist mir aufgefallen, das mein sram_wren, trotz alledem nicht in einem Output-FF gelandet ist. Dem werde ich mal nachgehen. Denn auch wenn das wohl eher nicht für das akute Problem verantwortlich ist, sollte das nicht sein.
Robert P. schrieb: > - set_global_assignment -name FMAX_REQUIREMENT "100 MHz" -section_id > sram Achtung, ab jetzt Mutmassung: Das sind Anweisungen an den alten "Classic"-Timing-Analyzer (und entsprechend dem Fitter). Würde mich wundern, wenn das in 18.1 noch funktioniert. Ich glaube, das ist irgendwann nach der 9er-Version verschwunden. Zumindest die älteren Quartus-Versionen haben die Project-Files noch automatisch migriert, ob die neueren das noch tun, weiss ich nicht.
Ok, das SRAM-Design stammt ursprünglich aus 2010 von einem alten Cyclone 2 mit Quartus 9, hab das nie mehr wesentlich angepasst. Ehrlich gesagt weiß ich gar nicht warum das überhaupt noch drin ist. Eigentlich sollte das 100 Mhz Contraint auf dem sdc, was ja auch für die SRAM Pins/IOFF gilt, ausreichen. Andersherum gefragt: wie würden deine Contraints da aussehen? Ich gebe zu, mit Constraints habe ich es nicht so. Versuche eigentlich immer die Logik so zu schreiben das ein allgemeines Clock Constraint reicht und die Tools sich den Rest daraus selbst errechnen, schon wegen der Portierbarkeit. Hier kann es aber sein das es nicht reicht...
Ich würde erst mal die PLL-Clocks nicht "von Hand" definieren. Sonst vergisst Du (ich jedenfalls) garantiert bei der nächsten Änderung in der PLL die constraints nachzuziehen.
1 | derive_pll_clocks |
2 | derive_clock_uncertainty |
reicht (und Du kannst die set_clock_uncertainty- und create_generated_clock- Zeilen rausschmeissen). Dann sollte für die SRAM-clock m.E. eine virtuelle clock her und die SRAM I/Os set_input_delay und set_output_delay-Statements bekommen (das ist doch synchones SRAM, oder?). bei mir sieht das (hier für ein DDR-RAM Interface) so aus:
1 | set period [expr roundto(1000.0 / 33.0, 3)] |
2 | |
3 | create_clock -period $period -name CLK_MAIN [get_ports {CLK_MAIN}] |
4 | create_clock -period $period -name virt_clk_main_in |
5 | create_clock -period $period -name virt_clk_main_out |
6 | |
7 | set_clock_groups -asynchronous \ |
8 | -group { I_PLL1|altpll_component|auto_generated|pll1|clk[1] } \ |
9 | -group { I_PLL1|altpll_component|auto_generated|pll1|clk[2] } \ |
10 | -group { I_PLL1|altpll_component|auto_generated|pll1|clk[3] } \ |
11 | -group { I_PLL1|altpll_component|auto_generated|pll1|clk[4] } \ |
12 | -group { i_ddr_pll|altpll_component|auto_generated|pll1|clk[0] \ |
13 | i_ddr_pll|altpll_component|auto_generated|pll1|clk[1] \ |
14 | i_ddr_pll|altpll_component|auto_generated|pll1|clk[2] \ |
15 | i_ddr_pll|altpll_component|auto_generated|pll1|clk[3] \ |
16 | i_ddr_pll|altpll_component|auto_generated|pll1|clk[4] \ |
17 | virt_clk_main_in \ |
18 | virt_clk_main_out \ |
19 | CLK_MAIN } \ |
20 | ... |
21 | |
22 | # constrain DDR writes |
23 | set_output_delay -clock virt_clk_ddr_out -max 0.5 [get_ports VD[*]] |
24 | set_output_delay -clock virt_clk_ddr_out -min -0.25 [get_ports VD[*]] -add_delay |
25 | set_output_delay -clock virt_clk_ddr_out -max 0.5 -clock_fall [get_ports VD[*]] -add_delay |
26 | set_output_delay -clock virt_clk_ddr_out -min -0.25 -clock_fall [get_ports VD[*]] -add_delay |
27 | |
28 | # set false paths for opposite edge data transfers (rising -> falling and falling -> rising) that Quartus |
29 | # Timing Analyzer would (falsely) check (and badly miss timing) by default otherwise |
30 | set_false_path -setup -rise_from i_ddr_pll|altpll_component|auto_generated|pll1|clk[3] -fall_to virt_clk_ddr_out |
31 | set_false_path -setup -fall_from i_ddr_pll|altpll_component|auto_generated|pll1|clk[3] -rise_to virt_clk_ddr_out |
32 | set_false_path -hold -rise_from i_ddr_pll|altpll_component|auto_generated|pll1|clk[3] -rise_to virt_clk_ddr_out |
33 | set_false_path -hold -fall_from i_ddr_pll|altpll_component|auto_generated|pll1|clk[3] -fall_to virt_clk_ddr_out |
Die clock groups sind eine "Abkürzung" für set_false_path. Clocks in einer Gruppe müssen zusammen betrachtet werden, Clocks in unterschiedlichen Gruppen betrachtet der Timing Analyzer nicht. Passt natürlich für SRAM nicht, aber vielleicht kannst Du ja was damit anfangen.
Danke! >> derive_pll_clocks Das klingt gut, werde ich so machen inklusive den clock groups. >> derive_clock_uncertainty Das habe ich auch gefunden, aber als tcl command im timing analyzer eingegeben. Der hat mir dann die Zeilen im sdc erzeugt. Wusste nicht das man es auch direkt ins sdc schreiben kann. Jetzt wirds mir aber auch klar: im sdc stehen einfach die tcl commands drin... >> das ist doch synchones SRAM leider nein, Asynchrones mit 10ns Zugriffszeit. Im Moment halte ich dort eh die Timings ein, indem ich einen Takt länger warte, was effektiv ~20ns Lesezeit statt 10ns sind. Zusammen mit IOFF sollte das dann völlig unproblematisch sein. Genau 10ns einzuhalten sind ja kaum machbar, ein bischen Schwankung zwischen den Addressleitungen ist immer und alles zwischen 50 und 100Mhz wird noch aufwändiger hinterher, wenn die Daten wieder auf 100 Mhz müssen. Ein synchrones SDRam habe ich auch noch, aber das dient nur als Framebuffer und läuft fehlerfrei mit IOFF + 120° verschobenen 200Mhz auf toggle FF -> 100 Mhz. Damit hatte ich erst Probleme als ich noch die 100 Mhz ohne IOFF mit Delays rausgegeben habe, deshalb würde ich da ungerne hin zurück.
Robert P. schrieb: >>> das ist doch synchones SRAM > > leider nein, Asynchrones mit 10ns Zugriffszeit. Im Moment halte ich dort > eh die Timings ein, indem ich einen Takt länger warte, was effektiv > ~20ns Lesezeit statt 10ns sind. Zusammen mit IOFF sollte das dann völlig > unproblematisch sein. Ich würde zumindest die Adress- und Datenleitungen mit set_output_delay constrainen und das 'Ausgabefenster' so 'zusammenziehen', um sicherzustellen, dass die Bus-Bits einigermassen gleichzeitig und nicht 'irgendwann' am RAM ankommen.
Ok, habe jetzt mal die write_enable Leitung mit Output Delay constraint. So:
1 | set_output_delay -clock { iPLL|altpll_component|auto_generated|pll1|clk[0] } -min 1 [get_ports {sram_wren}] |
2 | set_output_delay -clock { iPLL|altpll_component|auto_generated|pll1|clk[0] } -max 2 [get_ports {sram_wren}] |
davon verspreche ich mir, das write_enable im Schreibfall immer NACH Adresse und Daten aktiv wird, so wie das Datenblatt das will. Wobei mir noch nicht klar ist worauf ich Addr und Data jetzt setzen soll. -0.5 bis 0.5? Ich will eigentlich gar kein Delay auf Addr/data beaufschlagen, die sollen einfach nur gleichzeitig raus kommen. Genau das habe ich mir von IO-FF ohne Delay versprochen: alle kommen immer zur gleichen Zeit raus, nämlich wenn die rising_edge vom Takt durch ist. Vorallem über alle Builds hinweg. Wenn ich jetzt da explizit ein Output Constraint hinschreibe, riskiere ich nicht das er mir dann in jedem Build wieder etwas anderes hinbaut, nur um das constraint zu erfüllen? Außerdem noch folgendes Problem: das constraint oben wird angenommen und taucht nicht in der Liste "ignored Comnstraints" auf. Unter "Check timing" finde ich den Eintrag: generated_io_delay 1 Soweit alles ok. Wenn ich dann auf "Report all I/O Timings" gehe, dann steht da aber "nothing to report". In der Console dann jedoch: Report Timing: Found 1 setup paths (0 violated). Worst case slack is 0.683 Report Timing: Found 1 hold paths (0 violated). Worst case slack is 8.006 Was im Moment schön und gut ist, aber spätestens wenn 2 constraints drin sind natürlich schwammig wird. WO finde ich denn nun welches Output delay tatsächlich vorliegt für diesen Build und Pin? Klar kann man der Analyse einfach glauben, aber ich würds gerne schon mal sehen was er gemacht hat.
Hast Du die "Timing-Driven Synthesis" (unter "Analysis/Synthesis settings) eingeschaltet? Wenn nicht, haben die Timing-Constraints nur Kontrollfunktion und der Fitter interessiert sich nicht dafür. Im Logfile sollte irgendwo "Timing-Driven Synthesis is running" erscheinen.
Robert P. schrieb: > Ich will eigentlich gar kein Delay auf Addr/data beaufschlagen, die > sollen einfach nur gleichzeitig raus kommen. Das machst Du ja mit set_output_delay auch nicht. set_output_delay sagt, dass die Signale vom Pin bis zum Port der externen Logik in Bezug auf die Clock eben minimal und maximal die Zeit brauchen, die Du angibst. Gibst Du nichts an, geht der Fitter davon aus, dass die Signale "in Nullzeit" (also 0 Setup und eine Periode Hold) dort ankommen und hat alle Freiheiten. set_output_delay "beschneidet" die Freiheitsgrade des Fitters, Du beschreibst damit die FPGA-Umgebung. Mit '-max' gibst Du die Setup-Zeit + das Board delay zum Zielregister an (der Fitter weiss also, dass er die Signale entsprechend früher "losschicken" muss), -min ist dessen Hold-Zeit (Kommando an den Fitter: "nicht zu früh loslassen"). Wenn Du dieses Fenster 'enger' machst, hast Du eine feine Kontrolle darüber, wie die Signale dort ankommen. Wenn die Timing-Driving Synthese eingeschaltet ist, versucht der Fitter diese Vorgaben einzuhalten.
Ja ist an. Mir ist noch nicht klar von welchem Freiheitsgrad wir hier sprechen. Annahme: IO-FF, mit steigender Taktflanke landet ein Vektor (sagen wir 20 Bits/Pins) in diesen FFs, welche direkt am Pin liegen. WIE soll der Fitter denn hier noch 2 ns Verzögerung zwischen den Einzelsignalen des Vektors einbauen? Gibt es dafür extra Logik? Er wird ja wohl nicht das Signal zurück ins FPGA schicken, dort ein bischen mäandern und danach erst raus.
Robert P. schrieb: > Annahme: IO-FF, mit steigender Taktflanke landet ein Vektor (sagen wir > 20 Bits/Pins) in diesen FFs, welche direkt am Pin liegen. > > WIE soll der Fitter denn hier noch 2 ns Verzögerung zwischen den > Einzelsignalen des Vektors einbauen? > Gibt es dafür extra Logik? Zunächst mal hilft dir das Constraining, erst mal rauszufinden ob und wo genau Du überhaupt ein Problem hast oder nicht (der Timing Analyzer muss dann nicht mehr raten, sondern **weiss** wirklich was). Dann hast Du eine Clock, die (wahrscheinlich) über den Clock-Tree (also über spezielle, besonders schnelle Verbindungen geroutet wird - oder auch nicht). Auch hier kann der Fitter einen Umweg nehmen oder nicht. Auch kann er (aber auch Du selbst) mit deinen PLL's was tun(so denn dein Takt von einer PLL erzeugt wird) - 'Clock compensation'. Dann gibt's (nicht bei jedem FPGA, aber den Meisten) noch so hübsche Sachen wie den 'Output Enable Delay' oder 'Delay from Output Register to Output Pin' (schau' mal im Assignment Editor, was es da so alles gibt bzw. ins Device Handbuch, was dein Typ kann). Die Frage ist hier m.E. eher "an welcher der vielen Schrauben dreh' ich für welches Problem"
Ok, dann arbeiten wir mal damit. Wie würdest du das jetzt setzen für das SRAM? Ich möchte eigentlich gerne folgendes: Adresse, Daten und WE gleichzeitig setzen. Laut Datenblatt ist das ok, man muss nur garantieren das WE definitiv nicht VOR Addr/data ankommt. Vorher hat mein Design scheinbar nur funktioniert, weil WE nicht im IOFF war und deshalb leicht verzögert rauskam. Mit der Umstellung auf alles IOFF lief es nicht mehr. Warscheinlich war das damals Absicht es nicht ins IOFF zu tun, weiß ich nach 9 Jahren nicht mehr. Nun will ich es fixen. Ich habe jetzt WE auf +1/+2 (min/max) gesetzt. Damit läuft es erstmal wieder. Meine Vorstellung ist jedoch, das ich auch Addr und Data constrainen sollte. Mit -0.5 bis +0.5? Kann man das machen? Oder sollte ich auf 0-1 für Addr und Data und 2-3 für WE? Möchte eigentlich das er Addr und Data garnicht "anfässt" und nur WE verzögert rausgibt. Auch hier wüsste ich gerne warum es ohne Constraint nicht geht und mit schon. Leider erfahre ich nicht wie er das Delay jetzt tatsächlich erreicht hat.
Robert P. schrieb: > Meine Vorstellung ist jedoch, das ich auch Addr und Data constrainen > sollte. > Mit -0.5 bis +0.5? > Kann man das machen? Da darf ich dir das hier: http://web02.gonzaga.edu/faculty/talarico/CP430/LEC/TimeQuest_User_Guide.pdf (das ist nicht der "echte" Link, es gab mal ein offizielles Wiki von Altera, aber das hat Intel sauber verbaselt, als sie die übernommen haben) wärmstens ans Herz legen. Hat zumindest mir enorm geholfen, das Thema timing constraints wesentlich besser zu verstehen.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.